forget difference between big and small commands - obsolete with vm.
[minix.git] / boot / boot.c
blob067dd6233f64ef24584c57d86bbce4479f94098a
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/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 <sys/video.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"
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)))
49 int serial_line = -1;
51 u16_t vid_port; /* Video i/o port. */
52 u32_t vid_mem_base; /* Video memory base address. */
53 u32_t vid_mem_size; /* Video memory size. */
55 int fsok= -1; /* File system state. Initially unknown. */
57 static int block_size;
59 #if BIOS
61 /* this data is reserved for BIOS int 0x13 to put the 'specification packet'
62 * in. It has a structure of course, but we don't define a struct because
63 * of compiler padding. We fiddle out the bytes ourselves later.
65 unsigned char boot_spec[24];
67 char *bios_err(int err)
68 /* Translate BIOS error code to a readable string. (This is a rare trait
69 * known as error checking and reporting. Take a good look at it, you won't
70 * see it often.)
73 static struct errlist {
74 int err;
75 char *what;
76 } errlist[] = {
77 #if !DOS
78 { 0x00, "No error" },
79 { 0x01, "Invalid command" },
80 { 0x02, "Address mark not found" },
81 { 0x03, "Disk write-protected" },
82 { 0x04, "Sector not found" },
83 { 0x05, "Reset failed" },
84 { 0x06, "Floppy disk removed" },
85 { 0x07, "Bad parameter table" },
86 { 0x08, "DMA overrun" },
87 { 0x09, "DMA crossed 64 KB boundary" },
88 { 0x0A, "Bad sector flag" },
89 { 0x0B, "Bad track flag" },
90 { 0x0C, "Media type not found" },
91 { 0x0D, "Invalid number of sectors on format" },
92 { 0x0E, "Control data address mark detected" },
93 { 0x0F, "DMA arbitration level out of range" },
94 { 0x10, "Uncorrectable CRC or ECC data error" },
95 { 0x11, "ECC corrected data error" },
96 { 0x20, "Controller failed" },
97 { 0x40, "Seek failed" },
98 { 0x80, "Disk timed-out" },
99 { 0xAA, "Drive not ready" },
100 { 0xBB, "Undefined error" },
101 { 0xCC, "Write fault" },
102 { 0xE0, "Status register error" },
103 { 0xFF, "Sense operation failed" }
104 #else /* DOS */
105 { 0x00, "No error" },
106 { 0x01, "Function number invalid" },
107 { 0x02, "File not found" },
108 { 0x03, "Path not found" },
109 { 0x04, "Too many open files" },
110 { 0x05, "Access denied" },
111 { 0x06, "Invalid handle" },
112 { 0x0C, "Access code invalid" },
113 #endif /* DOS */
115 struct errlist *errp;
117 for (errp= errlist; errp < arraylimit(errlist); errp++) {
118 if (errp->err == err) return errp->what;
120 return "Unknown error";
123 /* CD's are addressed in 2048-byte sectors.
124 * In order to be able to read CD's but maintain the same interface of 512-byte
125 * sector addressing, we check if the device is a CD in readsectors() and if so,
126 * read it into our own buffer first
128 int readsectors(u32_t bufaddr, u32_t sector, U8_t count)
130 #define CDSECTOR_SIZE 2048
131 static char cdbuf[CDSECTOR_SIZE];
132 static i32_t cdbuf_sec = -1;
133 i32_t cdsec;
135 if(device != cddevice) {
136 return biosreadsectors(bufaddr, sector, count);
139 while(count > 0) {
140 u32_t offset;
141 #define FACTOR (CDSECTOR_SIZE/SECTOR_SIZE)
142 cdsec = sector / FACTOR;
143 offset = (sector % FACTOR) * SECTOR_SIZE;
144 if(cdsec != cdbuf_sec) {
145 int r;
146 if((r=biosreadsectors(mon2abs(cdbuf), cdsec, 1)) != 0) {
147 printf("error %d\n", r);
148 return r;
150 cdbuf_sec = cdsec;
152 raw_copy(bufaddr, mon2abs(cdbuf) + offset, SECTOR_SIZE);
153 bufaddr += SECTOR_SIZE;
154 count--;
155 sector++;
158 return 0;
161 char *unix_err(int err)
162 /* Translate the few errors rawfs can give. */
164 switch (err) {
165 case ENOENT: return "No such file or directory";
166 case ENOTDIR: return "Not a directory";
167 default: return "Unknown error";
171 void rwerr(char *rw, off_t sec, int err)
173 printf("\n%s error 0x%02x (%s) at sector %ld absolute\n",
174 rw, err, bios_err(err), sec);
177 void readerr(off_t sec, int err) { rwerr("Read", sec, err); }
178 void writerr(off_t sec, int err) { rwerr("Write", sec, err); }
180 void readblock(off_t blk, char *buf, int block_size)
181 /* Read blocks for the rawfs package. */
183 int r;
184 u32_t sec= lowsec + blk * RATIO(block_size);
186 if(!block_size) {
187 printf("block_size 0\n");
188 exit(1);
191 if ((r= readsectors(mon2abs(buf), sec, 1 * RATIO(block_size))) != 0) {
192 readerr(sec, r); exit(1);
196 #define istty (1)
197 #define alarm(n) (0)
199 #endif /* BIOS */
201 #if UNIX
203 /* The Minix boot block must start with these bytes: */
204 char boot_magic[] = { 0x31, 0xC0, 0x8E, 0xD8, 0xFA, 0x8E, 0xD0, 0xBC };
206 struct biosdev {
207 char *name; /* Name of device. */
208 int device; /* Device to edit parameters. */
209 } bootdev;
211 struct termios termbuf;
212 int istty;
214 void quit(int status)
216 if (istty) (void) tcsetattr(0, TCSANOW, &termbuf);
217 exit(status);
220 #define exit(s) quit(s)
222 void report(char *label)
223 /* edparams: label: No such file or directory */
225 fprintf(stderr, "edparams: %s: %s\n", label, strerror(errno));
228 void fatal(char *label)
230 report(label);
231 exit(1);
234 void *alloc(void *m, size_t n)
236 m= m == nil ? malloc(n) : realloc(m, n);
237 if (m == nil) fatal("");
238 return m;
241 #define malloc(n) alloc(nil, n)
242 #define realloc(m, n) alloc(m, n)
244 #define mon2abs(addr) ((void *) (addr))
246 int rwsectors(int rw, void *addr, u32_t sec, int nsec)
248 ssize_t r;
249 size_t len= nsec * SECTOR_SIZE;
251 if (lseek(bootdev.device, sec * SECTOR_SIZE, SEEK_SET) == -1)
252 return errno;
254 if (rw == 0) {
255 r= read(bootdev.device, (char *) addr, len);
256 } else {
257 r= write(bootdev.device, (char *) addr, len);
259 if (r == -1) return errno;
260 if (r != len) return EIO;
261 return 0;
264 #define readsectors(a, s, n) rwsectors(0, (a), (s), (n))
265 #define writesectors(a, s, n) rwsectors(1, (a), (s), (n))
266 #define readerr(sec, err) (errno= (err), report(bootdev.name))
267 #define writerr(sec, err) (errno= (err), report(bootdev.name))
268 #define putch(c) putchar(c)
269 #define unix_err(err) strerror(err)
271 void readblock(off_t blk, char *buf, int block_size)
272 /* Read blocks for the rawfs package. */
274 if(!block_size) fatal("block_size 0");
275 errno= EIO;
276 if (lseek(bootdev.device, blk * block_size, SEEK_SET) == -1
277 || read(bootdev.device, buf, block_size) != block_size)
279 fatal(bootdev.name);
283 sig_atomic_t trapsig;
285 void trap(int sig)
287 trapsig= sig;
288 signal(sig, trap);
291 int escape(void)
293 if (trapsig == SIGINT) {
294 trapsig= 0;
295 return 1;
297 return 0;
300 static unsigned char unchar;
302 int getch(void)
304 unsigned char c;
306 fflush(stdout);
308 if (unchar != 0) {
309 c= unchar;
310 unchar= 0;
311 return c;
314 switch (read(0, &c, 1)) {
315 case -1:
316 if (errno != EINTR) fatal("");
317 return(ESC);
318 case 0:
319 if (istty) putch('\n');
320 exit(0);
321 default:
322 if (istty && c == termbuf.c_cc[VEOF]) {
323 putch('\n');
324 exit(0);
326 return c;
330 #define ungetch(c) ((void) (unchar = (c)))
332 #define get_tick() ((u32_t) time(nil))
333 #define clear_screen() printf("[clear]")
334 #define boot_device(device) printf("[boot %s]\n", device)
335 #define ctty(line) printf("[ctty %s]\n", line)
336 #define bootminix() (run_trailer() && printf("[boot]\n"))
337 #define off() printf("[off]")
339 #endif /* UNIX */
341 char *readline(void)
342 /* Read a line including a newline with echoing. */
344 char *line;
345 size_t i, z;
346 int c;
348 i= 0;
349 z= 20;
350 line= malloc(z * sizeof(char));
352 do {
353 c= getch();
355 if (strchr("\b\177\25\30", c) != nil) {
356 /* Backspace, DEL, ctrl-U, or ctrl-X. */
357 do {
358 if (i == 0) break;
359 printf("\b \b");
360 i--;
361 } while (c == '\25' || c == '\30');
362 } else
363 if (c < ' ' && c != '\n') {
364 putch('\7');
365 } else {
366 putch(c);
367 line[i++]= c;
368 if (i == z) {
369 z*= 2;
370 line= realloc(line, z * sizeof(char));
373 } while (c != '\n');
374 line[i]= 0;
375 return line;
378 int sugar(char *tok)
379 /* Recognize special tokens. */
381 return strchr("=(){};\n", tok[0]) != nil;
384 char *onetoken(char **aline)
385 /* Returns a string with one token for tokenize. */
387 char *line= *aline;
388 size_t n;
389 char *tok;
391 /* Skip spaces and runs of newlines. */
392 while (*line == ' ' || (*line == '\n' && line[1] == '\n')) line++;
394 *aline= line;
396 /* Don't do odd junk (nor the terminating 0!). */
397 if ((unsigned) *line < ' ' && *line != '\n') return nil;
399 if (*line == '(') {
400 /* Function argument, anything goes but () must match. */
401 int depth= 0;
403 while ((unsigned) *line >= ' ') {
404 if (*line == '(') depth++;
405 if (*line++ == ')' && --depth == 0) break;
407 } else
408 if (sugar(line)) {
409 /* Single character token. */
410 line++;
411 } else {
412 /* Multicharacter token. */
413 do line++; while ((unsigned) *line > ' ' && !sugar(line));
415 n= line - *aline;
416 tok= malloc((n + 1) * sizeof(char));
417 memcpy(tok, *aline, n);
418 tok[n]= 0;
419 if (tok[0] == '\n') tok[0]= ';'; /* ';' same as '\n' */
421 *aline= line;
422 return tok;
425 /* Typed commands form strings of tokens. */
427 typedef struct token {
428 struct token *next; /* Next in a command chain. */
429 char *token;
430 } token;
432 token **tokenize(token **acmds, char *line)
433 /* Takes a line apart to form tokens. The tokens are inserted into a command
434 * chain at *acmds. Tokenize returns a reference to where another line could
435 * be added. Tokenize looks at spaces as token separators, and recognizes only
436 * ';', '=', '{', '}', and '\n' as single character tokens. One token is
437 * formed from '(' and ')' with anything in between as long as more () match.
440 char *tok;
441 token *newcmd;
443 while ((tok= onetoken(&line)) != nil) {
444 newcmd= malloc(sizeof(*newcmd));
445 newcmd->token= tok;
446 newcmd->next= *acmds;
447 *acmds= newcmd;
448 acmds= &newcmd->next;
450 return acmds;
453 token *cmds; /* String of commands to execute. */
454 int err; /* Set on an error. */
456 char *poptoken(void)
457 /* Pop one token off the command chain. */
459 token *cmd= cmds;
460 char *tok= cmd->token;
462 cmds= cmd->next;
463 free(cmd);
465 return tok;
468 void voidtoken(void)
469 /* Remove one token from the command chain. */
471 free(poptoken());
474 void parse_code(char *code)
475 /* Tokenize a string of monitor code, making sure there is a delimiter. It is
476 * to be executed next. (Prepended to the current input.)
479 if (cmds != nil && cmds->token[0] != ';') (void) tokenize(&cmds, ";");
480 (void) tokenize(&cmds, code);
483 int interrupt(void)
484 /* Clean up after an ESC has been typed. */
486 if (escape()) {
487 printf("[ESC]\n");
488 err= 1;
489 return 1;
491 return 0;
494 #if BIOS
496 int activate;
498 struct biosdev {
499 char name[8];
500 int device, primary, secondary;
501 } bootdev, tmpdev;
503 int get_master(char *master, struct part_entry **table, u32_t pos)
504 /* Read a master boot sector and its partition table. */
506 int r, n;
507 struct part_entry *pe, **pt;
509 if ((r= readsectors(mon2abs(master), pos, 1)) != 0) return r;
511 pe= (struct part_entry *) (master + PART_TABLE_OFF);
512 for (pt= table; pt < table + NR_PARTITIONS; pt++) *pt= pe++;
514 /* DOS has the misguided idea that partition tables must be sorted. */
515 if (pos != 0) return 0; /* But only the primary. */
517 n= NR_PARTITIONS;
518 do {
519 for (pt= table; pt < table + NR_PARTITIONS-1; pt++) {
520 if (pt[0]->sysind == NO_PART
521 || pt[0]->lowsec > pt[1]->lowsec) {
522 pe= pt[0]; pt[0]= pt[1]; pt[1]= pe;
525 } while (--n > 0);
526 return 0;
529 void initialize(void)
531 char master[SECTOR_SIZE];
532 struct part_entry *table[NR_PARTITIONS];
533 int r, p;
534 u32_t masterpos;
535 char *argp;
537 /* Copy the boot program to the far end of low memory, this must be
538 * done to get out of the way of Minix, and to put the data area
539 * cleanly inside a 64K chunk if using BIOS I/O (no DMA problems).
541 u32_t oldaddr= caddr;
542 u32_t memend= mem[0].base + mem[0].size;
543 u32_t newaddr= (memend - runsize) & ~0x0000FL;
544 #if !DOS
545 u32_t dma64k= (memend - 1) & ~0x0FFFFL;
548 /* Check if data segment crosses a 64K boundary. */
549 if (newaddr + (daddr - caddr) < dma64k) {
550 newaddr= (dma64k - runsize) & ~0x0000FL;
552 #endif
554 /* If we were booted from CD, remember what device it was. */
555 if(cdbooted)
556 cddevice = device;
557 else
558 cddevice = 0xff; /* Invalid. */
560 /* Set the new caddr for relocate. */
561 caddr= newaddr;
563 /* Copy code and data. */
564 raw_copy(newaddr, oldaddr, runsize);
566 /* Make the copy running. */
567 relocate();
569 #if !DOS
571 /* Take the monitor out of the memory map if we have memory to spare,
572 * and also keep the BIOS data area safe (1.5K), plus a bit extra for
573 * where we may have to put a.out headers for older kernels.
575 if (mon_return = (mem[1].size > 512*1024L)) mem[0].size = newaddr;
576 mem[0].base += 2048;
577 mem[0].size -= 2048;
579 /* Find out what the boot device and partition was. */
580 bootdev.name[0]= 0;
581 bootdev.device= device;
582 bootdev.primary= -1;
583 bootdev.secondary= -1;
585 if (device < 0x80) {
586 /* Floppy. */
587 strcpy(bootdev.name, "fd0");
588 bootdev.name[2] += bootdev.device;
589 return;
592 /* Disk: Get the partition table from the very first sector, and
593 * determine the partition we booted from using the information from
594 * the booted partition entry as passed on by the bootstrap (rem_part).
595 * All we need from it is the partition offset.
597 raw_copy(mon2abs(&lowsec),
598 vec2abs(&rem_part) + offsetof(struct part_entry, lowsec),
599 sizeof(lowsec));
601 masterpos= 0; /* Master bootsector position. */
603 for (;;) {
604 /* Extract the partition table from the master boot sector. */
605 if ((r= get_master(master, table, masterpos)) != 0) {
606 readerr(masterpos, r); exit(1);
609 /* If we're a CD, we know what we want. */
610 if(device == cddevice) {
611 p = 1; /* We know this is the root FS. */
612 lowsec = table[p]->lowsec;
613 bootdev.primary = p;
614 break; /* Found! */
617 /* See if you can find "lowsec" back. */
618 for (p= 0; p < NR_PARTITIONS; p++) {
619 if (lowsec - table[p]->lowsec < table[p]->size) break;
622 if (lowsec == table[p]->lowsec) { /* Found! */
623 if (bootdev.primary < 0)
624 bootdev.primary= p;
625 else
626 bootdev.secondary= p;
627 break;
630 if (p == NR_PARTITIONS || bootdev.primary >= 0
631 || table[p]->sysind != MINIX_PART) {
632 /* The boot partition cannot be named, this only means
633 * that "bootdev" doesn't work.
635 bootdev.device= -1;
636 return;
639 /* See if the primary partition is subpartitioned. */
640 bootdev.primary= p;
641 masterpos= table[p]->lowsec;
644 if(device == cddevice) {
645 strcpy(bootdev.name, CDNAME);
646 } else {
647 strcpy(bootdev.name, "d0p0");
648 bootdev.name[1] += (device - 0x80);
649 bootdev.name[3] += bootdev.primary;
650 if (bootdev.secondary >= 0) {
651 strcat(bootdev.name, "s0");
652 bootdev.name[5] += bootdev.secondary;
656 /* Find out about the video hardware. */
657 raw_copy(mon2abs(&vid_port), VDU_CRT_BASE_ADDR, sizeof(vid_port));
658 if(vid_port == C_6845) {
659 vid_mem_base = COLOR_BASE;
660 vid_mem_size = COLOR_SIZE;
661 } else {
662 vid_mem_base = MONO_BASE;
663 vid_mem_size = MONO_SIZE;
666 if(get_video() >= 3)
667 vid_mem_size = EGA_SIZE;
669 #else /* DOS */
670 /* Take the monitor out of the memory map if we have memory to spare,
671 * note that only half our PSP is needed at the new place, the first
672 * half is to be kept in its place.
674 if (mem[1].size > 0) mem[0].size = newaddr + 0x80 - mem[0].base;
676 /* Parse the command line. */
677 argp= PSP + 0x81;
678 argp[PSP[0x80]]= 0;
679 while (between('\1', *argp, ' ')) argp++;
680 vdisk= argp;
681 while (!between('\0', *argp, ' ')) argp++;
682 while (between('\1', *argp, ' ')) *argp++= 0;
683 if (*vdisk == 0) {
684 printf("\nUsage: boot <vdisk> [commands ...]\n");
685 exit(1);
687 drun= *argp == 0 ? "main" : argp;
689 if ((r= dev_open()) != 0) {
690 printf("\n%s: Error %02x (%s)\n", vdisk, r, bios_err(r));
691 exit(1);
694 /* Find the active partition on the virtual disk. */
695 if ((r= get_master(master, table, 0)) != 0) {
696 readerr(0, r); exit(1);
699 strcpy(bootdev.name, "d0");
700 bootdev.primary= -1;
701 for (p= 0; p < NR_PARTITIONS; p++) {
702 if (table[p]->bootind != 0 && table[p]->sysind == MINIX_PART) {
703 bootdev.primary= p;
704 strcat(bootdev.name, "p0");
705 bootdev.name[3] += p;
706 lowsec= table[p]->lowsec;
707 break;
710 #endif /* DOS */
713 #endif /* BIOS */
715 /* Reserved names: */
716 enum resnames {
717 R_NULL, R_BOOT, R_CTTY, R_DELAY, R_ECHO, R_EXIT, R_HELP,
718 R_LS, R_MENU, R_OFF, R_SAVE, R_SET, R_TRAP, R_UNSET
721 char resnames[][6] = {
722 "", "boot", "ctty", "delay", "echo", "exit", "help",
723 "ls", "menu", "off", "save", "set", "trap", "unset",
726 /* Using this for all null strings saves a lot of memory. */
727 #define null (resnames[0])
729 enum resnames reserved(char *s)
730 /* Recognize reserved strings. */
732 enum resnames r;
734 for (r= R_BOOT; r <= R_UNSET; r++) {
735 if (strcmp(s, resnames[r]) == 0) return r;
737 return R_NULL;
740 void sfree(char *s)
741 /* Free a non-null string. */
743 if (s != nil && s != null) free(s);
746 char *copystr(char *s)
747 /* Copy a non-null string using malloc. */
749 char *c;
751 if (*s == 0) return null;
752 c= malloc((strlen(s) + 1) * sizeof(char));
753 strcpy(c, s);
754 return c;
757 int is_default(environment *e)
759 return (e->flags & E_SPECIAL) && e->defval == nil;
762 environment **searchenv(char *name)
764 environment **aenv= &env;
766 while (*aenv != nil && strcmp((*aenv)->name, name) != 0) {
767 aenv= &(*aenv)->next;
770 return aenv;
773 #define b_getenv(name) (*searchenv(name))
774 /* Return the environment *structure* belonging to name, or nil if not found. */
776 char *b_value(char *name)
777 /* The value of a variable. */
779 environment *e= b_getenv(name);
781 return e == nil || !(e->flags & E_VAR) ? nil : e->value;
784 char *b_body(char *name)
785 /* The value of a function. */
787 environment *e= b_getenv(name);
789 return e == nil || !(e->flags & E_FUNCTION) ? nil : e->value;
792 int b_setenv(int flags, char *name, char *arg, char *value)
793 /* Change the value of an environment variable. Returns the flags of the
794 * variable if you are not allowed to change it, 0 otherwise.
797 environment **aenv, *e;
799 if (*(aenv= searchenv(name)) == nil) {
800 if (reserved(name)) return E_RESERVED;
801 e= malloc(sizeof(*e));
802 e->name= copystr(name);
803 e->flags= flags;
804 e->defval= nil;
805 e->next= nil;
806 *aenv= e;
807 } else {
808 e= *aenv;
810 /* Don't change special variables to functions or vv. */
811 if (e->flags & E_SPECIAL
812 && (e->flags & E_FUNCTION) != (flags & E_FUNCTION)
813 ) return e->flags;
815 e->flags= (e->flags & E_STICKY) | flags;
816 if (is_default(e)) {
817 e->defval= e->value;
818 } else {
819 sfree(e->value);
821 sfree(e->arg);
823 e->arg= copystr(arg);
824 e->value= copystr(value);
826 return 0;
829 int b_setvar(int flags, char *name, char *value)
830 /* Set variable or simple function. */
832 int r;
834 if((r=b_setenv(flags, name, null, value))) {
835 return r;
838 return r;
841 void b_unset(char *name)
842 /* Remove a variable from the environment. A special variable is reset to
843 * its default value.
846 environment **aenv, *e;
848 if ((e= *(aenv= searchenv(name))) == nil) return;
850 if (e->flags & E_SPECIAL) {
851 if (e->defval != nil) {
852 sfree(e->arg);
853 e->arg= null;
854 sfree(e->value);
855 e->value= e->defval;
856 e->defval= nil;
858 } else {
859 sfree(e->name);
860 sfree(e->arg);
861 sfree(e->value);
862 *aenv= e->next;
863 free(e);
867 long a2l(char *a)
868 /* Cheap atol(). */
870 int sign= 1;
871 long n= 0;
873 if (*a == '-') { sign= -1; a++; }
875 while (between('0', *a, '9')) n= n * 10 + (*a++ - '0');
877 return sign * n;
880 char *ul2a(u32_t n, unsigned b)
881 /* Transform a long number to ascii at base b, (b >= 8). */
883 static char num[(CHAR_BIT * sizeof(n) + 2) / 3 + 1];
884 char *a= arraylimit(num) - 1;
885 static char hex[16] = "0123456789ABCDEF";
887 do *--a = hex[(int) (n % b)]; while ((n/= b) > 0);
888 return a;
891 char *ul2a10(u32_t n)
892 /* Transform a long number to ascii at base 10. */
894 return ul2a(n, 10);
897 unsigned a2x(char *a)
898 /* Ascii to hex. */
900 unsigned n= 0;
901 int c;
903 for (;;) {
904 c= *a;
905 if (between('0', c, '9')) c= c - '0' + 0x0;
906 else
907 if (between('A', c, 'F')) c= c - 'A' + 0xA;
908 else
909 if (between('a', c, 'f')) c= c - 'a' + 0xa;
910 else
911 break;
912 n= (n<<4) | c;
913 a++;
915 return n;
918 void get_parameters(void)
920 char params[SECTOR_SIZE + 1];
921 token **acmds;
922 int r, bus, processor;
923 memory *mp;
924 static char bus_type[][4] = {
925 "xt", "at", "mca"
927 static char vid_type[][4] = {
928 "mda", "cga", "ega", "ega", "vga", "vga"
930 static char vid_chrome[][6] = {
931 "mono", "color"
934 /* Variables that Minix needs: */
935 b_setvar(E_SPECIAL|E_VAR|E_DEV, "rootdev", "ram");
936 b_setvar(E_SPECIAL|E_VAR|E_DEV, "ramimagedev", "bootdev");
937 b_setvar(E_SPECIAL|E_VAR, "ramsize", "0");
938 #define STRINGIT2(x) #x
939 #define STRINGIT1(x) STRINGIT2(x)
940 b_setvar(E_SPECIAL|E_VAR, "hz", STRINGIT1(DEFAULT_HZ));
941 #if BIOS
942 processor = getprocessor();
943 if(processor == 1586) processor = 686;
944 b_setvar(E_SPECIAL|E_VAR, "processor", ul2a10(processor));
945 b_setvar(E_SPECIAL|E_VAR, "bus", bus_type[get_bus()]);
946 b_setvar(E_SPECIAL|E_VAR, "video", vid_type[get_video()]);
947 b_setvar(E_SPECIAL|E_VAR, "chrome", vid_chrome[get_video() & 1]);
948 params[0]= 0;
949 for (mp= mem; mp < arraylimit(mem); mp++) {
950 if (mp->size == 0) continue;
951 if (params[0] != 0) strcat(params, ",");
952 strcat(params, ul2a(mp->base, 0x10));
953 strcat(params, ":");
954 strcat(params, ul2a(mp->size, 0x10));
956 b_setvar(E_SPECIAL|E_VAR, "memory", params);
958 #if DOS
959 b_setvar(E_SPECIAL|E_VAR, "dosfile-d0", vdisk);
960 #endif
962 #endif
963 #if UNIX
964 b_setvar(E_SPECIAL|E_VAR, "processor", "?");
965 b_setvar(E_SPECIAL|E_VAR, "bus", "?");
966 b_setvar(E_SPECIAL|E_VAR, "video", "?");
967 b_setvar(E_SPECIAL|E_VAR, "chrome", "?");
968 b_setvar(E_SPECIAL|E_VAR, "memory", "?");
969 b_setvar(E_SPECIAL|E_VAR, "c0", "?");
970 #endif
972 /* Variables boot needs: */
973 b_setvar(E_SPECIAL|E_VAR, "image", "boot/image");
974 b_setvar(E_SPECIAL|E_FUNCTION, "leader",
975 "echo --- Welcome to MINIX 3. This is the boot monitor. ---\\n");
976 b_setvar(E_SPECIAL|E_FUNCTION, "main", "menu");
977 b_setvar(E_SPECIAL|E_FUNCTION, "trailer", "");
979 /* Default hidden menu function: */
980 b_setenv(E_RESERVED|E_FUNCTION, null, "=,Start MINIX", "boot");
982 /* Tokenize bootparams sector. */
983 if ((r= readsectors(mon2abs(params), lowsec+PARAMSEC, 1)) != 0) {
984 readerr(lowsec+PARAMSEC, r);
985 exit(1);
987 params[SECTOR_SIZE]= 0;
988 acmds= tokenize(&cmds, params);
990 /* Stuff the default action into the command chain. */
991 #if UNIX
992 (void) tokenize(acmds, ":;");
993 #elif DOS
994 (void) tokenize(tokenize(acmds, ":;leader;"), drun);
995 #else /* BIOS */
996 (void) tokenize(acmds, ":;leader;main");
997 #endif
1000 char *addptr;
1002 void addparm(char *n)
1004 while (*n != 0 && *addptr != 0) *addptr++ = *n++;
1007 void save_parameters(void)
1008 /* Save nondefault environment variables to the bootparams sector. */
1010 environment *e;
1011 char params[SECTOR_SIZE + 1];
1012 int r;
1014 /* Default filling: */
1015 memset(params, '\n', SECTOR_SIZE);
1017 /* Don't touch the 0! */
1018 params[SECTOR_SIZE]= 0;
1019 addptr= params;
1021 for (e= env; e != nil; e= e->next) {
1022 if (e->flags & E_RESERVED || is_default(e)) continue;
1024 addparm(e->name);
1025 if (e->flags & E_FUNCTION) {
1026 addparm("(");
1027 addparm(e->arg);
1028 addparm(")");
1029 } else {
1030 addparm((e->flags & (E_DEV|E_SPECIAL)) != E_DEV
1031 ? "=" : "=d ");
1033 addparm(e->value);
1034 if (*addptr == 0) {
1035 printf("The environment is too big\n");
1036 return;
1038 *addptr++= '\n';
1041 /* Save the parameters on disk. */
1042 if ((r= writesectors(mon2abs(params), lowsec+PARAMSEC, 1)) != 0) {
1043 writerr(lowsec+PARAMSEC, r);
1044 printf("Can't save environment\n");
1048 void show_env(void)
1049 /* Show the environment settings. */
1051 environment *e;
1052 unsigned more= 0;
1053 int c;
1055 for (e= env; e != nil; e= e->next) {
1056 if (e->flags & E_RESERVED) continue;
1057 if (!istty && is_default(e)) continue;
1059 if (e->flags & E_FUNCTION) {
1060 printf("%s(%s) %s\n", e->name, e->arg, e->value);
1061 } else {
1062 printf(is_default(e) ? "%s = (%s)\n" : "%s = %s\n",
1063 e->name, e->value);
1066 if (e->next != nil && istty && ++more % 20 == 0) {
1067 printf("More? ");
1068 c= getch();
1069 if (c == ESC || c > ' ') {
1070 putch('\n');
1071 if (c > ' ') ungetch(c);
1072 break;
1074 printf("\b\b\b\b\b\b");
1079 int numprefix(char *s, char **ps)
1080 /* True iff s is a string of digits. *ps will be set to the first nondigit
1081 * if non-nil, otherwise the string should end.
1084 char *n= s;
1086 while (between('0', *n, '9')) n++;
1088 if (n == s) return 0;
1090 if (ps == nil) return *n == 0;
1092 *ps= n;
1093 return 1;
1096 int numeric(char *s)
1098 return numprefix(s, (char **) nil);
1101 #if BIOS
1103 /* Device numbers of standard MINIX devices. */
1104 #define DEV_FD0 0x0200
1105 static dev_t dev_cNd0[] = { 0x0300, 0x0800, 0x0A00, 0x0C00, 0x1000 };
1106 #define minor_p0s0 128
1108 static int block_size;
1110 dev_t name2dev(char *name)
1111 /* Translate, say, /dev/c0d0p2 to a device number. If the name can't be
1112 * found on the boot device, then do some guesswork. The global structure
1113 * "tmpdev" will be filled in based on the name, so that "boot d1p0" knows
1114 * what device to boot without interpreting device numbers.
1117 dev_t dev;
1118 ino_t ino;
1119 int drive;
1120 struct stat st;
1121 char *n, *s;
1123 /* "boot *d0p2" means: make partition 2 active before you boot it. */
1124 if ((activate= (name[0] == '*'))) name++;
1126 /* The special name "bootdev" must be translated to the boot device. */
1127 if (strcmp(name, "bootdev") == 0) {
1128 if (bootdev.device == -1) {
1129 printf("The boot device could not be named\n");
1130 errno= 0;
1131 return -1;
1133 name= bootdev.name;
1136 /* If our boot device doesn't have a file system, or we want to know
1137 * what a name means for the BIOS, then we need to interpret the
1138 * device name ourselves: "fd" = floppy, "c0d0" = hard disk, etc.
1140 tmpdev.device= tmpdev.primary= tmpdev.secondary= -1;
1141 dev= -1;
1142 n= name;
1143 if (strncmp(n, "/dev/", 5) == 0) n+= 5;
1145 if (strcmp(n, "ram") == 0 || strcmp(n, CDNAME) == 0) {
1146 dev= DEV_RAM;
1147 } else
1148 if (n[0] == 'f' && n[1] == 'd' && numeric(n+2)) {
1149 /* Floppy. */
1150 tmpdev.device= a2l(n+2);
1151 dev= DEV_FD0 + tmpdev.device;
1152 } else
1153 if ((n[0] == 'h' || n[0] == 's') && n[1] == 'd' && numprefix(n+2, &s)
1154 && (*s == 0 || (between('a', *s, 'd') && s[1] == 0))
1156 /* Old style hard disk (backwards compatibility.) */
1157 dev= a2l(n+2);
1158 tmpdev.device= dev / (1 + NR_PARTITIONS);
1159 tmpdev.primary= (dev % (1 + NR_PARTITIONS)) - 1;
1160 if (*s != 0) {
1161 /* Subpartition. */
1162 tmpdev.secondary= *s - 'a';
1163 dev= minor_p0s0
1164 + (tmpdev.device * NR_PARTITIONS
1165 + tmpdev.primary) * NR_PARTITIONS
1166 + tmpdev.secondary;
1168 tmpdev.device+= 0x80;
1169 dev+= n[0] == 'h' ? dev_cNd0[0] : dev_cNd0[2];
1170 } else {
1171 /* Hard disk. */
1172 int ctrlr= 0;
1174 if (n[0] == 'c' && between('0', n[1], '4')) {
1175 ctrlr= (n[1] - '0');
1176 tmpdev.device= 0;
1177 n+= 2;
1179 if (n[0] == 'd' && between('0', n[1], '7')) {
1180 tmpdev.device= (n[1] - '0');
1181 n+= 2;
1182 if (n[0] == 'p' && between('0', n[1], '3')) {
1183 tmpdev.primary= (n[1] - '0');
1184 n+= 2;
1185 if (n[0] == 's' && between('0', n[1], '3')) {
1186 tmpdev.secondary= (n[1] - '0');
1187 n+= 2;
1191 if (*n == 0) {
1192 dev= dev_cNd0[ctrlr];
1193 if (tmpdev.secondary < 0) {
1194 dev += tmpdev.device * (NR_PARTITIONS+1)
1195 + (tmpdev.primary + 1);
1196 } else {
1197 dev += minor_p0s0
1198 + (tmpdev.device * NR_PARTITIONS
1199 + tmpdev.primary) * NR_PARTITIONS
1200 + tmpdev.secondary;
1202 tmpdev.device+= 0x80;
1206 /* Look the name up on the boot device for the UNIX device number. */
1207 if (fsok == -1) fsok= r_super(&block_size) != 0;
1208 if (fsok) {
1209 /* The current working directory is "/dev". */
1210 ino= r_lookup(r_lookup(ROOT_INO, "dev"), name);
1212 if (ino != 0) {
1213 /* Name has been found, extract the device number. */
1214 r_stat(ino, &st);
1215 if (!S_ISBLK(st.st_mode)) {
1216 printf("%s is not a block device\n", name);
1217 errno= 0;
1218 return (dev_t) -1;
1220 dev= st.st_rdev;
1224 if (tmpdev.primary < 0) activate= 0; /* Careful now! */
1226 if (dev == -1) {
1227 printf("Can't recognize '%s' as a device\n", name);
1228 errno= 0;
1230 return dev;
1233 #if DEBUG
1234 static void apm_perror(char *label, u16_t ax)
1236 unsigned ah;
1237 char *str;
1239 ah= (ax >> 8);
1240 switch(ah)
1242 case 0x01: str= "APM functionality disabled"; break;
1243 case 0x03: str= "interface not connected"; break;
1244 case 0x09: str= "unrecognized device ID"; break;
1245 case 0x0A: str= "parameter value out of range"; break;
1246 case 0x0B: str= "interface not engaged"; break;
1247 case 0x60: str= "unable to enter requested state"; break;
1248 case 0x86: str= "APM not present"; break;
1249 default: printf("%s: error 0x%02x\n", label, ah); return;
1251 printf("%s: %s\n", label, str);
1254 #define apm_printf printf
1255 #else
1256 #define apm_perror(label, ax) ((void)0)
1257 #define apm_printf
1258 #endif
1260 static void off(void)
1262 bios_env_t be;
1263 unsigned al, ah;
1265 /* Try to switch off the system. Print diagnostic information
1266 * that can be useful if the operation fails.
1269 be.ax= 0x5300; /* APM, Installation check */
1270 be.bx= 0; /* Device, APM BIOS */
1271 int15(&be);
1272 if (be.flags & FL_CARRY)
1274 apm_perror("APM installation check failed", be.ax);
1275 return;
1277 if (be.bx != (('P' << 8) | 'M'))
1279 apm_printf("APM signature not found (got 0x%04x)\n", be.bx);
1280 return;
1283 ah= be.ax >> 8;
1284 if (ah > 9)
1285 ah= (ah >> 4)*10 + (ah & 0xf);
1286 al= be.ax & 0xff;
1287 if (al > 9)
1288 al= (al >> 4)*10 + (al & 0xf);
1289 apm_printf("APM version %u.%u%s%s%s%s%s\n",
1290 ah, al,
1291 (be.cx & 0x1) ? ", 16-bit PM" : "",
1292 (be.cx & 0x2) ? ", 32-bit PM" : "",
1293 (be.cx & 0x4) ? ", CPU-Idle" : "",
1294 (be.cx & 0x8) ? ", APM-disabled" : "",
1295 (be.cx & 0x10) ? ", APM-disengaged" : "");
1297 /* Connect */
1298 be.ax= 0x5301; /* APM, Real mode interface connect */
1299 be.bx= 0x0000; /* APM BIOS */
1300 int15(&be);
1301 if (be.flags & FL_CARRY)
1303 apm_perror("APM real mode connect failed", be.ax);
1304 return;
1307 /* Ask for a seat upgrade */
1308 be.ax= 0x530e; /* APM, Driver Version */
1309 be.bx= 0x0000; /* BIOS */
1310 be.cx= 0x0102; /* version 1.2 */
1311 int15(&be);
1312 if (be.flags & FL_CARRY)
1314 apm_perror("Set driver version failed", be.ax);
1315 goto disco;
1318 /* Is this version really worth reporting. Well, if the system
1319 * does switch off, you won't see it anyway.
1321 ah= be.ax >> 8;
1322 if (ah > 9)
1323 ah= (ah >> 4)*10 + (ah & 0xf);
1324 al= be.ax & 0xff;
1325 if (al > 9)
1326 al= (al >> 4)*10 + (al & 0xf);
1327 apm_printf("Got APM connection version %u.%u\n", ah, al);
1329 /* Enable */
1330 be.ax= 0x5308; /* APM, Enable/disable power management */
1331 be.bx= 0x0001; /* All device managed by APM BIOS */
1332 #if 0
1333 /* For old APM 1.0 systems, we need 0xffff. Assume that those
1334 * systems do not exist.
1336 be.bx= 0xffff; /* All device managed by APM BIOS (compat) */
1337 #endif
1338 be.cx= 0x0001; /* Enable power management */
1339 int15(&be);
1340 if (be.flags & FL_CARRY)
1342 apm_perror("Enable power management failed", be.ax);
1343 goto disco;
1346 /* Off */
1347 be.ax= 0x5307; /* APM, Set Power State */
1348 be.bx= 0x0001; /* All devices managed by APM */
1349 be.cx= 0x0003; /* Off */
1350 int15(&be);
1351 if (be.flags & FL_CARRY)
1353 apm_perror("Set power state failed", be.ax);
1354 goto disco;
1357 apm_printf("Power off sequence successfully completed.\n\n");
1358 apm_printf("Ha, ha, just kidding!\n");
1360 disco:
1361 /* Disconnect */
1362 be.ax= 0x5304; /* APM, interface disconnect */
1363 be.bx= 0x0000; /* APM BIOS */
1364 int15(&be);
1365 if (be.flags & FL_CARRY)
1367 apm_perror("APM interface disconnect failed", be.ax);
1368 return;
1372 #if !DOS
1373 #define B_NOSIG -1 /* "No signature" error code. */
1375 int exec_bootstrap(void)
1376 /* Load boot sector from the disk or floppy described by tmpdev and execute it.
1379 int r, n, dirty= 0;
1380 char master[SECTOR_SIZE];
1381 struct part_entry *table[NR_PARTITIONS], dummy, *active= &dummy;
1382 u32_t masterpos;
1384 active->lowsec= 0;
1386 /* Select a partition table entry. */
1387 while (tmpdev.primary >= 0) {
1388 masterpos= active->lowsec;
1390 if ((r= get_master(master, table, masterpos)) != 0) return r;
1392 active= table[tmpdev.primary];
1394 /* How does one check a partition table entry? */
1395 if (active->sysind == NO_PART) return B_NOSIG;
1397 tmpdev.primary= tmpdev.secondary;
1398 tmpdev.secondary= -1;
1401 if (activate && !active->bootind) {
1402 for (n= 0; n < NR_PARTITIONS; n++) table[n]->bootind= 0;
1403 active->bootind= ACTIVE_FLAG;
1404 dirty= 1;
1407 /* Read the boot sector. */
1408 if ((r= readsectors(BOOTPOS, active->lowsec, 1)) != 0) return r;
1410 /* Check signature word. */
1411 if (get_word(BOOTPOS+SIGNATOFF) != SIGNATURE) return B_NOSIG;
1413 /* Write the partition table if a member must be made active. */
1414 if (dirty && (r= writesectors(mon2abs(master), masterpos, 1)) != 0)
1415 return r;
1417 bootstrap(device, active);
1418 return 0;
1421 void boot_device(char *devname)
1422 /* Boot the device named by devname. */
1424 dev_t dev= name2dev(devname);
1425 int save_dev= device;
1426 int r;
1427 char *err;
1429 if (tmpdev.device < 0) {
1430 if (dev != -1) printf("Can't boot from %s\n", devname);
1431 return;
1434 /* Change current device and try to load and execute its bootstrap. */
1435 device= tmpdev.device;
1437 if ((r= dev_open()) == 0) r= exec_bootstrap();
1439 err= r == B_NOSIG ? "Not bootable" : bios_err(r);
1440 printf("Can't boot %s: %s\n", devname, err);
1442 /* Restore boot device setting. */
1443 device= save_dev;
1444 (void) dev_open();
1447 void ctty(char *line)
1449 if (line == nil) {
1450 serial_line = -1;
1451 } else if (between('0', line[0], '3') && line[1] == 0) {
1452 serial_line = line[0] - '0';
1453 } else {
1454 printf("Bad serial line number: %s\n", line);
1455 return;
1457 serial_init(serial_line);
1460 #else /* DOS */
1462 void boot_device(char *devname)
1463 /* No booting of other devices under DOS. */
1465 printf("Can't boot devices under DOS\n");
1468 void ctty(char *line)
1469 /* Don't know how to handle serial lines under DOS. */
1471 printf("No serial line support under DOS\n");
1474 #endif /* DOS */
1475 #endif /* BIOS */
1477 void ls(char *dir)
1478 /* List the contents of a directory. */
1480 ino_t ino;
1481 struct stat st;
1482 char name[NAME_MAX+1];
1484 if (fsok == -1) fsok= r_super(&block_size) != 0;
1485 if (!fsok) return;
1487 /* (,) construct because r_stat returns void */
1488 if ((ino= r_lookup(ROOT_INO, dir)) == 0 ||
1489 (r_stat(ino, &st), r_readdir(name)) == -1)
1491 printf("ls: %s: %s\n", dir, unix_err(errno));
1492 return;
1494 (void) r_readdir(name); /* Skip ".." too. */
1496 while ((ino= r_readdir(name)) != 0) printf("%s/%s\n", dir, name);
1499 u32_t milli_time(void)
1501 return get_tick() * MSEC_PER_TICK;
1504 u32_t milli_since(u32_t base)
1506 return (milli_time() + (TICKS_PER_DAY*MSEC_PER_TICK) - base)
1507 % (TICKS_PER_DAY*MSEC_PER_TICK);
1510 char *Thandler;
1511 u32_t Tbase, Tcount;
1513 void unschedule(void)
1514 /* Invalidate a waiting command. */
1516 alarm(0);
1518 if (Thandler != nil) {
1519 free(Thandler);
1520 Thandler= nil;
1524 void schedule(long msec, char *cmd)
1525 /* Schedule command at a certain time from now. */
1527 unschedule();
1528 Thandler= cmd;
1529 Tbase= milli_time();
1530 Tcount= msec;
1531 alarm(1);
1534 int expired(void)
1535 /* Check if the timer expired for getch(). */
1537 return (Thandler != nil && milli_since(Tbase) >= Tcount);
1540 void delay(char *msec)
1541 /* Delay for a given time. */
1543 u32_t base, count;
1545 if ((count= a2l(msec)) == 0) return;
1546 base= milli_time();
1548 alarm(1);
1550 do {
1551 pause();
1552 } while (!interrupt() && !expired() && milli_since(base) < count);
1555 enum whatfun { NOFUN, SELECT, DEFFUN, USERFUN } menufun(environment *e)
1557 if (!(e->flags & E_FUNCTION) || e->arg[0] == 0) return NOFUN;
1558 if (e->arg[1] != ',') return SELECT;
1559 return e->flags & E_RESERVED ? DEFFUN : USERFUN;
1562 void menu(void)
1563 /* By default: Show a simple menu.
1564 * Multiple kernels/images: Show extra selection options.
1565 * User defined function: Kill the defaults and show these.
1566 * Wait for a keypress and execute the given function.
1569 int c, def= 1;
1570 char *choice= nil;
1571 environment *e;
1573 /* Just a default menu? */
1574 for (e= env; e != nil; e= e->next) if (menufun(e) == USERFUN) def= 0;
1576 printf("\nHit a key as follows:\n\n");
1578 /* Show the choices. */
1579 for (e= env; e != nil; e= e->next) {
1580 switch (menufun(e)) {
1581 case DEFFUN:
1582 if (!def) break;
1583 /*FALL THROUGH*/
1584 case USERFUN:
1585 printf(" %c %s\n", e->arg[0], e->arg+2);
1586 break;
1587 case SELECT:
1588 printf(" %c Select %s kernel\n", e->arg[0],e->name);
1589 break;
1590 default:;
1594 /* Wait for a keypress. */
1595 do {
1596 c= getch();
1597 if (interrupt() || expired()) return;
1599 unschedule();
1601 for (e= env; e != nil; e= e->next) {
1602 switch (menufun(e)) {
1603 case DEFFUN:
1604 if (!def) break;
1605 case USERFUN:
1606 case SELECT:
1607 if (c == e->arg[0]) choice= e->value;
1610 } while (choice == nil);
1612 /* Execute the chosen function. */
1613 printf("%c\n", c);
1614 (void) tokenize(&cmds, choice);
1617 void help(void)
1618 /* Not everyone is a rocket scientist. */
1620 struct help {
1621 char *thing;
1622 char *help;
1623 } *pi;
1624 static struct help info[] = {
1625 { nil, "Names:" },
1626 { "rootdev", "Root device" },
1627 { "ramimagedev", "Device to use as RAM disk image " },
1628 { "ramsize", "RAM disk size (if no image device) " },
1629 { "bootdev", "Special name for the boot device" },
1630 { "fd0, d0p2, c0d0p1s0", "Devices (as in /dev)" },
1631 { "image", "Name of the boot image to use" },
1632 { "main", "Startup function" },
1633 { "bootdelay", "Delay in msec after loading image" },
1634 { nil, "Commands:" },
1635 { "name = [device] value", "Set environment variable" },
1636 { "name() { ... }", "Define function" },
1637 { "name(key,text) { ... }",
1638 "A menu option like: minix(=,Start MINIX) {boot}" },
1639 { "name", "Call function" },
1640 { "boot [device]", "Boot Minix or another O.S." },
1641 { "ctty [line]", "Duplicate to serial line" },
1642 { "delay [msec]", "Delay (500 msec default)" },
1643 { "echo word ...", "Display the words" },
1644 { "ls [directory]", "List contents of directory" },
1645 { "menu", "Show menu and choose menu option" },
1646 { "save / set", "Save or show environment" },
1647 { "trap msec command", "Schedule command " },
1648 { "unset name ...", "Unset variable or set to default" },
1649 { "exit / off", "Exit the Monitor / Power off" },
1652 for (pi= info; pi < arraylimit(info); pi++) {
1653 if (pi->thing != nil) printf(" %-24s- ", pi->thing);
1654 printf("%s\n", pi->help);
1658 void execute(void)
1659 /* Get one command from the command chain and execute it. */
1661 token *second, *third, *fourth, *sep;
1662 char *name;
1663 enum resnames res;
1664 size_t n= 0;
1666 if (err) {
1667 /* An error occured, stop interpreting. */
1668 while (cmds != nil) voidtoken();
1669 return;
1672 if (expired()) { /* Timer expired? */
1673 parse_code(Thandler);
1674 unschedule();
1677 /* There must be a separator lurking somewhere. */
1678 for (sep= cmds; sep != nil && sep->token[0] != ';'; sep= sep->next) n++;
1680 name= cmds->token;
1681 res= reserved(name);
1682 if ((second= cmds->next) != nil
1683 && (third= second->next) != nil)
1684 fourth= third->next;
1686 /* Null command? */
1687 if (n == 0) {
1688 voidtoken();
1689 return;
1690 } else
1691 /* name = [device] value? */
1692 if ((n == 3 || n == 4)
1693 && !sugar(name)
1694 && second->token[0] == '='
1695 && !sugar(third->token)
1696 && (n == 3 || (n == 4 && third->token[0] == 'd'
1697 && !sugar(fourth->token)
1698 ))) {
1699 char *value= third->token;
1700 int flags= E_VAR;
1702 if (n == 4) { value= fourth->token; flags|= E_DEV; }
1704 if ((flags= b_setvar(flags, name, value)) != 0) {
1705 printf("%s is a %s\n", name,
1706 flags & E_RESERVED ? "reserved word" :
1707 "special function");
1708 err= 1;
1710 while (cmds != sep) voidtoken();
1711 return;
1712 } else
1713 /* name '(arg)' ... ? */
1714 if (n >= 3
1715 && !sugar(name)
1716 && second->token[0] == '('
1718 token *fun;
1719 int c, flags, depth;
1720 char *body;
1721 size_t len;
1723 sep= fun= third;
1724 depth= 0;
1725 len= 1;
1726 while (sep != nil) {
1727 if ((c= sep->token[0]) == ';' && depth == 0) break;
1728 len+= strlen(sep->token) + 1;
1729 sep= sep->next;
1730 if (c == '{') depth++;
1731 if (c == '}' && --depth == 0) break;
1734 body= malloc(len * sizeof(char));
1735 *body= 0;
1737 while (fun != sep) {
1738 strcat(body, fun->token);
1739 if (!sugar(fun->token)
1740 && !sugar(fun->next->token)
1741 ) strcat(body, " ");
1742 fun= fun->next;
1744 second->token[strlen(second->token)-1]= 0;
1746 if (depth != 0) {
1747 printf("Missing '}'\n");
1748 err= 1;
1749 } else
1750 if ((flags= b_setenv(E_FUNCTION, name,
1751 second->token+1, body)) != 0) {
1752 printf("%s is a %s\n", name,
1753 flags & E_RESERVED ? "reserved word" :
1754 "special variable");
1755 err= 1;
1757 while (cmds != sep) voidtoken();
1758 free(body);
1759 return;
1760 } else
1761 /* Grouping? */
1762 if (name[0] == '{') {
1763 token **acmds= &cmds->next;
1764 char *t;
1765 int depth= 1;
1767 /* Find and remove matching '}' */
1768 depth= 1;
1769 while (*acmds != nil) {
1770 t= (*acmds)->token;
1771 if (t[0] == '{') depth++;
1772 if (t[0] == '}' && --depth == 0) { t[0]= ';'; break; }
1773 acmds= &(*acmds)->next;
1775 voidtoken();
1776 return;
1777 } else
1778 /* Command coming up, check if ESC typed. */
1779 if (interrupt()) {
1780 return;
1781 } else
1782 /* unset name ..., echo word ...? */
1783 if (n >= 1 && (res == R_UNSET || res == R_ECHO)) {
1784 char *arg= poptoken(), *p;
1786 for (;;) {
1787 free(arg);
1788 if (cmds == sep) break;
1789 arg= poptoken();
1790 if (res == R_UNSET) { /* unset arg */
1791 b_unset(arg);
1792 } else { /* echo arg */
1793 p= arg;
1794 while (*p != 0) {
1795 if (*p != '\\') {
1796 putch(*p);
1797 } else
1798 switch (*++p) {
1799 case 0:
1800 if (cmds == sep) return;
1801 continue;
1802 case 'n':
1803 putch('\n');
1804 break;
1805 case 'v':
1806 printf(version);
1807 break;
1808 case 'c':
1809 clear_screen();
1810 break;
1811 case 'w':
1812 for (;;) {
1813 if (interrupt())
1814 return;
1815 if (getch() == '\n')
1816 break;
1818 break;
1819 default:
1820 putch(*p);
1822 p++;
1824 putch(cmds != sep ? ' ' : '\n');
1827 return;
1828 } else
1829 /* boot -opts? */
1830 if (n == 2 && res == R_BOOT && second->token[0] == '-') {
1831 static char optsvar[]= "bootopts";
1832 (void) b_setvar(E_VAR, optsvar, second->token);
1833 voidtoken();
1834 voidtoken();
1835 bootminix();
1836 b_unset(optsvar);
1837 return;
1838 } else
1839 /* boot device, ls dir, delay msec? */
1840 if (n == 2 && (res == R_BOOT || res == R_CTTY
1841 || res == R_DELAY || res == R_LS)
1843 if (res == R_BOOT) boot_device(second->token);
1844 if (res == R_CTTY) ctty(second->token);
1845 if (res == R_DELAY) delay(second->token);
1846 if (res == R_LS) ls(second->token);
1847 voidtoken();
1848 voidtoken();
1849 return;
1850 } else
1851 /* trap msec command? */
1852 if (n == 3 && res == R_TRAP && numeric(second->token)) {
1853 long msec= a2l(second->token);
1855 voidtoken();
1856 voidtoken();
1857 schedule(msec, poptoken());
1858 return;
1859 } else
1860 /* Simple command. */
1861 if (n == 1) {
1862 char *body;
1863 int ok= 0;
1865 name= poptoken();
1867 switch (res) {
1868 case R_BOOT: bootminix(); ok= 1; break;
1869 case R_DELAY: delay("500"); ok= 1; break;
1870 case R_LS: ls(null); ok= 1; break;
1871 case R_MENU: menu(); ok= 1; break;
1872 case R_SAVE: save_parameters(); ok= 1;break;
1873 case R_SET: show_env(); ok= 1; break;
1874 case R_HELP: help(); ok= 1; break;
1875 case R_EXIT: exit(0);
1876 case R_OFF: off(); ok= 1; break;
1877 case R_CTTY: ctty(nil); ok= 1; break;
1880 /* Command to check bootparams: */
1881 if (strcmp(name, ":") == 0) ok= 1;
1883 /* User defined function. */
1884 if (!ok && (body= b_body(name)) != nil) {
1885 (void) tokenize(&cmds, body);
1886 ok= 1;
1888 if (!ok) printf("%s: unknown function", name);
1889 free(name);
1890 if (ok) return;
1891 } else {
1892 /* Syntax error. */
1893 printf("Can't parse:");
1894 while (cmds != sep) {
1895 printf(" %s", cmds->token); voidtoken();
1899 /* Getting here means that the command is not understood. */
1900 printf("\nTry 'help'\n");
1901 err= 1;
1904 int run_trailer(void)
1905 /* Run the trailer function between loading Minix and handing control to it.
1906 * Return true iff there was no error.
1909 token *save_cmds= cmds;
1911 cmds= nil;
1912 (void) tokenize(&cmds, "trailer");
1913 while (cmds != nil) execute();
1914 cmds= save_cmds;
1915 return !err;
1918 void monitor(void)
1919 /* Read a line and tokenize it. */
1921 char *line;
1923 unschedule(); /* Kill a trap. */
1924 err= 0; /* Clear error state. */
1926 if (istty) printf("%s>", bootdev.name);
1927 line= readline();
1928 (void) tokenize(&cmds, line);
1929 free(line);
1930 (void) escape(); /* Forget if ESC typed. */
1933 #if BIOS
1935 void boot(void)
1936 /* Load Minix and start it, among other things. */
1938 /* Initialize tables. */
1939 initialize();
1941 /* Get environment variables from the parameter sector. */
1942 get_parameters();
1944 while (1) {
1945 /* While there are commands, execute them! */
1947 while (cmds != nil) execute();
1949 /* The "monitor" is just a "read one command" thing. */
1950 monitor();
1953 #endif /* BIOS */
1955 #if UNIX
1957 void main(int argc, char **argv)
1958 /* Do not load or start anything, just edit parameters. */
1960 int i;
1961 char bootcode[SECTOR_SIZE];
1962 struct termios rawterm;
1964 istty= (argc <= 2 && tcgetattr(0, &termbuf) == 0);
1966 if (argc < 2) {
1967 fprintf(stderr, "Usage: edparams device [command ...]\n");
1968 exit(1);
1971 /* Go over the arguments, changing control characters to spaces. */
1972 for (i= 2; i < argc; i++) {
1973 char *p;
1975 for (p= argv[i]; *p != 0; p++) {
1976 if ((unsigned) *p < ' ' && *p != '\n') *p= ' ';
1980 bootdev.name= argv[1];
1981 if (strncmp(bootdev.name, "/dev/", 5) == 0) bootdev.name+= 5;
1982 if ((bootdev.device= open(argv[1], O_RDWR, 0666)) < 0)
1983 fatal(bootdev.name);
1985 /* Check if it is a bootable Minix device. */
1986 if (readsectors(mon2abs(bootcode), lowsec, 1) != 0) {
1987 fprintf(stderr, "edparams: %s: not a bootable Minix device\n",
1988 bootdev.name);
1989 exit(1);
1992 /* Print greeting message. */
1993 if (istty) printf("Boot parameters editor.\n");
1995 signal(SIGINT, trap);
1996 signal(SIGALRM, trap);
1998 if (istty) {
1999 rawterm= termbuf;
2000 rawterm.c_lflag&= ~(ICANON|ECHO|IEXTEN);
2001 rawterm.c_cc[VINTR]= ESC;
2002 if (tcsetattr(0, TCSANOW, &rawterm) < 0) fatal("");
2005 /* Get environment variables from the parameter sector. */
2006 get_parameters();
2008 i= 2;
2009 for (;;) {
2010 /* While there are commands, execute them! */
2011 while (cmds != nil || i < argc) {
2012 if (cmds == nil) {
2013 /* A command line command. */
2014 parse_code(argv[i++]);
2016 execute();
2018 /* Bail out on errors if not interactive. */
2019 if (err && !istty) exit(1);
2022 /* Commands on the command line? */
2023 if (argc > 2) break;
2025 /* The "monitor" is just a "read one command" thing. */
2026 monitor();
2028 exit(0);
2030 #endif /* UNIX */
2033 * $PchId: boot.c,v 1.14 2002/02/27 19:46:14 philip Exp $