]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] EDD: Get Legacy Parameters
authorAndrew Morton <akpm@osdl.org>
Fri, 12 Mar 2004 00:18:43 +0000 (16:18 -0800)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Fri, 12 Mar 2004 00:18:43 +0000 (16:18 -0800)
From: Matt Domsch <Matt_Domsch@dell.com>

Patch below from Patrick J. LoPresti and myself.  Patrick describes:

Why this patch?  The problem is that the legacy BIOS interface
(INT13/AH=3D08) for querying the disk geometry returns different values
than the extended INT13 interface which the EDD code currently uses.  This
is because the legacy interface only provides a 10-bit cylinder field, so
modern BIOSes "lie" about the head/sector counts in order to make more of
the disk visible within the first 1024 cylinders.

Many non-Linux applications, including the stock Windows boot loader, DOS
fdisk, etc., rely upon the legacy interface and geometry.  So it is useful
to be able to obtain the legacy values from a running Linux kernel.

What this patch does is to add new entries under
/sys/firmware/edd/int13_devXX named "legacy_cylinders", "legacy_heads", and
"legacy_sectors".  These provide the geometry given by the legacy
INT13/AH=3D08 BIOS interface, just like the current "default_cylinders"
etc.  provide the the geometry given by the INT13/AH=3D48 interface.

Without this patch, I cannot use Linux to partition a drive and install
Windows, which happens to be my application.

 - Pat
   http://unattended.sourceforge.net/

In addition, this adds two buggy BIOS workarounds  in the EDD int13
calls as suggested by Ralf Brown's interrupt list.

I'm also interested in moving this code out of arch/i386/kernel/edd.c and
include/asm-i386/edd.h, as I believe it is applicable on x86-64 as well.
However, there's no good place under drivers/ to put edd.c when it's not
tied to a bus, but to several CPU architectures and their firmwares...
Maybe a new directory drivers/firmware?

Documentation/i386/zero-page.txt
arch/i386/boot/setup.S
arch/i386/kernel/edd.c
include/asm-i386/edd.h

index 7cc2f05f86ac3438982e3940ca38ceb97892ed6a..7df3080b519049492606c38e120ff871420f50ea 100644 (file)
@@ -75,7 +75,7 @@ Offset        Type            Description
 0x2cc  4 bytes         DISK80_SIG_BUFFER (setup.S)
 0x2d0 - 0x600          E820MAP
 0x600 - 0x7ff          EDDBUF (setup.S) for disk signature read sector
-0x600 - 0x7d3          EDDBUF (setup.S) for edd data
+0x600 - 0x7eb          EDDBUF (setup.S) for edd data
 
 0x800  string, 2K max  COMMAND_LINE, the kernel commandline as
                        copied using CL_OFFSET.
index 35ba236afc333521de03ca70f234ee5a676f4ff3..7ed058ddc51fd14e7147616cabf16d895879cab2 100644 (file)
@@ -51,6 +51,8 @@
  *   projects 1572D, 1484D, 1386D, 1226DT
  * disk signature read by Matt Domsch <Matt_Domsch@dell.com>
  *     and Andrew Wilks <Andrew_Wilks@dell.com> September 2003
+ * legacy CHS retreival by Patrick J. LoPresti <patl@users.sourceforge.net>
+ *      March 2004
  */
 
 #include <linux/config.h>
@@ -592,7 +594,11 @@ done_apm_bios:
        pushw   %ds
        popw    %es
        movw    $EDDBUF, %bx
-       int     $0x13
+       pushw   %dx             # work around buggy BIOSes
+       stc                     # work around buggy BIOSes
+       int     $0x13
+       sti                     # work around buggy BIOSes
+       popw    %dx
        jc      disk_sig_done
        movl    (EDDBUF+MBR_SIG_OFFSET), %eax
        movl    %eax, (DISK80_SIG_BUFFER)       # store success
@@ -603,32 +609,34 @@ disk_sig_done:
 # This consists of two calls:
 #    int 13h ah=41h "Check Extensions Present"
 #    int 13h ah=48h "Get Device Parameters"
