[LinuxBIOS] filo ide speedup patch!

Peter Stuge stuge-linuxbios at cdy.org
Mon Mar 26 03:42:08 CEST 2007


Sorry for the delays here..

On Tue, Mar 20, 2007 at 03:43:46PM +0100, Stefan Reinauer wrote:
> * Peter Stuge <stuge-linuxbios at cdy.org> [070320 04:35]:
> > Please test this patch. Note I have only implemented multiread
> > support in fsys_ext2fs.c.
>  
> I am getting "Can't read kernel" on a CF:
> 
>   hda: LBA 128MB: TRANSCEND DOM128M

On Tue, Mar 20, 2007 at 10:09:39AM -0400, Ward Vandewege wrote:
> Hmm, that doesn't seem to work. This is what's in my grub stanza to
> boot with an umodified filo:
> 
>   title   Ubuntu LB, kernel 2.6.21-rc3
>   root    (hd4,0)
>   kernel    /boot/vmlinuz-2.6.21-rc3 root=/dev/sda1 ro apic=debug
> acpi_dbg_level=0xffffffff pci=noacpi,routeirq
> snd-hda-intel.enable_msi=1
>   console=tty0 console=ttyS0,115200
>   savedefault
>   boot
> 
> I've attached the boot log.

You're booting FILO from GRUB? Ok.


> load_linux_kernel: offset=0x1e00 addr=0x100000 size=0x1d1473
> Loading kernel... Can't read kernel

Same error as Stefan; FILO did not get enough bytes back from the
filesystem.

I've added debugging output with this patch, please test this patch
with DEBUG_BLOCKDEV, DEBUG_IDE and DEBUG_EXT2 (or DEBUG_ALL) set so
I can get more details.


Unfortunately my own EPIA-MII board doesn't run anymore (blown GSC
capacitors) so I haven't been able to test it myself.

Otherwise no new code since last patch. Please enable this debugging
output (DEBUG_ALL=1 is easiest) and send it to the list.

The debugging was added back in a hurry and since I can't test I may
need to send yet another patch if there's something missing.

No sign-off this time.


//Peter
-------------- next part --------------
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 <peter at stuge.se>
  *
  *  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 <debug.h>
+
 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 <peter at stuge.se>
  *
  *   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)
 {


More information about the coreboot mailing list