xref: /OK3568_Linux_fs/kernel/arch/x86/boot/edd.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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