From e7b870af70cc8cd3e20b385be3643e7206f99a85 Mon Sep 17 00:00:00 2001 From: Will Sowerbutts Date: Sun, 25 Jan 2015 21:55:30 +0000 Subject: [PATCH] blkdev: Support rawflag=1, pass parameters using global variables rather than function call parameters. --- Kernel/dev/blkdev.c | 70 +++++++++++++++++++++++++++++---------------- Kernel/dev/blkdev.h | 39 ++++++++++++++++++------- Kernel/dev/mbr.c | 28 +++++++++++------- Kernel/dev/mbr.h | 2 +- 4 files changed, 92 insertions(+), 47 deletions(-) diff --git a/Kernel/dev/blkdev.c b/Kernel/dev/blkdev.c index 514bf644..1475736a 100644 --- a/Kernel/dev/blkdev.c +++ b/Kernel/dev/blkdev.c @@ -51,6 +51,7 @@ */ static blkdev_t blkdev_table[MAX_BLKDEV]; +struct blkparam blk_op; int blkdev_open(uint8_t minor, uint16_t flags) { @@ -71,38 +72,56 @@ int blkdev_open(uint8_t minor, uint16_t flags) return -1; } -static int blkdev_transfer(uint8_t minor, bool is_read, uint8_t rawflag) +static int blkdev_transfer(uint8_t minor, uint8_t rawflag) { - void *target; - uint32_t lba; - uint8_t partition; - blkdev_t *blk; + uint8_t partition, n, count=0; /* we trust that blkdev_open() has already verified that this minor number is valid */ - blk = &blkdev_table[minor >> 4]; + blk_op.blkdev = &blkdev_table[minor >> 4]; partition = minor & 0x0F; - if(rawflag == 0) { - target = udata.u_buf->bf_data; - lba = udata.u_buf->bf_blk; - }else - goto xferfail; + switch(rawflag){ + case 0: + /* read single 512-byte sector to buffer in kernel memory */ + blk_op.nblock = 1; + blk_op.lba = udata.u_buf->bf_blk; + blk_op.addr = udata.u_buf->bf_data; + blk_op.is_user = false; + break; + case 1: + /* read some number of 512-byte sectors directly to user memory */ + blk_op.nblock = (udata.u_count >> BLKSHIFT); + if((udata.u_count | (uint16_t)udata.u_offset) & BLKMASK) + panic("blkdev: not integral"); + blk_op.lba = (udata.u_offset >> BLKSHIFT); + blk_op.addr = udata.u_base; + blk_op.is_user = true; + break; + default: + goto xferfail; + } if(partition == 0){ /* partition 0 is the whole disk and requires no translation */ - if(lba >= blk->drive_lba_count) + if(blk_op.lba >= blk_op.blkdev->drive_lba_count) goto xferfail; }else{ /* partitions 1+ require us to add in an offset */ - if(lba >= blk->lba_count[partition-1]) + if(blk_op.lba >= blk_op.blkdev->lba_count[partition-1]) goto xferfail; - lba += blk->lba_first[partition-1]; + blk_op.lba += blk_op.blkdev->lba_first[partition-1]; } - if(blk->transfer(blk->drive_number, lba, target, is_read)) - return 1; /* 10/10, would transfer sectors again */ + while(blk_op.nblock){ + n = blk_op.blkdev->transfer(); + if(n == 0) + goto xferfail; + blk_op.nblock -= n; + count += n; + } + return count; /* 10/10, would transfer sectors again */ xferfail: udata.u_error = EIO; return -1; @@ -111,28 +130,29 @@ xferfail: int blkdev_read(uint8_t minor, uint8_t rawflag, uint8_t flag) { flag; /* not used */ - return blkdev_transfer(minor, true, rawflag); + blk_op.is_read = true; + return blkdev_transfer(minor, rawflag); } int blkdev_write(uint8_t minor, uint8_t rawflag, uint8_t flag) { flag; /* not used */ - return blkdev_transfer(minor, false, rawflag); + blk_op.is_read = false; + return blkdev_transfer(minor, rawflag); } int blkdev_ioctl(uint8_t minor, uint16_t request, char *data) { - blkdev_t *blk; data; /* unused */ if (request != BLKFLSBUF) return -1; /* we trust that blkdev_open() has already verified that this minor number is valid */ - blk = &blkdev_table[minor >> 4]; + blk_op.blkdev = &blkdev_table[minor >> 4]; - if (blk->flush) - return blk->flush(blk->drive_number); + if (blk_op.blkdev->flush) + return blk_op.blkdev->flush(); else return 0; } @@ -159,9 +179,9 @@ void blkdev_scan(blkdev_t *blk, uint8_t flags) { uint8_t letter = 'a' + (blk - blkdev_table); - flags; + flags; /* not used */ - kprintf("hd%c: ", letter); - mbr_parse(blk, letter); + blk_op.blkdev = blk; + mbr_parse(letter); kputchar('\n'); } diff --git a/Kernel/dev/blkdev.h b/Kernel/dev/blkdev.h index 099ad3b7..c1b5f326 100644 --- a/Kernel/dev/blkdev.h +++ b/Kernel/dev/blkdev.h @@ -3,25 +3,42 @@ /* block device drives should call blkdev_add() for each block device found, and implement a sector transfer function matching the following prototype. */ -typedef bool (*transfer_function_t)(uint8_t drive, uint32_t lba, void *buffer, bool read_notwrite); -typedef int (*flush_function_t)(uint8_t drive); +typedef uint8_t (*transfer_function_t)(void); +typedef int (*flush_function_t)(void); /* the following details should be required only by partition parsing code */ -#define MAX_PARTITIONS 15 /* must be at least 4, at most 15 */ +#define MAX_PARTITIONS 15 /* must be at least 4, at most 15 */ typedef struct { - transfer_function_t transfer; /* function to read and write sectors */ - flush_function_t flush; /* flush device cache */ - uint32_t drive_lba_count; /* count of sectors on raw disk device */ - uint32_t lba_first[MAX_PARTITIONS]; /* LBA of first sector of each partition; 0 if partition absent */ - uint32_t lba_count[MAX_PARTITIONS]; /* count of sectors in each partition; 0 if partition absent */ - uint8_t drive_number; /* driver's drive number */ + uint8_t driver_data; /* opaque parameter used by underlying driver (should be first) */ + transfer_function_t transfer; /* function to read and write sectors */ + flush_function_t flush; /* flush device cache */ + uint32_t drive_lba_count; /* count of sectors on raw disk device */ + uint32_t lba_first[MAX_PARTITIONS]; /* LBA of first sector of each partition; 0 if partition absent */ + uint32_t lba_count[MAX_PARTITIONS]; /* count of sectors in each partition; 0 if partition absent */ } blkdev_t; -/* public interface */ +/* Holds the parameters for the current block operation. + * Block I/O being single threaded is deep in the design of UZI/FUZIX + * so let's make good use of every advantage we can from it. */ +struct blkparam { + /* do not change the order without adjusting BLKPARAM_*_OFFSET macros below */ + void *addr; /* address for transfer buffer */ + bool is_user; /* true: addr is in user memory, false: addr is in kernel memory */ + blkdev_t *blkdev; /* active block device */ + uint32_t lba; /* LBA for first sectors to transfer */ + uint8_t nblock; /* number of sectors to transfer */ + bool is_read; /* true: read sectors, false: write sectors */ +}; +/* macros that inline assembler code can use to access blkparam fields */ +#define BLKPARAM_ADDR_OFFSET 0 +#define BLKPARAM_IS_USER_OFFSET 2 + +extern struct blkparam blk_op; + /* public interface */ extern blkdev_t *blkdev_alloc(void); extern void blkdev_scan(blkdev_t *blk, uint8_t flags); -#define SWAPSCAN 0x01 +#define SWAPSCAN 0x01 extern int blkdev_open(uint8_t minor, uint16_t flags); extern int blkdev_read(uint8_t minor, uint8_t rawflag, uint8_t flag); extern int blkdev_write(uint8_t minor, uint8_t rawflag, uint8_t flag); diff --git a/Kernel/dev/mbr.c b/Kernel/dev/mbr.c index 2c7855c1..9f9ee208 100644 --- a/Kernel/dev/mbr.c +++ b/Kernel/dev/mbr.c @@ -22,18 +22,26 @@ typedef struct { uint16_t signature; } boot_record_t; -void mbr_parse(blkdev_t *blk, char letter) +void mbr_parse(char letter) { boot_record_t *br; uint8_t i, seen = 0; - uint32_t lba = 0, ep_offset = 0, br_offset = 0; + uint32_t ep_offset = 0, br_offset = 0; uint8_t next = 0; + kprintf("hd%c: ", letter); + /* allocate temporary memory */ br = (boot_record_t *)tmpbuf(); + blk_op.is_read = true; + blk_op.is_user = false; + blk_op.nblock = 1; + blk_op.addr = br; + blk_op.lba = 0; + do{ - if(!blk->transfer(blk->drive_number, lba, br, true) || le16_to_cpu(br->signature) != MBR_SIGNATURE) + if(!blk_op.blkdev->transfer() || le16_to_cpu(br->signature) != MBR_SIGNATURE) break; /* avoid an infinite loop where extended boot records form a loop */ @@ -42,13 +50,13 @@ void mbr_parse(blkdev_t *blk, char letter) if(seen == 1){ /* we just loaded the first extended boot record */ - ep_offset = lba; + ep_offset = blk_op.lba; next = 4; kputs("< "); } - br_offset = lba; - lba = 0; + br_offset = blk_op.lba; + blk_op.lba = 0; for(i=0; ipartition[i].type){ @@ -60,7 +68,7 @@ void mbr_parse(blkdev_t *blk, char letter) /* Extended boot record, or chained table; in principle a drive should contain at most one extended partition so this code is OK even for parsing the MBR. Chained EBR addresses are relative to the start of the extended partiton. */ - lba = ep_offset + le32_to_cpu(br->partition[i].lba_first); + blk_op.lba = ep_offset + le32_to_cpu(br->partition[i].lba_first); if(next >= 4) break; /* we include all primary partitions but we deliberately knobble the size in @@ -70,14 +78,14 @@ void mbr_parse(blkdev_t *blk, char letter) default: /* Regular partition: In EBRs these are relative to the EBR (not the disk, nor the extended partition) */ - blk->lba_first[next] = br_offset + le32_to_cpu(br->partition[i].lba_first); - blk->lba_count[next] = le32_to_cpu(br->partition[i].lba_count); + blk_op.blkdev->lba_first[next] = br_offset + le32_to_cpu(br->partition[i].lba_first); + blk_op.blkdev->lba_count[next] = le32_to_cpu(br->partition[i].lba_count); next++; kprintf("hd%c%d ", letter, next); } } seen++; - }while(lba); + }while(blk_op.lba); if(ep_offset && next >= 4) kputs("> "); diff --git a/Kernel/dev/mbr.h b/Kernel/dev/mbr.h index 82861608..e728803e 100644 --- a/Kernel/dev/mbr.h +++ b/Kernel/dev/mbr.h @@ -1,6 +1,6 @@ #ifndef __MBR_DOT_H__ #define __MBR_DOT_H__ -void mbr_parse(blkdev_t *blk, char letter); +void mbr_parse(char letter); #endif -- 2.34.1