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 <sys/param.h>
24 #include <IOKit/assert.h>
25 #include <IOKit/IOBufferMemoryDescriptor.h>
26 #include <IOKit/IOLib.h>
27 #include <IOKit/storage/IONeXTPartitionScheme.h>
28 #include <libkern/OSByteOrder.h>
30 #define super IOPartitionScheme
31 OSDefineMetaClassAndStructors(IONeXTPartitionScheme
, IOPartitionScheme
);
33 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
36 // o the on-disk structure's fields are: 16-bit packed, big-endian formatted
37 // o the on-disk structure is stored four times in succession, each takes up
38 // sizeof(disk_label_t) bytes rounded up to the drive's natural block size
39 // o the dl_label_blkno block value assumes the drive's natural block size
40 // o the dl_part[].p_base, dl_part[].p_size and dl_front block values assume
41 // a dl_secsize byte block size
42 // o the dl_part[].p_base and dl_label_blkno block values are absolute, with
43 // respect to the whole disk
44 // o the dl_part[].p_base block value doesn't take into account the dl_front
45 // offset, which is required in order to compute the actual start position
46 // of the partition on the disk
47 // o note that CDs often suffer from the mastering-with-a-different-natural-
48 // block-size problem, but we can assume that the first map will always be
49 // valid in those cases, and that we'll never need to compute the position
50 // of the next map correctly
51 // o note that bootable i386 disks will never have a valid first map, due to
52 // the boot code that lives in block zero, however the second map is valid
53 // o this implementation checks for the existence of the first map only; it
54 // does not bother with the last three maps, since backwards compatibility
55 // with unreleased NeXT-style i386 disks is a non-goal, and for reasons of
56 // minimizing access to the media during probe
59 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
61 #define kIONeXTPartitionSchemeContentTable "Content Table"
63 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
65 bool IONeXTPartitionScheme::init(OSDictionary
* properties
= 0)
68 // Initialize this object's minimal state.
71 // State our assumptions.
73 assert(sizeof(disktab_t
) == 514); // (compiler/platform check)
74 assert(sizeof(partition_t
) == 46); // (compiler/platform check)
75 assert(sizeof(disk_label_t
) == 7240); // (compiler/platform check)
77 // Ask our superclass' opinion.
79 if ( super::init(properties
) == false ) return false;
81 // Initialize our state.
88 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
90 void IONeXTPartitionScheme::free()
93 // Free all of this object's outstanding resources.
96 if ( _partitions
) _partitions
->release();
101 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
103 IOService
* IONeXTPartitionScheme::probe(IOService
* provider
, SInt32
* score
)
106 // Determine whether the provider media contains a NeXT partition map. If
107 // it does, we return "this" to indicate success, otherwise we return zero.
110 // State our assumptions.
112 assert(OSDynamicCast(IOMedia
, provider
));
114 // Ask our superclass' opinion.
116 if ( super::probe(provider
, score
) == 0 ) return 0;
118 // Scan the provider media for a NeXT partition map.
120 _partitions
= scan(score
);
122 return ( _partitions
) ? this : 0;
125 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
127 bool IONeXTPartitionScheme::start(IOService
* provider
)
130 // Publish the new media objects which represent our partitions.
134 OSIterator
* partitionIterator
;
136 // State our assumptions.
140 // Ask our superclass' opinion.
142 if ( super::start(provider
) == false ) return false;
144 // Attach and register the new media objects representing our partitions.
146 partitionIterator
= OSCollectionIterator::withCollection(_partitions
);
147 if ( partitionIterator
== 0 ) return false;
149 while ( (partition
= (IOMedia
*) partitionIterator
->getNextObject()) )
151 if ( partition
->attach(this) )
153 partition
->registerService();
157 partitionIterator
->release();
163 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
165 OSSet
* IONeXTPartitionScheme::scan(SInt32
* score
)
168 // Scan the provider media for a NeXT partition map. Returns the set
169 // of media objects representing each of the partitions (the retain for
170 // the set is passed to the caller), or null should no partition map be
171 // found. The default probe score can be adjusted up or down, based on
172 // the confidence of the scan.
175 IOBufferMemoryDescriptor
* buffer
= 0;
176 UInt32 bufferSize
= 0;
177 UInt64 labelBase
= 0;
178 UInt32 labelBlock
= 0;
179 UInt16
* labelCheckPtr
= 0;
180 UInt32 labelCheckSize
= 0;
181 bool labelFound
= false;
182 UInt32 labelIndex
= 0;
183 disk_label_t
* labelMap
= 0;
184 IOMedia
* media
= getProvider();
185 UInt64 mediaBlockSize
= media
->getPreferredBlockSize();
186 bool mediaIsOpen
= false;
187 OSSet
* partitions
= 0;
188 IOReturn status
= kIOReturnError
;
190 // Determine whether this media is formatted.
192 if ( media
->isFormatted() == false ) goto scanErr
;
194 // Determine whether this media has an appropriate block size.
196 if ( (mediaBlockSize
% DEV_BSIZE
) ) goto scanErr
;
198 // Allocate a buffer large enough to hold one map, rounded to a media block.
200 bufferSize
= IORound(sizeof(disk_label_t
), mediaBlockSize
);
201 buffer
= IOBufferMemoryDescriptor::withCapacity(
202 /* capacity */ bufferSize
,
203 /* withDirection */ kIODirectionIn
);
204 if ( buffer
== 0 ) goto scanErr
;
206 // Allocate a set to hold the set of media objects representing partitions.
208 partitions
= OSSet::withCapacity(1);
209 if ( partitions
== 0 ) goto scanErr
;
211 // Open the media with read access.
213 mediaIsOpen
= media
->open(this, 0, kIOStorageAccessReader
);
214 if ( mediaIsOpen
== false ) goto scanErr
;
216 // Compute this partition's absolute offset with respect to the whole media,
217 // since the disk_label structure requires this information; we go down the
218 // service hierarchy summing bases until we reach the whole media object.
220 for (IOService
* service
= media
; service
; service
= service
->getProvider())
222 if ( OSDynamicCast(IOMedia
, service
) ) // (is this a media object?)
224 labelBase
+= ((IOMedia
*)service
)->getBase();
225 if ( ((IOMedia
*)service
)->isWhole() ) break;
229 // Scan the media for a NeXT partition map.
231 // In the spirit of minimizing reads, we only check the first of the four
232 // possible label positions. Backwards compatibility with old NeXT-style
233 // i386 disks, including redundancy for NeXT-style disks in general, is a
236 for ( labelIndex
= 0; labelIndex
< 1; labelIndex
++ ) // (first map only)
238 // Read the next NeXT map into our buffer.
240 ///m:2333367:workaround:commented:start
241 // status = media->read(this, labelIndex * bufferSize, buffer);
242 ///m:2333367:workaround:commented:stop
243 ///m:2333367:workaround:added:start
244 status
= media
->IOStorage::read(this, labelIndex
* bufferSize
, buffer
);
245 ///m:2333367:workaround:added:stop
246 if ( status
!= kIOReturnSuccess
) goto scanErr
;
248 labelBlock
= ((labelIndex
* bufferSize
) + labelBase
) / mediaBlockSize
;
249 labelMap
= (disk_label_t
*) buffer
->getBytesNoCopy();
251 // Determine whether the partition map signature is present.
253 if ( OSSwapBigToHostInt32(labelMap
->dl_version
) == DL_V3
)
255 labelCheckPtr
= &(labelMap
->dl_v3_checksum
);
257 else if ( OSSwapBigToHostInt32(labelMap
->dl_version
) == DL_V2
||
258 OSSwapBigToHostInt32(labelMap
->dl_version
) == DL_V1
)
260 labelCheckPtr
= &(labelMap
->dl_checksum
);
267 labelCheckSize
= (UInt8
*) labelCheckPtr
-
268 (UInt8
*) labelMap
- sizeof(UInt16
);
270 // Determine whether the partition map block position is correct.
272 if ( OSSwapBigToHostInt32(labelMap
->dl_label_blkno
) != labelBlock
)
277 // Determine whether the partition map checksum is correct.
279 labelMap
->dl_label_blkno
= OSSwapHostToBigInt32(0);
281 if ( checksum16(labelMap
, labelCheckSize
) != *labelCheckPtr
)
286 labelMap
->dl_label_blkno
= labelBlock
;
292 if ( labelFound
== false )
297 // Scan for valid partition entries in the partition map.
299 for ( unsigned index
= 0; index
< NPART
; index
++ )
301 if ( isPartitionUsed(labelMap
->dl_part
+ index
) )
303 // Determine whether the partition is corrupt (fatal).
305 if ( isPartitionCorrupt(
306 /* partition */ labelMap
->dl_part
+ index
,
307 /* partitionID */ index
+ 1,
308 /* labelBase */ labelBase
,
309 /* labelMap */ labelMap
) )
314 // Determine whether the partition is invalid (skipped).
316 if ( isPartitionInvalid(
317 /* partition */ labelMap
->dl_part
+ index
,
318 /* partitionID */ index
+ 1,
319 /* labelBase */ labelBase
,
320 /* labelMap */ labelMap
) )
325 // Create a media object to represent this partition.
327 IOMedia
* newMedia
= instantiateMediaObject(
328 /* partition */ labelMap
->dl_part
+ index
,
329 /* partitionID */ index
+ 1,
330 /* labelBase */ labelBase
,
331 /* labelMap */ labelMap
);
335 partitions
->setObject(newMedia
);
341 // Release our resources.
350 // Release our resources.
352 if ( mediaIsOpen
) media
->close(this);
353 if ( partitions
) partitions
->release();
354 if ( buffer
) buffer
->release();
359 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
361 bool IONeXTPartitionScheme::isPartitionUsed(partition_t
* partition
)
364 // Ask whether the given partition is used.
367 return ( (SInt32
) OSSwapBigToHostInt32(partition
->p_base
) >= 0 &&
368 (SInt32
) OSSwapBigToHostInt32(partition
->p_size
) > 0 );
371 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
373 bool IONeXTPartitionScheme::isPartitionCorrupt(
374 partition_t
* /* partition */ ,
375 UInt32
/* partitionID */ ,
376 UInt64
/* labelBase */ ,
377 disk_label_t
* /* labelMap */ )
380 // Ask whether the given partition appears to be corrupt. A partition that
381 // is corrupt will cause the failure of the NeXT partition map recognition
388 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
390 bool IONeXTPartitionScheme::isPartitionInvalid( partition_t
* partition
,
393 disk_label_t
* labelMap
)
396 // Ask whether the given partition appears to be invalid. A partition that
397 // is invalid will cause it to be skipped in the scan, but will not cause a
398 // failure of the NeXT partition map recognition.
401 IOMedia
* media
= getProvider();
402 UInt64 partitionBase
= 0;
403 UInt64 partitionSize
= 0;
405 // Compute the absolute byte position and size of the new partition.
407 partitionBase
= OSSwapBigToHostInt32(partition
->p_base
) +
408 OSSwapBigToHostInt16(labelMap
->dl_front
);
409 partitionSize
= OSSwapBigToHostInt32(partition
->p_size
);
410 partitionBase
*= OSSwapBigToHostInt32(labelMap
->dl_secsize
);
411 partitionSize
*= OSSwapBigToHostInt32(labelMap
->dl_secsize
);
413 // Determine whether the new partition leaves the confines of the container.
415 if ( partitionBase
< labelBase
) return true; // (absolute partitionBase)
417 // Compute the relative byte position of the new partition.
419 partitionBase
-= labelBase
; // (relative partitionBase)
421 // Determine whether the new partition leaves the confines of the container.
423 if ( partitionBase
+ partitionSize
> media
->getSize() ) return true;
428 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
430 IOMedia
* IONeXTPartitionScheme::instantiateMediaObject(
431 partition_t
* partition
,
434 disk_label_t
* labelMap
)
437 // Instantiate a new media object to represent the given partition.
440 IOMedia
* media
= getProvider();
441 UInt64 partitionBase
= 0;
442 UInt64 partitionBlockSize
= OSSwapBigToHostInt32(labelMap
->dl_secsize
);
443 char * partitionHint
= 0;
444 char * partitionName
= 0;
445 UInt64 partitionSize
= 0;
447 // Compute the relative byte position and size of the new partition.
449 partitionBase
= OSSwapBigToHostInt32(partition
->p_base
) +
450 OSSwapBigToHostInt16(labelMap
->dl_front
);
451 partitionSize
= OSSwapBigToHostInt32(partition
->p_size
);
452 partitionBase
*= OSSwapBigToHostInt32(labelMap
->dl_secsize
);
453 partitionSize
*= OSSwapBigToHostInt32(labelMap
->dl_secsize
);
454 partitionBase
-= labelBase
;
456 // Look up a type for the new partition.
458 OSDictionary
* hintTable
= OSDynamicCast(
459 /* type */ OSDictionary
,
460 /* instance */ getProperty(kIONeXTPartitionSchemeContentTable
) );
464 OSString
* hintValue
= OSDynamicCast(
466 /* instance */ hintTable
->getObject(partition
->p_type
) );
468 if ( hintValue
) partitionHint
= (char *) hintValue
->getCStringNoCopy();
471 // Look up a name for the new partition.
473 if ( partition
->p_mountpt
[0] )
474 partitionName
= partition
->p_mountpt
;
475 else if ( labelMap
->dl_label
[0] )
476 partitionName
= labelMap
->dl_label
;
478 // Create the new media object.
480 IOMedia
* newMedia
= instantiateDesiredMediaObject(
481 /* partition */ partition
,
482 /* partitionID */ partitionID
,
483 /* labelBase */ labelBase
,
484 /* labelMap */ labelMap
);
489 /* base */ partitionBase
,
490 /* size */ partitionSize
,
491 /* preferredBlockSize */ partitionBlockSize
,
492 /* isEjectable */ media
->isEjectable(),
494 /* isWritable */ media
->isWritable(),
495 /* contentHint */ partitionHint
) )
497 // Set a name for this partition.
500 sprintf(name
, "Untitled %ld", partitionID
);
501 newMedia
->setName(partitionName
? partitionName
: name
);
503 // Set a location value (the partition number) for this partition.
506 sprintf(location
, "%ld", partitionID
);
507 newMedia
->setLocation(location
);
509 // Set the "Partition ID" key for this partition.
511 newMedia
->setProperty(kIOMediaPartitionIDKey
, partitionID
, 32);
523 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
525 IOMedia
* IONeXTPartitionScheme::instantiateDesiredMediaObject(
526 partition_t
* partition
,
529 disk_label_t
* labelMap
)
532 // Allocate a new media object (called from instantiateMediaObject).
538 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
540 UInt16
IONeXTPartitionScheme::checksum16(void * data
, UInt32 bytes
) const
543 // Compute a big-endian, 16-bit checksum over the specified data range.
548 UInt16
* wp
= (UInt16
*) data
;
552 sum1
+= OSSwapBigToHostInt16(*wp
);
553 bytes
-= sizeof(UInt16
);
557 sum2
= ((sum1
& 0xFFFF0000) >> 16) + (sum1
& 0xFFFF);
565 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
567 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme
, 0);
569 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
571 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme
, 1);
573 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
575 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme
, 2);
577 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
579 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme
, 3);
581 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
583 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme
, 4);
585 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
587 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme
, 5);
589 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
591 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme
, 6);
593 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
595 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme
, 7);
597 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
599 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme
, 8);
601 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
603 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme
, 9);
605 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
607 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme
, 10);
609 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
611 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme
, 11);
613 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
615 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme
, 12);
617 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
619 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme
, 13);
621 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
623 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme
, 14);
625 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
627 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme
, 15);