boot.c

Go to the documentation of this file.
00001 /*      boot.c - Load and start Minix.                  Author: Kees J. Bot
00002  *                                                              27 Dec 1991
00003  */
00004 
00005 char version[]=         "2.20";
00006 
00007 #define BIOS    (!UNIX)         /* Either uses BIOS or UNIX syscalls. */
00008 
00009 #define nil 0
00010 #define _POSIX_SOURCE   1
00011 #define _MINIX          1
00012 #include <stddef.h>
00013 #include <sys/types.h>
00014 #include <sys/stat.h>
00015 #include <stdlib.h>
00016 #include <stdio.h>
00017 #include <limits.h>
00018 #include <string.h>
00019 #include <errno.h>
00020 #include <ibm/partition.h>
00021 #include <minix/config.h>
00022 #include <minix/type.h>
00023 #include <minix/com.h>
00024 #include <minix/dmap.h>
00025 #include <minix/const.h>
00026 #include <minix/minlib.h>
00027 #include <minix/syslib.h>
00028 #if BIOS
00029 #include <kernel/const.h>
00030 #include <kernel/type.h>
00031 #endif
00032 #if UNIX
00033 #include <stdio.h>
00034 #include <time.h>
00035 #include <unistd.h>
00036 #include <fcntl.h>
00037 #include <signal.h>
00038 #include <termios.h>
00039 #endif
00040 #include "rawfs.h"
00041 #undef EXTERN
00042 #define EXTERN  /* Empty */
00043 #include "boot.h"
00044 
00045 #define arraysize(a)            (sizeof(a) / sizeof((a)[0]))
00046 #define arraylimit(a)           ((a) + arraysize(a))
00047 #define between(a, c, z)        ((unsigned) ((c) - (a)) <= ((z) - (a)))
00048 
00049 int fsok= -1;           /* File system state.  Initially unknown. */
00050 
00051 static int block_size;
00052 
00053 #if BIOS
00054 
00055 /* this data is reserved for BIOS int 0x13 to put the 'specification packet'
00056  * in. It has a structure of course, but we don't define a struct because
00057  * of compiler padding. We fiddle out the bytes ourselves later.
00058  */
00059 unsigned char boot_spec[24];
00060 
00061 char *bios_err(int err)
00062 /* Translate BIOS error code to a readable string.  (This is a rare trait
00063  * known as error checking and reporting.  Take a good look at it, you won't
00064  * see it often.)
00065  */
00066 {
00067         static struct errlist {
00068                 int     err;
00069                 char    *what;
00070         } errlist[] = {
00071 #if !DOS
00072                 { 0x00, "No error" },
00073                 { 0x01, "Invalid command" },
00074                 { 0x02, "Address mark not found" },
00075                 { 0x03, "Disk write-protected" },
00076                 { 0x04, "Sector not found" },
00077                 { 0x05, "Reset failed" },
00078                 { 0x06, "Floppy disk removed" },
00079                 { 0x07, "Bad parameter table" },
00080                 { 0x08, "DMA overrun" },
00081                 { 0x09, "DMA crossed 64 KB boundary" },
00082                 { 0x0A, "Bad sector flag" },
00083                 { 0x0B, "Bad track flag" },
00084                 { 0x0C, "Media type not found" },
00085                 { 0x0D, "Invalid number of sectors on format" },
00086                 { 0x0E, "Control data address mark detected" },
00087                 { 0x0F, "DMA arbitration level out of range" },
00088                 { 0x10, "Uncorrectable CRC or ECC data error" },
00089                 { 0x11, "ECC corrected data error" },
00090                 { 0x20, "Controller failed" },
00091                 { 0x40, "Seek failed" },
00092                 { 0x80, "Disk timed-out" },
00093                 { 0xAA, "Drive not ready" },
00094                 { 0xBB, "Undefined error" },
00095                 { 0xCC, "Write fault" },
00096                 { 0xE0, "Status register error" },
00097                 { 0xFF, "Sense operation failed" }
00098 #else /* DOS */
00099                 { 0x00, "No error" },
00100                 { 0x01, "Function number invalid" },
00101                 { 0x02, "File not found" },
00102                 { 0x03, "Path not found" },
00103                 { 0x04, "Too many open files" },
00104                 { 0x05, "Access denied" },
00105                 { 0x06, "Invalid handle" },
00106                 { 0x0C, "Access code invalid" },
00107 #endif /* DOS */
00108         };
00109         struct errlist *errp;
00110 
00111         for (errp= errlist; errp < arraylimit(errlist); errp++) {
00112                 if (errp->err == err) return errp->what;
00113         }
00114         return "Unknown error";
00115 }
00116 
00117 char *unix_err(int err)
00118 /* Translate the few errors rawfs can give. */
00119 {
00120         switch (err) {
00121         case ENOENT:    return "No such file or directory";
00122         case ENOTDIR:   return "Not a directory";
00123         default:        return "Unknown error";
00124         }
00125 }
00126 
00127 void rwerr(char *rw, off_t sec, int err)
00128 {
00129         printf("\n%s error 0x%02x (%s) at sector %ld absolute\n",
00130                 rw, err, bios_err(err), sec);
00131 }
00132 
00133 void readerr(off_t sec, int err)        { rwerr("Read", sec, err); }
00134 void writerr(off_t sec, int err)        { rwerr("Write", sec, err); }
00135 
00136 void readblock(off_t blk, char *buf, int block_size)
00137 /* Read blocks for the rawfs package. */
00138 {
00139         int r;
00140         u32_t sec= lowsec + blk * RATIO(block_size);
00141 
00142         if(!block_size) {
00143                 printf("block_size 0\n");
00144                 exit(1);
00145         }
00146 
00147         if ((r= readsectors(mon2abs(buf), sec, 1 * RATIO(block_size))) != 0) {
00148                 readerr(sec, r); exit(1);
00149         }
00150 }
00151 
00152 #define istty           (1)
00153 #define alarm(n)        (0)
00154 
00155 #endif /* BIOS */
00156 
00157 #if UNIX
00158 
00159 /* The Minix boot block must start with these bytes: */
00160 char boot_magic[] = { 0x31, 0xC0, 0x8E, 0xD8, 0xFA, 0x8E, 0xD0, 0xBC };
00161 
00162 struct biosdev {
00163         char *name;             /* Name of device. */
00164         int device;             /* Device to edit parameters. */
00165 } bootdev;
00166 
00167 struct termios termbuf;
00168 int istty;
00169 
00170 void quit(int status)
00171 {
00172         if (istty) (void) tcsetattr(0, TCSANOW, &termbuf);
00173         exit(status);
00174 }
00175 
00176 #define exit(s) quit(s)
00177 
00178 void report(char *label)
00179 /* edparams: label: No such file or directory */
00180 {
00181         fprintf(stderr, "edparams: %s: %s\n", label, strerror(errno));
00182 }
00183 
00184 void fatal(char *label)
00185 {
00186         report(label);
00187         exit(1);
00188 }
00189 
00190 void *alloc(void *m, size_t n)
00191 {
00192         m= m == nil ? malloc(n) : realloc(m, n);
00193         if (m == nil) fatal("");
00194         return m;
00195 }
00196 
00197 #define malloc(n)       alloc(nil, n)
00198 #define realloc(m, n)   alloc(m, n)
00199 
00200 #define mon2abs(addr)   ((void *) (addr))
00201 
00202 int rwsectors(int rw, void *addr, u32_t sec, int nsec)
00203 {
00204         ssize_t r;
00205         size_t len= nsec * SECTOR_SIZE;
00206 
00207         if (lseek(bootdev.device, sec * SECTOR_SIZE, SEEK_SET) == -1)
00208                 return errno;
00209 
00210         if (rw == 0) {
00211                 r= read(bootdev.device, (char *) addr, len);
00212         } else {
00213                 r= write(bootdev.device, (char *) addr, len);
00214         }
00215         if (r == -1) return errno;
00216         if (r != len) return EIO;
00217         return 0;
00218 }
00219 
00220 #define readsectors(a, s, n)     rwsectors(0, (a), (s), (n))
00221 #define writesectors(a, s, n)    rwsectors(1, (a), (s), (n))
00222 #define readerr(sec, err)       (errno= (err), report(bootdev.name))
00223 #define writerr(sec, err)       (errno= (err), report(bootdev.name))
00224 #define putch(c)                putchar(c)
00225 #define unix_err(err)           strerror(err)
00226 
00227 void readblock(off_t blk, char *buf, int block_size)
00228 /* Read blocks for the rawfs package. */
00229 {
00230         if(!block_size) fatal("block_size 0");
00231         errno= EIO;
00232         if (lseek(bootdev.device, blk * block_size, SEEK_SET) == -1
00233                 || read(bootdev.device, buf, block_size) != block_size)
00234         {
00235                 fatal(bootdev.name);
00236         }
00237 }
00238 
00239 sig_atomic_t trapsig;
00240 
00241 void trap(int sig)
00242 {
00243         trapsig= sig;
00244         signal(sig, trap);
00245 }
00246 
00247 int escape(void)
00248 {
00249         if (trapsig == SIGINT) {
00250                 trapsig= 0;
00251                 return 1;
00252         }
00253         return 0;
00254 }
00255 
00256 static unsigned char unchar;
00257 
00258 int getch(void)
00259 {
00260         unsigned char c;
00261 
00262         fflush(stdout);
00263 
00264         if (unchar != 0) {
00265                 c= unchar;
00266                 unchar= 0;
00267                 return c;
00268         }
00269 
00270         switch (read(0, &c, 1)) {
00271         case -1:
00272                 if (errno != EINTR) fatal("");
00273                 return(ESC);
00274         case 0:
00275                 if (istty) putch('\n');
00276                 exit(0);
00277         default:
00278                 if (istty && c == termbuf.c_cc[VEOF]) {
00279                         putch('\n');
00280                         exit(0);
00281                 }
00282                 return c;
00283         }
00284 }
00285 
00286 #define ungetch(c)      ((void) (unchar = (c)))
00287 
00288 #define get_tick()              ((u32_t) time(nil))
00289 #define clear_screen()          printf("[clear]")
00290 #define boot_device(device)     printf("[boot %s]\n", device)
00291 #define ctty(line)              printf("[ctty %s]\n", line)
00292 #define bootminix()             (run_trailer() && printf("[boot]\n"))
00293 #define off()                   printf("[off]")
00294 
00295 #endif /* UNIX */
00296 
00297 char *readline(void)
00298 /* Read a line including a newline with echoing. */
00299 {
00300         char *line;
00301         size_t i, z;
00302         int c;
00303 
00304         i= 0;
00305         z= 20;
00306         line= malloc(z * sizeof(char));
00307 
00308         do {
00309                 c= getch();
00310 
00311                 if (strchr("\b\177\25\30", c) != nil) {
00312                         /* Backspace, DEL, ctrl-U, or ctrl-X. */
00313                         do {
00314                                 if (i == 0) break;
00315                                 printf("\b \b");
00316                                 i--;
00317                         } while (c == '\25' || c == '\30');
00318                 } else
00319                 if (c < ' ' && c != '\n') {
00320                         putch('\7');
00321                 } else {
00322                         putch(c);
00323                         line[i++]= c;
00324                         if (i == z) {
00325                                 z*= 2;
00326                                 line= realloc(line, z * sizeof(char));
00327                         }
00328                 }
00329         } while (c != '\n');
00330         line[i]= 0;
00331         return line;
00332 }
00333 
00334 int sugar(char *tok)
00335 /* Recognize special tokens. */
00336 {
00337         return strchr("=(){};\n", tok[0]) != nil;
00338 }
00339 
00340 char *onetoken(char **aline)
00341 /* Returns a string with one token for tokenize. */
00342 {
00343         char *line= *aline;
00344         size_t n;
00345         char *tok;
00346 
00347         /* Skip spaces and runs of newlines. */
00348         while (*line == ' ' || (*line == '\n' && line[1] == '\n')) line++;
00349 
00350         *aline= line;
00351 
00352         /* Don't do odd junk (nor the terminating 0!). */
00353         if ((unsigned) *line < ' ' && *line != '\n') return nil;
00354 
00355         if (*line == '(') {
00356                 /* Function argument, anything goes but () must match. */
00357                 int depth= 0;
00358 
00359                 while ((unsigned) *line >= ' ') {
00360                         if (*line == '(') depth++;
00361                         if (*line++ == ')' && --depth == 0) break;
00362                 }
00363         } else
00364         if (sugar(line)) {
00365                 /* Single character token. */
00366                 line++;
00367         } else {
00368                 /* Multicharacter token. */
00369                 do line++; while ((unsigned) *line > ' ' && !sugar(line));
00370         }
00371         n= line - *aline;
00372         tok= malloc((n + 1) * sizeof(char));
00373         memcpy(tok, *aline, n);
00374         tok[n]= 0;
00375         if (tok[0] == '\n') tok[0]= ';';        /* ';' same as '\n' */
00376 
00377         *aline= line;
00378         return tok;
00379 }
00380 
00381 /* Typed commands form strings of tokens. */
00382 
00383 typedef struct token {
00384         struct token    *next;  /* Next in a command chain. */
00385         char            *token;
00386 } token;
00387 
00388 token **tokenize(token **acmds, char *line)
00389 /* Takes a line apart to form tokens.  The tokens are inserted into a command
00390  * chain at *acmds.  Tokenize returns a reference to where another line could
00391  * be added.  Tokenize looks at spaces as token separators, and recognizes only
00392  * ';', '=', '{', '}', and '\n' as single character tokens.  One token is
00393  * formed from '(' and ')' with anything in between as long as more () match.
00394  */
00395 {
00396         char *tok;
00397         token *newcmd;
00398 
00399         while ((tok= onetoken(&line)) != nil) {
00400                 newcmd= malloc(sizeof(*newcmd));
00401                 newcmd->token= tok;
00402                 newcmd->next= *acmds;
00403                 *acmds= newcmd;
00404                 acmds= &newcmd->next;
00405         }
00406         return acmds;
00407 }
00408 
00409 token *cmds;            /* String of commands to execute. */
00410 int err;                /* Set on an error. */
00411 
00412 char *poptoken(void)
00413 /* Pop one token off the command chain. */
00414 {
00415         token *cmd= cmds;
00416         char *tok= cmd->token;
00417 
00418         cmds= cmd->next;
00419         free(cmd);
00420 
00421         return tok;
00422 }
00423 
00424 void voidtoken(void)
00425 /* Remove one token from the command chain. */
00426 {
00427         free(poptoken());
00428 }
00429 
00430 void parse_code(char *code)
00431 /* Tokenize a string of monitor code, making sure there is a delimiter.  It is
00432  * to be executed next.  (Prepended to the current input.)
00433  */
00434 {
00435         if (cmds != nil && cmds->token[0] != ';') (void) tokenize(&cmds, ";");
00436         (void) tokenize(&cmds, code);
00437 }
00438 
00439 int interrupt(void)
00440 /* Clean up after an ESC has been typed. */
00441 {
00442         if (escape()) {
00443                 printf("[ESC]\n");
00444                 err= 1;
00445                 return 1;
00446         }
00447         return 0;
00448 }
00449 
00450 #if BIOS
00451 
00452 int activate;
00453 
00454 struct biosdev {
00455         char name[8];
00456         int device, primary, secondary;
00457 } bootdev, tmpdev;
00458 
00459 int get_master(char *master, struct part_entry **table, u32_t pos)
00460 /* Read a master boot sector and its partition table. */
00461 {
00462         int r, n;
00463         struct part_entry *pe, **pt;
00464 
00465         if ((r= readsectors(mon2abs(master), pos, 1)) != 0) return r;
00466 
00467         pe= (struct part_entry *) (master + PART_TABLE_OFF);
00468         for (pt= table; pt < table + NR_PARTITIONS; pt++) *pt= pe++;
00469 
00470         /* DOS has the misguided idea that partition tables must be sorted. */
00471         if (pos != 0) return 0;         /* But only the primary. */
00472 
00473         n= NR_PARTITIONS;
00474         do {
00475                 for (pt= table; pt < table + NR_PARTITIONS-1; pt++) {
00476                         if (pt[0]->sysind == NO_PART
00477                                         || pt[0]->lowsec > pt[1]->lowsec) {
00478                                 pe= pt[0]; pt[0]= pt[1]; pt[1]= pe;
00479                         }
00480                 }
00481         } while (--n > 0);
00482         return 0;
00483 }
00484 
00485 void initialize(void)
00486 {
00487         char master[SECTOR_SIZE];
00488         struct part_entry *table[NR_PARTITIONS];
00489         int r, p;
00490         u32_t masterpos;
00491         char *argp;
00492 
00493         /* Copy the boot program to the far end of low memory, this must be
00494          * done to get out of the way of Minix, and to put the data area
00495          * cleanly inside a 64K chunk if using BIOS I/O (no DMA problems).
00496          */
00497         u32_t oldaddr= caddr;
00498         u32_t memend= mem[0].base + mem[0].size;
00499         u32_t newaddr= (memend - runsize) & ~0x0000FL;
00500 #if !DOS
00501         u32_t dma64k= (memend - 1) & ~0x0FFFFL;
00502 
00503 
00504         /* Check if data segment crosses a 64K boundary. */
00505         if (newaddr + (daddr - caddr) < dma64k)  {
00506                 newaddr= (dma64k - runsize) & ~0x0000FL;
00507         }
00508 #endif
00509 
00510         /* Set the new caddr for relocate. */
00511         caddr= newaddr;
00512 
00513         /* Copy code and data. */
00514         raw_copy(newaddr, oldaddr, runsize);
00515 
00516         /* Make the copy running. */
00517         relocate();
00518 
00519 #if !DOS
00520 
00521         /* Take the monitor out of the memory map if we have memory to spare,
00522          * and also keep the BIOS data area safe (1.5K), plus a bit extra for
00523          * where we may have to put a.out headers for older kernels.
00524          */
00525         if (mon_return = (mem[1].size > 512*1024L)) mem[0].size = newaddr;
00526         mem[0].base += 2048;
00527         mem[0].size -= 2048;
00528 
00529         /* Find out what the boot device and partition was. */
00530         bootdev.name[0]= 0;
00531         bootdev.device= device;
00532         bootdev.primary= -1;
00533         bootdev.secondary= -1;
00534 
00535         if (device < 0x80) {
00536                 /* Floppy. */
00537                 strcpy(bootdev.name, "fd0");
00538                 bootdev.name[2] += bootdev.device;
00539                 return;
00540         }
00541 
00542         /* Disk: Get the partition table from the very first sector, and
00543          * determine the partition we booted from using the information from
00544          * the booted partition entry as passed on by the bootstrap (rem_part).
00545          * All we need from it is the partition offset.
00546          */
00547         raw_copy(mon2abs(&lowsec),
00548                 vec2abs(&rem_part) + offsetof(struct part_entry, lowsec),
00549                 sizeof(lowsec));
00550 
00551         masterpos= 0;   /* Master bootsector position. */
00552 
00553         for (;;) {
00554                 /* Extract the partition table from the master boot sector. */
00555                 if ((r= get_master(master, table, masterpos)) != 0) {
00556                         readerr(masterpos, r); exit(1);
00557                 }
00558 
00559                 /* See if you can find "lowsec" back. */
00560                 for (p= 0; p < NR_PARTITIONS; p++) {
00561                         if (lowsec - table[p]->lowsec < table[p]->size) break;
00562                 }
00563 
00564                 if (lowsec == table[p]->lowsec) {       /* Found! */
00565                         if (bootdev.primary < 0)
00566                                 bootdev.primary= p;
00567                         else
00568                                 bootdev.secondary= p;
00569                         break;
00570                 }
00571 
00572                 if (p == NR_PARTITIONS || bootdev.primary >= 0
00573                                         || table[p]->sysind != MINIX_PART) {
00574                         /* The boot partition cannot be named, this only means
00575                          * that "bootdev" doesn't work.
00576                          */
00577                         bootdev.device= -1;
00578                         return;
00579                 }
00580 
00581                 /* See if the primary partition is subpartitioned. */
00582                 bootdev.primary= p;
00583                 masterpos= table[p]->lowsec;
00584         }
00585         strcpy(bootdev.name, "d0p0");
00586         bootdev.name[1] += (device - 0x80);
00587         bootdev.name[3] += bootdev.primary;
00588         if (bootdev.secondary >= 0) {
00589                 strcat(bootdev.name, "s0");
00590                 bootdev.name[5] += bootdev.secondary;
00591         }
00592 
00593 #else /* DOS */
00594         /* Take the monitor out of the memory map if we have memory to spare,
00595          * note that only half our PSP is needed at the new place, the first
00596          * half is to be kept in its place.
00597          */
00598         if (mem[1].size > 0) mem[0].size = newaddr + 0x80 - mem[0].base;
00599 
00600         /* Parse the command line. */
00601         argp= PSP + 0x81;
00602         argp[PSP[0x80]]= 0;
00603         while (between('\1', *argp, ' ')) argp++;
00604         vdisk= argp;
00605         while (!between('\0', *argp, ' ')) argp++;
00606         while (between('\1', *argp, ' ')) *argp++= 0;
00607         if (*vdisk == 0) {
00608                 printf("\nUsage: boot <vdisk> [commands ...]\n");
00609                 exit(1);
00610         }
00611         drun= *argp == 0 ? "main" : argp;
00612 
00613         if ((r= dev_open()) != 0) {
00614                 printf("\n%s: Error %02x (%s)\n", vdisk, r, bios_err(r));
00615                 exit(1);
00616         }
00617 
00618         /* Find the active partition on the virtual disk. */
00619         if ((r= get_master(master, table, 0)) != 0) {
00620                 readerr(0, r); exit(1);
00621         }
00622 
00623         strcpy(bootdev.name, "d0");
00624         bootdev.primary= -1;
00625         for (p= 0; p < NR_PARTITIONS; p++) {
00626                 if (table[p]->bootind != 0 && table[p]->sysind == MINIX_PART) {
00627                         bootdev.primary= p;
00628                         strcat(bootdev.name, "p0");
00629                         bootdev.name[3] += p;
00630                         lowsec= table[p]->lowsec;
00631                         break;
00632                 }
00633         }
00634 #endif /* DOS */
00635 }
00636 
00637 #endif /* BIOS */
00638 
00639 /* Reserved names: */
00640 enum resnames {
00641         R_NULL, R_BOOT, R_CTTY, R_DELAY, R_ECHO, R_EXIT, R_HELP,
00642         R_LS, R_MENU, R_OFF, R_SAVE, R_SET, R_TRAP, R_UNSET
00643 };
00644 
00645 char resnames[][6] = {
00646         "", "boot", "ctty", "delay", "echo", "exit", "help",
00647         "ls", "menu", "off", "save", "set", "trap", "unset",
00648 };
00649 
00650 /* Using this for all null strings saves a lot of memory. */
00651 #define null (resnames[0])
00652 
00653 int reserved(char *s)
00654 /* Recognize reserved strings. */
00655 {
00656         int r;
00657 
00658         for (r= R_BOOT; r <= R_UNSET; r++) {
00659                 if (strcmp(s, resnames[r]) == 0) return r;
00660         }
00661         return R_NULL;
00662 }
00663 
00664 void sfree(char *s)
00665 /* Free a non-null string. */
00666 {
00667         if (s != nil && s != null) free(s);
00668 }
00669 
00670 char *copystr(char *s)
00671 /* Copy a non-null string using malloc. */
00672 {
00673         char *c;
00674 
00675         if (*s == 0) return null;
00676         c= malloc((strlen(s) + 1) * sizeof(char));
00677         strcpy(c, s);
00678         return c;
00679 }
00680 
00681 int is_default(environment *e)
00682 {
00683         return (e->flags & E_SPECIAL) && e->defval == nil;
00684 }
00685 
00686 environment **searchenv(char *name)
00687 {
00688         environment **aenv= &env;
00689 
00690         while (*aenv != nil && strcmp((*aenv)->name, name) != 0) {
00691                 aenv= &(*aenv)->next;
00692         }
00693 
00694         return aenv;
00695 }
00696 
00697 #define b_getenv(name)  (*searchenv(name))
00698 /* Return the environment *structure* belonging to name, or nil if not found. */
00699 
00700 char *b_value(char *name)
00701 /* The value of a variable. */
00702 {
00703         environment *e= b_getenv(name);
00704 
00705         return e == nil || !(e->flags & E_VAR) ? nil : e->value;
00706 }
00707 
00708 char *b_body(char *name)
00709 /* The value of a function. */
00710 {
00711         environment *e= b_getenv(name);
00712 
00713         return e == nil || !(e->flags & E_FUNCTION) ? nil : e->value;
00714 }
00715 
00716 int b_setenv(int flags, char *name, char *arg, char *value)
00717 /* Change the value of an environment variable.  Returns the flags of the
00718  * variable if you are not allowed to change it, 0 otherwise.
00719  */
00720 {
00721         environment **aenv, *e;
00722 
00723         if (*(aenv= searchenv(name)) == nil) {
00724                 if (reserved(name)) return E_RESERVED;
00725                 e= malloc(sizeof(*e));
00726                 e->name= copystr(name);
00727                 e->flags= flags;
00728                 e->defval= nil;
00729                 e->next= nil;
00730                 *aenv= e;
00731         } else {
00732                 e= *aenv;
00733 
00734                 /* Don't change special variables to functions or vv. */
00735                 if (e->flags & E_SPECIAL
00736                         && (e->flags & E_FUNCTION) != (flags & E_FUNCTION)
00737                 ) return e->flags;
00738 
00739                 e->flags= (e->flags & E_STICKY) | flags;
00740                 if (is_default(e)) {
00741                         e->defval= e->value;
00742                 } else {
00743                         sfree(e->value);
00744                 }
00745                 sfree(e->arg);
00746         }
00747         e->arg= copystr(arg);
00748         e->value= copystr(value);
00749 
00750         return 0;
00751 }
00752 
00753 int b_setvar(int flags, char *name, char *value)
00754 /* Set variable or simple function. */
00755 {
00756         int r;
00757 
00758         if((r=b_setenv(flags, name, null, value))) {
00759                 return r;
00760         }
00761 
00762         return r;
00763 }
00764 
00765 void b_unset(char *name)
00766 /* Remove a variable from the environment.  A special variable is reset to
00767  * its default value.
00768  */
00769 {
00770         environment **aenv, *e;
00771 
00772         if ((e= *(aenv= searchenv(name))) == nil) return;
00773 
00774         if (e->flags & E_SPECIAL) {
00775                 if (e->defval != nil) {
00776                         sfree(e->arg);
00777                         e->arg= null;
00778                         sfree(e->value);
00779                         e->value= e->defval;
00780                         e->defval= nil;
00781                 }
00782         } else {
00783                 sfree(e->name);
00784                 sfree(e->arg);
00785                 sfree(e->value);
00786                 *aenv= e->next;
00787                 free(e);
00788         }
00789 }
00790 
00791 long a2l(char *a)
00792 /* Cheap atol(). */
00793 {
00794         int sign= 1;
00795         long n= 0;
00796 
00797         if (*a == '-') { sign= -1; a++; }
00798 
00799         while (between('0', *a, '9')) n= n * 10 + (*a++ - '0');
00800 
00801         return sign * n;
00802 }
00803 
00804 char *ul2a(u32_t n, unsigned b)
00805 /* Transform a long number to ascii at base b, (b >= 8). */
00806 {
00807         static char num[(CHAR_BIT * sizeof(n) + 2) / 3 + 1];
00808         char *a= arraylimit(num) - 1;
00809         static char hex[16] = "0123456789ABCDEF";
00810 
00811         do *--a = hex[(int) (n % b)]; while ((n/= b) > 0);
00812         return a;
00813 }
00814 
00815 char *ul2a10(u32_t n)
00816 /* Transform a long number to ascii at base 10. */
00817 {
00818         return ul2a(n, 10);
00819 }
00820 
00821 unsigned a2x(char *a)
00822 /* Ascii to hex. */
00823 {
00824         unsigned n= 0;
00825         int c;
00826 
00827         for (;;) {
00828                 c= *a;
00829                 if (between('0', c, '9')) c= c - '0' + 0x0;
00830                 else
00831                 if (between('A', c, 'F')) c= c - 'A' + 0xA;
00832                 else
00833                 if (between('a', c, 'f')) c= c - 'a' + 0xa;
00834                 else
00835                         break;
00836                 n= (n<<4) | c;
00837                 a++;
00838         }
00839         return n;
00840 }
00841 
00842 void get_parameters(void)
00843 {
00844         char params[SECTOR_SIZE + 1];
00845         token **acmds;
00846         int r, bus, processor;
00847         memory *mp;
00848         static char bus_type[][4] = {
00849                 "xt", "at", "mca"
00850         };
00851         static char vid_type[][4] = {
00852                 "mda", "cga", "ega", "ega", "vga", "vga"
00853         };
00854         static char vid_chrome[][6] = {
00855                 "mono", "color"
00856         };
00857 
00858         /* Variables that Minix needs: */
00859         b_setvar(E_SPECIAL|E_VAR|E_DEV, "rootdev", "ram");
00860         b_setvar(E_SPECIAL|E_VAR|E_DEV, "ramimagedev", "bootdev");
00861         b_setvar(E_SPECIAL|E_VAR, "ramsize", "0");
00862 #if BIOS
00863         processor = getprocessor();
00864         if(processor == 1586) processor = 686;
00865         b_setvar(E_SPECIAL|E_VAR, "processor", ul2a10(processor));
00866         b_setvar(E_SPECIAL|E_VAR, "bus", bus_type[get_bus()]);
00867         b_setvar(E_SPECIAL|E_VAR, "video", vid_type[get_video()]);
00868         b_setvar(E_SPECIAL|E_VAR, "chrome", vid_chrome[get_video() & 1]);
00869         params[0]= 0;
00870         for (mp= mem; mp < arraylimit(mem); mp++) {
00871                 if (mp->size == 0) continue;
00872                 if (params[0] != 0) strcat(params, ",");
00873                 strcat(params, ul2a(mp->base, 0x10));
00874                 strcat(params, ":");
00875                 strcat(params, ul2a(mp->size, 0x10));
00876         }
00877         b_setvar(E_SPECIAL|E_VAR, "memory", params);
00878 
00879 #if DOS
00880         b_setvar(E_SPECIAL|E_VAR, "dosfile-d0", vdisk);
00881 #endif
00882 
00883 #endif
00884 #if UNIX
00885         b_setvar(E_SPECIAL|E_VAR, "processor", "?");
00886         b_setvar(E_SPECIAL|E_VAR, "bus", "?");
00887         b_setvar(E_SPECIAL|E_VAR, "video", "?");
00888         b_setvar(E_SPECIAL|E_VAR, "chrome", "?");
00889         b_setvar(E_SPECIAL|E_VAR, "memory", "?");
00890         b_setvar(E_SPECIAL|E_VAR, "c0", "?");
00891 #endif
00892 
00893         /* Variables boot needs: */
00894         b_setvar(E_SPECIAL|E_VAR, "image", "boot/image");
00895         b_setvar(E_SPECIAL|E_FUNCTION, "leader", 
00896                 "echo --- Welcome to MINIX 3. This is the boot monitor. ---\\n");
00897         b_setvar(E_SPECIAL|E_FUNCTION, "main", "menu");
00898         b_setvar(E_SPECIAL|E_FUNCTION, "trailer", "");
00899 
00900         /* Default hidden menu function: */
00901         b_setenv(E_RESERVED|E_FUNCTION, null, "=,Start MINIX", "boot");
00902 
00903         /* Tokenize bootparams sector. */
00904         if ((r= readsectors(mon2abs(params), lowsec+PARAMSEC, 1)) != 0) {
00905                 readerr(lowsec+PARAMSEC, r);
00906                 exit(1);
00907         }
00908         params[SECTOR_SIZE]= 0;
00909         acmds= tokenize(&cmds, params);
00910 
00911         /* Stuff the default action into the command chain. */
00912 #if UNIX
00913         (void) tokenize(acmds, ":;");
00914 #elif DOS
00915         (void) tokenize(tokenize(acmds, ":;leader;"), drun);
00916 #else /* BIOS */
00917         (void) tokenize(acmds, ":;leader;main");
00918 #endif
00919 }
00920 
00921 char *addptr;
00922 
00923 void addparm(char *n)
00924 {
00925         while (*n != 0 && *addptr != 0) *addptr++ = *n++;
00926 }
00927 
00928 void save_parameters(void)
00929 /* Save nondefault environment variables to the bootparams sector. */
00930 {
00931         environment *e;
00932         char params[SECTOR_SIZE + 1];
00933         int r;
00934 
00935         /* Default filling: */
00936         memset(params, '\n', SECTOR_SIZE);
00937 
00938         /* Don't touch the 0! */
00939         params[SECTOR_SIZE]= 0;
00940         addptr= params;
00941 
00942         for (e= env; e != nil; e= e->next) {
00943                 if (e->flags & E_RESERVED || is_default(e)) continue;
00944 
00945                 addparm(e->name);
00946                 if (e->flags & E_FUNCTION) {
00947                         addparm("(");
00948                         addparm(e->arg);
00949                         addparm(")");
00950                 } else {
00951                         addparm((e->flags & (E_DEV|E_SPECIAL)) != E_DEV
00952                                                         ? "=" : "=d ");
00953                 }
00954                 addparm(e->value);
00955                 if (*addptr == 0) {
00956                         printf("The environment is too big\n");
00957                         return;
00958                 }
00959                 *addptr++= '\n';
00960         }
00961 
00962         /* Save the parameters on disk. */
00963         if ((r= writesectors(mon2abs(params), lowsec+PARAMSEC, 1)) != 0) {
00964                 writerr(lowsec+PARAMSEC, r);
00965                 printf("Can't save environment\n");
00966         }
00967 }
00968 
00969 void show_env(void)
00970 /* Show the environment settings. */
00971 {
00972         environment *e;
00973         unsigned more= 0;
00974         int c;
00975 
00976         for (e= env; e != nil; e= e->next) {
00977                 if (e->flags & E_RESERVED) continue;
00978                 if (!istty && is_default(e)) continue;
00979 
00980                 if (e->flags & E_FUNCTION) {
00981                         printf("%s(%s) %s\n", e->name, e->arg, e->value);
00982                 } else {
00983                         printf(is_default(e) ? "%s = (%s)\n" : "%s = %s\n",
00984                                 e->name, e->value);
00985                 }
00986 
00987                 if (e->next != nil && istty && ++more % 20 == 0) {
00988                         printf("More? ");
00989                         c= getch();
00990                         if (c == ESC || c > ' ') {
00991                                 putch('\n');
00992                                 if (c > ' ') ungetch(c);
00993                                 break;
00994                         }
00995                         printf("\b\b\b\b\b\b");
00996                 }
00997         }
00998 }
00999 
01000 int numprefix(char *s, char **ps)
01001 /* True iff s is a string of digits.  *ps will be set to the first nondigit
01002  * if non-nil, otherwise the string should end.
01003  */
01004 {
01005         char *n= s;
01006 
01007         while (between('0', *n, '9')) n++;
01008 
01009         if (n == s) return 0;
01010 
01011         if (ps == nil) return *n == 0;
01012 
01013         *ps= n;
01014         return 1;
01015 }
01016 
01017 int numeric(char *s)
01018 {
01019         return numprefix(s, (char **) nil);
01020 }
01021 
01022 #if BIOS
01023 
01024 /* Device numbers of standard MINIX devices. */
01025 #define DEV_FD0         0x0200
01026 static dev_t dev_cNd0[] = { 0x0300, 0x0800, 0x0A00, 0x0C00, 0x1000 };
01027 #define minor_p0s0         128
01028 
01029 static int block_size;
01030 
01031 dev_t name2dev(char *name)
01032 /* Translate, say, /dev/c0d0p2 to a device number.  If the name can't be
01033  * found on the boot device, then do some guesswork.  The global structure
01034  * "tmpdev" will be filled in based on the name, so that "boot d1p0" knows
01035  * what device to boot without interpreting device numbers.
01036  */
01037 {
01038         dev_t dev;
01039         ino_t ino;
01040         int drive;
01041         struct stat st;
01042         char *n, *s;
01043 
01044         /* "boot *d0p2" means: make partition 2 active before you boot it. */
01045         if ((activate= (name[0] == '*'))) name++;
01046 
01047         /* The special name "bootdev" must be translated to the boot device. */
01048         if (strcmp(name, "bootdev") == 0) {
01049                 if (bootdev.device == -1) {
01050                         printf("The boot device could not be named\n");
01051                         errno= 0;
01052                         return -1;
01053                 }
01054                 name= bootdev.name;
01055         }
01056 
01057         /* If our boot device doesn't have a file system, or we want to know
01058          * what a name means for the BIOS, then we need to interpret the
01059          * device name ourselves: "fd" = floppy, "c0d0" = hard disk, etc.
01060          */
01061         tmpdev.device= tmpdev.primary= tmpdev.secondary= -1;
01062         dev= -1;
01063         n= name;
01064         if (strncmp(n, "/dev/", 5) == 0) n+= 5;
01065 
01066         if (strcmp(n, "ram") == 0) {
01067                 dev= DEV_RAM;
01068         } else
01069         if (strcmp(n, "boot") == 0) {
01070                 dev= DEV_BOOT;
01071         } else
01072         if (n[0] == 'f' && n[1] == 'd' && numeric(n+2)) {
01073                 /* Floppy. */
01074                 tmpdev.device= a2l(n+2);
01075                 dev= DEV_FD0 + tmpdev.device;
01076         } else
01077         if ((n[0] == 'h' || n[0] == 's') && n[1] == 'd' && numprefix(n+2, &s)
01078                 && (*s == 0 || (between('a', *s, 'd') && s[1] == 0))
01079         ) {
01080                 /* Old style hard disk (backwards compatibility.) */
01081                 dev= a2l(n+2);
01082                 tmpdev.device= dev / (1 + NR_PARTITIONS);
01083                 tmpdev.primary= (dev % (1 + NR_PARTITIONS)) - 1;
01084                 if (*s != 0) {
01085                         /* Subpartition. */
01086                         tmpdev.secondary= *s - 'a';
01087                         dev= minor_p0s0
01088                                 + (tmpdev.device * NR_PARTITIONS
01089                                         + tmpdev.primary) * NR_PARTITIONS
01090                                 + tmpdev.secondary;
01091                 }
01092                 tmpdev.device+= 0x80;
01093                 dev+= n[0] == 'h' ? dev_cNd0[0] : dev_cNd0[2];
01094         } else {
01095                 /* Hard disk. */
01096                 int ctrlr= 0;
01097 
01098                 if (n[0] == 'c' && between('0', n[1], '4')) {
01099                         ctrlr= (n[1] - '0');
01100                         tmpdev.device= 0;
01101                         n+= 2;
01102                 }
01103                 if (n[0] == 'd' && between('0', n[1], '7')) {
01104                         tmpdev.device= (n[1] - '0');
01105                         n+= 2;
01106                         if (n[0] == 'p' && between('0', n[1], '3')) {
01107                                 tmpdev.primary= (n[1] - '0');
01108                                 n+= 2;
01109                                 if (n[0] == 's' && between('0', n[1], '3')) {
01110                                         tmpdev.secondary= (n[1] - '0');
01111                                         n+= 2;
01112                                 }
01113                         }
01114                 }
01115                 if (*n == 0) {
01116                         dev= dev_cNd0[ctrlr];
01117                         if (tmpdev.secondary < 0) {
01118                                 dev += tmpdev.device * (NR_PARTITIONS+1)
01119                                         + (tmpdev.primary + 1);
01120                         } else {
01121                                 dev += minor_p0s0
01122                                         + (tmpdev.device * NR_PARTITIONS
01123                                             + tmpdev.primary) * NR_PARTITIONS
01124                                         + tmpdev.secondary;
01125                         }
01126                         tmpdev.device+= 0x80;
01127                 }
01128         }
01129 
01130         /* Look the name up on the boot device for the UNIX device number. */
01131         if (fsok == -1) fsok= r_super(&block_size) != 0;
01132         if (fsok) {
01133                 /* The current working directory is "/dev". */
01134                 ino= r_lookup(r_lookup(ROOT_INO, "dev"), name);
01135 
01136                 if (ino != 0) {
01137                         /* Name has been found, extract the device number. */
01138                         r_stat(ino, &st);
01139                         if (!S_ISBLK(st.st_mode)) {
01140                                 printf("%s is not a block device\n", name);
01141                                 errno= 0;
01142                                 return (dev_t) -1;
01143                         }
01144                         dev= st.st_rdev;
01145                 }
01146         }
01147 
01148         if (tmpdev.primary < 0) activate= 0;    /* Careful now! */
01149 
01150         if (dev == -1) {
01151                 printf("Can't recognize '%s' as a device\n", name);
01152                 errno= 0;
01153         }
01154         return dev;
01155 }
01156 
01157 #if DEBUG
01158 static void apm_perror(char *label, u16_t ax)
01159 {
01160         unsigned ah;
01161         char *str;
01162 
01163         ah= (ax >> 8);
01164         switch(ah)
01165         {
01166         case 0x01: str= "APM functionality disabled"; break;
01167         case 0x03: str= "interface not connected"; break;
01168         case 0x09: str= "unrecognized device ID"; break;
01169         case 0x0A: str= "parameter value out of range"; break;
01170         case 0x0B: str= "interface not engaged"; break;
01171         case 0x60: str= "unable to enter requested state"; break;
01172         case 0x86: str= "APM not present"; break;
01173         default: printf("%s: error 0x%02x\n", label, ah); return;
01174         }
01175         printf("%s: %s\n", label, str);
01176 }
01177 
01178 #define apm_printf printf
01179 #else
01180 #define apm_perror(label, ax) ((void)0)
01181 #define apm_printf
01182 #endif
01183 
01184 static void off(void)
01185 {
01186         bios_env_t be;
01187         unsigned al, ah;
01188 
01189         /* Try to switch off the system. Print diagnostic information
01190          * that can be useful if the operation fails.
01191          */
01192 
01193         be.ax= 0x5300;  /* APM, Installation check */
01194         be.bx= 0;       /* Device, APM BIOS */
01195         int15(&be);
01196         if (be.flags & FL_CARRY)
01197         {
01198                 apm_perror("APM installation check failed", be.ax);
01199                 return;
01200         }
01201         if (be.bx != (('P' << 8) | 'M'))
01202         {
01203                 apm_printf("APM signature not found (got 0x%04x)\n", be.bx);
01204                 return;
01205         }
01206 
01207         ah= be.ax >> 8;
01208         if (ah > 9)
01209                 ah= (ah >> 4)*10 + (ah & 0xf);
01210         al= be.ax & 0xff;
01211         if (al > 9)
01212                 al= (al >> 4)*10 + (al & 0xf);
01213         apm_printf("APM version %u.%u%s%s%s%s%s\n",
01214                 ah, al,
01215                 (be.cx & 0x1) ? ", 16-bit PM" : "",
01216                 (be.cx & 0x2) ? ", 32-bit PM" : "",
01217                 (be.cx & 0x4) ? ", CPU-Idle" : "",
01218                 (be.cx & 0x8) ? ", APM-disabled" : "",
01219                 (be.cx & 0x10) ? ", APM-disengaged" : "");
01220 
01221         /* Connect */
01222         be.ax= 0x5301;  /* APM, Real mode interface connect */
01223         be.bx= 0x0000;  /* APM BIOS */
01224         int15(&be);
01225         if (be.flags & FL_CARRY)
01226         {
01227                 apm_perror("APM real mode connect failed", be.ax);
01228                 return;
01229         }
01230 
01231         /* Ask for a seat upgrade */
01232         be.ax= 0x530e;  /* APM, Driver Version */
01233         be.bx= 0x0000;  /* BIOS */
01234         be.cx= 0x0102;  /* version 1.2 */
01235         int15(&be);
01236         if (be.flags & FL_CARRY)
01237         {
01238                 apm_perror("Set driver version failed", be.ax);
01239                 goto disco;
01240         }
01241 
01242         /* Is this version really worth reporting. Well, if the system
01243          * does switch off, you won't see it anyway.
01244          */
01245         ah= be.ax >> 8;
01246         if (ah > 9)
01247                 ah= (ah >> 4)*10 + (ah & 0xf);
01248         al= be.ax & 0xff;
01249         if (al > 9)
01250                 al= (al >> 4)*10 + (al & 0xf);
01251         apm_printf("Got APM connection version %u.%u\n", ah, al);
01252 
01253         /* Enable */
01254         be.ax= 0x5308;  /* APM, Enable/disable power management */
01255         be.bx= 0x0001;  /* All device managed by APM BIOS */
01256 #if 0
01257         /* For old APM 1.0 systems, we need 0xffff. Assume that those
01258          * systems do not exist.
01259          */
01260         be.bx= 0xffff;  /* All device managed by APM BIOS (compat) */
01261 #endif
01262         be.cx= 0x0001;  /* Enable power management */
01263         int15(&be);
01264         if (be.flags & FL_CARRY)
01265         {
01266                 apm_perror("Enable power management failed", be.ax);
01267                 goto disco;
01268         }
01269 
01270         /* Off */
01271         be.ax= 0x5307;  /* APM, Set Power State */
01272         be.bx= 0x0001;  /* All devices managed by APM */
01273         be.cx= 0x0003;  /* Off */
01274         int15(&be);
01275         if (be.flags & FL_CARRY)
01276         {
01277                 apm_perror("Set power state failed", be.ax);
01278                 goto disco;
01279         }
01280 
01281         apm_printf("Power off sequence successfully completed.\n\n");
01282         apm_printf("Ha, ha, just kidding!\n");
01283 
01284 disco:
01285         /* Disconnect */
01286         be.ax= 0x5304;  /* APM, interface disconnect */
01287         be.bx= 0x0000;  /* APM BIOS */
01288         int15(&be);
01289         if (be.flags & FL_CARRY)
01290         {
01291                 apm_perror("APM interface disconnect failed", be.ax);
01292                 return;
01293         }
01294 }
01295 
01296 #if !DOS
01297 #define B_NOSIG         -1      /* "No signature" error code. */
01298 
01299 int exec_bootstrap(void)
01300 /* Load boot sector from the disk or floppy described by tmpdev and execute it.
01301  */
01302 {
01303         int r, n, dirty= 0;
01304         char master[SECTOR_SIZE];
01305         struct part_entry *table[NR_PARTITIONS], dummy, *active= &dummy;
01306         u32_t masterpos;
01307 
01308         active->lowsec= 0;
01309 
01310         /* Select a partition table entry. */
01311         while (tmpdev.primary >= 0) {
01312                 masterpos= active->lowsec;
01313 
01314                 if ((r= get_master(master, table, masterpos)) != 0) return r;
01315 
01316                 active= table[tmpdev.primary];
01317 
01318                 /* How does one check a partition table entry? */
01319                 if (active->sysind == NO_PART) return B_NOSIG;
01320 
01321                 tmpdev.primary= tmpdev.secondary;
01322                 tmpdev.secondary= -1;
01323         }
01324 
01325         if (activate && !active->bootind) {
01326                 for (n= 0; n < NR_PARTITIONS; n++) table[n]->bootind= 0;
01327                 active->bootind= ACTIVE_FLAG;
01328                 dirty= 1;
01329         }
01330 
01331         /* Read the boot sector. */
01332         if ((r= readsectors(BOOTPOS, active->lowsec, 1)) != 0) return r;
01333 
01334         /* Check signature word. */
01335         if (get_word(BOOTPOS+SIGNATOFF) != SIGNATURE) return B_NOSIG;
01336 
01337         /* Write the partition table if a member must be made active. */
01338         if (dirty && (r= writesectors(mon2abs(master), masterpos, 1)) != 0)
01339                 return r;
01340 
01341         bootstrap(device, active);
01342 }
01343 
01344 void boot_device(char *devname)
01345 /* Boot the device named by devname. */
01346 {
01347         dev_t dev= name2dev(devname);
01348         int save_dev= device;
01349         int r;
01350         char *err;
01351 
01352         if (tmpdev.device < 0) {
01353                 if (dev != -1) printf("Can't boot from %s\n", devname);
01354                 return;
01355         }
01356 
01357         /* Change current device and try to load and execute its bootstrap. */
01358         device= tmpdev.device;
01359 
01360         if ((r= dev_open()) == 0) r= exec_bootstrap();
01361 
01362         err= r == B_NOSIG ? "Not bootable" : bios_err(r);
01363         printf("Can't boot %s: %s\n", devname, err);
01364 
01365         /* Restore boot device setting. */
01366         device= save_dev;
01367         (void) dev_open();
01368 }
01369 
01370 void ctty(char *line)
01371 {
01372         if (between('0', line[0], '3') && line[1] == 0) {
01373                 serial_init(line[0] - '0');
01374         } else {
01375                 printf("Bad serial line number: %s\n", line);
01376         }
01377 }
01378 
01379 #else /* DOS */
01380 
01381 void boot_device(char *devname)
01382 /* No booting of other devices under DOS. */
01383 {
01384         printf("Can't boot devices under DOS\n");
01385 }
01386 
01387 void ctty(char *line)
01388 /* Don't know how to handle serial lines under DOS. */
01389 {
01390         printf("No serial line support under DOS\n");
01391 }
01392 
01393 #endif /* DOS */
01394 #endif /* BIOS */
01395 
01396 void ls(char *dir)
01397 /* List the contents of a directory. */
01398 {
01399         ino_t ino;
01400         struct stat st;
01401         char name[NAME_MAX+1];
01402 
01403         if (fsok == -1) fsok= r_super(&block_size) != 0;
01404         if (!fsok) return;
01405 
01406         /* (,) construct because r_stat returns void */
01407         if ((ino= r_lookup(ROOT_INO, dir)) == 0 ||
01408                 (r_stat(ino, &st), r_readdir(name)) == -1)
01409         {
01410                 printf("ls: %s: %s\n", dir, unix_err(errno));
01411                 return;
01412         }
01413         (void) r_readdir(name); /* Skip ".." too. */
01414 
01415         while ((ino= r_readdir(name)) != 0) printf("%s/%s\n", dir, name);
01416 }
01417 
01418 u32_t milli_time(void)
01419 {
01420         return get_tick() * MSEC_PER_TICK;
01421 }
01422 
01423 u32_t milli_since(u32_t base)
01424 {
01425         return (milli_time() + (TICKS_PER_DAY*MSEC_PER_TICK) - base)
01426                         % (TICKS_PER_DAY*MSEC_PER_TICK);
01427 }
01428 
01429 char *Thandler;
01430 u32_t Tbase, Tcount;
01431 
01432 void unschedule(void)
01433 /* Invalidate a waiting command. */
01434 {
01435         alarm(0);
01436 
01437         if (Thandler != nil) {
01438                 free(Thandler);
01439                 Thandler= nil;
01440         }
01441 }
01442 
01443 void schedule(long msec, char *cmd)
01444 /* Schedule command at a certain time from now. */
01445 {
01446         unschedule();
01447         Thandler= cmd;
01448         Tbase= milli_time();
01449         Tcount= msec;
01450         alarm(1);
01451 }
01452 
01453 int expired(void)
01454 /* Check if the timer expired for getch(). */
01455 {
01456         return (Thandler != nil && milli_since(Tbase) >= Tcount);
01457 }
01458 
01459 void delay(char *msec)
01460 /* Delay for a given time. */
01461 {
01462         u32_t base, count;
01463 
01464         if ((count= a2l(msec)) == 0) return;
01465         base= milli_time();
01466 
01467         alarm(1);
01468 
01469         do {
01470                 pause();
01471         } while (!interrupt() && !expired() && milli_since(base) < count);
01472 }
01473 
01474 enum whatfun { NOFUN, SELECT, DEFFUN, USERFUN } menufun(environment *e)
01475 {
01476         if (!(e->flags & E_FUNCTION) || e->arg[0] == 0) return NOFUN;
01477         if (e->arg[1] != ',') return SELECT;
01478         return e->flags & E_RESERVED ? DEFFUN : USERFUN;
01479 }
01480 
01481 void menu(void)
01482 /* By default:  Show a simple menu.
01483  * Multiple kernels/images:  Show extra selection options.
01484  * User defined function:  Kill the defaults and show these.
01485  * Wait for a keypress and execute the given function.
01486  */
01487 {
01488         int c, def= 1;
01489         char *choice= nil;
01490         environment *e;
01491 
01492         /* Just a default menu? */
01493         for (e= env; e != nil; e= e->next) if (menufun(e) == USERFUN) def= 0;
01494 
01495         printf("\nHit a key as follows:\n\n");
01496 
01497         /* Show the choices. */
01498         for (e= env; e != nil; e= e->next) {
01499                 switch (menufun(e)) {
01500                 case DEFFUN:
01501                         if (!def) break;
01502                         /*FALL THROUGH*/
01503                 case USERFUN:
01504                         printf("    %c  %s\n", e->arg[0], e->arg+2);
01505                         break;
01506                 case SELECT:
01507                         printf("    %c  Select %s kernel\n", e->arg[0],e->name);
01508                         break;
01509                 default:;
01510                 }
01511         }
01512 
01513         /* Wait for a keypress. */
01514         do {
01515                 c= getch();
01516                 if (interrupt() || expired()) return;
01517 
01518                 unschedule();
01519 
01520                 for (e= env; e != nil; e= e->next) {
01521                         switch (menufun(e)) {
01522                         case DEFFUN:
01523                                 if (!def) break;
01524                         case USERFUN:
01525                         case SELECT:
01526                                 if (c == e->arg[0]) choice= e->value;
01527                         }
01528                 }
01529         } while (choice == nil);
01530 
01531         /* Execute the chosen function. */
01532         printf("%c\n", c);
01533         (void) tokenize(&cmds, choice);
01534 }
01535 
01536 void help(void)
01537 /* Not everyone is a rocket scientist. */
01538 {
01539         struct help {
01540                 char    *thing;
01541                 char    *help;
01542         } *pi;
01543         static struct help info[] = {
01544                 { nil,  "Names:" },
01545                 { "rootdev",            "Root device" },
01546                 { "ramimagedev",        "Device to use as RAM disk image " },
01547                 { "ramsize",            "RAM disk size (if no image device) " },
01548                 { "bootdev",            "Special name for the boot device" },
01549                 { "fd0, d0p2, c0d0p1s0",        "Devices (as in /dev)" },
01550                 { "image",              "Name of the boot image to use" },
01551                 { "main",               "Startup function" },
01552                 { "bootdelay",          "Delay in msec after loading image" },
01553                 { nil,  "Commands:" },
01554                 { "name = [device] value",  "Set environment variable" },
01555                 { "name() { ... }",         "Define function" },
01556                 { "name(key,text) { ... }",
01557                         "A menu option like: minix(=,Start MINIX) {boot}" },
01558                 { "name",               "Call function" },
01559                 { "boot [device]",      "Boot Minix or another O.S." },
01560                 { "ctty [line]",        "Duplicate to serial line" },
01561                 { "delay [msec]",       "Delay (500 msec default)" },
01562                 { "echo word ...",      "Display the words" },
01563                 { "ls [directory]",     "List contents of directory" },
01564                 { "menu",               "Show menu and choose menu option" },
01565                 { "save / set",         "Save or show environment" },
01566                 { "trap msec command",  "Schedule command " },
01567                 { "unset name ...",     "Unset variable or set to default" },
01568                 { "exit / off",         "Exit the Monitor / Power off" },
01569         };
01570 
01571         for (pi= info; pi < arraylimit(info); pi++) {
01572                 if (pi->thing != nil) printf("    %-24s- ", pi->thing);
01573                 printf("%s\n", pi->help);
01574         }
01575 }
01576 
01577 void execute(void)
01578 /* Get one command from the command chain and execute it. */
01579 {
01580         token *second, *third, *fourth, *sep;
01581         char *name;
01582         int res;
01583         size_t n= 0;
01584 
01585         if (err) {
01586                 /* An error occured, stop interpreting. */
01587                 while (cmds != nil) voidtoken();
01588                 return;
01589         }
01590 
01591         if (expired()) {        /* Timer expired? */
01592                 parse_code(Thandler);
01593                 unschedule();
01594         }
01595 
01596         /* There must be a separator lurking somewhere. */
01597         for (sep= cmds; sep != nil && sep->token[0] != ';'; sep= sep->next) n++;
01598 
01599         name= cmds->token;
01600         res= reserved(name);
01601         if ((second= cmds->next) != nil
01602                 && (third= second->next) != nil)
01603                         fourth= third->next;
01604 
01605                 /* Null command? */
01606         if (n == 0) {
01607                 voidtoken();
01608                 return;
01609         } else
01610                 /* name = [device] value? */
01611         if ((n == 3 || n == 4)
01612                 && !sugar(name)
01613                 && second->token[0] == '='
01614                 && !sugar(third->token)
01615                 && (n == 3 || (n == 4 && third->token[0] == 'd'
01616                                         && !sugar(fourth->token)
01617         ))) {
01618                 char *value= third->token;
01619                 int flags= E_VAR;
01620 
01621                 if (n == 4) { value= fourth->token; flags|= E_DEV; }
01622 
01623                 if ((flags= b_setvar(flags, name, value)) != 0) {
01624                         printf("%s is a %s\n", name,
01625                                 flags & E_RESERVED ? "reserved word" :
01626                                                 "special function");
01627                         err= 1;
01628                 }
01629                 while (cmds != sep) voidtoken();
01630                 return;
01631         } else
01632                 /* name '(arg)' ... ? */
01633         if (n >= 3
01634                 && !sugar(name)
01635                 && second->token[0] == '('
01636         ) {
01637                 token *fun;
01638                 int c, flags, depth;
01639                 char *body;
01640                 size_t len;
01641 
01642                 sep= fun= third;
01643                 depth= 0;
01644                 len= 1;
01645                 while (sep != nil) {
01646                         if ((c= sep->token[0]) == ';' && depth == 0) break;
01647                         len+= strlen(sep->token) + 1;
01648                         sep= sep->next;
01649                         if (c == '{') depth++;
01650                         if (c == '}' && --depth == 0) break;
01651                 }
01652 
01653                 body= malloc(len * sizeof(char));
01654                 *body= 0;
01655 
01656                 while (fun != sep) {
01657                         strcat(body, fun->token);
01658                         if (!sugar(fun->token)
01659                                 && !sugar(fun->next->token)
01660                         ) strcat(body, " ");
01661                         fun= fun->next;
01662                 }
01663                 second->token[strlen(second->token)-1]= 0;
01664 
01665                 if (depth != 0) {
01666                         printf("Missing '}'\n");
01667                         err= 1;
01668                 } else
01669                 if ((flags= b_setenv(E_FUNCTION, name,
01670                                         second->token+1, body)) != 0) {
01671                         printf("%s is a %s\n", name,
01672                                 flags & E_RESERVED ? "reserved word" :
01673                                                 "special variable");
01674                         err= 1;
01675                 }
01676                 while (cmds != sep) voidtoken();
01677                 free(body);
01678                 return;
01679         } else
01680                 /* Grouping? */
01681         if (name[0] == '{') {
01682                 token **acmds= &cmds->next;
01683                 char *t;
01684                 int depth= 1;
01685 
01686                 /* Find and remove matching '}' */
01687                 depth= 1;
01688                 while (*acmds != nil) {
01689                         t= (*acmds)->token;
01690                         if (t[0] == '{') depth++;
01691                         if (t[0] == '}' && --depth == 0) { t[0]= ';'; break; }
01692                         acmds= &(*acmds)->next;
01693                 }
01694                 voidtoken();
01695                 return;
01696         } else
01697                 /* Command coming up, check if ESC typed. */
01698         if (interrupt()) {
01699                 return;
01700         } else
01701                 /* unset name ..., echo word ...? */
01702         if (n >= 1 && (res == R_UNSET || res == R_ECHO)) {
01703                 char *arg= poptoken(), *p;
01704 
01705                 for (;;) {
01706                         free(arg);
01707                         if (cmds == sep) break;
01708                         arg= poptoken();
01709                         if (res == R_UNSET) {   /* unset arg */
01710                                 b_unset(arg);
01711                         } else {                /* echo arg */
01712                                 p= arg;
01713                                 while (*p != 0) {
01714                                         if (*p != '\\') {
01715                                                 putch(*p);
01716                                         } else
01717                                         switch (*++p) {
01718                                         case 0:
01719                                                 if (cmds == sep) return;
01720                                                 continue;
01721                                         case 'n':
01722                                                 putch('\n');
01723                                                 break;
01724                                         case 'v':
01725                                                 printf(version);
01726                                                 break;
01727                                         case 'c':
01728                                                 clear_screen();
01729                                                 break;
01730                                         case 'w':
01731                                                 for (;;) {
01732                                                         if (interrupt())
01733                                                                 return;
01734                                                         if (getch() == '\n')
01735                                                                 break;
01736                                                 }
01737                                                 break;
01738                                         default:
01739                                                 putch(*p);
01740                                         }
01741                                         p++;
01742                                 }
01743                                 putch(cmds != sep ? ' ' : '\n');
01744                         }
01745                 }
01746                 return;
01747         } else
01748                 /* boot -opts? */
01749         if (n == 2 && res == R_BOOT && second->token[0] == '-') {
01750                 static char optsvar[]= "bootopts";
01751                 (void) b_setvar(E_VAR, optsvar, second->token);
01752                 voidtoken();
01753                 voidtoken();
01754                 bootminix();
01755                 b_unset(optsvar);
01756                 return;
01757         } else
01758                 /* boot device, ls dir, delay msec? */
01759         if (n == 2 && (res == R_BOOT || res == R_CTTY
01760                         || res == R_DELAY || res == R_LS)
01761         ) {
01762                 if (res == R_BOOT) boot_device(second->token);
01763                 if (res == R_CTTY) ctty(second->token);
01764                 if (res == R_DELAY) delay(second->token);
01765                 if (res == R_LS) ls(second->token);
01766                 voidtoken();
01767                 voidtoken();
01768                 return;
01769         } else
01770                 /* trap msec command? */
01771         if (n == 3 && res == R_TRAP && numeric(second->token)) {
01772                 long msec= a2l(second->token);
01773 
01774                 voidtoken();
01775                 voidtoken();
01776                 schedule(msec, poptoken());
01777                 return;
01778         } else
01779                 /* Simple command. */
01780         if (n == 1) {
01781                 char *body;
01782                 int ok= 0;
01783 
01784                 name= poptoken();
01785 
01786                 switch (res) {
01787                 case R_BOOT:    bootminix();    ok= 1;  break;
01788                 case R_DELAY:   delay("500");   ok= 1;  break;
01789                 case R_LS:      ls(null);       ok= 1;  break;
01790                 case R_MENU:    menu();         ok= 1;  break;
01791                 case R_SAVE:    save_parameters(); ok= 1;break;
01792                 case R_SET:     show_env();     ok= 1;  break;
01793                 case R_HELP:    help();         ok= 1;  break;
01794                 case R_EXIT:    exit(0);
01795                 case R_OFF:     off();          ok= 1;  break;
01796                 }
01797 
01798                 /* Command to check bootparams: */
01799                 if (strcmp(name, ":") == 0) ok= 1;
01800 
01801                 /* User defined function. */
01802                 if (!ok && (body= b_body(name)) != nil) {
01803                         (void) tokenize(&cmds, body);
01804                         ok= 1;
01805                 }
01806                 if (!ok) printf("%s: unknown function", name);
01807                 free(name);
01808                 if (ok) return;
01809         } else {
01810                 /* Syntax error. */
01811                 printf("Can't parse:");
01812                 while (cmds != sep) {
01813                         printf(" %s", cmds->token); voidtoken();
01814                 }
01815         }
01816 
01817         /* Getting here means that the command is not understood. */
01818         printf("\nTry 'help'\n");
01819         err= 1;
01820 }
01821 
01822 int run_trailer(void)
01823 /* Run the trailer function between loading Minix and handing control to it.
01824  * Return true iff there was no error.
01825  */
01826 {
01827         token *save_cmds= cmds;
01828 
01829         cmds= nil;
01830         (void) tokenize(&cmds, "trailer");
01831         while (cmds != nil) execute();
01832         cmds= save_cmds;
01833         return !err;
01834 }
01835 
01836 void monitor(void)
01837 /* Read a line and tokenize it. */
01838 {
01839         char *line;
01840 
01841         unschedule();           /* Kill a trap. */
01842         err= 0;                 /* Clear error state. */
01843 
01844         if (istty) printf("%s>", bootdev.name);
01845         line= readline();
01846         (void) tokenize(&cmds, line);
01847         free(line);
01848         (void) escape();        /* Forget if ESC typed. */
01849 }
01850 
01851 #if BIOS
01852 
01853 unsigned char cdspec[25];
01854 void bootcdinfo(u32_t, int *, int drive);
01855 
01856 void boot(void)
01857 /* Load Minix and start it, among other things. */
01858 {
01859 
01860         /* Initialize tables. */
01861         initialize();
01862 
01863         /* Get environment variables from the parameter sector. */
01864         get_parameters();
01865 
01866         while (1) {
01867                 /* While there are commands, execute them! */
01868 
01869                 while (cmds != nil) execute();
01870 
01871                 /* The "monitor" is just a "read one command" thing. */
01872                 monitor();
01873         }
01874 }
01875 #endif /* BIOS */
01876 
01877 #if UNIX
01878 
01879 void main(int argc, char **argv)
01880 /* Do not load or start anything, just edit parameters. */
01881 {
01882         int i;
01883         char bootcode[SECTOR_SIZE];
01884         struct termios rawterm;
01885 
01886         istty= (argc <= 2 && tcgetattr(0, &termbuf) == 0);
01887 
01888         if (argc < 2) {
01889                 fprintf(stderr, "Usage: edparams device [command ...]\n");
01890                 exit(1);
01891         }
01892 
01893         /* Go over the arguments, changing control characters to spaces. */
01894         for (i= 2; i < argc; i++) {
01895                 char *p;
01896 
01897                 for (p= argv[i]; *p != 0; p++) {
01898                         if ((unsigned) *p < ' ' && *p != '\n') *p= ' ';
01899                 }
01900         }
01901 
01902         bootdev.name= argv[1];
01903         if (strncmp(bootdev.name, "/dev/", 5) == 0) bootdev.name+= 5;
01904         if ((bootdev.device= open(argv[1], O_RDWR, 0666)) < 0)
01905                 fatal(bootdev.name);
01906 
01907         /* Check if it is a bootable Minix device. */
01908         if (readsectors(mon2abs(bootcode), lowsec, 1) != 0
01909                 || memcmp(bootcode, boot_magic, sizeof(boot_magic)) != 0) {
01910                 fprintf(stderr, "edparams: %s: not a bootable Minix device\n",
01911                         bootdev.name);
01912                 exit(1);
01913         }
01914 
01915         /* Print greeting message.  */
01916         if (istty) printf("Boot parameters editor.\n");
01917 
01918         signal(SIGINT, trap);
01919         signal(SIGALRM, trap);
01920 
01921         if (istty) {
01922                 rawterm= termbuf;
01923                 rawterm.c_lflag&= ~(ICANON|ECHO|IEXTEN);
01924                 rawterm.c_cc[VINTR]= ESC;
01925                 if (tcsetattr(0, TCSANOW, &rawterm) < 0) fatal("");
01926         }
01927 
01928         /* Get environment variables from the parameter sector. */
01929         get_parameters();
01930 
01931         i= 2;
01932         for (;;) {
01933                 /* While there are commands, execute them! */
01934                 while (cmds != nil || i < argc) {
01935                         if (cmds == nil) {
01936                                 /* A command line command. */
01937                                 parse_code(argv[i++]);
01938                         }
01939                         execute();
01940 
01941                         /* Bail out on errors if not interactive. */
01942                         if (err && !istty) exit(1);
01943                 }
01944 
01945                 /* Commands on the command line? */
01946                 if (argc > 2) break;
01947 
01948                 /* The "monitor" is just a "read one command" thing. */
01949                 monitor();
01950         }
01951         exit(0);
01952 }
01953 #endif /* UNIX */
01954 
01955 /*
01956  * $PchId: boot.c,v 1.14 2002/02/27 19:46:14 philip Exp $
01957  */

Generated on Fri Apr 14 22:56:37 2006 for minix by  doxygen 1.4.6