Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 4 Feb 2020 07:15:33 +0000 (UTC)
From:      Toomas Soome <tsoome@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org
Subject:   svn commit: r357495 - stable/12/stand/i386/libi386
Message-ID:  <202002040715.0147FXxt003926@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: tsoome
Date: Tue Feb  4 07:15:33 2020
New Revision: 357495
URL: https://svnweb.freebsd.org/changeset/base/357495

Log:
  MFC r357442:
  
  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

Modified:
  stable/12/stand/i386/libi386/biosdisk.c
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/stand/i386/libi386/biosdisk.c
==============================================================================
--- stable/12/stand/i386/libi386/biosdisk.c	Tue Feb  4 05:27:45 2020	(r357494)
+++ stable/12/stand/i386/libi386/biosdisk.c	Tue Feb  4 07:15:33 2020	(r357495)
@@ -82,6 +82,7 @@ struct specification_packet {
 	uint16_t	sp_sectorcount;
 	uint16_t	sp_cylsec;
 	uint8_t		sp_head;
+	uint8_t		sp_dummy[16];	/* Avoid memory corruption */
 };
 
 /*
@@ -356,54 +357,92 @@ cd_init(void)
 	return (0);
 }
 
-int
-bc_add(int biosdev)
+/*
+ * Information from bootable CD-ROM.
+ */
+static int
+bd_get_diskinfo_cd(struct bdinfo *bd)
 {
-	bdinfo_t *bd;
 	struct specification_packet bc_sp;
-	int nbcinfo = 0;
+	int ret = -1;
 
-	if (!STAILQ_EMPTY(&cdinfo))
-                return (-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 = 0x13;
         v86.eax = 0x4b01;
-        v86.edx = biosdev;
+        v86.edx = bd->bd_unit;
         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);
+	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;
 
-	bd->bd_flags = BD_CDROM;
-	bd->bd_unit = biosdev;
-	bd->bd_sectorsize = 2048;
+		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;
+		}
+	}
 
 	/*
-	 * Ignore result from bd_int13probe(), we will use local
-	 * workaround below.
+	 * If this is the boot_drive, default to non-emulation bootable CD-ROM.
 	 */
-	(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 (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;
 	}
-	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 */
+	/*
+	 * 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;
+	int nbcinfo = 0;
+
+	if (!STAILQ_EMPTY(&cdinfo))
+                return (-1);
+
+	if ((bd = calloc(1, sizeof(*bd))) == NULL)
+		return (-1);
+
+	bd->bd_unit = biosdev;
+	if (bd_get_diskinfo_cd(bd) < 0) {
+		free(bd);
+		return (-1);
+	}
+
 	STAILQ_INSERT_TAIL(&cdinfo, bd, bd_link);
         printf("BIOS CD is cd%d\n", nbcinfo);
         nbcinfo++;
@@ -483,22 +522,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 = 0x13;
 	v86.eax = 0x4800;
 	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))
@@ -510,20 +559,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;
 
@@ -540,6 +589,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;
@@ -588,10 +641,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;
@@ -603,8 +652,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;
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202002040715.0147FXxt003926>