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