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