aboutsummaryrefslogtreecommitdiff
path: root/sys/boot
diff options
context:
space:
mode:
authorJohn Baldwin <jhb@FreeBSD.org>2009-03-12 20:41:52 +0000
committerJohn Baldwin <jhb@FreeBSD.org>2009-03-12 20:41:52 +0000
commit47193e464b4d01db52f9de4903afa549aa7330dd (patch)
treeb7511a8a70205f8121c7a49ba35fe0c33ff11426 /sys/boot
parent834772014b1bbc4ec6c3bb54ce6631c1cc7d031b (diff)
downloadsrc-47193e464b4d01db52f9de4903afa549aa7330dd.tar.gz
src-47193e464b4d01db52f9de4903afa549aa7330dd.zip
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
Notes
Notes: svn path=/head/; revision=189749
Diffstat (limited to 'sys/boot')
-rw-r--r--sys/boot/i386/libi386/bioscd.c112
-rw-r--r--sys/boot/i386/libi386/biosdisk.c8
-rw-r--r--sys/boot/i386/libi386/libi386.h8
3 files changed, 81 insertions, 47 deletions
diff --git a/sys/boot/i386/libi386/bioscd.c b/sys/boot/i386/libi386/bioscd.c
index 93de9586c31f..a6dba0c60538 100644
--- a/sys/boot/i386/libi386/bioscd.c
+++ b/sys/boot/i386/libi386/bioscd.c
@@ -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_t dblk, size_t size, char *buf,
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_t dblk, size_t size, char *buf,
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_t dblk, size_t size, char *buf,
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, caddr_t dest)
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);
diff --git a/sys/boot/i386/libi386/biosdisk.c b/sys/boot/i386/libi386/biosdisk.c
index 9d313252698d..a54418f37578 100644
--- a/sys/boot/i386/libi386/biosdisk.c
+++ b/sys/boot/i386/libi386/biosdisk.c
@@ -1125,14 +1125,6 @@ bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, siz
/* 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)
{
diff --git a/sys/boot/i386/libi386/libi386.h b/sys/boot/i386/libi386/libi386.h
index ccae09957e71..4ac5cb569fb2 100644
--- a/sys/boot/i386/libi386/libi386.h
+++ b/sys/boot/i386/libi386/libi386.h
@@ -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);