Index: include/fs.h =================================================================== --- include/fs.h (revision 34) +++ include/fs.h (working copy) @@ -7,6 +7,7 @@ #ifdef IDE_DISK int ide_probe(int drive); +int ide_readmany(int drive, sector_t sector, sector_t num_sectors, void *buffer); int ide_read(int drive, sector_t sector, void *buffer); #endif @@ -22,6 +23,7 @@ int devopen(const char *name, int *reopen); int devread(unsigned long sector, unsigned long byte_offset, unsigned long byte_len, void *buf); +int devreadmany(void *buf, unsigned long first_sector, unsigned long num_sectors); int file_open(const char *filename); int file_read(void *buf, unsigned long len); Index: fs/fsys_ext2fs.c =================================================================== --- fs/fsys_ext2fs.c (revision 34) +++ fs/fsys_ext2fs.c (working copy) @@ -1,6 +1,9 @@ /* - * GRUB -- GRand Unified Bootloader + * FILO ext2fs driver + * + * Mostly code from GRUB -- GRand Unified Bootloader * Copyright (C) 1999, 2001 Free Software Foundation, Inc. + * Copyright (C) 2007 Peter Stuge * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +25,9 @@ #include "shared.h" #include "filesys.h" +#define DEBUG_THIS DEBUG_EXT2 +#include + static int mapblock1, mapblock2; /* sizes are always in bytes, BLOCK values are always in DEV_BSIZE (sectors) */ @@ -386,6 +392,8 @@ [logical_block & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)]; } +#define E2_BSIZE (EXT2_BLOCK_SIZE (SUPERBLOCK)) + /* preconditions: all preconds of ext2fs_block_map */ int ext2fs_read (char *buf, int len) @@ -395,7 +403,46 @@ int map; int ret = 0; int size = 0; + int manylen,manypos,blocks,mapped; + /* read one full or partial device block */ + int ext2fs_read_one(void) { + if (map < 0) + return 1; + size = E2_BSIZE; + size -= offset; + if (size > len) + size = len; + disk_read_func = disk_read_hook; + devread (map * (E2_BSIZE/DEV_BSIZE), offset, size, buf); + disk_read_func = NULL; + buf += size; + len -= size; + filepos += size; + ret += size; + return 0; + } + + /* read several full fs blocks at once */ + int ext2fs_read_many(int first_block, int num_blocks) { + int first_devblock, num_devblocks; + if (first_block < 0 || num_blocks < 0 || offset) + return 1; + if (!num_blocks) + return 0; + first_devblock = first_block * (E2_BSIZE/DEV_BSIZE); + num_devblocks = num_blocks * (E2_BSIZE/DEV_BSIZE); + disk_read_func = disk_read_hook; + devreadmany (buf, first_devblock, num_devblocks); + disk_read_func = NULL; + size = num_blocks * E2_BSIZE; + buf += size; + len -= size; + filepos += size; + ret += size; + return 0; + } + #ifdef E2DEBUG static char hexdigit[] = "0123456789abcdef"; unsigned char *i; @@ -415,6 +462,50 @@ } } #endif /* E2DEBUG */ + + /* read up to 128 blocks (256 sectors) at once */ + if(len > (2 * DEV_BSIZE)) + { + offset = filepos & (E2_BSIZE-1); + if(offset) + { + logical_block = filepos >> EXT2_BLOCK_SIZE_BITS(SUPERBLOCK); + map = ext2fs_block_map (logical_block); + if (ext2fs_read_one ()) + goto err; + } + blocks = 0; + mapped = -2; /* make sure the for loop condition starts out true */ + while( len >= E2_BSIZE) + { + manylen = len; + manypos = filepos; + for(blocks = 0; manylen >= E2_BSIZE && blocks < 128; blocks++) + { + map = ext2fs_block_map (manypos >> EXT2_BLOCK_SIZE_BITS (SUPERBLOCK)); + if (blocks > 0 && map != mapped + blocks) + break; + if (!blocks) + mapped = map; + if (manylen < E2_BSIZE) + continue; + manypos += E2_BSIZE; + manylen -= E2_BSIZE; + } + debug ("%3d consecutive blocks %d - %d len=%d ret=%d\n", blocks, mapped, mapped + blocks - 1, len, ret); + if (ext2fs_read_many (mapped, blocks)) + goto err; + } + if(len > 0) + { + map = mapped + blocks; + debug ("reading final block %d len=%d ret=%d\n", map, len, ret); + if (ext2fs_read_one ()) + goto err; + } + debug ("done reading many len=%d ret=%d\n", len, ret); + } + while (len > 0) { /* find the (logical) block component of our location */ @@ -424,27 +515,11 @@ #ifdef E2DEBUG printf ("map=%d\n", map); #endif /* E2DEBUG */ - if (map < 0) - break; - - size = EXT2_BLOCK_SIZE (SUPERBLOCK); - size -= offset; - if (size > len) - size = len; - - disk_read_func = disk_read_hook; - - devread (map * (EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE), - offset, size, buf); - - disk_read_func = NULL; - - buf += size; - len -= size; - filepos += size; - ret += size; + if (ext2fs_read_one ()) + break; } +err: if (errnum) ret = 0; Index: fs/blockdev.c =================================================================== --- fs/blockdev.c (revision 34) +++ fs/blockdev.c (working copy) @@ -335,6 +335,50 @@ return 0; } +/* reads many sectors from opened device into a buffer */ +static int read_manysectors(char *buf, unsigned long first_sector, unsigned long num_sectors) +{ + /* If reading memory, just copy into the buffer */ + if (dev_type == DISK_MEM) { + unsigned long phys = first_sector << 9; + char *virt; + //debug("mem: %#lx\n", phys); + virt = phys_to_virt(phys); + memcpy(buf, virt, num_sectors * 512); + return num_sectors; + } + + switch (dev_type) { +#ifdef IDE_DISK + case DISK_IDE: + if (ide_readmany(dev_drive, first_sector, num_sectors, buf) != 0) + goto readerr; + break; +#endif +#ifdef USB_DISK + case DISK_USB: + while (num_sectors) { + if (usb_read(dev_drive, first_sector, buf) != 0) + goto readerr; + first_sector++; + num_sectors--; + buf += 512; + } + break; +#endif + default: + printf("read_manysectors: device not open\n"); + return 0; + } + return 1; + +readerr: + printf("Disk readmany error dev=%d drive=%d first_sector=%lu num_sectors=%lu\n", + dev_type, dev_drive, first_sector, num_sectors); + dev_name[0] = '\0'; /* force re-open the device next time */ + return 0; +} + int devread(unsigned long sector, unsigned long byte_offset, unsigned long byte_len, void *buf) { @@ -369,3 +413,22 @@ } return 1; } + +int devreadmany(void *buf,unsigned long first_sector, unsigned long num_sectors) +{ + char *dest = buf; + unsigned long last_sector = first_sector + num_sectors - 1; + + if (last_sector > part_length) { + printf("Attempt to readmany out of device/partition\n"); + debug("last_sector=%lu part_length=%lu\n", last_sector, part_length); + return 0; + } + + debug("devreadmany %lu - %lu to %p\n", first_sector, first_sector + num_sectors - 1, buf); + if (!read_manysectors (dest, part_start + first_sector, num_sectors)) { + debug("read many sectors failed\n"); + return 0; + } + return 1; +} Index: drivers/ide.c =================================================================== --- drivers/ide.c (revision 34) +++ drivers/ide.c (working copy) @@ -19,6 +19,7 @@ * UBL, The Universal Talkware Boot Loader * Copyright (C) 2000 Universal Talkware Inc. * Copyright (C) 2002 Eric Biederman + * Copyright (C) 2007 Peter Stuge * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -39,6 +40,7 @@ struct controller { uint16_t cmd_base; uint16_t ctrl_base; + uint8_t stat; }; struct harddisk_info { @@ -255,7 +257,8 @@ static int not_bsy(struct controller *ctrl) { - return !(inb(IDE_REG_STATUS(ctrl)) & IDE_STATUS_BSY); + ctrl->stat=inb(IDE_REG_STATUS(ctrl)); + return !(ctrl->stat & IDE_STATUS_BSY); } /* IDE drives assert BSY bit within 400 nsec when SRST is set. @@ -264,7 +267,8 @@ static int bsy(struct controller *ctrl) { - return inb(IDE_REG_STATUS(ctrl)) & IDE_STATUS_BSY; + ctrl->stat=inb(IDE_REG_STATUS(ctrl)); + return ctrl->stat & IDE_STATUS_BSY; } #if !BSY_SET_DURING_SPINUP @@ -364,9 +368,16 @@ static int pio_data_in(struct controller *ctrl, const struct ide_pio_command *cmd, void *buffer, size_t bytes) { + int drive; + size_t count; unsigned int status; - /* FIXME handle commands with multiple blocks */ + for(drive=0; drive < IDE_MAX_DRIVES; drive++) + if(ctrl == harddisk_info[drive].ctrl) + break; + if(IDE_MAX_DRIVES == drive) + return -1; + /* Wait until the busy bit is clear */ if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { return -1; @@ -375,16 +386,26 @@ /* How do I tell if INTRQ is asserted? */ pio_set_registers(ctrl, cmd); ndelay(400); - if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) { - return -1; - } + + /* multi block capable logic inspired by OpenBIOS ide.c + * ob_ide_pio_data_in() */ + do { + count = bytes; + if (count > harddisk_info[drive].hw_sector_size) + count = harddisk_info[drive].hw_sector_size; + if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) + return -1; + if (!(ctrl->stat & IDE_STATUS_DRQ)) { + print_status(ctrl); + return -1; + } + insw(IDE_REG_DATA(ctrl), buffer, count/2); + buffer += count; + bytes -= count; + ndelay(400); + } while(bytes); + status = inb(IDE_REG_STATUS(ctrl)); - if (!(status & IDE_STATUS_DRQ)) { - print_status(ctrl); - return -1; - } - insw(IDE_REG_DATA(ctrl), buffer, bytes/2); - status = inb(IDE_REG_STATUS(ctrl)); if (status & IDE_STATUS_DRQ) { print_status(ctrl); return -1; @@ -456,8 +477,9 @@ return 0; } -static inline int ide_read_sector_chs( - struct harddisk_info *info, void *buffer, unsigned long sector) +static inline int ide_read_sectors_chs( + struct harddisk_info *info, void *buffer, unsigned long sector, + unsigned long num_sectors) { struct ide_pio_command cmd; unsigned int track; @@ -465,8 +487,11 @@ unsigned int cylinder; memset(&cmd, 0, sizeof(cmd)); - cmd.sector_count = 1; + if (num_sectors > 256) + return -1; + cmd.sector_count = 256 == num_sectors ? 0 : num_sectors; + //debug("ide_read_sector_chs: sector= %ld.\n",sector); track = sector / info->sectors_per_track; @@ -481,16 +506,20 @@ info->slave | IDE_DH_CHS; cmd.command = IDE_CMD_READ_SECTORS; - return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE); + return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE * num_sectors); } -static inline int ide_read_sector_lba( - struct harddisk_info *info, void *buffer, unsigned long sector) +static inline int ide_read_sectors_lba( + struct harddisk_info *info, void *buffer, unsigned long sector, + unsigned long num_sectors) { struct ide_pio_command cmd; memset(&cmd, 0, sizeof(cmd)); - cmd.sector_count = 1; + if (num_sectors > 256) + return -1; + cmd.sector_count = 256 == num_sectors ? 0 : num_sectors; + cmd.lba_low = sector & 0xff; cmd.lba_mid = (sector >> 8) & 0xff; cmd.lba_high = (sector >> 16) & 0xff; @@ -500,17 +529,20 @@ IDE_DH_LBA; cmd.command = IDE_CMD_READ_SECTORS; //debug("%s: sector= %ld, device command= 0x%x.\n",__FUNCTION__,(unsigned long) sector, cmd.device); - return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE); + return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE * num_sectors); } -static inline int ide_read_sector_lba48( - struct harddisk_info *info, void *buffer, sector_t sector) +static inline int ide_read_sectors_lba48( + struct harddisk_info *info, void *buffer, sector_t sector, sector_t num_sectors) { struct ide_pio_command cmd; memset(&cmd, 0, sizeof(cmd)); //debug("ide_read_sector_lba48: sector= %ld.\n",(unsigned long) sector); - cmd.sector_count = 1; + if (num_sectors > 256) + return -1; + cmd.sector_count = 256 == num_sectors ? 0 : num_sectors; + cmd.lba_low = sector & 0xff; cmd.lba_mid = (sector >> 8) & 0xff; cmd.lba_high = (sector >> 16) & 0xff; @@ -519,7 +551,7 @@ cmd.lba_high2 = (sector >> 40) & 0xff; cmd.device = info->slave | IDE_DH_LBA; cmd.command = IDE_CMD_READ_SECTORS_EXT; - return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE); + return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE * num_sectors); } static inline int ide_read_sector_packet( @@ -567,27 +599,30 @@ return 0; } -int ide_read(int drive, sector_t sector, void *buffer) +int ide_readmany(int drive, sector_t first_sector, sector_t num_sectors, void *buffer) { struct harddisk_info *info = &harddisk_info[drive]; - int result; + int result, i; - //debug("drive=%d, sector=%ld\n",drive,(unsigned long) sector); /* Report the buffer is empty */ - if (sector > info->sectors) { + if (first_sector + num_sectors - 1 > info->sectors) { return -1; } if (info->address_mode == ADDRESS_MODE_CHS) { - result = ide_read_sector_chs(info, buffer, sector); + result = ide_read_sectors_chs(info, buffer, first_sector, num_sectors); } else if (info->address_mode == ADDRESS_MODE_LBA) { - result = ide_read_sector_lba(info, buffer, sector); + result = ide_read_sectors_lba(info, buffer, first_sector, num_sectors); } else if (info->address_mode == ADDRESS_MODE_LBA48) { - result = ide_read_sector_lba48(info, buffer, sector); + result = ide_read_sectors_lba48(info, buffer, first_sector, num_sectors); } else if (info->address_mode == ADDRESS_MODE_PACKET) { - result = ide_read_sector_packet(info, buffer, sector); + for(i = 0; i < num_sectors; i++) { + result = ide_read_sector_packet(info, buffer, first_sector); + if(-1 == result) + return result; + } } else { result = -1; @@ -595,6 +630,11 @@ return result; } +int ide_read(int drive, sector_t sector, void *buffer) +{ + return ide_readmany(drive, sector, 1, buffer); +} + static int init_drive(struct harddisk_info *info, struct controller *ctrl, int slave, int drive, unsigned char *buffer, int ident_command) {