diff --git a/stand/i386/libi386/biosdisk.c b/stand/i386/libi386/biosdisk.c index d473f65e23f6..00d2de5de418 100644 --- a/stand/i386/libi386/biosdisk.c +++ b/stand/i386/libi386/biosdisk.c @@ -98,6 +98,7 @@ struct specification_packet { uint16_t sp_sectorcount; uint16_t sp_cylsec; uint8_t sp_head; + uint8_t sp_dummy[16]; /* Avoid memory corruption */ }; /* @@ -372,53 +373,91 @@ cd_init(void) return (0); } +/* + * Information from bootable CD-ROM. + */ +static int +bd_get_diskinfo_cd(struct bdinfo *bd) +{ + struct specification_packet bc_sp; + int ret = -1; + + (void) memset(&bc_sp, 0, sizeof (bc_sp)); + /* Set sp_size as per specification. */ + bc_sp.sp_size = sizeof (bc_sp) - sizeof (bc_sp.sp_dummy); + + v86.ctl = V86_FLAGS; + v86.addr = DISK_BIOS; + v86.eax = CMD_CD_GET_STATUS; + v86.edx = bd->bd_unit; + v86.ds = VTOPSEG(&bc_sp); + v86.esi = VTOPOFF(&bc_sp); + v86int(); + + if ((v86.eax & 0xff00) == 0 && + bc_sp.sp_drive == bd->bd_unit) { + bd->bd_cyl = ((bc_sp.sp_cylsec & 0xc0) << 2) + + ((bc_sp.sp_cylsec & 0xff00) >> 8) + 1; + bd->bd_sec = bc_sp.sp_cylsec & 0x3f; + bd->bd_hds = bc_sp.sp_head + 1; + bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec; + + if (bc_sp.sp_bootmedia & 0x0F) { + /* Floppy or hard-disk emulation */ + bd->bd_sectorsize = BIOSDISK_SECSIZE; + return (-1); + } else { + bd->bd_sectorsize = 2048; + bd->bd_flags = BD_MODEEDD | BD_CDROM; + ret = 0; + } + } + + /* + * If this is the boot_drive, default to non-emulation bootable CD-ROM. + */ + if (ret != 0 && bd->bd_unit >= 0x88) { + bd->bd_cyl = 0; + bd->bd_hds = 1; + bd->bd_sec = 15; + bd->bd_sectorsize = 2048; + bd->bd_flags = BD_MODEEDD | BD_CDROM; + bd->bd_sectors = 0; + ret = 0; + } + + /* + * Note we can not use bd_get_diskinfo_ext() nor bd_get_diskinfo_std() + * here - some systems do get hung with those. + */ + /* + * Still no size? use 7.961GB. The size does not really matter + * as long as it is reasonably large to make our reads to pass + * the sector count check. + */ + if (bd->bd_sectors == 0) + bd->bd_sectors = 4173824; + + return (ret); +} + int bc_add(int biosdev) { bdinfo_t *bd; - struct specification_packet bc_sp; int nbcinfo = 0; if (!STAILQ_EMPTY(&cdinfo)) return (-1); - v86.ctl = V86_FLAGS; - v86.addr = DISK_BIOS; - v86.eax = CMD_CD_GET_STATUS; - v86.edx = biosdev; - v86.ds = VTOPSEG(&bc_sp); - v86.esi = VTOPOFF(&bc_sp); - v86int(); - if ((v86.eax & 0xff00) != 0) - return (-1); - if ((bd = calloc(1, sizeof(*bd))) == NULL) return (-1); - bd->bd_flags = BD_CDROM; bd->bd_unit = biosdev; - bd->bd_sectorsize = 2048; - - /* - * Ignore result from bd_int13probe(), we will use local - * workaround below. - */ - (void)bd_int13probe(bd); - - if (bd->bd_cyl == 0) { - bd->bd_cyl = ((bc_sp.sp_cylsec & 0xc0) << 2) + - ((bc_sp.sp_cylsec & 0xff00) >> 8) + 1; + if (bd_get_diskinfo_cd(bd) < 0) { + free(bd); + return (-1); } - if (bd->bd_hds == 0) - bd->bd_hds = bc_sp.sp_head + 1; - if (bd->bd_sec == 0) - bd->bd_sec = bc_sp.sp_cylsec & 0x3f; - if (bd->bd_sectors == 0) - bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec; - - /* Still no size? use 7.961GB */ - if (bd->bd_sectors == 0) - bd->bd_sectors = 4173824; STAILQ_INSERT_TAIL(&cdinfo, bd, bd_link); printf("BIOS CD is cd%d\n", nbcinfo); @@ -499,22 +538,32 @@ bd_get_diskinfo_std(struct bdinfo *bd) /* * Read EDD info. Return 0 on success, error otherwise. + * + * Avoid stack corruption on some systems by adding extra bytes to + * params block. */ static int bd_get_diskinfo_ext(struct bdinfo *bd) { - struct edd_params params; + struct disk_params { + struct edd_params head; + struct edd_device_path_v3 device_path; + uint8_t dummy[16]; + } __packed dparams; + struct edd_params *params; uint64_t total; + params = &dparams.head; + /* Get disk params */ - bzero(¶ms, sizeof(params)); - params.len = sizeof(params); + bzero(&dparams, sizeof(dparams)); + params->len = sizeof(struct edd_params_v3); v86.ctl = V86_FLAGS; v86.addr = DISK_BIOS; v86.eax = CMD_EXT_PARAM; v86.edx = bd->bd_unit; - v86.ds = VTOPSEG(¶ms); - v86.esi = VTOPOFF(¶ms); + v86.ds = VTOPSEG(&dparams); + v86.esi = VTOPOFF(&dparams); v86int(); if (V86_CY(v86.efl) && ((v86.eax & 0xff00) != 0)) @@ -526,20 +575,20 @@ bd_get_diskinfo_ext(struct bdinfo *bd) * powerof2(params.sector_size). * 16K is largest read buffer we can use at this time. */ - if (params.sector_size >= 512 && - params.sector_size <= 16384 && - (params.sector_size % BIOSDISK_SECSIZE) == 0) - bd->bd_sectorsize = params.sector_size; + if (params->sector_size >= 512 && + params->sector_size <= 16384 && + (params->sector_size % BIOSDISK_SECSIZE) == 0) + bd->bd_sectorsize = params->sector_size; - bd->bd_cyl = params.cylinders; - bd->bd_hds = params.heads; - bd->bd_sec = params.sectors_per_track; + bd->bd_cyl = params->cylinders; + bd->bd_hds = params->heads; + bd->bd_sec = params->sectors_per_track; - if (params.sectors != 0) { - total = params.sectors; + if (params->sectors != 0) { + total = params->sectors; } else { - total = (uint64_t)params.cylinders * - params.heads * params.sectors_per_track; + total = (uint64_t)params->cylinders * + params->heads * params->sectors_per_track; } bd->bd_sectors = total; @@ -556,6 +605,10 @@ bd_int13probe(bdinfo_t *bd) bd->bd_flags &= ~BD_NO_MEDIA; + if ((bd->bd_flags & BD_CDROM) != 0) { + return (bd_get_diskinfo_cd(bd) == 0); + } + edd = bd_check_extensions(bd->bd_unit); if (edd == 0) bd->bd_flags |= BD_MODEINT13; @@ -604,10 +657,6 @@ bd_int13probe(bdinfo_t *bd) } if (ret != 0) { - /* CD is special case, bc_add() has its own fallback. */ - if ((bd->bd_flags & BD_CDROM) != 0) - return (true); - if (bd->bd_sectors != 0 && edd != 0) { bd->bd_sec = 63; bd->bd_hds = 255; @@ -619,8 +668,6 @@ bd_int13probe(bdinfo_t *bd) if ((bd->bd_flags & BD_FLOPPY) != 0) dv_name = biosfd.dv_name; - else if ((bd->bd_flags & BD_CDROM) != 0) - dv_name = bioscd.dv_name; else dv_name = bioshd.dv_name;