+#    int 13h ah=08h "Legacy Get Device Parameters"
 #
 # A buffer of size EDDMAXNR*(EDDEXTSIZE+EDDPARMSIZE) is reserved for our use
 # in the empty_zero_page at EDDBUF.  The first four bytes of which are
 # used to store the device number, interface support map and version
-# results from fn41.  The following 74 bytes are used to store
-# the results from fn48.  Starting from device 80h, fn41, then fn48
+# results from fn41.  The next four bytes are used to store the legacy
+# cylinders, heads, and sectors from fn08. The following 74 bytes are used to
+# store the results from fn48.  Starting from device 80h, fn41, then fn48
 # are called and their results stored in EDDBUF+n*(EDDEXTSIZE+EDDPARMIZE).
 # Then the pointer is incremented to store the data for the next call.
 # This repeats until either a device doesn't exist, or until EDDMAXNR
 # devices have been stored.
-# The one tricky part is that ds:si always points four bytes into
-# the structure, and the fn41 results are stored at offsets
+# The one tricky part is that ds:si always points EDDEXTSIZE bytes into
+# the structure, and the fn41 and fn08 results are stored at offsets
 # from there.  This removes the need to increment the pointer for
 # every store, and leaves it ready for the fn48 call.
 # A second one-byte buffer, EDDNR, in the empty_zero_page stores
 # the number of BIOS devices which exist, up to EDDMAXNR.
 # In setup.c, copy_edd() stores both empty_zero_page buffers away
-# for later use, as they would get overwritten otherwise. 
+# for later use, as they would get overwritten otherwise.
 # This code is sensitive to the size of the structs in edd.h
-edd_start:  
+edd_start:
                                                # %ds points to the bootsector
                                                        # result buffer for fn48
-       movw    $EDDBUF+EDDEXTSIZE, %si         # in ds:si, fn41 results
-                                               # kept just before that    
+       movw    $EDDBUF+EDDEXTSIZE, %si         # in ds:si, fn41 results
+                                               # kept just before that
        movb    $0, (EDDNR)                     # zero value at EDDNR
-       movb    $0x80, %dl                      # BIOS device 0x80
+       movb    $0x80, %dl                      # BIOS device 0x80
 
 edd_check_ext:
        movb    $CHECKEXTENSIONSPRESENT, %ah    # Function 41
@@ -636,30 +644,56 @@ edd_check_ext:
        int     $0x13                           # make the call
        jc      edd_done                        # no more BIOS devices
 
-       cmpw    $EDDMAGIC2, %bx                 # is magic right?
+       cmpw    $EDDMAGIC2, %bx                 # is magic right?
        jne     edd_next                        # nope, next...
 
-       movb    %dl, %ds:-4(%si)                # store device number
-       movb    %ah, %ds:-3(%si)                # store version
-       movw    %cx, %ds:-2(%si)                # store extensions
+       movb    %dl, %ds:-8(%si)                # store device number
+       movb    %ah, %ds:-7(%si)                # store version
+       movw    %cx, %ds:-6(%si)                # store extensions
        incb    (EDDNR)                         # note that we stored something
-        
-edd_get_device_params:  
+
+edd_get_device_params:
        movw    $EDDPARMSIZE, %ds:(%si)         # put size
-       movb    $GETDEVICEPARAMETERS, %ah       # Function 48
+       movw    $0x0, %ds:2(%si)                # work around buggy BIOSes
+       movb    $GETDEVICEPARAMETERS, %ah       # Function 48
        int     $0x13                           # make the call
                                                # Don't check for fail return
                                                # it doesn't matter.
+edd_get_legacy_chs:
+       xorw    %ax, %ax
+       movw    %ax, %ds:-4(%si)
+       movw    %ax, %ds:-2(%si)
+        # Ralf Brown's Interrupt List says to set ES:DI to
+       # 0000h:0000h "to guard against BIOS bugs"
+       pushw   %es
+       movw    %ax, %es
+       movw    %ax, %di
+       pushw   %dx                             # legacy call clobbers %dl
+       movb    $LEGACYGETDEVICEPARAMETERS, %ah # Function 08
+       int     $0x13                           # make the call
+       jc      edd_legacy_done                 # failed
+       movb    %cl, %al                        # Low 6 bits are max
+       andb    $0x3F, %al                      #   sector number
+       movb    %al, %ds:-1(%si)                # Record max sect
+       movb    %dh, %ds:-2(%si)                # Record max head number
+       movb    %ch, %al                        # Low 8 bits of max cyl
+       shr     $6, %cl
+       movb    %cl, %ah                        # High 2 bits of max cyl
+       movw    %ax, %ds:-4(%si)
+
+edd_legacy_done:
+       popw    %dx
+       popw    %es
        movw    %si, %ax                        # increment si
        addw    $EDDPARMSIZE+EDDEXTSIZE, %ax
        movw    %ax, %si
 
 edd_next:
