]>
git.saurik.com Git - apple/boot.git/blob - i386/libsaio/disk.c
3f6157439d8850465a508afdeac9ced4545251e8
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 * Mach Operating System
27 * Copyright (c) 1990 Carnegie-Mellon University
28 * Copyright (c) 1989 Carnegie-Mellon University
29 * All rights reserved. The CMU software License Agreement specifies
30 * the terms and conditions for use and redistribution.
34 * INTEL CORPORATION PROPRIETARY INFORMATION
36 * This software is supplied under the terms of a license agreement or
37 * nondisclosure agreement with Intel Corporation and may not be copied
38 * nor disclosed except in accordance with the terms of that agreement.
40 * Copyright 1988, 1989 Intel Corporation
44 * Copyright 1993 NeXT Computer, Inc.
45 * All rights reserved.
48 #include "bootstruct.h"
55 #include <IOKit/storage/IOApplePartitionScheme.h>
57 #define BPS 512 /* sector size of the device */
58 #define CD_BPS 2048 /* CD-ROM block size */
59 #define N_CACHE_SECS (BIOS_LEN / BPS) /* Must be a multiple of 4 for CD-ROMs */
60 #define UFS_FRONT_PORCH 0
61 #define kAPMSector 2 /* Sector number of Apple partition map */
62 #define kAPMCDSector 8 /* Translated sector of Apple partition map on a CD */
65 * trackbuf points to the start of the track cache. Biosread()
66 * will store the sectors read from disk to this memory area.
68 * biosbuf points to a sector within the track cache, and is
69 * updated by Biosread().
71 static const char * const trackbuf
= (char *) ptov(BIOS_ADDR
);
72 static const char * biosbuf
;
75 * Map a disk drive to bootable volumes contained within.
78 int biosdev
; // BIOS device number (unique)
79 BVRef bvr
; // chain of boot volumes on the disk
80 int bvrcnt
; // number of boot volumes
81 struct DiskBVMap
* next
; // linkage to next mapping
84 static struct DiskBVMap
* gDiskBVMap
= NULL
;
85 static struct disk_blk0
* gBootSector
= NULL
;
87 extern void spinActivityIndicator();
89 static void getVolumeDescription(BVRef bvr
, char * str
, long strMaxLen
);
91 //==========================================================================
93 static int getDriveInfo( int biosdev
, struct driveInfo
*dip
)
95 static struct driveInfo cached_di
;
98 if ( !cached_di
.valid
|| biosdev
!= cached_di
.biosdev
)
100 cc
= get_drive_info(biosdev
, &cached_di
);
103 return (-1); // BIOS call error
107 bcopy(&cached_di
, dip
, sizeof(cached_di
));
112 //==========================================================================
113 // Maps (E)BIOS return codes to message strings.
120 static const char * getNameForValue( const struct NamedValue
* nameTable
,
121 unsigned char value
)
123 const struct NamedValue
* np
;
125 for ( np
= nameTable
; np
->value
; np
++)
126 if (np
->value
== value
)
132 #define ECC_CORRECTED_ERR 0x11
134 static const struct NamedValue bios_errors
[] = {
135 { 0x10, "Media error" },
136 { 0x11, "Corrected ECC error" },
137 { 0x20, "Controller or device error" },
138 { 0x40, "Seek failed" },
139 { 0x80, "Device timeout" },
140 { 0xAA, "Drive not ready" },
144 static const char * bios_error(int errnum
)
146 static char errorstr
[] = "Error 0x00";
147 const char * errname
;
149 errname
= getNameForValue( bios_errors
, errnum
);
150 if ( errname
) return errname
;
152 sprintf(errorstr
, "Error 0x%02x", errnum
);
153 return errorstr
; // No string, print error code only
156 //==========================================================================
157 // Use BIOS INT13 calls to read the sector specified. This function will
158 // also perform read-ahead to cache a few subsequent sector to the sector
162 // 0 on success, or an error code from INT13/F2 or INT13/F42 BIOS call.
164 static int Biosread( int biosdev
, unsigned int secno
)
166 static int xbiosdev
, xcyl
, xhead
;
167 static unsigned int xsec
, xnsecs
;
168 static BOOL cache_valid
= FALSE
;
176 if (getDriveInfo(biosdev
, &di
) < 0) {
179 if (di
.no_emulation
) {
180 /* Always assume 2k block size; BIOS may lie about geometry */
183 bps
= di
.di
.params
.phys_nbps
;
187 DEBUG_DISK(("Biosread dev %x sec %d bps %d\n", biosdev
, secno
, bps
));
189 // To read the disk sectors, use EBIOS if we can. Otherwise,
190 // revert to the standard BIOS calls.
192 if ((biosdev
>= kBIOSDevTypeHardDrive
) &&
193 (di
.uses_ebios
& EBIOS_FIXED_DISK_ACCESS
))
196 (biosdev
== xbiosdev
) &&
198 ((unsigned int)secno
< (xsec
+ xnsecs
)))
200 biosbuf
= trackbuf
+ (BPS
* (secno
- xsec
));
204 xnsecs
= N_CACHE_SECS
;
205 xsec
= (secno
/ divisor
) * divisor
;
208 while ((rc
= ebiosread(biosdev
, secno
/ divisor
, xnsecs
/ divisor
)) && (++tries
< 5))
210 if (rc
== ECC_CORRECTED_ERR
) {
211 /* Ignore corrected ECC errors */
215 error(" EBIOS read error: %s\n", bios_error(rc
), rc
);
216 error(" Block %d Sectors %d\n", secno
, xnsecs
);
222 /* spc = spt * heads */
223 int spc
= (di
.di
.params
.phys_spt
* di
.di
.params
.phys_heads
);
225 head
= (secno
% spc
) / di
.di
.params
.phys_spt
;
226 sec
= secno
% di
.di
.params
.phys_spt
;
229 (biosdev
== xbiosdev
) &&
232 ((unsigned int)sec
>= xsec
) &&
233 ((unsigned int)sec
< (xsec
+ xnsecs
)))
235 // this sector is in trackbuf cache
236 biosbuf
= trackbuf
+ (BPS
* (sec
- xsec
));
240 // Cache up to a track worth of sectors, but do not cross a
246 xnsecs
= ((unsigned int)(sec
+ N_CACHE_SECS
) > di
.di
.params
.phys_spt
) ? (di
.di
.params
.phys_spt
- sec
) : N_CACHE_SECS
;
249 while ((rc
= biosread(biosdev
, cyl
, head
, sec
, xnsecs
)) &&
252 if (rc
== ECC_CORRECTED_ERR
) {
253 /* Ignore corrected ECC errors */
257 error(" BIOS read error: %s\n", bios_error(rc
), rc
);
258 error(" Block %d, Cyl %d Head %d Sector %d\n",
259 secno
, cyl
, head
, sec
);
264 // If the BIOS reported success, mark the sector cache as valid.
269 biosbuf
= trackbuf
+ (secno
% divisor
) * BPS
;
272 spinActivityIndicator();
277 //==========================================================================
279 static int readBytes( int biosdev
, unsigned int blkno
,
280 unsigned int byteCount
, void * buffer
)
283 char * cbuf
= (char *) buffer
;
287 DEBUG_DISK(("%s: dev %x block %x [%d] -> 0x%x...", __FUNCTION__
,
288 biosdev
, blkno
, byteCount
, (unsigned)cbuf
));
290 for ( ; byteCount
; cbuf
+= BPS
, blkno
++ )
292 error
= Biosread( biosdev
, blkno
);
295 DEBUG_DISK(("error\n"));
299 copy_len
= (byteCount
> BPS
) ? BPS
: byteCount
;
300 bcopy( biosbuf
, cbuf
, copy_len
);
301 byteCount
-= copy_len
;
304 DEBUG_DISK(("done\n"));
309 //==========================================================================
311 static int isExtendedFDiskPartition( const struct fdisk_part
* part
)
313 static unsigned char extParts
[] =
316 0x0f, /* Win95 extended */
317 0x85, /* Linux extended */
322 for (i
= 0; i
< sizeof(extParts
)/sizeof(extParts
[0]); i
++)
324 if (extParts
[i
] == part
->systid
) return 1;
329 //==========================================================================
331 static int getNextFDiskPartition( int biosdev
, int * partno
,
332 const struct fdisk_part
** outPart
)
334 static int sBiosdev
= -1;
335 static int sNextPartNo
;
336 static unsigned int sExtBase
;
337 static unsigned int sExtDepth
;
338 static struct fdisk_part
* sExtPart
;
339 struct fdisk_part
* part
;
341 if ( sBiosdev
!= biosdev
|| *partno
< 0 )
344 if ( readBootSector( biosdev
, DISK_BLK0
, 0 ) ) return 0;
357 if ( sNextPartNo
< FDISK_NPART
)
359 part
= (struct fdisk_part
*) gBootSector
->parts
[sNextPartNo
];
363 unsigned int blkno
= sExtPart
->relsect
+ sExtBase
;
365 // Save the block offset of the first extended partition.
367 if ( sExtDepth
== 0 ) sExtBase
= sExtPart
->relsect
;
369 // Load extended partition table.
371 if ( readBootSector( biosdev
, blkno
, 0 ) == 0 )
380 if ( part
== NULL
) break; // Reached end of partition chain.
382 // Advance to next partition number.
386 // Assume at most one extended partition per table.
388 if ( isExtendedFDiskPartition(part
) )
396 if ( part
->systid
== 0x00 )
401 // Change relative offset to an absolute offset.
403 part
->relsect
+= sExtBase
;
406 *partno
= sExtDepth
? (int)(sExtDepth
+ 4) : sNextPartNo
;
411 return (part
!= NULL
);
414 //==========================================================================
416 static BVRef
newFDiskBVRef( int biosdev
, int partno
, unsigned int blkoff
,
417 const struct fdisk_part
* part
,
418 FSInit initFunc
, FSLoadFile loadFunc
,
419 FSGetDirEntry getdirFunc
, int probe
, int type
)
421 BVRef bvr
= (BVRef
) malloc( sizeof(*bvr
) );
424 bzero(bvr
, sizeof(*bvr
));
426 bvr
->biosdev
= biosdev
;
427 bvr
->part_no
= partno
;
428 bvr
->part_boff
= blkoff
;
429 bvr
->part_type
= part
->systid
;
430 bvr
->fs_loadfile
= loadFunc
;
431 bvr
->fs_getdirentry
= getdirFunc
;
432 bvr
->description
= getVolumeDescription
;
435 if ( part
->bootid
& FDISK_ACTIVE
)
436 bvr
->flags
|= kBVFlagPrimary
;
438 // Probe the filesystem.
442 bvr
->flags
|= kBVFlagNativeBoot
;
444 if ( probe
&& initFunc( bvr
) != 0 )
446 // filesystem probe failed.
448 DEBUG_DISK(("%s: failed probe on dev %x part %d\n",
449 __FUNCTION__
, biosdev
, partno
));
455 else if ( readBootSector( biosdev
, blkoff
, (void *)0x7e00 ) == 0 )
457 bvr
->flags
|= kBVFlagForeignBoot
;
468 //==========================================================================
470 BVRef
newAPMBVRef( int biosdev
, int partno
, unsigned int blkoff
,
472 FSInit initFunc
, FSLoadFile loadFunc
,
473 FSGetDirEntry getdirFunc
, int probe
, int type
)
475 BVRef bvr
= (BVRef
) malloc( sizeof(*bvr
) );
478 bzero(bvr
, sizeof(*bvr
));
480 bvr
->biosdev
= biosdev
;
481 bvr
->part_no
= partno
;
482 bvr
->part_boff
= blkoff
;
483 bvr
->fs_loadfile
= loadFunc
;
484 bvr
->fs_getdirentry
= getdirFunc
;
485 bvr
->description
= getVolumeDescription
;
487 strlcpy(bvr
->name
, part
->dpme_name
, DPISTRLEN
);
488 strlcpy(bvr
->type_name
, part
->dpme_type
, DPISTRLEN
);
491 if ( part->bootid & FDISK_ACTIVE )
492 bvr->flags |= kBVFlagPrimary;
495 // Probe the filesystem.
499 bvr
->flags
|= kBVFlagNativeBoot
;
501 if ( probe
&& initFunc( bvr
) != 0 )
503 // filesystem probe failed.
505 DEBUG_DISK(("%s: failed probe on dev %x part %d\n",
506 __FUNCTION__
, biosdev
, partno
));
513 else if ( readBootSector( biosdev, blkoff, (void *)0x7e00 ) == 0 )
515 bvr->flags |= kBVFlagForeignBoot;
527 //==========================================================================
529 static BVRef
diskScanFDiskBootVolumes( int biosdev
, int * countPtr
)
531 const struct fdisk_part
* part
;
532 struct DiskBVMap
* map
;
535 BVRef booterUFS
= NULL
;
538 boot_drive_info_t
*dp
;
540 /* Initialize disk info */
541 if (getDriveInfo(biosdev
, &di
) != 0) {
545 spc
= (dp
->params
.phys_spt
* dp
->params
.phys_heads
);
547 /* This is probably a CD-ROM; punt on the geometry. */
552 // Create a new mapping.
554 map
= (struct DiskBVMap
*) malloc( sizeof(*map
) );
557 map
->biosdev
= biosdev
;
560 map
->next
= gDiskBVMap
;
563 // Create a record for each partition found on the disk.
565 while ( getNextFDiskPartition( biosdev
, &partno
, &part
) )
567 DEBUG_DISK(("%s: part %d [%x]\n", __FUNCTION__
,
568 partno
, part
->systid
));
571 switch ( part
->systid
)
576 part
->relsect
+ UFS_FRONT_PORCH
/BPS
,
582 kBIOSDevTypeHardDrive
);
594 kBIOSDevTypeHardDrive
);
598 booterUFS
= newFDiskBVRef(
600 ((part
->relsect
+ spc
- 1) / spc
) * spc
,
606 kBIOSDevTypeHardDrive
);
615 kBIOSDevTypeHardDrive
);
621 bvr
->next
= map
->bvr
;
627 // Booting from a CD with an UFS filesystem embedded
628 // in a booter partition.
632 if ( map
->bvrcnt
== 0 )
634 map
->bvr
= booterUFS
;
637 else free( booterUFS
);
643 * If no FDisk partition, then we will check for
644 * an Apple partition map elsewhere.
647 if (map
->bvrcnt
== 0) {
648 static struct fdisk_part cdpart
;
649 cdpart
.systid
= 0xCD;
651 /* Let's try assuming we are on a hybrid HFS/ISO9660 CD. */
660 kBIOSDevTypeHardDrive
);
661 bvr
->next
= map
->bvr
;
667 if (countPtr
) *countPtr
= map
? map
->bvrcnt
: 0;
669 return map
? map
->bvr
: NULL
;
672 //==========================================================================
674 static BVRef
diskScanAPMBootVolumes( int biosdev
, int * countPtr
)
676 struct DiskBVMap
* map
;
677 struct Block0
*block0_p
;
678 unsigned int blksize
;
680 void *buffer
= malloc(BPS
);
682 /* Check for alternate block size */
683 if (readBytes( biosdev
, 0, BPS
, buffer
) != 0) {
687 if (NXSwapBigShortToHost(block0_p
->sbSig
) == BLOCK0_SIGNATURE
) {
688 blksize
= NXSwapBigShortToHost(block0_p
->sbBlkSize
);
689 if (blksize
!= BPS
) {
691 buffer
= malloc(blksize
);
693 factor
= blksize
/ BPS
;
700 // Create a new mapping.
702 map
= (struct DiskBVMap
*) malloc( sizeof(*map
) );
706 DPME
*dpme_p
= (DPME
*)buffer
;
707 UInt32 i
, npart
= UINT_MAX
;
710 map
->biosdev
= biosdev
;
713 map
->next
= gDiskBVMap
;
716 for (i
=0; i
<npart
; i
++) {
717 error
= readBytes( biosdev
, (kAPMSector
+ i
) * factor
, blksize
, buffer
);
719 if (error
|| NXSwapBigShortToHost(dpme_p
->dpme_signature
) != DPME_SIGNATURE
) {
724 npart
= NXSwapBigLongToHost(dpme_p
->dpme_map_entries
);
727 printf("name = %s, %s%s %d -> %d [%d -> %d] {%d}\n",
728 dpme.dpme_name, dpme.dpme_type, (dpme.dpme_flags & DPME_FLAGS_BOOTABLE) ? "(bootable)" : "",
729 dpme.dpme_pblock_start, dpme.dpme_pblocks,
730 dpme.dpme_lblock_start, dpme.dpme_lblocks,
731 dpme.dpme_boot_block);
734 if (strcmp(dpme_p
->dpme_type
, "Apple_HFS") == 0) {
735 bvr
= newAPMBVRef(biosdev
,
737 NXSwapBigLongToHost(dpme_p
->dpme_pblock_start
) * factor
,
743 kBIOSDevTypeHardDrive
);
744 bvr
->next
= map
->bvr
;
754 if (countPtr
) *countPtr
= map
? map
->bvrcnt
: 0;
756 return map
? map
->bvr
: NULL
;
759 //==========================================================================
761 BVRef
diskScanBootVolumes( int biosdev
, int * countPtr
)
763 struct DiskBVMap
* map
;
767 // Find an existing mapping for this device.
769 for ( map
= gDiskBVMap
; map
; map
= map
->next
) {
770 if ( biosdev
== map
->biosdev
) {
777 bvr
= diskScanFDiskBootVolumes(biosdev
, &count
);
779 bvr
= diskScanAPMBootVolumes(biosdev
, &count
);
784 if (countPtr
) *countPtr
= count
;
789 //==========================================================================
791 static const struct NamedValue fdiskTypes
[] =
793 { 0x07, "Windows NTFS" },
794 { 0x0c, "Windows FAT32" },
796 { FDISK_UFS
, "Apple UFS" },
797 { FDISK_HFS
, "Apple HFS" },
798 { FDISK_BOOTER
, "Apple Boot/UFS" },
800 { 0x00, 0 } /* must be last */
803 static void getVolumeDescription( BVRef bvr
, char * str
, long strMaxLen
)
805 unsigned char type
= (unsigned char) bvr
->part_type
;
806 const char * name
= getNameForValue( fdiskTypes
, type
);
809 name
= bvr
->type_name
;
812 sprintf( str
, "hd(%d,%d) %s",
813 BIOS_DEV_UNIT(bvr
), bvr
->part_no
, name
);
815 sprintf( str
, "hd(%d,%d) TYPE %02x",
816 BIOS_DEV_UNIT(bvr
), bvr
->part_no
, type
);
820 //==========================================================================
822 int readBootSector( int biosdev
, unsigned int secno
, void * buffer
)
824 struct disk_blk0
* bootSector
= (struct disk_blk0
*) buffer
;
827 if ( bootSector
== NULL
)
829 if ( gBootSector
== NULL
)
831 gBootSector
= (struct disk_blk0
*) malloc(sizeof(*gBootSector
));
832 if ( gBootSector
== NULL
) return -1;
834 bootSector
= gBootSector
;
837 error
= readBytes( biosdev
, secno
, BPS
, bootSector
);
838 if ( error
|| bootSector
->signature
!= DISK_SIGNATURE
)
844 //==========================================================================
845 // Handle seek request from filesystem modules.
847 void diskSeek( BVRef bvr
, long long position
)
849 bvr
->fs_boff
= position
/ BPS
;
852 //==========================================================================
853 // Handle read request from filesystem modules.
855 int diskRead( BVRef bvr
, long addr
, long length
)
857 return readBytes( bvr
->biosdev
,
858 bvr
->fs_boff
+ bvr
->part_boff
,
863 void turnOffFloppy(void)
867 * Hold controller in reset,
868 * disable DMA and IRQ,
869 * turn off floppy motors.