FreeBSD/Linux Kernel Cross Reference
sys/boot/boot.c
1 /* boot.c - Load and start Minix. Author: Kees J. Bot
2 * 27 Dec 1991
3 */
4
5 char version[]= "2.20";
6
7 #define BIOS (!UNIX) /* Either uses BIOS or UNIX syscalls. */
8
9 #define nil 0
10 #define _POSIX_SOURCE 1
11 #define _MINIX 1
12 #include <stddef.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <limits.h>
18 #include <string.h>
19 #include <errno.h>
20 #include <ibm/partition.h>
21 #include <minix/config.h>
22 #include <minix/type.h>
23 #include <minix/com.h>
24 #include <minix/dmap.h>
25 #include <minix/const.h>
26 #include <minix/minlib.h>
27 #include <minix/syslib.h>
28 #if BIOS
29 #include <kernel/const.h>
30 #include <kernel/type.h>
31 #endif
32 #if UNIX
33 #include <stdio.h>
34 #include <time.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <signal.h>
38 #include <termios.h>
39 #endif
40 #include "rawfs.h"
41 #undef EXTERN
42 #define EXTERN /* Empty */
43 #include "boot.h"
44
45 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
46 #define arraylimit(a) ((a) + arraysize(a))
47 #define between(a, c, z) ((unsigned) ((c) - (a)) <= ((z) - (a)))
48
49 int fsok= -1; /* File system state. Initially unknown. */
50
51 static int block_size;
52
53 #if BIOS
54
55 /* this data is reserved for BIOS int 0x13 to put the 'specification packet'
56 * in. It has a structure of course, but we don't define a struct because
57 * of compiler padding. We fiddle out the bytes ourselves later.
58 */
59 unsigned char boot_spec[24];
60
61 char *bios_err(int err)
62 /* Translate BIOS error code to a readable string. (This is a rare trait
63 * known as error checking and reporting. Take a good look at it, you won't
64 * see it often.)
65 */
66 {
67 static struct errlist {
68 int err;
69 char *what;
70 } errlist[] = {
71 #if !DOS
72 { 0x00, "No error" },
73 { 0x01, "Invalid command" },
74 { 0x02, "Address mark not found" },
75 { 0x03, "Disk write-protected" },
76 { 0x04, "Sector not found" },
77 { 0x05, "Reset failed" },
78 { 0x06, "Floppy disk removed" },
79 { 0x07, "Bad parameter table" },
80 { 0x08, "DMA overrun" },
81 { 0x09, "DMA crossed 64 KB boundary" },
82 { 0x0A, "Bad sector flag" },
83 { 0x0B, "Bad track flag" },
84 { 0x0C, "Media type not found" },
85 { 0x0D, "Invalid number of sectors on format" },
86 { 0x0E, "Control data address mark detected" },
87 { 0x0F, "DMA arbitration level out of range" },
88 { 0x10, "Uncorrectable CRC or ECC data error" },
89 { 0x11, "ECC corrected data error" },
90 { 0x20, "Controller failed" },
91 { 0x40, "Seek failed" },
92 { 0x80, "Disk timed-out" },
93 { 0xAA, "Drive not ready" },
94 { 0xBB, "Undefined error" },
95 { 0xCC, "Write fault" },
96 { 0xE0, "Status register error" },
97 { 0xFF, "Sense operation failed" }
98 #else /* DOS */
99 { 0x00, "No error" },
100 { 0x01, "Function number invalid" },
101 { 0x02, "File not found" },
102 { 0x03, "Path not found" },
103 { 0x04, "Too many open files" },
104 { 0x05, "Access denied" },
105 { 0x06, "Invalid handle" },
106 { 0x0C, "Access code invalid" },
107 #endif /* DOS */
108 };
109 struct errlist *errp;
110
111 for (errp= errlist; errp < arraylimit(errlist); errp++) {
112 if (errp->err == err) return errp->what;
113 }
114 return "Unknown error";
115 }
116
117 char *unix_err(int err)
118 /* Translate the few errors rawfs can give. */
119 {
120 switch (err) {
121 case ENOENT: return "No such file or directory";
122 case ENOTDIR: return "Not a directory";
123 default: return "Unknown error";
124 }
125 }
126
127 void rwerr(char *rw, off_t sec, int err)
128 {
129 printf("\n%s error 0x%02x (%s) at sector %ld absolute\n",
130 rw, err, bios_err(err), sec);
131 }
132
133 void readerr(off_t sec, int err) { rwerr("Read", sec, err); }
134 void writerr(off_t sec, int err) { rwerr("Write", sec, err); }
135
136 void readblock(off_t blk, char *buf, int block_size)
137 /* Read blocks for the rawfs package. */
138 {
139 int r;
140 u32_t sec= lowsec + blk * RATIO(block_size);
141
142 if(!block_size) {
143 printf("block_size 0\n");
144 exit(1);
145 }
146
147 if ((r= readsectors(mon2abs(buf), sec, 1 * RATIO(block_size))) != 0) {
148 readerr(sec, r); exit(1);
149 }
150 }
151
152 #define istty (1)
153 #define alarm(n) (0)
154
155 #endif /* BIOS */
156
157 #if UNIX
158
159 /* The Minix boot block must start with these bytes: */
160 char boot_magic[] = { 0x31, 0xC0, 0x8E, 0xD8, 0xFA, 0x8E, 0xD0, 0xBC };
161
162 struct biosdev {
163 char *name; /* Name of device. */
164 int device; /* Device to edit parameters. */
165 } bootdev;
166
167 struct termios termbuf;
168 int istty;
169
170 void quit(int status)
171 {
172 if (istty) (void) tcsetattr(0, TCSANOW, &termbuf);
173 exit(status);
174 }
175
176 #define exit(s) quit(s)
177
178 void report(char *label)
179 /* edparams: label: No such file or directory */
180 {
181 fprintf(stderr, "edparams: %s: %s\n", label, strerror(errno));
182 }
183
184 void fatal(char *label)
185 {
186 report(label);
187 exit(1);
188 }
189
190 void *alloc(void *m, size_t n)
191 {
192 m= m == nil ? malloc(n) : realloc(m, n);
193 if (m == nil) fatal("");
194 return m;
195 }
196
197 #define malloc(n) alloc(nil, n)
198 #define realloc(m, n) alloc(m, n)
199
200 #define mon2abs(addr) ((void *) (addr))
201
202 int rwsectors(int rw, void *addr, u32_t sec, int nsec)
203 {
204 ssize_t r;
205 size_t len= nsec * SECTOR_SIZE;
206
207 if (lseek(bootdev.device, sec * SECTOR_SIZE, SEEK_SET) == -1)
208 return errno;
209
210 if (rw == 0) {
211 r= read(bootdev.device, (char *) addr, len);
212 } else {
213 r= write(bootdev.device, (char *) addr, len);
214 }
215 if (r == -1) return errno;
216 if (r != len) return EIO;
217 return 0;
218 }
219
220 #define readsectors(a, s, n) rwsectors(0, (a), (s), (n))
221 #define writesectors(a, s, n) rwsectors(1, (a), (s), (n))
222 #define readerr(sec, err) (errno= (err), report(bootdev.name))
223 #define writerr(sec, err) (errno= (err), report(bootdev.name))
224 #define putch(c) putchar(c)
225 #define unix_err(err) strerror(err)
226
227 void readblock(off_t blk, char *buf, int block_size)
228 /* Read blocks for the rawfs package. */
229 {
230 if(!block_size) fatal("block_size 0");
231 errno= EIO;
232 if (lseek(bootdev.device, blk * block_size, SEEK_SET) == -1
233 || read(bootdev.device, buf, block_size) != block_size)
234 {
235 fatal(bootdev.name);
236 }
237 }
238
239 sig_atomic_t trapsig;
240
241 void trap(int sig)
242 {
243 trapsig= sig;
244 signal(sig, trap);
245 }
246
247 int escape(void)
248 {
249 if (trapsig == SIGINT) {
250 trapsig= 0;
251 return 1;
252 }
253 return 0;
254 }
255
256 static unsigned char unchar;
257
258 int getch(void)
259 {
260 unsigned char c;
261
262 fflush(stdout);
263
264 if (unchar != 0) {
265 c= unchar;
266 unchar= 0;
267 return c;
268 }
269
270 switch (read(0, &c, 1)) {
271 case -1:
272 if (errno != EINTR) fatal("");
273 return(ESC);
274 case 0:
275 if (istty) putch('\n');
276 exit(0);
277 default:
278 if (istty && c == termbuf.c_cc[VEOF]) {
279 putch('\n');
280 exit(0);
281 }
282 return c;
283 }
284 }
285
286 #define ungetch(c) ((void) (unchar = (c)))
287
288 #define get_tick() ((u32_t) time(nil))
289 #define clear_screen() printf("[clear]")
290 #define boot_device(device) printf("[boot %s]\n", device)
291 #define ctty(line) printf("[ctty %s]\n", line)
292 #define bootminix() (run_trailer() && printf("[boot]\n"))
293 #define off() printf("[off]")
294
295 #endif /* UNIX */
296
297 char *readline(void)
298 /* Read a line including a newline with echoing. */
299 {
300 char *line;
301 size_t i, z;
302 int c;
303
304 i= 0;
305 z= 20;
306 line= malloc(z * sizeof(char));
307
308 do {
309 c= getch();
310
311 if (strchr("\b\177\25\3", c) != nil) {
312 /* Backspace, DEL, ctrl-U, or ctrl-X. */
313 do {
314 if (i == 0) break;
315 printf("\b \b");
316 i--;
317 } while (c == '\25' || c == '\3');
318 } else
319 if (c < ' ' && c != '\n') {
320 putch('\7');
321 } else {
322 putch(c);
323 line[i++]= c;
324 if (i == z) {
325 z*= 2;
326 line= realloc(line, z * sizeof(char));
327 }
328 }
329 } while (c != '\n');
330 line[i]= 0;
331 return line;
332 }
333
334 int sugar(char *tok)
335 /* Recognize special tokens. */
336 {
337 return strchr("=(){};\n", tok[0]) != nil;
338 }
339
340 char *onetoken(char **aline)
341 /* Returns a string with one token for tokenize. */
342 {
343 char *line= *aline;
344 size_t n;
345 char *tok;
346
347 /* Skip spaces and runs of newlines. */
348 while (*line == ' ' || (*line == '\n' && line[1] == '\n')) line++;
349
350 *aline= line;
351
352 /* Don't do odd junk (nor the terminating 0!). */
353 if ((unsigned) *line < ' ' && *line != '\n') return nil;
354
355 if (*line == '(') {
356 /* Function argument, anything goes but () must match. */
357 int depth= 0;
358
359 while ((unsigned) *line >= ' ') {
360 if (*line == '(') depth++;
361 if (*line++ == ')' && --depth == 0) break;
362 }
363 } else
364 if (sugar(line)) {
365 /* Single character token. */
366 line++;
367 } else {
368 /* Multicharacter token. */
369 do line++; while ((unsigned) *line > ' ' && !sugar(line));
370 }
371 n= line - *aline;
372 tok= malloc((n + 1) * sizeof(char));
373 memcpy(tok, *aline, n);
374 tok[n]= 0;
375 if (tok[0] == '\n') tok[0]= ';'; /* ';' same as '\n' */
376
377 *aline= line;
378 return tok;
379 }
380
381 /* Typed commands form strings of tokens. */
382
383 typedef struct token {
384 struct token *next; /* Next in a command chain. */
385 char *token;
386 } token;
387
388 token **tokenize(token **acmds, char *line)
389 /* Takes a line apart to form tokens. The tokens are inserted into a command
390 * chain at *acmds. Tokenize returns a reference to where another line could
391 * be added. Tokenize looks at spaces as token separators, and recognizes only
392 * ';', '=', '{', '}', and '\n' as single character tokens. One token is
393 * formed from '(' and ')' with anything in between as long as more () match.
394 */
395 {
396 char *tok;
397 token *newcmd;
398
399 while ((tok= onetoken(&line)) != nil) {
400 newcmd= malloc(sizeof(*newcmd));
401 newcmd->token= tok;
402 newcmd->next= *acmds;
403 *acmds= newcmd;
404 acmds= &newcmd->next;
405 }
406 return acmds;
407 }
408
409 token *cmds; /* String of commands to execute. */
410 int err; /* Set on an error. */
411
412 char *poptoken(void)
413 /* Pop one token off the command chain. */
414 {
415 token *cmd= cmds;
416 char *tok= cmd->token;
417
418 cmds= cmd->next;
419 free(cmd);
420
421 return tok;
422 }
423
424 void voidtoken(void)
425 /* Remove one token from the command chain. */
426 {
427 free(poptoken());
428 }
429
430 void parse_code(char *code)
431 /* Tokenize a string of monitor code, making sure there is a delimiter. It is
432 * to be executed next. (Prepended to the current input.)
433 */
434 {
435 if (cmds != nil && cmds->token[0] != ';') (void) tokenize(&cmds, ";");
436 (void) tokenize(&cmds, code);
437 }
438
439 int interrupt(void)
440 /* Clean up after an ESC has been typed. */
441 {
442 if (escape()) {
443 printf("[ESC]\n");
444 err= 1;
445 return 1;
446 }
447 return 0;
448 }
449
450 #if BIOS
451
452 int activate;
453
454 struct biosdev {
455 char name[8];
456 int device, primary, secondary;
457 } bootdev, tmpdev;
458
459 int get_master(char *master, struct part_entry **table, u32_t pos)
460 /* Read a master boot sector and its partition table. */
461 {
462 int r, n;
463 struct part_entry *pe, **pt;
464
465 if ((r= readsectors(mon2abs(master), pos, 1)) != 0) return r;
466
467 pe= (struct part_entry *) (master + PART_TABLE_OFF);
468 for (pt= table; pt < table + NR_PARTITIONS; pt++) *pt= pe++;
469
470 /* DOS has the misguided idea that partition tables must be sorted. */
471 if (pos != 0) return 0; /* But only the primary. */
472
473 n= NR_PARTITIONS;
474 do {
475 for (pt= table; pt < table + NR_PARTITIONS-1; pt++) {
476 if (pt[0]->sysind == NO_PART
477 || pt[0]->lowsec > pt[1]->lowsec) {
478 pe= pt[0]; pt[0]= pt[1]; pt[1]= pe;
479 }
480 }
481 } while (--n > 0);
482 return 0;
483 }
484
485 void initialize(void)
486 {
487 char master[SECTOR_SIZE];
488 struct part_entry *table[NR_PARTITIONS];
489 int r, p;
490 u32_t masterpos;
491 char *argp;
492
493 /* Copy the boot program to the far end of low memory, this must be
494 * done to get out of the way of Minix, and to put the data area
495 * cleanly inside a 64K chunk if using BIOS I/O (no DMA problems).
496 */
497 u32_t oldaddr= caddr;
498 u32_t memend= mem[0].base + mem[0].size;
499 u32_t newaddr= (memend - runsize) & ~0x0000FL;
500 #if !DOS
501 u32_t dma64k= (memend - 1) & ~0x0FFFFL;
502
503
504 /* Check if data segment crosses a 64K boundary. */
505 if (newaddr + (daddr - caddr) < dma64k) newaddr= dma64k - runsize;
506 #endif
507
508 /* Set the new caddr for relocate. */
509 caddr= newaddr;
510
511 /* Copy code and data. */
512 raw_copy(newaddr, oldaddr, runsize);
513
514 /* Make the copy running. */
515 relocate();
516
517 #if !DOS
518
519 /* Take the monitor out of the memory map if we have memory to spare,
520 * and also keep the BIOS data area safe (1.5K), plus a bit extra for
521 * where we may have to put a.out headers for older kernels.
522 */
523 if (mon_return = (mem[1].size > 512*1024L)) mem[0].size = newaddr;
524 mem[0].base += 2048;
525 mem[0].size -= 2048;
526
527 /* Find out what the boot device and partition was. */
528 bootdev.name[0]= 0;
529 bootdev.device= device;
530 bootdev.primary= -1;
531 bootdev.secondary= -1;
532
533 if (device < 0x80) {
534 /* Floppy. */
535 strcpy(bootdev.name, "fd0");
536 bootdev.name[2] += bootdev.device;
537 return;
538 }
539
540 /* Disk: Get the partition table from the very first sector, and
541 * determine the partition we booted from using the information from
542 * the booted partition entry as passed on by the bootstrap (rem_part).
543 * All we need from it is the partition offset.
544 */
545 raw_copy(mon2abs(&lowsec),
546 vec2abs(&rem_part) + offsetof(struct part_entry, lowsec),
547 sizeof(lowsec));
548
549 masterpos= 0; /* Master bootsector position. */
550
551 for (;;) {
552 /* Extract the partition table from the master boot sector. */
553 if ((r= get_master(master, table, masterpos)) != 0) {
554 readerr(masterpos, r); exit(1);
555 }
556
557 /* See if you can find "lowsec" back. */
558 for (p= 0; p < NR_PARTITIONS; p++) {
559 if (lowsec - table[p]->lowsec < table[p]->size) break;
560 }
561
562 if (lowsec == table[p]->lowsec) { /* Found! */
563 if (bootdev.primary < 0)
564 bootdev.primary= p;
565 else
566 bootdev.secondary= p;
567 break;
568 }
569
570 if (p == NR_PARTITIONS || bootdev.primary >= 0
571 || table[p]->sysind != MINIX_PART) {
572 /* The boot partition cannot be named, this only means
573 * that "bootdev" doesn't work.
574 */
575 bootdev.device= -1;
576 return;
577 }
578
579 /* See if the primary partition is subpartitioned. */
580 bootdev.primary= p;
581 masterpos= table[p]->lowsec;
582 }
583 strcpy(bootdev.name, "d0p0");
584 bootdev.name[1] += (device - 0x80);
585 bootdev.name[3] += bootdev.primary;
586 if (bootdev.secondary >= 0) {
587 strcat(bootdev.name, "s0");
588 bootdev.name[5] += bootdev.secondary;
589 }
590
591 #else /* DOS */
592 /* Take the monitor out of the memory map if we have memory to spare,
593 * note that only half our PSP is needed at the new place, the first
594 * half is to be kept in its place.
595 */
596 if (mem[1].size > 0) mem[0].size = newaddr + 0x80 - mem[0].base;
597
598 /* Parse the command line. */
599 argp= PSP + 0x81;
600 argp[PSP[0x80]]= 0;
601 while (between('\1', *argp, ' ')) argp++;
602 vdisk= argp;
603 while (!between('\0', *argp, ' ')) argp++;
604 while (between('\1', *argp, ' ')) *argp++= 0;
605 if (*vdisk == 0) {
606 printf("\nUsage: boot <vdisk> [commands ...]\n");
607 exit(1);
608 }
609 drun= *argp == 0 ? "main" : argp;
610
611 if ((r= dev_open()) != 0) {
612 printf("\n%s: Error %02x (%s)\n", vdisk, r, bios_err(r));
613 exit(1);
614 }
615
616 /* Find the active partition on the virtual disk. */
617 if ((r= get_master(master, table, 0)) != 0) {
618 readerr(0, r); exit(1);
619 }
620
621 strcpy(bootdev.name, "d0");
622 bootdev.primary= -1;
623 for (p= 0; p < NR_PARTITIONS; p++) {
624 if (table[p]->bootind != 0 && table[p]->sysind == MINIX_PART) {
625 bootdev.primary= p;
626 strcat(bootdev.name, "p0");
627 bootdev.name[3] += p;
628 lowsec= table[p]->lowsec;
629 break;
630 }
631 }
632 #endif /* DOS */
633 }
634
635 #endif /* BIOS */
636
637 /* Reserved names: */
638 enum resnames {
639 R_NULL, R_BOOT, R_CTTY, R_DELAY, R_ECHO, R_EXIT, R_HELP,
640 R_LS, R_MENU, R_OFF, R_SAVE, R_SET, R_TRAP, R_UNSET
641 };
642
643 char resnames[][6] = {
644 "", "boot", "ctty", "delay", "echo", "exit", "help",
645 "ls", "menu", "off", "save", "set", "trap", "unset",
646 };
647
648 /* Using this for all null strings saves a lot of memory. */
649 #define null (resnames[0])
650
651 int reserved(char *s)
652 /* Recognize reserved strings. */
653 {
654 int r;
655
656 for (r= R_BOOT; r <= R_UNSET; r++) {
657 if (strcmp(s, resnames[r]) == 0) return r;
658 }
659 return R_NULL;
660 }
661
662 void sfree(char *s)
663 /* Free a non-null string. */
664 {
665 if (s != nil && s != null) free(s);
666 }
667
668 char *copystr(char *s)
669 /* Copy a non-null string using malloc. */
670 {
671 char *c;
672
673 if (*s == 0) return null;
674 c= malloc((strlen(s) + 1) * sizeof(char));
675 strcpy(c, s);
676 return c;
677 }
678
679 int is_default(environment *e)
680 {
681 return (e->flags & E_SPECIAL) && e->defval == nil;
682 }
683
684 environment **searchenv(char *name)
685 {
686 environment **aenv= &env;
687
688 while (*aenv != nil && strcmp((*aenv)->name, name) != 0) {
689 aenv= &(*aenv)->next;
690 }
691
692 return aenv;
693 }
694
695 #define b_getenv(name) (*searchenv(name))
696 /* Return the environment *structure* belonging to name, or nil if not found. */
697
698 char *b_value(char *name)
699 /* The value of a variable. */
700 {
701 environment *e= b_getenv(name);
702
703 return e == nil || !(e->flags & E_VAR) ? nil : e->value;
704 }
705
706 char *b_body(char *name)
707 /* The value of a function. */
708 {
709 environment *e= b_getenv(name);
710
711 return e == nil || !(e->flags & E_FUNCTION) ? nil : e->value;
712 }
713
714 int b_setenv(int flags, char *name, char *arg, char *value)
715 /* Change the value of an environment variable. Returns the flags of the
716 * variable if you are not allowed to change it, 0 otherwise.
717 */
718 {
719 environment **aenv, *e;
720
721 if (*(aenv= searchenv(name)) == nil) {
722 if (reserved(name)) return E_RESERVED;
723 e= malloc(sizeof(*e));
724 e->name= copystr(name);
725 e->flags= flags;
726 e->defval= nil;
727 e->next= nil;
728 *aenv= e;
729 } else {
730 e= *aenv;
731
732 /* Don't change special variables to functions or vv. */
733 if (e->flags & E_SPECIAL
734 && (e->flags & E_FUNCTION) != (flags & E_FUNCTION)
735 ) return e->flags;
736
737 e->flags= (e->flags & E_STICKY) | flags;
738 if (is_default(e)) {
739 e->defval= e->value;
740 } else {
741 sfree(e->value);
742 }
743 sfree(e->arg);
744 }
745 e->arg= copystr(arg);
746 e->value= copystr(value);
747
748 return 0;
749 }
750
751 int b_setvar(int flags, char *name, char *value)
752 /* Set variable or simple function. */
753 {
754 int r;
755
756 if((r=b_setenv(flags, name, null, value))) {
757 return r;
758 }
759
760 return r;
761 }
762
763 void b_unset(char *name)
764 /* Remove a variable from the environment. A special variable is reset to
765 * its default value.
766 */
767 {
768 environment **aenv, *e;
769
770 if ((e= *(aenv= searchenv(name))) == nil) return;
771
772 if (e->flags & E_SPECIAL) {
773 if (e->defval != nil) {
774 sfree(e->arg);
775 e->arg= null;
776 sfree(e->value);
777 e->value= e->defval;
778 e->defval= nil;
779 }
780 } else {
781 sfree(e->name);
782 sfree(e->arg);
783 sfree(e->value);
784 *aenv= e->next;
785 free(e);
786 }
787 }
788
789 long a2l(char *a)
790 /* Cheap atol(). */
791 {
792 int sign= 1;
793 long n= 0;
794
795 if (*a == '-') { sign= -1; a++; }
796
797 while (between('', *a, '9')) n= n * 10 + (*a++ - '');
798
799 return sign * n;
800 }
801
802 char *ul2a(u32_t n, unsigned b)
803 /* Transform a long number to ascii at base b, (b >= 8). */
804 {
805 static char num[(CHAR_BIT * sizeof(n) + 2) / 3 + 1];
806 char *a= arraylimit(num) - 1;
807 static char hex[16] = "0123456789ABCDEF";
808
809 do *--a = hex[(int) (n % b)]; while ((n/= b) > 0);
810 return a;
811 }
812
813 char *ul2a10(u32_t n)
814 /* Transform a long number to ascii at base 10. */
815 {
816 return ul2a(n, 10);
817 }
818
819 unsigned a2x(char *a)
820 /* Ascii to hex. */
821 {
822 unsigned n= 0;
823 int c;
824
825 for (;;) {
826 c= *a;
827 if (between('', c, '9')) c= c - '' + 0x0;
828 else
829 if (between('A', c, 'F')) c= c - 'A' + 0xA;
830 else
831 if (between('a', c, 'f')) c= c - 'a' + 0xa;
832 else
833 break;
834 n= (n<<4) | c;
835 a++;
836 }
837 return n;
838 }
839
840 void get_parameters(void)
841 {
842 char params[SECTOR_SIZE + 1];
843 token **acmds;
844 int r, bus;
845 memory *mp;
846 static char bus_type[][4] = {
847 "xt", "at", "mca"
848 };
849 static char vid_type[][4] = {
850 "mda", "cga", "ega", "ega", "vga", "vga"
851 };
852 static char vid_chrome[][6] = {
853 "mono", "color"
854 };
855
856 /* Variables that Minix needs: */
857 b_setvar(E_SPECIAL|E_VAR|E_DEV, "rootdev", "ram");
858 b_setvar(E_SPECIAL|E_VAR|E_DEV, "ramimagedev", "bootdev");
859 b_setvar(E_SPECIAL|E_VAR, "ramsize", "");
860 #if BIOS
861 b_setvar(E_SPECIAL|E_VAR, "processor", ul2a10(getprocessor()));
862 b_setvar(E_SPECIAL|E_VAR, "bus", bus_type[get_bus()]);
863 b_setvar(E_SPECIAL|E_VAR, "video", vid_type[get_video()]);
864 b_setvar(E_SPECIAL|E_VAR, "chrome", vid_chrome[get_video() & 1]);
865 params[0]= 0;
866 for (mp= mem; mp < arraylimit(mem); mp++) {
867 if (mp->size == 0) continue;
868 if (params[0] != 0) strcat(params, ",");
869 strcat(params, ul2a(mp->base, 0x10));
870 strcat(params, ":");
871 strcat(params, ul2a(mp->size, 0x10));
872 }
873 b_setvar(E_SPECIAL|E_VAR, "memory", params);
874
875 #if 0
876 b_setvar(E_SPECIAL|E_VAR, "c0",
877 DOS ? "dosfile" : get_bus() == 1 ? "at" : "bios");
878 #else
879 b_setvar(E_SPECIAL|E_VAR, "label", "AT");
880 b_setvar(E_SPECIAL|E_VAR, "controller", "c0");
881 #endif
882
883 #if DOS
884 b_setvar(E_SPECIAL|E_VAR, "dosfile-d0", vdisk);
885 #endif
886
887 #endif
888 #if UNIX
889 b_setvar(E_SPECIAL|E_VAR, "processor", "?");
890 b_setvar(E_SPECIAL|E_VAR, "bus", "?");
891 b_setvar(E_SPECIAL|E_VAR, "video", "?");
892 b_setvar(E_SPECIAL|E_VAR, "chrome", "?");
893 b_setvar(E_SPECIAL|E_VAR, "memory", "?");
894 b_setvar(E_SPECIAL|E_VAR, "c0", "?");
895 #endif
896
897 /* Variables boot needs: */
898 b_setvar(E_SPECIAL|E_VAR, "image", "boot/image");
899 b_setvar(E_SPECIAL|E_FUNCTION, "leader",
900 "echo --- Welcome to MINIX 3. This is the boot monitor. ---\\n");
901 b_setvar(E_SPECIAL|E_FUNCTION, "main", "menu");
902 b_setvar(E_SPECIAL|E_FUNCTION, "trailer", "");
903
904 /* Default hidden menu function: */
905 b_setenv(E_RESERVED|E_FUNCTION, null, "=,Start MINIX", "boot");
906
907 /* Tokenize bootparams sector. */
908 if ((r= readsectors(mon2abs(params), lowsec+PARAMSEC, 1)) != 0) {
909 readerr(lowsec+PARAMSEC, r);
910 exit(1);
911 }
912 params[SECTOR_SIZE]= 0;
913 acmds= tokenize(&cmds, params);
914
915 /* Stuff the default action into the command chain. */
916 #if UNIX
917 (void) tokenize(acmds, ":;");
918 #elif DOS
919 (void) tokenize(tokenize(acmds, ":;leader;"), drun);
920 #else /* BIOS */
921 (void) tokenize(acmds, ":;leader;main");
922 #endif
923 }
924
925 char *addptr;
926
927 void addparm(char *n)
928 {
929 while (*n != 0 && *addptr != 0) *addptr++ = *n++;
930 }
931
932 void save_parameters(void)
933 /* Save nondefault environment variables to the bootparams sector. */
934 {
935 environment *e;
936 char params[SECTOR_SIZE + 1];
937 int r;
938
939 /* Default filling: */
940 memset(params, '\n', SECTOR_SIZE);
941
942 /* Don't touch the 0! */
943 params[SECTOR_SIZE]= 0;
944 addptr= params;
945
946 for (e= env; e != nil; e= e->next) {
947 if (e->flags & E_RESERVED || is_default(e)) continue;
948
949 addparm(e->name);
950 if (e->flags & E_FUNCTION) {
951 addparm("(");
952 addparm(e->arg);
953 addparm(")");
954 } else {
955 addparm((e->flags & (E_DEV|E_SPECIAL)) != E_DEV
956 ? "=" : "=d ");
957 }
958 addparm(e->value);
959 if (*addptr == 0) {
960 printf("The environment is too big\n");
961 return;
962 }
963 *addptr++= '\n';
964 }
965
966 /* Save the parameters on disk. */
967 if ((r= writesectors(mon2abs(params), lowsec+PARAMSEC, 1)) != 0) {
968 writerr(lowsec+PARAMSEC, r);
969 printf("Can't save environment\n");
970 }
971 }
972
973 void show_env(void)
974 /* Show the environment settings. */
975 {
976 environment *e;
977 unsigned more= 0;
978 int c;
979
980 for (e= env; e != nil; e= e->next) {
981 if (e->flags & E_RESERVED) continue;
982 if (!istty && is_default(e)) continue;
983
984 if (e->flags & E_FUNCTION) {
985 printf("%s(%s) %s\n", e->name, e->arg, e->value);
986 } else {
987 printf(is_default(e) ? "%s = (%s)\n" : "%s = %s\n",
988 e->name, e->value);
989 }
990
991 if (e->next != nil && istty && ++more % 20 == 0) {
992 printf("More? ");
993 c= getch();
994 if (c == ESC || c > ' ') {
995 putch('\n');
996 if (c > ' ') ungetch(c);
997 break;
998 }
999 printf("\b\b\b\b\b\b");
1000 }
1001 }
1002 }
1003
1004 int numprefix(char *s, char **ps)
1005 /* True iff s is a string of digits. *ps will be set to the first nondigit
1006 * if non-nil, otherwise the string should end.
1007 */
1008 {
1009 char *n= s;
1010
1011 while (between('', *n, '9')) n++;
1012
1013 if (n == s) return 0;
1014
1015 if (ps == nil) return *n == 0;
1016
1017 *ps= n;
1018 return 1;
1019 }
1020
1021 int numeric(char *s)
1022 {
1023 return numprefix(s, (char **) nil);
1024 }
1025
1026 #if BIOS
1027
1028 /* Device numbers of standard MINIX devices. */
1029 #define DEV_FD0 0x0200
1030 static dev_t dev_cNd0[] = { 0x0300, 0x0800, 0x0A00, 0x0C00, 0x1000 };
1031 #define minor_p0s0 128
1032
1033 static int block_size;
1034
1035 dev_t name2dev(char *name)
1036 /* Translate, say, /dev/c0d0p2 to a device number. If the name can't be
1037 * found on the boot device, then do some guesswork. The global structure
1038 * "tmpdev" will be filled in based on the name, so that "boot d1p0" knows
1039 * what device to boot without interpreting device numbers.
1040 */
1041 {
1042 dev_t dev;
1043 ino_t ino;
1044 int drive;
1045 struct stat st;
1046 char *n, *s;
1047
1048 /* "boot *d0p2" means: make partition 2 active before you boot it. */
1049 if ((activate= (name[0] == '*'))) name++;
1050
1051 /* The special name "bootdev" must be translated to the boot device. */
1052 if (strcmp(name, "bootdev") == 0) {
1053 if (bootdev.device == -1) {
1054 printf("The boot device could not be named\n");
1055 errno= 0;
1056 return -1;
1057 }
1058 name= bootdev.name;
1059 }
1060
1061 /* If our boot device doesn't have a file system, or we want to know
1062 * what a name means for the BIOS, then we need to interpret the
1063 * device name ourselves: "fd" = floppy, "c0d0" = hard disk, etc.
1064 */
1065 tmpdev.device= tmpdev.primary= tmpdev.secondary= -1;
1066 dev= -1;
1067 n= name;
1068 if (strncmp(n, "/dev/", 5) == 0) n+= 5;
1069
1070 if (strcmp(n, "ram") == 0) {
1071 dev= DEV_RAM;
1072 } else
1073 if (strcmp(n, "boot") == 0) {
1074 dev= DEV_BOOT;
1075 } else
1076 if (n[0] == 'f' && n[1] == 'd' && numeric(n+2)) {
1077 /* Floppy. */
1078 tmpdev.device= a2l(n+2);
1079 dev= DEV_FD0 + tmpdev.device;
1080 } else
1081 if ((n[0] == 'h' || n[0] == 's') && n[1] == 'd' && numprefix(n+2, &s)
1082 && (*s == 0 || (between('a', *s, 'd') && s[1] == 0))
1083 ) {
1084 /* Old style hard disk (backwards compatibility.) */
1085 dev= a2l(n+2);
1086 tmpdev.device= dev / (1 + NR_PARTITIONS);
1087 tmpdev.primary= (dev % (1 + NR_PARTITIONS)) - 1;
1088 if (*s != 0) {
1089 /* Subpartition. */
1090 tmpdev.secondary= *s - 'a';
1091 dev= minor_p0s0
1092 + (tmpdev.device * NR_PARTITIONS
1093 + tmpdev.primary) * NR_PARTITIONS
1094 + tmpdev.secondary;
1095 }
1096 tmpdev.device+= 0x80;
1097 dev+= n[0] == 'h' ? dev_cNd0[0] : dev_cNd0[2];
1098 } else {
1099 /* Hard disk. */
1100 int ctrlr= 0;
1101
1102 if (n[0] == 'c' && between('', n[1], '4')) {
1103 ctrlr= (n[1] - '');
1104 tmpdev.device= 0;
1105 n+= 2;
1106 }
1107 if (n[0] == 'd' && between('', n[1], '7')) {
1108 tmpdev.device= (n[1] - '');
1109 n+= 2;
1110 if (n[0] == 'p' && between('', n[1], '3')) {
1111 tmpdev.primary= (n[1] - '');
1112 n+= 2;
1113 if (n[0] == 's' && between('', n[1], '3')) {
1114 tmpdev.secondary= (n[1] - '');
1115 n+= 2;
1116 }
1117 }
1118 }
1119 if (*n == 0) {
1120 dev= dev_cNd0[ctrlr];
1121 if (tmpdev.secondary < 0) {
1122 dev += tmpdev.device * (NR_PARTITIONS+1)
1123 + (tmpdev.primary + 1);
1124 } else {
1125 dev += minor_p0s0
1126 + (tmpdev.device * NR_PARTITIONS
1127 + tmpdev.primary) * NR_PARTITIONS
1128 + tmpdev.secondary;
1129 }
1130 tmpdev.device+= 0x80;
1131 }
1132 }
1133
1134 /* Look the name up on the boot device for the UNIX device number. */
1135 if (fsok == -1) fsok= r_super(&block_size) != 0;
1136 if (fsok) {
1137 /* The current working directory is "/dev". */
1138 ino= r_lookup(r_lookup(ROOT_INO, "dev"), name);
1139
1140 if (ino != 0) {
1141 /* Name has been found, extract the device number. */
1142 r_stat(ino, &st);
1143 if (!S_ISBLK(st.st_mode)) {
1144 printf("%s is not a block device\n", name);
1145 errno= 0;
1146 return (dev_t) -1;
1147 }
1148 dev= st.st_rdev;
1149 }
1150 }
1151
1152 if (tmpdev.primary < 0) activate= 0; /* Careful now! */
1153
1154 if (dev == -1) {
1155 printf("Can't recognize '%s' as a device\n", name);
1156 errno= 0;
1157 }
1158 return dev;
1159 }
1160
1161 #if DEBUG
1162 static void apm_perror(char *label, u16_t ax)
1163 {
1164 unsigned ah;
1165 char *str;
1166
1167 ah= (ax >> 8);
1168 switch(ah)
1169 {
1170 case 0x01: str= "APM functionality disabled"; break;
1171 case 0x03: str= "interface not connected"; break;
1172 case 0x09: str= "unrecognized device ID"; break;
1173 case 0x0A: str= "parameter value out of range"; break;
1174 case 0x0B: str= "interface not engaged"; break;
1175 case 0x60: str= "unable to enter requested state"; break;
1176 case 0x86: str= "APM not present"; break;
1177 default: printf("%s: error 0x%02x\n", label, ah); return;
1178 }
1179 printf("%s: %s\n", label, str);
1180 }
1181
1182 #define apm_printf printf
1183 #else
1184 #define apm_perror(label, ax) ((void)0)
1185 #define apm_printf
1186 #endif
1187
1188 static void off(void)
1189 {
1190 bios_env_t be;
1191 unsigned al, ah;
1192
1193 /* Try to switch off the system. Print diagnostic information
1194 * that can be useful if the operation fails.
1195 */
1196
1197 be.ax= 0x5300; /* APM, Installation check */
1198 be.bx= 0; /* Device, APM BIOS */
1199 int15(&be);
1200 if (be.flags & FL_CARRY)
1201 {
1202 apm_perror("APM installation check failed", be.ax);
1203 return;
1204 }
1205 if (be.bx != (('P' << 8) | 'M'))
1206 {
1207 apm_printf("APM signature not found (got 0x%04x)\n", be.bx);
1208 return;
1209 }
1210
1211 ah= be.ax >> 8;
1212 if (ah > 9)
1213 ah= (ah >> 4)*10 + (ah & 0xf);
1214 al= be.ax & 0xff;
1215 if (al > 9)
1216 al= (al >> 4)*10 + (al & 0xf);
1217 apm_printf("APM version %u.%u%s%s%s%s%s\n",
1218 ah, al,
1219 (be.cx & 0x1) ? ", 16-bit PM" : "",
1220 (be.cx & 0x2) ? ", 32-bit PM" : "",
1221 (be.cx & 0x4) ? ", CPU-Idle" : "",
1222 (be.cx & 0x8) ? ", APM-disabled" : "",
1223 (be.cx & 0x10) ? ", APM-disengaged" : "");
1224
1225 /* Connect */
1226 be.ax= 0x5301; /* APM, Real mode interface connect */
1227 be.bx= 0x0000; /* APM BIOS */
1228 int15(&be);
1229 if (be.flags & FL_CARRY)
1230 {
1231 apm_perror("APM real mode connect failed", be.ax);
1232 return;
1233 }
1234
1235 /* Ask for a seat upgrade */
1236 be.ax= 0x530e; /* APM, Driver Version */
1237 be.bx= 0x0000; /* BIOS */
1238 be.cx= 0x0102; /* version 1.2 */
1239 int15(&be);
1240 if (be.flags & FL_CARRY)
1241 {
1242 apm_perror("Set driver version failed", be.ax);
1243 goto disco;
1244 }
1245
1246 /* Is this version really worth reporting. Well, if the system
1247 * does switch off, you won't see it anyway.
1248 */
1249 ah= be.ax >> 8;
1250 if (ah > 9)
1251 ah= (ah >> 4)*10 + (ah & 0xf);
1252 al= be.ax & 0xff;
1253 if (al > 9)
1254 al= (al >> 4)*10 + (al & 0xf);
1255 apm_printf("Got APM connection version %u.%u\n", ah, al);
1256
1257 /* Enable */
1258 be.ax= 0x5308; /* APM, Enable/disable power management */
1259 be.bx= 0x0001; /* All device managed by APM BIOS */
1260 #if 0
1261 /* For old APM 1.0 systems, we need 0xffff. Assume that those
1262 * systems do not exist.
1263 */
1264 be.bx= 0xffff; /* All device managed by APM BIOS (compat) */
1265 #endif
1266 be.cx= 0x0001; /* Enable power management */
1267 int15(&be);
1268 if (be.flags & FL_CARRY)
1269 {
1270 apm_perror("Enable power management failed", be.ax);
1271 goto disco;
1272 }
1273
1274 /* Off */
1275 be.ax= 0x5307; /* APM, Set Power State */
1276 be.bx= 0x0001; /* All devices managed by APM */
1277 be.cx= 0x0003; /* Off */
1278 int15(&be);
1279 if (be.flags & FL_CARRY)
1280 {
1281 apm_perror("Set power state failed", be.ax);
1282 goto disco;
1283 }
1284
1285 apm_printf("Power off sequence successfully completed.\n\n");
1286 apm_printf("Ha, ha, just kidding!\n");
1287
1288 disco:
1289 /* Disconnect */
1290 be.ax= 0x5304; /* APM, interface disconnect */
1291 be.bx= 0x0000; /* APM BIOS */
1292 int15(&be);
1293 if (be.flags & FL_CARRY)
1294 {
1295 apm_perror("APM interface disconnect failed", be.ax);
1296 return;
1297 }
1298 }
1299
1300 #if !DOS
1301 #define B_NOSIG -1 /* "No signature" error code. */
1302
1303 int exec_bootstrap(void)
1304 /* Load boot sector from the disk or floppy described by tmpdev and execute it.
1305 */
1306 {
1307 int r, n, dirty= 0;
1308 char master[SECTOR_SIZE];
1309 struct part_entry *table[NR_PARTITIONS], dummy, *active= &dummy;
1310 u32_t masterpos;
1311
1312 active->lowsec= 0;
1313
1314 /* Select a partition table entry. */
1315 while (tmpdev.primary >= 0) {
1316 masterpos= active->lowsec;
1317
1318 if ((r= get_master(master, table, masterpos)) != 0) return r;
1319
1320 active= table[tmpdev.primary];
1321
1322 /* How does one check a partition table entry? */
1323 if (active->sysind == NO_PART) return B_NOSIG;
1324
1325 tmpdev.primary= tmpdev.secondary;
1326 tmpdev.secondary= -1;
1327 }
1328
1329 if (activate && !active->bootind) {
1330 for (n= 0; n < NR_PARTITIONS; n++) table[n]->bootind= 0;
1331 active->bootind= ACTIVE_FLAG;
1332 dirty= 1;
1333 }
1334
1335 /* Read the boot sector. */
1336 if ((r= readsectors(BOOTPOS, active->lowsec, 1)) != 0) return r;
1337
1338 /* Check signature word. */
1339 if (get_word(BOOTPOS+SIGNATOFF) != SIGNATURE) return B_NOSIG;
1340
1341 /* Write the partition table if a member must be made active. */
1342 if (dirty && (r= writesectors(mon2abs(master), masterpos, 1)) != 0)
1343 return r;
1344
1345 bootstrap(device, active);
1346 }
1347
1348 void boot_device(char *devname)
1349 /* Boot the device named by devname. */
1350 {
1351 dev_t dev= name2dev(devname);
1352 int save_dev= device;
1353 int r;
1354 char *err;
1355
1356 if (tmpdev.device < 0) {
1357 if (dev != -1) printf("Can't boot from %s\n", devname);
1358 return;
1359 }
1360
1361 /* Change current device and try to load and execute its bootstrap. */
1362 device= tmpdev.device;
1363
1364 if ((r= dev_open()) == 0) r= exec_bootstrap();
1365
1366 err= r == B_NOSIG ? "Not bootable" : bios_err(r);
1367 printf("Can't boot %s: %s\n", devname, err);
1368
1369 /* Restore boot device setting. */
1370 device= save_dev;
1371 (void) dev_open();
1372 }
1373
1374 void ctty(char *line)
1375 {
1376 if (between('', line[0], '3') && line[1] == 0) {
1377 serial_init(line[0] - '');
1378 } else {
1379 printf("Bad serial line number: %s\n", line);
1380 }
1381 }
1382
1383 #else /* DOS */
1384
1385 void boot_device(char *devname)
1386 /* No booting of other devices under DOS. */
1387 {
1388 printf("Can't boot devices under DOS\n");
1389 }
1390
1391 void ctty(char *line)
1392 /* Don't know how to handle serial lines under DOS. */
1393 {
1394 printf("No serial line support under DOS\n");
1395 }
1396
1397 #endif /* DOS */
1398 #endif /* BIOS */
1399
1400 void ls(char *dir)
1401 /* List the contents of a directory. */
1402 {
1403 ino_t ino;
1404 struct stat st;
1405 char name[NAME_MAX+1];
1406
1407 if (fsok == -1) fsok= r_super(&block_size) != 0;
1408 if (!fsok) return;
1409
1410 /* (,) construct because r_stat returns void */
1411 if ((ino= r_lookup(ROOT_INO, dir)) == 0 ||
1412 (r_stat(ino, &st), r_readdir(name)) == -1)
1413 {
1414 printf("ls: %s: %s\n", dir, unix_err(errno));
1415 return;
1416 }
1417 (void) r_readdir(name); /* Skip ".." too. */
1418
1419 while ((ino= r_readdir(name)) != 0) printf("%s/%s\n", dir, name);
1420 }
1421
1422 u32_t milli_time(void)
1423 {
1424 return get_tick() * MSEC_PER_TICK;
1425 }
1426
1427 u32_t milli_since(u32_t base)
1428 {
1429 return (milli_time() + (TICKS_PER_DAY*MSEC_PER_TICK) - base)
1430 % (TICKS_PER_DAY*MSEC_PER_TICK);
1431 }
1432
1433 char *Thandler;
1434 u32_t Tbase, Tcount;
1435
1436 void unschedule(void)
1437 /* Invalidate a waiting command. */
1438 {
1439 alarm(0);
1440
1441 if (Thandler != nil) {
1442 free(Thandler);
1443 Thandler= nil;
1444 }
1445 }
1446
1447 void schedule(long msec, char *cmd)
1448 /* Schedule command at a certain time from now. */
1449 {
1450 unschedule();
1451 Thandler= cmd;
1452 Tbase= milli_time();
1453 Tcount= msec;
1454 alarm(1);
1455 }
1456
1457 int expired(void)
1458 /* Check if the timer expired for getch(). */
1459 {
1460 return (Thandler != nil && milli_since(Tbase) >= Tcount);
1461 }
1462
1463 void delay(char *msec)
1464 /* Delay for a given time. */
1465 {
1466 u32_t base, count;
1467
1468 if ((count= a2l(msec)) == 0) return;
1469 base= milli_time();
1470
1471 alarm(1);
1472
1473 do {
1474 pause();
1475 } while (!interrupt() && !expired() && milli_since(base) < count);
1476 }
1477
1478 enum whatfun { NOFUN, SELECT, DEFFUN, USERFUN } menufun(environment *e)
1479 {
1480 if (!(e->flags & E_FUNCTION) || e->arg[0] == 0) return NOFUN;
1481 if (e->arg[1] != ',') return SELECT;
1482 return e->flags & E_RESERVED ? DEFFUN : USERFUN;
1483 }
1484
1485 void menu(void)
1486 /* By default: Show a simple menu.
1487 * Multiple kernels/images: Show extra selection options.
1488 * User defined function: Kill the defaults and show these.
1489 * Wait for a keypress and execute the given function.
1490 */
1491 {
1492 int c, def= 1;
1493 char *choice= nil;
1494 environment *e;
1495
1496 /* Just a default menu? */
1497 for (e= env; e != nil; e= e->next) if (menufun(e) == USERFUN) def= 0;
1498
1499 printf("\nHit a key as follows:\n\n");
1500
1501 /* Show the choices. */
1502 for (e= env; e != nil; e= e->next) {
1503 switch (menufun(e)) {
1504 case DEFFUN:
1505 if (!def) break;
1506 /*FALL THROUGH*/
1507 case USERFUN:
1508 printf(" %c %s\n", e->arg[0], e->arg+2);
1509 break;
1510 case SELECT:
1511 printf(" %c Select %s kernel\n", e->arg[0],e->name);
1512 break;
1513 default:;
1514 }
1515 }
1516
1517 /* Wait for a keypress. */
1518 do {
1519 c= getch();
1520 if (interrupt() || expired()) return;
1521
1522 unschedule();
1523
1524 for (e= env; e != nil; e= e->next) {
1525 switch (menufun(e)) {
1526 case DEFFUN:
1527 if (!def) break;
1528 case USERFUN:
1529 case SELECT:
1530 if (c == e->arg[0]) choice= e->value;
1531 }
1532 }
1533 } while (choice == nil);
1534
1535 /* Execute the chosen function. */
1536 printf("%c\n", c);
1537 (void) tokenize(&cmds, choice);
1538 }
1539
1540 void help(void)
1541 /* Not everyone is a rocket scientist. */
1542 {
1543 struct help {
1544 char *thing;
1545 char *help;
1546 } *pi;
1547 static struct help info[] = {
1548 { nil, "Names:" },
1549 { "rootdev", "Root device" },
1550 { "ramimagedev", "Device to use as RAM disk image " },
1551 { "ramsize", "RAM disk size (if no image device) " },
1552 { "bootdev", "Special name for the boot device" },
1553 { "fd0, d0p2, c0d0p1s0", "Devices (as in /dev)" },
1554 { "image", "Name of the boot image to use" },
1555 { "main", "Startup function" },
1556 { "bootdelay", "Delay in msec after loading image" },
1557 { nil, "Commands:" },
1558 { "name = [device] value", "Set environment variable" },
1559 { "name() { ... }", "Define function" },
1560 { "name(key,text) { ... }",
1561 "A menu option like: minix(=,Start MINIX) {boot}" },
1562 { "name", "Call function" },
1563 { "boot [device]", "Boot Minix or another O.S." },
1564 { "ctty [line]", "Duplicate to serial line" },
1565 { "delay [msec]", "Delay (500 msec default)" },
1566 { "echo word ...", "Display the words" },
1567 { "ls [directory]", "List contents of directory" },
1568 { "menu", "Show menu and choose menu option" },
1569 { "save / set", "Save or show environment" },
1570 { "trap msec command", "Schedule command " },
1571 { "unset name ...", "Unset variable or set to default" },
1572 { "exit / off", "Exit the Monitor / Power off" },
1573 };
1574
1575 for (pi= info; pi < arraylimit(info); pi++) {
1576 if (pi->thing != nil) printf(" %-24s- ", pi->thing);
1577 printf("%s\n", pi->help);
1578 }
1579 }
1580
1581 void execute(void)
1582 /* Get one command from the command chain and execute it. */
1583 {
1584 token *second, *third, *fourth, *sep;
1585 char *name;
1586 int res;
1587 size_t n= 0;
1588
1589 if (err) {
1590 /* An error occured, stop interpreting. */
1591 while (cmds != nil) voidtoken();
1592 return;
1593 }
1594
1595 if (expired()) { /* Timer expired? */
1596 parse_code(Thandler);
1597 unschedule();
1598 }
1599
1600 /* There must be a separator lurking somewhere. */
1601 for (sep= cmds; sep != nil && sep->token[0] != ';'; sep= sep->next) n++;
1602
1603 name= cmds->token;
1604 res= reserved(name);
1605 if ((second= cmds->next) != nil
1606 && (third= second->next) != nil)
1607 fourth= third->next;
1608
1609 /* Null command? */
1610 if (n == 0) {
1611 voidtoken();
1612 return;
1613 } else
1614 /* name = [device] value? */
1615 if ((n == 3 || n == 4)
1616 && !sugar(name)
1617 && second->token[0] == '='
1618 && !sugar(third->token)
1619 && (n == 3 || (n == 4 && third->token[0] == 'd'
1620 && !sugar(fourth->token)
1621 ))) {
1622 char *value= third->token;
1623 int flags= E_VAR;
1624
1625 if (n == 4) { value= fourth->token; flags|= E_DEV; }
1626
1627 if ((flags= b_setvar(flags, name, value)) != 0) {
1628 printf("%s is a %s\n", name,
1629 flags & E_RESERVED ? "reserved word" :
1630 "special function");
1631 err= 1;
1632 }
1633 while (cmds != sep) voidtoken();
1634 return;
1635 } else
1636 /* name '(arg)' ... ? */
1637 if (n >= 3
1638 && !sugar(name)
1639 && second->token[0] == '('
1640 ) {
1641 token *fun;
1642 int c, flags, depth;
1643 char *body;
1644 size_t len;
1645
1646 sep= fun= third;
1647 depth= 0;
1648 len= 1;
1649 while (sep != nil) {
1650 if ((c= sep->token[0]) == ';' && depth == 0) break;
1651 len+= strlen(sep->token) + 1;
1652 sep= sep->next;
1653 if (c == '{') depth++;
1654 if (c == '}' && --depth == 0) break;
1655 }
1656
1657 body= malloc(len * sizeof(char));
1658 *body= 0;
1659
1660 while (fun != sep) {
1661 strcat(body, fun->token);
1662 if (!sugar(fun->token)
1663 && !sugar(fun->next->token)
1664 ) strcat(body, " ");
1665 fun= fun->next;
1666 }
1667 second->token[strlen(second->token)-1]= 0;
1668
1669 if (depth != 0) {
1670 printf("Missing '}'\n");
1671 err= 1;
1672 } else
1673 if ((flags= b_setenv(E_FUNCTION, name,
1674 second->token+1, body)) != 0) {
1675 printf("%s is a %s\n", name,
1676 flags & E_RESERVED ? "reserved word" :
1677 "special variable");
1678 err= 1;
1679 }
1680 while (cmds != sep) voidtoken();
1681 free(body);
1682 return;
1683 } else
1684 /* Grouping? */
1685 if (name[0] == '{') {
1686 token **acmds= &cmds->next;
1687 char *t;
1688 int depth= 1;
1689
1690 /* Find and remove matching '}' */
1691 depth= 1;
1692 while (*acmds != nil) {
1693 t= (*acmds)->token;
1694 if (t[0] == '{') depth++;
1695 if (t[0] == '}' && --depth == 0) { t[0]= ';'; break; }
1696 acmds= &(*acmds)->next;
1697 }
1698 voidtoken();
1699 return;
1700 } else
1701 /* Command coming up, check if ESC typed. */
1702 if (interrupt()) {
1703 return;
1704 } else
1705 /* unset name ..., echo word ...? */
1706 if (n >= 1 && (res == R_UNSET || res == R_ECHO)) {
1707 char *arg= poptoken(), *p;
1708
1709 for (;;) {
1710 free(arg);
1711 if (cmds == sep) break;
1712 arg= poptoken();
1713 if (res == R_UNSET) { /* unset arg */
1714 b_unset(arg);
1715 } else { /* echo arg */
1716 p= arg;
1717 while (*p != 0) {
1718 if (*p != '\\') {
1719 putch(*p);
1720 } else
1721 switch (*++p) {
1722 case 0:
1723 if (cmds == sep) return;
1724 continue;
1725 case 'n':
1726 putch('\n');
1727 break;
1728 case 'v':
1729 printf(version);
1730 break;
1731 case 'c':
1732 clear_screen();
1733 break;
1734 case 'w':
1735 for (;;) {
1736 if (interrupt())
1737 return;
1738 if (getch() == '\n')
1739 break;
1740 }
1741 break;
1742 default:
1743 putch(*p);
1744 }
1745 p++;
1746 }
1747 putch(cmds != sep ? ' ' : '\n');
1748 }
1749 }
1750 return;
1751 } else
1752 /* boot -opts? */
1753 if (n == 2 && res == R_BOOT && second->token[0] == '-') {
1754 static char optsvar[]= "bootopts";
1755 (void) b_setvar(E_VAR, optsvar, second->token);
1756 voidtoken();
1757 voidtoken();
1758 bootminix();
1759 b_unset(optsvar);
1760 return;
1761 } else
1762 /* boot device, ls dir, delay msec? */
1763 if (n == 2 && (res == R_BOOT || res == R_CTTY
1764 || res == R_DELAY || res == R_LS)
1765 ) {
1766 if (res == R_BOOT) boot_device(second->token);
1767 if (res == R_CTTY) ctty(second->token);
1768 if (res == R_DELAY) delay(second->token);
1769 if (res == R_LS) ls(second->token);
1770 voidtoken();
1771 voidtoken();
1772 return;
1773 } else
1774 /* trap msec command? */
1775 if (n == 3 && res == R_TRAP && numeric(second->token)) {
1776 long msec= a2l(second->token);
1777
1778 voidtoken();
1779 voidtoken();
1780 schedule(msec, poptoken());
1781 return;
1782 } else
1783 /* Simple command. */
1784 if (n == 1) {
1785 char *body;
1786 int ok= 0;
1787
1788 name= poptoken();
1789
1790 switch (res) {
1791 case R_BOOT: bootminix(); ok= 1; break;
1792 case R_DELAY: delay("500"); ok= 1; break;
1793 case R_LS: ls(null); ok= 1; break;
1794 case R_MENU: menu(); ok= 1; break;
1795 case R_SAVE: save_parameters(); ok= 1;break;
1796 case R_SET: show_env(); ok= 1; break;
1797 case R_HELP: help(); ok= 1; break;
1798 case R_EXIT: exit(0);
1799 case R_OFF: off(); ok= 1; break;
1800 }
1801
1802 /* Command to check bootparams: */
1803 if (strcmp(name, ":") == 0) ok= 1;
1804
1805 /* User defined function. */
1806 if (!ok && (body= b_body(name)) != nil) {
1807 (void) tokenize(&cmds, body);
1808 ok= 1;
1809 }
1810 if (!ok) printf("%s: unknown function", name);
1811 free(name);
1812 if (ok) return;
1813 } else {
1814 /* Syntax error. */
1815 printf("Can't parse:");
1816 while (cmds != sep) {
1817 printf(" %s", cmds->token); voidtoken();
1818 }
1819 }
1820
1821 /* Getting here means that the command is not understood. */
1822 printf("\nTry 'help'\n");
1823 err= 1;
1824 }
1825
1826 int run_trailer(void)
1827 /* Run the trailer function between loading Minix and handing control to it.
1828 * Return true iff there was no error.
1829 */
1830 {
1831 token *save_cmds= cmds;
1832
1833 cmds= nil;
1834 (void) tokenize(&cmds, "trailer");
1835 while (cmds != nil) execute();
1836 cmds= save_cmds;
1837 return !err;
1838 }
1839
1840 void monitor(void)
1841 /* Read a line and tokenize it. */
1842 {
1843 char *line;
1844
1845 unschedule(); /* Kill a trap. */
1846 err= 0; /* Clear error state. */
1847
1848 if (istty) printf("%s>", bootdev.name);
1849 line= readline();
1850 (void) tokenize(&cmds, line);
1851 free(line);
1852 (void) escape(); /* Forget if ESC typed. */
1853 }
1854
1855 #if BIOS
1856
1857 unsigned char cdspec[25];
1858 void bootcdinfo(u32_t, int *, int drive);
1859
1860 void boot(void)
1861 /* Load Minix and start it, among other things. */
1862 {
1863
1864 /* Initialize tables. */
1865 initialize();
1866
1867 /* Get environment variables from the parameter sector. */
1868 get_parameters();
1869
1870 while (1) {
1871 /* While there are commands, execute them! */
1872
1873 while (cmds != nil) execute();
1874
1875 /* The "monitor" is just a "read one command" thing. */
1876 monitor();
1877 }
1878 }
1879 #endif /* BIOS */
1880
1881 #if UNIX
1882
1883 void main(int argc, char **argv)
1884 /* Do not load or start anything, just edit parameters. */
1885 {
1886 int i;
1887 char bootcode[SECTOR_SIZE];
1888 struct termios rawterm;
1889
1890 istty= (argc <= 2 && tcgetattr(0, &termbuf) == 0);
1891
1892 if (argc < 2) {
1893 fprintf(stderr, "Usage: edparams device [command ...]\n");
1894 exit(1);
1895 }
1896
1897 /* Go over the arguments, changing control characters to spaces. */
1898 for (i= 2; i < argc; i++) {
1899 char *p;
1900
1901 for (p= argv[i]; *p != 0; p++) {
1902 if ((unsigned) *p < ' ' && *p != '\n') *p= ' ';
1903 }
1904 }
1905
1906 bootdev.name= argv[1];
1907 if (strncmp(bootdev.name, "/dev/", 5) == 0) bootdev.name+= 5;
1908 if ((bootdev.device= open(argv[1], O_RDWR, 0666)) < 0)
1909 fatal(bootdev.name);
1910
1911 /* Check if it is a bootable Minix device. */
1912 if (readsectors(mon2abs(bootcode), lowsec, 1) != 0
1913 || memcmp(bootcode, boot_magic, sizeof(boot_magic)) != 0) {
1914 fprintf(stderr, "edparams: %s: not a bootable Minix device\n",
1915 bootdev.name);
1916 exit(1);
1917 }
1918
1919 /* Print greeting message. */
1920 if (istty) printf("Boot parameters editor.\n");
1921
1922 signal(SIGINT, trap);
1923 signal(SIGALRM, trap);
1924
1925 if (istty) {
1926 rawterm= termbuf;
1927 rawterm.c_lflag&= ~(ICANON|ECHO|IEXTEN);
1928 rawterm.c_cc[VINTR]= ESC;
1929 if (tcsetattr(0, TCSANOW, &rawterm) < 0) fatal("");
1930 }
1931
1932 /* Get environment variables from the parameter sector. */
1933 get_parameters();
1934
1935 i= 2;
1936 for (;;) {
1937 /* While there are commands, execute them! */
1938 while (cmds != nil || i < argc) {
1939 if (cmds == nil) {
1940 /* A command line command. */
1941 parse_code(argv[i++]);
1942 }
1943 execute();
1944
1945 /* Bail out on errors if not interactive. */
1946 if (err && !istty) exit(1);
1947 }
1948
1949 /* Commands on the command line? */
1950 if (argc > 2) break;
1951
1952 /* The "monitor" is just a "read one command" thing. */
1953 monitor();
1954 }
1955 exit(0);
1956 }
1957 #endif /* UNIX */
1958
1959 /*
1960 * $PchId: boot.c,v 1.14 2002/02/27 19:46:14 philip Exp $
1961 */
Cache object: e22c4bec4559f414d338361dd5190132
|