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