direct-io.hg

changeset 13287:416d3086a572

[HVM] Add expansion-ROM boot support again.
Boot info now stored at 0x9ff00; registers saved in HDD load code.
Signed-off-by: Tim Deegan <Tim.Deegan@xensource.com>
author Tim Deegan <Tim.Deegan@xensource.com>
date Mon Jan 08 14:24:30 2007 +0000 (2007-01-08)
parents 9ba91a854787
children a06ee98fa3b6
files tools/firmware/rombios/rombios.c tools/ioemu/hw/pc.c tools/ioemu/vl.c
line diff
     1.1 --- a/tools/firmware/rombios/rombios.c	Sat Jan 06 15:56:52 2007 +0000
     1.2 +++ b/tools/firmware/rombios/rombios.c	Mon Jan 08 14:24:30 2007 +0000
     1.3 @@ -278,7 +278,6 @@ typedef unsigned short Bit16u;
     1.4  typedef unsigned short bx_bool;
     1.5  typedef unsigned long  Bit32u;
     1.6  
     1.7 -#if BX_USE_ATADRV
     1.8  
     1.9    void memsetb(seg,offset,value,count);
    1.10    void memcpyb(dseg,doffset,sseg,soffset,count);
    1.11 @@ -418,7 +417,6 @@ typedef unsigned long  Bit32u;
    1.12    ASM_END
    1.13    }
    1.14  #endif
    1.15 -#endif //BX_USE_ATADRV
    1.16  
    1.17    // read_dword and write_dword functions
    1.18    static Bit32u         read_dword();
    1.19 @@ -728,6 +726,8 @@ typedef struct {
    1.20    //     The EBDA structure should conform to 
    1.21    //     http://www.cybertrails.com/~fys/rombios.htm document
    1.22    //     I made the ata and cdemu structs begin at 0x121 in the EBDA seg
    1.23 +  // EBDA must be at most 768 bytes; it lives at 0x9fc00, and the boot 
    1.24 +  // device tables are at 0x9ff00 -- 0x9ffff
    1.25    typedef struct {
    1.26      unsigned char filler1[0x3D];
    1.27  
    1.28 @@ -885,7 +885,7 @@ static void           int14_function();
    1.29  static void           int15_function();
    1.30  static void           int16_function();
    1.31  static void           int17_function();
    1.32 -static Bit32u         int19_function();
    1.33 +static void           int19_function();
    1.34  static void           int1a_function();
    1.35  static void           int70_function();
    1.36  static void           int74_function();
    1.37 @@ -1854,28 +1854,100 @@ print_bios_banner()
    1.38    printf("\n");
    1.39  }
    1.40  
    1.41 +
    1.42 +//--------------------------------------------------------------------------
    1.43 +// BIOS Boot Specification 1.0.1 compatibility
    1.44 +//
    1.45 +// Very basic support for the BIOS Boot Specification, which allows expansion 
    1.46 +// ROMs to register themselves as boot devices, instead of just stealing the 
    1.47 +// INT 19h boot vector.
    1.48 +// 
    1.49 +// This is a hack: to do it properly requires a proper PnP BIOS and we aren't
    1.50 +// one; we just lie to the option ROMs to make them behave correctly. 
    1.51 +// We also don't support letting option ROMs register as bootable disk 
    1.52 +// drives (BCVs), only as bootable devices (BEVs). 
    1.53 +//
    1.54 +// http://www.phoenix.com/en/Customer+Services/White+Papers-Specs/pc+industry+specifications.htm
    1.55 +//--------------------------------------------------------------------------
    1.56 +
    1.57 +/* 256 bytes at 0x9ff00 -- 0x9ffff is used for the IPL boot table. */
    1.58 +#define IPL_SEG              0x9ff0
    1.59 +#define IPL_TABLE_OFFSET     0x0000
    1.60 +#define IPL_TABLE_ENTRIES    8
    1.61 +#define IPL_COUNT_OFFSET     0x0080  /* u16: number of valid table entries */
    1.62 +#define IPL_SEQUENCE_OFFSET  0x0082  /* u16: next boot device */
    1.63 +
    1.64 +struct ipl_entry {
    1.65 +  Bit16u type;
    1.66 +  Bit16u flags;
    1.67 +  Bit32u vector;
    1.68 +  Bit32u description;
    1.69 +  Bit32u reserved;
    1.70 +};
    1.71 +
    1.72 +static void 
    1.73 +init_boot_vectors() 
    1.74 +{
    1.75 +  struct ipl_entry e; 
    1.76 +  Bit16u count = 0;
    1.77 +  Bit16u ss = get_SS();
    1.78 +
    1.79 +  /* Clear out the IPL table. */
    1.80 +  memsetb(IPL_SEG, IPL_TABLE_OFFSET, 0, 0xff);
    1.81 +
    1.82 +  /* Floppy drive */
    1.83 +  e.type = 1; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
    1.84 +  memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
    1.85 +  count++;
    1.86 +
    1.87 +  /* First HDD */
    1.88 +  e.type = 2; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
    1.89 +  memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
    1.90 +  count++;
    1.91 +
    1.92 +#if BX_ELTORITO_BOOT
    1.93 +  /* CDROM */
    1.94 +  e.type = 3; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
    1.95 +  memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
    1.96 +  count++;
    1.97 +#endif  
    1.98 +
    1.99 +  /* Remember how many devices we have */
   1.100 +  write_word(IPL_SEG, IPL_COUNT_OFFSET, count);
   1.101 +  /* Not tried booting anything yet */
   1.102 +  write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xffff);
   1.103 +}
   1.104 +
   1.105 +static Bit8u
   1.106 +get_boot_vector(i, e)
   1.107 +Bit16u i; struct ipl_entry *e; 
   1.108 +{
   1.109 +  Bit16u count;
   1.110 +  Bit16u ss = get_SS();
   1.111 +  /* Get the count of boot devices, and refuse to overrun the array */
   1.112 +  count = read_word(IPL_SEG, IPL_COUNT_OFFSET);
   1.113 +  if (i >= count) return 0;
   1.114 +  /* OK to read this device */
   1.115 +  memcpyb(ss, e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (*e), sizeof (*e));
   1.116 +  return 1;
   1.117 +}
   1.118 +
   1.119 +
   1.120  //--------------------------------------------------------------------------
   1.121  // print_boot_device
   1.122  //   displays the boot device
   1.123  //--------------------------------------------------------------------------
   1.124  
   1.125 -static char drivetypes[][10]={"Floppy","Hard Disk","CD-Rom"};
   1.126 +static char drivetypes[][10]={"", "Floppy","Hard Disk","CD-Rom", "Network"};
   1.127  
   1.128  void
   1.129 -print_boot_device(cdboot, drive)
   1.130 -  Bit8u cdboot; Bit16u drive;
   1.131 +print_boot_device(type)
   1.132 +  Bit16u type;
   1.133  {
   1.134 -  Bit8u i;
   1.135 -
   1.136 -  // cdboot contains 0 if floppy/harddisk, 1 otherwise
   1.137 -  // drive contains real/emulated boot drive
   1.138 -
   1.139 -  if(cdboot)i=2;                    // CD-Rom
   1.140 -  else if((drive&0x0080)==0x00)i=0; // Floppy
   1.141 -  else if((drive&0x0080)==0x80)i=1; // Hard drive
   1.142 -  else return;
   1.143 -  
   1.144 -  printf("Booting from %s...\n",drivetypes[i]);
   1.145 +  /* NIC appears as type 0x80 */ 
   1.146 +  if (type == 0x80 ) type = 0x4;
   1.147 +  if (type == 0 || type > 0x4) BX_PANIC("Bad drive type\n"); 
   1.148 +  printf("Booting from %s...\n", drivetypes[type]);
   1.149  }
   1.150  
   1.151  //--------------------------------------------------------------------------
   1.152 @@ -1883,29 +1955,20 @@ print_boot_device(cdboot, drive)
   1.153  //   displays the reason why boot failed
   1.154  //--------------------------------------------------------------------------
   1.155    void
   1.156 -print_boot_failure(cdboot, drive, reason, lastdrive)
   1.157 -  Bit8u cdboot; Bit8u drive; Bit8u lastdrive;
   1.158 +print_boot_failure(type, reason)
   1.159 +  Bit16u type; Bit8u reason;
   1.160  {
   1.161 -  Bit16u drivenum = drive&0x7f;
   1.162 -
   1.163 -  // cdboot: 1 if boot from cd, 0 otherwise
   1.164 -  // drive : drive number
   1.165 -  // reason: 0 signature check failed, 1 read error
   1.166 -  // lastdrive: 1 boot drive is the last one in boot sequence
   1.167 - 
   1.168 -  if (cdboot)
   1.169 -    bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s failed\n",drivetypes[2]);
   1.170 -  else if (drive & 0x80)
   1.171 -    bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s %d failed\n", drivetypes[1],drivenum);
   1.172 +  if (type == 0 || type > 0x3) BX_PANIC("Bad drive type\n"); 
   1.173 +
   1.174 +  printf("Boot from %s failed", drivetypes[type]);
   1.175 +  if (type < 4) {
   1.176 +    /* Report the reason too */
   1.177 +  if (reason==0) 
   1.178 +    printf(": not a bootable disk");
   1.179    else
   1.180 -    bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s %d failed\n", drivetypes[0],drivenum);
   1.181 -
   1.182 -  if (lastdrive==1) {
   1.183 -    if (reason==0)
   1.184 -      BX_PANIC("Not a bootable disk\n");
   1.185 -    else
   1.186 -      BX_PANIC("Could not read the boot disk\n");
   1.187 +    printf(": could not read the boot disk");
   1.188    }
   1.189 +  printf("\n");
   1.190  }
   1.191  
   1.192  //--------------------------------------------------------------------------
   1.193 @@ -7553,19 +7616,19 @@ int17_function(regs, ds, iret_addr)
   1.194    }
   1.195  }
   1.196  
   1.197 -// returns bootsegment in ax, drive in bl
   1.198 -  Bit32u 
   1.199 -int19_function(bseqnr)
   1.200 -Bit8u bseqnr;
   1.201 +void
   1.202 +int19_function(seq_nr)
   1.203 +Bit16u seq_nr;
   1.204  {
   1.205    Bit16u ebda_seg=read_word(0x0040,0x000E);
   1.206 -  Bit16u bootseq;
   1.207 +  Bit16u bootdev;
   1.208    Bit8u  bootdrv;
   1.209 -  Bit8u  bootcd;
   1.210    Bit8u  bootchk;
   1.211    Bit16u bootseg;
   1.212 +  Bit16u bootip;
   1.213    Bit16u status;
   1.214 -  Bit8u  lastdrive=0;
   1.215 +
   1.216 +  struct ipl_entry e;
   1.217  
   1.218    // if BX_ELTORITO_BOOT is not defined, old behavior
   1.219    //   check bit 5 in CMOS reg 0x2d.  load either 0x00 or 0x80 into DL
   1.220 @@ -7582,62 +7645,54 @@ Bit8u bseqnr;
   1.221    //     0x01 : first floppy 
   1.222    //     0x02 : first harddrive
   1.223    //     0x03 : first cdrom
   1.224 +  //     0x04 - 0x0f : PnP expansion ROMs (e.g. Etherboot)
   1.225    //     else : boot failure
   1.226  
   1.227    // Get the boot sequence
   1.228  #if BX_ELTORITO_BOOT
   1.229 -  bootseq=inb_cmos(0x3d);
   1.230 -  bootseq|=((inb_cmos(0x38) & 0xf0) << 4);
   1.231 -
   1.232 -  if (bseqnr==2) bootseq >>= 4;
   1.233 -  if (bseqnr==3) bootseq >>= 8;
   1.234 -  if (bootseq<0x10) lastdrive = 1;
   1.235 -  bootdrv=0x00; bootcd=0;
   1.236 -  switch(bootseq & 0x0f) {
   1.237 -    case 0x01: bootdrv=0x00; bootcd=0; break;
   1.238 -    case 0x02: bootdrv=0x80; bootcd=0; break;
   1.239 -    case 0x03: bootdrv=0x00; bootcd=1; break;
   1.240 -    default:   return 0x00000000;
   1.241 -    }
   1.242 -#else
   1.243 -  bootseq=inb_cmos(0x2d);
   1.244 -
   1.245 -  if (bseqnr==2) {
   1.246 -    bootseq ^= 0x20;
   1.247 -    lastdrive = 1;
   1.248 +  bootdev = inb_cmos(0x3d);
   1.249 +  bootdev |= ((inb_cmos(0x38) & 0xf0) << 4);
   1.250 +  bootdev >>= 4 * seq_nr;
   1.251 +  bootdev &= 0xf;
   1.252 +  if (bootdev == 0) BX_PANIC("No bootable device.\n");
   1.253 +  
   1.254 +  /* Translate from CMOS runes to an IPL table offset by subtracting 1 */
   1.255 +  bootdev -= 1;
   1.256 +#else  
   1.257 +  if (seq_nr ==2) BX_PANIC("No more boot devices.");
   1.258 +  if (!!(inb_cmos(0x2d) & 0x20) ^ (seq_nr == 1)) 
   1.259 +      /* Boot from floppy if the bit is set or it's the second boot */
   1.260 +    bootdev = 0x00;
   1.261 +  else 
   1.262 +    bootdev = 0x01;
   1.263 +#endif
   1.264 +
   1.265 +  /* Read the boot device from the IPL table */
   1.266 +  if (get_boot_vector(bootdev, &e) == 0) {
   1.267 +    BX_INFO("Invalid boot device (0x%x)\n", bootdev);
   1.268 +    return;
   1.269    }
   1.270 -  bootdrv=0x00; bootcd=0;
   1.271 -  if((bootseq&0x20)==0) bootdrv=0x80;
   1.272 -#endif // BX_ELTORITO_BOOT
   1.273 -
   1.274 -#if BX_ELTORITO_BOOT
   1.275 -  // We have to boot from cd
   1.276 -  if (bootcd != 0) {
   1.277 -    status = cdrom_boot();
   1.278 -
   1.279 -    // If failure
   1.280 -    if ( (status & 0x00ff) !=0 ) {
   1.281 -      print_cdromboot_failure(status);
   1.282 -      print_boot_failure(bootcd, bootdrv, 1, lastdrive);
   1.283 -      return 0x00000000;
   1.284 -      }
   1.285 -
   1.286 -    bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
   1.287 -    bootdrv = (Bit8u)(status>>8);
   1.288 -    }
   1.289 -
   1.290 -#endif // BX_ELTORITO_BOOT
   1.291 -
   1.292 -  // We have to boot from harddisk or floppy
   1.293 -  if (bootcd == 0) {
   1.294 -    bootseg=0x07c0;
   1.295 +
   1.296 +  /* Do the loading, and set up vector as a far pointer to the boot
   1.297 +   * address, and bootdrv as the boot drive */
   1.298 +  print_boot_device(e.type);
   1.299 +
   1.300 +  switch(e.type) {
   1.301 +  case 0x01: /* FDD */
   1.302 +  case 0x02: /* HDD */
   1.303 +
   1.304 +    bootdrv = (e.type == 0x02) ? 0x80 : 0x00;
   1.305 +    bootseg = 0x07c0;
   1.306 +    status = 0;
   1.307  
   1.308  ASM_START
   1.309      push bp
   1.310      mov  bp, sp
   1.311 -
   1.312 -    mov  ax, #0x0000
   1.313 -    mov  _int19_function.status + 2[bp], ax
   1.314 +    push ax
   1.315 +    push bx
   1.316 +    push cx
   1.317 +    push dx
   1.318 +
   1.319      mov  dl, _int19_function.bootdrv + 2[bp]
   1.320      mov  ax, _int19_function.bootseg + 2[bp]
   1.321      mov  es, ax         ;; segment
   1.322 @@ -7653,43 +7708,83 @@ ASM_START
   1.323      mov  _int19_function.status + 2[bp], ax
   1.324  
   1.325  int19_load_done:
   1.326 +    pop  dx
   1.327 +    pop  cx
   1.328 +    pop  bx
   1.329 +    pop  ax
   1.330      pop  bp
   1.331  ASM_END
   1.332      
   1.333      if (status != 0) {
   1.334 -      print_boot_failure(bootcd, bootdrv, 1, lastdrive);
   1.335 -      return 0x00000000;
   1.336 +      print_boot_failure(e.type, 1);
   1.337 +      return;
   1.338 +    }
   1.339 +
   1.340 +    /* Always check the signature on a HDD boot sector; on FDD, only do
   1.341 +     * the check if the CMOS doesn't tell us to skip it */
   1.342 +    if (e.type != 0x00 || !((inb_cmos(0x38) & 0x01))) {
   1.343 +      if (read_word(bootseg,0x1fe) != 0xaa55) {
   1.344 +        print_boot_failure(e.type, 0);
   1.345 +        return;
   1.346        }
   1.347      }
   1.348  
   1.349 -  // check signature if instructed by cmos reg 0x38, only for floppy
   1.350 -  // bootchk = 1 : signature check disabled
   1.351 -  // bootchk = 0 : signature check enabled
   1.352 -  if (bootdrv != 0) bootchk = 0;
   1.353 -  else bootchk = inb_cmos(0x38) & 0x01;
   1.354 +    /* Canonicalize bootseg:bootip */
   1.355 +    bootip = (bootseg & 0x0fff) << 4;
   1.356 +    bootseg &= 0xf000;
   1.357 +  break;
   1.358  
   1.359  #if BX_ELTORITO_BOOT
   1.360 -  // if boot from cd, no signature check
   1.361 -  if (bootcd != 0)
   1.362 -    bootchk = 1;
   1.363 -#endif // BX_ELTORITO_BOOT
   1.364 -
   1.365 -  if (bootchk == 0) {
   1.366 -    if (read_word(bootseg,0x1fe) != 0xaa55) {
   1.367 -      print_boot_failure(bootcd, bootdrv, 0, lastdrive);
   1.368 -      return 0x00000000;
   1.369 -      }
   1.370 +  case 0x03: /* CD-ROM */
   1.371 +    status = cdrom_boot();
   1.372 +
   1.373 +    // If failure
   1.374 +    if ( (status & 0x00ff) !=0 ) {
   1.375 +      print_cdromboot_failure(status);
   1.376 +      print_boot_failure(e.type, 1);
   1.377 +      return;
   1.378      }
   1.379 +
   1.380 +    bootdrv = (Bit8u)(status>>8);
   1.381 +    bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
   1.382 +    /* Canonicalize bootseg:bootip */
   1.383 +    bootip = (bootseg & 0x0fff) << 4;
   1.384 +    bootseg &= 0xf000;
   1.385 +    break;
   1.386 +#endif
   1.387 +
   1.388 +  case 0x80: /* Expansion ROM with a Bootstrap Entry Vector (a far pointer) */
   1.389 +    bootseg = e.vector >> 16;
   1.390 +    bootip = e.vector & 0xffff;
   1.391 +    break;
   1.392 +
   1.393 +  default: return;
   1.394 +  }
   1.395 +
   1.396 +  /* Debugging info */
   1.397 +  printf("Booting from %x:%x\n", bootseg, bootip);
   1.398    
   1.399 -#if BX_ELTORITO_BOOT
   1.400 -  // Print out the boot string
   1.401 -  print_boot_device(bootcd, bootdrv);
   1.402 -#else // BX_ELTORITO_BOOT
   1.403 -  print_boot_device(0, bootdrv);
   1.404 -#endif // BX_ELTORITO_BOOT
   1.405 -
   1.406 -  // return the boot segment
   1.407 -  return (((Bit32u)bootdrv) << 16) + bootseg;
   1.408 +  /* Jump to the boot vector */
   1.409 +ASM_START
   1.410 +    mov  bp, sp
   1.411 +    ;; Build an iret stack frame that will take us to the boot vector.
   1.412 +    ;; iret pops ip, then cs, then flags, so push them in the opposite order.
   1.413 +    pushf
   1.414 +    mov  ax, _int19_function.bootseg + 0[bp] 
   1.415 +    push ax
   1.416 +    mov  ax, _int19_function.bootip + 0[bp] 
   1.417 +    push ax
   1.418 +    ;; Set the magic number in ax and the boot drive in dl.
   1.419 +    mov  ax, #0xaa55
   1.420 +    mov  dl, _int19_function.bootdrv + 0[bp]
   1.421 +    ;; Zero some of the other registers.
   1.422 +    xor  bx, bx
   1.423 +    mov  ds, bx
   1.424 +    mov  es, bx
   1.425 +    mov  bp, bx
   1.426 +    ;; Go!
   1.427 +    iret
   1.428 +ASM_END
   1.429  }
   1.430  
   1.431    void
   1.432 @@ -8146,14 +8241,29 @@ int13_out:
   1.433    popa
   1.434    iret 
   1.435  
   1.436 -
   1.437  ;----------
   1.438  ;- INT18h -
   1.439  ;----------
   1.440 -int18_handler: ;; Boot Failure routing
   1.441 -  call _int18_panic_msg
   1.442 -  hlt
   1.443 -  iret
   1.444 +int18_handler: ;; Boot Failure recovery: try the next device.
   1.445 +
   1.446 +  ;; Reset SP and SS
   1.447 +  mov  ax, #0xfffe
   1.448 +  mov  sp, ax
   1.449 +  xor  ax, ax
   1.450 +  mov  ss, ax
   1.451 +
   1.452 +  ;; Get the boot sequence number out of the IPL memory
   1.453 +  mov  bx, #IPL_SEG 
   1.454 +  mov  ds, bx                     ;; Set segment
   1.455 +  mov  bx, IPL_SEQUENCE_OFFSET    ;; BX is now the sequence number
   1.456 +  inc  bx                         ;; ++
   1.457 +  mov  IPL_SEQUENCE_OFFSET, bx    ;; Write it back
   1.458 +  mov  ds, ax                     ;; and reset the segment to zero. 
   1.459 +
   1.460 +  ;; Carry on in the INT 19h handler, using the new sequence number
   1.461 +  push bx
   1.462 +
   1.463 +  jmp  int19_next_boot
   1.464  
   1.465  ;----------
   1.466  ;- INT19h -
   1.467 @@ -8161,62 +8271,32 @@ int18_handler: ;; Boot Failure routing
   1.468  int19_relocated: ;; Boot function, relocated
   1.469  
   1.470    ;; int19 was beginning to be really complex, so now it
   1.471 -  ;; just calls an C function, that does the work
   1.472 -  ;; it returns in BL the boot drive, and in AX the boot segment
   1.473 -  ;; the boot segment will be 0x0000 if something has failed
   1.474 +  ;; just calls a C function that does the work
   1.475  
   1.476    push bp
   1.477    mov  bp, sp
   1.478 -
   1.479 -  ;; drop ds
   1.480 +  
   1.481 +  ;; Reset SS and SP
   1.482 +  mov  ax, #0xfffe
   1.483 +  mov  sp, ax
   1.484    xor  ax, ax
   1.485 -  mov  ds, ax
   1.486 -
   1.487 -  ;; 1st boot device
   1.488 -  mov  ax, #0x0001
   1.489 +  mov  ss, ax
   1.490 +
   1.491 +  ;; Start from the first boot device (0, in AX)
   1.492 +  mov  bx, #IPL_SEG 
   1.493 +  mov  ds, bx                     ;; Set segment to write to the IPL memory
   1.494 +  mov  IPL_SEQUENCE_OFFSET, ax    ;; Save the sequence number 
   1.495 +  mov  ds, ax                     ;; and reset the segment.
   1.496 +
   1.497    push ax
   1.498 -  call _int19_function
   1.499 -  inc  sp
   1.500 -  inc  sp
   1.501 -  ;; bl contains the boot drive
   1.502 -  ;; ax contains the boot segment or 0 if failure
   1.503 -
   1.504 -  test       ax, ax  ;; if ax is 0 try next boot device
   1.505 -  jnz        boot_setup
   1.506 -
   1.507 -  ;; 2nd boot device
   1.508 -  mov  ax, #0x0002
   1.509 -  push ax
   1.510 +
   1.511 +int19_next_boot:
   1.512 +
   1.513 +  ;; Call the C code for the next boot device
   1.514    call _int19_function
   1.515 -  inc  sp
   1.516 -  inc  sp
   1.517 -  test       ax, ax  ;; if ax is 0 try next boot device
   1.518 -  jnz        boot_setup
   1.519 -
   1.520 -  ;; 3rd boot device
   1.521 -  mov  ax, #0x0003
   1.522 -  push ax
   1.523 -  call _int19_function
   1.524 -  inc  sp
   1.525 -  inc  sp
   1.526 -  test       ax, ax  ;; if ax is 0 call int18
   1.527 -  jz         int18_handler
   1.528 -
   1.529 -boot_setup:
   1.530 -  mov dl,    bl      ;; set drive so guest os find it
   1.531 -  shl eax,   #0x04   ;; convert seg to ip
   1.532 -  mov 2[bp], ax      ;; set ip
   1.533 -
   1.534 -  shr eax,   #0x04   ;; get cs back
   1.535 -  and ax,    #0xF000 ;; remove what went in ip
   1.536 -  mov 4[bp], ax      ;; set cs
   1.537 -  xor ax,    ax
   1.538 -  mov es,    ax      ;; set es to zero fixes [ 549815 ]
   1.539 -  mov [bp],  ax      ;; set bp to zero
   1.540 -  mov ax,    #0xaa55 ;; set ok flag
   1.541 -
   1.542 -  pop bp
   1.543 -  iret               ;; Beam me up Scotty
   1.544 +
   1.545 +  ;; Boot failed: invoke the boot recovery function
   1.546 +  int  #0x18
   1.547  
   1.548  ;----------
   1.549  ;- INT1Ch -
   1.550 @@ -9394,6 +9474,15 @@ checksum_loop:
   1.551    pop  ax
   1.552    ret
   1.553  
   1.554 +
   1.555 +;; We need a copy of this string, but we are not actually a PnP BIOS, 
   1.556 +;; so make sure it is *not* aligned, so OSes will not see it if they scan.
   1.557 +.align 16
   1.558 +  db 0
   1.559 +pnp_string:
   1.560 +  .ascii "$PnP"
   1.561 +
   1.562 +
   1.563  rom_scan:
   1.564    ;; Scan for existence of valid expansion ROMS.
   1.565    ;;   Video ROM:   from 0xC0000..0xC7FFF in 2k increments
   1.566 @@ -9428,9 +9517,17 @@ block_count_rounded:
   1.567    xor  bx, bx   ;; Restore DS back to 0000:
   1.568    mov  ds, bx
   1.569    push ax       ;; Save AX
   1.570 +  push di       ;; Save DI
   1.571    ;; Push addr of ROM entry point
   1.572    push cx       ;; Push seg
   1.573    push #0x0003  ;; Push offset
   1.574 +
   1.575 +  ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.  
   1.576 +  ;; That should stop it grabbing INT 19h; we will use its BEV instead.
   1.577 +  mov  ax, #0xf000
   1.578 +  mov  es, ax
   1.579 +  lea  di, pnp_string 
   1.580 +
   1.581    mov  bp, sp   ;; Call ROM init routine using seg:off on stack
   1.582    db   0xff     ;; call_far ss:[bp+0]
   1.583    db   0x5e
   1.584 @@ -9438,6 +9535,38 @@ block_count_rounded:
   1.585    cli           ;; In case expansion ROM BIOS turns IF on
   1.586    add  sp, #2   ;; Pop offset value
   1.587    pop  cx       ;; Pop seg value (restore CX)
   1.588 +
   1.589 +  ;; Look at the ROM's PnP Expansion header.  Properly, we're supposed 
   1.590 +  ;; to init all the ROMs and then go back and build an IPL table of 
   1.591 +  ;; all the bootable devices, but we can get away with one pass.
   1.592 +  mov  ds, cx       ;; ROM base
   1.593 +  mov  bx, 0x001a   ;; 0x1A is the offset into ROM header that contains...
   1.594 +  mov  ax, [bx]     ;; the offset of PnP expansion header, where...
   1.595 +  cmp  ax, #0x5024  ;; we look for signature "$PnP"
   1.596 +  jne  no_bev
   1.597 +  mov  ax, 2[bx]
   1.598 +  cmp  ax, #0x506e 
   1.599 +  jne  no_bev
   1.600 +  mov  ax, 0x1a[bx] ;; 0x1A is also the offset into the expansion header of...
   1.601 +  cmp  ax, #0x0000  ;; the Bootstrap Entry Vector, or zero if there is none.
   1.602 +  je   no_bev
   1.603 +
   1.604 +  ;; Found a device that thinks it can boot the system.  Record its BEV.
   1.605 +  mov  bx, #IPL_SEG            ;; Go to the segment where the IPL table lives 
   1.606 +  mov  ds, bx
   1.607 +  mov  bx, IPL_COUNT_OFFSET    ;; Read the number of entries so far
   1.608 +  cmp  bx, #IPL_TABLE_ENTRIES
   1.609 +  je   no_bev                  ;; Get out if the table is full
   1.610 +  shl  bx, #0x4                ;; Turn count into offset (entries are 16 bytes)
   1.611 +  mov  0[bx], #0x80            ;; This entry is a BEV device
   1.612 +  mov  6[bx], cx               ;; Build a far pointer from the segment...
   1.613 +  mov  4[bx], ax               ;; and the offset
   1.614 +  shr  bx, #0x4                ;; Turn the offset back into a count
   1.615 +  inc  bx                      ;; We have one more entry now
   1.616 +  mov  IPL_COUNT_OFFSET, bx    ;; Remember that.
   1.617 +
   1.618 +no_bev:
   1.619 +  pop  di       ;; Restore DI
   1.620    pop  ax       ;; Restore AX
   1.621  rom_scan_increment:
   1.622    shl  ax, #5   ;; convert 512-bytes blocks to 16-byte increments
   1.623 @@ -9771,6 +9900,8 @@ post_default_ints:
   1.624    call smbios_init
   1.625  #endif
   1.626  
   1.627 +  call _init_boot_vectors
   1.628 +
   1.629    call rom_scan
   1.630  
   1.631    call _print_bios_banner 
     2.1 --- a/tools/ioemu/hw/pc.c	Sat Jan 06 15:56:52 2007 +0000
     2.2 +++ b/tools/ioemu/hw/pc.c	Mon Jan 08 14:24:30 2007 +0000
     2.3 @@ -168,6 +168,8 @@ static int get_bios_disk(char *boot_devi
     2.4              return 0x02;            /* hard drive */
     2.5          case 'd':
     2.6              return 0x03;            /* cdrom */
     2.7 +        case 'n':
     2.8 +            return 0x04;            /* network */
     2.9          }
    2.10      }
    2.11      return 0x00;                /* no device */
     3.1 --- a/tools/ioemu/vl.c	Sat Jan 06 15:56:52 2007 +0000
     3.2 +++ b/tools/ioemu/vl.c	Mon Jan 08 14:24:30 2007 +0000
     3.3 @@ -6153,7 +6153,7 @@ int main(int argc, char **argv)
     3.4              case QEMU_OPTION_boot:
     3.5                  boot_device = strdup(optarg);
     3.6                  if (strspn(boot_device, "acd"
     3.7 -#ifdef TARGET_SPARC
     3.8 +#if defined(TARGET_SPARC) || defined(TARGET_I386)
     3.9                             "n"
    3.10  #endif
    3.11                          ) != strlen(boot_device)) {