\ \ Copyright (c) 2001 Apple Computer, Inc. All rights reserved. \ \ @APPLE_LICENSE_HEADER_START@ \ \ The contents of this file constitute Original Code as defined in and \ are subject to the Apple Public Source License Version 1.1 (the \ "License"). You may not use this file except in compliance with the \ License. Please obtain a copy of the License at \ http://www.apple.com/publicsource and read it before using this file. \ \ This Original Code and all software distributed under the License are \ distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER \ EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, \ INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, \ FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the \ License for the specific language governing rights and limitations \ under the License. \ \ @APPLE_LICENSE_HEADER_END@ \ \ \ AppleRAID.of \ \ Version 1.1.0 \ \ DRI: Josh de Cesare \ hex \ Frequently used constants h# 200 constant h#200 h# 2C constant ',' h# 3A constant ':' instance 0 value rsName instance 0 value rsUUID instance 0 value rsLevel instance 0 value rsSequenceNumber instance 0 value rsInfo instance 0 value rsSliceCount instance 0 value rsSliceCountFound instance 0 value rsMainSlice instance 0 value rsBlockSize instance 0 value rsBlockCount instance 0 value rsSliceBlockOffset instance 0 value rsBlocksPerSlice instance 0 value rsOFPaths instance 0 value rsDeblockerIH instance 0 value rsDeblockerFlush instance 0 value rsPMHacked 0 value pmPyPartStart \ starting block# 0 value pmPartBlkCnt \ # of blocks in partition 0 value pmBlockSize \ size of "blocks" in partition 0 value pmOffsetHi \ offset of partition 0 value pmOffsetLo 0 value pmPartNumber 0 value raidSetSaveInfo 0 value pmBfr 0 value rsBfr h# 4552 constant kERSIG h# 504D constant kPMSIG struct \ Block0 format /W field >sbSig \ s.b., 'ER' for Mac /W field >sbBlkSize \ typically, h#200 or h#800 (for CD, DVD) /L field >sbBlkCount drop struct \ Mac partition map entry /W field >pmSig \ s.b., 'PM' /W field >pmSigPad /L field >pmMapBlkCnt \ number of blocks in map /L field >pmPyPartStart \ first physical block of partition /L field >pmPartBlkCnt \ number of blocks in partition 20 field >pmPartName \ its name 20 field >pmPartType \ its type /L field >pmLgDataStart \ first logical block of data area /L field >pmDataCnt \ number of blocks in data area /L field >pmPartStatus \ status of partition /L field >pmLgBootStart \ logical block of boot code /L field >pmBootSize \ size of boot code /L field >pmBootLoad \ boot's preferred load address /L field >pmBootLoad2 \ ??? /L field >pmBootEntry \ entry pt of boot code /L field >pmBootEntry2 \ ??? /L field >pmBootCksum \ checksum of boot code 10 field >pmProcessor \ processor type drop h# 00010000 constant kAppleRAIDHeaderV1_0_0 h# 00000000 constant kAppleRAIDStripe h# 00000001 constant kAppleRAIDMirror h# 00000100 constant kAppleRAIDConcat h# 00001000 constant kAppleRAIDHeaderSize struct \ AppleRAID Header 10 field >raidSignature \ signature "AppleRAIDHeader\0" /L field >raidHeaderSize \ size of the RAID header /L field >raidHeaderVersion \ version of the RAID Header 0x00010000 /L field >raidHeaderSequence \ 0 slice is bad, >0 slice could be good /L field >raidLevel \ One of kAppleRAIDStripe, kAppleRAIDMirror, kAppleRAIDConcat 10 field >raidUUID \ 128 bit unique id 20 field >raidSetName \ unique name of the raid set /L field >raidSliceCount \ number of slices in the set /L field >raidSliceNumber \ slice number of this slice in the set /L field >raidChunkSize \ size of each logical RAID chunk, usual 32KB /L field >raidChunkCount \ number of chunks 1A0 field >reserved1 \ reserved init to zero, preserve on update 0 field >raidOFPaths \ OF Paths for each device drop struct \ AppleRAIDSetInfo /L field >rssName /L field >rssUUID /L field >rssLevel /L field >rssSequenceNumber /L field >rssInfo /L field >rssSliceCount /L field >rssSliceCountFound /L field >rssMainSlice /L field >rssBlockSize /L field >rssBlockCount /L field >rssSliceBlockOffset /L field >rssBlocksPerSlice constant /AppleRAIDSetInfo /AppleRAIDSetInfo alloc-mem to raidSetSaveInfo struct \ AppleRAIDSetSliceInfo /L field >rsIHandle /L field >rsPartNumber /L field >rsPyPartStart /L field >rsPartBlkCnt /L field >rsOffsetHi /L field >rsOffsetLo constant /AppleRAIDSetSliceInfo : str-to-decimal ( str len -- number ) base @ >r decimal \ save current base $number \ convert to a number if 0 then \ supply zero if conversion fails r> base ! \ restore base ; : cstr-len ( cstr maxlen -- len ) { _cstr _maxlen } _maxlen 0 do _cstr i + c@ 0= if i unloop exit then loop _maxlen ; : seek-ih ( offsetlow offsethigh ihandle -- flag ) >r " seek" r> $call-method ; : read-ih ( buffer length ihandle -- actual ) >r " read" r> $call-method ; : read-pm ( ihandle -- true|false ) { _ih } 0 0 _ih seek-ih drop \ read block0 pmBfr h#200 _ih read-ih drop pmBfr 2c@-be kERSIG <> if false exit then \ verify the signature 'ER' pmBfr >sbBlkSize 2c@-be to pmBlockSize \ get the block size true ; : read-pm-entry ( ihandle part# -- true|false ) { _ih _part# } _part# pmBlockSize um* _ih seek-ih drop \ read the pm entry pmBfr h#200 _ih read-ih drop pmBfr >pmSig 2c@-be kPMSIG <> if false exit then \ verify the signature pmBfr >pmPyPartStart 4c@-be to pmPyPartStart \ save the start block pmBfr >pmPartBlkCnt 4c@-be to pmPartBlkCnt \ save the block count pmPyPartStart pmBlockSize um* to pmOffsetHi to pmOffsetLo \ calculate the offset true ; : is-raid-pm-entry ( -- true|false ) pmBfr >pmPartType " Apple_Boot_RAID"(00)" comp 0= ; : read-raid-header ( ihandle -- true|false ) { _ih } pmOffsetLo pmOffsetHi _ih seek-ih drop \ read the RAID header rsBfr kAppleRAIDHeaderSize _ih read-ih drop rsBfr >raidSignature " AppleRAIDHeader"(00)" comp 0= \ verify the signature rsBfr >raidHeaderVersion 4c@-be kAppleRAIDHeaderV1_0_0 = \ verify the version and ; : raidset-save-info ( -- ) rsName raidSetSaveInfo >rssName 4c!-be rsLevel raidSetSaveInfo >rssLevel 4c!-be rsSequenceNumber raidSetSaveInfo >rssSequenceNumber 4c!-be rsInfo raidSetSaveInfo >rssInfo 4c!-be rsSliceCount raidSetSaveInfo >rssSliceCount 4c!-be rsSliceCountFound raidSetSaveInfo >rssSliceCountFound 4c!-be rsMainSlice raidSetSaveInfo >rssMainSlice 4c!-be rsBlockSize raidSetSaveInfo >rssBlockSize 4c!-be rsBlockCount raidSetSaveInfo >rssBlockCount 4c!-be rsSliceBlockOffset raidSetSaveInfo >rssSliceBlockOffset 4c!-be rsBlocksPerSlice raidSetSaveInfo >rssBlocksPerSlice 4c!-be rsUUID raidSetSaveInfo >rssUUID 4c!-be ; : raidset-restore-info ( -- ) raidSetSaveInfo >rssName 4c@-be to rsName raidSetSaveInfo >rssLevel 4c@-be to rsLevel raidSetSaveInfo >rssSequenceNumber 4c@-be to rsSequenceNumber raidSetSaveInfo >rssInfo 4c@-be to rsInfo raidSetSaveInfo >rssSliceCount 4c@-be to rsSliceCount raidSetSaveInfo >rssSliceCountFound 4c@-be to rsSliceCountFound raidSetSaveInfo >rssMainSlice 4c@-be to rsMainSlice raidSetSaveInfo >rssBlockSize 4c@-be to rsBlockSize raidSetSaveInfo >rssBlockCount 4c@-be to rsBlockCount raidSetSaveInfo >rssSliceBlockOffset 4c@-be to rsSliceBlockOffset raidSetSaveInfo >rssBlocksPerSlice 4c@-be to rsBlocksPerSlice raidSetSaveInfo >rssUUID 4c@-be to rsUUID ; : raidset-save-slice-info ( ihandle rsInfo -- ) { _rsInfo } _rsInfo >rsIHandle 4c!-be \ save the ihandle pmPyPartStart _rsInfo >rsPyPartStart 4c!-be \ save the PyPartStart pmPartBlkCnt _rsInfo >rsPartBlkCnt 4c!-be \ save the PartBlkCnt pmOffsetHi _rsInfo >rsOffsetHi 4c!-be \ save the partition offset pmOffsetLo _rsInfo >rsOffsetLo 4c!-be ; : add-raid-slice ( ihandle partNumber -- true|false ) { _ih _partNumber ; _sequenceNumber _raidLevel _sliceNumber _curPath _curLength } _ih read-pm 0= if false exit then \ check the partition map header _ih _partNumber read-pm-entry 0= if false exit then \ read partition map entry is-raid-pm-entry 0= if false exit then \ make sure it is an AppleRAID partition _ih read-raid-header 0= if false exit then \ read the raid header rsBfr >raidHeaderSequence 4c@-be -> _sequenceNumber \ get the header sequence number _sequenceNumber 0= if false exit then \ slices with a zero header sequence number are not valid rsBfr >raidLevel 4c@-be -> _raidLevel \ get the raid set level rsBfr >raidSliceNumber 4c@-be -> _sliceNumber \ get the slice number _raidLevel kAppleRAIDMirror = _sliceNumber 0= or \ non mirrors must be slice zero rsInfo 0= and if \ only make rsInfo if it has not already been made _sliceNumber to rsMainSlice \ save the main slice number for mirrors rsBfr >raidHeaderSize 4c@-be h#200 / to rsSliceBlockOffset \ save the slice offset _sequenceNumber to rsSequenceNumber \ save the header sequence number _raidLevel to rsLevel \ save the raid level rsBfr >raidSliceCount 4c@-be to rsSliceCount \ save the slice count rsBfr >raidChunkSize 4c@-be to rsBlockSize \ save the raid set block size rsBlockSize h#200 / to rsBlocksPerSlice rsBfr >raidChunkCount 4c@-be rsBlocksPerSlice * to rsBlockCount \ save the number of blocks 20 alloc-mem to rsName \ alloc memory for rsName rsBfr >raidSetName rsName 20 move \ save the raid set name 10 alloc-mem to rsUUID \ alloc memory for rsUUID rsBfr >raidUUID rsUUID 10 move \ save the raid set UUID rsSliceCount /AppleRAIDSetSliceInfo * alloc-mem \ alloc memory for rsInfo to rsInfo rsInfo rsSliceCount /AppleRAIDSetSliceInfo * 0 filll \ zero the memory for rsInfo rsSliceCount /L * alloc-mem to rsOFPaths \ alloc memory for the OF Paths array rsBfr >raidOFPaths -> _curPath rsSliceCount 0 do _curPath h#200 cstr-len 1+ -> _curLength \ get the length with the '\0' _curLength alloc-mem dup \ allocate memory for the string rsOFPaths i /L * + 4c!-be \ save the path's location _curPath swap _curLength move \ copy the path _curPath _curLength + -> _curPath \ update to the next loop else rsInfo 0= if true exit then \ return true for open on not zero slice rsLevel kAppleRAIDMirror = if \ handle main slice number and sequence numbers for mirrors _sliceNumber rsMainSlice < if \ set rsMainSlice if a lower numbered slice comes along _sliceNumber to rsMainSlice then _sequenceNumber rsSequenceNumber < if \ return false for slices older than the set true else _sequenceNumber rsSequenceNumber > if \ update the set's sequence number if the slice is newer _sequenceNumber to rsSequenceNumber then false \ return true if same age or newer then else false \ return true for non mirrors then rsBfr >raidSetName rsName dup 20 cstr-len comp \ make sure this slice is part of the set rsBfr >raidUUID rsUUID 10 comp \ check the name and uuid or or if false exit then then _ih \ save the info for this slice _sliceNumber /AppleRAIDSetSliceInfo * rsInfo + raidset-save-slice-info rsSliceCountFound 1+ to rsSliceCountFound \ found another slice true ; : open ( -- true|false ) { ; _partNumber _sliceNumber _sequenceNumber } pmBfr 0= if \ allocate memory for the PM entry h#200 alloc-mem to pmBfr then rsBfr 0= if \ allocate memory for the RAID set header kAppleRAIDHeaderSize alloc-mem to rsBfr then my-args ?dup 0= if drop false exit then \ my-args must not be a null string ',' left-parse-string \ get the partition number 2swap 2drop \ drop the file path str-to-decimal -> _partNumber \ save the partition number _partNumber 2 < if false exit then \ partition number 0 & 1 can not be valid _partNumber pmPartNumber <> if \ create the set if its a new partition 0 to pmPartNumber my-parent _partNumber add-raid-slice \ try to add the first slice 0= if false exit then \ bad slice rsMainSlice -> _sliceNumber \ save the mail slice number rsSequenceNumber -> _sequenceNumber \ save the sequence number for the set rsInfo if \ find the rest of the slices if the first was good rsSliceCount 0 ?do i /AppleRAIDSetSliceInfo * rsInfo + \ only try to add empty sliceslots >rsIHandle 4c@-be 0= if rsOFPaths i /L * + 4c@-be \ ( OFPath-cstr ) dup h#200 cstr-len \ ( str len ) ?dup if ':' right-parse-string 2swap str-to-decimal -> _partNumber 2+ 2dup + 1- ascii 0 swap c! open-dev ?dup if _partNumber add-raid-slice drop then else drop then then loop rsLevel kAppleRAIDMirror = if rsMainSlice _sliceNumber <> \ check the main slice number rsSequenceNumber _sequenceNumber <> \ check sequenceNumber for Mirrors or if 0 to rsInfo then else rsSliceCount rsSliceCountFound <> \ Make sure all the slices were found if 0 to rsInfo then then then raidset-save-info _partNumber to pmPartNumber else raidset-restore-info then 0 0 " deblocker" $open-package to rsDeblockerIH rsDeblockerIH 0= if false exit then true ; : close ( -- ) rsDeblockerIH if rsDeblockerIH close-package 0 to rsDeblockerIH then ; : block-size ( -- block-len ) " block-size" $call-parent ; : max-transfer ( -- max-len ) " max-transfer" $call-parent ; : dma-alloc ( ... size -- virt ) " dma-alloc" $call-parent ; : dma-free ( ... virt size -- ) " dma-free" $call-parent ; : #blocks ( -- #blocks ) rsBlockCount ; : read ( addr len -- actual ) rsDeblockerIH if \ return error if there is no deblocker " read" rsDeblockerIH $call-method else 2drop 0 then ; : write ( addr len -- actual ) rsDeblockerIH if \ return error if there is no deblocker " write" rsDeblockerIH $call-method else 2drop 0 then ; : seek ( pos.lo pos.hi -- flag ) rsDeblockerIH if \ return error if there is no deblocker rsDeblockerFlush if \ flush on the second seek " empty-buffers" rsDeblockerIH $call-method false to rsDeblockerFlush then " seek" rsDeblockerIH $call-method else 2drop -1 then ; : slice-read-blocks ( addr block# #blocks ihandle sliceNumber -- #read ) { _ih _sliceNumber } swap \ ( addr #blocks block# ) h#200 um* _sliceNumber 0= if \ do the seek " parent-seek" $call-parent else " seek" _ih $call-method then drop \ ( addr #blocks ) h#200 * _sliceNumber 0= if " parent-read" $call-parent else " read" _ih $call-method then h#200 / \ ( #read ) ; : read-blocks ( addr block# #blocks -- #read ) { _addr _block# _#blocks ; _#read _curBlock# } rsInfo 0= if _addr _#blocks pmBlockSize * 0 filll \ clear the memory _#blocks exit \ return #blocks then rsPMHacked 0= if \ return a partition map on access true to rsPMHacked \ clear the hack true to rsDeblockerFlush \ tell seek to flush _addr _#blocks h#200 * 0 filll \ clear the buffer kPMSIG _addr >pmSig 2c!-be \ set the partition signature 0 _addr >pmPyPartStart 4c!-be \ set the partition start rsBlockCount _addr >pmPartBlkCnt 4c!-be \ set the partition size " Apple_HFS" _addr >pmPartType swap move \ set the partition type _#blocks \ return the request number of blocks exit then rsLevel case kAppleRAIDStripe of 0 -> _#read _#blocks 0 do _block# i + -> _curBlock# _addr h#200 i * + \ ( addr ) _curBlock# rsBlocksPerSlice / rsSliceCount 2dup / rsBlocksPerSlice * -rot mod dup >r \ ( addr sliceBlockOffset sliceNumber ; sliceNumber ) /AppleRAIDSetSliceInfo * rsInfo + \ ( addr sliceBlockOffset rsSliceInfo ; sliceNumber ) dup >rsIHandle 4c@-be >r \ ( addr sliceBlockOffset rsSliceInfo ; sliceNumber sliceIH ) >rsPyPartStart 4c@-be rsSliceBlockOffset + + \ ( addr sliceBlockOffset ; sliceNumber sliceIH ) _curBlock# rsBlocksPerSlice mod + 1 \ ( addr currentBlock# #blocks ; sliceNumber sliceIH ) r> r> slice-read-blocks \ ( #read ) _#read + -> _#read loop endof kAppleRAIDMirror of _addr _block# rsSliceBlockOffset + \ ( addr sliceBlockOffset ) rsMainSlice /AppleRAIDSetSliceInfo * rsInfo + \ ( addr sliceBlockOffset rsSliceInfo ) >rsPyPartStart 4c@-be + \ ( addr sliceBlockOffset ) _#blocks 0 0 \ ( addr sliceBlockOffset #blocks ihandle sliceNumber ) slice-read-blocks \ ( #read ) -> _#read endof kAppleRAIDConcat of 0 -> _#read endof endcase _#read \ return the number of blocks read ; \ There is a CTRL-D on the following line.