-        incb   %dl                             # increment to next device
-               cmpb    $EDDMAXNR, (EDDNR)              # Out of space?
+       incb    %dl                             # increment to next device
+       cmpb    $EDDMAXNR, (EDDNR)              # Out of space?
        jb      edd_check_ext                   # keep looping
-    
-edd_done:   
+
+edd_done:
 #endif
 
 # Now we want to move to protected mode ...
index f43e94510bd1f4f0218f021b8ca4f5146b97f425..ba27c761545d3b6d1d8f8d0835e5e0ee45b15727 100644 (file)
@@ -1,8 +1,9 @@
 /*
  * linux/arch/i386/kernel/edd.c
- *  Copyright (C) 2002, 2003 Dell Inc.
+ *  Copyright (C) 2002, 2003, 2004 Dell Inc.
  *  by Matt Domsch <Matt_Domsch@dell.com>
  *  disk80 signature by Matt Domsch, Andrew Wilks, and Sandeep K. Shandilya
+ *  legacy CHS by Patrick J. LoPresti <patl@users.sourceforge.net>
  *
  * BIOS Enhanced Disk Drive Services (EDD)
  * conformant to T13 Committee www.t13.org
@@ -60,7 +61,7 @@ MODULE_AUTHOR("Matt Domsch <Matt_Domsch@Dell.com>");
 MODULE_DESCRIPTION("sysfs interface to BIOS EDD information");
 MODULE_LICENSE("GPL");
 
-#define EDD_VERSION "0.12 2004-Jan-26"
+#define EDD_VERSION "0.13 2004-Mar-09"
 #define EDD_DEVICE_NAME_SIZE 16
 #define REPORT_URL "http://linux.dell.com/edd/results.html"
 
@@ -231,7 +232,7 @@ static ssize_t
 edd_show_raw_data(struct edd_device *edev, char *buf)
 {
        struct edd_info *info = edd_dev_get_info(edev);
-       ssize_t len = sizeof (*info) - 4;
+       ssize_t len = sizeof (info->params);
        if (!edev || !info || !buf) {
                return -EINVAL;
        }
@@ -240,10 +241,10 @@ edd_show_raw_data(struct edd_device *edev, char *buf)
                len = info->params.length;
 
        /* In case of buggy BIOSs */
-       if (len > (sizeof(*info) - 4))
-               len = sizeof(*info) - 4;
+       if (len > (sizeof(info->params)))
+               len = sizeof(info->params);
 
-       memcpy(buf, ((char *)info) + 4, len);
+       memcpy(buf, &info->params, len);
        return len;
 }
 
@@ -320,6 +321,45 @@ edd_show_info_flags(struct edd_device *edev, char *buf)
        return (p - buf);
 }
 
