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/dmap.h>
25 #include <minix/const.h>
26 #include <minix/minlib.h>
27 #include <minix/syslib.h>
29 #include <kernel/const.h>
30 #include <sys/video.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)))
51 u16_t vid_port
; /* Video i/o port. */
52 u32_t vid_mem_base
; /* Video memory base address. */
53 u32_t vid_mem_size
; /* Video memory size. */
55 int fsok
= -1; /* File system state. Initially unknown. */
57 static int block_size
;
61 /* this data is reserved for BIOS int 0x13 to put the 'specification packet'
62 * in. It has a structure of course, but we don't define a struct because
63 * of compiler padding. We fiddle out the bytes ourselves later.
65 unsigned char boot_spec
[24];
67 static const char *bios_err(int err
)
68 /* Translate BIOS error code to a readable string. (This is a rare trait
69 * known as error checking and reporting. Take a good look at it, you won't
73 static struct errlist
{
79 { 0x01, "Invalid command" },
80 { 0x02, "Address mark not found" },
81 { 0x03, "Disk write-protected" },
82 { 0x04, "Sector not found" },
83 { 0x05, "Reset failed" },
84 { 0x06, "Floppy disk removed" },
85 { 0x07, "Bad parameter table" },
86 { 0x08, "DMA overrun" },
87 { 0x09, "DMA crossed 64 KB boundary" },
88 { 0x0A, "Bad sector flag" },
89 { 0x0B, "Bad track flag" },
90 { 0x0C, "Media type not found" },
91 { 0x0D, "Invalid number of sectors on format" },
92 { 0x0E, "Control data address mark detected" },
93 { 0x0F, "DMA arbitration level out of range" },
94 { 0x10, "Uncorrectable CRC or ECC data error" },
95 { 0x11, "ECC corrected data error" },
96 { 0x20, "Controller failed" },
97 { 0x40, "Seek failed" },
98 { 0x80, "Disk timed-out" },
99 { 0xAA, "Drive not ready" },
100 { 0xBB, "Undefined error" },
101 { 0xCC, "Write fault" },
102 { 0xE0, "Status register error" },
103 { 0xFF, "Sense operation failed" }
105 { 0x00, "No error" },
106 { 0x01, "Function number invalid" },
107 { 0x02, "File not found" },
108 { 0x03, "Path not found" },
109 { 0x04, "Too many open files" },
110 { 0x05, "Access denied" },
111 { 0x06, "Invalid handle" },
112 { 0x0C, "Access code invalid" },
115 struct errlist
*errp
;
117 for (errp
= errlist
; errp
< arraylimit(errlist
); errp
++) {
118 if (errp
->err
== err
) return errp
->what
;
120 return "Unknown error";
123 /* CD's are addressed in 2048-byte sectors.
124 * In order to be able to read CD's but maintain the same interface of 512-byte
125 * sector addressing, we check if the device is a CD in readsectors() and if so,
126 * read it into our own buffer first
128 int readsectors(u32_t bufaddr
, u32_t sector
, U8_t count
)
130 #define CDSECTOR_SIZE 2048
131 static char cdbuf
[CDSECTOR_SIZE
];
132 static i32_t cdbuf_sec
= -1;
135 if(device
!= cddevice
) {
136 return biosreadsectors(bufaddr
, sector
, count
);
141 #define FACTOR (CDSECTOR_SIZE/SECTOR_SIZE)
142 cdsec
= sector
/ FACTOR
;
143 offset
= (sector
% FACTOR
) * SECTOR_SIZE
;
144 if(cdsec
!= cdbuf_sec
) {
146 if((r
=biosreadsectors(mon2abs(cdbuf
), cdsec
, 1)) != 0) {
147 printf("error %d\n", r
);
152 raw_copy(bufaddr
, mon2abs(cdbuf
) + offset
, SECTOR_SIZE
);
153 bufaddr
+= SECTOR_SIZE
;
161 char *unix_err(int err
)
162 /* Translate the few errors rawfs can give. */
165 case ENOENT
: return "No such file or directory";
166 case ENOTDIR
: return "Not a directory";
167 default: return "Unknown error";
171 static void rwerr(const char *rw
, off_t sec
, int err
)
173 printf("\n%s error 0x%02x (%s) at sector %ld absolute\n",
174 rw
, err
, bios_err(err
), sec
);
177 void readerr(off_t sec
, int err
) { rwerr("Read", sec
, err
); }
178 void writerr(off_t sec
, int err
) { rwerr("Write", sec
, err
); }
180 void readblock(off_t blk
, char *buf
, int block_size
)
181 /* Read blocks for the rawfs package. */
184 u32_t sec
= lowsec
+ blk
* RATIO(block_size
);
187 printf("block_size 0\n");
191 if ((r
= readsectors(mon2abs(buf
), sec
, 1 * RATIO(block_size
))) != 0) {
192 readerr(sec
, r
); exit(1);
203 /* The Minix boot block must start with these bytes: */
204 char boot_magic
[] = { 0x31, 0xC0, 0x8E, 0xD8, 0xFA, 0x8E, 0xD0, 0xBC };
207 char *name
; /* Name of device. */
208 int device
; /* Device to edit parameters. */
211 static struct termios termbuf
;
214 void quit(int status
)
216 if (istty
) (void) tcsetattr(0, TCSANOW
, &termbuf
);
220 #define exit(s) quit(s)
222 void report(const char *label
)
223 /* edparams: label: No such file or directory */
225 fprintf(stderr
, "edparams: %s: %s\n", label
, strerror(errno
));
228 void fatal(const char *label
)
234 void *alloc(void *m
, size_t n
)
236 m
= m
== nil
? malloc(n
) : realloc(m
, n
);
237 if (m
== nil
) fatal("");
241 #define malloc(n) alloc(nil, n)
242 #define realloc(m, n) alloc(m, n)
244 #define mon2abs(addr) ((void *) (addr))
246 int rwsectors(int rw
, void *addr
, u32_t sec
, int nsec
)
249 size_t len
= nsec
* SECTOR_SIZE
;
251 if (lseek(bootdev
.device
, sec
* SECTOR_SIZE
, SEEK_SET
) == -1)
255 r
= read(bootdev
.device
, (char *) addr
, len
);
257 r
= write(bootdev
.device
, (char *) addr
, len
);
259 if (r
== -1) return errno
;
260 if (r
!= len
) return EIO
;
264 #define readsectors(a, s, n) rwsectors(0, (a), (s), (n))
265 #define writesectors(a, s, n) rwsectors(1, (a), (s), (n))
266 #define readerr(sec, err) (errno= (err), report(bootdev.name))
267 #define writerr(sec, err) (errno= (err), report(bootdev.name))
268 #define putch(c) putchar(c)
269 #define unix_err(err) strerror(err)
271 void readblock(off_t blk
, char *buf
, int block_size
)
272 /* Read blocks for the rawfs package. */
274 if(!block_size
) fatal("block_size 0");
276 if (lseek(bootdev
.device
, blk
* block_size
, SEEK_SET
) == -1
277 || read(bootdev
.device
, buf
, block_size
) != block_size
)
283 sig_atomic_t trapsig
;
293 if (trapsig
== SIGINT
) {
300 static unsigned char unchar
;
314 switch (read(0, &c
, 1)) {
316 if (errno
!= EINTR
) fatal("");
319 if (istty
) putch('\n');
322 if (istty
&& c
== termbuf
.c_cc
[VEOF
]) {
330 #define ungetch(c) ((void) (unchar = (c)))
332 #define get_tick() ((u32_t) time(nil))
333 #define clear_screen() printf("[clear]")
334 #define boot_device(device) printf("[boot %s]\n", device)
335 #define ctty(line) printf("[ctty %s]\n", line)
336 #define bootminix() (run_trailer() && printf("[boot]\n"))
337 #define off() printf("[off]")
341 static char *readline(void)
342 /* Read a line including a newline with echoing. */
350 line
= malloc(z
* sizeof(char));
355 if (strchr("\b\177\25\30", c
) != nil
) {
356 /* Backspace, DEL, ctrl-U, or ctrl-X. */
361 } while (c
== '\25' || c
== '\30');
363 if (c
< ' ' && c
!= '\n') {
370 line
= realloc(line
, z
* sizeof(char));
378 static int sugar(const char *tok
)
379 /* Recognize special tokens. */
381 return strchr("=(){};\n", tok
[0]) != nil
;
384 static char *onetoken(char **aline
)
385 /* Returns a string with one token for tokenize. */
391 /* Skip spaces and runs of newlines. */
392 while (*line
== ' ' || (*line
== '\n' && line
[1] == '\n')) line
++;
396 /* Don't do odd junk (nor the terminating 0!). */
397 if ((unsigned) *line
< ' ' && *line
!= '\n') return nil
;
400 /* Function argument, anything goes but () must match. */
403 while ((unsigned) *line
>= ' ') {
404 if (*line
== '(') depth
++;
405 if (*line
++ == ')' && --depth
== 0) break;
409 /* Single character token. */
412 /* Multicharacter token. */
413 do line
++; while ((unsigned) *line
> ' ' && !sugar(line
));
416 tok
= malloc((n
+ 1) * sizeof(char));
417 memcpy(tok
, *aline
, n
);
419 if (tok
[0] == '\n') tok
[0]= ';'; /* ';' same as '\n' */
425 /* Typed commands form strings of tokens. */
427 typedef struct token
{
428 struct token
*next
; /* Next in a command chain. */
432 static token
**tokenize(token
**acmds
, char *line
)
433 /* Takes a line apart to form tokens. The tokens are inserted into a command
434 * chain at *acmds. Tokenize returns a reference to where another line could
435 * be added. Tokenize looks at spaces as token separators, and recognizes only
436 * ';', '=', '{', '}', and '\n' as single character tokens. One token is
437 * formed from '(' and ')' with anything in between as long as more () match.
443 while ((tok
= onetoken(&line
)) != nil
) {
444 newcmd
= malloc(sizeof(*newcmd
));
446 newcmd
->next
= *acmds
;
448 acmds
= &newcmd
->next
;
453 static token
*cmds
; /* String of commands to execute. */
454 static int err
; /* Set on an error. */
456 static char *poptoken(void)
457 /* Pop one token off the command chain. */
460 char *tok
= cmd
->token
;
468 static void voidtoken(void)
469 /* Remove one token from the command chain. */
474 void parse_code(char *code
)
475 /* Tokenize a string of monitor code, making sure there is a delimiter. It is
476 * to be executed next. (Prepended to the current input.)
479 if (cmds
!= nil
&& cmds
->token
[0] != ';') (void) tokenize(&cmds
, ";");
480 (void) tokenize(&cmds
, code
);
483 static int interrupt(void)
484 /* Clean up after an ESC has been typed. */
500 int device
, primary
, secondary
;
503 static int get_master(char *master
, struct part_entry
**table
, u32_t pos
)
504 /* Read a master boot sector and its partition table. */
507 struct part_entry
*pe
, **pt
;
509 if ((r
= readsectors(mon2abs(master
), pos
, 1)) != 0) return r
;
511 pe
= (struct part_entry
*) (master
+ PART_TABLE_OFF
);
512 for (pt
= table
; pt
< table
+ NR_PARTITIONS
; pt
++) *pt
= pe
++;
514 /* DOS has the misguided idea that partition tables must be sorted. */
515 if (pos
!= 0) return 0; /* But only the primary. */
519 for (pt
= table
; pt
< table
+ NR_PARTITIONS
-1; pt
++) {
520 if (pt
[0]->sysind
== NO_PART
521 || pt
[0]->lowsec
> pt
[1]->lowsec
) {
522 pe
= pt
[0]; pt
[0]= pt
[1]; pt
[1]= pe
;
529 static void initialize(void)
531 char master
[SECTOR_SIZE
];
532 struct part_entry
*table
[NR_PARTITIONS
];
537 /* Copy the boot program to the far end of low memory, this must be
538 * done to get out of the way of Minix, and to put the data area
539 * cleanly inside a 64K chunk if using BIOS I/O (no DMA problems).
541 u32_t oldaddr
= caddr
;
542 u32_t memend
= mem
[0].base
+ mem
[0].size
;
543 u32_t newaddr
= (memend
- runsize
) & ~0x0000FL
;
545 u32_t dma64k
= (memend
- 1) & ~0x0FFFFL
;
548 /* Check if data segment crosses a 64K boundary. */
549 if (newaddr
+ (daddr
- caddr
) < dma64k
) {
550 newaddr
= (dma64k
- runsize
) & ~0x0000FL
;
554 /* If we were booted from CD, remember what device it was. */
558 cddevice
= 0xff; /* Invalid. */
560 /* Set the new caddr for relocate. */
563 /* Copy code and data. */
564 raw_copy(newaddr
, oldaddr
, runsize
);
566 /* Make the copy running. */
571 /* Take the monitor out of the memory map if we have memory to spare,
572 * and also keep the BIOS data area safe (1.5K), plus a bit extra for
573 * where we may have to put a.out headers for older kernels.
575 if (mon_return
= (mem
[1].size
> 512*1024L)) mem
[0].size
= newaddr
;
579 /* Find out what the boot device and partition was. */
581 bootdev
.device
= device
;
583 bootdev
.secondary
= -1;
587 strcpy(bootdev
.name
, "fd0");
588 bootdev
.name
[2] += bootdev
.device
;
592 /* Disk: Get the partition table from the very first sector, and
593 * determine the partition we booted from using the information from
594 * the booted partition entry as passed on by the bootstrap (rem_part).
595 * All we need from it is the partition offset.
597 raw_copy(mon2abs(&lowsec
),
598 vec2abs(&rem_part
) + offsetof(struct part_entry
, lowsec
),
601 masterpos
= 0; /* Master bootsector position. */
604 /* Extract the partition table from the master boot sector. */
605 if ((r
= get_master(master
, table
, masterpos
)) != 0) {
606 readerr(masterpos
, r
); exit(1);
609 /* If we're a CD, we know what we want. */
610 if(device
== cddevice
) {
611 p
= 1; /* We know this is the root FS. */
612 lowsec
= table
[p
]->lowsec
;
617 /* See if you can find "lowsec" back. */
618 for (p
= 0; p
< NR_PARTITIONS
; p
++) {
619 if (lowsec
- table
[p
]->lowsec
< table
[p
]->size
) break;
622 if (lowsec
== table
[p
]->lowsec
) { /* Found! */
623 if (bootdev
.primary
< 0)
626 bootdev
.secondary
= p
;
630 if (p
== NR_PARTITIONS
|| bootdev
.primary
>= 0
631 || table
[p
]->sysind
!= MINIX_PART
) {
632 /* The boot partition cannot be named, this only means
633 * that "bootdev" doesn't work.
639 /* See if the primary partition is subpartitioned. */
641 masterpos
= table
[p
]->lowsec
;
644 if(device
== cddevice
) {
645 strcpy(bootdev
.name
, CDNAME
);
647 strcpy(bootdev
.name
, "d0p0");
648 bootdev
.name
[1] += (device
- 0x80);
649 bootdev
.name
[3] += bootdev
.primary
;
650 if (bootdev
.secondary
>= 0) {
651 strcat(bootdev
.name
, "s0");
652 bootdev
.name
[5] += bootdev
.secondary
;
656 /* Find out about the video hardware. */
657 raw_copy(mon2abs(&vid_port
), VDU_CRT_BASE_ADDR
, sizeof(vid_port
));
658 if(vid_port
== C_6845
) {
659 vid_mem_base
= COLOR_BASE
;
660 vid_mem_size
= COLOR_SIZE
;
662 vid_mem_base
= MONO_BASE
;
663 vid_mem_size
= MONO_SIZE
;
667 vid_mem_size
= EGA_SIZE
;
670 /* Take the monitor out of the memory map if we have memory to spare,
671 * note that only half our PSP is needed at the new place, the first
672 * half is to be kept in its place.
674 if (mem
[1].size
> 0) mem
[0].size
= newaddr
+ 0x80 - mem
[0].base
;
676 /* Parse the command line. */
679 while (between('\1', *argp
, ' ')) argp
++;
681 while (!between('\0', *argp
, ' ')) argp
++;
682 while (between('\1', *argp
, ' ')) *argp
++= 0;
684 printf("\nUsage: boot <vdisk> [commands ...]\n");
687 drun
= *argp
== 0 ? "main" : argp
;
689 if ((r
= dev_open()) != 0) {
690 printf("\n%s: Error %02x (%s)\n", vdisk
, r
, bios_err(r
));
694 /* Find the active partition on the virtual disk. */
695 if ((r
= get_master(master
, table
, 0)) != 0) {
696 readerr(0, r
); exit(1);
699 strcpy(bootdev
.name
, "d0");
701 for (p
= 0; p
< NR_PARTITIONS
; p
++) {
702 if (table
[p
]->bootind
!= 0 && table
[p
]->sysind
== MINIX_PART
) {
704 strcat(bootdev
.name
, "p0");
705 bootdev
.name
[3] += p
;
706 lowsec
= table
[p
]->lowsec
;
715 /* Reserved names: */
717 R_NULL
, R_BOOT
, R_CTTY
, R_DELAY
, R_ECHO
, R_EXIT
, R_HELP
,
718 R_LS
, R_MENU
, R_OFF
, R_SAVE
, R_SET
, R_TRAP
, R_UNSET
721 static char resnames
[][6] = {
722 "", "boot", "ctty", "delay", "echo", "exit", "help",
723 "ls", "menu", "off", "save", "set", "trap", "unset",
726 /* Using this for all null strings saves a lot of memory. */
727 #define null (resnames[0])
729 static enum resnames
reserved(const char *s
)
730 /* Recognize reserved strings. */
734 for (r
= R_BOOT
; r
<= R_UNSET
; r
++) {
735 if (strcmp(s
, resnames
[r
]) == 0) return r
;
740 static void sfree(char *s
)
741 /* Free a non-null string. */
743 if (s
!= nil
&& s
!= null
) free(s
);
746 static char *copystr(char *s
)
747 /* Copy a non-null string using malloc. */
751 if (*s
== 0) return null
;
752 c
= malloc((strlen(s
) + 1) * sizeof(char));
757 static int is_default(environment
*e
)
759 return (e
->flags
& E_SPECIAL
) && e
->defval
== nil
;
762 static environment
**searchenv(char *name
)
764 environment
**aenv
= &env
;
766 while (*aenv
!= nil
&& strcmp((*aenv
)->name
, name
) != 0) {
767 aenv
= &(*aenv
)->next
;
773 #define b_getenv(name) (*searchenv(name))
774 /* Return the environment *structure* belonging to name, or nil if not found. */
776 char *b_value(char *name
)
777 /* The value of a variable. */
779 environment
*e
= b_getenv(name
);
781 return e
== nil
|| !(e
->flags
& E_VAR
) ? nil
: e
->value
;
784 static char *b_body(char *name
)
785 /* The value of a function. */
787 environment
*e
= b_getenv(name
);
789 return e
== nil
|| !(e
->flags
& E_FUNCTION
) ? nil
: e
->value
;
792 static int b_setenv(int flags
, char *name
, char *arg
, char *value
)
793 /* Change the value of an environment variable. Returns the flags of the
794 * variable if you are not allowed to change it, 0 otherwise.
797 environment
**aenv
, *e
;
799 if (*(aenv
= searchenv(name
)) == nil
) {
800 if (reserved(name
)) return E_RESERVED
;
801 e
= malloc(sizeof(*e
));
802 e
->name
= copystr(name
);
810 /* Don't change special variables to functions or vv. */
811 if (e
->flags
& E_SPECIAL
812 && (e
->flags
& E_FUNCTION
) != (flags
& E_FUNCTION
)
815 e
->flags
= (e
->flags
& E_STICKY
) | flags
;
823 e
->arg
= copystr(arg
);
824 e
->value
= copystr(value
);
829 int b_setvar(int flags
, char *name
, char *value
)
830 /* Set variable or simple function. */
834 if((r
=b_setenv(flags
, name
, null
, value
))) {
841 void b_unset(char *name
)
842 /* Remove a variable from the environment. A special variable is reset to
846 environment
**aenv
, *e
;
848 if ((e
= *(aenv
= searchenv(name
))) == nil
) return;
850 if (e
->flags
& E_SPECIAL
) {
851 if (e
->defval
!= nil
) {
867 long a2l(const char *a
)
873 if (*a
== '-') { sign
= -1; a
++; }
875 while (between('0', *a
, '9')) n
= n
* 10 + (*a
++ - '0');
880 char *ul2a(u32_t n
, unsigned b
)
881 /* Transform a long number to ascii at base b, (b >= 8). */
883 static char num
[(CHAR_BIT
* sizeof(n
) + 2) / 3 + 1];
884 char *a
= arraylimit(num
) - 1;
885 static char hex
[16] = "0123456789ABCDEF";
887 do *--a
= hex
[(int) (n
% b
)]; while ((n
/= b
) > 0);
891 char *ul2a10(u32_t n
)
892 /* Transform a long number to ascii at base 10. */
897 unsigned a2x(const char *a
)
905 if (between('0', c
, '9')) c
= c
- '0' + 0x0;
907 if (between('A', c
, 'F')) c
= c
- 'A' + 0xA;
909 if (between('a', c
, 'f')) c
= c
- 'a' + 0xa;
918 static void get_parameters(void)
920 char params
[SECTOR_SIZE
+ 1];
922 int r
, bus
, processor
;
924 static char bus_type
[][4] = {
927 static char vid_type
[][4] = {
928 "mda", "cga", "ega", "ega", "vga", "vga"
930 static char vid_chrome
[][6] = {
934 /* Variables that Minix needs: */
935 b_setvar(E_SPECIAL
|E_VAR
|E_DEV
, "rootdev", "ram");
936 b_setvar(E_SPECIAL
|E_VAR
|E_DEV
, "ramimagedev", "bootdev");
937 b_setvar(E_SPECIAL
|E_VAR
, "ramsize", "0");
938 #define STRINGIT2(x) #x
939 #define STRINGIT1(x) STRINGIT2(x)
940 b_setvar(E_SPECIAL
|E_VAR
, "hz", STRINGIT1(DEFAULT_HZ
));
942 processor
= getprocessor();
943 if(processor
== 1586) processor
= 686;
944 b_setvar(E_SPECIAL
|E_VAR
, "processor", ul2a10(processor
));
945 b_setvar(E_SPECIAL
|E_VAR
, "bus", bus_type
[get_bus()]);
946 b_setvar(E_SPECIAL
|E_VAR
, "video", vid_type
[get_video()]);
947 b_setvar(E_SPECIAL
|E_VAR
, "chrome", vid_chrome
[get_video() & 1]);
949 for (mp
= mem
; mp
< arraylimit(mem
); mp
++) {
950 if (mp
->size
== 0) continue;
951 if (params
[0] != 0) strcat(params
, ",");
952 strcat(params
, ul2a(mp
->base
, 0x10));
954 strcat(params
, ul2a(mp
->size
, 0x10));
956 b_setvar(E_SPECIAL
|E_VAR
, "memory", params
);
959 b_setvar(E_SPECIAL
|E_VAR
, "dosfile-d0", vdisk
);
964 b_setvar(E_SPECIAL
|E_VAR
, "processor", "?");
965 b_setvar(E_SPECIAL
|E_VAR
, "bus", "?");
966 b_setvar(E_SPECIAL
|E_VAR
, "video", "?");
967 b_setvar(E_SPECIAL
|E_VAR
, "chrome", "?");
968 b_setvar(E_SPECIAL
|E_VAR
, "memory", "?");
969 b_setvar(E_SPECIAL
|E_VAR
, "c0", "?");
972 /* Variables boot needs: */
973 b_setvar(E_SPECIAL
|E_VAR
, "image", "boot/image");
974 b_setvar(E_SPECIAL
|E_FUNCTION
, "leader",
975 "echo --- Welcome to MINIX 3. This is the boot monitor. ---\\n");
976 b_setvar(E_SPECIAL
|E_FUNCTION
, "main", "menu");
977 b_setvar(E_SPECIAL
|E_FUNCTION
, "trailer", "");
979 /* Default hidden menu function: */
980 b_setenv(E_RESERVED
|E_FUNCTION
, null
, "=,Start MINIX", "boot");
982 /* Tokenize bootparams sector. */
983 if ((r
= readsectors(mon2abs(params
), lowsec
+PARAMSEC
, 1)) != 0) {
984 readerr(lowsec
+PARAMSEC
, r
);
987 params
[SECTOR_SIZE
]= 0;
988 acmds
= tokenize(&cmds
, params
);
990 /* Stuff the default action into the command chain. */
992 (void) tokenize(acmds
, ":;");
994 (void) tokenize(tokenize(acmds
, ":;leader;"), drun
);
996 (void) tokenize(acmds
, ":;leader;main");
1000 static char *addptr
;
1002 static void addparm(const char *n
)
1004 while (*n
!= 0 && *addptr
!= 0) *addptr
++ = *n
++;
1007 static void save_parameters(void)
1008 /* Save nondefault environment variables to the bootparams sector. */
1011 char params
[SECTOR_SIZE
+ 1];
1014 /* Default filling: */
1015 memset(params
, '\n', SECTOR_SIZE
);
1017 /* Don't touch the 0! */
1018 params
[SECTOR_SIZE
]= 0;
1021 for (e
= env
; e
!= nil
; e
= e
->next
) {
1022 if (e
->flags
& E_RESERVED
|| is_default(e
)) continue;
1025 if (e
->flags
& E_FUNCTION
) {
1030 addparm((e
->flags
& (E_DEV
|E_SPECIAL
)) != E_DEV
1035 printf("The environment is too big\n");
1041 /* Save the parameters on disk. */
1042 if ((r
= writesectors(mon2abs(params
), lowsec
+PARAMSEC
, 1)) != 0) {
1043 writerr(lowsec
+PARAMSEC
, r
);
1044 printf("Can't save environment\n");
1048 static void show_env(void)
1049 /* Show the environment settings. */
1055 for (e
= env
; e
!= nil
; e
= e
->next
) {
1056 if (e
->flags
& E_RESERVED
) continue;
1057 if (!istty
&& is_default(e
)) continue;
1059 if (e
->flags
& E_FUNCTION
) {
1060 printf("%s(%s) %s\n", e
->name
, e
->arg
, e
->value
);
1062 printf(is_default(e
) ? "%s = (%s)\n" : "%s = %s\n",
1066 if (e
->next
!= nil
&& istty
&& ++more
% 20 == 0) {
1069 if (c
== ESC
|| c
> ' ') {
1071 if (c
> ' ') ungetch(c
);
1074 printf("\b\b\b\b\b\b");
1079 int numprefix(char *s
, char **ps
)
1080 /* True iff s is a string of digits. *ps will be set to the first nondigit
1081 * if non-nil, otherwise the string should end.
1086 while (between('0', *n
, '9')) n
++;
1088 if (n
== s
) return 0;
1090 if (ps
== nil
) return *n
== 0;
1096 int numeric(char *s
)
1098 return numprefix(s
, (char **) nil
);
1103 /* Device numbers of standard MINIX devices. */
1104 #define DEV_FD0 0x0200
1105 static dev_t dev_cNd0
[] = { 0x0300, 0x0800, 0x0A00, 0x0C00, 0x1000 };
1106 #define minor_p0s0 128
1108 static int block_size
;
1110 dev_t
name2dev(char *name
)
1111 /* Translate, say, /dev/c0d0p2 to a device number. If the name can't be
1112 * found on the boot device, then do some guesswork. The global structure
1113 * "tmpdev" will be filled in based on the name, so that "boot d1p0" knows
1114 * what device to boot without interpreting device numbers.
1123 /* "boot *d0p2" means: make partition 2 active before you boot it. */
1124 if ((activate
= (name
[0] == '*'))) name
++;
1126 /* The special name "bootdev" must be translated to the boot device. */
1127 if (strcmp(name
, "bootdev") == 0) {
1128 if (bootdev
.device
== -1) {
1129 printf("The boot device could not be named\n");
1136 /* If our boot device doesn't have a file system, or we want to know
1137 * what a name means for the BIOS, then we need to interpret the
1138 * device name ourselves: "fd" = floppy, "c0d0" = hard disk, etc.
1140 tmpdev
.device
= tmpdev
.primary
= tmpdev
.secondary
= -1;
1143 if (strncmp(n
, "/dev/", 5) == 0) n
+= 5;
1145 if (strcmp(n
, "ram") == 0 || strcmp(n
, CDNAME
) == 0) {
1148 if (n
[0] == 'f' && n
[1] == 'd' && numeric(n
+2)) {
1150 tmpdev
.device
= a2l(n
+2);
1151 dev
= DEV_FD0
+ tmpdev
.device
;
1153 if ((n
[0] == 'h' || n
[0] == 's') && n
[1] == 'd' && numprefix(n
+2, &s
)
1154 && (*s
== 0 || (between('a', *s
, 'd') && s
[1] == 0))
1156 /* Old style hard disk (backwards compatibility.) */
1158 tmpdev
.device
= dev
/ (1 + NR_PARTITIONS
);
1159 tmpdev
.primary
= (dev
% (1 + NR_PARTITIONS
)) - 1;
1162 tmpdev
.secondary
= *s
- 'a';
1164 + (tmpdev
.device
* NR_PARTITIONS
1165 + tmpdev
.primary
) * NR_PARTITIONS
1168 tmpdev
.device
+= 0x80;
1169 dev
+= n
[0] == 'h' ? dev_cNd0
[0] : dev_cNd0
[2];
1174 if (n
[0] == 'c' && between('0', n
[1], '4')) {
1175 ctrlr
= (n
[1] - '0');
1179 if (n
[0] == 'd' && between('0', n
[1], '7')) {
1180 tmpdev
.device
= (n
[1] - '0');
1182 if (n
[0] == 'p' && between('0', n
[1], '3')) {
1183 tmpdev
.primary
= (n
[1] - '0');
1185 if (n
[0] == 's' && between('0', n
[1], '3')) {
1186 tmpdev
.secondary
= (n
[1] - '0');
1192 dev
= dev_cNd0
[ctrlr
];
1193 if (tmpdev
.secondary
< 0) {
1194 dev
+= tmpdev
.device
* (NR_PARTITIONS
+1)
1195 + (tmpdev
.primary
+ 1);
1198 + (tmpdev
.device
* NR_PARTITIONS
1199 + tmpdev
.primary
) * NR_PARTITIONS
1202 tmpdev
.device
+= 0x80;
1206 /* Look the name up on the boot device for the UNIX device number. */
1207 if (fsok
== -1) fsok
= r_super(&block_size
) != 0;
1209 /* The current working directory is "/dev". */
1210 ino
= r_lookup(r_lookup(ROOT_INO
, "dev"), name
);
1213 /* Name has been found, extract the device number. */
1215 if (!S_ISBLK(st
.st_mode
)) {
1216 printf("%s is not a block device\n", name
);
1224 if (tmpdev
.primary
< 0) activate
= 0; /* Careful now! */
1227 printf("Can't recognize '%s' as a device\n", name
);
1234 static void apm_perror(char *label
, u16_t ax
)
1242 case 0x01: str
= "APM functionality disabled"; break;
1243 case 0x03: str
= "interface not connected"; break;
1244 case 0x09: str
= "unrecognized device ID"; break;
1245 case 0x0A: str
= "parameter value out of range"; break;
1246 case 0x0B: str
= "interface not engaged"; break;
1247 case 0x60: str
= "unable to enter requested state"; break;
1248 case 0x86: str
= "APM not present"; break;
1249 default: printf("%s: error 0x%02x\n", label
, ah
); return;
1251 printf("%s: %s\n", label
, str
);
1254 #define apm_printf printf
1256 #define apm_perror(label, ax) ((void)0)
1260 static void off(void)
1265 /* Try to switch off the system. Print diagnostic information
1266 * that can be useful if the operation fails.
1269 be
.ax
= 0x5300; /* APM, Installation check */
1270 be
.bx
= 0; /* Device, APM BIOS */
1272 if (be
.flags
& FL_CARRY
)
1274 apm_perror("APM installation check failed", be
.ax
);
1277 if (be
.bx
!= (('P' << 8) | 'M'))
1279 apm_printf("APM signature not found (got 0x%04x)\n", be
.bx
);
1285 ah
= (ah
>> 4)*10 + (ah
& 0xf);
1288 al
= (al
>> 4)*10 + (al
& 0xf);
1289 apm_printf("APM version %u.%u%s%s%s%s%s\n",
1291 (be
.cx
& 0x1) ? ", 16-bit PM" : "",
1292 (be
.cx
& 0x2) ? ", 32-bit PM" : "",
1293 (be
.cx
& 0x4) ? ", CPU-Idle" : "",
1294 (be
.cx
& 0x8) ? ", APM-disabled" : "",
1295 (be
.cx
& 0x10) ? ", APM-disengaged" : "");
1298 be
.ax
= 0x5301; /* APM, Real mode interface connect */
1299 be
.bx
= 0x0000; /* APM BIOS */
1301 if (be
.flags
& FL_CARRY
)
1303 apm_perror("APM real mode connect failed", be
.ax
);
1307 /* Ask for a seat upgrade */
1308 be
.ax
= 0x530e; /* APM, Driver Version */
1309 be
.bx
= 0x0000; /* BIOS */
1310 be
.cx
= 0x0102; /* version 1.2 */
1312 if (be
.flags
& FL_CARRY
)
1314 apm_perror("Set driver version failed", be
.ax
);
1318 /* Is this version really worth reporting. Well, if the system
1319 * does switch off, you won't see it anyway.
1323 ah
= (ah
>> 4)*10 + (ah
& 0xf);
1326 al
= (al
>> 4)*10 + (al
& 0xf);
1327 apm_printf("Got APM connection version %u.%u\n", ah
, al
);
1330 be
.ax
= 0x5308; /* APM, Enable/disable power management */
1331 be
.bx
= 0x0001; /* All device managed by APM BIOS */
1333 /* For old APM 1.0 systems, we need 0xffff. Assume that those
1334 * systems do not exist.
1336 be
.bx
= 0xffff; /* All device managed by APM BIOS (compat) */
1338 be
.cx
= 0x0001; /* Enable power management */
1340 if (be
.flags
& FL_CARRY
)
1342 apm_perror("Enable power management failed", be
.ax
);
1347 be
.ax
= 0x5307; /* APM, Set Power State */
1348 be
.bx
= 0x0001; /* All devices managed by APM */
1349 be
.cx
= 0x0003; /* Off */
1351 if (be
.flags
& FL_CARRY
)
1353 apm_perror("Set power state failed", be
.ax
);
1357 apm_printf("Power off sequence successfully completed.\n\n");
1358 apm_printf("Ha, ha, just kidding!\n");
1362 be
.ax
= 0x5304; /* APM, interface disconnect */
1363 be
.bx
= 0x0000; /* APM BIOS */
1365 if (be
.flags
& FL_CARRY
)
1367 apm_perror("APM interface disconnect failed", be
.ax
);
1373 #define B_NOSIG -1 /* "No signature" error code. */
1375 int exec_bootstrap(void)
1376 /* Load boot sector from the disk or floppy described by tmpdev and execute it.
1380 char master
[SECTOR_SIZE
];
1381 struct part_entry
*table
[NR_PARTITIONS
], dummy
, *active
= &dummy
;
1386 /* Select a partition table entry. */
1387 while (tmpdev
.primary
>= 0) {
1388 masterpos
= active
->lowsec
;
1390 if ((r
= get_master(master
, table
, masterpos
)) != 0) return r
;
1392 active
= table
[tmpdev
.primary
];
1394 /* How does one check a partition table entry? */
1395 if (active
->sysind
== NO_PART
) return B_NOSIG
;
1397 tmpdev
.primary
= tmpdev
.secondary
;
1398 tmpdev
.secondary
= -1;
1401 if (activate
&& !active
->bootind
) {
1402 for (n
= 0; n
< NR_PARTITIONS
; n
++) table
[n
]->bootind
= 0;
1403 active
->bootind
= ACTIVE_FLAG
;
1407 /* Read the boot sector. */
1408 if ((r
= readsectors(BOOTPOS
, active
->lowsec
, 1)) != 0) return r
;
1410 /* Check signature word. */
1411 if (get_word(BOOTPOS
+SIGNATOFF
) != SIGNATURE
) return B_NOSIG
;
1413 /* Write the partition table if a member must be made active. */
1414 if (dirty
&& (r
= writesectors(mon2abs(master
), masterpos
, 1)) != 0)
1417 bootstrap(device
, active
);
1421 static void boot_device(char *devname
)
1422 /* Boot the device named by devname. */
1424 dev_t dev
= name2dev(devname
);
1425 int save_dev
= device
;
1429 if (tmpdev
.device
< 0) {
1430 /* FIXME: clearer error message. */
1431 if (dev
!= -1) printf("Can't boot from %s\n", devname
);
1435 /* Change current device and try to load and execute its bootstrap. */
1436 device
= tmpdev
.device
;
1438 if ((r
= dev_open()) == 0) r
= exec_bootstrap();
1440 err
= r
== B_NOSIG
? "Not bootable" : bios_err(r
);
1441 printf("Can't boot %s: %s\n", devname
, err
);
1443 /* Restore boot device setting. */
1448 static void ctty(char *line
)
1452 } else if (between('0', line
[0], '3') && line
[1] == 0) {
1453 serial_line
= line
[0] - '0';
1455 printf("Bad serial line number: %s\n", line
);
1458 serial_init(serial_line
);
1463 static void boot_device(char *devname
)
1464 /* No booting of other devices under DOS. */
1466 printf("Can't boot devices under DOS\n");
1469 static void ctty(char *line
)
1470 /* Don't know how to handle serial lines under DOS. */
1472 printf("No serial line support under DOS\n");
1478 static void ls(char *dir
)
1479 /* List the contents of a directory. */
1483 char name
[NAME_MAX
+1];
1485 if (fsok
== -1) fsok
= r_super(&block_size
) != 0;
1488 /* (,) construct because r_stat returns void */
1489 if ((ino
= r_lookup(ROOT_INO
, dir
)) == 0 ||
1490 (r_stat(ino
, &st
), r_readdir(name
)) == -1)
1492 printf("ls: %s: %s\n", dir
, unix_err(errno
));
1495 (void) r_readdir(name
); /* Skip ".." too. */
1497 while ((ino
= r_readdir(name
)) != 0) printf("%s/%s\n", dir
, name
);
1500 static u32_t
milli_time(void)
1502 return get_tick() * MSEC_PER_TICK
;
1505 static u32_t
milli_since(u32_t base
)
1507 return (milli_time() + (TICKS_PER_DAY
*MSEC_PER_TICK
) - base
)
1508 % (TICKS_PER_DAY
*MSEC_PER_TICK
);
1511 static char *Thandler
;
1512 static u32_t Tbase
, Tcount
;
1514 static void unschedule(void)
1515 /* Invalidate a waiting command. */
1519 if (Thandler
!= nil
) {
1525 static void schedule(long msec
, char *cmd
)
1526 /* Schedule command at a certain time from now. */
1530 Tbase
= milli_time();
1536 /* Check if the timer expired for getch(). */
1538 return (Thandler
!= nil
&& milli_since(Tbase
) >= Tcount
);
1541 void delay(char *msec
)
1542 /* Delay for a given time. */
1546 if ((count
= a2l(msec
)) == 0) return;
1553 } while (!interrupt() && !expired() && milli_since(base
) < count
);
1556 static enum whatfun
{ NOFUN
, SELECT
, DEFFUN
, USERFUN
} menufun(environment
*e
)
1558 if (!(e
->flags
& E_FUNCTION
) || e
->arg
[0] == 0) return NOFUN
;
1559 if (e
->arg
[1] != ',') return SELECT
;
1560 return e
->flags
& E_RESERVED
? DEFFUN
: USERFUN
;
1564 /* By default: Show a simple menu.
1565 * Multiple kernels/images: Show extra selection options.
1566 * User defined function: Kill the defaults and show these.
1567 * Wait for a keypress and execute the given function.
1574 /* Just a default menu? */
1575 for (e
= env
; e
!= nil
; e
= e
->next
) if (menufun(e
) == USERFUN
) def
= 0;
1577 printf("\nHit a key as follows:\n\n");
1579 /* Show the choices. */
1580 for (e
= env
; e
!= nil
; e
= e
->next
) {
1581 switch (menufun(e
)) {
1586 printf(" %c %s\n", e
->arg
[0], e
->arg
+2);
1589 printf(" %c Select %s kernel\n", e
->arg
[0],e
->name
);
1596 /* Wait for a keypress. */
1599 if (interrupt() || expired()) return;
1603 for (e
= env
; e
!= nil
; e
= e
->next
) {
1604 switch (menufun(e
)) {
1609 if (c
== e
->arg
[0]) choice
= e
->value
;
1612 } while (choice
== nil
);
1614 /* Execute the chosen function. */
1616 (void) tokenize(&cmds
, choice
);
1620 /* Not everyone is a rocket scientist. */
1626 static struct help info
[] = {
1628 { "rootdev", "Root device" },
1629 { "ramimagedev", "Device to use as RAM disk image " },
1630 { "ramsize", "RAM disk size (if no image device) " },
1631 { "bootdev", "Special name for the boot device" },
1632 { "fd0, d0p2, c0d0p1s0", "Devices (as in /dev)" },
1633 { "image", "Name of the boot image to use" },
1634 { "main", "Startup function" },
1635 { "bootdelay", "Delay in msec after loading image" },
1636 { nil
, "Commands:" },
1637 { "name = [device] value", "Set environment variable" },
1638 { "name() { ... }", "Define function" },
1639 { "name(key,text) { ... }",
1640 "A menu option like: minix(=,Start MINIX) {boot}" },
1641 { "name", "Call function" },
1642 { "boot [device]", "Boot Minix or another O.S." },
1643 { "ctty [line]", "Duplicate to serial line" },
1644 { "delay [msec]", "Delay (500 msec default)" },
1645 { "echo word ...", "Display the words" },
1646 { "ls [directory]", "List contents of directory" },
1647 { "menu", "Show menu and choose menu option" },
1648 { "save / set", "Save or show environment" },
1649 { "trap msec command", "Schedule command " },
1650 { "unset name ...", "Unset variable or set to default" },
1651 { "exit / off", "Exit the Monitor / Power off" },
1654 for (pi
= info
; pi
< arraylimit(info
); pi
++) {
1655 if (pi
->thing
!= nil
) printf(" %-24s- ", pi
->thing
);
1656 printf("%s\n", pi
->help
);
1660 static void execute(void)
1661 /* Get one command from the command chain and execute it. */
1663 token
*second
, *third
, *fourth
, *sep
;
1669 /* An error occured, stop interpreting. */
1670 while (cmds
!= nil
) voidtoken();
1674 if (expired()) { /* Timer expired? */
1675 parse_code(Thandler
);
1679 /* There must be a separator lurking somewhere. */
1680 for (sep
= cmds
; sep
!= nil
&& sep
->token
[0] != ';'; sep
= sep
->next
) n
++;
1683 res
= reserved(name
);
1684 if ((second
= cmds
->next
) != nil
1685 && (third
= second
->next
) != nil
)
1686 fourth
= third
->next
;
1693 /* name = [device] value? */
1694 if ((n
== 3 || n
== 4)
1696 && second
->token
[0] == '='
1697 && !sugar(third
->token
)
1698 && (n
== 3 || (n
== 4 && third
->token
[0] == 'd'
1699 && !sugar(fourth
->token
)
1701 char *value
= third
->token
;
1704 if (n
== 4) { value
= fourth
->token
; flags
|= E_DEV
; }
1706 if ((flags
= b_setvar(flags
, name
, value
)) != 0) {
1707 printf("%s is a %s\n", name
,
1708 flags
& E_RESERVED
? "reserved word" :
1709 "special function");
1712 while (cmds
!= sep
) voidtoken();
1715 /* name '(arg)' ... ? */
1718 && second
->token
[0] == '('
1721 int c
, flags
, depth
;
1728 while (sep
!= nil
) {
1729 if ((c
= sep
->token
[0]) == ';' && depth
== 0) break;
1730 len
+= strlen(sep
->token
) + 1;
1732 if (c
== '{') depth
++;
1733 if (c
== '}' && --depth
== 0) break;
1736 body
= malloc(len
* sizeof(char));
1739 while (fun
!= sep
) {
1740 strcat(body
, fun
->token
);
1741 if (!sugar(fun
->token
)
1742 && !sugar(fun
->next
->token
)
1743 ) strcat(body
, " ");
1746 second
->token
[strlen(second
->token
)-1]= 0;
1749 printf("Missing '}'\n");
1752 if ((flags
= b_setenv(E_FUNCTION
, name
,
1753 second
->token
+1, body
)) != 0) {
1754 printf("%s is a %s\n", name
,
1755 flags
& E_RESERVED
? "reserved word" :
1756 "special variable");
1759 while (cmds
!= sep
) voidtoken();
1764 if (name
[0] == '{') {
1765 token
**acmds
= &cmds
->next
;
1769 /* Find and remove matching '}' */
1771 while (*acmds
!= nil
) {
1773 if (t
[0] == '{') depth
++;
1774 if (t
[0] == '}' && --depth
== 0) { t
[0]= ';'; break; }
1775 acmds
= &(*acmds
)->next
;
1780 /* Command coming up, check if ESC typed. */
1784 /* unset name ..., echo word ...? */
1785 if (n
>= 1 && (res
== R_UNSET
|| res
== R_ECHO
)) {
1786 char *arg
= poptoken(), *p
;
1790 if (cmds
== sep
) break;
1792 if (res
== R_UNSET
) { /* unset arg */
1794 } else { /* echo arg */
1802 if (cmds
== sep
) return;
1817 if (getch() == '\n')
1826 putch(cmds
!= sep
? ' ' : '\n');
1832 if (n
== 2 && res
== R_BOOT
&& second
->token
[0] == '-') {
1833 static char optsvar
[]= "bootopts";
1834 (void) b_setvar(E_VAR
, optsvar
, second
->token
);
1841 /* boot device, ls dir, delay msec? */
1842 if (n
== 2 && (res
== R_BOOT
|| res
== R_CTTY
1843 || res
== R_DELAY
|| res
== R_LS
)
1845 if (res
== R_BOOT
) boot_device(second
->token
);
1846 if (res
== R_CTTY
) ctty(second
->token
);
1847 if (res
== R_DELAY
) delay(second
->token
);
1848 if (res
== R_LS
) ls(second
->token
);
1853 /* trap msec command? */
1854 if (n
== 3 && res
== R_TRAP
&& numeric(second
->token
)) {
1855 long msec
= a2l(second
->token
);
1859 schedule(msec
, poptoken());
1862 /* Simple command. */
1870 case R_BOOT
: bootminix(); ok
= 1; break;
1871 case R_DELAY
: delay("500"); ok
= 1; break;
1872 case R_LS
: ls(null
); ok
= 1; break;
1873 case R_MENU
: menu(); ok
= 1; break;
1874 case R_SAVE
: save_parameters(); ok
= 1;break;
1875 case R_SET
: show_env(); ok
= 1; break;
1876 case R_HELP
: help(); ok
= 1; break;
1877 case R_EXIT
: exit(0);
1878 case R_OFF
: off(); ok
= 1; break;
1879 case R_CTTY
: ctty(nil
); ok
= 1; break;
1882 /* Command to check bootparams: */
1883 if (strcmp(name
, ":") == 0) ok
= 1;
1885 /* User defined function. */
1886 if (!ok
&& (body
= b_body(name
)) != nil
) {
1887 (void) tokenize(&cmds
, body
);
1890 if (!ok
) printf("%s: unknown function", name
);
1895 printf("Can't parse:");
1896 while (cmds
!= sep
) {
1897 printf(" %s", cmds
->token
); voidtoken();
1901 /* Getting here means that the command is not understood. */
1902 printf("\nTry 'help'\n");
1906 int run_trailer(void)
1907 /* Run the trailer function between loading Minix and handing control to it.
1908 * Return true iff there was no error.
1911 token
*save_cmds
= cmds
;
1914 (void) tokenize(&cmds
, "trailer");
1915 while (cmds
!= nil
) execute();
1920 static void monitor(void)
1921 /* Read a line and tokenize it. */
1925 unschedule(); /* Kill a trap. */
1926 err
= 0; /* Clear error state. */
1928 if (istty
) printf("%s>", bootdev
.name
);
1930 (void) tokenize(&cmds
, line
);
1932 (void) escape(); /* Forget if ESC typed. */
1938 /* Load Minix and start it, among other things. */
1940 /* Initialize tables. */
1943 /* Get environment variables from the parameter sector. */
1947 /* While there are commands, execute them! */
1949 while (cmds
!= nil
) execute();
1951 /* The "monitor" is just a "read one command" thing. */
1959 void main(int argc
, char **argv
)
1960 /* Do not load or start anything, just edit parameters. */
1963 char bootcode
[SECTOR_SIZE
];
1964 struct termios rawterm
;
1966 istty
= (argc
<= 2 && tcgetattr(0, &termbuf
) == 0);
1969 fprintf(stderr
, "Usage: edparams device [command ...]\n");
1973 /* Go over the arguments, changing control characters to spaces. */
1974 for (i
= 2; i
< argc
; i
++) {
1977 for (p
= argv
[i
]; *p
!= 0; p
++) {
1978 if ((unsigned) *p
< ' ' && *p
!= '\n') *p
= ' ';
1982 bootdev
.name
= argv
[1];
1983 if (strncmp(bootdev
.name
, "/dev/", 5) == 0) bootdev
.name
+= 5;
1984 if ((bootdev
.device
= open(argv
[1], O_RDWR
, 0666)) < 0)
1985 fatal(bootdev
.name
);
1987 /* Check if it is a bootable Minix device. */
1988 if (readsectors(mon2abs(bootcode
), lowsec
, 1) != 0) {
1989 fprintf(stderr
, "edparams: %s: not a bootable Minix device\n",
1994 /* Print greeting message. */
1995 if (istty
) printf("Boot parameters editor.\n");
1997 signal(SIGINT
, trap
);
1998 signal(SIGALRM
, trap
);
2002 rawterm
.c_lflag
&= ~(ICANON
|ECHO
|IEXTEN
);
2003 rawterm
.c_cc
[VINTR
]= ESC
;
2004 if (tcsetattr(0, TCSANOW
, &rawterm
) < 0) fatal("");
2007 /* Get environment variables from the parameter sector. */
2012 /* While there are commands, execute them! */
2013 while (cmds
!= nil
|| i
< argc
) {
2015 /* A command line command. */
2016 parse_code(argv
[i
++]);
2020 /* Bail out on errors if not interactive. */
2021 if (err
&& !istty
) exit(1);
2024 /* Commands on the command line? */
2025 if (argc
> 2) break;
2027 /* The "monitor" is just a "read one command" thing. */
2035 * $PchId: boot.c,v 1.14 2002/02/27 19:46:14 philip Exp $