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