More syslink messaging work. Now basically done except for the I/O 'DMA'
[dragonfly.git] / sys / boot / pc32 / boot2 / boot2.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * Copyright (c) 1998 Robert Nordier
35 * All rights reserved.
36 *
37 * Redistribution and use in source and binary forms are freely
38 * permitted provided that the above copyright notice and this
39 * paragraph and the following disclaimer are duplicated in all
40 * such forms.
41 *
42 * This software is provided "AS IS" and without any express or
43 * implied warranties, including, without limitation, the implied
44 * warranties of merchantability and fitness for a particular
45 * purpose.
46 *
47 * $FreeBSD: src/sys/boot/i386/boot2/boot2.c,v 1.64 2003/08/25 23:28:31 obrien Exp $
48 * $DragonFly: src/sys/boot/pc32/boot2/boot2.c,v 1.14 2005/05/25 06:22:22 y0netan1 Exp $
49 */
50#include <sys/param.h>
51#include <sys/disklabel.h>
52#include <sys/diskslice.h>
53#include <sys/diskmbr.h>
54#include <sys/dirent.h>
55#include <machine/bootinfo.h>
56#include <machine/elf.h>
57
58#include <stdarg.h>
59
60#include <a.out.h>
61
62#include <btxv86.h>
63
64#include "boot2.h"
65#include "lib.h"
66#include "../bootasm.h"
67
68#define SECOND 18 /* Circa that many ticks in a second. */
69
70#define RBX_ASKNAME 0x0 /* -a */
71#define RBX_SINGLE 0x1 /* -s */
72#define RBX_DFLTROOT 0x5 /* -r */
73#define RBX_KDB 0x6 /* -d */
74#define RBX_CONFIG 0xa /* -c */
75#define RBX_VERBOSE 0xb /* -v */
76#define RBX_SERIAL 0xc /* -h */
77#define RBX_CDROM 0xd /* -C */
78#define RBX_GDB 0xf /* -g */
79#define RBX_MUTE 0x10 /* -m */
80#define RBX_PAUSE 0x12 /* -p */
81#define RBX_NOINTR 0x1c /* -n */
82#define RBX_VIDEO 0x1d /* -V */
83#define RBX_PROBEKBD 0x1e /* -P */
84/* 0x1f is reserved for the historical RB_BOOTINFO option */
85
86#define RBF_MUTE (1 << RBX_MUTE)
87#define RBF_SERIAL (1 << RBX_SERIAL)
88#define RBF_VIDEO (1 << RBX_VIDEO)
89
90/* pass: -a, -s, -r, -d, -c, -v, -h, -C, -g, -m, -p, -V */
91#define RBX_MASK 0x2005ffff
92
93#define PATH_CONFIG "/boot.config"
94#define PATH_BOOT3 "/boot/loader"
95#define PATH_KERNEL "/kernel"
96
97#define NDEV 3
98#define MEM_BASE 0x12
99#define MEM_EXT 0x15
100#define V86_CY(x) ((x) & 1)
101#define V86_ZR(x) ((x) & 0x40)
102
103#define DRV_HARD 0x80
104#define DRV_MASK 0x7f
105
106#define TYPE_AD 0
107#define TYPE_DA 1
108#define TYPE_MAXHARD TYPE_DA
109#define TYPE_FD 2
110
111#define NOPT 12
112
113#define INVALID_S "Bad %s\n"
114
115extern uint32_t _end;
116
117static const char optstr[NOPT] = { "VhaCgmnPprsv" };
118static const unsigned char flags[NOPT] = {
119 RBX_VIDEO,
120 RBX_SERIAL,
121 RBX_ASKNAME,
122 RBX_CDROM,
123 RBX_GDB,
124 RBX_MUTE,
125 RBX_NOINTR,
126 RBX_PROBEKBD,
127 RBX_PAUSE,
128 RBX_DFLTROOT,
129 RBX_SINGLE,
130 RBX_VERBOSE
131};
132
133static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
134static const unsigned char dev_maj[NDEV] = {30, 4, 2};
135
136static struct dsk {
137 unsigned drive;
138 unsigned type;
139 unsigned unit;
140 unsigned slice;
141 unsigned part;
142 unsigned start;
143 int init;
144} dsk;
145static char cmd[512];
146static char kname[1024];
147static uint32_t opts;
148static struct bootinfo bootinfo;
149
150void exit(int);
151static void load(void);
152static int parse(void);
153static int xfsread(ino_t, void *, size_t);
154static int dskread(void *, unsigned, unsigned);
155static void printf(const char *,...);
156static void putchar(int);
157static uint32_t memsize(void);
158static int drvread(void *, unsigned, unsigned);
159static int keyhit(unsigned);
160static void xputc(int);
161static int xgetc(int);
162static int getc(int);
163
164static void
165memcpy(void *d, const void *s, int len)
166{
167 char *dd = d;
168 const char *ss = s;
169
170#if 0
171 if (dd < ss) {
172 while (--len >= 0)
173 *dd++ = *ss++;
174 } else {
175#endif
176 while (--len >= 0)
177 dd[len] = ss[len];
178#if 0
179 }
180#endif
181}
182
183static inline int
184strcmp(const char *s1, const char *s2)
185{
186 for (; *s1 == *s2 && *s1; s1++, s2++);
187 return (unsigned char)*s1 - (unsigned char)*s2;
188}
189
190#include "ufsread.c"
191
192static int
193xfsread(ino_t inode, void *buf, size_t nbyte)
194{
195 if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
196 printf(INVALID_S, "format");
197 return -1;
198 }
199 return 0;
200}
201
202static inline uint32_t
203memsize(void)
204{
205 v86.addr = MEM_EXT;
206 v86.eax = 0x8800;
207 v86int();
208 return v86.eax;
209}
210
211static inline void
212getstr(void)
213{
214 char *s;
215 int c;
216
217 s = cmd;
218 for (;;) {
219 switch (c = xgetc(0)) {
220 case 0:
221 break;
222 case '\177':
223 case '\b':
224 if (s > cmd) {
225 s--;
226 printf("\b \b");
227 }
228 break;
229 case '\n':
230 case '\r':
231 *s = 0;
232 return;
233 default:
234 if (s - cmd < sizeof(cmd) - 1)
235 *s++ = c;
236 putchar(c);
237 }
238 }
239}
240
241static inline void
242putc(int c)
243{
244 v86.addr = 0x10;
245 v86.eax = 0xe00 | (c & 0xff);
246 v86.ebx = 0x7;
247 v86int();
248}
249
250int
251main(void)
252{
253 int autoboot;
254 ino_t ino;
255
256 dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
257 v86.ctl = V86_FLAGS;
258 dsk.drive = *(uint8_t *)PTOV(MEM_BTX_USR_ARG);
259 dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
260 dsk.unit = dsk.drive & DRV_MASK;
261 dsk.slice = *(uint8_t *)PTOV(MEM_BTX_USR_ARG + 1) + 1;
262 bootinfo.bi_version = BOOTINFO_VERSION;
263 bootinfo.bi_size = sizeof(bootinfo);
264 bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */
265 bootinfo.bi_extmem = memsize();
266 bootinfo.bi_memsizes_valid++;
267
268 /* Process configuration file */
269
270 autoboot = 1;
271
272 if ((ino = lookup(PATH_CONFIG)))
273 fsread(ino, cmd, sizeof(cmd));
274
275 if (cmd[0]) {
276 printf("%s: %s", PATH_CONFIG, cmd);
277 if (parse())
278 autoboot = 0;
279 /* Do not process this command twice */
280 *cmd = 0;
281 }
282
283 /*
284 * Setup our (serial) console after processing the config file. If
285 * the initialization fails, don't try to use the serial port. This
286 * can happen if the serial port is unmaped (happens on new laptops a lot).
287 */
288 if ((opts & (RBF_MUTE|RBF_SERIAL|RBF_VIDEO)) == 0)
289 opts |= RBF_SERIAL|RBF_VIDEO;
290 if (opts & RBF_SERIAL) {
291 if (sio_init())
292 opts = RBF_VIDEO;
293 }
294
295
296 /*
297 * Try to exec stage 3 boot loader. If interrupted by a keypress,
298 * or in case of failure, try to load a kernel directly instead.
299 */
300
301 if (autoboot && !*kname) {
302 memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3));
303 if (!keyhit(3*SECOND)) {
304 load();
305 memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
306 }
307 }
308
309 /* Present the user with the boot2 prompt. */
310
311 for (;;) {
312 printf("\nDragonFly boot\n"
313 "%u:%s(%u,%c)%s: ",
314 dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
315 'a' + dsk.part, kname);
316 if (!autoboot || keyhit(5*SECOND))
317 getstr();
318 else
319 putchar('\n');
320 autoboot = 0;
321 if (parse())
322 putchar('\a');
323 else
324 load();
325 }
326}
327
328/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
329void
330exit(int x)
331{
332}
333
334static void
335load(void)
336{
337 union {
338 struct exec ex;
339 Elf32_Ehdr eh;
340 } hdr;
341 Elf32_Phdr ep[2];
342 Elf32_Shdr es[2];
343 caddr_t p;
344 ino_t ino;
345 uint32_t addr, x;
346 int fmt, i, j;
347
348 if (!(ino = lookup(kname))) {
349 if (!ls)
350 printf("No %s\n", kname);
351 return;
352 }
353 if (xfsread(ino, &hdr, sizeof(hdr)))
354 return;
355 if (N_GETMAGIC(hdr.ex) == ZMAGIC)
356 fmt = 0;
357 else if (IS_ELF(hdr.eh))
358 fmt = 1;
359 else {
360 printf(INVALID_S, "format");
361 return;
362 }
363 if (fmt == 0) {
364 addr = hdr.ex.a_entry & 0xffffff;
365 p = PTOV(addr);
366 fs_off = PAGE_SIZE;
367 if (xfsread(ino, p, hdr.ex.a_text))
368 return;
369 p += roundup2(hdr.ex.a_text, PAGE_SIZE);
370 if (xfsread(ino, p, hdr.ex.a_data))
371 return;
372 p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
373 bootinfo.bi_symtab = VTOP(p);
374 memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
375 p += sizeof(hdr.ex.a_syms);
376 if (hdr.ex.a_syms) {
377 if (xfsread(ino, p, hdr.ex.a_syms))
378 return;
379 p += hdr.ex.a_syms;
380 if (xfsread(ino, p, sizeof(int)))
381 return;
382 x = *(uint32_t *)p;
383 p += sizeof(int);
384 x -= sizeof(int);
385 if (xfsread(ino, p, x))
386 return;
387 p += x;
388 }
389 } else {
390 fs_off = hdr.eh.e_phoff;
391 for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
392 if (xfsread(ino, ep + j, sizeof(ep[0])))
393 return;
394 if (ep[j].p_type == PT_LOAD)
395 j++;
396 }
397 for (i = 0; i < 2; i++) {
398 p = PTOV(ep[i].p_paddr & 0xffffff);
399 fs_off = ep[i].p_offset;
400 if (xfsread(ino, p, ep[i].p_filesz))
401 return;
402 }
403 p += roundup2(ep[1].p_memsz, PAGE_SIZE);
404 bootinfo.bi_symtab = VTOP(p);
405 if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
406 fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
407 (hdr.eh.e_shstrndx + 1);
408 if (xfsread(ino, &es, sizeof(es)))
409 return;
410 for (i = 0; i < 2; i++) {
411 memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size));
412 p += sizeof(es[i].sh_size);
413 fs_off = es[i].sh_offset;
414 if (xfsread(ino, p, es[i].sh_size))
415 return;
416 p += es[i].sh_size;
417 }
418 }
419 addr = hdr.eh.e_entry & 0xffffff;
420 }
421 bootinfo.bi_esymtab = VTOP(p);
422 bootinfo.bi_kernelname = VTOP(kname);
423 bootinfo.bi_bios_dev = dsk.drive;
424 __exec((caddr_t)addr, opts & RBX_MASK,
425 MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part),
426 0, 0, 0, VTOP(&bootinfo));
427}
428
429static int
430parse()
431{
432 char *arg = cmd;
433 char *p, *q;
434 unsigned int drv;
435 int c, i;
436
437 while ((c = *arg++)) {
438 if (c == ' ' || c == '\t' || c == '\n')
439 continue;
440 for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++)
441 ;
442 if (*p)
443 *p++ = 0;
444 if (c == '-') {
445 while ((c = *arg++)) {
446 for (i = NOPT - 1; i >= 0; --i) {
447 if (optstr[i] == c) {
448 opts ^= 1 << flags[i];
449 goto ok;
450 }
451 }
452 return(-1);
453 ok: ; /* ugly but save space */
454 }
455 if (opts & (1 << RBX_PROBEKBD)) {
456 i = *(uint8_t *)PTOV(0x496) & 0x10;
457 if (!i) {
458 printf("NO KB\n");
459 opts |= RBF_VIDEO | RBF_SERIAL;
460 }
461 opts &= ~(1 << RBX_PROBEKBD);
462 }
463 } else {
464 for (q = arg--; *q && *q != '('; q++);
465 if (*q) {
466 drv = -1;
467 if (arg[1] == ':') {
468 drv = *arg - '0';
469 if (drv > 9)
470 return (-1);
471 arg += 2;
472 }
473 if (q - arg != 2)
474 return -1;
475 for (i = 0; arg[0] != dev_nm[i][0] ||
476 arg[1] != dev_nm[i][1]; i++)
477 if (i == NDEV - 1)
478 return -1;
479 dsk.type = i;
480 arg += 3;
481 dsk.unit = *arg - '0';
482 if (arg[1] != ',' || dsk.unit > 9)
483 return -1;
484 arg += 2;
485 dsk.slice = WHOLE_DISK_SLICE;
486 if (arg[1] == ',') {
487 dsk.slice = *arg - '0' + 1;
488 if (dsk.slice > NDOSPART)
489 return -1;
490 arg += 2;
491 }
492 if (arg[1] != ')')
493 return -1;
494 dsk.part = *arg - 'a';
495 if (dsk.part > 7)
496 return (-1);
497 arg += 2;
498 if (drv == -1)
499 drv = dsk.unit;
500 dsk.drive = (dsk.type <= TYPE_MAXHARD
501 ? DRV_HARD : 0) + drv;
502 dsk_meta = 0;
503 }
504 if ((i = p - arg - !*(p - 1))) {
505 if ((size_t)i >= sizeof(kname))
506 return -1;
507 memcpy(kname, arg, i + 1);
508 }
509 }
510 arg = p;
511 }
512 return 0;
513}
514
515static int
516dskread(void *buf, unsigned lba, unsigned nblk)
517{
518 struct dos_partition *dp;
519 struct disklabel *d;
520 char *sec;
521 unsigned sl, i;
522
523 if (!dsk_meta) {
524 sec = dmadat->secbuf;
525 dsk.start = 0;
526 if (drvread(sec, DOSBBSECTOR, 1))
527 return -1;
528 dp = (void *)(sec + DOSPARTOFF);
529 sl = dsk.slice;
530 if (sl < BASE_SLICE) {
531 for (i = 0; i < NDOSPART; i++)
532 if (dp[i].dp_typ == DOSPTYP_386BSD &&
533 (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
534 sl = BASE_SLICE + i;
535 if (dp[i].dp_flag & 0x80 ||
536 dsk.slice == COMPATIBILITY_SLICE)
537 break;
538 }
539 if (dsk.slice == WHOLE_DISK_SLICE)
540 dsk.slice = sl;
541 }
542 if (sl != WHOLE_DISK_SLICE) {
543 if (sl != COMPATIBILITY_SLICE)
544 dp += sl - BASE_SLICE;
545 if (dp->dp_typ != DOSPTYP_386BSD) {
546 printf(INVALID_S, "slice");
547 return -1;
548 }
549 dsk.start = dp->dp_start;
550 }
551 if (drvread(sec, dsk.start + LABELSECTOR, 1))
552 return -1;
553 d = (void *)(sec + LABELOFFSET);
554 if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
555 if (dsk.part != RAW_PART) {
556 printf(INVALID_S, "label");
557 return -1;
558 }
559 } else {
560 if (!dsk.init) {
561 if (d->d_type == DTYPE_SCSI)
562 dsk.type = TYPE_DA;
563 dsk.init++;
564 }
565 if (dsk.part >= d->d_npartitions ||
566 !d->d_partitions[dsk.part].p_size) {
567 printf(INVALID_S, "partition");
568 return -1;
569 }
570 dsk.start += d->d_partitions[dsk.part].p_offset;
571 dsk.start -= d->d_partitions[RAW_PART].p_offset;
572 }
573 }
574 return drvread(buf, dsk.start + lba, nblk);
575}
576
577static void
578printf(const char *fmt,...)
579{
580 va_list ap;
581 char buf[10];
582 char *s;
583 unsigned u;
584 int c;
585
586 va_start(ap, fmt);
587 while ((c = *fmt++)) {
588 if (c == '%') {
589 c = *fmt++;
590 switch (c) {
591 case 'c':
592 putchar(va_arg(ap, int));
593 continue;
594 case 's':
595 for (s = va_arg(ap, char *); *s; s++)
596 putchar(*s);
597 continue;
598 case 'u':
599 u = va_arg(ap, unsigned);
600 s = buf;
601 do
602 *s++ = '0' + u % 10U;
603 while (u /= 10U);
604 while (--s >= buf)
605 putchar(*s);
606 continue;
607 }
608 }
609 putchar(c);
610 }
611 va_end(ap);
612 return;
613}
614
615static void
616putchar(int c)
617{
618 if (c == '\n')
619 xputc('\r');
620 xputc(c);
621}
622
623static int
624drvread(void *buf, unsigned lba, unsigned nblk)
625{
626 static unsigned c = 0x2d5c7c2f; /* twiddle */
627
628 c = (c << 8) | (c >> 24);
629 xputc(c);
630 xputc('\b');
631 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
632 v86.addr = XREADORG; /* call to xread in boot1 */
633 v86.es = VTOPSEG(buf);
634 v86.eax = lba;
635 v86.ebx = VTOPOFF(buf);
636 v86.ecx = lba >> 16;
637 v86.edx = nblk << 8 | dsk.drive;
638 v86int();
639 v86.ctl = V86_FLAGS;
640 if (V86_CY(v86.efl)) {
641 printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
642 return -1;
643 }
644 return 0;
645}
646
647static int
648keyhit(unsigned ticks)
649{
650 uint32_t t0, t1;
651
652 if (opts & 1 << RBX_NOINTR)
653 return 0;
654 t0 = 0;
655 for (;;) {
656 if (xgetc(1))
657 return 1;
658 t1 = *(uint32_t *)PTOV(0x46c);
659 if (!t0)
660 t0 = t1;
661 if (t1 < t0 || t1 >= t0 + ticks)
662 return 0;
663 }
664}
665
666static void
667xputc(int c)
668{
669 if (opts & RBF_VIDEO)
670 putc(c);
671 if (opts & RBF_SERIAL)
672 sio_putc(c);
673}
674
675static int
676xgetc(int fn)
677{
678 if (opts & 1 << RBX_NOINTR)
679 return 0;
680 for (;;) {
681 if ((opts & RBF_VIDEO) && getc(1))
682 return fn ? 1 : getc(0);
683 if ((opts & RBF_SERIAL) && sio_ischar())
684 return fn ? 1 : sio_getc();
685 if (fn)
686 return 0;
687 }
688}
689
690static int
691getc(int fn)
692{
693 v86.addr = 0x16;
694 v86.eax = fn << 8;
695 v86int();
696 return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl);
697}