Re: Problem with larger IDE drive

Andries.Brouwer@cwi.nl
Thu, 25 Mar 1999 23:31:15 +0100 (MET)


> That is exactly what I was looking for. Does anyone have
> a patch for 2.0.36?

The essential part of the patch is a patch of drivers/block/ide.c like

/* calculate drive capacity, and select LBA if possible */
- (void) current_capacity (drive);
+ capacity = current_capacity (drive);

- /* Correct the number of cyls if the bios value is too small */
- if (drive->sect == drive->bios_sect && drive->head == drive->bios_head) {
- if (drive->cyl > drive->bios_cyl)
- drive->bios_cyl = drive->cyl;
- }
+ /* Set the cylinder count - the BIOS reported value may be too small */
+ if (capacity >= (drive->bios_cyl * drive->bios_sect * drive->bios_head) &&
+ (!drive->forced_geom) && drive->bios_sect && drive->bios_head)
+ drive->bios_cyl = (capacity / drive->bios_sect) / drive->bios_head;

(I gave it a few months ago.)

Below a somewhat longer patch that I constructed a moment ago
by making a source halfway Alan's 2.0.37pre8 and my own.

Andries

[P.S. Recently someone remarked that Linux would get into a loop
at boot time with an appropriately constructed extended partition table.
I left the trivial patch in.]

[P.S.2 Let me take this opportunity to advertise
ftp://ftp.cwi.nl/pub/aeb/getbios/* a bit more. This contains a kernel patch
for 2.2.2 and a C program getbiosdrivedata.c, where the kernel patch collects
BIOS information at boot time, and the C program interprets the results.
The idea is to eventually remove all geometry knowledge from the kernel
(it isnt needed there) to a library used by LILO and fdisk.
The most difficult problem is finding the correspondence between the BIOS
disks 0x80, 0x81, ... and the Linux disks /dev/sda, /dev/hda, ...
A dozen people or so already sent a report, but the variation is large,
different BIOSes have different bugs, and I need many more. Please, if you
have a spare half hour and either a very new BIOS or a lot of disks,
try this out and mail me the output. - aeb@cwi.nl.]

diff -u --recursive --new-file ../linux-2.0.37pre8/linux/drivers/block/genhd.c ./linux/drivers/block/genhd.c
--- ../linux-2.0.37pre8/linux/drivers/block/genhd.c Sat Mar 20 02:12:02 1999
+++ ./linux/drivers/block/genhd.c Thu Mar 25 20:49:19 1999
@@ -162,6 +162,8 @@
struct partition *p;
unsigned long first_sector, first_size, this_sector, this_size;
int mask = (1 << hd->minor_shift) - 1;
+ int loopct = 0; /* number of links followed
+ without finding a data partition */
int i;

first_sector = hd->part[MINOR(dev)].start_sect;
@@ -169,6 +171,8 @@
this_sector = first_sector;

while (1) {
+ if (++loopct > 100)
+ return;
if ((current_minor & mask) == 0)
return;
if (!(bh = bread(dev,0,1024)))
@@ -213,6 +217,7 @@

add_partition(hd, current_minor, this_sector+START_SECT(p), NR_SECTS(p));
current_minor++;
+ loopct = 0;
if ((current_minor & mask) == 0)
goto done;
}
diff -u --recursive --new-file ../linux-2.0.37pre8/linux/drivers/block/ide.c ./linux/drivers/block/ide.c
--- ../linux-2.0.37pre8/linux/drivers/block/ide.c Sat Mar 20 02:12:02 1999
+++ ./linux/drivers/block/ide.c Thu Mar 25 22:39:28 1999
@@ -659,15 +659,6 @@
drive->cyl = id->lba_capacity / (drive->head * drive->sect);
capacity = id->lba_capacity;
drive->select.b.lba = 1;
-#if 0
- /*
- * This is the correct place to perform this task;
- * however, we do this later for reporting.
- */
- if (*(int *)&id->cur_capacity0 != id->lba_capacity) {
- *(int *)&id->cur_capacity0 = id->lba_capacity;
- }
-#endif
}
}
return (capacity - drive->sect0);
@@ -1597,11 +1588,16 @@
goto kill_rq;
}
block += drive->part[minor&PARTN_MASK].start_sect + drive->sect0;
+
#if FAKE_FDISK_FOR_EZDRIVE
+ /* Yecch - this will shift the entire interval,
+ possibly killing some innocent following sector */
if (block == 0 && drive->remap_0_to_1)
block = 1; /* redirect MBR access to EZ-Drive partn table */
#endif /* FAKE_FDISK_FOR_EZDRIVE */
+
((ide_hwgroup_t *)hwif->hwgroup)->drive = drive;
+
#if (DISK_RECOVERY_TIME > 0)
while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME);
#endif
@@ -2557,6 +2553,7 @@
}

