blob: 77d92daf7923505d45580899a5476aa74f6ed989 [file] [log] [blame]
H. Peter Anvind13444a2007-07-11 12:18:48 -07001/* -*- 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[];
H. Peter Anvind13444a2007-07-11 12:18:48 -070075
76 sector_size = ei->params.bytes_per_sector;
77 if (!sector_size)
78 sector_size = 512; /* Best available guess */
79
H. Peter Anvinff659d12007-07-31 16:17:13 -040080 /* Produce a naturally aligned buffer on the heap */
H. Peter Anvind13444a2007-07-11 12:18:48 -070081 buf_base = (ds() << 4) + (u32)&_end;
82 mbr_base = (buf_base+sector_size-1) & ~(sector_size-1);
H. Peter Anvinff659d12007-07-31 16:17:13 -040083 mbrbuf_ptr = _end + (mbr_base-buf_base);
H. Peter Anvind13444a2007-07-11 12:18:48 -070084 mbrbuf_end = mbrbuf_ptr + sector_size;
85
H. Peter Anvinff659d12007-07-31 16:17:13 -040086 /* Make sure we actually have space on the heap... */
H. Peter Anvind13444a2007-07-11 12:18:48 -070087 if (!(boot_params.hdr.loadflags & CAN_USE_HEAP))
88 return 0;
89 if (mbrbuf_end > (char *)(size_t)boot_params.hdr.heap_end_ptr)
90 return 0;
91
92 if (read_mbr(devno, mbrbuf_ptr))
93 return 0;
94
95 mbrsig = *(u32 *)&mbrbuf_ptr[EDD_MBR_SIG_OFFSET];
96 return mbrsig;
97}
98
99static int get_edd_info(u8 devno, struct edd_info *ei)
100{
101 u16 ax, bx, cx, dx, di;
102
103 memset(ei, 0, sizeof *ei);
104
105 /* Check Extensions Present */
106
107 ax = 0x4100;
108 bx = EDDMAGIC1;
109 dx = devno;
110 asm("pushfl; stc; int $0x13; setc %%al; popfl"
111 : "+a" (ax), "+b" (bx), "=c" (cx), "+d" (dx)
112 : : "esi", "edi");
113
114 if ((u8)ax)
115 return -1; /* No extended information */
116
117 if (bx != EDDMAGIC2)
118 return -1;
119
120 ei->device = devno;
121 ei->version = ax >> 8; /* EDD version number */
122 ei->interface_support = cx; /* EDD functionality subsets */
123
124 /* Extended Get Device Parameters */
125
126 ei->params.length = sizeof(ei->params);
127 ax = 0x4800;
128 dx = devno;
129 asm("pushfl; int $0x13; popfl"
130 : "+a" (ax), "+d" (dx)
131 : "S" (&ei->params)
132 : "ebx", "ecx", "edi");
133
134 /* Get legacy CHS parameters */
135
136 /* Ralf Brown recommends setting ES:DI to 0:0 */
137 ax = 0x0800;
138 dx = devno;
139 di = 0;
140 asm("pushw %%es; "
141 "movw %%di,%%es; "
142 "pushfl; stc; int $0x13; setc %%al; popfl; "
143 "popw %%es"
144 : "+a" (ax), "=b" (bx), "=c" (cx), "+d" (dx), "+D" (di)
145 : : "esi");
146
147 if ((u8)ax == 0) {
148 ei->legacy_max_cylinder = (cx >> 8) + ((cx & 0xc0) << 2);
149 ei->legacy_max_head = dx >> 8;
150 ei->legacy_sectors_per_track = cx & 0x3f;
151 }
152
153 return 0;
154}
155
156void query_edd(void)
157{
158 char eddarg[8];
159 int do_mbr = 1;
160 int do_edd = 1;
161 int devno;
162 struct edd_info ei, *edp;
163
164 if (cmdline_find_option("edd", eddarg, sizeof eddarg) > 0) {
165 if (!strcmp(eddarg, "skipmbr") || !strcmp(eddarg, "skip"))
166 do_mbr = 0;
167 else if (!strcmp(eddarg, "off"))
168 do_edd = 0;
169 }
170
171 edp = (struct edd_info *)boot_params.eddbuf;
172
173 if (!do_edd)
174 return;
175
176 for (devno = 0x80; devno < 0x80+EDD_MBR_SIG_MAX; devno++) {
177 /*
178 * Scan the BIOS-supported hard disks and query EDD
179 * information...
180 */
181 get_edd_info(devno, &ei);
182
183 if (boot_params.eddbuf_entries < EDDMAXNR) {
184 memcpy(edp, &ei, sizeof ei);
185 edp++;
186 boot_params.eddbuf_entries++;
187 }
188
189 if (do_mbr) {
190 u32 mbr_sig;
191 mbr_sig = read_mbr_sig(devno, &ei);
192 boot_params.edd_mbr_sig_buffer[devno-0x80] = mbr_sig;
193 }
194 }
195}
196
197#endif