The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/boot/boot.c

Version: -  FREEBSD  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

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

Cache object: e22c4bec4559f414d338361dd5190132


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.