2007-07-11 23:18:48 +04:00
/* -*- 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.
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
* arch / i386 / boot / edd . c
*
* 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 )
{
2007-08-14 03:27:42 +04:00
u16 ax , bx , cx , dx ;
2007-07-11 23:18:48 +04:00
ax = 0x0201 ; /* Legacy Read, one sector */
cx = 0x0001 ; /* Sector 0-0-1 */
dx = devno ;
bx = ( size_t ) buf ;
2007-08-23 03:28:01 +04:00
asm volatile ( " pushfl; stc; int $0x13; setc %%al; popfl "
: " +a " ( ax ) , " +c " ( cx ) , " +d " ( dx ) , " +b " ( bx )
: : " esi " , " edi " , " memory " ) ;
2007-07-11 23:18:48 +04:00
return - ( u8 ) ax ; /* 0 or -1 */
}
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 [ ] ;
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
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 ] ;
return 0 ;
2007-07-11 23:18:48 +04:00
}
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 "
2007-08-02 21:45:49 +04:00
: " +a " ( ax ) , " +d " ( dx ) , " =m " ( ei - > params )
2007-07-11 23:18:48 +04:00
: " 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 ;
int do_edd = 1 ;
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 ) {
if ( ! strcmp ( eddarg , " skipmbr " ) | | ! strcmp ( eddarg , " skip " ) )
do_mbr = 0 ;
else if ( ! strcmp ( eddarg , " off " ) )
do_edd = 0 ;
}
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 . . .
*/
get_edd_info ( devno , & ei ) ;
if ( boot_params . eddbuf_entries < EDDMAXNR ) {
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