Changeset View
Standalone View
stand/i386/libi386/biosdisk.c
Show First 20 Lines • Show All 254 Lines • ▼ Show 20 Lines | if (!V86_CY(v86.efl)) { | ||||
* Sector size must be a multiple of 512 bytes. | * Sector size must be a multiple of 512 bytes. | ||||
* An alternate test would be to check power of 2, | * An alternate test would be to check power of 2, | ||||
* powerof2(params.sector_size). | * powerof2(params.sector_size). | ||||
*/ | */ | ||||
if (params.sector_size % BIOSDISK_SECSIZE) | if (params.sector_size % BIOSDISK_SECSIZE) | ||||
bd->bd_sectorsize = BIOSDISK_SECSIZE; | bd->bd_sectorsize = BIOSDISK_SECSIZE; | ||||
else | else | ||||
bd->bd_sectorsize = params.sector_size; | bd->bd_sectorsize = params.sector_size; | ||||
total = bd->bd_sectorsize * params.sectors; | total = bd->bd_sectorsize * params.sectors; | ||||
imp: speaking of which...
We are getting pretty big with all the crazy new features. Is there a… | |||||
Done Inline Actionsnot really. The fundamental issue (and also way out in a sense) is the memory layout. The BTX is at 0x9000, loader is starting at 0xa000 - thats code + data + bss. We get the memory size from 0x413 (2 bytes, in pages), and thats basically our initial stack pointer. Since the BDA is variable, we have no way to know ahead how much space we have for sure. However, our stack does not have to be in low memory, we could set it just below the heap too - but that means we wont provide stack variables for BIOS calls. Not too hard to clean up. tsoome: not really.
The fundamental issue (and also way out in a sense) is the memory layout.
The BTX… | |||||
if (params.sectors != 0) { | if (params.sectors != 0) { | ||||
/* Only update if we did not overflow. */ | /* Only update if we did not overflow. */ | ||||
if (total > params.sectors) | if (total > params.sectors) | ||||
bd->bd_sectors = params.sectors; | bd->bd_sectors = params.sectors; | ||||
} | } | ||||
total = (uint64_t)params.cylinders * | total = (uint64_t)params.cylinders * | ||||
params.heads * params.sectors_per_track; | params.heads * params.sectors_per_track; | ||||
▲ Show 20 Lines • Show All 170 Lines • ▼ Show 20 Lines | return (bcache_strategy(&bcd, rw, dblk + dev->d_offset, size, | ||||
buf, rsize)); | buf, rsize)); | ||||
} | } | ||||
static int | static int | ||||
bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, | bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, | ||||
char *buf, size_t *rsize) | char *buf, size_t *rsize) | ||||
{ | { | ||||
struct disk_devdesc *dev = (struct disk_devdesc *)devdata; | struct disk_devdesc *dev = (struct disk_devdesc *)devdata; | ||||
uint64_t disk_blocks; | uint64_t disk_blocks, offset; | ||||
int blks, rc; | size_t blks, blkoff, bsize, bio_size, rest; | ||||
caddr_t bbuf; | |||||
int rc; | |||||
if (size % BD(dev).bd_sectorsize) { | /* | ||||
panic("bd_strategy: %d bytes I/O not multiple of block size", | * First make sure the IO size is a multiple of 512 bytes. While we do | ||||
size); | * process partial reads below, the strategy mechanism is built | ||||
* assuming IO is a multiple of 512B blocks. If the request is not | |||||
* a multiple of 512B blocks, it has to be some sort of bug. | |||||
*/ | |||||
if (size == 0 || (size % BIOSDISK_SECSIZE) != 0) { | |||||
printf("bd_strategy: %d bytes I/O not multiple of %d\n", | |||||
size, BIOSDISK_SECSIZE); | |||||
return (EIO); | |||||
} | } | ||||
DEBUG("open_disk %p", dev); | DEBUG("open_disk %p", dev); | ||||
offset = dblk * BIOSDISK_SECSIZE; | |||||
dblk = offset / BD(dev).bd_sectorsize; | |||||
blkoff = offset % BD(dev).bd_sectorsize; | |||||
/* | /* | ||||
* Check the value of the size argument. We do have quite small | * Check the value of the size argument. We do have quite small | ||||
* heap (64MB), but we do not know good upper limit, so we check against | * heap (64MB), but we do not know good upper limit, so we check against | ||||
* INT_MAX here. This will also protect us against possible overflows | * INT_MAX here. This will also protect us against possible overflows | ||||
* while translating block count to bytes. | * while translating block count to bytes. | ||||
*/ | */ | ||||
if (size > INT_MAX) { | if (size > INT_MAX) { | ||||
DEBUG("too large read: %zu bytes", size); | DEBUG("too large I/O: %zu bytes", size); | ||||
return (EIO); | return (EIO); | ||||
} | } | ||||
blks = size / BD(dev).bd_sectorsize; | blks = size / BD(dev).bd_sectorsize; | ||||
if (blks == 0 || (size % BD(dev).bd_sectorsize) != 0) | |||||
blks++; | |||||
if (dblk > dblk + blks) | if (dblk > dblk + blks) | ||||
return (EIO); | return (EIO); | ||||
if (rsize) | if (rsize) | ||||
*rsize = 0; | *rsize = 0; | ||||
/* | /* | ||||
* Get disk blocks, this value is either for whole disk or for | * Get disk blocks, this value is either for whole disk or for | ||||
Show All 12 Lines | if (dblk < dev->d_offset || dblk >= dev->d_offset + disk_blocks) | ||||
return (EIO); | return (EIO); | ||||
/* | /* | ||||
* Truncate if we are crossing disk or partition end. | * Truncate if we are crossing disk or partition end. | ||||
*/ | */ | ||||
if (dblk + blks >= dev->d_offset + disk_blocks) { | if (dblk + blks >= dev->d_offset + disk_blocks) { | ||||
blks = dev->d_offset + disk_blocks - dblk; | blks = dev->d_offset + disk_blocks - dblk; | ||||
size = blks * BD(dev).bd_sectorsize; | size = blks * BD(dev).bd_sectorsize; | ||||
DEBUG("short read %d", blks); | DEBUG("short I/O %d", blks); | ||||
} | } | ||||
bio_size = min(BIO_BUFFER_SIZE, size); | |||||
while (bio_size > BD(dev).bd_sectorsize) { | |||||
bbuf = bio_alloc(bio_size); | |||||
if (bbuf != NULL) | |||||
break; | |||||
bio_size -= BD(dev).bd_sectorsize; | |||||
} | |||||
if (bbuf == NULL) { | |||||
bio_size = V86_IO_BUFFER_SIZE; | |||||
if (bio_size / BD(dev).bd_sectorsize == 0) | |||||
panic("BUG: Real mode buffer is too small\n"); | |||||
/* Use alternate 4k buffer */ | |||||
Not Done Inline Actionsyou said 16k above? Is that still right or ??? imp: you said 16k above? Is that still right or ??? | |||||
Done Inline ActionsThats the 1 page from current setup. There is one page in between BTX data areas we are using. if somehow we end up the "bio" area exhausted, this is our fallback. I'd expect it never happen, but ... tsoome: Thats the 1 page from current setup. There is one page in between BTX data areas we are using. | |||||
bbuf = PTOV(V86_IO_BUFFER); | |||||
} | |||||
rest = size; | |||||
rc = 0; | |||||
while (blks > 0) { | |||||
int x = min(blks, bio_size / BD(dev).bd_sectorsize); | |||||
switch (rw & F_MASK) { | switch (rw & F_MASK) { | ||||
case F_READ: | case F_READ: | ||||
DEBUG("read %d from %lld to %p", blks, dblk, buf); | DEBUG("read %d from %lld to %p", x, dblk, buf); | ||||
bsize = BD(dev).bd_sectorsize * x - blkoff; | |||||
if (rest < bsize) | |||||
bsize = rest; | |||||
if (blks && (rc = bd_io(dev, dblk, blks, buf, BD_RD))) { | if ((rc = bd_io(dev, dblk, x, bbuf, BD_RD)) != 0) { | ||||
/* Filter out floppy controller errors */ | rc = EIO; | ||||
if (BD(dev).bd_flags != BD_FLOPPY || rc != 0x20) { | goto error; | ||||
printf("read %d from %lld to %p, error: 0x%x\n", | |||||
blks, dblk, buf, rc); | |||||
} | } | ||||
return (EIO); | |||||
} | bcopy(bbuf + blkoff, buf, bsize); | ||||
break; | break; | ||||
case F_WRITE : | case F_WRITE : | ||||
DEBUG("write %d from %lld to %p", blks, dblk, buf); | DEBUG("write %d from %lld to %p", x, dblk, buf); | ||||
if (blkoff != 0) { | |||||
if (blks && bd_io(dev, dblk, blks, buf, BD_WR)) { | /* | ||||
DEBUG("write error"); | * We got offset to sector, read 1 sector to | ||||
return (EIO); | * bbuf. | ||||
*/ | |||||
x = 1; | |||||
bsize = BD(dev).bd_sectorsize - blkoff; | |||||
bsize = min(bsize, rest); | |||||
rc = bd_io(dev, dblk, x, bbuf, BD_RD); | |||||
} else if (rest < BD(dev).bd_sectorsize) { | |||||
/* | |||||
* The remaining block is not full | |||||
* sector. Read 1 sector to bbuf. | |||||
*/ | |||||
x = 1; | |||||
bsize = rest; | |||||
rc = bd_io(dev, dblk, x, bbuf, BD_RD); | |||||
} else { | |||||
/* We can write full sector(s). */ | |||||
bsize = BD(dev).bd_sectorsize * x; | |||||
} | } | ||||
/* | |||||
* Put your Data In, Put your Data out, | |||||
* Put your Data In, and shake it all about | |||||
*/ | |||||
bcopy(buf, bbuf + blkoff, bsize); | |||||
if ((rc = bd_io(dev, dblk, x, bbuf, BD_WR)) != 0) { | |||||
rc = EIO; | |||||
goto error; | |||||
} | |||||
break; | break; | ||||
default: | default: | ||||
/* DO NOTHING */ | /* DO NOTHING */ | ||||
return (EROFS); | rc = EROFS; | ||||
goto error; | |||||
} | } | ||||
if (rsize) | blkoff = 0; | ||||
buf += bsize; | |||||
rest -= bsize; | |||||
blks -= x; | |||||
dblk += x; | |||||
} | |||||
if (rsize != NULL) | |||||
*rsize = size; | *rsize = size; | ||||
return (0); | error: | ||||
if (bbuf != PTOV(V86_IO_BUFFER)) | |||||
bio_free(bbuf, bio_size); | |||||
return (rc); | |||||
} | } | ||||
static int | static int | ||||
bd_edd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, | bd_edd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, | ||||
int dowrite) | int dowrite) | ||||
{ | { | ||||
static struct edd_packet packet; | static struct edd_packet packet; | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | |||||
static void | static void | ||||
bd_io_workaround(struct disk_devdesc *dev) | bd_io_workaround(struct disk_devdesc *dev) | ||||
{ | { | ||||
uint8_t buf[8 * 1024]; | uint8_t buf[8 * 1024]; | ||||
bd_edd_io(dev, 0xffffffff, 1, (caddr_t)buf, BD_RD); | bd_edd_io(dev, 0xffffffff, 1, (caddr_t)buf, BD_RD); | ||||
} | } | ||||
static int | static int | ||||
bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, | bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, | ||||
int dowrite) | int dowrite) | ||||
{ | { | ||||
u_int x, sec, result, resid, retry, maxfer; | int result, retry; | ||||
caddr_t p, xp, bbuf; | |||||
/* Just in case some idiot actually tries to read/write -1 blocks... */ | /* Just in case some idiot actually tries to read/write -1 blocks... */ | ||||
if (blks < 0) | if (blks < 0) | ||||
return (-1); | return (-1); | ||||
resid = blks; | |||||
p = dest; | |||||
/* | /* | ||||
* Workaround for a problem with some HP ProLiant BIOS failing to work | * Workaround for a problem with some HP ProLiant BIOS failing to work | ||||
* out the boot disk after installation. hrs and kuriyama discovered | * out the boot disk after installation. hrs and kuriyama discovered | ||||
* this problem with an HP ProLiant DL320e Gen 8 with a 3TB HDD, and | * this problem with an HP ProLiant DL320e Gen 8 with a 3TB HDD, and | ||||
* discovered that an int13h call seems to cause a buffer overrun in | * discovered that an int13h call seems to cause a buffer overrun in | ||||
* the bios. The problem is alleviated by doing an extra read before | * the bios. The problem is alleviated by doing an extra read before | ||||
* the buggy read. It is not immediately known whether other models | * the buggy read. It is not immediately known whether other models | ||||
* are similarly affected. | * are similarly affected. | ||||
*/ | |||||
if (dowrite == BD_RD && dblk >= 0x100000000) | |||||
bd_io_workaround(dev); | |||||
/* Decide whether we have to bounce */ | |||||
if (VTOP(dest) >> 20 != 0 || (BD(dev).bd_unit < 0x80 && | |||||
(VTOP(dest) >> 16) != | |||||
(VTOP(dest + blks * BD(dev).bd_sectorsize) >> 16))) { | |||||
/* | |||||
* There is a 64k physical boundary somewhere in the | |||||
* destination buffer, or the destination buffer is above | |||||
* first 1MB of physical memory so we have to arrange a | |||||
* suitable bounce buffer. Allocate a buffer twice as large | |||||
* as we need to. Use the bottom half unless there is a break | |||||
* there, in which case we use the top half. | |||||
*/ | |||||
x = V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize; | |||||
x = min(x, (unsigned)blks); | |||||
bbuf = PTOV(V86_IO_BUFFER); | |||||
maxfer = x; /* limit transfers to bounce region size */ | |||||
} else { | |||||
bbuf = NULL; | |||||
maxfer = 0; | |||||
} | |||||
while (resid > 0) { | |||||
/* | |||||
* Play it safe and don't cross track boundaries. | |||||
* (XXX this is probably unnecessary) | |||||
*/ | |||||
sec = dblk % BD(dev).bd_sec; /* offset into track */ | |||||
x = min(BD(dev).bd_sec - sec, resid); | |||||
if (maxfer > 0) | |||||
x = min(x, maxfer); /* fit bounce buffer */ | |||||
/* where do we transfer to? */ | |||||
xp = bbuf == NULL ? p : bbuf; | |||||
/* | |||||
* Put your Data In, Put your Data out, | |||||
* Put your Data In, and shake it all about | |||||
*/ | |||||
if (dowrite == BD_WR && bbuf != NULL) | |||||
bcopy(p, bbuf, x * BD(dev).bd_sectorsize); | |||||
/* | |||||
* Loop retrying the operation a couple of times. The BIOS | * Loop retrying the operation a couple of times. The BIOS | ||||
* may also retry. | * may also retry. | ||||
*/ | */ | ||||
if (dowrite == BD_RD && dblk >= 0x100000000) | |||||
bd_io_workaround(dev); | |||||
for (retry = 0; retry < 3; retry++) { | for (retry = 0; retry < 3; retry++) { | ||||
/* if retrying, reset the drive */ | /* if retrying, reset the drive */ | ||||
if (retry > 0) { | if (retry > 0) { | ||||
v86.ctl = V86_FLAGS; | v86.ctl = V86_FLAGS; | ||||
v86.addr = 0x13; | v86.addr = 0x13; | ||||
v86.eax = 0; | v86.eax = 0; | ||||
v86.edx = BD(dev).bd_unit; | v86.edx = BD(dev).bd_unit; | ||||
v86int(); | v86int(); | ||||
} | } | ||||
if (BD(dev).bd_flags & BD_MODEEDD1) | if (BD(dev).bd_flags & BD_MODEEDD1) | ||||
result = bd_edd_io(dev, dblk, x, xp, dowrite); | result = bd_edd_io(dev, dblk, blks, dest, dowrite); | ||||
else | else | ||||
result = bd_chs_io(dev, dblk, x, xp, dowrite); | result = bd_chs_io(dev, dblk, blks, dest, dowrite); | ||||
if (result == 0) | if (result == 0) | ||||
break; | break; | ||||
} | } | ||||
if (dowrite == BD_WR) | /* | ||||
DEBUG("Write %d sector(s) from %p (0x%x) to %lld %s", x, | * 0x20 - Controller failure. This is common error when the | ||||
p, VTOP(p), dblk, result ? "failed" : "ok"); | * media is not present. | ||||
else | */ | ||||
DEBUG("Read %d sector(s) from %lld to %p (0x%x) %s", x, | if (result != 0 && result != 0x20) { | ||||
dblk, p, VTOP(p), result ? "failed" : "ok"); | if (dowrite == BD_WR) { | ||||
if (result) { | printf("%s%d: Write %d sector(s) from %p (0x%x) " | ||||
return (result); | "to %lld: 0x%x\n", dev->dd.d_dev->dv_name, | ||||
dev->dd.d_unit, blks, dest, VTOP(dest), dblk, | |||||
result); | |||||
} else { | |||||
printf("%s%d: Read %d sector(s) from %lld to %p " | |||||
"(0x%x): 0x%x\n", dev->dd.d_dev->dv_name, | |||||
dev->dd.d_unit, blks, dblk, dest, VTOP(dest), | |||||
result); | |||||
} | } | ||||
if (dowrite == BD_RD && bbuf != NULL) | |||||
bcopy(bbuf, p, x * BD(dev).bd_sectorsize); | |||||
p += (x * BD(dev).bd_sectorsize); | |||||
dblk += x; | |||||
resid -= x; | |||||
} | } | ||||
return (0); | return (result); | ||||
} | } | ||||
/* | /* | ||||
* Return the BIOS geometry of a given "fixed drive" in a format | * Return the BIOS geometry of a given "fixed drive" in a format | ||||
* suitable for the legacy bootinfo structure. Since the kernel is | * suitable for the legacy bootinfo structure. Since the kernel is | ||||
* expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we | * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we | ||||
* prefer to get the information directly, rather than rely on being | * prefer to get the information directly, rather than rely on being | ||||
* able to put it together from information already maintained for | * able to put it together from information already maintained for | ||||
▲ Show 20 Lines • Show All 78 Lines • Show Last 20 Lines |
speaking of which...
We are getting pretty big with all the crazy new features. Is there a reliable way to tell the largest bootable image since it must all fit <= 640k? I run into trouble in the mid 500's...