2007-07-11 23:18:48 +04:00
/* -*- linux-c -*- ------------------------------------------------------- *
*
* Copyright ( C ) 1991 , 1992 Linus Torvalds
* Copyright 2007 rPath , Inc . - All Rights Reserved
2009-04-02 05:17:17 +04:00
* Copyright 2009 Intel Corporation ; author H . Peter Anvin
2007-07-11 23:18:48 +04:00
*
* 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>
2014-03-18 23:26:37 +04:00
# include "string.h"
2007-07-11 23:18:48 +04:00
# 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 )
{
2009-04-02 05:17:17 +04:00
struct biosregs ireg , oreg ;
2007-07-11 23:18:48 +04:00
2009-04-02 05:17:17 +04:00
initregs ( & ireg ) ;
ireg . ax = 0x0201 ; /* Legacy Read, one sector */
ireg . cx = 0x0001 ; /* Sector 0-0-1 */
ireg . dl = devno ;
ireg . bx = ( size_t ) buf ;
2007-07-11 23:18:48 +04:00
2009-04-02 05:17:17 +04:00
intcall ( 0x13 , & ireg , & oreg ) ;
return - ( oreg . eflags & X86_EFLAGS_CF ) ; /* 0 or -1 */
2007-07-11 23:18:48 +04:00
}
2007-08-15 04:36:00 +04:00
static u32 read_mbr_sig ( u8 devno , struct edd_info * ei , u32 * mbrsig )
2007-07-11 23:18:48 +04:00
{
int sector_size ;
char * mbrbuf_ptr , * mbrbuf_end ;
u32 buf_base , mbr_base ;
extern char _end [ ] ;
2008-10-03 21:08:49 +04:00
u16 mbr_magic ;
2007-07-11 23:18:48 +04:00
sector_size = ei - > params . bytes_per_sector ;
if ( ! sector_size )
sector_size = 512 ; /* Best available guess */
2007-08-01 00:17:13 +04:00
/* Produce a naturally aligned buffer on the heap */
2007-07-11 23:18:48 +04:00
buf_base = ( ds ( ) < < 4 ) + ( u32 ) & _end ;
mbr_base = ( buf_base + sector_size - 1 ) & ~ ( sector_size - 1 ) ;
2007-08-01 00:17:13 +04:00
mbrbuf_ptr = _end + ( mbr_base - buf_base ) ;
2007-07-11 23:18:48 +04:00
mbrbuf_end = mbrbuf_ptr + sector_size ;
2007-08-01 00:17:13 +04:00
/* Make sure we actually have space on the heap... */
2007-07-11 23:18:48 +04:00
if ( ! ( boot_params . hdr . loadflags & CAN_USE_HEAP ) )
2007-08-15 04:36:00 +04:00
return - 1 ;
2007-07-11 23:18:48 +04:00
if ( mbrbuf_end > ( char * ) ( size_t ) boot_params . hdr . heap_end_ptr )
2007-08-15 04:36:00 +04:00
return - 1 ;
2007-07-11 23:18:48 +04:00
2008-10-03 21:08:49 +04:00
memset ( mbrbuf_ptr , 0 , sector_size ) ;
2007-07-11 23:18:48 +04:00
if ( read_mbr ( devno , mbrbuf_ptr ) )
2007-08-15 04:36:00 +04:00
return - 1 ;
2007-07-11 23:18:48 +04:00
2007-08-15 04:36:00 +04:00
* mbrsig = * ( u32 * ) & mbrbuf_ptr [ EDD_MBR_SIG_OFFSET ] ;
2008-10-03 21:08:49 +04:00
mbr_magic = * ( u16 * ) & mbrbuf_ptr [ 510 ] ;
/* check for valid MBR magic */
return mbr_magic = = 0xAA55 ? 0 : - 1 ;
2007-07-11 23:18:48 +04:00
}
static int get_edd_info ( u8 devno , struct edd_info * ei )
{
2009-04-02 05:17:17 +04:00
struct biosregs ireg , oreg ;
2007-07-11 23:18:48 +04:00
memset ( ei , 0 , sizeof * ei ) ;
/* Check Extensions Present */
2009-04-02 05:17:17 +04:00
initregs ( & ireg ) ;
ireg . ah = 0x41 ;
ireg . bx = EDDMAGIC1 ;
ireg . dl = devno ;
intcall ( 0x13 , & ireg , & oreg ) ;
2007-07-11 23:18:48 +04:00
2009-04-02 05:17:17 +04:00
if ( oreg . eflags & X86_EFLAGS_CF )
2007-07-11 23:18:48 +04:00
return - 1 ; /* No extended information */
2009-04-02 05:17:17 +04:00
if ( oreg . bx ! = EDDMAGIC2 )
2007-07-11 23:18:48 +04:00
return - 1 ;
ei - > device = devno ;
2009-04-02 05:17:17 +04:00
ei - > version = oreg . ah ; /* EDD version number */
ei - > interface_support = oreg . cx ; /* EDD functionality subsets */
2007-07-11 23:18:48 +04:00
/* Extended Get Device Parameters */
ei - > params . length = sizeof ( ei - > params ) ;
2009-04-02 05:17:17 +04:00
ireg . ah = 0x48 ;
ireg . si = ( size_t ) & ei - > params ;
intcall ( 0x13 , & ireg , & oreg ) ;
2007-07-11 23:18:48 +04:00
/* Get legacy CHS parameters */
/* Ralf Brown recommends setting ES:DI to 0:0 */
2009-04-02 05:17:17 +04:00
ireg . ah = 0x08 ;
ireg . es = 0 ;
intcall ( 0x13 , & ireg , & oreg ) ;
if ( ! ( oreg . eflags & X86_EFLAGS_CF ) ) {
ei - > legacy_max_cylinder = oreg . ch + ( ( oreg . cl & 0xc0 ) < < 2 ) ;
ei - > legacy_max_head = oreg . dh ;
ei - > legacy_sectors_per_track = oreg . cl & 0x3f ;
2007-07-11 23:18:48 +04:00
}
return 0 ;
}
void query_edd ( void )
{
char eddarg [ 8 ] ;
int do_mbr = 1 ;
2008-04-29 12:02:45 +04:00
# ifdef CONFIG_EDD_OFF
int do_edd = 0 ;
# else
2007-07-11 23:18:48 +04:00
int do_edd = 1 ;
2008-04-29 12:02:45 +04:00
# endif
2008-01-30 15:33:03 +03:00
int be_quiet ;
2007-07-11 23:18:48 +04:00
int devno ;
struct edd_info ei , * edp ;
2007-08-15 04:36:00 +04:00
u32 * mbrptr ;
2007-07-11 23:18:48 +04:00
if ( cmdline_find_option ( " edd " , eddarg , sizeof eddarg ) > 0 ) {
2008-04-29 12:02:45 +04:00
if ( ! strcmp ( eddarg , " skipmbr " ) | | ! strcmp ( eddarg , " skip " ) ) {
do_edd = 1 ;
2007-07-11 23:18:48 +04:00
do_mbr = 0 ;
2008-04-29 12:02:45 +04:00
}
2007-07-11 23:18:48 +04:00
else if ( ! strcmp ( eddarg , " off " ) )
do_edd = 0 ;
2008-04-29 12:02:45 +04:00
else if ( ! strcmp ( eddarg , " on " ) )
do_edd = 1 ;
2007-07-11 23:18:48 +04:00
}
2008-01-30 15:33:03 +03:00
be_quiet = cmdline_find_option_bool ( " quiet " ) ;
2007-08-15 04:36:00 +04:00
edp = boot_params . eddbuf ;
mbrptr = boot_params . edd_mbr_sig_buffer ;
2007-07-11 23:18:48 +04:00
if ( ! do_edd )
return ;
2008-01-30 15:33:03 +03:00
/* Bugs in OnBoard or AddOnCards Bios may hang the EDD probe,
* so give a hint if this happens .
*/
if ( ! be_quiet )
2008-01-30 15:33:03 +03:00
printf ( " Probing EDD (edd=off to disable)... " ) ;
2008-01-30 15:33:03 +03:00
2007-07-11 23:18:48 +04:00
for ( devno = 0x80 ; devno < 0x80 + EDD_MBR_SIG_MAX ; devno + + ) {
/*
* Scan the BIOS - supported hard disks and query EDD
* information . . .
*/
2008-07-18 16:35:37 +04:00
if ( ! get_edd_info ( devno , & ei )
& & boot_params . eddbuf_entries < EDDMAXNR ) {
2007-07-11 23:18:48 +04:00
memcpy ( edp , & ei , sizeof ei ) ;
edp + + ;
boot_params . eddbuf_entries + + ;
}
2007-08-15 04:36:00 +04:00
if ( do_mbr & & ! read_mbr_sig ( devno , & ei , mbrptr + + ) )
boot_params . edd_mbr_sig_buf_entries = devno - 0x80 + 1 ;
2007-07-11 23:18:48 +04:00
}
2008-01-30 15:33:03 +03:00
if ( ! be_quiet )
2008-01-30 15:33:03 +03:00
printf ( " ok \n " ) ;
2007-07-11 23:18:48 +04:00
}
# endif