162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* -*- linux-c -*- ------------------------------------------------------- * 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 1991, 1992 Linus Torvalds 562306a36Sopenharmony_ci * Copyright 2007 rPath, Inc. - All Rights Reserved 662306a36Sopenharmony_ci * Copyright 2009 Intel Corporation; author H. Peter Anvin 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * ----------------------------------------------------------------------- */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/* 1162306a36Sopenharmony_ci * Get EDD BIOS disk information 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "boot.h" 1562306a36Sopenharmony_ci#include <linux/edd.h> 1662306a36Sopenharmony_ci#include "string.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * Read the MBR (first sector) from a specific device. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_cistatic int read_mbr(u8 devno, void *buf) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci struct biosregs ireg, oreg; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci initregs(&ireg); 2862306a36Sopenharmony_ci ireg.ax = 0x0201; /* Legacy Read, one sector */ 2962306a36Sopenharmony_ci ireg.cx = 0x0001; /* Sector 0-0-1 */ 3062306a36Sopenharmony_ci ireg.dl = devno; 3162306a36Sopenharmony_ci ireg.bx = (size_t)buf; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci intcall(0x13, &ireg, &oreg); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci return -(oreg.eflags & X86_EFLAGS_CF); /* 0 or -1 */ 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic u32 read_mbr_sig(u8 devno, struct edd_info *ei, u32 *mbrsig) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci int sector_size; 4162306a36Sopenharmony_ci char *mbrbuf_ptr, *mbrbuf_end; 4262306a36Sopenharmony_ci u32 buf_base, mbr_base; 4362306a36Sopenharmony_ci extern char _end[]; 4462306a36Sopenharmony_ci u16 mbr_magic; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci sector_size = ei->params.bytes_per_sector; 4762306a36Sopenharmony_ci if (!sector_size) 4862306a36Sopenharmony_ci sector_size = 512; /* Best available guess */ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci /* Produce a naturally aligned buffer on the heap */ 5162306a36Sopenharmony_ci buf_base = (ds() << 4) + (u32)&_end; 5262306a36Sopenharmony_ci mbr_base = (buf_base+sector_size-1) & ~(sector_size-1); 5362306a36Sopenharmony_ci mbrbuf_ptr = _end + (mbr_base-buf_base); 5462306a36Sopenharmony_ci mbrbuf_end = mbrbuf_ptr + sector_size; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* Make sure we actually have space on the heap... */ 5762306a36Sopenharmony_ci if (!(boot_params.hdr.loadflags & CAN_USE_HEAP)) 5862306a36Sopenharmony_ci return -1; 5962306a36Sopenharmony_ci if (mbrbuf_end > (char *)(size_t)boot_params.hdr.heap_end_ptr) 6062306a36Sopenharmony_ci return -1; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci memset(mbrbuf_ptr, 0, sector_size); 6362306a36Sopenharmony_ci if (read_mbr(devno, mbrbuf_ptr)) 6462306a36Sopenharmony_ci return -1; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci *mbrsig = *(u32 *)&mbrbuf_ptr[EDD_MBR_SIG_OFFSET]; 6762306a36Sopenharmony_ci mbr_magic = *(u16 *)&mbrbuf_ptr[510]; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* check for valid MBR magic */ 7062306a36Sopenharmony_ci return mbr_magic == 0xAA55 ? 0 : -1; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int get_edd_info(u8 devno, struct edd_info *ei) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct biosregs ireg, oreg; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci memset(ei, 0, sizeof(*ei)); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* Check Extensions Present */ 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci initregs(&ireg); 8262306a36Sopenharmony_ci ireg.ah = 0x41; 8362306a36Sopenharmony_ci ireg.bx = EDDMAGIC1; 8462306a36Sopenharmony_ci ireg.dl = devno; 8562306a36Sopenharmony_ci intcall(0x13, &ireg, &oreg); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (oreg.eflags & X86_EFLAGS_CF) 8862306a36Sopenharmony_ci return -1; /* No extended information */ 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (oreg.bx != EDDMAGIC2) 9162306a36Sopenharmony_ci return -1; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci ei->device = devno; 9462306a36Sopenharmony_ci ei->version = oreg.ah; /* EDD version number */ 9562306a36Sopenharmony_ci ei->interface_support = oreg.cx; /* EDD functionality subsets */ 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* Extended Get Device Parameters */ 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci ei->params.length = sizeof(ei->params); 10062306a36Sopenharmony_ci ireg.ah = 0x48; 10162306a36Sopenharmony_ci ireg.si = (size_t)&ei->params; 10262306a36Sopenharmony_ci intcall(0x13, &ireg, &oreg); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* Get legacy CHS parameters */ 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* Ralf Brown recommends setting ES:DI to 0:0 */ 10762306a36Sopenharmony_ci ireg.ah = 0x08; 10862306a36Sopenharmony_ci ireg.es = 0; 10962306a36Sopenharmony_ci intcall(0x13, &ireg, &oreg); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (!(oreg.eflags & X86_EFLAGS_CF)) { 11262306a36Sopenharmony_ci ei->legacy_max_cylinder = oreg.ch + ((oreg.cl & 0xc0) << 2); 11362306a36Sopenharmony_ci ei->legacy_max_head = oreg.dh; 11462306a36Sopenharmony_ci ei->legacy_sectors_per_track = oreg.cl & 0x3f; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_civoid query_edd(void) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci char eddarg[8]; 12362306a36Sopenharmony_ci int do_mbr = 1; 12462306a36Sopenharmony_ci#ifdef CONFIG_EDD_OFF 12562306a36Sopenharmony_ci int do_edd = 0; 12662306a36Sopenharmony_ci#else 12762306a36Sopenharmony_ci int do_edd = 1; 12862306a36Sopenharmony_ci#endif 12962306a36Sopenharmony_ci int be_quiet; 13062306a36Sopenharmony_ci int devno; 13162306a36Sopenharmony_ci struct edd_info ei, *edp; 13262306a36Sopenharmony_ci u32 *mbrptr; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (cmdline_find_option("edd", eddarg, sizeof(eddarg)) > 0) { 13562306a36Sopenharmony_ci if (!strcmp(eddarg, "skipmbr") || !strcmp(eddarg, "skip")) { 13662306a36Sopenharmony_ci do_edd = 1; 13762306a36Sopenharmony_ci do_mbr = 0; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci else if (!strcmp(eddarg, "off")) 14062306a36Sopenharmony_ci do_edd = 0; 14162306a36Sopenharmony_ci else if (!strcmp(eddarg, "on")) 14262306a36Sopenharmony_ci do_edd = 1; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci be_quiet = cmdline_find_option_bool("quiet"); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci edp = boot_params.eddbuf; 14862306a36Sopenharmony_ci mbrptr = boot_params.edd_mbr_sig_buffer; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (!do_edd) 15162306a36Sopenharmony_ci return; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* Bugs in OnBoard or AddOnCards Bios may hang the EDD probe, 15462306a36Sopenharmony_ci * so give a hint if this happens. 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (!be_quiet) 15862306a36Sopenharmony_ci printf("Probing EDD (edd=off to disable)... "); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci for (devno = 0x80; devno < 0x80+EDD_MBR_SIG_MAX; devno++) { 16162306a36Sopenharmony_ci /* 16262306a36Sopenharmony_ci * Scan the BIOS-supported hard disks and query EDD 16362306a36Sopenharmony_ci * information... 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci if (!get_edd_info(devno, &ei) 16662306a36Sopenharmony_ci && boot_params.eddbuf_entries < EDDMAXNR) { 16762306a36Sopenharmony_ci memcpy(edp, &ei, sizeof(ei)); 16862306a36Sopenharmony_ci edp++; 16962306a36Sopenharmony_ci boot_params.eddbuf_entries++; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (do_mbr && !read_mbr_sig(devno, &ei, mbrptr++)) 17362306a36Sopenharmony_ci boot_params.edd_mbr_sig_buf_entries = devno-0x80+1; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (!be_quiet) 17762306a36Sopenharmony_ci printf("ok\n"); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci#endif 181