18 19#include <sys/param.h> 20#include <sys/gpt.h> 21#include <sys/dirent.h> 22#include <sys/reboot.h> 23 24#include <machine/bootinfo.h> 25#include <machine/elf.h> 26#include <machine/psl.h> 27 28#include <stdarg.h> 29 30#include <a.out.h> 31 32#include <btxv86.h> 33 34#include "lib.h" 35#include "rbx.h" 36#include "drv.h" 37#include "util.h" 38#include "cons.h" 39#include "gpt.h" 40 41#define PATH_DOTCONFIG "/boot.config" 42#define PATH_CONFIG "/boot/config" 43#define PATH_BOOT3 "/boot/loader" 44#define PATH_KERNEL "/boot/kernel/kernel" 45 46#define ARGS 0x900 47#define NOPT 14 48#define NDEV 3 49#define MEM_BASE 0x12 50#define MEM_EXT 0x15 51 52#define DRV_HARD 0x80 53#define DRV_MASK 0x7f 54 55#define TYPE_AD 0 56#define TYPE_DA 1 57#define TYPE_MAXHARD TYPE_DA 58#define TYPE_FD 2 59 60extern uint32_t _end; 61 62static const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS; 63static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ 64static const unsigned char flags[NOPT] = { 65 RBX_DUAL, 66 RBX_SERIAL, 67 RBX_ASKNAME, 68 RBX_CDROM, 69 RBX_CONFIG, 70 RBX_KDB, 71 RBX_GDB, 72 RBX_MUTE, 73 RBX_NOINTR, 74 RBX_PAUSE, 75 RBX_QUIET, 76 RBX_DFLTROOT, 77 RBX_SINGLE, 78 RBX_VERBOSE 79}; 80uint32_t opts; 81 82static const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; 83static const unsigned char dev_maj[NDEV] = {30, 4, 2}; 84 85static struct dsk dsk; 86static char kname[1024]; 87static int comspeed = SIOSPD; 88static struct bootinfo bootinfo; 89 90void exit(int); 91static void load(void); 92static int parse(char *, int *); 93static int dskread(void *, daddr_t, unsigned); 94static uint32_t memsize(void); 95 96#include "ufsread.c" 97 98static inline int 99xfsread(ufs_ino_t inode, void *buf, size_t nbyte) 100{ 101 102 if ((size_t)fsread(inode, buf, nbyte) != nbyte) { 103 printf("Invalid %s\n", "format"); 104 return (-1); 105 } 106 return (0); 107} 108 109static inline uint32_t 110memsize(void) 111{ 112 113 v86.addr = MEM_EXT; 114 v86.eax = 0x8800; 115 v86int(); 116 return (v86.eax); 117} 118 119static int 120gptinit(void) 121{ 122 123 if (gptread(&freebsd_ufs_uuid, &dsk, dmadat->secbuf) == -1) { 124 printf("%s: unable to load GPT\n", BOOTPROG); 125 return (-1); 126 } 127 if (gptfind(&freebsd_ufs_uuid, &dsk, dsk.part) == -1) { 128 printf("%s: no UFS partition was found\n", BOOTPROG); 129 return (-1); 130 } 131 dsk_meta = 0; 132 return (0); 133} 134 135int 136main(void) 137{ 138 char cmd[512], cmdtmp[512]; 139 int autoboot, dskupdated; 140 ufs_ino_t ino; 141 142 dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); 143 v86.ctl = V86_FLAGS; 144 v86.efl = PSL_RESERVED_DEFAULT | PSL_I; 145 dsk.drive = *(uint8_t *)PTOV(ARGS); 146 dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; 147 dsk.unit = dsk.drive & DRV_MASK; 148 dsk.part = -1; 149 dsk.start = 0; 150 bootinfo.bi_version = BOOTINFO_VERSION; 151 bootinfo.bi_size = sizeof(bootinfo); 152 bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */ 153 bootinfo.bi_extmem = memsize(); 154 bootinfo.bi_memsizes_valid++; 155 156 /* Process configuration file */ 157 158 if (gptinit() != 0) 159 return (-1); 160 161 autoboot = 1; 162 *cmd = '\0'; 163 164 for (;;) { 165 *kname = '\0'; 166 if ((ino = lookup(PATH_CONFIG)) || 167 (ino = lookup(PATH_DOTCONFIG))) 168 fsread(ino, cmd, sizeof(cmd)); 169 170 if (*cmd != '\0') { 171 memcpy(cmdtmp, cmd, sizeof(cmdtmp)); 172 if (parse(cmdtmp, &dskupdated)) 173 break; 174 if (dskupdated && gptinit() != 0) 175 break; 176 if (!OPT_CHECK(RBX_QUIET)) 177 printf("%s: %s", PATH_CONFIG, cmd); 178 *cmd = '\0'; 179 } 180 181 if (autoboot && keyhit(3)) { 182 if (*kname == '\0') 183 memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3)); 184 break; 185 } 186 autoboot = 0; 187 188 /* 189 * Try to exec stage 3 boot loader. If interrupted by a 190 * keypress, or in case of failure, try to load a kernel 191 * directly instead. 192 */ 193 if (*kname != '\0') 194 load(); 195 memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3)); 196 load(); 197 memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); 198 load(); 199 gptbootfailed(&dsk); 200 if (gptfind(&freebsd_ufs_uuid, &dsk, -1) == -1) 201 break; 202 dsk_meta = 0; 203 } 204 205 /* Present the user with the boot2 prompt. */ 206 207 for (;;) { 208 if (!OPT_CHECK(RBX_QUIET)) { 209 printf("\nFreeBSD/x86 boot\n" 210 "Default: %u:%s(%up%u)%s\n" 211 "boot: ", 212 dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 213 dsk.part, kname); 214 } 215 if (ioctrl & IO_SERIAL) 216 sio_flush(); 217 *cmd = '\0'; 218 if (keyhit(0)) 219 getstr(cmd, sizeof(cmd)); 220 else if (!OPT_CHECK(RBX_QUIET)) 221 putchar('\n'); 222 if (parse(cmd, &dskupdated)) { 223 putchar('\a'); 224 continue; 225 } 226 if (dskupdated && gptinit() != 0) 227 continue; 228 load(); 229 } 230 /* NOTREACHED */ 231} 232 233/* XXX - Needed for btxld to link the boot2 binary; do not remove. */ 234void 235exit(int x) 236{ 237} 238 239static void 240load(void) 241{ 242 union { 243 struct exec ex; 244 Elf32_Ehdr eh; 245 } hdr; 246 static Elf32_Phdr ep[2]; 247 static Elf32_Shdr es[2]; 248 caddr_t p; 249 ufs_ino_t ino; 250 uint32_t addr, x; 251 int fmt, i, j; 252 253 if (!(ino = lookup(kname))) { 254 if (!ls) { 255 printf("%s: No %s on %u:%s(%up%u)\n", BOOTPROG, 256 kname, dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 257 dsk.part); 258 } 259 return; 260 } 261 if (xfsread(ino, &hdr, sizeof(hdr))) 262 return; 263 if (N_GETMAGIC(hdr.ex) == ZMAGIC) 264 fmt = 0; 265 else if (IS_ELF(hdr.eh)) 266 fmt = 1; 267 else { 268 printf("Invalid %s\n", "format"); 269 return; 270 } 271 if (fmt == 0) { 272 addr = hdr.ex.a_entry & 0xffffff; 273 p = PTOV(addr); 274 fs_off = PAGE_SIZE; 275 if (xfsread(ino, p, hdr.ex.a_text)) 276 return; 277 p += roundup2(hdr.ex.a_text, PAGE_SIZE); 278 if (xfsread(ino, p, hdr.ex.a_data)) 279 return; 280 p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); 281 bootinfo.bi_symtab = VTOP(p); 282 memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); 283 p += sizeof(hdr.ex.a_syms); 284 if (hdr.ex.a_syms) { 285 if (xfsread(ino, p, hdr.ex.a_syms)) 286 return; 287 p += hdr.ex.a_syms; 288 if (xfsread(ino, p, sizeof(int))) 289 return; 290 x = *(uint32_t *)p; 291 p += sizeof(int); 292 x -= sizeof(int); 293 if (xfsread(ino, p, x)) 294 return; 295 p += x; 296 } 297 } else { 298 fs_off = hdr.eh.e_phoff; 299 for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { 300 if (xfsread(ino, ep + j, sizeof(ep[0]))) 301 return; 302 if (ep[j].p_type == PT_LOAD) 303 j++; 304 } 305 for (i = 0; i < 2; i++) { 306 p = PTOV(ep[i].p_paddr & 0xffffff); 307 fs_off = ep[i].p_offset; 308 if (xfsread(ino, p, ep[i].p_filesz)) 309 return; 310 } 311 p += roundup2(ep[1].p_memsz, PAGE_SIZE); 312 bootinfo.bi_symtab = VTOP(p); 313 if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { 314 fs_off = hdr.eh.e_shoff + sizeof(es[0]) * 315 (hdr.eh.e_shstrndx + 1); 316 if (xfsread(ino, &es, sizeof(es))) 317 return; 318 for (i = 0; i < 2; i++) { 319 memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size)); 320 p += sizeof(es[i].sh_size); 321 fs_off = es[i].sh_offset; 322 if (xfsread(ino, p, es[i].sh_size)) 323 return; 324 p += es[i].sh_size; 325 } 326 } 327 addr = hdr.eh.e_entry & 0xffffff; 328 } 329 bootinfo.bi_esymtab = VTOP(p); 330 bootinfo.bi_kernelname = VTOP(kname); 331 bootinfo.bi_bios_dev = dsk.drive; 332 __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), 333 MAKEBOOTDEV(dev_maj[dsk.type], dsk.part + 1, dsk.unit, 0xff), 334 0, 0, 0, VTOP(&bootinfo)); 335} 336 337static int 338parse(char *cmdstr, int *dskupdated) 339{ 340 char *arg = cmdstr; 341 char *ep, *p, *q; 342 const char *cp; 343 unsigned int drv; 344 int c, i, j; 345 346 *dskupdated = 0; 347 while ((c = *arg++)) { 348 if (c == ' ' || c == '\t' || c == '\n') 349 continue; 350 for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); 351 ep = p; 352 if (*p) 353 *p++ = 0; 354 if (c == '-') { 355 while ((c = *arg++)) { 356 if (c == 'P') { 357 if (*(uint8_t *)PTOV(0x496) & 0x10) { 358 cp = "yes"; 359 } else { 360 opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL); 361 cp = "no"; 362 } 363 printf("Keyboard: %s\n", cp); 364 continue; 365 } else if (c == 'S') { 366 j = 0; 367 while ((unsigned int)(i = *arg++ - '0') <= 9) 368 j = j * 10 + i; 369 if (j > 0 && i == -'0') { 370 comspeed = j; 371 break; 372 } 373 /* Fall through to error below ('S' not in optstr[]). */ 374 } 375 for (i = 0; c != optstr[i]; i++) 376 if (i == NOPT - 1) 377 return -1; 378 opts ^= OPT_SET(flags[i]); 379 } 380 ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : 381 OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
| 18 19#include <sys/param.h> 20#include <sys/gpt.h> 21#include <sys/dirent.h> 22#include <sys/reboot.h> 23 24#include <machine/bootinfo.h> 25#include <machine/elf.h> 26#include <machine/psl.h> 27 28#include <stdarg.h> 29 30#include <a.out.h> 31 32#include <btxv86.h> 33 34#include "lib.h" 35#include "rbx.h" 36#include "drv.h" 37#include "util.h" 38#include "cons.h" 39#include "gpt.h" 40 41#define PATH_DOTCONFIG "/boot.config" 42#define PATH_CONFIG "/boot/config" 43#define PATH_BOOT3 "/boot/loader" 44#define PATH_KERNEL "/boot/kernel/kernel" 45 46#define ARGS 0x900 47#define NOPT 14 48#define NDEV 3 49#define MEM_BASE 0x12 50#define MEM_EXT 0x15 51 52#define DRV_HARD 0x80 53#define DRV_MASK 0x7f 54 55#define TYPE_AD 0 56#define TYPE_DA 1 57#define TYPE_MAXHARD TYPE_DA 58#define TYPE_FD 2 59 60extern uint32_t _end; 61 62static const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS; 63static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ 64static const unsigned char flags[NOPT] = { 65 RBX_DUAL, 66 RBX_SERIAL, 67 RBX_ASKNAME, 68 RBX_CDROM, 69 RBX_CONFIG, 70 RBX_KDB, 71 RBX_GDB, 72 RBX_MUTE, 73 RBX_NOINTR, 74 RBX_PAUSE, 75 RBX_QUIET, 76 RBX_DFLTROOT, 77 RBX_SINGLE, 78 RBX_VERBOSE 79}; 80uint32_t opts; 81 82static const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; 83static const unsigned char dev_maj[NDEV] = {30, 4, 2}; 84 85static struct dsk dsk; 86static char kname[1024]; 87static int comspeed = SIOSPD; 88static struct bootinfo bootinfo; 89 90void exit(int); 91static void load(void); 92static int parse(char *, int *); 93static int dskread(void *, daddr_t, unsigned); 94static uint32_t memsize(void); 95 96#include "ufsread.c" 97 98static inline int 99xfsread(ufs_ino_t inode, void *buf, size_t nbyte) 100{ 101 102 if ((size_t)fsread(inode, buf, nbyte) != nbyte) { 103 printf("Invalid %s\n", "format"); 104 return (-1); 105 } 106 return (0); 107} 108 109static inline uint32_t 110memsize(void) 111{ 112 113 v86.addr = MEM_EXT; 114 v86.eax = 0x8800; 115 v86int(); 116 return (v86.eax); 117} 118 119static int 120gptinit(void) 121{ 122 123 if (gptread(&freebsd_ufs_uuid, &dsk, dmadat->secbuf) == -1) { 124 printf("%s: unable to load GPT\n", BOOTPROG); 125 return (-1); 126 } 127 if (gptfind(&freebsd_ufs_uuid, &dsk, dsk.part) == -1) { 128 printf("%s: no UFS partition was found\n", BOOTPROG); 129 return (-1); 130 } 131 dsk_meta = 0; 132 return (0); 133} 134 135int 136main(void) 137{ 138 char cmd[512], cmdtmp[512]; 139 int autoboot, dskupdated; 140 ufs_ino_t ino; 141 142 dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); 143 v86.ctl = V86_FLAGS; 144 v86.efl = PSL_RESERVED_DEFAULT | PSL_I; 145 dsk.drive = *(uint8_t *)PTOV(ARGS); 146 dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; 147 dsk.unit = dsk.drive & DRV_MASK; 148 dsk.part = -1; 149 dsk.start = 0; 150 bootinfo.bi_version = BOOTINFO_VERSION; 151 bootinfo.bi_size = sizeof(bootinfo); 152 bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */ 153 bootinfo.bi_extmem = memsize(); 154 bootinfo.bi_memsizes_valid++; 155 156 /* Process configuration file */ 157 158 if (gptinit() != 0) 159 return (-1); 160 161 autoboot = 1; 162 *cmd = '\0'; 163 164 for (;;) { 165 *kname = '\0'; 166 if ((ino = lookup(PATH_CONFIG)) || 167 (ino = lookup(PATH_DOTCONFIG))) 168 fsread(ino, cmd, sizeof(cmd)); 169 170 if (*cmd != '\0') { 171 memcpy(cmdtmp, cmd, sizeof(cmdtmp)); 172 if (parse(cmdtmp, &dskupdated)) 173 break; 174 if (dskupdated && gptinit() != 0) 175 break; 176 if (!OPT_CHECK(RBX_QUIET)) 177 printf("%s: %s", PATH_CONFIG, cmd); 178 *cmd = '\0'; 179 } 180 181 if (autoboot && keyhit(3)) { 182 if (*kname == '\0') 183 memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3)); 184 break; 185 } 186 autoboot = 0; 187 188 /* 189 * Try to exec stage 3 boot loader. If interrupted by a 190 * keypress, or in case of failure, try to load a kernel 191 * directly instead. 192 */ 193 if (*kname != '\0') 194 load(); 195 memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3)); 196 load(); 197 memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); 198 load(); 199 gptbootfailed(&dsk); 200 if (gptfind(&freebsd_ufs_uuid, &dsk, -1) == -1) 201 break; 202 dsk_meta = 0; 203 } 204 205 /* Present the user with the boot2 prompt. */ 206 207 for (;;) { 208 if (!OPT_CHECK(RBX_QUIET)) { 209 printf("\nFreeBSD/x86 boot\n" 210 "Default: %u:%s(%up%u)%s\n" 211 "boot: ", 212 dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 213 dsk.part, kname); 214 } 215 if (ioctrl & IO_SERIAL) 216 sio_flush(); 217 *cmd = '\0'; 218 if (keyhit(0)) 219 getstr(cmd, sizeof(cmd)); 220 else if (!OPT_CHECK(RBX_QUIET)) 221 putchar('\n'); 222 if (parse(cmd, &dskupdated)) { 223 putchar('\a'); 224 continue; 225 } 226 if (dskupdated && gptinit() != 0) 227 continue; 228 load(); 229 } 230 /* NOTREACHED */ 231} 232 233/* XXX - Needed for btxld to link the boot2 binary; do not remove. */ 234void 235exit(int x) 236{ 237} 238 239static void 240load(void) 241{ 242 union { 243 struct exec ex; 244 Elf32_Ehdr eh; 245 } hdr; 246 static Elf32_Phdr ep[2]; 247 static Elf32_Shdr es[2]; 248 caddr_t p; 249 ufs_ino_t ino; 250 uint32_t addr, x; 251 int fmt, i, j; 252 253 if (!(ino = lookup(kname))) { 254 if (!ls) { 255 printf("%s: No %s on %u:%s(%up%u)\n", BOOTPROG, 256 kname, dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 257 dsk.part); 258 } 259 return; 260 } 261 if (xfsread(ino, &hdr, sizeof(hdr))) 262 return; 263 if (N_GETMAGIC(hdr.ex) == ZMAGIC) 264 fmt = 0; 265 else if (IS_ELF(hdr.eh)) 266 fmt = 1; 267 else { 268 printf("Invalid %s\n", "format"); 269 return; 270 } 271 if (fmt == 0) { 272 addr = hdr.ex.a_entry & 0xffffff; 273 p = PTOV(addr); 274 fs_off = PAGE_SIZE; 275 if (xfsread(ino, p, hdr.ex.a_text)) 276 return; 277 p += roundup2(hdr.ex.a_text, PAGE_SIZE); 278 if (xfsread(ino, p, hdr.ex.a_data)) 279 return; 280 p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); 281 bootinfo.bi_symtab = VTOP(p); 282 memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); 283 p += sizeof(hdr.ex.a_syms); 284 if (hdr.ex.a_syms) { 285 if (xfsread(ino, p, hdr.ex.a_syms)) 286 return; 287 p += hdr.ex.a_syms; 288 if (xfsread(ino, p, sizeof(int))) 289 return; 290 x = *(uint32_t *)p; 291 p += sizeof(int); 292 x -= sizeof(int); 293 if (xfsread(ino, p, x)) 294 return; 295 p += x; 296 } 297 } else { 298 fs_off = hdr.eh.e_phoff; 299 for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { 300 if (xfsread(ino, ep + j, sizeof(ep[0]))) 301 return; 302 if (ep[j].p_type == PT_LOAD) 303 j++; 304 } 305 for (i = 0; i < 2; i++) { 306 p = PTOV(ep[i].p_paddr & 0xffffff); 307 fs_off = ep[i].p_offset; 308 if (xfsread(ino, p, ep[i].p_filesz)) 309 return; 310 } 311 p += roundup2(ep[1].p_memsz, PAGE_SIZE); 312 bootinfo.bi_symtab = VTOP(p); 313 if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { 314 fs_off = hdr.eh.e_shoff + sizeof(es[0]) * 315 (hdr.eh.e_shstrndx + 1); 316 if (xfsread(ino, &es, sizeof(es))) 317 return; 318 for (i = 0; i < 2; i++) { 319 memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size)); 320 p += sizeof(es[i].sh_size); 321 fs_off = es[i].sh_offset; 322 if (xfsread(ino, p, es[i].sh_size)) 323 return; 324 p += es[i].sh_size; 325 } 326 } 327 addr = hdr.eh.e_entry & 0xffffff; 328 } 329 bootinfo.bi_esymtab = VTOP(p); 330 bootinfo.bi_kernelname = VTOP(kname); 331 bootinfo.bi_bios_dev = dsk.drive; 332 __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), 333 MAKEBOOTDEV(dev_maj[dsk.type], dsk.part + 1, dsk.unit, 0xff), 334 0, 0, 0, VTOP(&bootinfo)); 335} 336 337static int 338parse(char *cmdstr, int *dskupdated) 339{ 340 char *arg = cmdstr; 341 char *ep, *p, *q; 342 const char *cp; 343 unsigned int drv; 344 int c, i, j; 345 346 *dskupdated = 0; 347 while ((c = *arg++)) { 348 if (c == ' ' || c == '\t' || c == '\n') 349 continue; 350 for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); 351 ep = p; 352 if (*p) 353 *p++ = 0; 354 if (c == '-') { 355 while ((c = *arg++)) { 356 if (c == 'P') { 357 if (*(uint8_t *)PTOV(0x496) & 0x10) { 358 cp = "yes"; 359 } else { 360 opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL); 361 cp = "no"; 362 } 363 printf("Keyboard: %s\n", cp); 364 continue; 365 } else if (c == 'S') { 366 j = 0; 367 while ((unsigned int)(i = *arg++ - '0') <= 9) 368 j = j * 10 + i; 369 if (j > 0 && i == -'0') { 370 comspeed = j; 371 break; 372 } 373 /* Fall through to error below ('S' not in optstr[]). */ 374 } 375 for (i = 0; c != optstr[i]; i++) 376 if (i == NOPT - 1) 377 return -1; 378 opts ^= OPT_SET(flags[i]); 379 } 380 ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : 381 OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
|