Archived
14
0
Fork 0
This repository has been archived on 2022-02-17. You can view files and clone it, but cannot push or open issues or pull requests.
linux-2.6/arch/x86/boot/edd.c
Andrey Borzenkov 464f04c9e9 x86: fix ghost EDD devices in /sys again
> This is regression but old enough. Apparently I had for whatever reasons
> EDD turned off till recently. This is 2.6.27-rc5 just in case.
>
> In 2006 I fixed ghost devices due to buggy BIOS:
>
> http://marc.info/?l=linux-kernel&m=114087765422490&w=2
>
> Later edd.S has been rewritten in C, and apparently this patch has been
> lost:
>
> {pts/1}% ls /sys/firmware/edd
> int13_dev80/  int13_dev84/  int13_dev88/  int13_dev8c/
> int13_dev81/  int13_dev85/  int13_dev89/  int13_dev8d/
> int13_dev82/  int13_dev86/  int13_dev8a/  int13_dev8e/
> int13_dev83/  int13_dev87/  int13_dev8b/  int13_dev8f/
>
> But I have just a single disk. This is the same system BTW.

Some BIOSes do not always set CF on error before return from int13.
The patch adds additional check for status being zero (AH == 0).

This was fixed for edd.S in
http://marc.info/?l=linux-kernel&m=114087765422490&w=2, but lost
again when edd.S was rewritten in C.

Signed-off-by: Andrey Borzenkov <arvidjaar@mail.ru>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-09-06 16:30:36 +02:00

188 lines
4.3 KiB
C

/* -*- linux-c -*- ------------------------------------------------------- *
*
* Copyright (C) 1991, 1992 Linus Torvalds
* Copyright 2007 rPath, Inc. - All Rights Reserved
*
* This file is part of the Linux kernel, and is made available under
* the terms of the GNU General Public License version 2.
*
* ----------------------------------------------------------------------- */
/*
* Get EDD BIOS disk information
*/
#include "boot.h"
#include <linux/edd.h>
#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
/*
* Read the MBR (first sector) from a specific device.
*/
static int read_mbr(u8 devno, void *buf)
{
u16 ax, bx, cx, dx;
ax = 0x0201; /* Legacy Read, one sector */
cx = 0x0001; /* Sector 0-0-1 */
dx = devno;
bx = (size_t)buf;
asm volatile("pushfl; stc; int $0x13; setc %%al; popfl"
: "+a" (ax), "+c" (cx), "+d" (dx), "+b" (bx)
: : "esi", "edi", "memory");
/* Some BIOSes do not set carry flag on error but still return
* error in AH. The condition below is expected to catch both */
return -!!ax; /* 0 or -1 */
}
static u32 read_mbr_sig(u8 devno, struct edd_info *ei, u32 *mbrsig)
{
int sector_size;
char *mbrbuf_ptr, *mbrbuf_end;
u32 buf_base, mbr_base;
extern char _end[];
sector_size = ei->params.bytes_per_sector;
if (!sector_size)
sector_size = 512; /* Best available guess */
/* Produce a naturally aligned buffer on the heap */
buf_base = (ds() << 4) + (u32)&_end;
mbr_base = (buf_base+sector_size-1) & ~(sector_size-1);
mbrbuf_ptr = _end + (mbr_base-buf_base);
mbrbuf_end = mbrbuf_ptr + sector_size;
/* Make sure we actually have space on the heap... */
if (!(boot_params.hdr.loadflags & CAN_USE_HEAP))
return -1;
if (mbrbuf_end > (char *)(size_t)boot_params.hdr.heap_end_ptr)
return -1;
if (read_mbr(devno, mbrbuf_ptr))
return -1;
*mbrsig = *(u32 *)&mbrbuf_ptr[EDD_MBR_SIG_OFFSET];
return 0;
}
static int get_edd_info(u8 devno, struct edd_info *ei)
{
u16 ax, bx, cx, dx, di;
memset(ei, 0, sizeof *ei);
/* Check Extensions Present */
ax = 0x4100;
bx = EDDMAGIC1;
dx = devno;
asm("pushfl; stc; int $0x13; setc %%al; popfl"
: "+a" (ax), "+b" (bx), "=c" (cx), "+d" (dx)
: : "esi", "edi");
if ((u8)ax)
return -1; /* No extended information */
if (bx != EDDMAGIC2)
return -1;
ei->device = devno;
ei->version = ax >> 8; /* EDD version number */
ei->interface_support = cx; /* EDD functionality subsets */
/* Extended Get Device Parameters */
ei->params.length = sizeof(ei->params);
ax = 0x4800;
dx = devno;
asm("pushfl; int $0x13; popfl"
: "+a" (ax), "+d" (dx), "=m" (ei->params)
: "S" (&ei->params)
: "ebx", "ecx", "edi");
/* Get legacy CHS parameters */
/* Ralf Brown recommends setting ES:DI to 0:0 */
ax = 0x0800;
dx = devno;
di = 0;
asm("pushw %%es; "
"movw %%di,%%es; "
"pushfl; stc; int $0x13; setc %%al; popfl; "
"popw %%es"
: "+a" (ax), "=b" (bx), "=c" (cx), "+d" (dx), "+D" (di)
: : "esi");
if ((u8)ax == 0) {
ei->legacy_max_cylinder = (cx >> 8) + ((cx & 0xc0) << 2);
ei->legacy_max_head = dx >> 8;
ei->legacy_sectors_per_track = cx & 0x3f;
}
return 0;
}
void query_edd(void)
{
char eddarg[8];
int do_mbr = 1;
#ifdef CONFIG_EDD_OFF
int do_edd = 0;
#else
int do_edd = 1;
#endif
int be_quiet;
int devno;
struct edd_info ei, *edp;
u32 *mbrptr;
if (cmdline_find_option("edd", eddarg, sizeof eddarg) > 0) {
if (!strcmp(eddarg, "skipmbr") || !strcmp(eddarg, "skip")) {
do_edd = 1;
do_mbr = 0;
}
else if (!strcmp(eddarg, "off"))
do_edd = 0;
else if (!strcmp(eddarg, "on"))
do_edd = 1;
}
be_quiet = cmdline_find_option_bool("quiet");
edp = boot_params.eddbuf;
mbrptr = boot_params.edd_mbr_sig_buffer;
if (!do_edd)
return;
/* Bugs in OnBoard or AddOnCards Bios may hang the EDD probe,
* so give a hint if this happens.
*/
if (!be_quiet)
printf("Probing EDD (edd=off to disable)... ");
for (devno = 0x80; devno < 0x80+EDD_MBR_SIG_MAX; devno++) {
/*
* Scan the BIOS-supported hard disks and query EDD
* information...
*/
if (!get_edd_info(devno, &ei)
&& boot_params.eddbuf_entries < EDDMAXNR) {
memcpy(edp, &ei, sizeof ei);
edp++;
boot_params.eddbuf_entries++;
}
if (do_mbr && !read_mbr_sig(devno, &ei, mbrptr++))
boot_params.edd_mbr_sig_buf_entries = devno-0x80+1;
}
if (!be_quiet)
printf("ok\n");
}
#endif