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>
22 #include <minix/config.h>
23 #include <minix/type.h>
24 #include <minix/com.h>
25 #include <minix/dmap.h>
26 #include <minix/const.h>
27 #include <minix/minlib.h>
28 #include <minix/syslib.h>
30 #include <kernel/const.h>
31 #include <kernel/type.h>
32 #include <sys/video.h>
44 #define EXTERN /* Empty */
47 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
48 #define arraylimit(a) ((a) + arraysize(a))
49 #define between(a, c, z) ((unsigned) ((c) - (a)) <= ((z) - (a)))
53 u16_t vid_port
; /* Video i/o port. */
54 u32_t vid_mem_base
; /* Video memory base address. */
55 u32_t vid_mem_size
; /* Video memory size. */
57 int fsok
= -1; /* File system state. Initially unknown. */
59 static int block_size
;
63 /* this data is reserved for BIOS int 0x13 to put the 'specification packet'
64 * in. It has a structure of course, but we don't define a struct because
65 * of compiler padding. We fiddle out the bytes ourselves later.
67 unsigned char boot_spec
[24];
69 char *bios_err(int err
)
70 /* Translate BIOS error code to a readable string. (This is a rare trait
71 * known as error checking and reporting. Take a good look at it, you won't
75 static struct errlist
{
81 { 0x01, "Invalid command" },
82 { 0x02, "Address mark not found" },
83 { 0x03, "Disk write-protected" },
84 { 0x04, "Sector not found" },
85 { 0x05, "Reset failed" },
86 { 0x06, "Floppy disk removed" },
87 { 0x07, "Bad parameter table" },
88 { 0x08, "DMA overrun" },
89 { 0x09, "DMA crossed 64 KB boundary" },
90 { 0x0A, "Bad sector flag" },
91 { 0x0B, "Bad track flag" },
92 { 0x0C, "Media type not found" },
93 { 0x0D, "Invalid number of sectors on format" },
94 { 0x0E, "Control data address mark detected" },
95 { 0x0F, "DMA arbitration level out of range" },
96 { 0x10, "Uncorrectable CRC or ECC data error" },
97 { 0x11, "ECC corrected data error" },
98 { 0x20, "Controller failed" },
99 { 0x40, "Seek failed" },
100 { 0x80, "Disk timed-out" },
101 { 0xAA, "Drive not ready" },
102 { 0xBB, "Undefined error" },
103 { 0xCC, "Write fault" },
104 { 0xE0, "Status register error" },
105 { 0xFF, "Sense operation failed" }
107 { 0x00, "No error" },
108 { 0x01, "Function number invalid" },
109 { 0x02, "File not found" },
110 { 0x03, "Path not found" },
111 { 0x04, "Too many open files" },
112 { 0x05, "Access denied" },
113 { 0x06, "Invalid handle" },
114 { 0x0C, "Access code invalid" },
117 struct errlist
*errp
;
119 for (errp
= errlist
; errp
< arraylimit(errlist
); errp
++) {
120 if (errp
->err
== err
) return errp
->what
;
122 return "Unknown error";
125 char *unix_err(int err
)
126 /* Translate the few errors rawfs can give. */
129 case ENOENT
: return "No such file or directory";
130 case ENOTDIR
: return "Not a directory";
131 default: return "Unknown error";
135 void rwerr(char *rw
, off_t sec
, int err
)
137 printf("\n%s error 0x%02x (%s) at sector %ld absolute\n",
138 rw
, err
, bios_err(err
), sec
);
141 void readerr(off_t sec
, int err
) { rwerr("Read", sec
, err
); }
142 void writerr(off_t sec
, int err
) { rwerr("Write", sec
, err
); }
144 void readblock(off_t blk
, char *buf
, int block_size
)
145 /* Read blocks for the rawfs package. */
148 u32_t sec
= lowsec
+ blk
* RATIO(block_size
);
151 printf("block_size 0\n");
155 if ((r
= readsectors(mon2abs(buf
), sec
, 1 * RATIO(block_size
))) != 0) {
156 readerr(sec
, r
); exit(1);
167 /* The Minix boot block must start with these bytes: */
168 char boot_magic
[] = { 0x31, 0xC0, 0x8E, 0xD8, 0xFA, 0x8E, 0xD0, 0xBC };
171 char *name
; /* Name of device. */
172 int device
; /* Device to edit parameters. */
175 struct termios termbuf
;
178 void quit(int status
)
180 if (istty
) (void) tcsetattr(0, TCSANOW
, &termbuf
);
184 #define exit(s) quit(s)
186 void report(char *label
)
187 /* edparams: label: No such file or directory */
189 fprintf(stderr
, "edparams: %s: %s\n", label
, strerror(errno
));
192 void fatal(char *label
)
198 void *alloc(void *m
, size_t n
)
200 m
= m
== nil
? malloc(n
) : realloc(m
, n
);
201 if (m
== nil
) fatal("");
205 #define malloc(n) alloc(nil, n)
206 #define realloc(m, n) alloc(m, n)
208 #define mon2abs(addr) ((void *) (addr))
210 int rwsectors(int rw
, void *addr
, u32_t sec
, int nsec
)
213 size_t len
= nsec
* SECTOR_SIZE
;
215 if (lseek(bootdev
.device
, sec
* SECTOR_SIZE
, SEEK_SET
) == -1)
219 r
= read(bootdev
.device
, (char *) addr
, len
);
221 r
= write(bootdev
.device
, (char *) addr
, len
);
223 if (r
== -1) return errno
;
224 if (r
!= len
) return EIO
;
228 #define readsectors(a, s, n) rwsectors(0, (a), (s), (n))
229 #define writesectors(a, s, n) rwsectors(1, (a), (s), (n))
230 #define readerr(sec, err) (errno= (err), report(bootdev.name))
231 #define writerr(sec, err) (errno= (err), report(bootdev.name))
232 #define putch(c) putchar(c)
233 #define unix_err(err) strerror(err)
235 void readblock(off_t blk
, char *buf
, int block_size
)
236 /* Read blocks for the rawfs package. */
238 if(!block_size
) fatal("block_size 0");
240 if (lseek(bootdev
.device
, blk
* block_size
, SEEK_SET
) == -1
241 || read(bootdev
.device
, buf
, block_size
) != block_size
)
247 sig_atomic_t trapsig
;
257 if (trapsig
== SIGINT
) {
264 static unsigned char unchar
;
278 switch (read(0, &c
, 1)) {
280 if (errno
!= EINTR
) fatal("");
283 if (istty
) putch('\n');
286 if (istty
&& c
== termbuf
.c_cc
[VEOF
]) {
294 #define ungetch(c) ((void) (unchar = (c)))
296 #define get_tick() ((u32_t) time(nil))
297 #define clear_screen() printf("[clear]")
298 #define boot_device(device) printf("[boot %s]\n", device)
299 #define ctty(line) printf("[ctty %s]\n", line)
300 #define bootminix() (run_trailer() && printf("[boot]\n"))
301 #define off() printf("[off]")
306 /* Read a line including a newline with echoing. */
314 line
= malloc(z
* sizeof(char));
319 if (strchr("\b\177\25\30", c
) != nil
) {
320 /* Backspace, DEL, ctrl-U, or ctrl-X. */
325 } while (c
== '\25' || c
== '\30');
327 if (c
< ' ' && c
!= '\n') {
334 line
= realloc(line
, z
* sizeof(char));
343 /* Recognize special tokens. */
345 return strchr("=(){};\n", tok
[0]) != nil
;
348 char *onetoken(char **aline
)
349 /* Returns a string with one token for tokenize. */
355 /* Skip spaces and runs of newlines. */
356 while (*line
== ' ' || (*line
== '\n' && line
[1] == '\n')) line
++;
360 /* Don't do odd junk (nor the terminating 0!). */
361 if ((unsigned) *line
< ' ' && *line
!= '\n') return nil
;
364 /* Function argument, anything goes but () must match. */
367 while ((unsigned) *line
>= ' ') {
368 if (*line
== '(') depth
++;
369 if (*line
++ == ')' && --depth
== 0) break;
373 /* Single character token. */
376 /* Multicharacter token. */
377 do line
++; while ((unsigned) *line
> ' ' && !sugar(line
));
380 tok
= malloc((n
+ 1) * sizeof(char));
381 memcpy(tok
, *aline
, n
);
383 if (tok
[0] == '\n') tok
[0]= ';'; /* ';' same as '\n' */
389 /* Typed commands form strings of tokens. */
391 typedef struct token
{
392 struct token
*next
; /* Next in a command chain. */
396 token
**tokenize(token
**acmds
, char *line
)
397 /* Takes a line apart to form tokens. The tokens are inserted into a command
398 * chain at *acmds. Tokenize returns a reference to where another line could
399 * be added. Tokenize looks at spaces as token separators, and recognizes only
400 * ';', '=', '{', '}', and '\n' as single character tokens. One token is
401 * formed from '(' and ')' with anything in between as long as more () match.
407 while ((tok
= onetoken(&line
)) != nil
) {
408 newcmd
= malloc(sizeof(*newcmd
));
410 newcmd
->next
= *acmds
;
412 acmds
= &newcmd
->next
;
417 token
*cmds
; /* String of commands to execute. */
418 int err
; /* Set on an error. */
421 /* Pop one token off the command chain. */
424 char *tok
= cmd
->token
;
433 /* Remove one token from the command chain. */
438 void parse_code(char *code
)
439 /* Tokenize a string of monitor code, making sure there is a delimiter. It is
440 * to be executed next. (Prepended to the current input.)
443 if (cmds
!= nil
&& cmds
->token
[0] != ';') (void) tokenize(&cmds
, ";");
444 (void) tokenize(&cmds
, code
);
448 /* Clean up after an ESC has been typed. */
464 int device
, primary
, secondary
;
467 int get_master(char *master
, struct part_entry
**table
, u32_t pos
)
468 /* Read a master boot sector and its partition table. */
471 struct part_entry
*pe
, **pt
;
473 if ((r
= readsectors(mon2abs(master
), pos
, 1)) != 0) return r
;
475 pe
= (struct part_entry
*) (master
+ PART_TABLE_OFF
);
476 for (pt
= table
; pt
< table
+ NR_PARTITIONS
; pt
++) *pt
= pe
++;
478 /* DOS has the misguided idea that partition tables must be sorted. */
479 if (pos
!= 0) return 0; /* But only the primary. */
483 for (pt
= table
; pt
< table
+ NR_PARTITIONS
-1; pt
++) {
484 if (pt
[0]->sysind
== NO_PART
485 || pt
[0]->lowsec
> pt
[1]->lowsec
) {
486 pe
= pt
[0]; pt
[0]= pt
[1]; pt
[1]= pe
;
493 void initialize(void)
495 char master
[SECTOR_SIZE
];
496 struct part_entry
*table
[NR_PARTITIONS
];
501 /* Copy the boot program to the far end of low memory, this must be
502 * done to get out of the way of Minix, and to put the data area
503 * cleanly inside a 64K chunk if using BIOS I/O (no DMA problems).
505 u32_t oldaddr
= caddr
;
506 u32_t memend
= mem
[0].base
+ mem
[0].size
;
507 u32_t newaddr
= (memend
- runsize
) & ~0x0000FL
;
509 u32_t dma64k
= (memend
- 1) & ~0x0FFFFL
;
512 /* Check if data segment crosses a 64K boundary. */
513 if (newaddr
+ (daddr
- caddr
) < dma64k
) {
514 newaddr
= (dma64k
- runsize
) & ~0x0000FL
;
518 /* Set the new caddr for relocate. */
521 /* Copy code and data. */
522 raw_copy(newaddr
, oldaddr
, runsize
);
524 /* Make the copy running. */
529 /* Take the monitor out of the memory map if we have memory to spare,
530 * and also keep the BIOS data area safe (1.5K), plus a bit extra for
531 * where we may have to put a.out headers for older kernels.
533 if (mon_return
= (mem
[1].size
> 512*1024L)) mem
[0].size
= newaddr
;
537 /* Find out what the boot device and partition was. */
539 bootdev
.device
= device
;
541 bootdev
.secondary
= -1;
545 strcpy(bootdev
.name
, "fd0");
546 bootdev
.name
[2] += bootdev
.device
;
550 /* Disk: Get the partition table from the very first sector, and
551 * determine the partition we booted from using the information from
552 * the booted partition entry as passed on by the bootstrap (rem_part).
553 * All we need from it is the partition offset.
555 raw_copy(mon2abs(&lowsec
),
556 vec2abs(&rem_part
) + offsetof(struct part_entry
, lowsec
),
559 masterpos
= 0; /* Master bootsector position. */
562 /* Extract the partition table from the master boot sector. */
563 if ((r
= get_master(master
, table
, masterpos
)) != 0) {
564 readerr(masterpos
, r
); exit(1);
567 /* See if you can find "lowsec" back. */
568 for (p
= 0; p
< NR_PARTITIONS
; p
++) {
569 if (lowsec
- table
[p
]->lowsec
< table
[p
]->size
) break;
572 if (lowsec
== table
[p
]->lowsec
) { /* Found! */
573 if (bootdev
.primary
< 0)
576 bootdev
.secondary
= p
;
580 if (p
== NR_PARTITIONS
|| bootdev
.primary
>= 0
581 || table
[p
]->sysind
!= MINIX_PART
) {
582 /* The boot partition cannot be named, this only means
583 * that "bootdev" doesn't work.
589 /* See if the primary partition is subpartitioned. */
591 masterpos
= table
[p
]->lowsec
;
593 strcpy(bootdev
.name
, "d0p0");
594 bootdev
.name
[1] += (device
- 0x80);
595 bootdev
.name
[3] += bootdev
.primary
;
596 if (bootdev
.secondary
>= 0) {
597 strcat(bootdev
.name
, "s0");
598 bootdev
.name
[5] += bootdev
.secondary
;
601 /* Find out about the video hardware. */
602 raw_copy(mon2abs(&vid_port
), VDU_CRT_BASE_ADDR
, sizeof(vid_port
));
603 if(vid_port
== C_6845
) {
604 vid_mem_base
= COLOR_BASE
;
605 vid_mem_size
= COLOR_SIZE
;
607 vid_mem_base
= MONO_BASE
;
608 vid_mem_size
= MONO_SIZE
;
612 vid_mem_size
= EGA_SIZE
;
615 /* Take the monitor out of the memory map if we have memory to spare,
616 * note that only half our PSP is needed at the new place, the first
617 * half is to be kept in its place.
619 if (mem
[1].size
> 0) mem
[0].size
= newaddr
+ 0x80 - mem
[0].base
;
621 /* Parse the command line. */
624 while (between('\1', *argp
, ' ')) argp
++;
626 while (!between('\0', *argp
, ' ')) argp
++;
627 while (between('\1', *argp
, ' ')) *argp
++= 0;
629 printf("\nUsage: boot <vdisk> [commands ...]\n");
632 drun
= *argp
== 0 ? "main" : argp
;
634 if ((r
= dev_open()) != 0) {
635 printf("\n%s: Error %02x (%s)\n", vdisk
, r
, bios_err(r
));
639 /* Find the active partition on the virtual disk. */
640 if ((r
= get_master(master
, table
, 0)) != 0) {
641 readerr(0, r
); exit(1);
644 strcpy(bootdev
.name
, "d0");
646 for (p
= 0; p
< NR_PARTITIONS
; p
++) {
647 if (table
[p
]->bootind
!= 0 && table
[p
]->sysind
== MINIX_PART
) {
649 strcat(bootdev
.name
, "p0");
650 bootdev
.name
[3] += p
;
651 lowsec
= table
[p
]->lowsec
;
660 /* Reserved names: */
662 R_NULL
, R_BOOT
, R_CTTY
, R_DELAY
, R_ECHO
, R_EXIT
, R_HELP
,
663 R_LS
, R_MENU
, R_OFF
, R_SAVE
, R_SET
, R_TRAP
, R_UNSET
666 char resnames
[][6] = {
667 "", "boot", "ctty", "delay", "echo", "exit", "help",
668 "ls", "menu", "off", "save", "set", "trap", "unset",
671 /* Using this for all null strings saves a lot of memory. */
672 #define null (resnames[0])
674 int reserved(char *s
)
675 /* Recognize reserved strings. */
679 for (r
= R_BOOT
; r
<= R_UNSET
; r
++) {
680 if (strcmp(s
, resnames
[r
]) == 0) return r
;
686 /* Free a non-null string. */
688 if (s
!= nil
&& s
!= null
) free(s
);
691 char *copystr(char *s
)
692 /* Copy a non-null string using malloc. */
696 if (*s
== 0) return null
;
697 c
= malloc((strlen(s
) + 1) * sizeof(char));
702 int is_default(environment
*e
)
704 return (e
->flags
& E_SPECIAL
) && e
->defval
== nil
;
707 environment
**searchenv(char *name
)
709 environment
**aenv
= &env
;
711 while (*aenv
!= nil
&& strcmp((*aenv
)->name
, name
) != 0) {
712 aenv
= &(*aenv
)->next
;
718 #define b_getenv(name) (*searchenv(name))
719 /* Return the environment *structure* belonging to name, or nil if not found. */
721 char *b_value(char *name
)
722 /* The value of a variable. */
724 environment
*e
= b_getenv(name
);
726 return e
== nil
|| !(e
->flags
& E_VAR
) ? nil
: e
->value
;
729 char *b_body(char *name
)
730 /* The value of a function. */
732 environment
*e
= b_getenv(name
);
734 return e
== nil
|| !(e
->flags
& E_FUNCTION
) ? nil
: e
->value
;
737 int b_setenv(int flags
, char *name
, char *arg
, char *value
)
738 /* Change the value of an environment variable. Returns the flags of the
739 * variable if you are not allowed to change it, 0 otherwise.
742 environment
**aenv
, *e
;
744 if (*(aenv
= searchenv(name
)) == nil
) {
745 if (reserved(name
)) return E_RESERVED
;
746 e
= malloc(sizeof(*e
));
747 e
->name
= copystr(name
);
755 /* Don't change special variables to functions or vv. */
756 if (e
->flags
& E_SPECIAL
757 && (e
->flags
& E_FUNCTION
) != (flags
& E_FUNCTION
)
760 e
->flags
= (e
->flags
& E_STICKY
) | flags
;
768 e
->arg
= copystr(arg
);
769 e
->value
= copystr(value
);
774 int b_setvar(int flags
, char *name
, char *value
)
775 /* Set variable or simple function. */
779 if((r
=b_setenv(flags
, name
, null
, value
))) {
786 void b_unset(char *name
)
787 /* Remove a variable from the environment. A special variable is reset to
791 environment
**aenv
, *e
;
793 if ((e
= *(aenv
= searchenv(name
))) == nil
) return;
795 if (e
->flags
& E_SPECIAL
) {
796 if (e
->defval
!= nil
) {
818 if (*a
== '-') { sign
= -1; a
++; }
820 while (between('0', *a
, '9')) n
= n
* 10 + (*a
++ - '0');
825 char *ul2a(u32_t n
, unsigned b
)
826 /* Transform a long number to ascii at base b, (b >= 8). */
828 static char num
[(CHAR_BIT
* sizeof(n
) + 2) / 3 + 1];
829 char *a
= arraylimit(num
) - 1;
830 static char hex
[16] = "0123456789ABCDEF";
832 do *--a
= hex
[(int) (n
% b
)]; while ((n
/= b
) > 0);
836 char *ul2a10(u32_t n
)
837 /* Transform a long number to ascii at base 10. */
842 unsigned a2x(char *a
)
850 if (between('0', c
, '9')) c
= c
- '0' + 0x0;
852 if (between('A', c
, 'F')) c
= c
- 'A' + 0xA;
854 if (between('a', c
, 'f')) c
= c
- 'a' + 0xa;
863 void get_parameters(void)
865 char params
[SECTOR_SIZE
+ 1];
867 int r
, bus
, processor
;
869 static char bus_type
[][4] = {
872 static char vid_type
[][4] = {
873 "mda", "cga", "ega", "ega", "vga", "vga"
875 static char vid_chrome
[][6] = {
879 /* Variables that Minix needs: */
880 b_setvar(E_SPECIAL
|E_VAR
|E_DEV
, "rootdev", "ram");
881 b_setvar(E_SPECIAL
|E_VAR
|E_DEV
, "ramimagedev", "bootdev");
882 b_setvar(E_SPECIAL
|E_VAR
, "ramsize", "0");
883 #define STRINGIT2(x) #x
884 #define STRINGIT1(x) STRINGIT2(x)
885 b_setvar(E_SPECIAL
|E_VAR
, "hz", STRINGIT1(DEFAULT_HZ
));
887 processor
= getprocessor();
888 if(processor
== 1586) processor
= 686;
889 b_setvar(E_SPECIAL
|E_VAR
, "processor", ul2a10(processor
));
890 b_setvar(E_SPECIAL
|E_VAR
, "bus", bus_type
[get_bus()]);
891 b_setvar(E_SPECIAL
|E_VAR
, "video", vid_type
[get_video()]);
892 b_setvar(E_SPECIAL
|E_VAR
, "chrome", vid_chrome
[get_video() & 1]);
894 for (mp
= mem
; mp
< arraylimit(mem
); mp
++) {
895 if (mp
->size
== 0) continue;
896 if (params
[0] != 0) strcat(params
, ",");
897 strcat(params
, ul2a(mp
->base
, 0x10));
899 strcat(params
, ul2a(mp
->size
, 0x10));
901 b_setvar(E_SPECIAL
|E_VAR
, "memory", params
);
904 b_setvar(E_SPECIAL
|E_VAR
, "dosfile-d0", vdisk
);
909 b_setvar(E_SPECIAL
|E_VAR
, "processor", "?");
910 b_setvar(E_SPECIAL
|E_VAR
, "bus", "?");
911 b_setvar(E_SPECIAL
|E_VAR
, "video", "?");
912 b_setvar(E_SPECIAL
|E_VAR
, "chrome", "?");
913 b_setvar(E_SPECIAL
|E_VAR
, "memory", "?");
914 b_setvar(E_SPECIAL
|E_VAR
, "c0", "?");
917 /* Variables boot needs: */
918 b_setvar(E_SPECIAL
|E_VAR
, "image", "boot/image");
919 b_setvar(E_SPECIAL
|E_FUNCTION
, "leader",
920 "echo --- Welcome to MINIX 3. This is the boot monitor. ---\\n");
921 b_setvar(E_SPECIAL
|E_FUNCTION
, "main", "menu");
922 b_setvar(E_SPECIAL
|E_FUNCTION
, "trailer", "");
924 /* Default hidden menu function: */
925 b_setenv(E_RESERVED
|E_FUNCTION
, null
, "=,Start MINIX", "boot");
927 /* Tokenize bootparams sector. */
928 if ((r
= readsectors(mon2abs(params
), lowsec
+PARAMSEC
, 1)) != 0) {
929 readerr(lowsec
+PARAMSEC
, r
);
932 params
[SECTOR_SIZE
]= 0;
933 acmds
= tokenize(&cmds
, params
);
935 /* Stuff the default action into the command chain. */
937 (void) tokenize(acmds
, ":;");
939 (void) tokenize(tokenize(acmds
, ":;leader;"), drun
);
941 (void) tokenize(acmds
, ":;leader;main");
947 void addparm(char *n
)
949 while (*n
!= 0 && *addptr
!= 0) *addptr
++ = *n
++;
952 void save_parameters(void)
953 /* Save nondefault environment variables to the bootparams sector. */
956 char params
[SECTOR_SIZE
+ 1];
959 /* Default filling: */
960 memset(params
, '\n', SECTOR_SIZE
);
962 /* Don't touch the 0! */
963 params
[SECTOR_SIZE
]= 0;
966 for (e
= env
; e
!= nil
; e
= e
->next
) {
967 if (e
->flags
& E_RESERVED
|| is_default(e
)) continue;
970 if (e
->flags
& E_FUNCTION
) {
975 addparm((e
->flags
& (E_DEV
|E_SPECIAL
)) != E_DEV
980 printf("The environment is too big\n");
986 /* Save the parameters on disk. */
987 if ((r
= writesectors(mon2abs(params
), lowsec
+PARAMSEC
, 1)) != 0) {
988 writerr(lowsec
+PARAMSEC
, r
);
989 printf("Can't save environment\n");
994 /* Show the environment settings. */
1000 for (e
= env
; e
!= nil
; e
= e
->next
) {
1001 if (e
->flags
& E_RESERVED
) continue;
1002 if (!istty
&& is_default(e
)) continue;
1004 if (e
->flags
& E_FUNCTION
) {
1005 printf("%s(%s) %s\n", e
->name
, e
->arg
, e
->value
);
1007 printf(is_default(e
) ? "%s = (%s)\n" : "%s = %s\n",
1011 if (e
->next
!= nil
&& istty
&& ++more
% 20 == 0) {
1014 if (c
== ESC
|| c
> ' ') {
1016 if (c
> ' ') ungetch(c
);
1019 printf("\b\b\b\b\b\b");
1024 int numprefix(char *s
, char **ps
)
1025 /* True iff s is a string of digits. *ps will be set to the first nondigit
1026 * if non-nil, otherwise the string should end.
1031 while (between('0', *n
, '9')) n
++;
1033 if (n
== s
) return 0;
1035 if (ps
== nil
) return *n
== 0;
1041 int numeric(char *s
)
1043 return numprefix(s
, (char **) nil
);
1048 /* Device numbers of standard MINIX devices. */
1049 #define DEV_FD0 0x0200
1050 static dev_t dev_cNd0
[] = { 0x0300, 0x0800, 0x0A00, 0x0C00, 0x1000 };
1051 #define minor_p0s0 128
1053 static int block_size
;
1055 dev_t
name2dev(char *name
)
1056 /* Translate, say, /dev/c0d0p2 to a device number. If the name can't be
1057 * found on the boot device, then do some guesswork. The global structure
1058 * "tmpdev" will be filled in based on the name, so that "boot d1p0" knows
1059 * what device to boot without interpreting device numbers.
1068 /* "boot *d0p2" means: make partition 2 active before you boot it. */
1069 if ((activate
= (name
[0] == '*'))) name
++;
1071 /* The special name "bootdev" must be translated to the boot device. */
1072 if (strcmp(name
, "bootdev") == 0) {
1073 if (bootdev
.device
== -1) {
1074 printf("The boot device could not be named\n");
1081 /* If our boot device doesn't have a file system, or we want to know
1082 * what a name means for the BIOS, then we need to interpret the
1083 * device name ourselves: "fd" = floppy, "c0d0" = hard disk, etc.
1085 tmpdev
.device
= tmpdev
.primary
= tmpdev
.secondary
= -1;
1088 if (strncmp(n
, "/dev/", 5) == 0) n
+= 5;
1090 if (strcmp(n
, "ram") == 0) {
1093 if (n
[0] == 'f' && n
[1] == 'd' && numeric(n
+2)) {
1095 tmpdev
.device
= a2l(n
+2);
1096 dev
= DEV_FD0
+ tmpdev
.device
;
1098 if ((n
[0] == 'h' || n
[0] == 's') && n
[1] == 'd' && numprefix(n
+2, &s
)
1099 && (*s
== 0 || (between('a', *s
, 'd') && s
[1] == 0))
1101 /* Old style hard disk (backwards compatibility.) */
1103 tmpdev
.device
= dev
/ (1 + NR_PARTITIONS
);
1104 tmpdev
.primary
= (dev
% (1 + NR_PARTITIONS
)) - 1;
1107 tmpdev
.secondary
= *s
- 'a';
1109 + (tmpdev
.device
* NR_PARTITIONS
1110 + tmpdev
.primary
) * NR_PARTITIONS
1113 tmpdev
.device
+= 0x80;
1114 dev
+= n
[0] == 'h' ? dev_cNd0
[0] : dev_cNd0
[2];
1119 if (n
[0] == 'c' && between('0', n
[1], '4')) {
1120 ctrlr
= (n
[1] - '0');
1124 if (n
[0] == 'd' && between('0', n
[1], '7')) {
1125 tmpdev
.device
= (n
[1] - '0');
1127 if (n
[0] == 'p' && between('0', n
[1], '3')) {
1128 tmpdev
.primary
= (n
[1] - '0');
1130 if (n
[0] == 's' && between('0', n
[1], '3')) {
1131 tmpdev
.secondary
= (n
[1] - '0');
1137 dev
= dev_cNd0
[ctrlr
];
1138 if (tmpdev
.secondary
< 0) {
1139 dev
+= tmpdev
.device
* (NR_PARTITIONS
+1)
1140 + (tmpdev
.primary
+ 1);
1143 + (tmpdev
.device
* NR_PARTITIONS
1144 + tmpdev
.primary
) * NR_PARTITIONS
1147 tmpdev
.device
+= 0x80;
1151 /* Look the name up on the boot device for the UNIX device number. */
1152 if (fsok
== -1) fsok
= r_super(&block_size
) != 0;
1154 /* The current working directory is "/dev". */
1155 ino
= r_lookup(r_lookup(ROOT_INO
, "dev"), name
);
1158 /* Name has been found, extract the device number. */
1160 if (!S_ISBLK(st
.st_mode
)) {
1161 printf("%s is not a block device\n", name
);
1169 if (tmpdev
.primary
< 0) activate
= 0; /* Careful now! */
1172 printf("Can't recognize '%s' as a device\n", name
);
1179 static void apm_perror(char *label
, u16_t ax
)
1187 case 0x01: str
= "APM functionality disabled"; break;
1188 case 0x03: str
= "interface not connected"; break;
1189 case 0x09: str
= "unrecognized device ID"; break;
1190 case 0x0A: str
= "parameter value out of range"; break;
1191 case 0x0B: str
= "interface not engaged"; break;
1192 case 0x60: str
= "unable to enter requested state"; break;
1193 case 0x86: str
= "APM not present"; break;
1194 default: printf("%s: error 0x%02x\n", label
, ah
); return;
1196 printf("%s: %s\n", label
, str
);
1199 #define apm_printf printf
1201 #define apm_perror(label, ax) ((void)0)
1205 static void off(void)
1210 /* Try to switch off the system. Print diagnostic information
1211 * that can be useful if the operation fails.
1214 be
.ax
= 0x5300; /* APM, Installation check */
1215 be
.bx
= 0; /* Device, APM BIOS */
1217 if (be
.flags
& FL_CARRY
)
1219 apm_perror("APM installation check failed", be
.ax
);
1222 if (be
.bx
!= (('P' << 8) | 'M'))
1224 apm_printf("APM signature not found (got 0x%04x)\n", be
.bx
);
1230 ah
= (ah
>> 4)*10 + (ah
& 0xf);
1233 al
= (al
>> 4)*10 + (al
& 0xf);
1234 apm_printf("APM version %u.%u%s%s%s%s%s\n",
1236 (be
.cx
& 0x1) ? ", 16-bit PM" : "",
1237 (be
.cx
& 0x2) ? ", 32-bit PM" : "",
1238 (be
.cx
& 0x4) ? ", CPU-Idle" : "",
1239 (be
.cx
& 0x8) ? ", APM-disabled" : "",
1240 (be
.cx
& 0x10) ? ", APM-disengaged" : "");
1243 be
.ax
= 0x5301; /* APM, Real mode interface connect */
1244 be
.bx
= 0x0000; /* APM BIOS */
1246 if (be
.flags
& FL_CARRY
)
1248 apm_perror("APM real mode connect failed", be
.ax
);
1252 /* Ask for a seat upgrade */
1253 be
.ax
= 0x530e; /* APM, Driver Version */
1254 be
.bx
= 0x0000; /* BIOS */
1255 be
.cx
= 0x0102; /* version 1.2 */
1257 if (be
.flags
& FL_CARRY
)
1259 apm_perror("Set driver version failed", be
.ax
);
1263 /* Is this version really worth reporting. Well, if the system
1264 * does switch off, you won't see it anyway.
1268 ah
= (ah
>> 4)*10 + (ah
& 0xf);
1271 al
= (al
>> 4)*10 + (al
& 0xf);
1272 apm_printf("Got APM connection version %u.%u\n", ah
, al
);
1275 be
.ax
= 0x5308; /* APM, Enable/disable power management */
1276 be
.bx
= 0x0001; /* All device managed by APM BIOS */
1278 /* For old APM 1.0 systems, we need 0xffff. Assume that those
1279 * systems do not exist.
1281 be
.bx
= 0xffff; /* All device managed by APM BIOS (compat) */
1283 be
.cx
= 0x0001; /* Enable power management */
1285 if (be
.flags
& FL_CARRY
)
1287 apm_perror("Enable power management failed", be
.ax
);
1292 be
.ax
= 0x5307; /* APM, Set Power State */
1293 be
.bx
= 0x0001; /* All devices managed by APM */
1294 be
.cx
= 0x0003; /* Off */
1296 if (be
.flags
& FL_CARRY
)
1298 apm_perror("Set power state failed", be
.ax
);
1302 apm_printf("Power off sequence successfully completed.\n\n");
1303 apm_printf("Ha, ha, just kidding!\n");
1307 be
.ax
= 0x5304; /* APM, interface disconnect */
1308 be
.bx
= 0x0000; /* APM BIOS */
1310 if (be
.flags
& FL_CARRY
)
1312 apm_perror("APM interface disconnect failed", be
.ax
);
1318 #define B_NOSIG -1 /* "No signature" error code. */
1320 int exec_bootstrap(void)
1321 /* Load boot sector from the disk or floppy described by tmpdev and execute it.
1325 char master
[SECTOR_SIZE
];
1326 struct part_entry
*table
[NR_PARTITIONS
], dummy
, *active
= &dummy
;
1331 /* Select a partition table entry. */
1332 while (tmpdev
.primary
>= 0) {
1333 masterpos
= active
->lowsec
;
1335 if ((r
= get_master(master
, table
, masterpos
)) != 0) return r
;
1337 active
= table
[tmpdev
.primary
];
1339 /* How does one check a partition table entry? */
1340 if (active
->sysind
== NO_PART
) return B_NOSIG
;
1342 tmpdev
.primary
= tmpdev
.secondary
;
1343 tmpdev
.secondary
= -1;
1346 if (activate
&& !active
->bootind
) {
1347 for (n
= 0; n
< NR_PARTITIONS
; n
++) table
[n
]->bootind
= 0;
1348 active
->bootind
= ACTIVE_FLAG
;
1352 /* Read the boot sector. */
1353 if ((r
= readsectors(BOOTPOS
, active
->lowsec
, 1)) != 0) return r
;
1355 /* Check signature word. */
1356 if (get_word(BOOTPOS
+SIGNATOFF
) != SIGNATURE
) return B_NOSIG
;
1358 /* Write the partition table if a member must be made active. */
1359 if (dirty
&& (r
= writesectors(mon2abs(master
), masterpos
, 1)) != 0)
1362 bootstrap(device
, active
);
1365 void boot_device(char *devname
)
1366 /* Boot the device named by devname. */
1368 dev_t dev
= name2dev(devname
);
1369 int save_dev
= device
;
1373 if (tmpdev
.device
< 0) {
1374 if (dev
!= -1) printf("Can't boot from %s\n", devname
);
1378 /* Change current device and try to load and execute its bootstrap. */
1379 device
= tmpdev
.device
;
1381 if ((r
= dev_open()) == 0) r
= exec_bootstrap();
1383 err
= r
== B_NOSIG
? "Not bootable" : bios_err(r
);
1384 printf("Can't boot %s: %s\n", devname
, err
);
1386 /* Restore boot device setting. */
1391 void ctty(char *line
)
1395 } else if (between('0', line
[0], '3') && line
[1] == 0) {
1396 serial_line
= line
[0] - '0';
1398 printf("Bad serial line number: %s\n", line
);
1401 serial_init(serial_line
);
1406 void boot_device(char *devname
)
1407 /* No booting of other devices under DOS. */
1409 printf("Can't boot devices under DOS\n");
1412 void ctty(char *line
)
1413 /* Don't know how to handle serial lines under DOS. */
1415 printf("No serial line support under DOS\n");
1422 /* List the contents of a directory. */
1426 char name
[NAME_MAX
+1];
1428 if (fsok
== -1) fsok
= r_super(&block_size
) != 0;
1431 /* (,) construct because r_stat returns void */
1432 if ((ino
= r_lookup(ROOT_INO
, dir
)) == 0 ||
1433 (r_stat(ino
, &st
), r_readdir(name
)) == -1)
1435 printf("ls: %s: %s\n", dir
, unix_err(errno
));
1438 (void) r_readdir(name
); /* Skip ".." too. */
1440 while ((ino
= r_readdir(name
)) != 0) printf("%s/%s\n", dir
, name
);
1443 u32_t
milli_time(void)
1445 return get_tick() * MSEC_PER_TICK
;
1448 u32_t
milli_since(u32_t base
)
1450 return (milli_time() + (TICKS_PER_DAY
*MSEC_PER_TICK
) - base
)
1451 % (TICKS_PER_DAY
*MSEC_PER_TICK
);
1455 u32_t Tbase
, Tcount
;
1457 void unschedule(void)
1458 /* Invalidate a waiting command. */
1462 if (Thandler
!= nil
) {
1468 void schedule(long msec
, char *cmd
)
1469 /* Schedule command at a certain time from now. */
1473 Tbase
= milli_time();
1479 /* Check if the timer expired for getch(). */
1481 return (Thandler
!= nil
&& milli_since(Tbase
) >= Tcount
);
1484 void delay(char *msec
)
1485 /* Delay for a given time. */
1489 if ((count
= a2l(msec
)) == 0) return;
1496 } while (!interrupt() && !expired() && milli_since(base
) < count
);
1499 enum whatfun
{ NOFUN
, SELECT
, DEFFUN
, USERFUN
} menufun(environment
*e
)
1501 if (!(e
->flags
& E_FUNCTION
) || e
->arg
[0] == 0) return NOFUN
;
1502 if (e
->arg
[1] != ',') return SELECT
;
1503 return e
->flags
& E_RESERVED
? DEFFUN
: USERFUN
;
1507 /* By default: Show a simple menu.
1508 * Multiple kernels/images: Show extra selection options.
1509 * User defined function: Kill the defaults and show these.
1510 * Wait for a keypress and execute the given function.
1517 /* Just a default menu? */
1518 for (e
= env
; e
!= nil
; e
= e
->next
) if (menufun(e
) == USERFUN
) def
= 0;
1520 printf("\nHit a key as follows:\n\n");
1522 /* Show the choices. */
1523 for (e
= env
; e
!= nil
; e
= e
->next
) {
1524 switch (menufun(e
)) {
1529 printf(" %c %s\n", e
->arg
[0], e
->arg
+2);
1532 printf(" %c Select %s kernel\n", e
->arg
[0],e
->name
);
1538 /* Wait for a keypress. */
1541 if (interrupt() || expired()) return;
1545 for (e
= env
; e
!= nil
; e
= e
->next
) {
1546 switch (menufun(e
)) {
1551 if (c
== e
->arg
[0]) choice
= e
->value
;
1554 } while (choice
== nil
);
1556 /* Execute the chosen function. */
1558 (void) tokenize(&cmds
, choice
);
1562 /* Not everyone is a rocket scientist. */
1568 static struct help info
[] = {
1570 { "rootdev", "Root device" },
1571 { "ramimagedev", "Device to use as RAM disk image " },
1572 { "ramsize", "RAM disk size (if no image device) " },
1573 { "bootdev", "Special name for the boot device" },
1574 { "fd0, d0p2, c0d0p1s0", "Devices (as in /dev)" },
1575 { "image", "Name of the boot image to use" },
1576 { "main", "Startup function" },
1577 { "bootdelay", "Delay in msec after loading image" },
1578 { nil
, "Commands:" },
1579 { "name = [device] value", "Set environment variable" },
1580 { "name() { ... }", "Define function" },
1581 { "name(key,text) { ... }",
1582 "A menu option like: minix(=,Start MINIX) {boot}" },
1583 { "name", "Call function" },
1584 { "boot [device]", "Boot Minix or another O.S." },
1585 { "ctty [line]", "Duplicate to serial line" },
1586 { "delay [msec]", "Delay (500 msec default)" },
1587 { "echo word ...", "Display the words" },
1588 { "ls [directory]", "List contents of directory" },
1589 { "menu", "Show menu and choose menu option" },
1590 { "save / set", "Save or show environment" },
1591 { "trap msec command", "Schedule command " },
1592 { "unset name ...", "Unset variable or set to default" },
1593 { "exit / off", "Exit the Monitor / Power off" },
1596 for (pi
= info
; pi
< arraylimit(info
); pi
++) {
1597 if (pi
->thing
!= nil
) printf(" %-24s- ", pi
->thing
);
1598 printf("%s\n", pi
->help
);
1603 /* Get one command from the command chain and execute it. */
1605 token
*second
, *third
, *fourth
, *sep
;
1611 /* An error occured, stop interpreting. */
1612 while (cmds
!= nil
) voidtoken();
1616 if (expired()) { /* Timer expired? */
1617 parse_code(Thandler
);
1621 /* There must be a separator lurking somewhere. */
1622 for (sep
= cmds
; sep
!= nil
&& sep
->token
[0] != ';'; sep
= sep
->next
) n
++;
1625 res
= reserved(name
);
1626 if ((second
= cmds
->next
) != nil
1627 && (third
= second
->next
) != nil
)
1628 fourth
= third
->next
;
1635 /* name = [device] value? */
1636 if ((n
== 3 || n
== 4)
1638 && second
->token
[0] == '='
1639 && !sugar(third
->token
)
1640 && (n
== 3 || (n
== 4 && third
->token
[0] == 'd'
1641 && !sugar(fourth
->token
)
1643 char *value
= third
->token
;
1646 if (n
== 4) { value
= fourth
->token
; flags
|= E_DEV
; }
1648 if ((flags
= b_setvar(flags
, name
, value
)) != 0) {
1649 printf("%s is a %s\n", name
,
1650 flags
& E_RESERVED
? "reserved word" :
1651 "special function");
1654 while (cmds
!= sep
) voidtoken();
1657 /* name '(arg)' ... ? */
1660 && second
->token
[0] == '('
1663 int c
, flags
, depth
;
1670 while (sep
!= nil
) {
1671 if ((c
= sep
->token
[0]) == ';' && depth
== 0) break;
1672 len
+= strlen(sep
->token
) + 1;
1674 if (c
== '{') depth
++;
1675 if (c
== '}' && --depth
== 0) break;
1678 body
= malloc(len
* sizeof(char));
1681 while (fun
!= sep
) {
1682 strcat(body
, fun
->token
);
1683 if (!sugar(fun
->token
)
1684 && !sugar(fun
->next
->token
)
1685 ) strcat(body
, " ");
1688 second
->token
[strlen(second
->token
)-1]= 0;
1691 printf("Missing '}'\n");
1694 if ((flags
= b_setenv(E_FUNCTION
, name
,
1695 second
->token
+1, body
)) != 0) {
1696 printf("%s is a %s\n", name
,
1697 flags
& E_RESERVED
? "reserved word" :
1698 "special variable");
1701 while (cmds
!= sep
) voidtoken();
1706 if (name
[0] == '{') {
1707 token
**acmds
= &cmds
->next
;
1711 /* Find and remove matching '}' */
1713 while (*acmds
!= nil
) {
1715 if (t
[0] == '{') depth
++;
1716 if (t
[0] == '}' && --depth
== 0) { t
[0]= ';'; break; }
1717 acmds
= &(*acmds
)->next
;
1722 /* Command coming up, check if ESC typed. */
1726 /* unset name ..., echo word ...? */
1727 if (n
>= 1 && (res
== R_UNSET
|| res
== R_ECHO
)) {
1728 char *arg
= poptoken(), *p
;
1732 if (cmds
== sep
) break;
1734 if (res
== R_UNSET
) { /* unset arg */
1736 } else { /* echo arg */
1744 if (cmds
== sep
) return;
1759 if (getch() == '\n')
1768 putch(cmds
!= sep
? ' ' : '\n');
1774 if (n
== 2 && res
== R_BOOT
&& second
->token
[0] == '-') {
1775 static char optsvar
[]= "bootopts";
1776 (void) b_setvar(E_VAR
, optsvar
, second
->token
);
1783 /* boot device, ls dir, delay msec? */
1784 if (n
== 2 && (res
== R_BOOT
|| res
== R_CTTY
1785 || res
== R_DELAY
|| res
== R_LS
)
1787 if (res
== R_BOOT
) boot_device(second
->token
);
1788 if (res
== R_CTTY
) ctty(second
->token
);
1789 if (res
== R_DELAY
) delay(second
->token
);
1790 if (res
== R_LS
) ls(second
->token
);
1795 /* trap msec command? */
1796 if (n
== 3 && res
== R_TRAP
&& numeric(second
->token
)) {
1797 long msec
= a2l(second
->token
);
1801 schedule(msec
, poptoken());
1804 /* Simple command. */
1812 case R_BOOT
: bootminix(); ok
= 1; break;
1813 case R_DELAY
: delay("500"); ok
= 1; break;
1814 case R_LS
: ls(null
); ok
= 1; break;
1815 case R_MENU
: menu(); ok
= 1; break;
1816 case R_SAVE
: save_parameters(); ok
= 1;break;
1817 case R_SET
: show_env(); ok
= 1; break;
1818 case R_HELP
: help(); ok
= 1; break;
1819 case R_EXIT
: exit(0);
1820 case R_OFF
: off(); ok
= 1; break;
1821 case R_CTTY
: ctty(nil
); ok
= 1; break;
1824 /* Command to check bootparams: */
1825 if (strcmp(name
, ":") == 0) ok
= 1;
1827 /* User defined function. */
1828 if (!ok
&& (body
= b_body(name
)) != nil
) {
1829 (void) tokenize(&cmds
, body
);
1832 if (!ok
) printf("%s: unknown function", name
);
1837 printf("Can't parse:");
1838 while (cmds
!= sep
) {
1839 printf(" %s", cmds
->token
); voidtoken();
1843 /* Getting here means that the command is not understood. */
1844 printf("\nTry 'help'\n");
1848 int run_trailer(void)
1849 /* Run the trailer function between loading Minix and handing control to it.
1850 * Return true iff there was no error.
1853 token
*save_cmds
= cmds
;
1856 (void) tokenize(&cmds
, "trailer");
1857 while (cmds
!= nil
) execute();
1863 /* Read a line and tokenize it. */
1867 unschedule(); /* Kill a trap. */
1868 err
= 0; /* Clear error state. */
1870 if (istty
) printf("%s>", bootdev
.name
);
1872 (void) tokenize(&cmds
, line
);
1874 (void) escape(); /* Forget if ESC typed. */
1880 /* Load Minix and start it, among other things. */
1883 /* Initialize tables. */
1886 /* Get environment variables from the parameter sector. */
1890 /* While there are commands, execute them! */
1892 while (cmds
!= nil
) execute();
1894 /* The "monitor" is just a "read one command" thing. */
1902 void main(int argc
, char **argv
)
1903 /* Do not load or start anything, just edit parameters. */
1906 char bootcode
[SECTOR_SIZE
];
1907 struct termios rawterm
;
1909 istty
= (argc
<= 2 && tcgetattr(0, &termbuf
) == 0);
1912 fprintf(stderr
, "Usage: edparams device [command ...]\n");
1916 /* Go over the arguments, changing control characters to spaces. */
1917 for (i
= 2; i
< argc
; i
++) {
1920 for (p
= argv
[i
]; *p
!= 0; p
++) {
1921 if ((unsigned) *p
< ' ' && *p
!= '\n') *p
= ' ';
1925 bootdev
.name
= argv
[1];
1926 if (strncmp(bootdev
.name
, "/dev/", 5) == 0) bootdev
.name
+= 5;
1927 if ((bootdev
.device
= open(argv
[1], O_RDWR
, 0666)) < 0)
1928 fatal(bootdev
.name
);
1930 /* Check if it is a bootable Minix device. */
1931 if (readsectors(mon2abs(bootcode
), lowsec
, 1) != 0
1932 || memcmp(bootcode
, boot_magic
, sizeof(boot_magic
)) != 0) {
1933 fprintf(stderr
, "edparams: %s: not a bootable Minix device\n",
1938 /* Print greeting message. */
1939 if (istty
) printf("Boot parameters editor.\n");
1941 signal(SIGINT
, trap
);
1942 signal(SIGALRM
, trap
);
1946 rawterm
.c_lflag
&= ~(ICANON
|ECHO
|IEXTEN
);
1947 rawterm
.c_cc
[VINTR
]= ESC
;
1948 if (tcsetattr(0, TCSANOW
, &rawterm
) < 0) fatal("");
1951 /* Get environment variables from the parameter sector. */
1956 /* While there are commands, execute them! */
1957 while (cmds
!= nil
|| i
< argc
) {
1959 /* A command line command. */
1960 parse_code(argv
[i
++]);
1964 /* Bail out on errors if not interactive. */
1965 if (err
&& !istty
) exit(1);
1968 /* Commands on the command line? */
1969 if (argc
> 2) break;
1971 /* The "monitor" is just a "read one command" thing. */
1979 * $PchId: boot.c,v 1.14 2002/02/27 19:46:14 philip Exp $