drive->media = ide_disk;
+
/* Extract geometry if we did not already have one for the drive */
if (!drive->present) {
drive->present = 1;
@@ -2596,34 +2593,18 @@
if ((id->lba_capacity > 16514064) || (id->cyls == 0x3fff)) {
id->cyls = ((int)(id->lba_capacity/(id->heads * id->sectors)));
}
- drive->cyl = id->cur_cyls = id->cyls;
- drive->head = id->cur_heads = id->heads;
- drive->sect = id->cur_sectors = id->sectors;
+ drive->cyl = id->cyls;
+ drive->head = id->heads;
+ drive->sect = id->sectors;
}

/* calculate drive capacity, and select LBA if possible */
capacity = current_capacity (drive);

- /*
- * if possible, give fdisk access to more of the drive,
- * by correcting bios_cyls:
- */
- if ((capacity >= (id->cyls * id->heads * id->sectors)) &&
- (!drive->forced_geom) && drive->bios_sect && drive->bios_head) {
+ /* Set the cylinder count - the BIOS reported value may be too small */
+ if (capacity >= (drive->bios_cyl * drive->bios_sect * drive->bios_head) &&
+ (!drive->forced_geom) && drive->bios_sect && drive->bios_head)
drive->bios_cyl = (capacity / drive->bios_sect) / drive->bios_head;
-#ifdef DEBUG
- printk("FDISK Fixing Geometry :: CHS=%d/%d/%d to CHS=%d/%d/%d\n",
- drive->id->cur_cyls,
- drive->id->cur_heads,
- drive->id->cur_sectors,
- drive->bios_cyl,
- drive->bios_head,
- drive->bios_sect);
-#endif
- drive->id->cur_cyls = drive->bios_cyl;
- drive->id->cur_heads = drive->bios_head;
- drive->id->cur_sectors = drive->bios_sect;
- }

if (!strncmp(id->model, "BMI ", 4) &&
strstr(id->model, " ENHANCED IDE ") &&
@@ -2666,18 +2647,6 @@
}
}
printk("\n");
- if (drive->select.b.lba) {
- if (*(int *)&id->cur_capacity0 != id->lba_capacity) {
-#ifdef DEBUG
- printk(" CurSects=%d, LBASects=%d, ",
- *(int *)&id->cur_capacity0, id->lba_capacity);
-#endif
- *(int *)&id->cur_capacity0 = id->lba_capacity;
-#ifdef DEBUG
- printk( "Fixed CurSects=%d\n", *(int *)&id->cur_capacity0);
-#endif
- }
- }
}

/*
@@ -2934,6 +2903,12 @@
* The only "perfect" way to handle this would be to modify the setup.[cS] code
* to do BIOS calls Int13h/Fn08h and Int13h/Fn48h to get all of the drive info
* for us during initialization. I have the necessary docs -- any takers? -ml
+ * [I did this. But the result is more suited for user space. -aeb]
+ *
+ * Unfortunately the above is far too optimistic. One of the problems is that
+ * drives 1 and 2 may be SCSI disks (even when IDE disks are present), so that
+ * the geometry we read here from BIOS is attributed to the wrong disks.
+ * Eventually the routine below should be removed.
*/
static void probe_cmos_for_drives (ide_hwif_t *hwif)
{
@@ -3379,65 +3354,53 @@
* an IDE disk drive, or if a geometry was "forced" on the commandline.
* Returns 1 if the geometry translation was successful.
*/
-
-/*
- * We update the values if the current CHS as determined by the kernel.
- * This now allows for the hdparm tool to read the actual settings of the
- * being used by the kernel. This removes the need for doing guess
- * translations based on the raw values of the drive.
- */
int ide_xlate_1024 (kdev_t i_rdev, int xparm, const char *msg)
{
ide_drive_t *drive;
- static const byte head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0};
- const byte *heads = head_vals;
+ /* This is the documented list of values (some version of)
+ OnTrack DM uses. */
+ static const byte dm_head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0};
+
+ /* This is a pure phantasy list - known to be incorrect. */
+ /* In fact it seems that EZD does not do anything to the CHS
+ values in the partition table, so whether EZD is present
+ or not should probably not influence the geometry. */
+ static const byte ez_head_vals[] = {4, 8, 16, 32, 64, 128, 240, 255, 0};
+ const byte *heads;
unsigned long tracks;

