2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 #include <IOKit/assert.h>
24 #include <IOKit/IOBufferMemoryDescriptor.h>
25 #include <IOKit/IODeviceTreeSupport.h>
26 #include <IOKit/IOLib.h>
27 #include <IOKit/storage/IOApplePartitionScheme.h>
28 #include <libkern/OSByteOrder.h>
30 #define super IOPartitionScheme
31 OSDefineMetaClassAndStructors(IOApplePartitionScheme
, IOPartitionScheme
);
33 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
36 // o the on-disk structure's fields are: 16-bit packed, big-endian formatted
37 // o the dpme_pblock_start and dpme_pblocks block values are:
38 // o for media without a driver map:
39 // o natural block size based
40 // o for media with a driver map:
41 // o driver map block size based, unless the driver map block size is 2048
42 // and a valid partition entry exists at a 512 byte offset into the disk,
43 // in which case, assume a 512 byte block size, except for the partition
44 // entries that lie on a 2048 byte multiple and are one of the following
45 // types: Apple_Patches, Apple_Driver, Apple_Driver43, Apple_Driver43_CD,
46 // Apple_Driver_ATA, Apple_Driver_ATAPI; in which case, we assume a 2048
47 // byte block size (for the one partition)
48 // o the dpme_pblock_start block value is relative to the media container
51 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
53 #define kIOApplePartitionSchemeContentTable "Content Table"
55 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
57 bool IOApplePartitionScheme::init(OSDictionary
* properties
= 0)
60 // Initialize this object's minimal state.
63 // State our assumptions.
65 assert(sizeof(dpme
) == 512); // (compiler/platform check)
66 assert(sizeof(DDMap
) == 8); // (compiler/platform check)
67 assert(sizeof(Block0
) == 512); // (compiler/platform check)
69 // Ask our superclass' opinion.
71 if (super::init(properties
) == false) return false;
73 // Initialize our state.
80 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
82 void IOApplePartitionScheme::free()
85 // Free all of this object's outstanding resources.
88 if ( _partitions
) _partitions
->release();
93 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
95 IOService
* IOApplePartitionScheme::probe(IOService
* provider
, SInt32
* score
)
98 // Determine whether the provider media contains an Apple partition map.
101 // State our assumptions.
103 assert(OSDynamicCast(IOMedia
, provider
));
105 // Ask superclass' opinion.
107 if (super::probe(provider
, score
) == 0) return 0;
109 // Scan the provider media for an Apple partition map.
111 _partitions
= scan(score
);
113 return ( _partitions
) ? this : 0;
116 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
118 bool IOApplePartitionScheme::start(IOService
* provider
)
121 // Publish the new media objects which represent our partitions.
125 OSIterator
* partitionIterator
;
127 // State our assumptions.
131 // Ask our superclass' opinion.
133 if ( super::start(provider
) == false ) return false;
135 // Attach and register the new media objects representing our partitions.
137 partitionIterator
= OSCollectionIterator::withCollection(_partitions
);
138 if ( partitionIterator
== 0 ) return false;
140 while ( (partition
= (IOMedia
*) partitionIterator
->getNextObject()) )
142 if ( partition
->attach(this) )
144 attachMediaObjectToDeviceTree(partition
);
146 partition
->registerService();
150 partitionIterator
->release();
155 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
157 void IOApplePartitionScheme::stop(IOService
* provider
)
160 // Clean up after the media objects we published before terminating.
164 OSIterator
* partitionIterator
;
166 // State our assumptions.
170 // Detach the media objects we previously attached to the device tree.
172 partitionIterator
= OSCollectionIterator::withCollection(_partitions
);
174 if ( partitionIterator
)
176 while ( (partition
= (IOMedia
*) partitionIterator
->getNextObject()) )
178 detachMediaObjectFromDeviceTree(partition
);
181 partitionIterator
->release();
184 super::stop(provider
);
187 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
189 OSSet
* IOApplePartitionScheme::scan(SInt32
* score
)
192 // Scan the provider media for an Apple partition map. Returns the set
193 // of media objects representing each of the partitions (the retain for
194 // the set is passed to the caller), or null should no partition map be
195 // found. The default probe score can be adjusted up or down, based on
196 // the confidence of the scan.
199 IOBufferMemoryDescriptor
* buffer
= 0;
200 UInt32 bufferReadAt
= 0;
201 UInt32 bufferSize
= 0;
202 UInt32 dpmeBlockSize
= 0;
203 UInt32 dpmeCount
= 0;
206 UInt32 dpmeMaxCount
= 0;
207 bool dpmeOldSchool
= false;
208 Block0
* driverMap
= 0;
209 IOMedia
* media
= getProvider();
210 UInt64 mediaBlockSize
= media
->getPreferredBlockSize();
211 bool mediaIsOpen
= false;
212 OSSet
* partitions
= 0;
213 IOReturn status
= kIOReturnError
;
215 // Determine whether this media is formatted.
217 if ( media
->isFormatted() == false ) goto scanErr
;
219 // Determine whether this media has an appropriate block size.
221 if ( (mediaBlockSize
% sizeof(dpme
)) ) goto scanErr
;
223 // Allocate a buffer large enough to hold one map, rounded to a media block.
225 bufferSize
= IORound(max(sizeof(Block0
), sizeof(dpme
)), mediaBlockSize
);
226 buffer
= IOBufferMemoryDescriptor::withCapacity(
227 /* capacity */ bufferSize
,
228 /* withDirection */ kIODirectionIn
);
229 if ( buffer
== 0 ) goto scanErr
;
231 // Allocate a set to hold the set of media objects representing partitions.
233 partitions
= OSSet::withCapacity(8);
234 if ( partitions
== 0 ) goto scanErr
;
236 // Open the media with read access.
238 mediaIsOpen
= media
->open(this, 0, kIOStorageAccessReader
);
239 if ( mediaIsOpen
== false ) goto scanErr
;
241 // Read the driver map into our buffer.
245 ///m:2333367:workaround:commented:start
246 // status = media->read(this, bufferReadAt, buffer);
247 ///m:2333367:workaround:commented:stop
248 ///m:2333367:workaround:added:start
249 status
= media
->IOStorage::read(this, bufferReadAt
, buffer
);
250 ///m:2333367:workaround:added:stop
251 if ( status
!= kIOReturnSuccess
) goto scanErr
;
253 driverMap
= (Block0
*) buffer
->getBytesNoCopy();
255 // Determine the official block size to use to scan the partition entries.
257 dpmeBlockSize
= mediaBlockSize
; // (natural block size)
259 if ( driverMap
->sbSig
== BLOCK0_SIGNATURE
)
261 dpmeBlockSize
= driverMap
->sbBlkSize
; // (driver map block size)
263 // Determine whether we have an old school partition map, where there is
264 // a partition entry at a 512 byte offset into the disk, even though the
265 // driver map block size is 2048.
267 if ( dpmeBlockSize
== 2048 )
269 if ( bufferSize
>= sizeof(Block0
) + sizeof(dpme
) ) // (in buffer?)
271 dpmeMap
= (dpme
*) (driverMap
+ 1);
273 else // (not in buffer)
275 // Read the partition entry at byte offset 512 into our buffer.
277 bufferReadAt
= sizeof(dpme
);
279 ///m:2333367:workaround:commented:start
280 // status = media->read(this, bufferReadAt, buffer);
281 ///m:2333367:workaround:commented:stop
282 ///m:2333367:workaround:added:start
283 status
= media
->IOStorage::read(this, bufferReadAt
, buffer
);
284 ///m:2333367:workaround:added:stop
285 if ( status
!= kIOReturnSuccess
) goto scanErr
;
287 dpmeMap
= (dpme
*) buffer
->getBytesNoCopy();
290 // Determine whether the partition entry signature is present.
292 if (OSSwapBigToHostInt16(dpmeMap
->dpme_signature
) == DPME_SIGNATURE
)
294 dpmeBlockSize
= sizeof(dpme
); // (old school block size)
295 dpmeOldSchool
= true;
299 // Increase the probe score when a driver map is detected, since we are
300 // more confident in the match when it is present. This will eliminate
301 // conflicts with FDisk when it shares the same block as the driver map.
306 // Scan the media for Apple partition entries.
308 for ( dpmeID
= 1, dpmeCount
= 1; dpmeID
<= dpmeCount
; dpmeID
++ )
310 UInt32 partitionBlockSize
= dpmeBlockSize
;
312 // Determine whether we've exhausted the current buffer of entries.
314 if ( dpmeID
* dpmeBlockSize
+ sizeof(dpme
) > bufferReadAt
+ bufferSize
)
316 // Read the next partition entry into our buffer.
318 bufferReadAt
= dpmeID
* dpmeBlockSize
;
320 ///m:2333367:workaround:commented:start
321 // status = media->read(this, bufferReadAt, buffer);
322 ///m:2333367:workaround:commented:stop
323 ///m:2333367:workaround:added:start
324 status
= media
->IOStorage::read(this, bufferReadAt
, buffer
);
325 ///m:2333367:workaround:added:stop
326 if ( status
!= kIOReturnSuccess
) goto scanErr
;
329 dpmeMap
= (dpme
*) ( ((UInt8
*) buffer
->getBytesNoCopy()) +
330 (dpmeID
* dpmeBlockSize
) - bufferReadAt
);
332 // Determine whether the partition entry signature is present.
334 if ( OSSwapBigToHostInt16(dpmeMap
->dpme_signature
) != DPME_SIGNATURE
)
339 // Obtain an accurate number of entries in the partition map.
341 if ( !strcmp(dpmeMap
->dpme_type
, "Apple_partition_map") ||
342 !strcmp(dpmeMap
->dpme_type
, "Apple_Partition_Map") ||
343 !strcmp(dpmeMap
->dpme_type
, "Apple_patition_map" ) )
345 dpmeCount
= OSSwapBigToHostInt32(dpmeMap
->dpme_map_entries
);
346 dpmeMaxCount
= OSSwapBigToHostInt32(dpmeMap
->dpme_pblocks
);
348 else if ( dpmeCount
== 1 )
350 dpmeCount
= OSSwapBigToHostInt32(dpmeMap
->dpme_map_entries
);
353 // Obtain an accurate block size for an old school partition map.
355 if ( dpmeOldSchool
&& (dpmeID
% 4) == 0 )
357 if ( !strcmp(dpmeMap
->dpme_type
, "Apple_Driver" ) ||
358 !strcmp(dpmeMap
->dpme_type
, "Apple_Driver43" ) ||
359 !strcmp(dpmeMap
->dpme_type
, "Apple_Driver43_CD" ) ||
360 !strcmp(dpmeMap
->dpme_type
, "Apple_Driver_ATA" ) ||
361 !strcmp(dpmeMap
->dpme_type
, "Apple_Driver_ATAPI") ||
362 !strcmp(dpmeMap
->dpme_type
, "Apple_Patches" ) )
364 partitionBlockSize
= 2048;
368 // Determine whether the partition is corrupt (fatal).
370 if ( isPartitionCorrupt(
371 /* partition */ dpmeMap
,
372 /* partitionID */ dpmeID
,
373 /* partitionBlockSize */ partitionBlockSize
) )
378 // Determine whether the partition is invalid (skipped).
380 if ( isPartitionInvalid(
381 /* partition */ dpmeMap
,
382 /* partitionID */ dpmeID
,
383 /* partitionBlockSize */ partitionBlockSize
) )
388 // Create a media object to represent this partition.
390 IOMedia
* newMedia
= instantiateMediaObject(
391 /* partition */ dpmeMap
,
392 /* partitionID */ dpmeID
,
393 /* partitionBlockSize */ partitionBlockSize
);
397 partitions
->setObject(newMedia
);
402 // Determine whether we ever came accross an Apple_partition_map partition.
404 if ( dpmeMaxCount
== 0 ) goto scanErr
;
406 // Release our resources.
415 // Release our resources.
417 if ( mediaIsOpen
) media
->close(this);
418 if ( partitions
) partitions
->release();
419 if ( buffer
) buffer
->release();
424 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
426 bool IOApplePartitionScheme::isPartitionCorrupt(
427 dpme
* /* partition */ ,
428 UInt32
/* partitionID */ ,
429 UInt32
/* partitionBlockSize */ )
432 // Ask whether the given partition appears to be corrupt. A partition that
433 // is corrupt will cause the failure of the Apple partition map recognition
440 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
442 bool IOApplePartitionScheme::isPartitionInvalid( dpme
* partition
,
444 UInt32 partitionBlockSize
)
447 // Ask whether the given partition appears to be invalid. A partition that
448 // is invalid will cause it to be skipped in the scan, but will not cause a
449 // failure of the Apple partition map recognition.
452 IOMedia
* media
= getProvider();
453 UInt64 partitionBase
= 0;
454 UInt64 partitionSize
= 0;
456 // Compute the relative byte position and size of the new partition.
458 partitionBase
= OSSwapBigToHostInt32(partition
->dpme_pblock_start
);
459 partitionSize
= OSSwapBigToHostInt32(partition
->dpme_pblocks
);
460 partitionBase
*= partitionBlockSize
;
461 partitionSize
*= partitionBlockSize
;
463 // Determine whether the partition is a placeholder.
465 if ( partitionSize
== 0 ) return true;
467 // Determine whether the partition starts at (or past) the end-of-media.
469 if ( partitionBase
>= media
->getSize() ) return true;
474 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
476 IOMedia
* IOApplePartitionScheme::instantiateMediaObject(
479 UInt32 partitionBlockSize
)
482 // Instantiate a new media object to represent the given partition.
485 IOMedia
* media
= getProvider();
486 UInt64 mediaBlockSize
= media
->getPreferredBlockSize();
487 UInt64 partitionBase
= 0;
488 char * partitionHint
= partition
->dpme_type
;
489 bool partitionIsWritable
= media
->isWritable();
490 char * partitionName
= partition
->dpme_name
;
491 UInt64 partitionSize
= 0;
493 // Compute the relative byte position and size of the new partition.
495 partitionBase
= OSSwapBigToHostInt32(partition
->dpme_pblock_start
);
496 partitionSize
= OSSwapBigToHostInt32(partition
->dpme_pblocks
);
497 partitionBase
*= partitionBlockSize
;
498 partitionSize
*= partitionBlockSize
;
500 // Clip the size of the new partition if it extends past the end-of-media.
502 if ( partitionBase
+ partitionSize
> media
->getSize() )
504 partitionSize
= media
->getSize() - partitionBase
;
507 // Look up a type for the new partition.
509 OSDictionary
* hintTable
= OSDynamicCast(
510 /* type */ OSDictionary
,
511 /* instance */ getProperty(kIOApplePartitionSchemeContentTable
) );
515 OSString
* hintValue
= OSDynamicCast(
517 /* instance */ hintTable
->getObject(partitionHint
) );
519 if ( hintValue
) partitionHint
= (char *) hintValue
->getCStringNoCopy();
522 // Look up a name for the new partition.
524 while ( *partitionName
== ' ' ) { partitionName
++; }
526 if ( *partitionName
== 0 ) partitionName
= 0;
528 // Determine whether the new partition type is Apple_Free, which we choose
529 // not to publish because it is an internal concept to the partition map.
531 if ( !strcmp(partitionHint
, "Apple_Free") ) return 0;
533 // Determine whether the new partition is read-only.
535 // Note that we treat the misspelt Apple_patition_map entries as equivalent
536 // to Apple_partition_map entries due to the messed up CDs noted in 2513960.
538 if ( !strcmp(partition
->dpme_type
, "Apple_partition_map") ||
539 !strcmp(partition
->dpme_type
, "Apple_Partition_Map") ||
540 !strcmp(partition
->dpme_type
, "Apple_patition_map" ) ||
541 ( ((partition
->dpme_flags
& DPME_FLAGS_WRITABLE
) == 0) &&
542 ((partition
->dpme_flags
& DPME_FLAGS_VALID
) != 0) ) )
544 partitionIsWritable
= false;
547 // Create the new media object.
549 IOMedia
* newMedia
= instantiateDesiredMediaObject(
550 /* partition */ partition
,
551 /* partitionID */ partitionID
,
552 /* partitionBlockSize */ partitionBlockSize
);
557 /* base */ partitionBase
,
558 /* size */ partitionSize
,
559 /* preferredBlockSize */ mediaBlockSize
,
560 /* isEjectable */ media
->isEjectable(),
562 /* isWritable */ partitionIsWritable
,
563 /* contentHint */ partitionHint
) )
565 // Set a name for this partition.
568 sprintf(name
, "Untitled %ld", partitionID
);
569 newMedia
->setName(partitionName
? partitionName
: name
);
571 // Set a location value (the partition number) for this partition.
574 sprintf(location
, "%ld", partitionID
);
575 newMedia
->setLocation(location
);
577 // Set the "Partition ID" key for this partition.
579 newMedia
->setProperty(kIOMediaPartitionIDKey
, partitionID
, 32);
591 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
593 IOMedia
* IOApplePartitionScheme::instantiateDesiredMediaObject(
596 UInt32 partitionBlockSize
)
599 // Allocate a new media object (called from instantiateMediaObject).
605 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
607 bool IOApplePartitionScheme::attachMediaObjectToDeviceTree( IOMedia
* media
)
610 // Attach the given media object to the device tree plane.
616 for ( service
= this; service
; service
= service
->getProvider() )
620 if ( (number
= OSDynamicCast(OSNumber
, service
->getProperty("IOUnit"))))
622 unit
= number
->unsigned32BitValue();
625 if ( service
->inPlane(gIODTPlane
) )
627 IORegistryEntry
* child
;
628 IORegistryIterator
* children
;
630 if ( unit
== -1 ) break;
632 children
= IORegistryIterator::iterateOver(service
, gIODTPlane
);
634 if ( children
== 0 ) break;
636 while ( (child
= children
->getNextObject()) )
638 const char * location
= child
->getLocation(gIODTPlane
);
639 const char * name
= child
->getName(gIODTPlane
);
641 if ( name
== 0 || strcmp(name
, "" ) != 0 ||
642 location
== 0 || strchr(location
, ':') == 0 )
644 child
->detachAll(gIODTPlane
);
650 if ( media
->attachToParent(service
, gIODTPlane
) )
652 char location
[ sizeof("hhhhhhhh:dddddddddd") ];
654 sprintf(location
, "%lx:", unit
);
655 strcat(location
, media
->getLocation());
656 media
->setLocation(location
, gIODTPlane
);
657 media
->setName("", gIODTPlane
);
669 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
671 void IOApplePartitionScheme::detachMediaObjectFromDeviceTree( IOMedia
* media
)
674 // Detach the given media object from the device tree plane.
677 IORegistryEntry
* parent
;
679 if ( (parent
= media
->getParentEntry(gIODTPlane
)) )
681 media
->detachFromParent(parent
, gIODTPlane
);
685 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
687 OSMetaClassDefineReservedUnused(IOApplePartitionScheme
, 0);
689 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
691 OSMetaClassDefineReservedUnused(IOApplePartitionScheme
, 1);
693 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
695 OSMetaClassDefineReservedUnused(IOApplePartitionScheme
, 2);
697 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
699 OSMetaClassDefineReservedUnused(IOApplePartitionScheme
, 3);
701 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
703 OSMetaClassDefineReservedUnused(IOApplePartitionScheme
, 4);
705 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
707 OSMetaClassDefineReservedUnused(IOApplePartitionScheme
, 5);
709 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
711 OSMetaClassDefineReservedUnused(IOApplePartitionScheme
, 6);
713 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
715 OSMetaClassDefineReservedUnused(IOApplePartitionScheme
, 7);
717 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
719 OSMetaClassDefineReservedUnused(IOApplePartitionScheme
, 8);
721 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
723 OSMetaClassDefineReservedUnused(IOApplePartitionScheme
, 9);
725 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
727 OSMetaClassDefineReservedUnused(IOApplePartitionScheme
, 10);
729 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
731 OSMetaClassDefineReservedUnused(IOApplePartitionScheme
, 11);
733 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
735 OSMetaClassDefineReservedUnused(IOApplePartitionScheme
, 12);
737 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
739 OSMetaClassDefineReservedUnused(IOApplePartitionScheme
, 13);
741 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
743 OSMetaClassDefineReservedUnused(IOApplePartitionScheme
, 14);
745 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
747 OSMetaClassDefineReservedUnused(IOApplePartitionScheme
, 15);