|
824
by Colin Watson
* Use EDD information if available to help generate a more correct |
1 |
--- a/lib/device.c 2008-04-02 13:49:57.000000000 +0100
|
2 |
+++ b/lib/device.c 2008-04-02 13:49:29.000000000 +0100
|
|
3 |
@@ -35,6 +35,11 @@
|
|
4 |
#include <errno.h> |
|
5 |
#include <limits.h> |
|
6 |
#include <stdarg.h> |
|
7 |
+#include <dirent.h>
|
|
8 |
+
|
|
9 |
+#define EDD_MBR_SIG_OFFSET 0x1B8
|
|
10 |
+
|
|
11 |
+typedef unsigned int grub_uint32_t;
|
|
12 |
||
13 |
#ifdef __linux__ |
|
14 |
# if !defined(__GLIBC__) || \ |
|
15 |
@@ -400,6 +405,92 @@
|
|
16 |
close (fd); |
|
17 |
} |
|
18 |
||
19 |
+/* Read MBR signatures from EDD, if available. */
|
|
20 |
+static void
|
|
21 |
+read_edd (grub_uint32_t *mbr_sig, char *mbr_sig_avail)
|
|
22 |
+{
|
|
23 |
+#ifdef __linux__
|
|
24 |
+ DIR *edd = opendir ("/sys/firmware/edd");
|
|
25 |
+ struct dirent *edd_entry;
|
|
26 |
+
|
|
27 |
+ if (! edd)
|
|
28 |
+ return;
|
|
29 |
+
|
|
30 |
+ while ((edd_entry = readdir (edd)))
|
|
31 |
+ {
|
|
32 |
+ char bios_dev_str[4];
|
|
33 |
+ int bios_dev;
|
|
34 |
+ char *signature_name;
|
|
35 |
+ FILE *signature_file;
|
|
36 |
+ char line[16];
|
|
37 |
+
|
|
38 |
+ if (strcmp (edd_entry->d_name, ".") == 0 ||
|
|
39 |
+ strcmp (edd_entry->d_name, "..") == 0)
|
|
40 |
+ continue;
|
|
41 |
+
|
|
42 |
+ if (sscanf (edd_entry->d_name, "int13_dev%3s", bios_dev_str) != 1)
|
|
43 |
+ continue;
|
|
44 |
+ errno = 0;
|
|
45 |
+ bios_dev = strtol (bios_dev_str, NULL, 16);
|
|
46 |
+ if (errno || bios_dev < 0 || bios_dev >= NUM_DISKS)
|
|
47 |
+ continue;
|
|
48 |
+
|
|
49 |
+ signature_name = malloc (strlen ("/sys/firmware/edd/") +
|
|
50 |
+ strlen (edd_entry->d_name) +
|
|
51 |
+ strlen ("/mbr_signature") + 1);
|
|
52 |
+ if (! signature_name)
|
|
53 |
+ continue;
|
|
54 |
+ sprintf (signature_name, "/sys/firmware/edd/%s/mbr_signature",
|
|
55 |
+ edd_entry->d_name);
|
|
56 |
+ signature_file = fopen (signature_name, "r");
|
|
57 |
+ if (! signature_file)
|
|
58 |
+ {
|
|
59 |
+ free (signature_name);
|
|
60 |
+ continue;
|
|
61 |
+ }
|
|
62 |
+
|
|
63 |
+ *line = 0;
|
|
64 |
+ fgets (line, 16, signature_file);
|
|
65 |
+ if (*line == '0' && *(line + 1) == 'x')
|
|
66 |
+ {
|
|
67 |
+ mbr_sig[bios_dev] = (grub_uint32_t) strtol(line, NULL, 16);
|
|
68 |
+ mbr_sig_avail[bios_dev] = 1;
|
|
69 |
+ }
|
|
70 |
+
|
|
71 |
+ fclose (signature_file);
|
|
72 |
+ free (signature_name);
|
|
73 |
+ }
|
|
74 |
+
|
|
75 |
+ closedir (edd);
|
|
76 |
+#endif /* __linux__ */
|
|
77 |
+}
|
|
78 |
+
|
|
79 |
+static int
|
|
80 |
+read_device_mbr_sig (char *device, grub_uint32_t *mbr)
|
|
81 |
+{
|
|
82 |
+#ifdef __linux__
|
|
83 |
+ char buf[512];
|
|
84 |
+ FILE *fp;
|
|
85 |
+
|
|
86 |
+ if (*device == 0)
|
|
87 |
+ return 0;
|
|
88 |
+
|
|
89 |
+ fp = fopen (device, "r");
|
|
90 |
+ if (! fp)
|
|
91 |
+ return 0;
|
|
92 |
+ if (fread (buf, 1, 512, fp) != 512)
|
|
93 |
+ {
|
|
94 |
+ fclose (fp);
|
|
95 |
+ return 0;
|
|
96 |
+ }
|
|
97 |
+
|
|
98 |
+ *mbr = *(grub_uint32_t *) &buf[EDD_MBR_SIG_OFFSET];
|
|
99 |
+ return 1;
|
|
100 |
+#else
|
|
101 |
+ return 0;
|
|
102 |
+#endif /* __linux__ */
|
|
103 |
+}
|
|
104 |
+
|
|
105 |
#ifdef __linux__ |
|
106 |
/* Check if we have devfs support. */ |
|
107 |
static int |
|
108 |
@@ -788,6 +879,8 @@
|
|
109 |
int i; |
|
110 |
int num_hd = 0; |
|
111 |
FILE *fp = 0; |
|
112 |
+ grub_uint32_t *edd_mbr_sig = 0;
|
|
113 |
+ char *edd_mbr_sig_avail = 0;
|
|
114 |
||
115 |
assert (map); |
|
116 |
assert (*map == 0); |
|
117 |
@@ -820,6 +913,17 @@
|
|
118 |
"Probing devices to guess BIOS drives. " |
|
119 |
"This may take a long time.\n"); |
|
120 |
||
121 |
+ /* Probe EDD, if available, to get more accurate device mapping. */
|
|
122 |
+ edd_mbr_sig = malloc (NUM_DISKS * sizeof (grub_uint32_t));
|
|
123 |
+ assert (edd_mbr_sig);
|
|
124 |
+ edd_mbr_sig_avail = malloc (NUM_DISKS * sizeof (char));
|
|
125 |
+ assert (edd_mbr_sig_avail);
|
|
126 |
+
|
|
127 |
+ for (i = 0; i < NUM_DISKS; i++)
|
|
128 |
+ edd_mbr_sig_avail[i] = 0;
|
|
129 |
+
|
|
130 |
+ read_edd (edd_mbr_sig, edd_mbr_sig_avail);
|
|
131 |
+
|
|
132 |
if (map_file) |
|
133 |
/* Try to open the device map file to write the probed data. */ |
|
134 |
fp = fopen (map_file, "w"); |
|
135 |
@@ -862,20 +966,12 @@
|
|
136 |
strcat (name, "/disc"); |
|
137 |
(*map)[num_hd + 0x80] = strdup (name); |
|
138 |
assert ((*map)[num_hd + 0x80]); |
|
139 |
-
|
|
140 |
- /* If the device map file is opened, write the map. */
|
|
141 |
- if (fp)
|
|
142 |
- fprintf (fp, "(hd%d)\t%s\n", num_hd, name);
|
|
143 |
} |
|
144 |
||
145 |
num_hd++; |
|
146 |
} |
|
147 |
||
148 |
- /* OK, close the device map file if opened. */
|
|
149 |
- if (fp)
|
|
150 |
- fclose (fp);
|
|
151 |
-
|
|
152 |
- return 1;
|
|
153 |
+ goto edd_reorder;
|
|
154 |
} |
|
155 |
#endif /* __linux__ */ |
|
156 |
||
157 |
@@ -891,10 +987,6 @@
|
|
158 |
(*map)[num_hd + 0x80] = strdup (name); |
|
159 |
assert ((*map)[num_hd + 0x80]); |
|
160 |
||
161 |
- /* If the device map file is opened, write the map. */
|
|
162 |
- if (fp)
|
|
163 |
- fprintf (fp, "(hd%d)\t%s\n", num_hd, name);
|
|
164 |
-
|
|
165 |
num_hd++; |
|
166 |
} |
|
167 |
} |
|
168 |
@@ -911,10 +1003,6 @@
|
|
169 |
(*map)[num_hd + 0x80] = strdup (name); |
|
170 |
assert ((*map)[num_hd + 0x80]); |
|
171 |
||
172 |
- /* If the device map file is opened, write the map. */
|
|
173 |
- if (fp)
|
|
174 |
- fprintf (fp, "(hd%d)\t%s\n", num_hd, name);
|
|
175 |
-
|
|
176 |
num_hd++; |
|
177 |
} |
|
178 |
} |
|
179 |
@@ -931,10 +1019,6 @@
|
|
180 |
(*map)[num_hd + 0x80] = strdup (name); |
|
181 |
assert ((*map)[num_hd + 0x80]); |
|
182 |
||
183 |
- /* If the device map file is opened, write the map. */
|
|
184 |
- if (fp)
|
|
185 |
- fprintf (fp, "(hd%d)\t%s\n", num_hd, name);
|
|
186 |
-
|
|
187 |
num_hd++; |
|
188 |
} |
|
189 |
} |
|
190 |
@@ -951,10 +1035,6 @@
|
|
191 |
(*map)[num_hd + 0x80] = strdup (name); |
|
192 |
assert ((*map)[num_hd + 0x80]); |
|
193 |
||
194 |
- /* If the device map file is opened, write the map. */
|
|
195 |
- if (fp)
|
|
196 |
- fprintf (fp, "(hd%d)\t%s\n", num_hd, name);
|
|
197 |
-
|
|
198 |
num_hd++; |
|
199 |
} |
|
200 |
} |
|
201 |
@@ -980,10 +1060,6 @@
|
|
202 |
(*map)[num_hd + 0x80] = strdup (name); |
|
203 |
assert ((*map)[num_hd + 0x80]); |
|
204 |
||
205 |
- /* If the device map file is opened, write the map. */
|
|
206 |
- if (fp)
|
|
207 |
- fprintf (fp, "(hd%d)\t%s\n", num_hd, name);
|
|
208 |
-
|
|
209 |
num_hd++; |
|
210 |
} |
|
211 |
} |
|
212 |
@@ -1003,10 +1079,6 @@
|
|
213 |
{
|
|
214 |
(*map)[num_hd + 0x80] = strdup (name); |
|
215 |
assert ((*map)[num_hd + 0x80]); |
|
216 |
-
|
|
217 |
- /* If the device map file is opened, write the map. */
|
|
218 |
- if (fp)
|
|
219 |
- fprintf (fp, "(hd%d)\t%s\n", num_hd, name);
|
|
220 |
||
221 |
num_hd++; |
|
222 |
} |
|
223 |
@@ -1037,10 +1109,6 @@
|
|
224 |
(*map)[num_hd + 0x80] = strdup (name); |
|
225 |
assert ((*map)[num_hd + 0x80]); |
|
226 |
||
227 |
- /* If the device map file is opened, write the map. */
|
|
228 |
- if (fp)
|
|
229 |
- fprintf (fp, "(hd%d)\t%s\n", num_hd, name);
|
|
230 |
-
|
|
231 |
num_hd++; |
|
232 |
} |
|
233 |
} |
|
234 |
@@ -1071,10 +1139,6 @@
|
|
235 |
(*map)[num_hd + 0x80] = strdup (name); |
|
236 |
assert ((*map)[num_hd + 0x80]); |
|
237 |
||
238 |
- /* If the device map file is opened, write the map. */
|
|
239 |
- if (fp)
|
|
240 |
- fprintf (fp, "(hd%d)\t%s\n", num_hd, name);
|
|
241 |
-
|
|
242 |
num_hd++; |
|
243 |
} |
|
244 |
} |
|
245 |
@@ -1082,10 +1146,184 @@
|
|
246 |
} |
|
247 |
#endif /* __linux__ */ |
|
248 |
||
249 |
+edd_reorder:
|
|
250 |
+ /* If any devices have unique matching MBR signatures, then we can be
|
|
251 |
+ sure of its BIOS ID, so use that.
|
|
252 |
+
|
|
253 |
+ If some devices match an MBR signature, but the signature is not
|
|
254 |
+ unique, then try to match those devices up with their signatures in
|
|
255 |
+ order to maximize our probability of being correct.
|
|
256 |
+
|
|
257 |
+ Any devices that remain fill in the remaining slots in the same order
|
|
258 |
+ as they were detected above.
|
|
259 |
+
|
|
260 |
+ The algorithm that follows could probably be faster, but correctness is
|
|
261 |
+ more important. */
|
|
262 |
+ {
|
|
263 |
+ char *unique_edd = malloc (NUM_DISKS * sizeof (char));
|
|
264 |
+ char *fixed = malloc (NUM_DISKS * sizeof (char));
|
|
265 |
+ grub_uint32_t *device_mbr_sig = malloc (NUM_DISKS *
|
|
266 |
+ sizeof (grub_uint32_t));
|
|
267 |
+ char *device_mbr_sig_avail = malloc (NUM_DISKS * sizeof (char));
|
|
268 |
+
|
|
269 |
+ auto void copy_device (int src, int dest);
|
|
270 |
+ auto void move_device (int src, int dest);
|
|
271 |
+
|
|
272 |
+ /* Copy device at position SRC to position DEST. */
|
|
273 |
+ auto void copy_device (int src, int dest)
|
|
274 |
+ {
|
|
275 |
+ (*map)[dest] = (*map)[src];
|
|
276 |
+ device_mbr_sig[dest] = device_mbr_sig[src];
|
|
277 |
+ device_mbr_sig_avail[dest] = device_mbr_sig_avail[src];
|
|
278 |
+ }
|
|
279 |
+
|
|
280 |
+ /* Move the device at position SRC to position DEST, shifting everything
|
|
281 |
+ else along to make room. Relies on devices starting at 0x80 so that
|
|
282 |
+ we always have a temporary slot available at position 0. */
|
|
283 |
+ auto void move_device (int src, int dest)
|
|
284 |
+ {
|
|
285 |
+ int cur = src;
|
|
286 |
+ int step, limit;
|
|
287 |
+
|
|
288 |
+ assert (! fixed[dest]);
|
|
289 |
+ assert (! fixed[src]);
|
|
290 |
+
|
|
291 |
+ fixed[dest] = 1;
|
|
292 |
+
|
|
293 |
+ if (dest == src)
|
|
294 |
+ return;
|
|
295 |
+
|
|
296 |
+ copy_device (src, 0);
|
|
297 |
+
|
|
298 |
+ if (dest < src)
|
|
299 |
+ {
|
|
300 |
+ step = -1;
|
|
301 |
+ limit = 0x80 - 1;
|
|
302 |
+ }
|
|
303 |
+ else
|
|
304 |
+ {
|
|
305 |
+ step = 1;
|
|
306 |
+ limit = NUM_DISKS;
|
|
307 |
+ }
|
|
308 |
+ for (;;)
|
|
309 |
+ {
|
|
310 |
+ int walk, next = 0;
|
|
311 |
+ for (walk = cur + step; walk != limit; walk += step)
|
|
312 |
+ if (walk == dest || ! fixed[walk])
|
|
313 |
+ {
|
|
314 |
+ next = walk;
|
|
315 |
+ break;
|
|
316 |
+ }
|
|
317 |
+ if (walk == limit)
|
|
318 |
+ break;
|
|
319 |
+ copy_device (next, cur);
|
|
320 |
+ if (next == dest)
|
|
321 |
+ break;
|
|
322 |
+ cur = next;
|
|
323 |
+ }
|
|
324 |
+
|
|
325 |
+ copy_device (0, dest);
|
|
326 |
+ (*map)[0] = 0;
|
|
327 |
+ device_mbr_sig[0] = 0;
|
|
328 |
+ device_mbr_sig_avail[0] = 0;
|
|
329 |
+ }
|
|
330 |
+
|
|
331 |
+ if (unique_edd && fixed && device_mbr_sig && device_mbr_sig_avail)
|
|
332 |
+ {
|
|
333 |
+ int other;
|
|
334 |
+
|
|
335 |
+ for (i = 0; i < NUM_DISKS; i++)
|
|
336 |
+ {
|
|
337 |
+ fixed[i] = 0;
|
|
338 |
+ device_mbr_sig[i] = 0;
|
|
339 |
+ device_mbr_sig_avail[i] = 0;
|
|
340 |
+ }
|
|
341 |
+
|
|
342 |
+ /* Read all the signatures from their devices. */
|
|
343 |
+ for (i = 0x80; i < NUM_DISKS; i++)
|
|
344 |
+ {
|
|
345 |
+ if ((*map)[i])
|
|
346 |
+ device_mbr_sig_avail[i] = read_device_mbr_sig
|
|
347 |
+ ((*map)[i], &device_mbr_sig[i]);
|
|
348 |
+ else
|
|
349 |
+ device_mbr_sig_avail[i] = 0;
|
|
350 |
+ }
|
|
351 |
+
|
|
352 |
+ /* Check for unique EDD signatures. */
|
|
353 |
+ for (i = 0x80; i < NUM_DISKS; i++)
|
|
354 |
+ {
|
|
355 |
+ if (! edd_mbr_sig_avail[i])
|
|
356 |
+ {
|
|
357 |
+ unique_edd[i] = 0;
|
|
358 |
+ continue;
|
|
359 |
+ }
|
|
360 |
+
|
|
361 |
+ unique_edd[i] = 1;
|
|
362 |
+ for (other = 0x80; other < NUM_DISKS; other++)
|
|
363 |
+ {
|
|
364 |
+ if (i != other && edd_mbr_sig[i] == edd_mbr_sig[other])
|
|
365 |
+ {
|
|
366 |
+ unique_edd[i] = 0;
|
|
367 |
+ break;
|
|
368 |
+ }
|
|
369 |
+ }
|
|
370 |
+ }
|
|
371 |
+
|
|
372 |
+ /* Move the disks with unique EDD signatures into place. */
|
|
373 |
+ for (i = 0x80; i < NUM_DISKS; i++)
|
|
374 |
+ {
|
|
375 |
+ if (! unique_edd[i])
|
|
376 |
+ continue;
|
|
377 |
+
|
|
378 |
+ for (other = 0x80; other < NUM_DISKS; other++)
|
|
379 |
+ {
|
|
380 |
+ if (! fixed[other] && edd_mbr_sig[i] == device_mbr_sig[other])
|
|
381 |
+ {
|
|
382 |
+ move_device (other, i);
|
|
383 |
+ break;
|
|
384 |
+ }
|
|
385 |
+ }
|
|
386 |
+ }
|
|
387 |
+
|
|
388 |
+ /* Move the disks with non-unique EDD signatures into place. */
|
|
389 |
+ for (i = 0x80; i < NUM_DISKS; i++)
|
|
390 |
+ {
|
|
391 |
+ if (fixed[i] || ! edd_mbr_sig_avail[i])
|
|
392 |
+ continue;
|
|
393 |
+
|
|
394 |
+ for (other = 0x80; other < NUM_DISKS; other++)
|
|
395 |
+ {
|
|
396 |
+ if (! fixed[other] && edd_mbr_sig[i] == device_mbr_sig[other])
|
|
397 |
+ {
|
|
398 |
+ move_device (other, i);
|
|
399 |
+ break;
|
|
400 |
+ }
|
|
401 |
+ }
|
|
402 |
+ }
|
|
403 |
+ }
|
|
404 |
+ free (device_mbr_sig_avail);
|
|
405 |
+ free (device_mbr_sig);
|
|
406 |
+ free (fixed);
|
|
407 |
+ free (unique_edd);
|
|
408 |
+ }
|
|
409 |
+
|
|
410 |
+ /* If the device map file is opened, write the map. */
|
|
411 |
+ if (fp)
|
|
412 |
+ {
|
|
413 |
+ for (i = 0x80; i < NUM_DISKS; i++)
|
|
414 |
+ {
|
|
415 |
+ if ((*map)[i])
|
|
416 |
+ fprintf (fp, "(hd%d)\t%s\n", i - 0x80, (*map)[i]);
|
|
417 |
+ }
|
|
418 |
+ }
|
|
419 |
+
|
|
420 |
/* OK, close the device map file if opened. */ |
|
421 |
if (fp) |
|
422 |
fclose (fp); |
|
423 |
||
424 |
+ free (edd_mbr_sig_avail);
|
|
425 |
+ free (edd_mbr_sig);
|
|
426 |
+
|
|
427 |
return 1; |
|
428 |
} |
|
429 |