than function call parameters.
*/
static blkdev_t blkdev_table[MAX_BLKDEV];
+struct blkparam blk_op;
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;
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;
}
{
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');
}
/* 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);
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 */
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; i<MBR_ENTRY_COUNT && next < MAX_PARTITIONS; i++){
switch(br->partition[i].type){
/* 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
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("> ");
#ifndef __MBR_DOT_H__
#define __MBR_DOT_H__
-void mbr_parse(blkdev_t *blk, char letter);
+void mbr_parse(char letter);
#endif