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