Changeset View
Changeset View
Standalone View
Standalone View
head/stand/i386/libi386/biosdisk.c
Show All 37 Lines | |||||
* | * | ||||
*/ | */ | ||||
#include <sys/disk.h> | #include <sys/disk.h> | ||||
#include <sys/limits.h> | #include <sys/limits.h> | ||||
#include <stand.h> | #include <stand.h> | ||||
#include <machine/bootinfo.h> | #include <machine/bootinfo.h> | ||||
#include <stdarg.h> | #include <stdarg.h> | ||||
#include <stdbool.h> | |||||
#include <bootstrap.h> | #include <bootstrap.h> | ||||
#include <btxv86.h> | #include <btxv86.h> | ||||
#include <edd.h> | #include <edd.h> | ||||
#include "disk.h" | #include "disk.h" | ||||
#include "libi386.h" | #include "libi386.h" | ||||
#define BIOS_NUMDRIVES 0x475 | #define BIOS_NUMDRIVES 0x475 | ||||
▲ Show 20 Lines • Show All 196 Lines • ▼ Show 20 Lines | bd_int13probe(struct bdinfo *bd) | ||||
v86int(); | v86int(); | ||||
if (!V86_CY(v86.efl)) { | if (!V86_CY(v86.efl)) { | ||||
uint64_t total; | uint64_t total; | ||||
/* | /* | ||||
* 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). | ||||
* 4K is largest read buffer we can use at this time. | |||||
*/ | */ | ||||
if (params.sector_size % BIOSDISK_SECSIZE) | if (params.sector_size >= 512 && | ||||
bd->bd_sectorsize = BIOSDISK_SECSIZE; | params.sector_size <= 4096 && | ||||
else | (params.sector_size % BIOSDISK_SECSIZE) == 0) | ||||
bd->bd_sectorsize = params.sector_size; | bd->bd_sectorsize = params.sector_size; | ||||
total = bd->bd_sectorsize * params.sectors; | total = bd->bd_sectorsize * params.sectors; | ||||
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; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 173 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, 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); | ||||
} | } | ||||
if (V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize == 0) | |||||
panic("BUG: Real mode buffer is too small\n"); | |||||
bbuf = PTOV(V86_IO_BUFFER); | |||||
rest = size; | |||||
while (blks > 0) { | |||||
int x = min(blks, V86_IO_BUFFER_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 */ | |||||
if (BD(dev).bd_flags != BD_FLOPPY || rc != 0x20) { | |||||
printf("read %d from %lld to %p, error: 0x%x\n", | |||||
blks, dblk, buf, rc); | |||||
} | |||||
return (EIO); | 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) | |||||
return (EIO); | |||||
break; | break; | ||||
default: | default: | ||||
/* DO NOTHING */ | /* DO NOTHING */ | ||||
return (EROFS); | return (EROFS); | ||||
} | } | ||||
if (rsize) | blkoff = 0; | ||||
buf += bsize; | |||||
rest -= bsize; | |||||
blks -= x; | |||||
dblk += x; | |||||
} | |||||
if (rsize != NULL) | |||||
*rsize = size; | *rsize = size; | ||||
return (0); | return (0); | ||||
} | } | ||||
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) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 60 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 |