loader: bc_add can not use any other probes than ah=0x4b

CD boot is broken for some systems since bioscd and biosdisk merge. The issue is that we can not use anything else than int 13 ah=0x4b to query cd information.

The patch does restore the same probe as was originally used in bioscd.c. Additionally extra buffer padding is used to avoid memory corruption caused by some systems.

PR:		234031
Reported by:	ultramage and others
MFC after:	1 day
This commit is contained in:
Toomas Soome 2020-02-03 11:33:33 +00:00
parent d9e9979c02
commit b0af1e20e6
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=357442

View File

@ -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(&params, 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(&params);
v86.esi = VTOPOFF(&params);
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;