1/*-
2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30/*
31 * MD bootstrap main() and assorted miscellaneous
32 * commands.
33 */
34
35#include <stand.h>
36#include <stddef.h>
37#include <string.h>
38#include <machine/bootinfo.h>
39#include <machine/cpufunc.h>
40#include <machine/psl.h>
41#include <sys/disk.h>
42#include <sys/reboot.h>
43#include <common/drv.h>
44
45#include "bootstrap.h"
46#include "common/bootargs.h"
47#include "libi386/libi386.h"
48#include "libi386/smbios.h"
49#include "btxv86.h"
50
51#ifdef LOADER_ZFS_SUPPORT
52#include "libzfs.h"
53#endif
54
55CTASSERT(sizeof(struct bootargs) == BOOTARGS_SIZE);
56CTASSERT(offsetof(struct bootargs, bootinfo) == BA_BOOTINFO);
57CTASSERT(offsetof(struct bootargs, bootflags) == BA_BOOTFLAGS);
58CTASSERT(offsetof(struct bootinfo, bi_size) == BI_SIZE);
59
60/* Arguments passed in from the boot1/boot2 loader */
61static struct bootargs *kargs;
62
63static uint32_t		initial_howto;
64static uint32_t		initial_bootdev;
65static struct bootinfo	*initial_bootinfo;
66
67struct arch_switch	archsw;		/* MI/MD interface boundary */
68
69static void		extract_currdev(void);
70static int		isa_inb(int port);
71static void		isa_outb(int port, int value);
72void			exit(int code);
73#ifdef LOADER_GELI_SUPPORT
74#include "geliboot.h"
75struct geli_boot_args	*gargs;
76struct geli_boot_data	*gbdata;
77#endif
78#ifdef LOADER_ZFS_SUPPORT
79struct zfs_boot_args	*zargs;
80static void		i386_zfs_probe(void);
81#endif
82
83/* XXX debugging */
84extern char end[];
85
86static void *heap_top;
87static void *heap_bottom;
88
89int
90main(void)
91{
92    int			i;
93
94    /* Pick up arguments */
95    kargs = (void *)__args;
96    initial_howto = kargs->howto;
97    initial_bootdev = kargs->bootdev;
98    initial_bootinfo = kargs->bootinfo ? (struct bootinfo *)PTOV(kargs->bootinfo) : NULL;
99
100    /* Initialize the v86 register set to a known-good state. */
101    bzero(&v86, sizeof(v86));
102    v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
103
104    /*
105     * Initialise the heap as early as possible.  Once this is done, malloc() is usable.
106     */
107    bios_getmem();
108
109#if defined(LOADER_BZIP2_SUPPORT) || defined(LOADER_FIREWIRE_SUPPORT) || \
110    defined(LOADER_GPT_SUPPORT) || defined(LOADER_ZFS_SUPPORT)
111    if (high_heap_size > 0) {
112	heap_top = PTOV(high_heap_base + high_heap_size);
113	heap_bottom = PTOV(high_heap_base);
114	if (high_heap_base < memtop_copyin)
115	    memtop_copyin = high_heap_base;
116    } else
117#endif
118    {
119	heap_top = (void *)PTOV(bios_basemem);
120	heap_bottom = (void *)end;
121    }
122    setheap(heap_bottom, heap_top);
123
124    /*
125     * XXX Chicken-and-egg problem; we want to have console output early, but some
126     * console attributes may depend on reading from eg. the boot device, which we
127     * can't do yet.
128     *
129     * We can use printf() etc. once this is done.
130     * If the previous boot stage has requested a serial console, prefer that.
131     */
132    bi_setboothowto(initial_howto);
133    if (initial_howto & RB_MULTIPLE) {
134	if (initial_howto & RB_SERIAL)
135	    setenv("console", "comconsole vidconsole", 1);
136	else
137	    setenv("console", "vidconsole comconsole", 1);
138    } else if (initial_howto & RB_SERIAL)
139	setenv("console", "comconsole", 1);
140    else if (initial_howto & RB_MUTE)
141	setenv("console", "nullconsole", 1);
142    cons_probe();
143
144    /*
145     * Initialise the block cache. Set the upper limit.
146     */
147    bcache_init(32768, 512);
148
149    /*
150     * Special handling for PXE and CD booting.
151     */
152    if (kargs->bootinfo == 0) {
153	/*
154	 * We only want the PXE disk to try to init itself in the below
155	 * walk through devsw if we actually booted off of PXE.
156	 */
157	if (kargs->bootflags & KARGS_FLAGS_PXE)
158	    pxe_enable(kargs->pxeinfo ? PTOV(kargs->pxeinfo) : NULL);
159	else if (kargs->bootflags & KARGS_FLAGS_CD)
160	    bc_add(initial_bootdev);
161    }
162
163    archsw.arch_autoload = i386_autoload;
164    archsw.arch_getdev = i386_getdev;
165    archsw.arch_copyin = i386_copyin;
166    archsw.arch_copyout = i386_copyout;
167    archsw.arch_readin = i386_readin;
168    archsw.arch_isainb = isa_inb;
169    archsw.arch_isaoutb = isa_outb;
170    archsw.arch_hypervisor = x86_hypervisor;
171#ifdef LOADER_ZFS_SUPPORT
172    archsw.arch_zfs_probe = i386_zfs_probe;
173
174    /*
175     * zfsboot and gptzfsboot have always passed KARGS_FLAGS_ZFS, so if that is
176     * set along with KARGS_FLAGS_EXTARG we know we can interpret the extarg
177     * data as a struct zfs_boot_args.
178     */
179#define	KARGS_EXTARGS_ZFS	(KARGS_FLAGS_EXTARG | KARGS_FLAGS_ZFS)
180
181    if ((kargs->bootflags & KARGS_EXTARGS_ZFS) == KARGS_EXTARGS_ZFS) {
182	zargs = (struct zfs_boot_args *)(kargs + 1);
183    }
184#endif /* LOADER_ZFS_SUPPORT */
185
186#ifdef LOADER_GELI_SUPPORT
187    /*
188     * If we decided earlier that we have zfs_boot_args extarg data, and it is
189     * big enough to contain the embedded geli data (the early zfs_boot_args
190     * structs weren't), then init the gbdata pointer accordingly. If there is
191     * extarg data which isn't zfs_boot_args data, determine whether it is
192     * geli_boot_args data.  Recent versions of gptboot set KARGS_FLAGS_GELI to
193     * indicate that.  Earlier versions didn't, but we presume that's what we
194     * have if the extarg size exactly matches the size of the geli_boot_args
195     * struct during that pre-flag era.
196     */
197#define	LEGACY_GELI_ARGS_SIZE	260	/* This can never change */
198
199#ifdef LOADER_ZFS_SUPPORT
200    if (zargs != NULL) {
201	if (zargs->size > offsetof(struct zfs_boot_args, gelidata)) {
202	    gbdata = &zargs->gelidata;
203	}
204    } else
205#endif /* LOADER_ZFS_SUPPORT */
206	   if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) {
207	gargs = (struct geli_boot_args *)(kargs + 1);
208	if ((kargs->bootflags & KARGS_FLAGS_GELI) ||
209	    gargs->size == LEGACY_GELI_ARGS_SIZE) {
210	    gbdata = &gargs->gelidata;
211	}
212    }
213
214    if (gbdata != NULL)
215	import_geli_boot_data(gbdata);
216#endif /* LOADER_GELI_SUPPORT */
217
218    /*
219     * March through the device switch probing for things.
220     */
221    for (i = 0; devsw[i] != NULL; i++)
222	if (devsw[i]->dv_init != NULL)
223	    (devsw[i]->dv_init)();
224    printf("BIOS %dkB/%dkB available memory\n", bios_basemem / 1024, bios_extmem / 1024);
225    if (initial_bootinfo != NULL) {
226	initial_bootinfo->bi_basemem = bios_basemem / 1024;
227	initial_bootinfo->bi_extmem = bios_extmem / 1024;
228    }
229
230    /* detect ACPI for future reference */
231    biosacpi_detect();
232
233    /* detect SMBIOS for future reference */
234    smbios_detect(NULL);
235
236    /* detect PCI BIOS for future reference */
237    biospci_detect();
238
239    printf("\n%s", bootprog_info);
240
241    extract_currdev();				/* set $currdev and $loaddev */
242    setenv("LINES", "24", 1);			/* optional */
243
244    bios_getsmap();
245
246    interact();
247
248    /* if we ever get here, it is an error */
249    return (1);
250}
251
252/*
253 * Set the 'current device' by (if possible) recovering the boot device as
254 * supplied by the initial bootstrap.
255 *
256 * XXX should be extended for netbooting.
257 */
258static void
259extract_currdev(void)
260{
261    struct i386_devdesc		new_currdev;
262#ifdef LOADER_ZFS_SUPPORT
263    char			buf[20];
264#endif
265    int				biosdev = -1;
266
267    /* Assume we are booting from a BIOS disk by default */
268    new_currdev.dd.d_dev = &bioshd;
269
270    /* new-style boot loaders such as pxeldr and cdldr */
271    if (kargs->bootinfo == 0) {
272        if ((kargs->bootflags & KARGS_FLAGS_CD) != 0) {
273	    /* we are booting from a CD with cdboot */
274	    new_currdev.dd.d_dev = &bioscd;
275	    new_currdev.dd.d_unit = bd_bios2unit(initial_bootdev);
276	} else if ((kargs->bootflags & KARGS_FLAGS_PXE) != 0) {
277	    /* we are booting from pxeldr */
278	    new_currdev.dd.d_dev = &pxedisk;
279	    new_currdev.dd.d_unit = 0;
280	} else {
281	    /* we don't know what our boot device is */
282	    new_currdev.d_kind.biosdisk.slice = -1;
283	    new_currdev.d_kind.biosdisk.partition = 0;
284	    biosdev = -1;
285	}
286#ifdef LOADER_ZFS_SUPPORT
287    } else if ((kargs->bootflags & KARGS_FLAGS_ZFS) != 0) {
288	/* zargs was set in main() if we have new style extended argument */
289	if (zargs != NULL &&
290	    zargs->size >= offsetof(struct zfs_boot_args, primary_pool)) {
291	    /* sufficient data is provided */
292	    new_currdev.d_kind.zfs.pool_guid = zargs->pool;
293	    new_currdev.d_kind.zfs.root_guid = zargs->root;
294	    if (zargs->size >= sizeof(*zargs) && zargs->primary_vdev != 0) {
295		sprintf(buf, "%llu", zargs->primary_pool);
296		setenv("vfs.zfs.boot.primary_pool", buf, 1);
297		sprintf(buf, "%llu", zargs->primary_vdev);
298		setenv("vfs.zfs.boot.primary_vdev", buf, 1);
299	    }
300	} else {
301	    /* old style zfsboot block */
302	    new_currdev.d_kind.zfs.pool_guid = kargs->zfspool;
303	    new_currdev.d_kind.zfs.root_guid = 0;
304	}
305	new_currdev.dd.d_dev = &zfs_dev;
306#endif
307    } else if ((initial_bootdev & B_MAGICMASK) != B_DEVMAGIC) {
308	/* The passed-in boot device is bad */
309	new_currdev.d_kind.biosdisk.slice = -1;
310	new_currdev.d_kind.biosdisk.partition = 0;
311	biosdev = -1;
312    } else {
313	new_currdev.d_kind.biosdisk.slice = B_SLICE(initial_bootdev) - 1;
314	new_currdev.d_kind.biosdisk.partition = B_PARTITION(initial_bootdev);
315	biosdev = initial_bootinfo->bi_bios_dev;
316
317	/*
318	 * If we are booted by an old bootstrap, we have to guess at the BIOS
319	 * unit number.  We will lose if there is more than one disk type
320	 * and we are not booting from the lowest-numbered disk type
321	 * (ie. SCSI when IDE also exists).
322	 */
323	if ((biosdev == 0) && (B_TYPE(initial_bootdev) != 2))	/* biosdev doesn't match major */
324	    biosdev = 0x80 + B_UNIT(initial_bootdev);		/* assume harddisk */
325    }
326
327    /*
328     * If we are booting off of a BIOS disk and we didn't succeed in determining
329     * which one we booted off of, just use disk0: as a reasonable default.
330     */
331    if ((new_currdev.dd.d_dev->dv_type == bioshd.dv_type) &&
332	((new_currdev.dd.d_unit = bd_bios2unit(biosdev)) == -1)) {
333	printf("Can't work out which disk we are booting from.\n"
334	       "Guessed BIOS device 0x%x not found by probes, defaulting to disk0:\n", biosdev);
335	new_currdev.dd.d_unit = 0;
336    }
337
338#ifdef LOADER_ZFS_SUPPORT
339    if (new_currdev.dd.d_dev->dv_type == DEVT_ZFS)
340	init_zfs_bootenv(zfs_fmtdev(&new_currdev));
341#endif
342
343    env_setenv("currdev", EV_VOLATILE, i386_fmtdev(&new_currdev),
344	       i386_setcurrdev, env_nounset);
345    env_setenv("loaddev", EV_VOLATILE, i386_fmtdev(&new_currdev), env_noset,
346	       env_nounset);
347}
348
349COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
350
351static int
352command_reboot(int argc, char *argv[])
353{
354    int i;
355
356    for (i = 0; devsw[i] != NULL; ++i)
357	if (devsw[i]->dv_cleanup != NULL)
358	    (devsw[i]->dv_cleanup)();
359
360    printf("Rebooting...\n");
361    delay(1000000);
362    __exit(0);
363}
364
365/* provide this for panic, as it's not in the startup code */
366void
367exit(int code)
368{
369    __exit(code);
370}
371
372COMMAND_SET(heap, "heap", "show heap usage", command_heap);
373
374static int
375command_heap(int argc, char *argv[])
376{
377    mallocstats();
378    printf("heap base at %p, top at %p, upper limit at %p\n", heap_bottom,
379      sbrk(0), heap_top);
380    return(CMD_OK);
381}
382
383/* ISA bus access functions for PnP. */
384static int
385isa_inb(int port)
386{
387
388    return (inb(port));
389}
390
391static void
392isa_outb(int port, int value)
393{
394
395    outb(port, value);
396}
397
398#ifdef LOADER_ZFS_SUPPORT
399static void
400i386_zfs_probe(void)
401{
402    char devname[32];
403    struct i386_devdesc dev;
404
405    /*
406     * Open all the disks we can find and see if we can reconstruct
407     * ZFS pools from them.
408     */
409    dev.dd.d_dev = &bioshd;
410    for (dev.dd.d_unit = 0; bd_unit2bios(&dev) >= 0; dev.dd.d_unit++) {
411	snprintf(devname, sizeof(devname), "%s%d:", bioshd.dv_name,
412	    dev.dd.d_unit);
413	zfs_probe_dev(devname, NULL);
414    }
415}
416#endif
417