]>
Commit | Line | Data |
---|---|---|
d13444a5 PA |
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 | /* | |
d13444a5 PA |
12 | * Get EDD BIOS disk information |
13 | */ | |
14 | ||
15 | #include "boot.h" | |
16 | #include <linux/edd.h> | |
17 | ||
18 | #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE) | |
19 | ||
d13444a5 PA |
20 | /* |
21 | * Read the MBR (first sector) from a specific device. | |
22 | */ | |
23 | static int read_mbr(u8 devno, void *buf) | |
24 | { | |
c1a6e2b0 | 25 | u16 ax, bx, cx, dx; |
d13444a5 PA |
26 | |
27 | ax = 0x0201; /* Legacy Read, one sector */ | |
28 | cx = 0x0001; /* Sector 0-0-1 */ | |
29 | dx = devno; | |
30 | bx = (size_t)buf; | |
b015124e PA |
31 | asm volatile("pushfl; stc; int $0x13; setc %%al; popfl" |
32 | : "+a" (ax), "+c" (cx), "+d" (dx), "+b" (bx) | |
33 | : : "esi", "edi", "memory"); | |
d13444a5 PA |
34 | |
35 | return -(u8)ax; /* 0 or -1 */ | |
36 | } | |
37 | ||
9a5f35d4 | 38 | static u32 read_mbr_sig(u8 devno, struct edd_info *ei, u32 *mbrsig) |
d13444a5 PA |
39 | { |
40 | int sector_size; | |
41 | char *mbrbuf_ptr, *mbrbuf_end; | |
d13444a5 PA |
42 | u32 buf_base, mbr_base; |
43 | extern char _end[]; | |
d13444a5 PA |
44 | |
45 | sector_size = ei->params.bytes_per_sector; | |
46 | if (!sector_size) | |
47 | sector_size = 512; /* Best available guess */ | |
48 | ||
ff659d13 | 49 | /* Produce a naturally aligned buffer on the heap */ |
d13444a5 PA |
50 | buf_base = (ds() << 4) + (u32)&_end; |
51 | mbr_base = (buf_base+sector_size-1) & ~(sector_size-1); | |
ff659d13 | 52 | mbrbuf_ptr = _end + (mbr_base-buf_base); |
d13444a5 PA |
53 | mbrbuf_end = mbrbuf_ptr + sector_size; |
54 | ||
ff659d13 | 55 | /* Make sure we actually have space on the heap... */ |
d13444a5 | 56 | if (!(boot_params.hdr.loadflags & CAN_USE_HEAP)) |
9a5f35d4 | 57 | return -1; |
d13444a5 | 58 | if (mbrbuf_end > (char *)(size_t)boot_params.hdr.heap_end_ptr) |
9a5f35d4 | 59 | return -1; |
d13444a5 PA |
60 | |
61 | if (read_mbr(devno, mbrbuf_ptr)) | |
9a5f35d4 | 62 | return -1; |
d13444a5 | 63 | |
9a5f35d4 PA |
64 | *mbrsig = *(u32 *)&mbrbuf_ptr[EDD_MBR_SIG_OFFSET]; |
65 | return 0; | |
d13444a5 PA |
66 | } |
67 | ||
68 | static int get_edd_info(u8 devno, struct edd_info *ei) | |
69 | { | |
70 | u16 ax, bx, cx, dx, di; | |
71 | ||
72 | memset(ei, 0, sizeof *ei); | |
73 | ||
74 | /* Check Extensions Present */ | |
75 | ||
76 | ax = 0x4100; | |
77 | bx = EDDMAGIC1; | |
78 | dx = devno; | |
79 | asm("pushfl; stc; int $0x13; setc %%al; popfl" | |
80 | : "+a" (ax), "+b" (bx), "=c" (cx), "+d" (dx) | |
81 | : : "esi", "edi"); | |
82 | ||
83 | if ((u8)ax) | |
84 | return -1; /* No extended information */ | |
85 | ||
86 | if (bx != EDDMAGIC2) | |
87 | return -1; | |
88 | ||
89 | ei->device = devno; | |
90 | ei->version = ax >> 8; /* EDD version number */ | |
91 | ei->interface_support = cx; /* EDD functionality subsets */ | |
92 | ||
93 | /* Extended Get Device Parameters */ | |
94 | ||
95 | ei->params.length = sizeof(ei->params); | |
96 | ax = 0x4800; | |
97 | dx = devno; | |
98 | asm("pushfl; int $0x13; popfl" | |
463c9a9f | 99 | : "+a" (ax), "+d" (dx), "=m" (ei->params) |
d13444a5 PA |
100 | : "S" (&ei->params) |
101 | : "ebx", "ecx", "edi"); | |
102 | ||
103 | /* Get legacy CHS parameters */ | |
104 | ||
105 | /* Ralf Brown recommends setting ES:DI to 0:0 */ | |
106 | ax = 0x0800; | |
107 | dx = devno; | |
108 | di = 0; | |
109 | asm("pushw %%es; " | |
110 | "movw %%di,%%es; " | |
111 | "pushfl; stc; int $0x13; setc %%al; popfl; " | |
112 | "popw %%es" | |
113 | : "+a" (ax), "=b" (bx), "=c" (cx), "+d" (dx), "+D" (di) | |
114 | : : "esi"); | |
115 | ||
116 | if ((u8)ax == 0) { | |
117 | ei->legacy_max_cylinder = (cx >> 8) + ((cx & 0xc0) << 2); | |
118 | ei->legacy_max_head = dx >> 8; | |
119 | ei->legacy_sectors_per_track = cx & 0x3f; | |
120 | } | |
121 | ||
122 | return 0; | |
123 | } | |
124 | ||
125 | void query_edd(void) | |
126 | { | |
127 | char eddarg[8]; | |
128 | int do_mbr = 1; | |
129 | int do_edd = 1; | |
e479c830 | 130 | int be_quiet; |
d13444a5 PA |
131 | int devno; |
132 | struct edd_info ei, *edp; | |
9a5f35d4 | 133 | u32 *mbrptr; |
d13444a5 PA |
134 | |
135 | if (cmdline_find_option("edd", eddarg, sizeof eddarg) > 0) { | |
136 | if (!strcmp(eddarg, "skipmbr") || !strcmp(eddarg, "skip")) | |
137 | do_mbr = 0; | |
138 | else if (!strcmp(eddarg, "off")) | |
139 | do_edd = 0; | |
140 | } | |
141 | ||
e479c830 | 142 | be_quiet = cmdline_find_option_bool("quiet"); |
143 | ||
9a5f35d4 PA |
144 | edp = boot_params.eddbuf; |
145 | mbrptr = boot_params.edd_mbr_sig_buffer; | |
d13444a5 PA |
146 | |
147 | if (!do_edd) | |
148 | return; | |
149 | ||
e479c830 | 150 | /* Bugs in OnBoard or AddOnCards Bios may hang the EDD probe, |
151 | * so give a hint if this happens. | |
152 | */ | |
153 | ||
154 | if (!be_quiet) | |
b7107636 | 155 | printf("Probing EDD (edd=off to disable)... "); |
e479c830 | 156 | |
d13444a5 PA |
157 | for (devno = 0x80; devno < 0x80+EDD_MBR_SIG_MAX; devno++) { |
158 | /* | |
159 | * Scan the BIOS-supported hard disks and query EDD | |
160 | * information... | |
161 | */ | |
162 | get_edd_info(devno, &ei); | |
163 | ||
164 | if (boot_params.eddbuf_entries < EDDMAXNR) { | |
165 | memcpy(edp, &ei, sizeof ei); | |
166 | edp++; | |
167 | boot_params.eddbuf_entries++; | |
168 | } | |
169 | ||
9a5f35d4 PA |
170 | if (do_mbr && !read_mbr_sig(devno, &ei, mbrptr++)) |
171 | boot_params.edd_mbr_sig_buf_entries = devno-0x80+1; | |
d13444a5 | 172 | } |
e479c830 | 173 | |
174 | if (!be_quiet) | |
f7775016 | 175 | printf("ok\n"); |
d13444a5 PA |
176 | } |
177 | ||
178 | #endif |