]> git.saurik.com Git - apple/boot.git/blob - i386/libsaio/disk.c
b08678df17c755583328275a3bdad0f4abd54bb0
[apple/boot.git] / i386 / libsaio / disk.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.1 (the "License"). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24 /*
25 * Mach Operating System
26 * Copyright (c) 1990 Carnegie-Mellon University
27 * Copyright (c) 1989 Carnegie-Mellon University
28 * All rights reserved. The CMU software License Agreement specifies
29 * the terms and conditions for use and redistribution.
30 */
31
32 /*
33 * INTEL CORPORATION PROPRIETARY INFORMATION
34 *
35 * This software is supplied under the terms of a license agreement or
36 * nondisclosure agreement with Intel Corporation and may not be copied
37 * nor disclosed except in accordance with the terms of that agreement.
38 *
39 * Copyright 1988, 1989 Intel Corporation
40 */
41
42 /*
43 * Copyright 1993 NeXT Computer, Inc.
44 * All rights reserved.
45 */
46
47 #define DRIVER_PRIVATE
48
49 #include "sys/types.h"
50 #include "legacy/disk.h"
51 #include "legacy/fdisk.h"
52 #include "libsaio.h"
53 #include "memory.h"
54
55 /*
56 * Type and constant definitions.
57 */
58 typedef struct disk_blk0 boot_sector;
59 #define BOOT_SIGNATURE DISK_SIGNATURE
60 #define PART_TYPE_EXT 0x05
61 #define PART_TYPE_APPLE 0xa8
62 #define UFS_FRONT_PORCH 0
63
64 #if DEBUG
65 #define DPRINT(x) { printf x; }
66 #define DSPRINT(x) { printf x; sleep(1); }
67 #else
68 #define DPRINT(x)
69 #define DSPRINT(x)
70 #endif
71
72 /*
73 * Function prototypes.
74 */
75 extern void spinActivityIndicator();
76 static void diskActivityHook();
77 static int Biosread(int biosdev, int secno);
78 static struct fdisk_part * find_partition(u_int8_t type,
79 u_int8_t biosdev,
80 BOOL mba);
81
82 /*
83 * diskinfo unpacking.
84 */
85 #define SPT(di) ((di) & 0xff)
86 #define HEADS(di) ((((di)>>8) & 0xff) + 1)
87 #define SPC(di) (SPT(di) * HEADS(di))
88 #define BPS 512 /* sector size of the device */
89 #define N_CACHE_SECS (BIOS_LEN / BPS)
90
91 /*
92 * Stores the geometry of the disk in order to
93 * perform LBA to CHS translation for non EBIOS calls.
94 */
95 static struct diskinfo {
96 int spt; /* sectors per track */
97 int spc; /* sectors per cylinder */
98 } diskinfo;
99
100 /*
101 * Globals variables.
102 */
103 int label_secsize = BPS;
104 char * b[NBUFS];
105 daddr_t blknos[NBUFS];
106 struct iob iob[NFILES];
107
108 /*
109 * intbuf points to the start of the sector cache. BIOS calls will
110 * store the sectors read into this memory area. If cache_valid
111 * is TRUE, then intbuf contents are valid. Otherwise, ignore the
112 * cache and read from disk.
113 *
114 * biosbuf points to a sector within the sector cache.
115 */
116 static char * const intbuf = (char *)ptov(BIOS_ADDR);
117 static BOOL cache_valid = FALSE;
118 static char * biosbuf;
119
120 /*==========================================================================
121 *
122 */
123 void
124 devopen(char * name, struct iob * io)
125 {
126 static int last_biosdev = -1;
127 static daddr_t last_offset = 0;
128
129 struct fdisk_part * part;
130 long di;
131
132 io->i_error = 0;
133 io->dirbuf_blkno = -1;
134
135 // Use cached values if possible.
136 //
137 if (io->biosdev == last_biosdev) {
138 io->i_boff = last_offset;
139 return;
140 }
141
142 // initialize disk parameters -- spt and spc
143 // must do this before doing reads from the device.
144
145 di = get_diskinfo(io->biosdev);
146 if (di == 0) {
147 io->i_error = ENXIO;
148 return;
149 }
150
151 diskinfo.spt = SPT(di);
152 diskinfo.spc = diskinfo.spt * HEADS(di);
153
154 // FIXME - io->partition is ignored. Doesn't make sense anymore.
155 // we also don't overwrite the 'name' argument.
156 // Whats special about "$LBL" ?
157
158 part = find_partition(PART_TYPE_APPLE, io->biosdev, FALSE);
159 if (part == NULL) {
160 io->i_error = EIO;
161 DSPRINT(("Unable to find partition: IO error\n"));
162 } else {
163 last_offset = io->i_boff = part->relsect + UFS_FRONT_PORCH/BPS;
164 last_biosdev = io->biosdev;
165 DSPRINT(("partition offset: %x\n", io->i_boff));
166 }
167 }
168
169 /*==========================================================================
170 *
171 */
172 void devflush()
173 {
174 cache_valid = FALSE; // invalidate the sector cache (intbuf)
175 }
176
177 /*==========================================================================
178 *
179 */
180 int devread(struct iob * io)
181 {
182 long sector;
183 int offset;
184 // int dev;
185
186 io->i_flgs |= F_RDDATA;
187
188 io->i_error = 0; // assume the best
189
190 // dev = io->i_ino.i_dev;
191
192 sector = io->i_bn * (label_secsize/BPS);
193
194 for (offset = 0; offset < io->i_cc; offset += BPS) {
195
196 io->i_error = Biosread(io->biosdev, sector);
197 if (io->i_error)
198 return (-1);
199
200 /* copy 1 sector from the internal buffer biosbuf into buf */
201 bcopy(biosbuf, &io->i_ma[offset], BPS);
202
203 sector++;
204 }
205
206 io->i_flgs &= ~F_TYPEMASK;
207
208 return (io->i_cc);
209 }
210
211 /*==========================================================================
212 * Maps (E)BIOS return codes to message strings.
213 */
214 struct bios_error_info {
215 int errno;
216 const char * string;
217 };
218
219 #define ECC_CORRECTED_ERR 0x11
220
221 static struct bios_error_info bios_errors[] = {
222 {0x10, "Media error"},
223 {0x11, "Corrected ECC error"},
224 {0x20, "Controller or device error"},
225 {0x40, "Seek failed"},
226 {0x80, "Device timeout"},
227 {0xAA, "Drive not ready"},
228 {0x00, 0}
229 };
230
231 static const char *
232 bios_error(int errno)
233 {
234 struct bios_error_info * bp;
235
236 for (bp = bios_errors; bp->errno; bp++) {
237 if (bp->errno == errno)
238 return bp->string;
239 }
240 return "Error 0x%02x"; // No string, print error code only
241 }
242
243 /*==========================================================================
244 * Use BIOS INT13 calls to read the sector specified. This function will
245 * also perform read-ahead to cache a few subsequent sector to the sector
246 * cache.
247 *
248 * The fields in diskinfo structure must be filled in before calling this
249 * function.
250 *
251 * Return:
252 * Return code from INT13/F2 or INT13/F42 call. If operation was
253 * successful, 0 is returned.
254 *
255 */
256 static int
257 Biosread(int biosdev, int secno)
258 {
259 static int xbiosdev, xcyl, xhead, xsec, xnsecs;
260
261 extern unsigned char uses_ebios[];
262
263 int rc;
264 int cyl, head, sec;
265 int spt, spc;
266 int tries = 0;
267
268 DSPRINT(("Biosread %d \n", secno));
269
270 // To read the disk sectors, use EBIOS if we can. Otherwise,
271 // revert to the standard BIOS calls.
272 //
273 if ((biosdev >= BIOS_DEV_HD) && uses_ebios[biosdev - BIOS_DEV_HD]) {
274 if (cache_valid &&
275 (biosdev == xbiosdev) &&
276 (secno >= xsec) &&
277 (secno < (xsec + xnsecs)))
278 {
279 biosbuf = intbuf + (BPS * (secno - xsec));
280 return 0;
281 }
282
283 xnsecs = N_CACHE_SECS;
284 xsec = secno;
285
286 while ((rc = ebiosread(biosdev, secno, xnsecs)) && (++tries < 5))
287 {
288 if (rc == ECC_CORRECTED_ERR) {
289 /* Ignore corrected ECC errors */
290 break;
291 }
292 error(" EBIOS read error: %s\n", bios_error(rc), rc);
293 error(" Block %d Sectors %d\n", secno, xnsecs);
294 sleep(1);
295 }
296 }
297 else {
298 spt = diskinfo.spt; // From previous INT13/F8 call.
299 spc = diskinfo.spc;
300
301 cyl = secno / spc;
302 head = (secno % spc) / spt;
303 sec = secno % spt;
304
305 if (cache_valid &&
306 (biosdev == xbiosdev) &&
307 (cyl == xcyl) &&
308 (head == xhead) &&
309 (sec >= xsec) &&
310 (sec < (xsec + xnsecs)))
311 {
312 // this sector is in intbuf cache
313 biosbuf = intbuf + (BPS * (sec - xsec));
314 return 0;
315 }
316
317 // Cache up to a track worth of sectors, but do not cross a
318 // track boundary.
319 //
320 xcyl = cyl;
321 xhead = head;
322 xsec = sec;
323 xnsecs = ((sec + N_CACHE_SECS) > spt) ? (spt - sec) : N_CACHE_SECS;
324
325 while ((rc = biosread(biosdev, cyl, head, sec, xnsecs)) &&
326 (++tries < 5))
327 {
328 if (rc == ECC_CORRECTED_ERR) {
329 /* Ignore corrected ECC errors */
330 break;
331 }
332 error(" BIOS read error: %s\n", bios_error(rc), rc);
333 error(" Block %d, Cyl %d Head %d Sector %d\n",
334 secno, cyl, head, sec);
335 sleep(1);
336 }
337 }
338
339 // If the BIOS reported success, mark the sector cache as valid.
340 //
341 if (rc == 0) {
342 cache_valid = TRUE;
343 }
344 biosbuf = intbuf;
345 xbiosdev = biosdev;
346
347 diskActivityHook();
348
349 return rc;
350 }
351
352 /*==========================================================================
353 * Replace this function if you want to change
354 * the way disk activity is indicated to the user.
355 */
356 void
357 diskActivityHook(void)
358 {
359 spinActivityIndicator();
360 }
361
362 /*==========================================================================
363 * Returns YES if the partition type specified is an extended fdisk
364 * partition.
365 */
366 static BOOL
367 isExtendedPartition(u_int8_t type)
368 {
369 int i;
370
371 u_int8_t extended_partitions[] = {
372 0x05, /* Extended */
373 0x0f, /* Win95 extended */
374 0x85, /* Linux extended */
375 };
376
377 for (i = 0;
378 i < sizeof(extended_partitions)/sizeof(extended_partitions[0]);
379 i++)
380 {
381 if (extended_partitions[i] == type)
382 return YES;
383 }
384 return NO;
385 }
386
387 /*==========================================================================
388 * Traverse the fdisk partition tables on disk until a partition is found
389 * that matches the specified type.
390 *
391 * Arguments:
392 * type - Partition type to search for (e.g. 0xa7 for NeXTSTEP).
393 * biosdev - BIOS device unit. 0x80 and up for hard-drive.
394 * mba - If true, the partition found must be marked active.
395 *
396 * Return:
397 * A pointer to the matching partition entry in biosbuf memory.
398 * Note that the starting LBA field in the partition entry is
399 * modified to contain the absolute sector address, rather than
400 * the relative address.
401 * A NULL is returned if a match is not found.
402 *
403 * There are certain fdisk rules that allows us to simplify the search.
404 *
405 * - There can be 0-1 extended partition entry in any partition table.
406 * - In the MBR, there can be 0-4 primary partitions entries.
407 * - In the extended partition, there can be 0-1 logical partition entry.
408 *
409 */
410 struct fdisk_part *
411 find_partition(u_int8_t type, u_int8_t biosdev, BOOL mba)
412 {
413 #define MAX_ITERATIONS 128
414
415 static u_int32_t iter = 0;
416 static u_int32_t offset_root;
417 static u_int32_t offset;
418
419 int n;
420 int rc;
421 boot_sector * bootsect;
422 struct fdisk_part * match = 0;
423 struct fdisk_part * parts;
424
425 if (iter == 0) {
426 if (rc = Biosread(biosdev, 0)) // Read MBR at sector zero.
427 return 0;
428 offset = 0;
429 }
430
431 bootsect = (boot_sector *) biosbuf;
432 if (bootsect->signature != BOOT_SIGNATURE)
433 return 0;
434
435 // Find a primary or a logical partition that matches the partition
436 // type specified.
437 //
438 for (n = 0, parts = (struct fdisk_part *) bootsect->parts;
439 n < 4;
440 n++, parts++)
441 {
442 DSPRINT(("fdisk: [%d] %02x\n", iter, parts->systid));
443
444 if (mba && ((parts->bootid & 0x80) == 0))
445 continue;
446
447 if (parts->systid == type) {
448 //
449 // Found it!!!
450 // Make the relsect field (LBA starting sector) absolute by
451 // adding in the offset.
452 //
453 parts->relsect += offset;
454
455 DSPRINT(("Found: %x (%d)\n", parts->relsect, parts->numsect));
456
457 return parts;
458 }
459 }
460
461 // Find if there is an extended partition entry that points to
462 // an extended partition table. Note that we only allow up to
463 // one extended partition per partition table.
464 //
465 for (n = 0, parts = (struct fdisk_part *) bootsect->parts;
466 n < 4;
467 n++, parts++)
468 {
469 DSPRINT(("fdisk: [E%d] %02x\n", iter, parts->systid));
470
471 if (isExtendedPartition(parts->systid))
472 {
473 if (iter > MAX_ITERATIONS) // limit recursion depth
474 return 0;
475
476 if (iter == 0)
477 offset = offset_root = parts->relsect;
478 else
479 offset = parts->relsect + offset_root;
480
481 iter++;
482
483 // Load extended partition table.
484 //
485 if (((rc = Biosread(biosdev, offset)) == 0) &&
486 (bootsect->signature == BOOT_SIGNATURE))
487 {
488 match = find_partition(type, biosdev, mba);
489 }
490
491 iter--;
492
493 break;
494 }
495 }
496
497 return match;
498 }