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