1/*
2 * Copyright (c) 2001-2007 Apple 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
24#include "AppleRAID.h"
25
26
27enum {
28    kAppleRAIDHeaderV1_0_0	= 0x00010000,
29    kAppleRAIDMaxOFPath		= 0x200,
30};
31
32// RAID levels  (version 1)
33enum {
34    kAppleRAIDStripe		= 0x00000000,
35    kAppleRAIDMirror		= 0x00000001,
36    kAppleRAIDConcat		= 0x00000100
37};
38
39struct AppleRAIDHeaderV1 {
40    char	raidSignature[16];		// 0x0000 - kAppleRAIDSignature
41    UInt32	raidHeaderSize;			// 0x0010 - Defaults to kAppleRAIDHeaderSize
42    UInt32	raidHeaderVersion;		// 0x0014 - kAppleRAIDHeaderV1_0_0
43    UInt32	raidHeaderSequence;		// 0x0018 - 0 member is bad, >0 member could be good
44    UInt32	raidLevel;			// 0x001C - one of kAppleRAIDStripe, kAppleRAIDMirror or kAppleRAIDConcat
45    uuid_t	raidUUID;			// 0x0020 - 128 bit univeral unique identifier
46    char	raidSetName[32];		// 0x0030 - Null Terminated 31 Character UTF8 String
47    UInt32	raidMemberCount;		// 0x0050 - Number of members in set
48    UInt32	raidMemberIndex;		// 0x0054 - 0 <= raidMemberIndex < raidMemberCount
49    UInt32	raidChunkSize;			// 0x0058 - Usually 32 KB
50    UInt32	raidChunkCount;			// 0x005C - Number of full chunks in set
51    UInt32	reserved1[104];			// 0x0060 - inited to zero, but preserved on update
52    char	raidOFPaths[0];			// 0x0200 - Allow kAppleRAIDMaxOFPath for each member
53                                                //        - Zero fill to size of header
54};
55typedef struct AppleRAIDHeaderV1 AppleRAIDHeaderV1;
56
57
58#define super IOStorage
59OSDefineMetaClassAndStructors(AppleRAIDMember, IOStorage);
60
61
62bool AppleRAIDMember::init(OSDictionary * properties)
63{
64    IOLog1("AppleRAIDMember::init(%p) isSet = %s\n", this, isRAIDSet() ? "yes":"no");
65
66    if (super::init(properties) == false) return false;
67
68    // get the controller object
69    arController = gAppleRAIDGlobals.getController();
70    if (!arController) return false;
71
72    arHeader = OSDictionary::withCapacity(32);
73    if (!arHeader) return false;
74
75    arTarget = 0;
76    arBaseOffset = 0xdeaddeaddeadbeefLL;
77    arHeaderOffset = 0xdeaddeaddeadbeefLL;
78
79    arIsWritable = false;
80    arIsEjectable = false;
81    arNativeBlockSize = 0;
82    arSyncronizeCacheThreadCall = 0;
83
84    arMemberIndex = 0xffffffff;
85
86#ifdef DEBUG
87    IOSleep(500);  // let the system log catch up
88#endif
89
90    return true;
91}
92
93
94void AppleRAIDMember::free(void)
95{
96    IOLog1("AppleRAIDMember::free(%p)\n", this);
97
98    if (arHeaderBuffer) arHeaderBuffer->release();
99    if (arHeader) arHeader->release();
100
101    if (arSyncronizeCacheThreadCall) {
102	thread_call_free(arSyncronizeCacheThreadCall);
103	arSyncronizeCacheThreadCall = 0;
104    }
105
106    gAppleRAIDGlobals.releaseController();
107
108    super::free();
109}
110
111
112//*************************************************************************************************************
113//***************************************************************************************************************
114//***************************************************************************************************************
115
116
117bool AppleRAIDMember::start(IOService * provider)
118{
119    IOLog1("AppleRAIDMember::start(%p) isSet = %s\n", this, isRAIDSet() ? "yes":"no");
120
121    if (!isRAIDSet()) {
122
123	assert(provider);
124	arTarget = (IOMedia *)provider;
125
126	if (super::start(provider) == false) return false;
127
128	arIsWritable = arTarget->isWritable();
129	arIsEjectable = arTarget->isEjectable();
130	arNativeBlockSize = arTarget->getPreferredBlockSize();
131
132	if (!arSyncronizeCacheThreadCall) {
133	    thread_call_func_t syncCacheMethod = OSMemberFunctionCast(thread_call_func_t, this, &AppleRAIDMember::synchronizeCacheCallout);
134	    arSyncronizeCacheThreadCall = thread_call_allocate(syncCacheMethod, (thread_call_param_t)this);
135	    if (arSyncronizeCacheThreadCall == 0) return false;
136	}
137    }
138
139    arIsRAIDMember = false;
140    arMemberState = kAppleRAIDMemberStateClosed;
141    setProperty(kAppleRAIDMemberStatusKey, kAppleRAIDStatusOnline);
142
143    //  if no raid header just return
144    if (readRAIDHeader()) {
145	return false;
146    }
147
148#ifdef DEBUG
149    if (isRAIDSet()) IOLog1("AppleRAIDMember::start(%p) this set is part of stacked raid.\n", this);
150#endif
151
152    arIsRAIDMember = true;
153
154    return (arController->newMember(this) == kIOReturnSuccess);
155}
156
157
158bool AppleRAIDMember::requestTerminate(IOService *provider, IOOptionBits options)
159{
160    IOLog1("AppleRAIDMember::requestTerminate(%p) isSet = %s\n", this, isRAIDSet() ? "yes":"no");
161
162    //
163    //  If the raid member is in use (opened) the stop method will not be called.
164    //  luckily, the client still gets notified here and we can fix things.
165    //  setting the state to closing will cause us to catch this on the next i/o
166    //
167
168    if (isRAIDSet()) {
169	return false;
170    } else {
171	if (arMemberState > kAppleRAIDMemberStateClosed) {
172	    changeMemberState(kAppleRAIDMemberStateClosing);
173	    arController->recoverMember(this);
174	}
175    }
176
177    return super::requestTerminate(provider, options);
178}
179
180
181void AppleRAIDMember::stop(IOService * provider)
182{
183    IOLog1("AppleRAIDMember::stop(%p) isMember = %s isSet = %s\n", this, isRAIDMember() ? "yes":"no", isRAIDSet() ? "yes":"no");
184
185    //
186    // when a member is pulled, it's parent set gets called here first.
187    // just ignore requests to stop sets, they are terminated in
188    // by the controller when all of the member/spares are gone.
189    //
190
191    if (!isRAIDSet()) {
192	arController->oldMember(this);
193	arIsRAIDMember = false;
194	super::stop(provider);		// this is noop
195    }
196}
197
198
199//***************************************************************************************************************
200//***************************************************************************************************************
201//***************************************************************************************************************
202
203
204bool AppleRAIDMember::handleOpen(IOService * /* client */,
205				 IOOptionBits options,
206				 void * argument)
207{
208    bool isOpen = arTarget->open(this, options, (IOStorageAccess) (uintptr_t) argument);
209    if (isOpen) changeMemberState(kAppleRAIDMemberStateOpen);
210    return isOpen;
211}
212
213
214bool AppleRAIDMember::handleIsOpen(const IOService * /* client */) const
215{
216    return arTarget->isOpen(this);
217}
218
219
220void AppleRAIDMember::handleClose(IOService * /* client */,
221				  IOOptionBits options)
222{
223    changeMemberState(kAppleRAIDMemberStateClosing);
224    arTarget->close(this, options);
225    changeMemberState(kAppleRAIDMemberStateClosed);
226}
227
228
229//***************************************************************************************************************
230//***************************************************************************************************************
231//***************************************************************************************************************
232
233void AppleRAIDMember::read(IOService * client,
234			   UInt64 byteStart,
235			   IOMemoryDescriptor * buffer,
236			   IOStorageAttributes * attributes,
237			   IOStorageCompletion * completion)
238{
239    IOLogRW("AppleRAIDMember::read, this %p start %llu size 0x%llx\n", this, byteStart, (UInt64)buffer->getLength());
240    assert(handleIsOpen(NULL));
241    arTarget->read(this, byteStart, buffer, attributes, completion);
242}
243
244
245void AppleRAIDMember::write(IOService * client,
246			    UInt64 byteStart,
247			    IOMemoryDescriptor * buffer,
248			    IOStorageAttributes * attributes,
249			    IOStorageCompletion * completion)
250{
251    IOLogRW("AppleRAIDMember::write, this %p start %llu size 0x%llx\n", this, byteStart, (UInt64)buffer->getLength());
252    assert(handleIsOpen(NULL));
253    arTarget->write(this, byteStart, buffer, attributes, completion);
254}
255
256
257IOReturn AppleRAIDMember::synchronizeCache(IOService * client)
258{
259    AppleRAIDSet * masterSet = OSDynamicCast(AppleRAIDSet, client);
260    assert(masterSet);
261
262    // we are running on the master's workloop
263    masterSet->synchronizeStarted();
264
265    bool bumped = thread_call_enter1(arSyncronizeCacheThreadCall, (thread_call_param_t)masterSet);
266
267    // we bumped another sync request, decrement count for them
268    assert(!bumped);
269    if (bumped) masterSet->synchronizeCompleted();
270
271    return 0;
272}
273
274IOReturn AppleRAIDMember::synchronizeCacheCallout(AppleRAIDSet *masterSet)
275{
276    assert(masterSet);
277
278    IOReturn result = arTarget->synchronizeCache(this);
279    if (result) IOLog("AppleRAIDMember::synchronizeCacheCallout: failed with %x on %s\n", result, getUUIDString());
280
281    masterSet->synchronizeCompleted();
282
283    return result;
284}
285
286
287//***************************************************************************************************************
288//***************************************************************************************************************
289//***************************************************************************************************************
290
291
292IOReturn AppleRAIDMember::readRAIDHeader(void)
293{
294    UInt64 size = getSize();
295    UInt64 headerSize = (UInt64)kAppleRAIDHeaderSize;
296
297    assert(size);
298
299    // look for version 2 header first
300    arHeaderOffset = ARHEADER_OFFSET(size);
301
302    // Allocate a buffer to read the AppleRAID Header.
303    if (arHeaderBuffer == 0) {
304	arHeaderBuffer = IOBufferMemoryDescriptor::withCapacity(headerSize, kIODirectionNone);
305	if (arHeaderBuffer == 0) return kIOReturnNoMemory;
306    }
307
308readheader:
309
310    IOLog2("AppleRAIDMember::readRAIDHeader(%p) size %llu  hdr off %llu\n", this, size, arHeaderOffset);
311
312    // Open the member
313    if (!getTarget()->open(this, 0, kIOStorageAccessReader)) return kIOReturnIOError;
314
315    // Read the raid header
316    arHeaderBuffer->setDirection(kIODirectionIn);
317    IOReturn rc = getTarget()->read(this, arHeaderOffset, arHeaderBuffer);
318
319    // Close the member.
320    getTarget()->close(this, 0);
321
322    if (rc) return rc;
323
324    // Make sure the AppleRAID Header contains the correct signature.
325    AppleRAIDHeaderV2 * header = (AppleRAIDHeaderV2 *)arHeaderBuffer->getBytesNoCopy();
326    if (strncmp(header->raidSignature, kAppleRAIDSignature, sizeof(header->raidSignature))) {
327
328	if (arHeaderOffset) {
329	    arHeaderOffset = 0;	// try for old v1 header at beginning of disk
330	    goto readheader;
331	}
332
333	if (!isRAIDSet()) {
334	    const OSString * diskname = getDiskName();
335	    if (diskname) IOLog("AppleRAIDMember::readRAIDHeader: failed, no header signature present on %s.\n",
336				diskname->getCStringNoCopy());
337	}
338
339	return kIOReturnUnformattedMedia;
340    }
341
342    if (arHeaderOffset) {
343	arBaseOffset = 0;
344	rc = parseRAIDHeaderV2();
345    } else {
346	arBaseOffset = kAppleRAIDHeaderSize;
347	rc = parseRAIDHeaderV1();
348    }
349    setHeaderProperty(kAppleRAIDBaseOffsetKey, arBaseOffset, 64);
350    setHeaderProperty(kAppleRAIDNativeBlockSizeKey, arNativeBlockSize, 64);
351
352    setProperty(kAppleRAIDMemberUUIDKey, getHeaderProperty(kAppleRAIDMemberUUIDKey));
353    setProperty(kAppleRAIDSetUUIDKey, getHeaderProperty(kAppleRAIDSetUUIDKey));
354
355    OSNumber * number = OSDynamicCast(OSNumber, getHeaderProperty(kAppleRAIDMemberIndexKey));
356    arMemberIndex= number ? number->unsigned32BitValue() : 0xffffffff;
357
358    IOLog1(">>>>> %s %s %s <<<<<\n", getSetNameString(), getSetUUIDString(), getUUIDString());
359    IOLog2("AppleRAIDMember::readRAIDHeader(%p): was %ssuccessful\n", this, rc ? "un" : "");
360    return rc;
361}
362
363IOReturn AppleRAIDMember::writeRAIDHeader(void)
364{
365    IOLog2("AppleRAIDMember::writeRAIDHeader(%p): entered.\n", this);
366
367    IOReturn rc = kIOReturnSuccess;
368
369    if ((arHeaderBuffer == 0) || (!handleIsOpen(0))) {
370	IOLog1("AppleRAIDMember::writeRAIDHeader(%p): aborting, rc = %x.\n", this, kIOReturnIOError);
371	return kIOReturnIOError;
372    }
373
374    if (arHeaderOffset) {
375	rc = buildOnDiskHeaderV2();
376    } else {
377	rc = buildOnDiskHeaderV1();
378    }
379
380    // write the raid header
381    if (!rc) {
382	arHeaderBuffer->setDirection(kIODirectionOut);
383	rc = getTarget()->write(this, arHeaderOffset, arHeaderBuffer);
384    }
385
386    // XXX if this fails we should change state or something?
387
388    IOLog1("AppleRAIDMember::writeRAIDHeader(%p): finished rc = %x.\n", this, rc);
389    return rc;
390}
391
392IOReturn AppleRAIDMember::updateRAIDHeader(OSDictionary * props)
393{
394    // merge props into member's property list
395    OSCollectionIterator * iter = OSCollectionIterator::withCollection(props);
396    if (!iter) return kIOReturnNoMemory;
397
398    while (const OSString * key = OSDynamicCast(OSString, iter->getNextObject())) {
399
400	// if the key starts with "AppleRAID-" add it to the incore header
401	const char * match = "AppleRAID-";
402	int matchSize = sizeof(match) - 1;
403
404	if (!strncmp(match, key->getCStringNoCopy(), matchSize)) {
405	    setHeaderProperty(key, props->getObject(key));
406	}
407    }
408    iter->release();
409
410    return kIOReturnSuccess;
411}
412
413IOReturn AppleRAIDMember::zeroRAIDHeader(void)
414{
415    IOLog1("AppleRAIDMember::zeroRAIDHeader(%p): entered.\n", this);
416
417    if (arHeaderBuffer == 0) {
418	IOLog1("AppleRAIDMember::zeroRAIDHeader(%p): no header buffer?\n", this);
419	return kIOReturnIOError;
420    }
421
422    // don't need to worry about read only/read-write state
423    // the member being removed from the set
424    bool alreadyOpen = handleIsOpen(0);
425    if ((!alreadyOpen) && (!getTarget()->open(this, 0, kIOStorageAccessReaderWriter))) {
426	IOLog("AppleRAIDMember::zeroRAIDHeader: failed trying to open member %s.\n", getUUIDString());
427	return kIOReturnIOError;
428    }
429
430    char * header = (char *)arHeaderBuffer->getBytesNoCopy();
431    bzero(header, arHeaderBuffer->getLength());
432
433    // zero out the raid header
434    arHeaderBuffer->setDirection(kIODirectionOut);
435    IOReturn rc = getTarget()->write(this, arHeaderOffset, arHeaderBuffer);
436
437    if (!alreadyOpen) getTarget()->close(this, 0);
438
439    arIsRAIDMember = false;
440
441    IOLog1("AppleRAIDMember::zeroRAIDHeader(%p): exiting, rc = %x.\n", this, rc);
442    return rc;
443}
444
445
446//***************************************************************************************************************
447//***************************************************************************************************************
448//***************************************************************************************************************
449
450
451#define ByteSwapHeaderV1(header) \
452{ \
453        (header)->raidHeaderSize	= OSSwapBigToHostInt32((header)->raidHeaderSize); \
454	(header)->raidHeaderVersion	= OSSwapBigToHostInt32((header)->raidHeaderVersion); \
455        (header)->raidHeaderSequence	= OSSwapBigToHostInt32((header)->raidHeaderSequence); \
456	(header)->raidLevel		= OSSwapBigToHostInt32((header)->raidLevel); \
457        (header)->raidMemberCount	= OSSwapBigToHostInt32((header)->raidMemberCount); \
458	(header)->raidMemberIndex	= OSSwapBigToHostInt32((header)->raidMemberIndex); \
459        (header)->raidChunkSize		= OSSwapBigToHostInt32((header)->raidChunkSize); \
460	(header)->raidChunkCount	= OSSwapBigToHostInt32((header)->raidChunkCount); \
461}
462
463IOReturn AppleRAIDMember::parseRAIDHeaderV1()
464{
465    IOLog1("AppleRAIDMember::parseRAIDHeaderV1(%p): entered.\n", this);
466
467    if (isRAIDSet()) return kIOReturnUnsupported;
468
469    AppleRAIDHeaderV1 * header = (AppleRAIDHeaderV1 *)arHeaderBuffer->getBytesNoCopy();
470
471    ByteSwapHeaderV1(header);
472
473    // check header size (not that it ever changes)
474    if (header->raidHeaderSize != kAppleRAIDHeaderSize) return kIOReturnUnformattedMedia;
475
476    // Make sure the header version is understood.
477    if (header->raidHeaderVersion != kAppleRAIDHeaderV1_0_0) return kIOReturnUnformattedMedia;
478    setHeaderProperty(kAppleRAIDHeaderVersionKey, header->raidHeaderVersion, 32);
479
480    // Make sure the header sequence is valid.  0 indicates a spare drive
481    if (header->raidHeaderSequence == 0) {
482	IOLog1("AppleRAIDMember::parseRAIDHeaderV1(%p): found a spare.\n", this);
483	setHeaderProperty(kAppleRAIDMemberTypeKey, kAppleRAIDSparesKey);
484	changeMemberState(kAppleRAIDMemberStateSpare);
485    }
486
487    setHeaderProperty(kAppleRAIDSequenceNumberKey, header->raidHeaderSequence, 32);
488
489    switch (header->raidLevel) {
490    case kAppleRAIDStripe:
491    {
492	setHeaderProperty(kAppleRAIDLevelNameKey, kAppleRAIDLevelNameStripe);
493	break;
494    }
495    case kAppleRAIDMirror:
496    {
497	setHeaderProperty(kAppleRAIDLevelNameKey, kAppleRAIDLevelNameMirror);
498	break;
499    }
500    default:
501	const OSString * diskname = getDiskName();
502	if (diskname) IOLog("AppleRAIDMember::parseRAIDHeaderV1: unknown raid type on %s.\n", diskname->getCStringNoCopy());
503	return kIOReturnUnformattedMedia;
504    }
505
506    char tmpString[kAppleRAIDMaxUUIDStringSize];
507    uuid_unparse(header->raidUUID, tmpString);
508    setHeaderProperty(kAppleRAIDSetUUIDKey, (char *)tmpString);
509
510    setHeaderProperty(kAppleRAIDSetNameKey, (char *)header->raidSetName);
511
512    setHeaderProperty(kAppleRAIDMemberCountKey, header->raidMemberCount, 32);
513    setHeaderProperty(kAppleRAIDMemberIndexKey, header->raidMemberIndex, 32);
514    setHeaderProperty(kAppleRAIDChunkSizeKey, header->raidChunkSize, 64);
515
516    UInt64 chunkCount = header->raidChunkCount;
517    if (header->raidLevel == kAppleRAIDStripe) {
518	chunkCount /= header->raidMemberCount;
519    }
520    setHeaderProperty(kAppleRAIDChunkCountKey, chunkCount, 64);
521
522    if (header->raidLevel == kAppleRAIDMirror) {
523	setHeaderProperty(kAppleRAIDSetTimeoutKey, 30, 32);
524	setHeaderProperty(kAppleRAIDSetAutoRebuildKey, kOSBooleanFalse);
525    }
526
527    // fake up the member unique name for old style headers (but only once)
528    if (!getUUID()) {
529	uuid_t fakeUUID;
530	uuid_generate(fakeUUID);
531	uuid_unparse(fakeUUID, tmpString);
532	setHeaderProperty(kAppleRAIDMemberUUIDKey, (char *)tmpString);
533    }
534
535    return kIOReturnSuccess;
536}
537
538IOReturn AppleRAIDMember::buildOnDiskHeaderV1(void)
539{
540    AppleRAIDHeaderV1 * header = (AppleRAIDHeaderV1 *)arHeaderBuffer->getBytesNoCopy();
541
542    OSNumber * number = OSDynamicCast(OSNumber, getHeaderProperty(kAppleRAIDSequenceNumberKey));
543    if (number) header->raidHeaderSequence = number->unsigned32BitValue();
544
545    ByteSwapHeaderV1(header);
546
547    return kIOReturnSuccess;
548}
549
550
551//***************************************************************************************************************
552
553
554IOReturn AppleRAIDMember::parseRAIDHeaderV2()
555{
556    IOLog1("AppleRAIDMember::parseRAIDHeaderV2(%p): entered.\n", this);
557
558    AppleRAIDHeaderV2 * headerBuffer = (AppleRAIDHeaderV2 *)arHeaderBuffer->getBytesNoCopy();
559
560    OSString * errmsg = 0;
561    OSDictionary * props = OSDynamicCast(OSDictionary, OSUnserializeXML(headerBuffer->plist, &errmsg));
562    if (!props) {
563	if (errmsg) {
564	    IOLog("AppleRAIDMember::parseRAIDHeaderV2 - RAID header parsing failed with %s\n", errmsg->getCStringNoCopy());
565	    errmsg->release();
566	}
567	return kIOReturnBadArgument;
568    }
569
570    // merge in member's property list
571    IOReturn rc = updateRAIDHeader(props);
572    props->release();
573    if (rc) return rc;
574
575    // ok, we are done parsing the header
576    // set up a few extra things
577
578    OSArray * members = OSDynamicCast(OSArray, getHeaderProperty(kAppleRAIDMembersKey));
579    if (members) setHeaderProperty(kAppleRAIDMemberCountKey, members->getCount(), 32);
580
581    OSString * tmpString = OSDynamicCast(OSString, getHeaderProperty(kAppleRAIDMemberTypeKey));
582    if (tmpString->isEqualTo(kAppleRAIDSparesKey)) {
583	IOLog1("AppleRAIDMember::parseRAIDHeaderV2(%p): found a spare.\n", this);
584	changeMemberState(kAppleRAIDMemberStateSpare);
585    }
586
587    return kIOReturnSuccess;
588}
589
590IOReturn AppleRAIDMember::buildOnDiskHeaderV2(void)
591{
592    IOLog2("AppleRAIDMember::buildOnDiskHeaderV2(%p) entered\n", this);
593
594    // make a copy of incore header and filter out the internal stuff
595    OSDictionary * copy = OSDictionary::withCapacity(arHeader->getCount());
596    if (!copy) return kIOReturnNoMemory;
597    OSCollectionIterator * iter = OSCollectionIterator::withCollection(arHeader);
598    if (!iter) return kIOReturnNoMemory;
599
600    while (const OSString * key = OSDynamicCast(OSString, iter->getNextObject())) {
601
602	// if the key starts with "AppleRAID-" copy it
603	const char * match = "AppleRAID-";
604	int matchSize = sizeof(match) - 1;
605
606	if (!strncmp(match, key->getCStringNoCopy(), matchSize)) {
607	    copy->setObject(key, arHeader->getObject(key));
608	}
609    }
610    iter->release();
611
612    // serialize header to on-disk format
613    OSSerialize * s = OSSerialize::withCapacity(kAppleRAIDHeaderSize);
614    if (!s) return kIOReturnNoMemory;
615
616    s->clearText();
617    if (!copy->serialize(s)) return kIOReturnInternalError;
618    copy->release();
619
620    if (s->getLength() >= (kAppleRAIDHeaderSize - sizeof(AppleRAIDHeaderV2))) return kIOReturnNoResources;
621
622    AppleRAIDHeaderV2 * headerBuffer = (AppleRAIDHeaderV2 *)arHeaderBuffer->getBytesNoCopy();
623
624    // set up header header
625    strlcpy(headerBuffer->raidSignature, kAppleRAIDSignature, sizeof(headerBuffer->raidSignature));
626    strlcpy(headerBuffer->raidUUID, getSetUUIDString(), sizeof(headerBuffer->raidUUID));
627    strlcpy(headerBuffer->memberUUID, getUUIDString(), sizeof(headerBuffer->memberUUID));
628
629    // calculate the byte size for the raid data
630    headerBuffer->size = getUsableSize();
631    if (headerBuffer->size == 0) return kIOReturnInternalError;
632
633    bcopy(s->text(), headerBuffer->plist, s->getLength());
634    UInt32 bzSize = kAppleRAIDHeaderSize - (UInt32)((char *)headerBuffer->plist - (char *)headerBuffer) - s->getLength();
635    bzero(headerBuffer->plist + s->getLength(), bzSize);
636
637    s->release();
638
639    ByteSwapHeaderV2(headerBuffer);
640
641    IOLog2("AppleRAIDMember::buildOnDiskHeaderV2(%p) successful.\n", this);
642    return kIOReturnSuccess;
643}
644
645
646//***************************************************************************************************************
647//***************************************************************************************************************
648//***************************************************************************************************************
649
650IOBufferMemoryDescriptor *
651AppleRAIDMember::readPrimaryMetaData()
652{
653    IOLog1("AppleRAIDMember::readPrimaryMetaData(%p) entered.\n", this);
654
655    // get the reserved size of primary data area
656    OSNumber * number = OSDynamicCast(OSNumber, getHeaderProperty(kAppleRAIDPrimaryMetaDataUsedKey));
657    if (!number) return NULL;
658    UInt64 primarySize = number->unsigned64BitValue();
659    if (primarySize == 0) return NULL;
660
661    primarySize = (((primarySize - 1) / 4096) + 1) * 4096;   // round up for i/o
662
663    // calculate the start of primary data
664    UInt64 primaryOffset = getUsableSize();
665    if (primaryOffset == 0) return NULL;
666
667    IOBufferMemoryDescriptor * primaryBuffer = 0;
668
669    // Allocate a buffer to read into
670    if (primaryBuffer == 0) {
671	primaryBuffer = IOBufferMemoryDescriptor::withCapacity(primarySize, kIODirectionNone);
672	if (primaryBuffer == 0) return NULL;
673    }
674
675    IOLog1("AppleRAIDMember::readPrimaryMetaData(%p) size %llu  primary off %llu\n", this, primarySize, primaryOffset);
676
677    // Open the member
678    if (!getTarget()->open(this, 0, kIOStorageAccessReader)) return NULL;
679
680    // Read in the primary data from member
681    primaryBuffer->setDirection(kIODirectionIn);
682    IOReturn rc = getTarget()->read(this, primaryOffset, primaryBuffer);
683
684    // Close the member.
685    getTarget()->close(this, 0);
686
687    if (rc) {
688	primaryBuffer->release();
689	return NULL;
690    }
691
692    // Make sure the AppleRAID Header contains the correct signature.
693    AppleRAIDPrimaryOnDisk * primary = (AppleRAIDPrimaryOnDisk *)primaryBuffer->getBytesNoCopy();
694    if (strncmp(primary->priMagic, kAppleRAIDPrimaryMagic, sizeof(primary->priMagic))) {
695	primaryBuffer->release();
696	return NULL;
697    }
698
699    IOLog1("AppleRAIDMember::readPrimaryMetaData(%p): was %ssuccessful\n", this, rc ? "un" : "");
700    return primaryBuffer;
701}
702
703
704IOReturn AppleRAIDMember::writePrimaryMetaData(IOBufferMemoryDescriptor * primaryBuffer)
705{
706    IOLog1("AppleRAIDMember::writePrimaryMetaData(%p) entered.\n", this);
707
708    // this code assumes that members are already opened for write
709    if ((primaryBuffer == 0) || (!handleIsOpen(0))) {
710	IOLog1("AppleRAIDMember::writePrimaryMetaData(%p): aborting, rc = %x.\n", this, kIOReturnInternalError);
711	return kIOReturnInternalError;
712    }
713
714    // get the reserved size of primary data area
715    OSNumber * number = OSDynamicCast(OSNumber, getHeaderProperty(kAppleRAIDPrimaryMetaDataUsedKey));
716    if (!number) return kIOReturnUnformattedMedia;
717    UInt64 primarySize = number->unsigned64BitValue();
718    if (primarySize == 0) return kIOReturnUnformattedMedia;
719
720    primarySize = (((primarySize - 1) / 4096) + 1) * 4096;   // round up for i/o
721
722    if (primaryBuffer->getLength() > primarySize) return kIOReturnInternalError;
723
724    // calculate the start of primary data
725    UInt64 primaryOffset = getUsableSize();
726    if (primaryOffset == 0) return kIOReturnUnformattedMedia;
727
728    // write the primary meta data to disk
729    primaryBuffer->setDirection(kIODirectionOut);
730    IOReturn rc = getTarget()->write(this, primaryOffset, primaryBuffer);
731
732    // XXX if this fails we should change state or something?
733
734    IOLog1("AppleRAIDMember::writePrimaryMetaData(%p): finished rc = %x.\n", this, rc);
735    return rc;
736}
737
738
739//***************************************************************************************************************
740//***************************************************************************************************************
741//***************************************************************************************************************
742
743
744OSDictionary * AppleRAIDMember::getHeader()
745{
746    return arHeader;
747}
748
749OSObject * AppleRAIDMember::getHeaderProperty(const OSString * aKey) const
750{
751    return arHeader->getObject(aKey);
752}
753
754OSObject * AppleRAIDMember::getHeaderProperty(const char * aKey) const
755{
756    return arHeader->getObject(aKey);
757}
758
759bool AppleRAIDMember::setHeaderProperty(const OSString * aKey, OSObject * anObject)
760{
761    return arHeader->setObject(aKey, anObject);
762}
763
764bool AppleRAIDMember::setHeaderProperty(const char * aKey, OSObject * anObject)
765{
766    return arHeader->setObject(aKey, anObject);
767}
768
769bool AppleRAIDMember::setHeaderProperty(const char * aKey, const char * cString)
770{
771    bool success = false;
772
773    OSString * aString = OSString::withCString(cString);
774    if (aString) {
775	success = arHeader->setObject(aKey, aString);
776	aString->release();
777    }
778
779    return success;
780}
781
782bool AppleRAIDMember::setHeaderProperty(const char * key, unsigned long long value, unsigned int numberOfBits)
783{
784    bool success = false;
785
786    OSNumber * number = OSNumber::withNumber(value, numberOfBits);
787    if (number) {
788	success = arHeader->setObject(key, number);
789	number->release();
790    }
791
792    return success;
793}
794
795
796//***************************************************************************************************************
797//***************************************************************************************************************
798//***************************************************************************************************************
799
800
801const OSString * AppleRAIDMember::getSetName(void)
802{
803    return OSDynamicCast(OSString, getHeaderProperty(kAppleRAIDSetNameKey));
804}
805
806const char * AppleRAIDMember::getSetNameString(void)
807{
808    const OSString * name = getSetName();
809
810    return name ? name->getCStringNoCopy() : "--internal error, set name not set--";
811}
812
813const OSString * AppleRAIDMember::getUUID(void)
814{
815    return OSDynamicCast(OSString, getHeaderProperty(kAppleRAIDMemberUUIDKey));
816}
817
818const char * AppleRAIDMember::getUUIDString(void)
819{
820    const OSString * uuid = getUUID();
821
822    return uuid ? uuid->getCStringNoCopy() : "--internal error, uuid not set--";
823}
824
825const OSString * AppleRAIDMember::getSetUUID(void)
826{
827    return OSDynamicCast(OSString, getHeaderProperty(kAppleRAIDSetUUIDKey));
828}
829
830const char * AppleRAIDMember::getSetUUIDString(void)
831{
832    const OSString * uuid = getSetUUID();
833
834    return uuid ? uuid->getCStringNoCopy() : "--internal error, set uuid not set--";
835}
836
837const OSString * AppleRAIDMember::getDiskName(void)
838{
839    return OSDynamicCast(OSString, arTarget->getProperty(kIOBSDNameKey));
840}
841
842void AppleRAIDMember::setMemberIndex(UInt32 index)
843{
844    arMemberIndex = index;
845    setHeaderProperty(kAppleRAIDMemberIndexKey, index, 32);
846}
847
848//***************************************************************************************************************
849//***************************************************************************************************************
850//***************************************************************************************************************
851
852
853IOStorage * AppleRAIDMember::getTarget(void) const
854{
855    return (IOStorage *)arTarget;
856}
857
858bool AppleRAIDMember::isRAIDSet(void)
859{
860    return false;
861}
862
863bool AppleRAIDMember::isRAIDMember(void)
864{
865    return arIsRAIDMember;
866}
867
868bool AppleRAIDMember::isSpare(void)
869{
870    return getMemberState() == kAppleRAIDMemberStateSpare;
871}
872
873bool AppleRAIDMember::isBroken(void)
874{
875    return getMemberState() == kAppleRAIDMemberStateBroken;
876}
877
878UInt64 AppleRAIDMember::getSize() const
879{
880    return arTarget->getSize();
881}
882
883UInt64 AppleRAIDMember::getUsableSize() const
884{
885    OSNumber * number = OSDynamicCast(OSNumber, getHeaderProperty(kAppleRAIDChunkCountKey));
886    if (!number) return 0;
887    UInt64 usable = number->unsigned64BitValue();
888
889    number = OSDynamicCast(OSNumber, getHeaderProperty(kAppleRAIDChunkSizeKey));
890    if (!number) return 0;
891    usable *= number->unsigned64BitValue();
892
893    return usable;
894}
895
896UInt64 AppleRAIDMember::getPrimaryMaxSize() const
897{
898    UInt64 primaryOffset = getUsableSize();
899    if (primaryOffset == 0) return 0;
900
901    return arHeaderOffset - primaryOffset;
902}
903
904UInt64 AppleRAIDMember::getSecondarySize() const
905{
906    OSNumber * number = OSDynamicCast(OSNumber, getHeaderProperty(kAppleRAIDSecondaryMetaDataSizeKey));
907    return number ? number->unsigned64BitValue() : 0;
908}
909
910//***************************************************************************************************************
911
912// IOMedia clones
913
914bool AppleRAIDMember::isEjectable() const
915{
916    return arIsEjectable;
917}
918
919bool AppleRAIDMember::isWritable() const
920{
921    return arIsWritable;
922}
923
924UInt64 AppleRAIDMember::getBase() const
925{
926    return arBaseOffset;
927}
928
929//***************************************************************************************************************
930
931#include <IOKit/IODeviceTreeSupport.h>
932
933bool
934AppleRAIDMember::addBootDeviceInfo(OSArray * bootArray)
935{
936    int length = kAppleRAIDMaxOFPath;
937    char ofPath[kAppleRAIDMaxOFPath];
938
939    // get the path
940    if (!arTarget || !arTarget->getPath(ofPath, &length, gIODTPlane)) {
941	return false;
942    }
943
944    UInt64 memberSize = arTarget->getSize();
945
946    IOLog2("AppleRAIDMember::addBootDeviceInfo %p, path = %s, size %llu\n", this, ofPath, memberSize);
947
948    OSString * string = OSString::withCString(ofPath);
949    OSNumber * number = OSNumber::withNumber(memberSize, 64);
950
951    if (bootArray && string && number) {
952	OSDictionary * dict = OSDictionary::withCapacity(2);
953	if (dict) {
954	    dict->setObject(kIOBootDevicePathKey, string);
955	    dict->setObject(kIOBootDeviceSizeKey, number);
956	    bootArray->setObject(dict);
957
958	    string->release();
959	    number->release();
960	    dict->release();
961
962	    return true;
963	}
964    }
965
966    return false;
967}
968
969//***************************************************************************************************************
970
971OSDictionary * AppleRAIDMember::getMemberProperties(void)
972{
973    OSDictionary * props = OSDictionary::withCapacity(16);
974    if (!props) return NULL;
975
976    props->setObject(kAppleRAIDHeaderVersionKey, getHeaderProperty(kAppleRAIDHeaderVersionKey));
977    props->setObject(kAppleRAIDMemberUUIDKey, getHeaderProperty(kAppleRAIDMemberUUIDKey));
978    props->setObject(kAppleRAIDSequenceNumberKey, getHeaderProperty(kAppleRAIDSequenceNumberKey));
979    props->setObject(kAppleRAIDMemberIndexKey, getHeaderProperty(kAppleRAIDMemberIndexKey));
980    props->setObject(kAppleRAIDChunkCountKey, getHeaderProperty(kAppleRAIDChunkCountKey));
981    props->setObject(kAppleRAIDMemberTypeKey, getHeaderProperty(kAppleRAIDMemberTypeKey));
982
983    props->setObject(kAppleRAIDSecondaryMetaDataSizeKey, getHeaderProperty(kAppleRAIDSecondaryMetaDataSizeKey));
984
985    props->setObject(kAppleRAIDMemberStatusKey, getProperty(kAppleRAIDMemberStatusKey));
986    props->setObject(kAppleRAIDRebuildStatus, getProperty(kAppleRAIDRebuildStatus));
987
988    props->setObject(kIOBSDNameKey, getDiskName());
989
990    if (isRAIDSet()) {
991	props->setObject(kAppleRAIDSetUUIDKey, getHeaderProperty(kAppleRAIDSetUUIDKey));
992    }
993
994    if (arMemberState == kAppleRAIDMemberStateSpare) {
995
996	// broken members get spared, but we really want them to look broken here
997	// scan to see if this members UUID matches in the member list
998	const OSString * uuid = getUUID();
999	OSArray * memberUUIDs = OSDynamicCast(OSArray, getHeaderProperty(kAppleRAIDMembersKey));
1000	if (uuid && memberUUIDs) {
1001	    UInt32 memberCount = memberUUIDs->getCount();
1002	    for (UInt32 i = 0; i < memberCount; i++) {
1003		if (uuid->isEqualTo(memberUUIDs->getObject(i))) {
1004		    OSString * statusString = OSString::withCString(kAppleRAIDStatusFailed);
1005		    if (statusString) props->setObject(kAppleRAIDMemberStatusKey, statusString);
1006		    break;
1007		}
1008	    }
1009	}
1010
1011	// after a rebuild fails and the drive is removed it comes back as a hot spare (technically correct)
1012	// DM doesn't like that and refuses future rebuilds on it.  so just make it look broken.
1013	bool autoRebuild = OSDynamicCast(OSBoolean, getHeaderProperty(kAppleRAIDSetAutoRebuildKey)) == kOSBooleanTrue;
1014	if (!autoRebuild) {
1015	    OSString * statusString = OSString::withCString(kAppleRAIDStatusFailed);
1016	    if (statusString) props->setObject(kAppleRAIDMemberStatusKey, statusString);
1017	}
1018    }
1019
1020    return props;
1021}
1022
1023
1024//***************************************************************************************************************
1025//***************************************************************************************************************
1026//***************************************************************************************************************
1027
1028
1029bool AppleRAIDMember::changeMemberState(UInt32 newState, bool force)
1030{
1031    bool	swapState = false;
1032    const char	*newStatus = "bogus";
1033
1034#ifdef DEBUG
1035    const char	*oldStatus = "not set";
1036    OSString    *oldStatusString = OSDynamicCast(OSString, getProperty(kAppleRAIDMemberStatusKey));
1037    if (oldStatusString) oldStatus = oldStatusString->getCStringNoCopy();
1038#endif
1039
1040    if (arMemberState == newState) return true;
1041
1042    switch (newState) {
1043
1044    case kAppleRAIDMemberStateBroken : // 0
1045	swapState = true;
1046	newStatus = kAppleRAIDStatusFailed;
1047	break;
1048
1049    case kAppleRAIDMemberStateSpare : // 1
1050	swapState = arMemberState <= kAppleRAIDMemberStateClosed;
1051	newStatus = kAppleRAIDStatusSpare;
1052	break;
1053
1054    case kAppleRAIDMemberStateClosed : // 2
1055	swapState = arMemberState >= kAppleRAIDMemberStateClosing;
1056	newStatus = kAppleRAIDStatusOnline;
1057	break;
1058
1059    case kAppleRAIDMemberStateClosing : // 3
1060	swapState = arMemberState >= kAppleRAIDMemberStateClosing;
1061	newStatus = kAppleRAIDStatusOnline;
1062	break;
1063
1064    case kAppleRAIDMemberStateRebuilding : // 4
1065	swapState = arMemberState == kAppleRAIDMemberStateSpare;
1066	newStatus = kAppleRAIDStatusRebuilding;
1067	break;
1068
1069    case kAppleRAIDMemberStateOpen : // 5
1070	newStatus = kAppleRAIDStatusOnline;
1071	if (arMemberState == kAppleRAIDMemberStateRebuilding) break;  // leave as is
1072	swapState = arMemberState >= kAppleRAIDMemberStateClosed;
1073	break;
1074    }
1075
1076    if (swapState) {
1077
1078	IOLog2("AppleRAIDMember::changeMemberState(%p) from %u (%s) to %u (%s) isSet = %s.\n",
1079	       this, (uint32_t)arMemberState, oldStatus, (uint32_t)newState, newStatus, isRAIDSet() ? "yes":"no");
1080	arMemberState = newState;
1081
1082    } else {
1083
1084#ifdef DEBUG
1085	if (arMemberState != newState) {
1086	    IOLog1("AppleRAIDMember::changeMemberState(%p) %s from %u (%s) to %u (%s) isSet = %s.\n",
1087			   this, force ? "FORCED" : "FAILED", (uint32_t)arMemberState, oldStatus, (uint32_t)newState, newStatus, isRAIDSet() ? "yes":"no");
1088	}
1089#endif
1090	if (force) arMemberState = newState;
1091    }
1092
1093    if ((swapState || force) && isRAIDMember()) setProperty(kAppleRAIDMemberStatusKey, newStatus);
1094
1095    return swapState;
1096}
1097
1098
1099