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