svn commit: r189749 - head/sys/boot/i386/libi386

John Baldwin jhb at FreeBSD.org
Thu Mar 12 13:41:53 PDT 2009


Author: jhb
Date: Thu Mar 12 20:41:52 2009
New Revision: 189749
URL: http://svn.freebsd.org/changeset/base/189749

Log:
  The recent change to use memory > 1MB for the heap by default broke CD
  booting because the CD driver did not use bounce buffers to ensure
  request buffers sent to the BIOS were always in the first 1MB.  Copy over
  the bounce buffer logic from the BIOS disk driver (minus the 64k boundary
  code for floppies) to fix this.
  
  Reported by:	kensmith

Modified:
  head/sys/boot/i386/libi386/bioscd.c
  head/sys/boot/i386/libi386/biosdisk.c
  head/sys/boot/i386/libi386/libi386.h

Modified: head/sys/boot/i386/libi386/bioscd.c
==============================================================================
--- head/sys/boot/i386/libi386/bioscd.c	Thu Mar 12 18:59:39 2009	(r189748)
+++ head/sys/boot/i386/libi386/bioscd.c	Thu Mar 12 20:41:52 2009	(r189749)
@@ -173,9 +173,9 @@ bc_add(int biosdev)
 static void
 bc_print(int verbose)
 {
-	int i;
 	char line[80];
-    
+	int i;
+
 	for (i = 0; i < nbcinfo; i++) {
 		sprintf(line, "    cd%d: Device 0x%x\n", i,
 		    bcinfo[i].bc_sp.sp_devicespec);
@@ -235,7 +235,7 @@ bc_strategy(void *devdata, int rw, daddr
 	if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
 		return (EINVAL);
 	dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
-	DEBUG("read %d from %d to %p", blks, dblk, buf);
+	DEBUG("read %d from %lld to %p", blks, dblk, buf);
 
 	if (rsize)
 		*rsize = 0;
@@ -244,9 +244,9 @@ bc_strategy(void *devdata, int rw, daddr
 		return (EIO);
 	}
 #ifdef BD_SUPPORT_FRAGS
-	DEBUG("bc_strategy: frag read %d from %d+%d to %p", 
+	DEBUG("frag read %d from %lld+%d to %p", 
 	    fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
-	if (fragsize && bc_read(unit, dblk + blks, 1, fragsize)) {
+	if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf)) {
 		DEBUG("frag read error");
 		return(EIO);
 	}
@@ -257,11 +257,15 @@ bc_strategy(void *devdata, int rw, daddr
 	return (0);
 }
 
+/* Max number of sectors to bounce-buffer at a time. */
+#define	CD_BOUNCEBUF	8
+
 static int
 bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
 {
-	u_int result, retry;
-	static unsigned short packet[8];
+	u_int maxfer, resid, result, retry, x;
+	caddr_t bbuf, p, xp;
+	static struct edd_packet packet;
 	int biosdev;
 #ifdef DISK_DEBUG
 	int error;
@@ -275,47 +279,77 @@ bc_read(int unit, daddr_t dblk, int blks
 	if (blks == 0)
 		return (0);
 
+	/* Decide whether we have to bounce */
+	if (VTOP(dest) >> 20 != 0) {
+		/* 
+		 * The destination buffer is above first 1MB of
+		 * physical memory so we have to arrange a suitable
+		 * bounce buffer.
+		 */
+		x = min(CD_BOUNCEBUF, (unsigned)blks);
+		bbuf = alloca(x * BIOSCD_SECSIZE);
+		maxfer = x;
+	} else {
+		bbuf = NULL;
+		maxfer = 0;
+	}
+	
 	biosdev = bc_unit2bios(unit);
-	/*
-	 * Loop retrying the operation a couple of times.  The BIOS
-	 * may also retry.
-	 */
-	for (retry = 0; retry < 3; retry++) {
-		/* If retrying, reset the drive */
-		if (retry > 0) {
+	resid = blks;
+	p = dest;
+
+	while (resid > 0) {
+		if (bbuf)
+			xp = bbuf;
+		else
+			xp = p;
+		x = resid;
+		if (maxfer > 0)
+			x = min(x, maxfer);
+
+		/*
+		 * Loop retrying the operation a couple of times.  The BIOS
+		 * may also retry.
+		 */
+		for (retry = 0; retry < 3; retry++) {
+			/* If retrying, reset the drive */
+			if (retry > 0) {
+				v86.ctl = V86_FLAGS;
+				v86.addr = 0x13;
+				v86.eax = 0;
+				v86.edx = biosdev;
+				v86int();
+			}
+
+			packet.len = 0x10;
+			packet.count = x;
+			packet.offset = VTOPOFF(xp);
+			packet.seg = VTOPSEG(xp);
+			packet.lba = dblk;
 			v86.ctl = V86_FLAGS;
 			v86.addr = 0x13;
-			v86.eax = 0;
+			v86.eax = 0x4200;
 			v86.edx = biosdev;
+			v86.ds = VTOPSEG(&packet);
+			v86.esi = VTOPOFF(&packet);
 			v86int();
+			result = (v86.efl & PSL_C);
+			if (result == 0)
+				break;
 		}
-	    
-		packet[0] = 0x10;
-		packet[1] = blks;
-		packet[2] = VTOPOFF(dest);
-		packet[3] = VTOPSEG(dest);
-		packet[4] = dblk & 0xffff;
-		packet[5] = dblk >> 16;
-		packet[6] = 0;
-		packet[7] = 0;
-		v86.ctl = V86_FLAGS;
-		v86.addr = 0x13;
-		v86.eax = 0x4200;
-		v86.edx = biosdev;
-		v86.ds = VTOPSEG(packet);
-		v86.esi = VTOPOFF(packet);
-		v86int();
-		result = (v86.efl & PSL_C);
-		if (result == 0)
-			break;
-	}
 	
 #ifdef DISK_DEBUG
-	error = (v86.eax >> 8) & 0xff;
+		error = (v86.eax >> 8) & 0xff;
 #endif
-	DEBUG("%d sectors from %ld to %p (0x%x) %s", blks, dblk, dest,
-	    VTOP(dest), result ? "failed" : "ok");
-	DEBUG("unit %d  status 0x%x",  unit, error);
+		DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p,
+		    VTOP(p), result ? "failed" : "ok");
+		DEBUG("unit %d  status 0x%x", unit, error);
+		if (bbuf != NULL)
+			bcopy(bbuf, p, x * BIOSCD_SECSIZE);
+		p += (x * BIOSCD_SECSIZE);
+		dblk += x;
+		resid -= x;
+	}
 	
 /*	hexdump(dest, (blks * BIOSCD_SECSIZE)); */
 	return(0);

Modified: head/sys/boot/i386/libi386/biosdisk.c
==============================================================================
--- head/sys/boot/i386/libi386/biosdisk.c	Thu Mar 12 18:59:39 2009	(r189748)
+++ head/sys/boot/i386/libi386/biosdisk.c	Thu Mar 12 20:41:52 2009	(r189749)
@@ -1125,14 +1125,6 @@ bd_realstrategy(void *devdata, int rw, d
 /* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */
 #define FLOPPY_BOUNCEBUF	18
 
-struct edd_packet {
-    uint16_t	len;
-    uint16_t	count;
-    uint16_t	offset;
-    uint16_t	seg;
-    uint64_t	lba;
-};
-
 static int
 bd_edd_io(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest, int write)
 {

Modified: head/sys/boot/i386/libi386/libi386.h
==============================================================================
--- head/sys/boot/i386/libi386/libi386.h	Thu Mar 12 18:59:39 2009	(r189748)
+++ head/sys/boot/i386/libi386/libi386.h	Thu Mar 12 20:41:52 2009	(r189749)
@@ -52,6 +52,14 @@ struct i386_devdesc
     } d_kind;
 };
 
+struct edd_packet {
+    uint16_t	len;
+    uint16_t	count;
+    uint16_t	offset;
+    uint16_t	seg;
+    uint64_t	lba;
+};
+                 
 int	i386_getdev(void **vdev, const char *devspec, const char **path);
 char	*i386_fmtdev(void *vdev);
 int	i386_setcurrdev(struct env_var *ev, int flags, const void *value);


More information about the svn-src-all mailing list