diff options
author | jhb <jhb@FreeBSD.org> | 2009-03-12 20:41:52 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2009-03-12 20:41:52 +0000 |
commit | da90bd71e1d84b40d4daf7ac20958c9c37d06b56 (patch) | |
tree | b7511a8a70205f8121c7a49ba35fe0c33ff11426 /sys/boot | |
parent | c4c0c0a4dfa1411f4f9792af4cf39663a24077a1 (diff) | |
download | FreeBSD-src-da90bd71e1d84b40d4daf7ac20958c9c37d06b56.zip FreeBSD-src-da90bd71e1d84b40d4daf7ac20958c9c37d06b56.tar.gz |
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
Diffstat (limited to 'sys/boot')
-rw-r--r-- | sys/boot/i386/libi386/bioscd.c | 112 | ||||
-rw-r--r-- | sys/boot/i386/libi386/biosdisk.c | 8 | ||||
-rw-r--r-- | sys/boot/i386/libi386/libi386.h | 8 |
3 files changed, 81 insertions, 47 deletions
diff --git a/sys/boot/i386/libi386/bioscd.c b/sys/boot/i386/libi386/bioscd.c index 93de958..a6dba0c 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 9d31325..a54418f 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 ccae099..4ac5cb5 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); |