| /* |
| * This file is subject to the terms and conditions of the GNU General Public |
| * License. See the file "COPYING" in the main directory of this archive |
| * for more details. |
| * |
| * Copyright (C) Hewlett-Packard (Paul Bame) paul_bame@hp.com |
| */ |
| #include "bootloader.h" |
| |
| /* A bunch of little routines to make reading the boot device easier. |
| * Caller can seekread into unaligned buffers |
| * Caller can seekread arbitrary amounts of data |
| * Caller can sort-of pretend they have a random-access device, |
| * as long as they only "seek" forwards. |
| * Caller need not keep track of where they are as much as before. |
| * Caller doesn't need to handle error returns -- there aren't any |
| * because a good error recovery isn't designed in, nor |
| * does bootdev_sread() check/recover in some cases I think. |
| * |
| * -Paul Bame |
| */ |
| |
| /* this struct should be considered private except to b2b.c */ |
| struct bs |
| { |
| short fd; /* fd of underlying device */ |
| |
| /* block size of underlying device */ |
| int blocksize; |
| /* alignment for reads from device */ |
| int align; |
| |
| /* data in 'bd_readbuf' was seekread from boot device offset 'bd_bufoffset' */ |
| int devoffset; |
| |
| /* offset of next byte to be seekread from device */ |
| int devnextptr; |
| |
| /* pointer to the properly-aligned 'blocksize' buffer */ |
| char *readbuf; |
| }; |
| |
| static struct bs *blockio = 0; |
| |
| static int |
| delivercache(struct bs *b, char **buf, unsigned *devaddr, unsigned endaddr) |
| { |
| unsigned block = *devaddr & ~(b->blocksize - 1); |
| int offset = *devaddr - block; |
| int n = 0; |
| |
| if (block == b->devoffset) /* anything useful in the cache? */ |
| { |
| n = b->blocksize - offset; /* we could deliver this many bytes */ |
| if (n > endaddr - *devaddr) /* user might want fewer bytes */ |
| n = endaddr - *devaddr; |
| |
| /* copy into user's buffer */ |
| memcpy(*buf, b->readbuf + offset, n); |
| if (Debug) printf("delivercache: %d bytes from devaddr 0x%x to 0x%p\r\n", |
| n, *devaddr, *buf); |
| |
| *devaddr += n; |
| *buf += n; |
| } |
| |
| return n; |
| } |
| |
| static int |
| cacheblock(struct bs *b, unsigned devaddr) |
| { |
| int n; |
| unsigned block = devaddr & ~(b->blocksize - 1); |
| |
| if ((n = seekread(b->fd, b->readbuf, b->blocksize, block)) != b->blocksize) |
| { |
| printf("byteio_read: seekread() returned %d expected %d\n", |
| n, b->blocksize); |
| |
| return 0; |
| } |
| b->devoffset = block; |
| if (Debug) printf("cacheblock: cached block at devaddr 0x%x\r\n", devaddr); |
| return 1; |
| } |
| |
| static int byteio_read(int fd, char *buf, unsigned count, unsigned devaddr) |
| { |
| struct bs *b = &blockio[fd]; |
| unsigned endaddr = devaddr + count; |
| unsigned remember = devaddr; |
| |
| if (Debug) printf("byteio_read(fd:%d, buf:%p, count:%u, devaddr:0x%x)\r\n", |
| fd, buf, count, devaddr); |
| |
| while (devaddr < endaddr) |
| { |
| int n, bigread; |
| char *alignedbuf; |
| |
| if (Debug) printf("b_r: buf:0x%p devaddr 0x%x %s endaddr 0x%x\r\n", |
| buf, devaddr, |
| (devaddr & (b->blocksize - 1)) == 0 ? "aligned" : "unaligned", |
| endaddr); |
| |
| if (delivercache(b, &buf, &devaddr, endaddr) > 0) |
| continue; |
| |
| /* if not block aligned, use the cache */ |
| if ((devaddr & (b->blocksize - 1)) != 0) |
| { |
| if (!cacheblock(b, devaddr)) |
| return -1; |
| continue; |
| } |
| |
| /* devaddr is aligned. If < 2 blocks remain, just use the cache */ |
| if (endaddr - devaddr < b->blocksize * 2) |
| { |
| if (!cacheblock(b, devaddr)) |
| return -1; |
| continue; |
| } |
| |
| /* devaddr is aligned and there are >= 2 blocks remaining */ |
| bigread = endaddr - devaddr; |
| |
| /* user's buf is probably not aligned */ |
| alignedbuf = (char *)(((unsigned int) buf + (b->align - 1)) & |
| ~(b->align - 1)); |
| |
| /* alignment loss */ |
| bigread -= alignedbuf - buf; |
| |
| /* must be a multiple of blocksize */ |
| bigread &= ~(b->blocksize - 1); |
| |
| if (Debug) printf("reading %u bytes from dev %x to ubuf %p\r\n", |
| bigread, devaddr, alignedbuf); |
| /* read the blocks into user's buf */ |
| if ((n = seekread(b->fd, alignedbuf, bigread, devaddr)) |
| != bigread) |
| { |
| printf("byteio_read: seekread() returned %d expected %d\n", |
| n, bigread); |
| |
| return -1; |
| } |
| |
| if (buf != alignedbuf) |
| { |
| /* move user's data to the right place in their buffer */ |
| if (Debug) printf("moving %u bytes from %p to %p\r\n", n, alignedbuf, buf); |
| memcpy(buf, alignedbuf, n); |
| } |
| devaddr += n; |
| buf += n; |
| } |
| |
| return devaddr - remember; |
| } |
| |
| static void byteio_describe(int fd, int *bufalign, |
| int *blocksize) |
| { |
| if (bufalign != 0) |
| *bufalign = 1; |
| |
| if (blocksize != 0) |
| *blocksize = 1; |
| } |
| |
| /* returns true if OK */ |
| int byteio_open(int otherfd) |
| { |
| int fd = fileio_open(byteio_describe, byteio_read); |
| |
| if (blockio == 0) |
| { |
| int sz = sizeof blockio[0] * MAX_FD; |
| blockio = (struct bs *)malloc(sz); |
| } |
| |
| if (fd >= 0) |
| { |
| struct bs *b = &blockio[fd]; |
| |
| memset(b, 0, sizeof *b); |
| b->fd = otherfd; |
| describe(otherfd, &b->align, &b->blocksize); |
| |
| b->readbuf = malloc_aligned(b->blocksize, b->align); |
| |
| if (!cacheblock(b, 0)) |
| die("initial read failed"); |
| } |
| |
| return fd; |
| } |
| |
| /* $Id: byteio.c,v 1.1 2000/02/29 17:41:28 bame Exp $ */ |