drive = get_info_ptr(i_rdev);
if (!drive)
return 0;

- if (drive->forced_geom) {
- /*
- * Update the current 3D drive values.
- */
- drive->id->cur_cyls = drive->bios_cyl;
- drive->id->cur_heads = drive->bios_head;
- drive->id->cur_sectors = drive->bios_sect;
- return 0;
- }
-
- if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63) {
- /*
- * Update the current 3D drive values.
- */
- drive->id->cur_cyls = drive->bios_cyl;
- drive->id->cur_heads = drive->bios_head;
- drive->id->cur_sectors = drive->bios_sect;
+ if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63)
return 0; /* we already have a translation */
+
+ if (xparm == -1) {
+ int ret = 0;
+#if FAKE_FDISK_FOR_EZDRIVE
+ if (drive->remap_0_to_1 == 0) {
+ drive->remap_0_to_1 = 1;
+ printk("%s [remap 0->1]", msg);
+ msg = NULL;
+ ret = 1;
+ }
+ if (drive->bios_head > 16)
+#endif /* FAKE_FDISK_FOR_EZDRIVE */
+ return ret; /* we already have a translation */
}

- printk("%s ", msg);
+ if (drive->forced_geom)
+ return 0;

- if (xparm < 0 && (drive->bios_cyl * drive->bios_head * drive->bios_sect) < (1024 * 16 * 63)) {
- /*
- * Update the current 3D drive values.
- */
- drive->id->cur_cyls = drive->bios_cyl;
- drive->id->cur_heads = drive->bios_head;
- drive->id->cur_sectors = drive->bios_sect;
- return 0; /* small disk: no translation needed */
- }
+ if (msg)
+ printk("%s", msg);

- if (drive->id) {
- drive->cyl = drive->id->cyls;
- drive->head = drive->id->heads;
- drive->sect = drive->id->sectors;
- }
- drive->bios_cyl = drive->cyl;
- drive->bios_head = drive->head;
- drive->bios_sect = drive->sect;
- drive->special.b.set_geometry = 1;
+ /* There used to be code here that assigned drive->id->CHS
+ to drive->CHS and that to drive->bios_CHS. However,
+ some disks have id->C/H/S = 4092/16/63 but are larger than 2.1 GB.
+ In such cases that code was wrong. Moreover,
+ there seems to be no reason to do any of these things. */

tracks = drive->bios_cyl * drive->bios_head * drive->bios_sect / 63;
drive->bios_sect = 63;
@@ -3445,33 +3408,21 @@
drive->bios_head = xparm;
drive->bios_cyl = tracks / drive->bios_head;
} else {
+ heads = (xparm == -1) ? ez_head_vals : dm_head_vals;
while (drive->bios_cyl >= 1024) {
drive->bios_head = *heads;
drive->bios_cyl = tracks / drive->bios_head;
if (0 == *++heads)
break;
}
-#if FAKE_FDISK_FOR_EZDRIVE
- if (xparm == -1) {
- drive->remap_0_to_1 = 1;
- msg = "0->1";
- } else
-#endif /* FAKE_FDISK_FOR_EZDRIVE */
if (xparm == 1) {
drive->sect0 = 63;
drive->bios_cyl = (tracks - 1) / drive->bios_head;
- msg = "+63";
+ printk(" [remap +63]");
}
- printk("[remap %s] ", msg);
}
drive->part[0].nr_sects = current_capacity(drive);
- printk("[%d/%d/%d]", drive->bios_cyl, drive->bios_head, drive->bios_sect);
- /*
- * Update the current 3D drive values.
- */
- drive->id->cur_cyls = drive->bios_cyl;
- drive->id->cur_heads = drive->bios_head;
- drive->id->cur_sectors = drive->bios_sect;
+ printk(" [%d/%d/%d]", drive->bios_cyl, drive->bios_head, drive->bios_sect);
return 1;
}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/