aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386/boot/edd.c
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2007-07-11 15:18:48 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-12 13:55:55 -0400
commitd13444a5a53b0159e6316a7a7be9890143a5af71 (patch)
tree5f1256618c62add74f27292e0f3bf1ab0df8345e /arch/i386/boot/edd.c
parent31b54f40e12e4d04941762be6615edaf3c6ed811 (diff)
EDD probing code for the new x86 setup code
Probe EDD and MBR signatures, in order to make it easier to map physical hard drives to BIOS drives. Signed-off-by: H. Peter Anvin <hpa@zytor.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/i386/boot/edd.c')
-rw-r--r--arch/i386/boot/edd.c196
1 files changed, 196 insertions, 0 deletions
diff --git a/arch/i386/boot/edd.c b/arch/i386/boot/edd.c
new file mode 100644
index 000000000000..25a282494f4c
--- /dev/null
+++ b/arch/i386/boot/edd.c
@@ -0,0 +1,196 @@
1/* -*- linux-c -*- ------------------------------------------------------- *
2 *
3 * Copyright (C) 1991, 1992 Linus Torvalds
4 * Copyright 2007 rPath, Inc. - All Rights Reserved
5 *
6 * This file is part of the Linux kernel, and is made available under
7 * the terms of the GNU General Public License version 2.
8 *
9 * ----------------------------------------------------------------------- */
10
11/*
12 * arch/i386/boot/edd.c
13 *
14 * Get EDD BIOS disk information
15 */
16
17#include "boot.h"
18#include <linux/edd.h>
19
20#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
21
22struct edd_dapa {
23 u8 pkt_size;
24 u8 rsvd;
25 u16 sector_cnt;
26 u16 buf_off, buf_seg;
27 u64 lba;
28 u64 buf_lin_addr;
29};
30
31/*
32 * Read the MBR (first sector) from a specific device.
33 */
34static int read_mbr(u8 devno, void *buf)
35{
36 struct edd_dapa dapa;
37 u16 ax, bx, cx, dx, si;
38
39 memset(&dapa, 0, sizeof dapa);
40 dapa.pkt_size = sizeof(dapa);
41 dapa.sector_cnt = 1;
42 dapa.buf_off = (size_t)buf;
43 dapa.buf_seg = ds();
44 /* dapa.lba = 0; */
45
46 ax = 0x4200; /* Extended Read */
47 si = (size_t)&dapa;
48 dx = devno;
49 asm("pushfl; stc; int $0x13; setc %%al; popfl"
50 : "+a" (ax), "+S" (si), "+d" (dx)
51 : "m" (dapa)
52 : "ebx", "ecx", "edi", "memory");
53
54 if (!(u8)ax)
55 return 0; /* OK */
56
57 ax = 0x0201; /* Legacy Read, one sector */
58 cx = 0x0001; /* Sector 0-0-1 */
59 dx = devno;
60 bx = (size_t)buf;
61 asm("pushfl; stc; int $0x13; setc %%al; popfl"
62 : "+a" (ax), "+c" (cx), "+d" (dx), "+b" (bx)
63 : : "esi", "edi", "memory");
64
65 return -(u8)ax; /* 0 or -1 */
66}
67
68static u32 read_mbr_sig(u8 devno, struct edd_info *ei)
69{
70 int sector_size;
71 char *mbrbuf_ptr, *mbrbuf_end;
72 u32 mbrsig;
73 u32 buf_base, mbr_base;
74 extern char _end[];
75 static char mbr_buf[1024];
76
77 sector_size = ei->params.bytes_per_sector;
78 if (!sector_size)
79 sector_size = 512; /* Best available guess */
80
81 buf_base = (ds() << 4) + (u32)&_end;
82 mbr_base = (buf_base+sector_size-1) & ~(sector_size-1);
83 mbrbuf_ptr = mbr_buf + (mbr_base-buf_base);
84 mbrbuf_end = mbrbuf_ptr + sector_size;
85
86 if (!(boot_params.hdr.loadflags & CAN_USE_HEAP))
87 return 0;
88 if (mbrbuf_end > (char *)(size_t)boot_params.hdr.heap_end_ptr)
89 return 0;
90
91 if (read_mbr(devno, mbrbuf_ptr))
92 return 0;
93
94 mbrsig = *(u32 *)&mbrbuf_ptr[EDD_MBR_SIG_OFFSET];
95 return mbrsig;
96}
97
98static int get_edd_info(u8 devno, struct edd_info *ei)
99{
100 u16 ax, bx, cx, dx, di;
101
102 memset(ei, 0, sizeof *ei);
103
104 /* Check Extensions Present */
105
106 ax = 0x4100;
107 bx = EDDMAGIC1;
108 dx = devno;
109 asm("pushfl; stc; int $0x13; setc %%al; popfl"
110 : "+a" (ax), "+b" (bx), "=c" (cx), "+d" (dx)
111 : : "esi", "edi");
112
113 if ((u8)ax)
114 return -1; /* No extended information */
115
116 if (bx != EDDMAGIC2)
117 return -1;
118
119 ei->device = devno;
120 ei->version = ax >> 8; /* EDD version number */
121 ei->interface_support = cx; /* EDD functionality subsets */
122
123 /* Extended Get Device Parameters */
124
125 ei->params.length = sizeof(ei->params);
126 ax = 0x4800;
127 dx = devno;
128 asm("pushfl; int $0x13; popfl"
129 : "+a" (ax), "+d" (dx)
130 : "S" (&ei->params)
131 : "ebx", "ecx", "edi");
132
133 /* Get legacy CHS parameters */
134
135 /* Ralf Brown recommends setting ES:DI to 0:0 */
136 ax = 0x0800;
137 dx = devno;
138 di = 0;
139 asm("pushw %%es; "
140 "movw %%di,%%es; "
141 "pushfl; stc; int $0x13; setc %%al; popfl; "
142 "popw %%es"
143 : "+a" (ax), "=b" (bx), "=c" (cx), "+d" (dx), "+D" (di)
144 : : "esi");
145
146 if ((u8)ax == 0) {
147 ei->legacy_max_cylinder = (cx >> 8) + ((cx & 0xc0) << 2);
148 ei->legacy_max_head = dx >> 8;
149 ei->legacy_sectors_per_track = cx & 0x3f;
150 }
151
152 return 0;
153}
154
155void query_edd(void)
156{
157 char eddarg[8];
158 int do_mbr = 1;
159 int do_edd = 1;
160 int devno;
161 struct edd_info ei, *edp;
162
163 if (cmdline_find_option("edd", eddarg, sizeof eddarg) > 0) {
164 if (!strcmp(eddarg, "skipmbr") || !strcmp(eddarg, "skip"))
165 do_mbr = 0;
166 else if (!strcmp(eddarg, "off"))
167 do_edd = 0;
168 }
169
170 edp = (struct edd_info *)boot_params.eddbuf;
171
172 if (!do_edd)
173 return;
174
175 for (devno = 0x80; devno < 0x80+EDD_MBR_SIG_MAX; devno++) {
176 /*
177 * Scan the BIOS-supported hard disks and query EDD
178 * information...
179 */
180 get_edd_info(devno, &ei);
181
182 if (boot_params.eddbuf_entries < EDDMAXNR) {
183 memcpy(edp, &ei, sizeof ei);
184 edp++;
185 boot_params.eddbuf_entries++;
186 }
187
188 if (do_mbr) {
189 u32 mbr_sig;
190 mbr_sig = read_mbr_sig(devno, &ei);
191 boot_params.edd_mbr_sig_buffer[devno-0x80] = mbr_sig;
192 }
193 }
194}
195
196#endif