]> git.saurik.com Git - apple/xnu.git/blob - iokit/Families/IOStorage/IONeXTPartitionScheme.cpp
xnu-123.5.tar.gz
[apple/xnu.git] / iokit / Families / IOStorage / IONeXTPartitionScheme.cpp
1 /*
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
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>
29
30 #define super IOPartitionScheme
31 OSDefineMetaClassAndStructors(IONeXTPartitionScheme, IOPartitionScheme);
32
33 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
34 // Notes
35 //
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
57 //
58
59 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
60
61 #define kIONeXTPartitionSchemeContentTable "Content Table"
62
63 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
64
65 bool IONeXTPartitionScheme::init(OSDictionary * properties = 0)
66 {
67 //
68 // Initialize this object's minimal state.
69 //
70
71 // State our assumptions.
72
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)
76
77 // Ask our superclass' opinion.
78
79 if ( super::init(properties) == false ) return false;
80
81 // Initialize our state.
82
83 _partitions = 0;
84
85 return true;
86 }
87
88 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
89
90 void IONeXTPartitionScheme::free()
91 {
92 //
93 // Free all of this object's outstanding resources.
94 //
95
96 if ( _partitions ) _partitions->release();
97
98 super::free();
99 }
100
101 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
102
103 IOService * IONeXTPartitionScheme::probe(IOService * provider, SInt32 * score)
104 {
105 //
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.
108 //
109
110 // State our assumptions.
111
112 assert(OSDynamicCast(IOMedia, provider));
113
114 // Ask our superclass' opinion.
115
116 if ( super::probe(provider, score) == 0 ) return 0;
117
118 // Scan the provider media for a NeXT partition map.
119
120 _partitions = scan(score);
121
122 return ( _partitions ) ? this : 0;
123 }
124
125 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
126
127 bool IONeXTPartitionScheme::start(IOService * provider)
128 {
129 //
130 // Publish the new media objects which represent our partitions.
131 //
132
133 IOMedia * partition;
134 OSIterator * partitionIterator;
135
136 // State our assumptions.
137
138 assert(_partitions);
139
140 // Ask our superclass' opinion.
141
142 if ( super::start(provider) == false ) return false;
143
144 // Attach and register the new media objects representing our partitions.
145
146 partitionIterator = OSCollectionIterator::withCollection(_partitions);
147 if ( partitionIterator == 0 ) return false;
148
149 while ( (partition = (IOMedia *) partitionIterator->getNextObject()) )
150 {
151 if ( partition->attach(this) )
152 {
153 partition->registerService();
154 }
155 }
156
157 partitionIterator->release();
158
159 return true;
160 }
161
162
163 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
164
165 OSSet * IONeXTPartitionScheme::scan(SInt32 * score)
166 {
167 //
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.
173 //
174
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;
189
190 // Determine whether this media is formatted.
191
192 if ( media->isFormatted() == false ) goto scanErr;
193
194 // Determine whether this media has an appropriate block size.
195
196 if ( (mediaBlockSize % DEV_BSIZE) ) goto scanErr;
197
198 // Allocate a buffer large enough to hold one map, rounded to a media block.
199
200 bufferSize = IORound(sizeof(disk_label_t), mediaBlockSize);
201 buffer = IOBufferMemoryDescriptor::withCapacity(
202 /* capacity */ bufferSize,
203 /* withDirection */ kIODirectionIn );
204 if ( buffer == 0 ) goto scanErr;
205
206 // Allocate a set to hold the set of media objects representing partitions.
207
208 partitions = OSSet::withCapacity(1);
209 if ( partitions == 0 ) goto scanErr;
210
211 // Open the media with read access.
212
213 mediaIsOpen = media->open(this, 0, kIOStorageAccessReader);
214 if ( mediaIsOpen == false ) goto scanErr;
215
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.
219
220 for (IOService * service = media; service; service = service->getProvider())
221 {
222 if ( OSDynamicCast(IOMedia, service) ) // (is this a media object?)
223 {
224 labelBase += ((IOMedia *)service)->getBase();
225 if ( ((IOMedia *)service)->isWhole() ) break;
226 }
227 }
228
229 // Scan the media for a NeXT partition map.
230 //
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
234 // non-goal.
235
236 for ( labelIndex = 0; labelIndex < 1; labelIndex++ ) // (first map only)
237 {
238 // Read the next NeXT map into our buffer.
239
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;
247
248 labelBlock = ((labelIndex * bufferSize) + labelBase) / mediaBlockSize;
249 labelMap = (disk_label_t *) buffer->getBytesNoCopy();
250
251 // Determine whether the partition map signature is present.
252
253 if ( OSSwapBigToHostInt32(labelMap->dl_version) == DL_V3 )
254 {
255 labelCheckPtr = &(labelMap->dl_v3_checksum);
256 }
257 else if ( OSSwapBigToHostInt32(labelMap->dl_version) == DL_V2 ||
258 OSSwapBigToHostInt32(labelMap->dl_version) == DL_V1 )
259 {
260 labelCheckPtr = &(labelMap->dl_checksum);
261 }
262 else
263 {
264 continue;
265 }
266
267 labelCheckSize = (UInt8 *) labelCheckPtr -
268 (UInt8 *) labelMap - sizeof(UInt16);
269
270 // Determine whether the partition map block position is correct.
271
272 if ( OSSwapBigToHostInt32(labelMap->dl_label_blkno) != labelBlock )
273 {
274 continue;
275 }
276
277 // Determine whether the partition map checksum is correct.
278
279 labelMap->dl_label_blkno = OSSwapHostToBigInt32(0);
280
281 if ( checksum16(labelMap, labelCheckSize) != *labelCheckPtr )
282 {
283 continue;
284 }
285
286 labelMap->dl_label_blkno = labelBlock;
287
288 labelFound = true;
289 break;
290 }
291
292 if ( labelFound == false )
293 {
294 goto scanErr;
295 }
296
297 // Scan for valid partition entries in the partition map.
298
299 for ( unsigned index = 0; index < NPART; index++ )
300 {
301 if ( isPartitionUsed(labelMap->dl_part + index) )
302 {
303 // Determine whether the partition is corrupt (fatal).
304
305 if ( isPartitionCorrupt(
306 /* partition */ labelMap->dl_part + index,
307 /* partitionID */ index + 1,
308 /* labelBase */ labelBase,
309 /* labelMap */ labelMap ) )
310 {
311 goto scanErr;
312 }
313
314 // Determine whether the partition is invalid (skipped).
315
316 if ( isPartitionInvalid(
317 /* partition */ labelMap->dl_part + index,
318 /* partitionID */ index + 1,
319 /* labelBase */ labelBase,
320 /* labelMap */ labelMap ) )
321 {
322 continue;
323 }
324
325 // Create a media object to represent this partition.
326
327 IOMedia * newMedia = instantiateMediaObject(
328 /* partition */ labelMap->dl_part + index,
329 /* partitionID */ index + 1,
330 /* labelBase */ labelBase,
331 /* labelMap */ labelMap );
332
333 if ( newMedia )
334 {
335 partitions->setObject(newMedia);
336 newMedia->release();
337 }
338 }
339 }
340
341 // Release our resources.
342
343 media->close(this);
344 buffer->release();
345
346 return partitions;
347
348 scanErr:
349
350 // Release our resources.
351
352 if ( mediaIsOpen ) media->close(this);
353 if ( partitions ) partitions->release();
354 if ( buffer ) buffer->release();
355
356 return 0;
357 }
358
359 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
360
361 bool IONeXTPartitionScheme::isPartitionUsed(partition_t * partition)
362 {
363 //
364 // Ask whether the given partition is used.
365 //
366
367 return ( (SInt32) OSSwapBigToHostInt32(partition->p_base) >= 0 &&
368 (SInt32) OSSwapBigToHostInt32(partition->p_size) > 0 );
369 }
370
371 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
372
373 bool IONeXTPartitionScheme::isPartitionCorrupt(
374 partition_t * /* partition */ ,
375 UInt32 /* partitionID */ ,
376 UInt64 /* labelBase */ ,
377 disk_label_t * /* labelMap */ )
378 {
379 //
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
382 // altogether.
383 //
384
385 return false;
386 }
387
388 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
389
390 bool IONeXTPartitionScheme::isPartitionInvalid( partition_t * partition,
391 UInt32 partitionID,
392 UInt64 labelBase,
393 disk_label_t * labelMap )
394 {
395 //
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.
399 //
400
401 IOMedia * media = getProvider();
402 UInt64 partitionBase = 0;
403 UInt64 partitionSize = 0;
404
405 // Compute the absolute byte position and size of the new partition.
406
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);
412
413 // Determine whether the new partition leaves the confines of the container.
414
415 if ( partitionBase < labelBase ) return true; // (absolute partitionBase)
416
417 // Compute the relative byte position of the new partition.
418
419 partitionBase -= labelBase; // (relative partitionBase)
420
421 // Determine whether the new partition leaves the confines of the container.
422
423 if ( partitionBase + partitionSize > media->getSize() ) return true;
424
425 return false;
426 }
427
428 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
429
430 IOMedia * IONeXTPartitionScheme::instantiateMediaObject(
431 partition_t * partition,
432 UInt32 partitionID,
433 UInt64 labelBase,
434 disk_label_t * labelMap )
435 {
436 //
437 // Instantiate a new media object to represent the given partition.
438 //
439
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;
446
447 // Compute the relative byte position and size of the new partition.
448
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;
455
456 // Look up a type for the new partition.
457
458 OSDictionary * hintTable = OSDynamicCast(
459 /* type */ OSDictionary,
460 /* instance */ getProperty(kIONeXTPartitionSchemeContentTable) );
461
462 if ( hintTable )
463 {
464 OSString * hintValue = OSDynamicCast(
465 /* type */ OSString,
466 /* instance */ hintTable->getObject(partition->p_type) );
467
468 if ( hintValue ) partitionHint = (char *) hintValue->getCStringNoCopy();
469 }
470
471 // Look up a name for the new partition.
472
473 if ( partition->p_mountpt[0] )
474 partitionName = partition->p_mountpt;
475 else if ( labelMap->dl_label[0] )
476 partitionName = labelMap->dl_label;
477
478 // Create the new media object.
479
480 IOMedia * newMedia = instantiateDesiredMediaObject(
481 /* partition */ partition,
482 /* partitionID */ partitionID,
483 /* labelBase */ labelBase,
484 /* labelMap */ labelMap );
485
486 if ( newMedia )
487 {
488 if ( newMedia->init(
489 /* base */ partitionBase,
490 /* size */ partitionSize,
491 /* preferredBlockSize */ partitionBlockSize,
492 /* isEjectable */ media->isEjectable(),
493 /* isWhole */ false,
494 /* isWritable */ media->isWritable(),
495 /* contentHint */ partitionHint ) )
496 {
497 // Set a name for this partition.
498
499 char name[24];
500 sprintf(name, "Untitled %ld", partitionID);
501 newMedia->setName(partitionName ? partitionName : name);
502
503 // Set a location value (the partition number) for this partition.
504
505 char location[12];
506 sprintf(location, "%ld", partitionID);
507 newMedia->setLocation(location);
508
509 // Set the "Partition ID" key for this partition.
510
511 newMedia->setProperty(kIOMediaPartitionIDKey, partitionID, 32);
512 }
513 else
514 {
515 newMedia->release();
516 newMedia = 0;
517 }
518 }
519
520 return newMedia;
521 }
522
523 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
524
525 IOMedia * IONeXTPartitionScheme::instantiateDesiredMediaObject(
526 partition_t * partition,
527 UInt32 partitionID,
528 UInt64 labelBase,
529 disk_label_t * labelMap )
530 {
531 //
532 // Allocate a new media object (called from instantiateMediaObject).
533 //
534
535 return new IOMedia;
536 }
537
538 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
539
540 UInt16 IONeXTPartitionScheme::checksum16(void * data, UInt32 bytes) const
541 {
542 //
543 // Compute a big-endian, 16-bit checksum over the specified data range.
544 //
545
546 UInt32 sum1 = 0;
547 UInt32 sum2;
548 UInt16 * wp = (UInt16 *) data;
549
550 while ( bytes >= 2 )
551 {
552 sum1 += OSSwapBigToHostInt16(*wp);
553 bytes -= sizeof(UInt16);
554 wp++;
555 }
556
557 sum2 = ((sum1 & 0xFFFF0000) >> 16) + (sum1 & 0xFFFF);
558
559 if ( sum2 > 65535 )
560 sum2 -= 65535;
561
562 return sum2;
563 }
564
565 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
566
567 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme, 0);
568
569 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
570
571 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme, 1);
572
573 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
574
575 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme, 2);
576
577 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
578
579 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme, 3);
580
581 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
582
583 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme, 4);
584
585 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
586
587 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme, 5);
588
589 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
590
591 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme, 6);
592
593 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
594
595 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme, 7);
596
597 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
598
599 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme, 8);
600
601 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
602
603 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme, 9);
604
605 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
606
607 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme, 10);
608
609 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
610
611 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme, 11);
612
613 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
614
615 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme, 12);
616
617 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
618
619 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme, 13);
620
621 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
622
623 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme, 14);
624
625 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
626
627 OSMetaClassDefineReservedUnused(IONeXTPartitionScheme, 15);