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: defconfig =================================================================== --- defconfig (revision 34) +++ defconfig (working copy) @@ -90,4 +90,5 @@ #DEBUG_IDE = 1 #DEBUG_USB = 1 #DEBUG_ELTORITO = 1 +#DEBUG_EXT2 = 1 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,26 +403,95 @@ int map; int ret = 0; int size = 0; + int manylen, manypos, blocks, firstmapped = 0; -#ifdef E2DEBUG - static char hexdigit[] = "0123456789abcdef"; - unsigned char *i; - for (i = (unsigned char *) INODE; - i < ((unsigned char *) INODE + sizeof (struct ext2_inode)); - i++) + /* read one full or partial block */ + int ext2fs_read_one(void) { + int res; + debug ("block %d offset=%d len=%d ret=%d\n", map, offset, len, ret); + if (map < 0) + return 1; + size = E2_BSIZE; + size -= offset; + if (size > len) + size = len; + disk_read_func = disk_read_hook; + res = devread (map * (E2_BSIZE/DEV_BSIZE), offset, size, buf); + disk_read_func = NULL; + if (!res) + return 1; + buf += size; + len -= size; + filepos += size; + ret += size; + return 0; + } + + /* read many fs blocks at once */ + int ext2fs_read_many(int first_block, int num_blocks) { + int first_devblock, num_devblocks, res; + debug ("%3d block%c %d - %d len=%d ret=%d\n", num_blocks, 1 == num_blocks ? ' ' : 's', first_block, first_block + num_blocks - 1, len, ret); + if (offset) + debug ("can't read many blocks with non-zero offset %d\n", offset); + 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; + res = devreadmany (buf, first_devblock, num_devblocks); + disk_read_func = NULL; + if (!res) + return 1; + size = num_blocks * E2_BSIZE; + buf += size; + len -= size; + filepos += size; + ret += size; + return 0; + } + + /* read many blocks at once? */ + if (len > E2_BSIZE) { - printf ("%c", hexdigit[*i >> 4]); - printf ("%c", hexdigit[*i % 16]); - if (!((i + 1 - (unsigned char *) INODE) % 16)) - { - printf ("\n"); - } - else - { - printf (" "); - } + 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; + offset = 0; + } + while (len > 0) + { + manylen = len; + manypos = filepos; + for (blocks = 0; manylen > 0; blocks++) + { + map = ext2fs_block_map (manypos >> EXT2_BLOCK_SIZE_BITS (SUPERBLOCK)); + if (blocks > 0 && map != firstmapped + blocks) + break; + if (!blocks) + firstmapped = map; + if (manylen < E2_BSIZE) + continue; + manypos += E2_BSIZE; + manylen -= E2_BSIZE; + } + if (ext2fs_read_many (firstmapped, blocks)) + goto err; + } + if (len < 0) + { + debug ("discarding %d surplus bytes\n", -len); + ret += len; + len = 0; + } + debug ("done reading many len=%d ret=%d\n", len, ret); } -#endif /* E2DEBUG */ + while (len > 0) { /* find the (logical) block component of our location */ @@ -424,27 +501,14 @@ #ifdef E2DEBUG printf ("map=%d\n", map); #endif /* E2DEBUG */ - if (map < 0) - break; + if (ext2fs_read_one ()) + break; + } - size = EXT2_BLOCK_SIZE (SUPERBLOCK); - size -= offset; - if (size > len) - size = len; +err: + if (len) + debug ("returning %d with len=%d ret=%d errnum=%d\n", errnum ? 0 : ret, len, ret, errnum); - 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 (errnum) ret = 0; Index: fs/blockdev.c =================================================================== --- fs/blockdev.c (revision 34) +++ fs/blockdev.c (working copy) @@ -335,6 +335,58 @@ return 0; } +/* reads many sectors from opened device into memory */ +static unsigned long read_manysectors(char *buf, unsigned long first_sector, unsigned long num_sectors) +{ + int n; + + /* If reading memory, just copy it */ + 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: + do { + n = num_sectors <= 256 ? num_sectors : 256; + if (ide_readmany(dev_drive, first_sector, n, buf) != 0) + goto readerr; + first_sector += n; + num_sectors -= n; + buf += 512 * n; + } while (num_sectors); + 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 +421,21 @@ } 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("part_length=%lu last_sector=%lu\n", part_length, last_sector); + return 0; + } + + debug("sectors %lu - %lu (%3lu) to %p\n", first_sector, last_sector, num_sectors, buf); + if (!read_manysectors (dest, part_start + first_sector, num_sectors)) + 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,20 +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; - } - 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; - } + + /* 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)) { + debug("no drq before insw\n"); + print_status(ctrl); + return -1; + } + insw(IDE_REG_DATA(ctrl), buffer, count/2); + buffer += count; + bytes -= count; + ndelay(400); + } while(bytes); + return 0; } @@ -456,8 +473,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 +483,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 +502,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 +525,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 +547,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 +595,33 @@ 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; + sector_t last_sector = first_sector + num_sectors - 1; - //debug("drive=%d, sector=%ld\n",drive,(unsigned long) sector); - /* Report the buffer is empty */ - if (sector > info->sectors) { + if (num_sectors > 1) + debug("sectors %Lu - %Lu (%3Lu) to %p\n", first_sector, last_sector, num_sectors, buffer); + if (last_sector > info->sectors) { + debug("attempt read past end of device"); 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 +629,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) {