+static ssize_t
+edd_show_legacy_cylinders(struct edd_device *edev, char *buf)
+{
+       struct edd_info *info = edd_dev_get_info(edev);
+       char *p = buf;
+       if (!edev || !info || !buf) {
+               return -EINVAL;
+       }
+
+       p += snprintf(p, left, "0x%x\n", info->legacy_cylinders);
+       return (p - buf);
+}
+
+static ssize_t
+edd_show_legacy_heads(struct edd_device *edev, char *buf)
+{
+       struct edd_info *info = edd_dev_get_info(edev);
+       char *p = buf;
+       if (!edev || !info || !buf) {
+               return -EINVAL;
+       }
+
+       p += snprintf(p, left, "0x%x\n", info->legacy_heads);
+       return (p - buf);
+}
+
+static ssize_t
+edd_show_legacy_sectors(struct edd_device *edev, char *buf)
+{
+       struct edd_info *info = edd_dev_get_info(edev);
+       char *p = buf;
+       if (!edev || !info || !buf) {
+               return -EINVAL;
+       }
+
+       p += snprintf(p, left, "0x%x\n", info->legacy_sectors);
+       return (p - buf);
+}
+
 static ssize_t
 edd_show_default_cylinders(struct edd_device *edev, char *buf)
 {
@@ -383,6 +423,33 @@ edd_show_sectors(struct edd_device *edev, char *buf)
  * creating files for them either.
  */
 
+static int
+edd_has_legacy_cylinders(struct edd_device *edev)
+{
+       struct edd_info *info = edd_dev_get_info(edev);
+       if (!edev || !info)
+               return -EINVAL;
+       return info->legacy_cylinders > 0;
+}
+
+static int
+edd_has_legacy_heads(struct edd_device *edev)
+{
+       struct edd_info *info = edd_dev_get_info(edev);
+       if (!edev || !info)
+               return -EINVAL;
+       return info->legacy_heads > 0;
+}
+
+static int
+edd_has_legacy_sectors(struct edd_device *edev)
+{
+       struct edd_info *info = edd_dev_get_info(edev);
+       if (!edev || !info)
+               return -EINVAL;
+       return info->legacy_sectors > 0;
+}
+
 static int
 edd_has_default_cylinders(struct edd_device *edev)
 {
@@ -452,6 +519,12 @@ static EDD_DEVICE_ATTR(version, 0444, edd_show_version, NULL);
 static EDD_DEVICE_ATTR(extensions, 0444, edd_show_extensions, NULL);
 static EDD_DEVICE_ATTR(info_flags, 0444, edd_show_info_flags, NULL);
 static EDD_DEVICE_ATTR(sectors, 0444, edd_show_sectors, NULL);
+static EDD_DEVICE_ATTR(legacy_cylinders, 0444, edd_show_legacy_cylinders,
+                      edd_has_legacy_cylinders);
+static EDD_DEVICE_ATTR(legacy_heads, 0444, edd_show_legacy_heads,
+                      edd_has_legacy_heads);
+static EDD_DEVICE_ATTR(legacy_sectors, 0444, edd_show_legacy_sectors,
+                      edd_has_legacy_sectors);
 static EDD_DEVICE_ATTR(default_cylinders, 0444, edd_show_default_cylinders,
                       edd_has_default_cylinders);
 static EDD_DEVICE_ATTR(default_heads, 0444, edd_show_default_heads,
@@ -478,6 +551,9 @@ static struct attribute * def_attrs[] = {
 
 /* These attributes are conditional and only added for some devices. */
 static struct edd_attribute * edd_attrs[] = {
+       &edd_attr_legacy_cylinders,
+       &edd_attr_legacy_heads,
+       &edd_attr_legacy_sectors,
        &edd_attr_default_cylinders,
        &edd_attr_default_heads,
        &edd_attr_default_sectors_per_track,
index 6f7dbc6db81125d518b4738fdc636ba67209b552..8e846c632f432a195dd659078b977e9d06fc4e89 100644 (file)
                                   in empty_zero_block - treat this as 1 byte  */
 #define EDDBUF 0x600           /* addr of edd_info structs in empty_zero_block */
 #define EDDMAXNR 6             /* number of edd_info structs starting at EDDBUF  */
-#define EDDEXTSIZE 4           /* change these if you muck with the structures */
+#define EDDEXTSIZE 8           /* change these if you muck with the structures */
 #define EDDPARMSIZE 74
 #define CHECKEXTENSIONSPRESENT 0x41
 #define GETDEVICEPARAMETERS 0x48
+#define LEGACYGETDEVICEPARAMETERS 0x08
 #define EDDMAGIC1 0x55AA
 #define EDDMAGIC2 0xAA55
 
@@ -165,6 +166,9 @@ struct edd_info {
        u8 device;
        u8 version;
        u16 interface_support;
+       u16 legacy_cylinders;
+       u8 legacy_heads;
+       u8 legacy_sectors;
        struct edd_device_params params;
 } __attribute__ ((packed));