1 /* -*- linux-c -*- ------------------------------------------------------- * 2 * 3 * Copyright (C) 1991, 1992 Linus Torvalds 4 * Copyright 2007 rPath, Inc. - All Rights Reserved 5 * Copyright 2009 Intel Corporation; author H. Peter Anvin 6 * 7 * This file is part of the Linux kernel, and is made available under 8 * the terms of the GNU General Public License version 2. 9 * 10 * ----------------------------------------------------------------------- */ 11 12 /* 13 * Get EDD BIOS disk information 14 */ 15 16 #include "boot.h" 17 #include <linux/edd.h> 18 19 #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE) 20 21 /* 22 * Read the MBR (first sector) from a specific device. 23 */ 24 static int read_mbr(u8 devno, void *buf) 25 { 26 struct biosregs ireg, oreg; 27 28 initregs(&ireg); 29 ireg.ax = 0x0201; /* Legacy Read, one sector */ 30 ireg.cx = 0x0001; /* Sector 0-0-1 */ 31 ireg.dl = devno; 32 ireg.bx = (size_t)buf; 33 34 intcall(0x13, &ireg, &oreg); 35 36 return -(oreg.eflags & X86_EFLAGS_CF); /* 0 or -1 */ 37 } 38 39 static u32 read_mbr_sig(u8 devno, struct edd_info *ei, u32 *mbrsig) 40 { 41 int sector_size; 42 char *mbrbuf_ptr, *mbrbuf_end; 43 u32 buf_base, mbr_base; 44 extern char _end[]; 45 u16 mbr_magic; 46 47 sector_size = ei->params.bytes_per_sector; 48 if (!sector_size) 49 sector_size = 512; /* Best available guess */ 50 51 /* Produce a naturally aligned buffer on the heap */ 52 buf_base = (ds() << 4) + (u32)&_end; 53 mbr_base = (buf_base+sector_size-1) & ~(sector_size-1); 54 mbrbuf_ptr = _end + (mbr_base-buf_base); 55 mbrbuf_end = mbrbuf_ptr + sector_size; 56 57 /* Make sure we actually have space on the heap... */ 58 if (!(boot_params.hdr.loadflags & CAN_USE_HEAP)) 59 return -1; 60 if (mbrbuf_end > (char *)(size_t)boot_params.hdr.heap_end_ptr) 61 return -1; 62 63 memset(mbrbuf_ptr, 0, sector_size); 64 if (read_mbr(devno, mbrbuf_ptr)) 65 return -1; 66 67 *mbrsig = *(u32 *)&mbrbuf_ptr[EDD_MBR_SIG_OFFSET]; 68 mbr_magic = *(u16 *)&mbrbuf_ptr[510]; 69 70 /* check for valid MBR magic */ 71 return mbr_magic == 0xAA55 ? 0 : -1; 72 } 73 74 static int get_edd_info(u8 devno, struct edd_info *ei) 75 { 76 struct biosregs ireg, oreg; 77 78 memset(ei, 0, sizeof *ei); 79 80 /* Check Extensions Present */ 81 82 initregs(&ireg); 83 ireg.ah = 0x41; 84 ireg.bx = EDDMAGIC1; 85 ireg.dl = devno; 86 intcall(0x13, &ireg, &oreg); 87 88 if (oreg.eflags & X86_EFLAGS_CF) 89 return -1; /* No extended information */ 90 91 if (oreg.bx != EDDMAGIC2) 92 return -1; 93 94 ei->device = devno; 95 ei->version = oreg.ah; /* EDD version number */ 96 ei->interface_support = oreg.cx; /* EDD functionality subsets */ 97 98 /* Extended Get Device Parameters */ 99 100 ei->params.length = sizeof(ei->params); 101 ireg.ah = 0x48; 102 ireg.si = (size_t)&ei->params; 103 intcall(0x13, &ireg, &oreg); 104 105 /* Get legacy CHS parameters */ 106 107 /* Ralf Brown recommends setting ES:DI to 0:0 */ 108 ireg.ah = 0x08; 109 ireg.es = 0; 110 intcall(0x13, &ireg, &oreg); 111 112 if (!(oreg.eflags & X86_EFLAGS_CF)) { 113 ei->legacy_max_cylinder = oreg.ch + ((oreg.cl & 0xc0) << 2); 114 ei->legacy_max_head = oreg.dh; 115 ei->legacy_sectors_per_track = oreg.cl & 0x3f; 116 } 117 118 return 0; 119 } 120 121 void query_edd(void) 122 { 123 char eddarg[8]; 124 int do_mbr = 1; 125 #ifdef CONFIG_EDD_OFF 126 int do_edd = 0; 127 #else 128 int do_edd = 1; 129 #endif 130 int be_quiet; 131 int devno; 132 struct edd_info ei, *edp; 133 u32 *mbrptr; 134 135 if (cmdline_find_option("edd", eddarg, sizeof eddarg) > 0) { 136 if (!strcmp(eddarg, "skipmbr") || !strcmp(eddarg, "skip")) { 137 do_edd = 1; 138 do_mbr = 0; 139 } 140 else if (!strcmp(eddarg, "off")) 141 do_edd = 0; 142 else if (!strcmp(eddarg, "on")) 143 do_edd = 1; 144 } 145 146 be_quiet = cmdline_find_option_bool("quiet"); 147 148 edp = boot_params.eddbuf; 149 mbrptr = boot_params.edd_mbr_sig_buffer; 150 151 if (!do_edd) 152 return; 153 154 /* Bugs in OnBoard or AddOnCards Bios may hang the EDD probe, 155 * so give a hint if this happens. 156 */ 157 158 if (!be_quiet) 159 printf("Probing EDD (edd=off to disable)... "); 160 161 for (devno = 0x80; devno < 0x80+EDD_MBR_SIG_MAX; devno++) { 162 /* 163 * Scan the BIOS-supported hard disks and query EDD 164 * information... 165 */ 166 if (!get_edd_info(devno, &ei) 167 && boot_params.eddbuf_entries < EDDMAXNR) { 168 memcpy(edp, &ei, sizeof ei); 169 edp++; 170 boot_params.eddbuf_entries++; 171 } 172 173 if (do_mbr && !read_mbr_sig(devno, &ei, mbrptr++)) 174 boot_params.edd_mbr_sig_buf_entries = devno-0x80+1; 175 } 176 177 if (!be_quiet) 178 printf("ok\n"); 179 } 180 181 #endif 182