]> git.saurik.com Git - apple/boot.git/blob - i386/libsaio/disk.c
boot-93.tar.gz
[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 #include "libsaio.h"
48 #include "fdisk.h"
49
50 /*
51 * diskinfo unpacking.
52 */
53 #define SPT(di) ((di) & 0xff)
54 #define HEADS(di) ((((di)>>8) & 0xff) + 1)
55 #define SPC(di) (SPT(di) * HEADS(di))
56
57 #define BPS 512 /* sector size of the device */
58 #define N_CACHE_SECS (BIOS_LEN / BPS)
59 #define UFS_FRONT_PORCH 0
60
61 /*
62 * trackbuf points to the start of the track cache. Biosread()
63 * will store the sectors read from disk to this memory area.
64 *
65 * biosbuf points to a sector within the track cache, and is
66 * updated by Biosread().
67 */
68 static const char * const trackbuf = (char *) ptov(BIOS_ADDR);
69 static const char * biosbuf;
70
71 /*
72 * Map a disk drive to bootable volumes contained within.
73 */
74 struct DiskBVMap {
75 int biosdev; // BIOS device number (unique)
76 BVRef bvr; // chain of boot volumes on the disk
77 int bvrcnt; // number of boot volumes
78 struct DiskBVMap * next; // linkage to next mapping
79 };
80
81 static struct DiskBVMap * gDiskBVMap = NULL;
82 static struct disk_blk0 * gBootSector = NULL;
83
84 extern long HFSInitPartition(CICell ih);
85 extern long HFSLoadFile(CICell ih, char * filePath);
86 extern long HFSGetDirEntry(CICell ih, char * dirPath, long * dirIndex,
87 char ** name, long * flags, long * time);
88
89 extern long UFSInitPartition(CICell ih);
90 extern long UFSLoadFile(CICell ih, char * filePath);
91 extern long UFSGetDirEntry(CICell ih, char * dirPath, long * dirIndex,
92 char ** name, long * flags, long * time);
93
94 extern void spinActivityIndicator();
95
96 static void getVolumeDescription(BVRef bvr, char * str, long strMaxLen);
97
98 //==========================================================================
99
100 static int getDiskGeometry( int biosdev, int * spt, int * spc )
101 {
102 static int cached_biosdev = -1;
103 static int cached_spt = 0;
104 static int cached_spc = 0;
105
106 if ( biosdev != cached_biosdev )
107 {
108 long di = get_diskinfo(biosdev);
109 if (di == 0) return (-1); // BIOS call error
110
111 cached_spt = SPT(di);
112 cached_spc = cached_spt * HEADS(di);
113
114 DEBUG_DISK(("%s: %d sectors, %d heads\n",
115 __FUNCTION__, cached_spt, (int)HEADS(di)));
116 }
117
118 *spt = cached_spt;
119 *spc = cached_spc;
120
121 return 0;
122 }
123
124 //==========================================================================
125 // Maps (E)BIOS return codes to message strings.
126
127 struct NamedValue {
128 unsigned char value;
129 const char * name;
130 };
131
132 static const char * getNameForValue( const struct NamedValue * nameTable,
133 unsigned char value )
134 {
135 const struct NamedValue * np;
136
137 for ( np = nameTable; np->value; np++)
138 if (np->value == value)
139 return np->name;
140
141 return NULL;
142 }
143
144 #define ECC_CORRECTED_ERR 0x11
145
146 static const struct NamedValue bios_errors[] = {
147 { 0x10, "Media error" },
148 { 0x11, "Corrected ECC error" },
149 { 0x20, "Controller or device error" },
150 { 0x40, "Seek failed" },
151 { 0x80, "Device timeout" },
152 { 0xAA, "Drive not ready" },
153 { 0x00, 0 }
154 };
155
156 static const char * bios_error(int errnum)
157 {
158 static char errorstr[] = "Error 0x00";
159 const char * errname;
160
161 errname = getNameForValue( bios_errors, errnum );
162 if ( errname ) return errname;
163
164 sprintf(errorstr, "Error 0x%02x", errnum);
165 return errorstr; // No string, print error code only
166 }
167
168 //==========================================================================
169 // Use BIOS INT13 calls to read the sector specified. This function will
170 // also perform read-ahead to cache a few subsequent sector to the sector
171 // cache.
172 //
173 // Return:
174 // 0 on success, or an error code from INT13/F2 or INT13/F42 BIOS call.
175
176 static int Biosread( int biosdev, unsigned int secno )
177 {
178 static int xbiosdev, xcyl, xhead;
179 static unsigned int xsec, xnsecs;
180 static BOOL cache_valid = FALSE;
181
182 int rc = -1;
183 int cyl, head, sec;
184 int spt, spc;
185 int tries = 0;
186
187 // DEBUG_DISK(("Biosread dev %x sec %d \n", biosdev, secno));
188
189 // To read the disk sectors, use EBIOS if we can. Otherwise,
190 // revert to the standard BIOS calls.
191
192 if ((biosdev >= kBIOSDevTypeHardDrive) &&
193 (uses_ebios[biosdev - kBIOSDevTypeHardDrive] & EBIOS_FIXED_DISK_ACCESS))
194 {
195 if (cache_valid &&
196 (biosdev == xbiosdev) &&
197 (secno >= xsec) &&
198 (secno < (xsec + xnsecs)))
199 {
200 biosbuf = trackbuf + (BPS * (secno - xsec));
201 return 0;
202 }
203
204 xnsecs = N_CACHE_SECS;
205 xsec = secno;
206 cache_valid = FALSE;
207
208 while ((rc = ebiosread(biosdev, secno, xnsecs)) && (++tries < 5))
209 {
210 if (rc == ECC_CORRECTED_ERR) {
211 /* Ignore corrected ECC errors */
212 rc = 0;
213 break;
214 }
215 error(" EBIOS read error: %s\n", bios_error(rc), rc);
216 error(" Block %d Sectors %d\n", secno, xnsecs);
217 sleep(1);
218 }
219 }
220 else if ( getDiskGeometry(biosdev, &spt, &spc) == 0 )
221 {
222 cyl = secno / spc;
223 head = (secno % spc) / spt;
224 sec = secno % spt;
225
226 if (cache_valid &&
227 (biosdev == xbiosdev) &&
228 (cyl == xcyl) &&
229 (head == xhead) &&
230 (sec >= xsec) &&
231 (sec < (xsec + xnsecs)))
232 {
233 // this sector is in trackbuf cache
234 biosbuf = trackbuf + (BPS * (sec - xsec));
235 return 0;
236 }
237
238 // Cache up to a track worth of sectors, but do not cross a
239 // track boundary.
240
241 xcyl = cyl;
242 xhead = head;
243 xsec = sec;
244 xnsecs = ((sec + N_CACHE_SECS) > spt) ? (spt - sec) : N_CACHE_SECS;
245 cache_valid = FALSE;
246
247 while ((rc = biosread(biosdev, cyl, head, sec, xnsecs)) &&
248 (++tries < 5))
249 {
250 if (rc == ECC_CORRECTED_ERR) {
251 /* Ignore corrected ECC errors */
252 rc = 0;
253 break;
254 }
255 error(" BIOS read error: %s\n", bios_error(rc), rc);
256 error(" Block %d, Cyl %d Head %d Sector %d\n",
257 secno, cyl, head, sec);
258 sleep(1);
259 }
260 }
261
262 // If the BIOS reported success, mark the sector cache as valid.
263
264 if (rc == 0) {
265 cache_valid = TRUE;
266 }
267 biosbuf = trackbuf;
268 xbiosdev = biosdev;
269
270 spinActivityIndicator();
271
272 return rc;
273 }
274
275 //==========================================================================
276
277 static int readBytes( int biosdev, unsigned int blkno,
278 unsigned int byteCount, void * buffer )
279 {
280
281 char * cbuf = (char *) buffer;
282 int error;
283 int copy_len;
284
285 DEBUG_DISK(("%s: dev %x block %x [%d] -> 0x%x...", __FUNCTION__,
286 biosdev, blkno, byteCount, (unsigned)cbuf));
287
288 for ( ; byteCount; cbuf += BPS, blkno++ )
289 {
290 error = Biosread( biosdev, blkno );
291 if ( error )
292 {
293 DEBUG_DISK(("error\n"));
294 return (-1);
295 }
296
297 copy_len = (byteCount > BPS) ? BPS : byteCount;
298 bcopy( biosbuf, cbuf, copy_len );
299 byteCount -= copy_len;
300 }
301
302 DEBUG_DISK(("done\n"));
303
304 return 0;
305 }
306
307 //==========================================================================
308
309 static int isExtendedFDiskPartition( const struct fdisk_part * part )
310 {
311 static unsigned char extParts[] =
312 {
313 0x05, /* Extended */
314 0x0f, /* Win95 extended */
315 0x85, /* Linux extended */
316 };
317
318 int i;
319
320 for (i = 0; i < sizeof(extParts)/sizeof(extParts[0]); i++)
321 {
322 if (extParts[i] == part->systid) return 1;
323 }
324 return 0;
325 }
326
327 //==========================================================================
328
329 static int getNextFDiskPartition( int biosdev, int * partno,
330 const struct fdisk_part ** outPart )
331 {
332 static int sBiosdev = -1;
333 static int sNextPartNo;
334 static unsigned int sExtBase;
335 static unsigned int sExtDepth;
336 static struct fdisk_part * sExtPart;
337 struct fdisk_part * part;
338
339 if ( sBiosdev != biosdev || *partno < 0 )
340 {
341 // Fetch MBR.
342 if ( readBootSector( biosdev, DISK_BLK0, 0 ) ) return 0;
343
344 sBiosdev = biosdev;
345 sNextPartNo = 0;
346 sExtBase = 0;
347 sExtDepth = 0;
348 sExtPart = NULL;
349 }
350
351 while (1)
352 {
353 part = NULL;
354
355 if ( sNextPartNo < FDISK_NPART )
356 {
357 part = (struct fdisk_part *) gBootSector->parts[sNextPartNo];
358 }
359 else if ( sExtPart )
360 {
361 unsigned int blkno = sExtPart->relsect + sExtBase;
362
363 // Save the block offset of the first extended partition.
364
365 if ( sExtDepth == 0 ) sExtBase = sExtPart->relsect;
366
367 // Load extended partition table.
368
369 if ( readBootSector( biosdev, blkno, 0 ) == 0 )
370 {
371 sNextPartNo = 0;
372 sExtDepth++;
373 sExtPart = NULL;
374 continue;
375 }
376 }
377
378 if ( part == NULL ) break; // Reached end of partition chain.
379
380 // Advance to next partition number.
381
382 sNextPartNo++;
383
384 // Assume at most one extended partition per table.
385
386 if ( isExtendedFDiskPartition(part) )
387 {
388 sExtPart = part;
389 continue;
390 }
391
392 // Skip empty slots.
393
394 if ( part->systid == 0x00 )
395 {
396 continue;
397 }
398
399 // Change relative offset to an absolute offset.
400
401 part->relsect += sExtBase;
402
403 *outPart = part;
404 *partno = sExtDepth ? (sExtDepth + 4) : sNextPartNo;
405
406 break;
407 }
408
409 return (part != NULL);
410 }
411
412 //==========================================================================
413
414 static BVRef newFDiskBVRef( int biosdev, int partno, unsigned int blkoff,
415 const struct fdisk_part * part,
416 FSInit initFunc, FSLoadFile loadFunc,
417 FSGetDirEntry getdirFunc, int probe )
418 {
419 BVRef bvr = (BVRef) malloc( sizeof(*bvr) );
420 if ( bvr )
421 {
422 bzero(bvr, sizeof(*bvr));
423
424 bvr->biosdev = biosdev;
425 bvr->part_no = partno;
426 bvr->part_boff = blkoff;
427 bvr->part_type = part->systid;
428 bvr->fs_loadfile = loadFunc;
429 bvr->fs_getdirentry = getdirFunc;
430 bvr->description = getVolumeDescription;
431
432 if ( part->bootid & FDISK_ACTIVE )
433 bvr->flags |= kBVFlagPrimary;
434
435 // Probe the filesystem.
436
437 if ( initFunc )
438 {
439 bvr->flags |= kBVFlagNativeBoot;
440
441 if ( probe && initFunc( bvr ) != 0 )
442 {
443 // filesystem probe failed.
444
445 DEBUG_DISK(("%s: failed probe on dev %x part %d\n",
446 __FUNCTION__, biosdev, partno));
447
448 free(bvr);
449 bvr = NULL;
450 }
451 }
452 else if ( readBootSector( biosdev, blkoff, (void *)0x7e00 ) == 0 )
453 {
454 bvr->flags |= kBVFlagForeignBoot;
455 }
456 else
457 {
458 free(bvr);
459 bvr = NULL;
460 }
461 }
462 return bvr;
463 }
464
465 //==========================================================================
466
467 BVRef diskScanBootVolumes( int biosdev, int * countPtr )
468 {
469 const struct fdisk_part * part;
470 struct DiskBVMap * map;
471 int partno = -1;
472 BVRef bvr;
473 BVRef booterUFS = NULL;
474 int spc, spt;
475
476 do {
477 // Find an existing mapping for this device.
478
479 for ( map = gDiskBVMap; map; map = map->next )
480 {
481 if ( biosdev == map->biosdev ) break;
482 }
483 if ( map ) break;
484
485 // Create a new mapping.
486
487 map = (struct DiskBVMap *) malloc( sizeof(*map) );
488 if ( map )
489 {
490 map->biosdev = biosdev;
491 map->bvr = NULL;
492 map->bvrcnt = 0;
493 map->next = gDiskBVMap;
494 gDiskBVMap = map;
495
496 // Create a record for each partition found on the disk.
497
498 while ( getNextFDiskPartition( biosdev, &partno, &part ) )
499 {
500 DEBUG_DISK(("%s: part %d [%x]\n", __FUNCTION__,
501 partno, part->systid));
502
503 bvr = 0;
504
505 switch ( part->systid )
506 {
507 case FDISK_UFS:
508 bvr = newFDiskBVRef(
509 biosdev, partno,
510 part->relsect + UFS_FRONT_PORCH/BPS,
511 part,
512 UFSInitPartition,
513 UFSLoadFile,
514 UFSGetDirEntry,
515 0 );
516 break;
517
518 case FDISK_HFS:
519 bvr = newFDiskBVRef(
520 biosdev, partno,
521 part->relsect,
522 part,
523 HFSInitPartition,
524 HFSLoadFile,
525 HFSGetDirEntry,
526 0 );
527 break;
528
529 case FDISK_BOOTER:
530 if (getDiskGeometry(biosdev, &spt, &spc) != 0)
531 break;
532
533 booterUFS = newFDiskBVRef(
534 biosdev, partno,
535 ((part->relsect + spc - 1) / spc) * spc,
536 part,
537 UFSInitPartition,
538 UFSLoadFile,
539 UFSGetDirEntry,
540 0 );
541 break;
542
543 default:
544 bvr = newFDiskBVRef(
545 biosdev, partno,
546 part->relsect,
547 part,
548 0, 0, 0, 0 );
549 break;
550 }
551
552 if ( bvr )
553 {
554 bvr->next = map->bvr;
555 map->bvr = bvr;
556 map->bvrcnt++;
557 }
558 }
559
560 // Booting from a CD with an UFS filesystem embedded
561 // in a booter partition.
562
563 if ( booterUFS )
564 {
565 if ( map->bvrcnt == 0 )
566 {
567 map->bvr = booterUFS;
568 map->bvrcnt++;
569 }
570 else free( booterUFS );
571 }
572 }
573 } while (0);
574
575 if (countPtr) *countPtr = map ? map->bvrcnt : 0;
576
577 return map ? map->bvr : NULL;
578 }
579
580 //==========================================================================
581
582 static const struct NamedValue fdiskTypes[] =
583 {
584 { 0x07, "Windows NTFS" },
585 { 0x0c, "Windows FAT32" },
586 { 0x83, "Linux" },
587 { FDISK_UFS, "Apple UFS" },
588 { FDISK_HFS, "Apple HFS" },
589 { FDISK_BOOTER, "Apple Boot/UFS" },
590 { 0x00, 0 } /* must be last */
591 };
592
593 static void getVolumeDescription( BVRef bvr, char * str, long strMaxLen )
594 {
595 unsigned char type = (unsigned char) bvr->part_type;
596 const char * name = getNameForValue( fdiskTypes, type );
597
598 if ( name )
599 sprintf( str, "hd(%d,%d) %s",
600 BIOS_DEV_UNIT(bvr->biosdev), bvr->part_no, name );
601 else
602 sprintf( str, "hd(%d,%d) TYPE %02x",
603 BIOS_DEV_UNIT(bvr->biosdev), bvr->part_no, type );
604 }
605
606 //==========================================================================
607
608 int readBootSector( int biosdev, unsigned int secno, void * buffer )
609 {
610 struct disk_blk0 * bootSector = (struct disk_blk0 *) buffer;
611 int error;
612
613 if ( bootSector == NULL )
614 {
615 if ( gBootSector == NULL )
616 {
617 gBootSector = (struct disk_blk0 *) malloc(sizeof(*gBootSector));
618 if ( gBootSector == NULL ) return -1;
619 }
620 bootSector = gBootSector;
621 }
622
623 error = readBytes( biosdev, secno, BPS, bootSector );
624 if ( error || bootSector->signature != DISK_SIGNATURE )
625 return -1;
626
627 return 0;
628 }
629
630 //==========================================================================
631 // Handle seek request from filesystem modules.
632
633 void diskSeek( BVRef bvr, long long position )
634 {
635 bvr->fs_boff = position / BPS;
636 }
637
638 //==========================================================================
639 // Handle read request from filesystem modules.
640
641 int diskRead( BVRef bvr, long addr, long length )
642 {
643 return readBytes( bvr->biosdev,
644 bvr->fs_boff + bvr->part_boff,
645 length,
646 (void *) addr );
647 }