Commit 66b61a5c authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] EDD: Get Legacy Parameters

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?
parent e0ed9d75
......@@ -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.
......
......@@ -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 ...
......
/*
* 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,
......
......@@ -34,10 +34,11 @@
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));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment