mirror of
https://github.com/freebsd/freebsd-src
synced 2024-09-06 17:18:32 +00:00
loader: biosdisk interface should be able to cope with 4k sectors
The 4kn support in current bios specific biosdisk.c is broken, as the code is only implementing the support for the 512B sector size. This work is building the support for custom size sectors, we still do assume the requested data to be multiple of 512B blocks and we only do address the biosdisk.c interface here. For reference, see also: https://www.illumos.org/issues/8303 https://www.illumos.org/rb/r/547 As the GELI is moved above biosdisk "layer", the GELI should just work Reviewed by: imp Differential Revision: https://reviews.freebsd.org/D11174
This commit is contained in:
parent
8c14a4095d
commit
8b7db46557
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=339658
|
@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
|
|||
#include <stand.h>
|
||||
#include <machine/bootinfo.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <bootstrap.h>
|
||||
#include <btxv86.h>
|
||||
|
@ -255,10 +256,11 @@ bd_int13probe(struct bdinfo *bd)
|
|||
* Sector size must be a multiple of 512 bytes.
|
||||
* An alternate test would be to check power of 2,
|
||||
* powerof2(params.sector_size).
|
||||
* 4K is largest read buffer we can use at this time.
|
||||
*/
|
||||
if (params.sector_size % BIOSDISK_SECSIZE)
|
||||
bd->bd_sectorsize = BIOSDISK_SECSIZE;
|
||||
else
|
||||
if (params.sector_size >= 512 &&
|
||||
params.sector_size <= 4096 &&
|
||||
(params.sector_size % BIOSDISK_SECSIZE) == 0)
|
||||
bd->bd_sectorsize = params.sector_size;
|
||||
|
||||
total = bd->bd_sectorsize * params.sectors;
|
||||
|
@ -448,16 +450,29 @@ bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
|
|||
char *buf, size_t *rsize)
|
||||
{
|
||||
struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
|
||||
uint64_t disk_blocks;
|
||||
int blks, rc;
|
||||
uint64_t disk_blocks, offset;
|
||||
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",
|
||||
size);
|
||||
/*
|
||||
* First make sure the IO size is a multiple of 512 bytes. While we do
|
||||
* 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);
|
||||
|
||||
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
|
||||
* heap (64MB), but we do not know good upper limit, so we check against
|
||||
|
@ -465,11 +480,14 @@ bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
|
|||
* while translating block count to bytes.
|
||||
*/
|
||||
if (size > INT_MAX) {
|
||||
DEBUG("too large read: %zu bytes", size);
|
||||
DEBUG("too large I/O: %zu bytes", size);
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
blks = size / BD(dev).bd_sectorsize;
|
||||
if (blks == 0 || (size % BD(dev).bd_sectorsize) != 0)
|
||||
blks++;
|
||||
|
||||
if (dblk > dblk + blks)
|
||||
return (EIO);
|
||||
|
||||
|
@ -498,36 +516,75 @@ bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
|
|||
if (dblk + blks >= dev->d_offset + disk_blocks) {
|
||||
blks = dev->d_offset + disk_blocks - dblk;
|
||||
size = blks * BD(dev).bd_sectorsize;
|
||||
DEBUG("short read %d", blks);
|
||||
DEBUG("short I/O %d", blks);
|
||||
}
|
||||
|
||||
switch (rw & F_MASK) {
|
||||
case F_READ:
|
||||
DEBUG("read %d from %lld to %p", blks, dblk, buf);
|
||||
if (V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize == 0)
|
||||
panic("BUG: Real mode buffer is too small\n");
|
||||
|
||||
if (blks && (rc = bd_io(dev, dblk, blks, buf, BD_RD))) {
|
||||
/* 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);
|
||||
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) {
|
||||
case F_READ:
|
||||
DEBUG("read %d from %lld to %p", x, dblk, buf);
|
||||
bsize = BD(dev).bd_sectorsize * x - blkoff;
|
||||
if (rest < bsize)
|
||||
bsize = rest;
|
||||
|
||||
if ((rc = bd_io(dev, dblk, x, bbuf, BD_RD)) != 0)
|
||||
return (EIO);
|
||||
|
||||
bcopy(bbuf + blkoff, buf, bsize);
|
||||
break;
|
||||
case F_WRITE :
|
||||
DEBUG("write %d from %lld to %p", x, dblk, buf);
|
||||
if (blkoff != 0) {
|
||||
/*
|
||||
* We got offset to sector, read 1 sector to
|
||||
* 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;
|
||||
}
|
||||
return (EIO);
|
||||
}
|
||||
break;
|
||||
case F_WRITE :
|
||||
DEBUG("write %d from %lld to %p", blks, dblk, buf);
|
||||
/*
|
||||
* 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);
|
||||
|
||||
if (blks && bd_io(dev, dblk, blks, buf, BD_WR)) {
|
||||
DEBUG("write error");
|
||||
return (EIO);
|
||||
break;
|
||||
default:
|
||||
/* DO NOTHING */
|
||||
return (EROFS);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* DO NOTHING */
|
||||
return (EROFS);
|
||||
|
||||
blkoff = 0;
|
||||
buf += bsize;
|
||||
rest -= bsize;
|
||||
blks -= x;
|
||||
dblk += x;
|
||||
}
|
||||
|
||||
if (rsize)
|
||||
if (rsize != NULL)
|
||||
*rsize = size;
|
||||
return (0);
|
||||
}
|
||||
|
@ -604,21 +661,16 @@ bd_io_workaround(struct disk_devdesc *dev)
|
|||
bd_edd_io(dev, 0xffffffff, 1, (caddr_t)buf, BD_RD);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
|
||||
int dowrite)
|
||||
{
|
||||
u_int x, sec, result, resid, retry, maxfer;
|
||||
caddr_t p, xp, bbuf;
|
||||
|
||||
int result, retry;
|
||||
|
||||
/* Just in case some idiot actually tries to read/write -1 blocks... */
|
||||
if (blks < 0)
|
||||
return (-1);
|
||||
|
||||
resid = blks;
|
||||
p = dest;
|
||||
|
||||
/*
|
||||
* Workaround for a problem with some HP ProLiant BIOS failing to work
|
||||
* out the boot disk after installation. hrs and kuriyama discovered
|
||||
|
@ -627,91 +679,49 @@ bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
|
|||
* the bios. The problem is alleviated by doing an extra read before
|
||||
* the buggy read. It is not immediately known whether other models
|
||||
* are similarly affected.
|
||||
* Loop retrying the operation a couple of times. The BIOS
|
||||
* may also retry.
|
||||
*/
|
||||
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
|
||||
* 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 = BD(dev).bd_unit;
|
||||
v86int();
|
||||
}
|
||||
|
||||
if (BD(dev).bd_flags & BD_MODEEDD1)
|
||||
result = bd_edd_io(dev, dblk, x, xp, dowrite);
|
||||
else
|
||||
result = bd_chs_io(dev, dblk, x, xp, dowrite);
|
||||
if (result == 0)
|
||||
break;
|
||||
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 = BD(dev).bd_unit;
|
||||
v86int();
|
||||
}
|
||||
|
||||
if (dowrite == BD_WR)
|
||||
DEBUG("Write %d sector(s) from %p (0x%x) to %lld %s", x,
|
||||
p, VTOP(p), dblk, result ? "failed" : "ok");
|
||||
if (BD(dev).bd_flags & BD_MODEEDD1)
|
||||
result = bd_edd_io(dev, dblk, blks, dest, dowrite);
|
||||
else
|
||||
DEBUG("Read %d sector(s) from %lld to %p (0x%x) %s", x,
|
||||
dblk, p, VTOP(p), result ? "failed" : "ok");
|
||||
if (result) {
|
||||
return (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;
|
||||
result = bd_chs_io(dev, dblk, blks, dest, dowrite);
|
||||
|
||||
if (result == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return (0);
|
||||
/*
|
||||
* 0x20 - Controller failure. This is common error when the
|
||||
* media is not present.
|
||||
*/
|
||||
if (result != 0 && result != 0x20) {
|
||||
if (dowrite == BD_WR) {
|
||||
printf("%s%d: Write %d sector(s) from %p (0x%x) "
|
||||
"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);
|
||||
}
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in a new issue