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
This commit is contained in:
John Baldwin 2009-03-12 20:41:52 +00:00
parent 834772014b
commit 47193e464b
3 changed files with 86 additions and 52 deletions

View File

@ -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);
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) {
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0;
v86.edx = biosdev;
v86int();
}
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;
/* 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);
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 = 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);

View File

@ -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)
{

View File

@ -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);