]> git.proxmox.com Git - grub2.git/blob - disk/i386/pc/biosdisk.c
af14c10fa16426eff7a8cf6a79bb26318e921eee
[grub2.git] / disk / i386 / pc / biosdisk.c
1 /*
2 * PUPA -- Preliminary Universal Programming Architecture for GRUB
3 * Copyright (C) 1999,2000,2001,2002 Free Software Foundation, Inc.
4 * Copyright (C) 2002 Yoshinori K. Okuji <okuji@enbug.org>
5 *
6 * PUPA is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with PUPA; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include <pupa/machine/biosdisk.h>
22 #include <pupa/machine/memory.h>
23 #include <pupa/disk.h>
24 #include <pupa/mm.h>
25 #include <pupa/types.h>
26 #include <pupa/misc.h>
27 #include <pupa/err.h>
28 #include <pupa/term.h>
29
30 /* Drive Parameters. */
31 struct pupa_biosdisk_drp
32 {
33 pupa_uint16_t size;
34 pupa_uint16_t flags;
35 pupa_uint32_t cylinders;
36 pupa_uint32_t heads;
37 pupa_uint32_t sectors;
38 pupa_uint64_t total_sectors;
39 pupa_uint16_t bytes_per_sector;
40 /* ver 2.0 or higher */
41 pupa_uint32_t EDD_configuration_parameters;
42 /* ver 3.0 or higher */
43 pupa_uint16_t signature_dpi;
44 pupa_uint8_t length_dpi;
45 pupa_uint8_t reserved[3];
46 pupa_uint8_t name_of_host_bus[4];
47 pupa_uint8_t name_of_interface_type[8];
48 pupa_uint8_t interface_path[8];
49 pupa_uint8_t device_path[8];
50 pupa_uint8_t reserved2;
51 pupa_uint8_t checksum;
52
53 /* XXX: This is necessary, because the BIOS of Thinkpad X20
54 writes a garbage to the tail of drive parameters,
55 regardless of a size specified in a caller. */
56 pupa_uint8_t dummy[16];
57 } __attribute__ ((packed));
58
59 /* Disk Address Packet. */
60 struct pupa_biosdisk_dap
61 {
62 pupa_uint8_t length;
63 pupa_uint8_t reserved;
64 pupa_uint16_t blocks;
65 pupa_uint32_t buffer;
66 pupa_uint64_t block;
67 } __attribute__ ((packed));
68
69
70 static int
71 pupa_biosdisk_get_drive (const char *name)
72 {
73 unsigned long drive;
74
75 if ((name[0] != 'f' && name[0] != 'h') || name[1] != 'd')
76 goto fail;
77
78 drive = pupa_strtoul (name + 2, 0, 10);
79 if (pupa_errno != PUPA_ERR_NONE)
80 goto fail;
81
82 if (name[0] == 'h')
83 drive += 0x80;
84
85 return (int) drive ;
86
87 fail:
88 pupa_error (PUPA_ERR_UNKNOWN_DEVICE, "not a biosdisk");
89 return -1;
90 }
91
92 static int
93 pupa_biosdisk_call_hook (int (*hook) (const char *name), int drive)
94 {
95 char name[4];
96
97 pupa_sprintf (name, (drive & 0x80) ? "hd%d" : "fd%d", drive & (~0x80));
98 return hook (name);
99 }
100
101 static int
102 pupa_biosdisk_iterate (int (*hook) (const char *name))
103 {
104 int drive;
105 int num_floppies;
106
107 /* For floppy disks, we can get the number safely. */
108 num_floppies = pupa_biosdisk_get_num_floppies ();
109 for (drive = 0; drive < num_floppies; drive++)
110 if (pupa_biosdisk_call_hook (hook, drive))
111 return 1;
112
113 /* For hard disks, attempt to read the MBR. */
114 for (drive = 0x80; drive < 0x88; drive++)
115 {
116 if (pupa_biosdisk_rw_standard (0x02, drive, 0, 0, 1, 1,
117 PUPA_MEMORY_MACHINE_SCRATCH_SEG) != 0)
118 break;
119
120 if (pupa_biosdisk_call_hook (hook, drive))
121 return 1;
122 }
123
124 return 0;
125 }
126
127 static pupa_err_t
128 pupa_biosdisk_open (const char *name, pupa_disk_t disk)
129 {
130 unsigned long total_sectors = 0;
131 int drive;
132 struct pupa_biosdisk_data *data;
133
134 drive = pupa_biosdisk_get_drive (name);
135 if (drive < 0)
136 return pupa_errno;
137
138 disk->has_partitions = (drive & 0x80);
139 disk->id = drive;
140
141 data = (struct pupa_biosdisk_data *) pupa_malloc (sizeof (*data));
142 if (! data)
143 return pupa_errno;
144
145 data->drive = drive;
146 data->flags = 0;
147
148 if (drive & 0x80)
149 {
150 /* HDD */
151 int version;
152
153 version = pupa_biosdisk_check_int13_extensions (drive);
154 if (version)
155 {
156 struct pupa_biosdisk_drp *drp
157 = (struct pupa_biosdisk_drp *) PUPA_MEMORY_MACHINE_SCRATCH_ADDR;
158
159 /* Clear out the DRP. */
160 pupa_memset (drp, 0, sizeof (*drp));
161 drp->size = sizeof (*drp);
162 if (pupa_biosdisk_get_diskinfo_int13_extensions (drive, drp))
163 {
164 data->flags = PUPA_BIOSDISK_FLAG_LBA;
165
166 /* FIXME: 2TB limit. */
167 if (drp->total_sectors)
168 total_sectors = drp->total_sectors & ~0L;
169 else
170 /* Some buggy BIOSes doesn't return the total sectors
171 correctly but returns zero. So if it is zero, compute
172 it by C/H/S returned by the LBA BIOS call. */
173 total_sectors = drp->cylinders * drp->heads * drp->sectors;
174 }
175 }
176 }
177
178 if (pupa_biosdisk_get_diskinfo_standard (drive,
179 &data->cylinders,
180 &data->heads,
181 &data->sectors) != 0)
182 {
183 pupa_free (data);
184 return pupa_error (PUPA_ERR_BAD_DEVICE, "cannot get C/H/S values");
185 }
186
187 if (! total_sectors)
188 total_sectors = data->cylinders * data->heads * data->sectors;
189
190 disk->total_sectors = total_sectors;
191 disk->data = data;
192
193 return PUPA_ERR_NONE;
194 }
195
196 static void
197 pupa_biosdisk_close (pupa_disk_t disk)
198 {
199 pupa_free (disk->data);
200 }
201
202 /* For readability. */
203 #define PUPA_BIOSDISK_READ 0
204 #define PUPA_BIOSDISK_WRITE 1
205
206 static pupa_err_t
207 pupa_biosdisk_rw (int cmd, pupa_disk_t disk,
208 unsigned long sector, unsigned long size,
209 unsigned segment)
210 {
211 struct pupa_biosdisk_data *data = disk->data;
212
213 if (data->flags & PUPA_BIOSDISK_FLAG_LBA)
214 {
215 struct pupa_biosdisk_dap *dap;
216
217 dap = (struct pupa_biosdisk_dap *) (PUPA_MEMORY_MACHINE_SCRATCH_ADDR
218 + (data->sectors
219 << PUPA_DISK_SECTOR_BITS));
220 dap->length = sizeof (*dap);
221 dap->reserved = 0;
222 dap->blocks = size;
223 dap->buffer = segment << 16; /* The format SEGMENT:ADDRESS. */
224 dap->block = sector;
225
226 if (pupa_biosdisk_rw_int13_extensions (cmd + 0x42, data->drive, dap))
227 {
228 /* Fall back to the CHS mode. */
229 data->flags &= ~PUPA_BIOSDISK_FLAG_LBA;
230 disk->total_sectors = data->cylinders * data->heads * data->sectors;
231 return pupa_biosdisk_rw (cmd, disk, sector, size, segment);
232 }
233 }
234 else
235 {
236 unsigned coff, hoff, soff;
237 unsigned head;
238
239 soff = sector % data->sectors + 1;
240 head = sector / data->sectors;
241 hoff = head % data->heads;
242 coff = head / data->heads;
243
244 if (coff >= data->cylinders)
245 return pupa_error (PUPA_ERR_OUT_OF_RANGE, "out of disk");
246
247 if (pupa_biosdisk_rw_standard (cmd + 0x02, data->drive,
248 coff, hoff, soff, size, segment))
249 {
250 switch (cmd)
251 {
252 case PUPA_BIOSDISK_READ:
253 return pupa_error (PUPA_ERR_READ_ERROR, "biosdisk read error");
254 case PUPA_BIOSDISK_WRITE:
255 return pupa_error (PUPA_ERR_WRITE_ERROR, "biosdisk write error");
256 }
257 }
258 }
259
260 return PUPA_ERR_NONE;
261 }
262
263 static pupa_err_t
264 pupa_biosdisk_read (pupa_disk_t disk, unsigned long sector,
265 unsigned long size, char *buf)
266 {
267 struct pupa_biosdisk_data *data = disk->data;
268
269 while (size)
270 {
271 unsigned long len;
272
273 len = data->sectors - (sector % data->sectors);
274 if (len > size)
275 len = size;
276
277 if (pupa_biosdisk_rw (PUPA_BIOSDISK_READ, disk, sector, len,
278 PUPA_MEMORY_MACHINE_SCRATCH_SEG))
279 return pupa_errno;
280
281 pupa_memcpy (buf, (void *) PUPA_MEMORY_MACHINE_SCRATCH_ADDR,
282 len << PUPA_DISK_SECTOR_BITS);
283 buf += len << PUPA_DISK_SECTOR_BITS;
284 sector += len;
285 size -= len;
286 }
287
288 return pupa_errno;
289 }
290
291 static pupa_err_t
292 pupa_biosdisk_write (pupa_disk_t disk, unsigned long sector,
293 unsigned long size, const char *buf)
294 {
295 struct pupa_biosdisk_data *data = disk->data;
296
297 while (size)
298 {
299 unsigned long len;
300
301 len = data->sectors - (sector % data->sectors);
302 if (len > size)
303 len = size;
304
305 pupa_memcpy ((void *) PUPA_MEMORY_MACHINE_SCRATCH_ADDR, buf,
306 len << PUPA_DISK_SECTOR_BITS);
307
308 if (pupa_biosdisk_rw (PUPA_BIOSDISK_WRITE, disk, sector, len,
309 PUPA_MEMORY_MACHINE_SCRATCH_SEG))
310 return pupa_errno;
311
312 buf += len << PUPA_DISK_SECTOR_BITS;
313 sector += len;
314 size -= len;
315 }
316
317 return pupa_errno;
318 }
319
320 static struct pupa_disk_dev pupa_biosdisk_dev =
321 {
322 .name = "biosdisk",
323 .iterate = pupa_biosdisk_iterate,
324 .open = pupa_biosdisk_open,
325 .close = pupa_biosdisk_close,
326 .read = pupa_biosdisk_read,
327 .write = pupa_biosdisk_write,
328 .next = 0
329 };
330
331 void
332 pupa_biosdisk_init (void)
333 {
334 pupa_disk_dev_register (&pupa_biosdisk_dev);
335 }