1 /* boot.c - Load and start Minix. Author: Kees J. Bot
5 char version
[]= "2.20";
7 #define BIOS (!UNIX) /* Either uses BIOS or UNIX syscalls. */
10 #define _POSIX_SOURCE 1
13 #include <sys/types.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>
29 #include <kernel/const.h>
30 #include <kernel/type.h>
42 #define EXTERN /* Empty */
45 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
46 #define arraylimit(a) ((a) + arraysize(a))
47 #define between(a, c, z) ((unsigned) ((c) - (a)) <= ((z) - (a)))
49 int fsok
= -1; /* File system state. Initially unknown. */
51 static int block_size
;
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.
59 unsigned char boot_spec
[24];
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
67 static struct errlist
{
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" }
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" },
109 struct errlist
*errp
;
111 for (errp
= errlist
; errp
< arraylimit(errlist
); errp
++) {
112 if (errp
->err
== err
) return errp
->what
;
114 return "Unknown error";
117 char *unix_err(int err
)
118 /* Translate the few errors rawfs can give. */
121 case ENOENT
: return "No such file or directory";
122 case ENOTDIR
: return "Not a directory";
123 default: return "Unknown error";
127 void rwerr(char *rw
, off_t sec
, int err
)
129 printf("\n%s error 0x%02x (%s) at sector %ld absolute\n",
130 rw
, err
, bios_err(err
), sec
);
133 void readerr(off_t sec
, int err
) { rwerr("Read", sec
, err
); }
134 void writerr(off_t sec
, int err
) { rwerr("Write", sec
, err
); }
136 void readblock(off_t blk
, char *buf
, int block_size
)
137 /* Read blocks for the rawfs package. */
140 u32_t sec
= lowsec
+ blk
* RATIO(block_size
);
143 printf("block_size 0\n");
147 if ((r
= readsectors(mon2abs(buf
), sec
, 1 * RATIO(block_size
))) != 0) {
148 readerr(sec
, r
); exit(1);
159 /* The Minix boot block must start with these bytes: */
160 char boot_magic
[] = { 0x31, 0xC0, 0x8E, 0xD8, 0xFA, 0x8E, 0xD0, 0xBC };
163 char *name
; /* Name of device. */
164 int device
; /* Device to edit parameters. */
167 struct termios termbuf
;
170 void quit(int status
)
172 if (istty
) (void) tcsetattr(0, TCSANOW
, &termbuf
);
176 #define exit(s) quit(s)
178 void report(char *label
)
179 /* edparams: label: No such file or directory */
181 fprintf(stderr
, "edparams: %s: %s\n", label
, strerror(errno
));
184 void fatal(char *label
)
190 void *alloc(void *m
, size_t n
)
192 m
= m
== nil
? malloc(n
) : realloc(m
, n
);
193 if (m
== nil
) fatal("");
197 #define malloc(n) alloc(nil, n)
198 #define realloc(m, n) alloc(m, n)
200 #define mon2abs(addr) ((void *) (addr))
202 int rwsectors(int rw
, void *addr
, u32_t sec
, int nsec
)
205 size_t len
= nsec
* SECTOR_SIZE
;
207 if (lseek(bootdev
.device
, sec
* SECTOR_SIZE
, SEEK_SET
) == -1)
211 r
= read(bootdev
.device
, (char *) addr
, len
);
213 r
= write(bootdev
.device
, (char *) addr
, len
);
215 if (r
== -1) return errno
;
216 if (r
!= len
) return EIO
;
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)
227 void readblock(off_t blk
, char *buf
, int block_size
)
228 /* Read blocks for the rawfs package. */
230 if(!block_size
) fatal("block_size 0");
232 if (lseek(bootdev
.device
, blk
* block_size
, SEEK_SET
) == -1
233 || read(bootdev
.device
, buf
, block_size
) != block_size
)
239 sig_atomic_t trapsig
;
249 if (trapsig
== SIGINT
) {
256 static unsigned char unchar
;
270 switch (read(0, &c
, 1)) {
272 if (errno
!= EINTR
) fatal("");
275 if (istty
) putch('\n');
278 if (istty
&& c
== termbuf
.c_cc
[VEOF
]) {
286 #define ungetch(c) ((void) (unchar = (c)))
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]")
298 /* Read a line including a newline with echoing. */
306 line
= malloc(z
* sizeof(char));
311 if (strchr("\b\177\25\30", c
) != nil
) {
312 /* Backspace, DEL, ctrl-U, or ctrl-X. */
317 } while (c
== '\25' || c
== '\30');
319 if (c
< ' ' && c
!= '\n') {
326 line
= realloc(line
, z
* sizeof(char));
335 /* Recognize special tokens. */
337 return strchr("=(){};\n", tok
[0]) != nil
;
340 char *onetoken(char **aline
)
341 /* Returns a string with one token for tokenize. */
347 /* Skip spaces and runs of newlines. */
348 while (*line
== ' ' || (*line
== '\n' && line
[1] == '\n')) line
++;
352 /* Don't do odd junk (nor the terminating 0!). */
353 if ((unsigned) *line
< ' ' && *line
!= '\n') return nil
;
356 /* Function argument, anything goes but () must match. */
359 while ((unsigned) *line
>= ' ') {
360 if (*line
== '(') depth
++;
361 if (*line
++ == ')' && --depth
== 0) break;
365 /* Single character token. */
368 /* Multicharacter token. */
369 do line
++; while ((unsigned) *line
> ' ' && !sugar(line
));
372 tok
= malloc((n
+ 1) * sizeof(char));
373 memcpy(tok
, *aline
, n
);
375 if (tok
[0] == '\n') tok
[0]= ';'; /* ';' same as '\n' */
381 /* Typed commands form strings of tokens. */
383 typedef struct token
{
384 struct token
*next
; /* Next in a command chain. */
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.
399 while ((tok
= onetoken(&line
)) != nil
) {
400 newcmd
= malloc(sizeof(*newcmd
));
402 newcmd
->next
= *acmds
;
404 acmds
= &newcmd
->next
;
409 token
*cmds
; /* String of commands to execute. */
410 int err
; /* Set on an error. */
413 /* Pop one token off the command chain. */
416 char *tok
= cmd
->token
;
425 /* Remove one token from the command chain. */
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.)
435 if (cmds
!= nil
&& cmds
->token
[0] != ';') (void) tokenize(&cmds
, ";");
436 (void) tokenize(&cmds
, code
);
440 /* Clean up after an ESC has been typed. */
456 int device
, primary
, secondary
;
459 int get_master(char *master
, struct part_entry
**table
, u32_t pos
)
460 /* Read a master boot sector and its partition table. */
463 struct part_entry
*pe
, **pt
;
465 if ((r
= readsectors(mon2abs(master
), pos
, 1)) != 0) return r
;
467 pe
= (struct part_entry
*) (master
+ PART_TABLE_OFF
);
468 for (pt
= table
; pt
< table
+ NR_PARTITIONS
; pt
++) *pt
= pe
++;
470 /* DOS has the misguided idea that partition tables must be sorted. */
471 if (pos
!= 0) return 0; /* But only the primary. */
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
;
485 void initialize(void)
487 char master
[SECTOR_SIZE
];
488 struct part_entry
*table
[NR_PARTITIONS
];
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).
497 u32_t oldaddr
= caddr
;
498 u32_t memend
= mem
[0].base
+ mem
[0].size
;
499 u32_t newaddr
= (memend
- runsize
) & ~0x0000FL
;
501 u32_t dma64k
= (memend
- 1) & ~0x0FFFFL
;
504 /* Check if data segment crosses a 64K boundary. */
505 if (newaddr
+ (daddr
- caddr
) < dma64k
) {
506 newaddr
= (dma64k
- runsize
) & ~0x0000FL
;
510 /* Set the new caddr for relocate. */
513 /* Copy code and data. */
514 raw_copy(newaddr
, oldaddr
, runsize
);
516 /* Make the copy running. */
521 /* Take the monitor out of the memory map if we have memory to spare,
522 * and also keep the BIOS data area safe (1.5K), plus a bit extra for
523 * where we may have to put a.out headers for older kernels.
525 if (mon_return
= (mem
[1].size
> 512*1024L)) mem
[0].size
= newaddr
;
529 /* Find out what the boot device and partition was. */
531 bootdev
.device
= device
;
533 bootdev
.secondary
= -1;
537 strcpy(bootdev
.name
, "fd0");
538 bootdev
.name
[2] += bootdev
.device
;
542 /* Disk: Get the partition table from the very first sector, and
543 * determine the partition we booted from using the information from
544 * the booted partition entry as passed on by the bootstrap (rem_part).
545 * All we need from it is the partition offset.
547 raw_copy(mon2abs(&lowsec
),
548 vec2abs(&rem_part
) + offsetof(struct part_entry
, lowsec
),
551 masterpos
= 0; /* Master bootsector position. */
554 /* Extract the partition table from the master boot sector. */
555 if ((r
= get_master(master
, table
, masterpos
)) != 0) {
556 readerr(masterpos
, r
); exit(1);
559 /* See if you can find "lowsec" back. */
560 for (p
= 0; p
< NR_PARTITIONS
; p
++) {
561 if (lowsec
- table
[p
]->lowsec
< table
[p
]->size
) break;
564 if (lowsec
== table
[p
]->lowsec
) { /* Found! */
565 if (bootdev
.primary
< 0)
568 bootdev
.secondary
= p
;
572 if (p
== NR_PARTITIONS
|| bootdev
.primary
>= 0
573 || table
[p
]->sysind
!= MINIX_PART
) {
574 /* The boot partition cannot be named, this only means
575 * that "bootdev" doesn't work.
581 /* See if the primary partition is subpartitioned. */
583 masterpos
= table
[p
]->lowsec
;
585 strcpy(bootdev
.name
, "d0p0");
586 bootdev
.name
[1] += (device
- 0x80);
587 bootdev
.name
[3] += bootdev
.primary
;
588 if (bootdev
.secondary
>= 0) {
589 strcat(bootdev
.name
, "s0");
590 bootdev
.name
[5] += bootdev
.secondary
;
594 /* Take the monitor out of the memory map if we have memory to spare,
595 * note that only half our PSP is needed at the new place, the first
596 * half is to be kept in its place.
598 if (mem
[1].size
> 0) mem
[0].size
= newaddr
+ 0x80 - mem
[0].base
;
600 /* Parse the command line. */
603 while (between('\1', *argp
, ' ')) argp
++;
605 while (!between('\0', *argp
, ' ')) argp
++;
606 while (between('\1', *argp
, ' ')) *argp
++= 0;
608 printf("\nUsage: boot <vdisk> [commands ...]\n");
611 drun
= *argp
== 0 ? "main" : argp
;
613 if ((r
= dev_open()) != 0) {
614 printf("\n%s: Error %02x (%s)\n", vdisk
, r
, bios_err(r
));
618 /* Find the active partition on the virtual disk. */
619 if ((r
= get_master(master
, table
, 0)) != 0) {
620 readerr(0, r
); exit(1);
623 strcpy(bootdev
.name
, "d0");
625 for (p
= 0; p
< NR_PARTITIONS
; p
++) {
626 if (table
[p
]->bootind
!= 0 && table
[p
]->sysind
== MINIX_PART
) {
628 strcat(bootdev
.name
, "p0");
629 bootdev
.name
[3] += p
;
630 lowsec
= table
[p
]->lowsec
;
639 /* Reserved names: */
641 R_NULL
, R_BOOT
, R_CTTY
, R_DELAY
, R_ECHO
, R_EXIT
, R_HELP
,
642 R_LS
, R_MENU
, R_OFF
, R_SAVE
, R_SET
, R_TRAP
, R_UNSET
645 char resnames
[][6] = {
646 "", "boot", "ctty", "delay", "echo", "exit", "help",
647 "ls", "menu", "off", "save", "set", "trap", "unset",
650 /* Using this for all null strings saves a lot of memory. */
651 #define null (resnames[0])
653 int reserved(char *s
)
654 /* Recognize reserved strings. */
658 for (r
= R_BOOT
; r
<= R_UNSET
; r
++) {
659 if (strcmp(s
, resnames
[r
]) == 0) return r
;
665 /* Free a non-null string. */
667 if (s
!= nil
&& s
!= null
) free(s
);
670 char *copystr(char *s
)
671 /* Copy a non-null string using malloc. */
675 if (*s
== 0) return null
;
676 c
= malloc((strlen(s
) + 1) * sizeof(char));
681 int is_default(environment
*e
)
683 return (e
->flags
& E_SPECIAL
) && e
->defval
== nil
;
686 environment
**searchenv(char *name
)
688 environment
**aenv
= &env
;
690 while (*aenv
!= nil
&& strcmp((*aenv
)->name
, name
) != 0) {
691 aenv
= &(*aenv
)->next
;
697 #define b_getenv(name) (*searchenv(name))
698 /* Return the environment *structure* belonging to name, or nil if not found. */
700 char *b_value(char *name
)
701 /* The value of a variable. */
703 environment
*e
= b_getenv(name
);
705 return e
== nil
|| !(e
->flags
& E_VAR
) ? nil
: e
->value
;
708 char *b_body(char *name
)
709 /* The value of a function. */
711 environment
*e
= b_getenv(name
);
713 return e
== nil
|| !(e
->flags
& E_FUNCTION
) ? nil
: e
->value
;
716 int b_setenv(int flags
, char *name
, char *arg
, char *value
)
717 /* Change the value of an environment variable. Returns the flags of the
718 * variable if you are not allowed to change it, 0 otherwise.
721 environment
**aenv
, *e
;
723 if (*(aenv
= searchenv(name
)) == nil
) {
724 if (reserved(name
)) return E_RESERVED
;
725 e
= malloc(sizeof(*e
));
726 e
->name
= copystr(name
);
734 /* Don't change special variables to functions or vv. */
735 if (e
->flags
& E_SPECIAL
736 && (e
->flags
& E_FUNCTION
) != (flags
& E_FUNCTION
)
739 e
->flags
= (e
->flags
& E_STICKY
) | flags
;
747 e
->arg
= copystr(arg
);
748 e
->value
= copystr(value
);
753 int b_setvar(int flags
, char *name
, char *value
)
754 /* Set variable or simple function. */
758 if((r
=b_setenv(flags
, name
, null
, value
))) {
765 void b_unset(char *name
)
766 /* Remove a variable from the environment. A special variable is reset to
770 environment
**aenv
, *e
;
772 if ((e
= *(aenv
= searchenv(name
))) == nil
) return;
774 if (e
->flags
& E_SPECIAL
) {
775 if (e
->defval
!= nil
) {
797 if (*a
== '-') { sign
= -1; a
++; }
799 while (between('0', *a
, '9')) n
= n
* 10 + (*a
++ - '0');
804 char *ul2a(u32_t n
, unsigned b
)
805 /* Transform a long number to ascii at base b, (b >= 8). */
807 static char num
[(CHAR_BIT
* sizeof(n
) + 2) / 3 + 1];
808 char *a
= arraylimit(num
) - 1;
809 static char hex
[16] = "0123456789ABCDEF";
811 do *--a
= hex
[(int) (n
% b
)]; while ((n
/= b
) > 0);
815 char *ul2a10(u32_t n
)
816 /* Transform a long number to ascii at base 10. */
821 unsigned a2x(char *a
)
829 if (between('0', c
, '9')) c
= c
- '0' + 0x0;
831 if (between('A', c
, 'F')) c
= c
- 'A' + 0xA;
833 if (between('a', c
, 'f')) c
= c
- 'a' + 0xa;
842 void get_parameters(void)
844 char params
[SECTOR_SIZE
+ 1];
846 int r
, bus
, processor
;
848 static char bus_type
[][4] = {
851 static char vid_type
[][4] = {
852 "mda", "cga", "ega", "ega", "vga", "vga"
854 static char vid_chrome
[][6] = {
858 /* Variables that Minix needs: */
859 b_setvar(E_SPECIAL
|E_VAR
|E_DEV
, "rootdev", "ram");
860 b_setvar(E_SPECIAL
|E_VAR
|E_DEV
, "ramimagedev", "bootdev");
861 b_setvar(E_SPECIAL
|E_VAR
, "ramsize", "0");
863 processor
= getprocessor();
864 if(processor
== 1586) processor
= 686;
865 b_setvar(E_SPECIAL
|E_VAR
, "processor", ul2a10(processor
));
866 b_setvar(E_SPECIAL
|E_VAR
, "bus", bus_type
[get_bus()]);
867 b_setvar(E_SPECIAL
|E_VAR
, "video", vid_type
[get_video()]);
868 b_setvar(E_SPECIAL
|E_VAR
, "chrome", vid_chrome
[get_video() & 1]);
870 for (mp
= mem
; mp
< arraylimit(mem
); mp
++) {
871 if (mp
->size
== 0) continue;
872 if (params
[0] != 0) strcat(params
, ",");
873 strcat(params
, ul2a(mp
->base
, 0x10));
875 strcat(params
, ul2a(mp
->size
, 0x10));
877 b_setvar(E_SPECIAL
|E_VAR
, "memory", params
);
880 b_setvar(E_SPECIAL
|E_VAR
, "dosfile-d0", vdisk
);
885 b_setvar(E_SPECIAL
|E_VAR
, "processor", "?");
886 b_setvar(E_SPECIAL
|E_VAR
, "bus", "?");
887 b_setvar(E_SPECIAL
|E_VAR
, "video", "?");
888 b_setvar(E_SPECIAL
|E_VAR
, "chrome", "?");
889 b_setvar(E_SPECIAL
|E_VAR
, "memory", "?");
890 b_setvar(E_SPECIAL
|E_VAR
, "c0", "?");
893 /* Variables boot needs: */
894 b_setvar(E_SPECIAL
|E_VAR
, "image", "boot/image");
895 b_setvar(E_SPECIAL
|E_FUNCTION
, "leader",
896 "echo --- Welcome to MINIX 3. This is the boot monitor. ---\\n");
897 b_setvar(E_SPECIAL
|E_FUNCTION
, "main", "menu");
898 b_setvar(E_SPECIAL
|E_FUNCTION
, "trailer", "");
900 /* Default hidden menu function: */
901 b_setenv(E_RESERVED
|E_FUNCTION
, null
, "=,Start MINIX", "boot");
903 /* Tokenize bootparams sector. */
904 if ((r
= readsectors(mon2abs(params
), lowsec
+PARAMSEC
, 1)) != 0) {
905 readerr(lowsec
+PARAMSEC
, r
);
908 params
[SECTOR_SIZE
]= 0;
909 acmds
= tokenize(&cmds
, params
);
911 /* Stuff the default action into the command chain. */
913 (void) tokenize(acmds
, ":;");
915 (void) tokenize(tokenize(acmds
, ":;leader;"), drun
);
917 (void) tokenize(acmds
, ":;leader;main");
923 void addparm(char *n
)
925 while (*n
!= 0 && *addptr
!= 0) *addptr
++ = *n
++;
928 void save_parameters(void)
929 /* Save nondefault environment variables to the bootparams sector. */
932 char params
[SECTOR_SIZE
+ 1];
935 /* Default filling: */
936 memset(params
, '\n', SECTOR_SIZE
);
938 /* Don't touch the 0! */
939 params
[SECTOR_SIZE
]= 0;
942 for (e
= env
; e
!= nil
; e
= e
->next
) {
943 if (e
->flags
& E_RESERVED
|| is_default(e
)) continue;
946 if (e
->flags
& E_FUNCTION
) {
951 addparm((e
->flags
& (E_DEV
|E_SPECIAL
)) != E_DEV
956 printf("The environment is too big\n");
962 /* Save the parameters on disk. */
963 if ((r
= writesectors(mon2abs(params
), lowsec
+PARAMSEC
, 1)) != 0) {
964 writerr(lowsec
+PARAMSEC
, r
);
965 printf("Can't save environment\n");
970 /* Show the environment settings. */
976 for (e
= env
; e
!= nil
; e
= e
->next
) {
977 if (e
->flags
& E_RESERVED
) continue;
978 if (!istty
&& is_default(e
)) continue;
980 if (e
->flags
& E_FUNCTION
) {
981 printf("%s(%s) %s\n", e
->name
, e
->arg
, e
->value
);
983 printf(is_default(e
) ? "%s = (%s)\n" : "%s = %s\n",
987 if (e
->next
!= nil
&& istty
&& ++more
% 20 == 0) {
990 if (c
== ESC
|| c
> ' ') {
992 if (c
> ' ') ungetch(c
);
995 printf("\b\b\b\b\b\b");
1000 int numprefix(char *s
, char **ps
)
1001 /* True iff s is a string of digits. *ps will be set to the first nondigit
1002 * if non-nil, otherwise the string should end.
1007 while (between('0', *n
, '9')) n
++;
1009 if (n
== s
) return 0;
1011 if (ps
== nil
) return *n
== 0;
1017 int numeric(char *s
)
1019 return numprefix(s
, (char **) nil
);
1024 /* Device numbers of standard MINIX devices. */
1025 #define DEV_FD0 0x0200
1026 static dev_t dev_cNd0
[] = { 0x0300, 0x0800, 0x0A00, 0x0C00, 0x1000 };
1027 #define minor_p0s0 128
1029 static int block_size
;
1031 dev_t
name2dev(char *name
)
1032 /* Translate, say, /dev/c0d0p2 to a device number. If the name can't be
1033 * found on the boot device, then do some guesswork. The global structure
1034 * "tmpdev" will be filled in based on the name, so that "boot d1p0" knows
1035 * what device to boot without interpreting device numbers.
1044 /* "boot *d0p2" means: make partition 2 active before you boot it. */
1045 if ((activate
= (name
[0] == '*'))) name
++;
1047 /* The special name "bootdev" must be translated to the boot device. */
1048 if (strcmp(name
, "bootdev") == 0) {
1049 if (bootdev
.device
== -1) {
1050 printf("The boot device could not be named\n");
1057 /* If our boot device doesn't have a file system, or we want to know
1058 * what a name means for the BIOS, then we need to interpret the
1059 * device name ourselves: "fd" = floppy, "c0d0" = hard disk, etc.
1061 tmpdev
.device
= tmpdev
.primary
= tmpdev
.secondary
= -1;
1064 if (strncmp(n
, "/dev/", 5) == 0) n
+= 5;
1066 if (strcmp(n
, "ram") == 0) {
1069 if (strcmp(n
, "boot") == 0) {
1072 if (n
[0] == 'f' && n
[1] == 'd' && numeric(n
+2)) {
1074 tmpdev
.device
= a2l(n
+2);
1075 dev
= DEV_FD0
+ tmpdev
.device
;
1077 if ((n
[0] == 'h' || n
[0] == 's') && n
[1] == 'd' && numprefix(n
+2, &s
)
1078 && (*s
== 0 || (between('a', *s
, 'd') && s
[1] == 0))
1080 /* Old style hard disk (backwards compatibility.) */
1082 tmpdev
.device
= dev
/ (1 + NR_PARTITIONS
);
1083 tmpdev
.primary
= (dev
% (1 + NR_PARTITIONS
)) - 1;
1086 tmpdev
.secondary
= *s
- 'a';
1088 + (tmpdev
.device
* NR_PARTITIONS
1089 + tmpdev
.primary
) * NR_PARTITIONS
1092 tmpdev
.device
+= 0x80;
1093 dev
+= n
[0] == 'h' ? dev_cNd0
[0] : dev_cNd0
[2];
1098 if (n
[0] == 'c' && between('0', n
[1], '4')) {
1099 ctrlr
= (n
[1] - '0');
1103 if (n
[0] == 'd' && between('0', n
[1], '7')) {
1104 tmpdev
.device
= (n
[1] - '0');
1106 if (n
[0] == 'p' && between('0', n
[1], '3')) {
1107 tmpdev
.primary
= (n
[1] - '0');
1109 if (n
[0] == 's' && between('0', n
[1], '3')) {
1110 tmpdev
.secondary
= (n
[1] - '0');
1116 dev
= dev_cNd0
[ctrlr
];
1117 if (tmpdev
.secondary
< 0) {
1118 dev
+= tmpdev
.device
* (NR_PARTITIONS
+1)
1119 + (tmpdev
.primary
+ 1);
1122 + (tmpdev
.device
* NR_PARTITIONS
1123 + tmpdev
.primary
) * NR_PARTITIONS
1126 tmpdev
.device
+= 0x80;
1130 /* Look the name up on the boot device for the UNIX device number. */
1131 if (fsok
== -1) fsok
= r_super(&block_size
) != 0;
1133 /* The current working directory is "/dev". */
1134 ino
= r_lookup(r_lookup(ROOT_INO
, "dev"), name
);
1137 /* Name has been found, extract the device number. */
1139 if (!S_ISBLK(st
.st_mode
)) {
1140 printf("%s is not a block device\n", name
);
1148 if (tmpdev
.primary
< 0) activate
= 0; /* Careful now! */
1151 printf("Can't recognize '%s' as a device\n", name
);
1158 static void apm_perror(char *label
, u16_t ax
)
1166 case 0x01: str
= "APM functionality disabled"; break;
1167 case 0x03: str
= "interface not connected"; break;
1168 case 0x09: str
= "unrecognized device ID"; break;
1169 case 0x0A: str
= "parameter value out of range"; break;
1170 case 0x0B: str
= "interface not engaged"; break;
1171 case 0x60: str
= "unable to enter requested state"; break;
1172 case 0x86: str
= "APM not present"; break;
1173 default: printf("%s: error 0x%02x\n", label
, ah
); return;
1175 printf("%s: %s\n", label
, str
);
1178 #define apm_printf printf
1180 #define apm_perror(label, ax) ((void)0)
1184 static void off(void)
1189 /* Try to switch off the system. Print diagnostic information
1190 * that can be useful if the operation fails.
1193 be
.ax
= 0x5300; /* APM, Installation check */
1194 be
.bx
= 0; /* Device, APM BIOS */
1196 if (be
.flags
& FL_CARRY
)
1198 apm_perror("APM installation check failed", be
.ax
);
1201 if (be
.bx
!= (('P' << 8) | 'M'))
1203 apm_printf("APM signature not found (got 0x%04x)\n", be
.bx
);
1209 ah
= (ah
>> 4)*10 + (ah
& 0xf);
1212 al
= (al
>> 4)*10 + (al
& 0xf);
1213 apm_printf("APM version %u.%u%s%s%s%s%s\n",
1215 (be
.cx
& 0x1) ? ", 16-bit PM" : "",
1216 (be
.cx
& 0x2) ? ", 32-bit PM" : "",
1217 (be
.cx
& 0x4) ? ", CPU-Idle" : "",
1218 (be
.cx
& 0x8) ? ", APM-disabled" : "",
1219 (be
.cx
& 0x10) ? ", APM-disengaged" : "");
1222 be
.ax
= 0x5301; /* APM, Real mode interface connect */
1223 be
.bx
= 0x0000; /* APM BIOS */
1225 if (be
.flags
& FL_CARRY
)
1227 apm_perror("APM real mode connect failed", be
.ax
);
1231 /* Ask for a seat upgrade */
1232 be
.ax
= 0x530e; /* APM, Driver Version */
1233 be
.bx
= 0x0000; /* BIOS */
1234 be
.cx
= 0x0102; /* version 1.2 */
1236 if (be
.flags
& FL_CARRY
)
1238 apm_perror("Set driver version failed", be
.ax
);
1242 /* Is this version really worth reporting. Well, if the system
1243 * does switch off, you won't see it anyway.
1247 ah
= (ah
>> 4)*10 + (ah
& 0xf);
1250 al
= (al
>> 4)*10 + (al
& 0xf);
1251 apm_printf("Got APM connection version %u.%u\n", ah
, al
);
1254 be
.ax
= 0x5308; /* APM, Enable/disable power management */
1255 be
.bx
= 0x0001; /* All device managed by APM BIOS */
1257 /* For old APM 1.0 systems, we need 0xffff. Assume that those
1258 * systems do not exist.
1260 be
.bx
= 0xffff; /* All device managed by APM BIOS (compat) */
1262 be
.cx
= 0x0001; /* Enable power management */
1264 if (be
.flags
& FL_CARRY
)
1266 apm_perror("Enable power management failed", be
.ax
);
1271 be
.ax
= 0x5307; /* APM, Set Power State */
1272 be
.bx
= 0x0001; /* All devices managed by APM */
1273 be
.cx
= 0x0003; /* Off */
1275 if (be
.flags
& FL_CARRY
)
1277 apm_perror("Set power state failed", be
.ax
);
1281 apm_printf("Power off sequence successfully completed.\n\n");
1282 apm_printf("Ha, ha, just kidding!\n");
1286 be
.ax
= 0x5304; /* APM, interface disconnect */
1287 be
.bx
= 0x0000; /* APM BIOS */
1289 if (be
.flags
& FL_CARRY
)
1291 apm_perror("APM interface disconnect failed", be
.ax
);
1297 #define B_NOSIG -1 /* "No signature" error code. */
1299 int exec_bootstrap(void)
1300 /* Load boot sector from the disk or floppy described by tmpdev and execute it.
1304 char master
[SECTOR_SIZE
];
1305 struct part_entry
*table
[NR_PARTITIONS
], dummy
, *active
= &dummy
;
1310 /* Select a partition table entry. */
1311 while (tmpdev
.primary
>= 0) {
1312 masterpos
= active
->lowsec
;
1314 if ((r
= get_master(master
, table
, masterpos
)) != 0) return r
;
1316 active
= table
[tmpdev
.primary
];
1318 /* How does one check a partition table entry? */
1319 if (active
->sysind
== NO_PART
) return B_NOSIG
;
1321 tmpdev
.primary
= tmpdev
.secondary
;
1322 tmpdev
.secondary
= -1;
1325 if (activate
&& !active
->bootind
) {
1326 for (n
= 0; n
< NR_PARTITIONS
; n
++) table
[n
]->bootind
= 0;
1327 active
->bootind
= ACTIVE_FLAG
;
1331 /* Read the boot sector. */
1332 if ((r
= readsectors(BOOTPOS
, active
->lowsec
, 1)) != 0) return r
;
1334 /* Check signature word. */
1335 if (get_word(BOOTPOS
+SIGNATOFF
) != SIGNATURE
) return B_NOSIG
;
1337 /* Write the partition table if a member must be made active. */
1338 if (dirty
&& (r
= writesectors(mon2abs(master
), masterpos
, 1)) != 0)
1341 bootstrap(device
, active
);
1344 void boot_device(char *devname
)
1345 /* Boot the device named by devname. */
1347 dev_t dev
= name2dev(devname
);
1348 int save_dev
= device
;
1352 if (tmpdev
.device
< 0) {
1353 if (dev
!= -1) printf("Can't boot from %s\n", devname
);
1357 /* Change current device and try to load and execute its bootstrap. */
1358 device
= tmpdev
.device
;
1360 if ((r
= dev_open()) == 0) r
= exec_bootstrap();
1362 err
= r
== B_NOSIG
? "Not bootable" : bios_err(r
);
1363 printf("Can't boot %s: %s\n", devname
, err
);
1365 /* Restore boot device setting. */
1370 void ctty(char *line
)
1375 if (between('0', line
[0], '3') && line
[1] == 0) {
1376 serial_init(line
[0] - '0');
1378 printf("Bad serial line number: %s\n", line
);
1384 void boot_device(char *devname
)
1385 /* No booting of other devices under DOS. */
1387 printf("Can't boot devices under DOS\n");
1390 void ctty(char *line
)
1391 /* Don't know how to handle serial lines under DOS. */
1393 printf("No serial line support under DOS\n");
1400 /* List the contents of a directory. */
1404 char name
[NAME_MAX
+1];
1406 if (fsok
== -1) fsok
= r_super(&block_size
) != 0;
1409 /* (,) construct because r_stat returns void */
1410 if ((ino
= r_lookup(ROOT_INO
, dir
)) == 0 ||
1411 (r_stat(ino
, &st
), r_readdir(name
)) == -1)
1413 printf("ls: %s: %s\n", dir
, unix_err(errno
));
1416 (void) r_readdir(name
); /* Skip ".." too. */
1418 while ((ino
= r_readdir(name
)) != 0) printf("%s/%s\n", dir
, name
);
1421 u32_t
milli_time(void)
1423 return get_tick() * MSEC_PER_TICK
;
1426 u32_t
milli_since(u32_t base
)
1428 return (milli_time() + (TICKS_PER_DAY
*MSEC_PER_TICK
) - base
)
1429 % (TICKS_PER_DAY
*MSEC_PER_TICK
);
1433 u32_t Tbase
, Tcount
;
1435 void unschedule(void)
1436 /* Invalidate a waiting command. */
1440 if (Thandler
!= nil
) {
1446 void schedule(long msec
, char *cmd
)
1447 /* Schedule command at a certain time from now. */
1451 Tbase
= milli_time();
1457 /* Check if the timer expired for getch(). */
1459 return (Thandler
!= nil
&& milli_since(Tbase
) >= Tcount
);
1462 void delay(char *msec
)
1463 /* Delay for a given time. */
1467 if ((count
= a2l(msec
)) == 0) return;
1474 } while (!interrupt() && !expired() && milli_since(base
) < count
);
1477 enum whatfun
{ NOFUN
, SELECT
, DEFFUN
, USERFUN
} menufun(environment
*e
)
1479 if (!(e
->flags
& E_FUNCTION
) || e
->arg
[0] == 0) return NOFUN
;
1480 if (e
->arg
[1] != ',') return SELECT
;
1481 return e
->flags
& E_RESERVED
? DEFFUN
: USERFUN
;
1485 /* By default: Show a simple menu.
1486 * Multiple kernels/images: Show extra selection options.
1487 * User defined function: Kill the defaults and show these.
1488 * Wait for a keypress and execute the given function.
1495 /* Just a default menu? */
1496 for (e
= env
; e
!= nil
; e
= e
->next
) if (menufun(e
) == USERFUN
) def
= 0;
1498 printf("\nHit a key as follows:\n\n");
1500 /* Show the choices. */
1501 for (e
= env
; e
!= nil
; e
= e
->next
) {
1502 switch (menufun(e
)) {
1507 printf(" %c %s\n", e
->arg
[0], e
->arg
+2);
1510 printf(" %c Select %s kernel\n", e
->arg
[0],e
->name
);
1516 /* Wait for a keypress. */
1519 if (interrupt() || expired()) return;
1523 for (e
= env
; e
!= nil
; e
= e
->next
) {
1524 switch (menufun(e
)) {
1529 if (c
== e
->arg
[0]) choice
= e
->value
;
1532 } while (choice
== nil
);
1534 /* Execute the chosen function. */
1536 (void) tokenize(&cmds
, choice
);
1540 /* Not everyone is a rocket scientist. */
1546 static struct help info
[] = {
1548 { "rootdev", "Root device" },
1549 { "ramimagedev", "Device to use as RAM disk image " },
1550 { "ramsize", "RAM disk size (if no image device) " },
1551 { "bootdev", "Special name for the boot device" },
1552 { "fd0, d0p2, c0d0p1s0", "Devices (as in /dev)" },
1553 { "image", "Name of the boot image to use" },
1554 { "main", "Startup function" },
1555 { "bootdelay", "Delay in msec after loading image" },
1556 { nil
, "Commands:" },
1557 { "name = [device] value", "Set environment variable" },
1558 { "name() { ... }", "Define function" },
1559 { "name(key,text) { ... }",
1560 "A menu option like: minix(=,Start MINIX) {boot}" },
1561 { "name", "Call function" },
1562 { "boot [device]", "Boot Minix or another O.S." },
1563 { "ctty [line]", "Duplicate to serial line" },
1564 { "delay [msec]", "Delay (500 msec default)" },
1565 { "echo word ...", "Display the words" },
1566 { "ls [directory]", "List contents of directory" },
1567 { "menu", "Show menu and choose menu option" },
1568 { "save / set", "Save or show environment" },
1569 { "trap msec command", "Schedule command " },
1570 { "unset name ...", "Unset variable or set to default" },
1571 { "exit / off", "Exit the Monitor / Power off" },
1574 for (pi
= info
; pi
< arraylimit(info
); pi
++) {
1575 if (pi
->thing
!= nil
) printf(" %-24s- ", pi
->thing
);
1576 printf("%s\n", pi
->help
);
1581 /* Get one command from the command chain and execute it. */
1583 token
*second
, *third
, *fourth
, *sep
;
1589 /* An error occured, stop interpreting. */
1590 while (cmds
!= nil
) voidtoken();
1594 if (expired()) { /* Timer expired? */
1595 parse_code(Thandler
);
1599 /* There must be a separator lurking somewhere. */
1600 for (sep
= cmds
; sep
!= nil
&& sep
->token
[0] != ';'; sep
= sep
->next
) n
++;
1603 res
= reserved(name
);
1604 if ((second
= cmds
->next
) != nil
1605 && (third
= second
->next
) != nil
)
1606 fourth
= third
->next
;
1613 /* name = [device] value? */
1614 if ((n
== 3 || n
== 4)
1616 && second
->token
[0] == '='
1617 && !sugar(third
->token
)
1618 && (n
== 3 || (n
== 4 && third
->token
[0] == 'd'
1619 && !sugar(fourth
->token
)
1621 char *value
= third
->token
;
1624 if (n
== 4) { value
= fourth
->token
; flags
|= E_DEV
; }
1626 if ((flags
= b_setvar(flags
, name
, value
)) != 0) {
1627 printf("%s is a %s\n", name
,
1628 flags
& E_RESERVED
? "reserved word" :
1629 "special function");
1632 while (cmds
!= sep
) voidtoken();
1635 /* name '(arg)' ... ? */
1638 && second
->token
[0] == '('
1641 int c
, flags
, depth
;
1648 while (sep
!= nil
) {
1649 if ((c
= sep
->token
[0]) == ';' && depth
== 0) break;
1650 len
+= strlen(sep
->token
) + 1;
1652 if (c
== '{') depth
++;
1653 if (c
== '}' && --depth
== 0) break;
1656 body
= malloc(len
* sizeof(char));
1659 while (fun
!= sep
) {
1660 strcat(body
, fun
->token
);
1661 if (!sugar(fun
->token
)
1662 && !sugar(fun
->next
->token
)
1663 ) strcat(body
, " ");
1666 second
->token
[strlen(second
->token
)-1]= 0;
1669 printf("Missing '}'\n");
1672 if ((flags
= b_setenv(E_FUNCTION
, name
,
1673 second
->token
+1, body
)) != 0) {
1674 printf("%s is a %s\n", name
,
1675 flags
& E_RESERVED
? "reserved word" :
1676 "special variable");
1679 while (cmds
!= sep
) voidtoken();
1684 if (name
[0] == '{') {
1685 token
**acmds
= &cmds
->next
;
1689 /* Find and remove matching '}' */
1691 while (*acmds
!= nil
) {
1693 if (t
[0] == '{') depth
++;
1694 if (t
[0] == '}' && --depth
== 0) { t
[0]= ';'; break; }
1695 acmds
= &(*acmds
)->next
;
1700 /* Command coming up, check if ESC typed. */
1704 /* unset name ..., echo word ...? */
1705 if (n
>= 1 && (res
== R_UNSET
|| res
== R_ECHO
)) {
1706 char *arg
= poptoken(), *p
;
1710 if (cmds
== sep
) break;
1712 if (res
== R_UNSET
) { /* unset arg */
1714 } else { /* echo arg */
1722 if (cmds
== sep
) return;
1737 if (getch() == '\n')
1746 putch(cmds
!= sep
? ' ' : '\n');
1752 if (n
== 2 && res
== R_BOOT
&& second
->token
[0] == '-') {
1753 static char optsvar
[]= "bootopts";
1754 (void) b_setvar(E_VAR
, optsvar
, second
->token
);
1761 /* boot device, ls dir, delay msec? */
1762 if (n
== 2 && (res
== R_BOOT
|| res
== R_CTTY
1763 || res
== R_DELAY
|| res
== R_LS
)
1765 if (res
== R_BOOT
) boot_device(second
->token
);
1766 if (res
== R_CTTY
) ctty(second
->token
);
1767 if (res
== R_DELAY
) delay(second
->token
);
1768 if (res
== R_LS
) ls(second
->token
);
1773 /* trap msec command? */
1774 if (n
== 3 && res
== R_TRAP
&& numeric(second
->token
)) {
1775 long msec
= a2l(second
->token
);
1779 schedule(msec
, poptoken());
1782 /* Simple command. */
1790 case R_BOOT
: bootminix(); ok
= 1; break;
1791 case R_DELAY
: delay("500"); ok
= 1; break;
1792 case R_LS
: ls(null
); ok
= 1; break;
1793 case R_MENU
: menu(); ok
= 1; break;
1794 case R_SAVE
: save_parameters(); ok
= 1;break;
1795 case R_SET
: show_env(); ok
= 1; break;
1796 case R_HELP
: help(); ok
= 1; break;
1797 case R_EXIT
: exit(0);
1798 case R_OFF
: off(); ok
= 1; break;
1799 case R_CTTY
: ctty(nil
); ok
= 1; break;
1802 /* Command to check bootparams: */
1803 if (strcmp(name
, ":") == 0) ok
= 1;
1805 /* User defined function. */
1806 if (!ok
&& (body
= b_body(name
)) != nil
) {
1807 (void) tokenize(&cmds
, body
);
1810 if (!ok
) printf("%s: unknown function", name
);
1815 printf("Can't parse:");
1816 while (cmds
!= sep
) {
1817 printf(" %s", cmds
->token
); voidtoken();
1821 /* Getting here means that the command is not understood. */
1822 printf("\nTry 'help'\n");
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.
1831 token
*save_cmds
= cmds
;
1834 (void) tokenize(&cmds
, "trailer");
1835 while (cmds
!= nil
) execute();
1841 /* Read a line and tokenize it. */
1845 unschedule(); /* Kill a trap. */
1846 err
= 0; /* Clear error state. */
1848 if (istty
) printf("%s>", bootdev
.name
);
1850 (void) tokenize(&cmds
, line
);
1852 (void) escape(); /* Forget if ESC typed. */
1857 unsigned char cdspec
[25];
1858 void bootcdinfo(u32_t
, int *, int drive
);
1861 /* Load Minix and start it, among other things. */
1864 /* Initialize tables. */
1867 /* Get environment variables from the parameter sector. */
1871 /* While there are commands, execute them! */
1873 while (cmds
!= nil
) execute();
1875 /* The "monitor" is just a "read one command" thing. */
1883 void main(int argc
, char **argv
)
1884 /* Do not load or start anything, just edit parameters. */
1887 char bootcode
[SECTOR_SIZE
];
1888 struct termios rawterm
;
1890 istty
= (argc
<= 2 && tcgetattr(0, &termbuf
) == 0);
1893 fprintf(stderr
, "Usage: edparams device [command ...]\n");
1897 /* Go over the arguments, changing control characters to spaces. */
1898 for (i
= 2; i
< argc
; i
++) {
1901 for (p
= argv
[i
]; *p
!= 0; p
++) {
1902 if ((unsigned) *p
< ' ' && *p
!= '\n') *p
= ' ';
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
);
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",
1919 /* Print greeting message. */
1920 if (istty
) printf("Boot parameters editor.\n");
1922 signal(SIGINT
, trap
);
1923 signal(SIGALRM
, trap
);
1927 rawterm
.c_lflag
&= ~(ICANON
|ECHO
|IEXTEN
);
1928 rawterm
.c_cc
[VINTR
]= ESC
;
1929 if (tcsetattr(0, TCSANOW
, &rawterm
) < 0) fatal("");
1932 /* Get environment variables from the parameter sector. */
1937 /* While there are commands, execute them! */
1938 while (cmds
!= nil
|| i
< argc
) {
1940 /* A command line command. */
1941 parse_code(argv
[i
++]);
1945 /* Bail out on errors if not interactive. */
1946 if (err
&& !istty
) exit(1);
1949 /* Commands on the command line? */
1950 if (argc
> 2) break;
1952 /* The "monitor" is just a "read one command" thing. */
1960 * $PchId: boot.c,v 1.14 2002/02/27 19:46:14 philip Exp $