diff --git a/ASFW.xcodeproj/project.pbxproj b/ASFW.xcodeproj/project.pbxproj index 60840bcc..168b82fc 100644 --- a/ASFW.xcodeproj/project.pbxproj +++ b/ASFW.xcodeproj/project.pbxproj @@ -596,9 +596,14 @@ isa = XCBuildConfiguration; buildSettings = { AD_HOC_CODE_SIGNING_ALLOWED = YES; + ASFW_DEXT_ENTITLEMENTS_NO = ASFWDriver/ASFWDriver.entitlements; + ASFW_DEXT_ENTITLEMENTS_YES = "ASFWDriver/ASFWDriver+SCSI.entitlements"; + ASFW_ENABLE_SCSI = NO; + ASFW_INFOPLIST_DEFS_NO = ""; + ASFW_INFOPLIST_DEFS_YES = "ASFW_ENABLE_SCSI=1"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++23"; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = ASFWDriver/ASFWDriver.entitlements; + CODE_SIGN_ENTITLEMENTS = "$(ASFW_DEXT_ENTITLEMENTS_$(ASFW_ENABLE_SCSI))"; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -612,6 +617,9 @@ ); GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = ASFWDriver/Info.plist; + INFOPLIST_OTHER_PREPROCESSOR_FLAGS = "-traditional"; + INFOPLIST_PREPROCESS = YES; + INFOPLIST_PREPROCESSOR_DEFINITIONS = "$(ASFW_INFOPLIST_DEFS_$(ASFW_ENABLE_SCSI))"; LIBRARY_SEARCH_PATHS = "$(inherited)"; MARKETING_VERSION = 1.0; OTHER_LDFLAGS = "-framework SCSIControllerDriverKit"; @@ -631,9 +639,14 @@ isa = XCBuildConfiguration; buildSettings = { AD_HOC_CODE_SIGNING_ALLOWED = YES; + ASFW_DEXT_ENTITLEMENTS_NO = ASFWDriver/ASFWDriver.entitlements; + ASFW_DEXT_ENTITLEMENTS_YES = "ASFWDriver/ASFWDriver+SCSI.entitlements"; + ASFW_ENABLE_SCSI = NO; + ASFW_INFOPLIST_DEFS_NO = ""; + ASFW_INFOPLIST_DEFS_YES = "ASFW_ENABLE_SCSI=1"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++23"; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = ASFWDriver/ASFWDriver.entitlements; + CODE_SIGN_ENTITLEMENTS = "$(ASFW_DEXT_ENTITLEMENTS_$(ASFW_ENABLE_SCSI))"; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -647,6 +660,9 @@ ); GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = ASFWDriver/Info.plist; + INFOPLIST_OTHER_PREPROCESSOR_FLAGS = "-traditional"; + INFOPLIST_PREPROCESS = YES; + INFOPLIST_PREPROCESSOR_DEFINITIONS = "$(ASFW_INFOPLIST_DEFS_$(ASFW_ENABLE_SCSI))"; LIBRARY_SEARCH_PATHS = "$(inherited)"; MARKETING_VERSION = 1.0; OTHER_LDFLAGS = "-framework SCSIControllerDriverKit"; diff --git a/ASFWDriver/ASFWDriver+SCSI.entitlements b/ASFWDriver/ASFWDriver+SCSI.entitlements new file mode 100644 index 00000000..b9057caa --- /dev/null +++ b/ASFWDriver/ASFWDriver+SCSI.entitlements @@ -0,0 +1,16 @@ + + + + + com.apple.developer.driverkit + + com.apple.developer.driverkit.transport.pci + + com.apple.developer.driverkit.transport.pci.bridge + + com.apple.developer.driverkit.family.audio + + com.apple.developer.driverkit.family.scsicontroller + + + diff --git a/ASFWDriver/ASFWDriver.entitlements b/ASFWDriver/ASFWDriver.entitlements index e76369c5..f90ff122 100644 --- a/ASFWDriver/ASFWDriver.entitlements +++ b/ASFWDriver/ASFWDriver.entitlements @@ -10,8 +10,6 @@ com.apple.developer.driverkit.family.audio - com.apple.developer.driverkit.family.scsicontroller - \ No newline at end of file diff --git a/ASFWDriver/Info.plist b/ASFWDriver/Info.plist index 95c77060..d8d388bf 100644 --- a/ASFWDriver/Info.plist +++ b/ASFWDriver/Info.plist @@ -101,6 +101,7 @@ IOUserServerOneProcess +#if ASFW_ENABLE_SCSI ASFWSCSIControllerService CFBundleIdentifier @@ -124,6 +125,7 @@ IOUserServerOneProcess +#endif OSBundleUsageDescription AS FireWire Driver diff --git a/README.md b/README.md index 31f3b643..6fa26166 100644 --- a/README.md +++ b/README.md @@ -355,6 +355,48 @@ NOTE: You need an Apple Developer account (paid) and appropriate entitlements Enabling `systemextensionsctl developer on` is recommended — it allows installing system extensions from the build +### SCSI HBA (SBP-2 scanners/disks) — opt-in + +The SCSI HBA (`ASFWSCSIControllerService`, for SBP-2 devices such as FireWire film +scanners and disks) is **excluded from the default build**, because shipping it by +default can **panic the machine into a boot loop** (~60 s registry busy-timeout, +`IOService.cpp:5986`, no device attached) through two independent paths: + +- Its Info.plist personality instantiates a kernel-side + `IOUserSCSIParallelInterfaceController` as soon as the FireWire card matches at + boot. The HBA currently reports SCSI target 0 as present unconditionally, and the + probe INQUIRY is then held with no deadline waiting for an SBP-2 login that never + arrives when no SBP-2 device is on the bus (audio interfaces are not SBP-2). The + stalled target registration keeps the PCI nub busy past watchdogd's 60 s boot + quiesce. This happens **regardless of SIP/AMFI state**. +- It requires the restricted + `com.apple.developer.driverkit.family.scsicontroller` entitlement. On a machine + where AMFI enforces entitlements, the ad-hoc-signed dext carrying it is killed at + launch (taking the audio driver down with it, since everything runs in one + process), and the orphaned kernel stub strands the same busy chain. + +The default build carries neither the personality nor the entitlement and cannot +trigger either path. + +To include the HBA, opt in explicitly: + +```bash +./build.sh --scsi # or: xcodebuild … ASFW_ENABLE_SCSI=YES +./sign.sh # picks the +SCSI entitlements automatically +``` + +> **Warning:** `--scsi` builds are currently **not cold-boot-safe**: cold-booting +> with the FireWire controller attached and no powered-on SBP-2 device on the bus +> can hit the 60 s boot panic even with SIP fully disabled. Power the SBP-2 device +> on before booting. A stalled probe on a running system does not panic +> immediately, but can panic later when the adapter is unplugged or the extension +> is torn down. The HBA-side fix (create the target at SBP-2 login instead of +> boot) will follow in a separate PR. + +If a machine ever ends up in a panic loop: boot into Recovery, `csrutil disable`, +boot normally, uninstall the extension +(`systemextensionsctl uninstall - net.mrmidi.ASFW.ASFWDriver`), then re-enable SIP. + ## Installing a prebuilt build (testers) If you want to test ASFireWire without building it yourself, tagged releases attach a @@ -366,6 +408,12 @@ for experimental testing only — not general use. > Only do this on a machine you are comfortable using for testing, and re-enable SIP > (`csrutil enable`) when you are done. The build is unsigned/un-notarized and provided > as-is; run it only if you understand and accept that. +> +> **Uninstall the extension _before_ re-enabling SIP.** With SIP back on, AMFI refuses +> to launch the ad-hoc-signed dext; an installed build that includes the SCSI HBA then +> leaves an orphaned kernel-side SCSI stub behind at every boot, which can panic the +> machine into a boot loop (recovery: Recovery → `csrutil disable` → boot → uninstall +> → `csrutil enable`). **Requirements:** an Apple Silicon Mac running macOS 26 (Tahoe), and FireWire hardware (a PCIe FireWire/OHCI card, or an Apple Thunderbolt-to-FireWire adapter). diff --git a/build.sh b/build.sh index 72d16847..5a0cd711 100755 --- a/build.sh +++ b/build.sh @@ -49,6 +49,16 @@ SWIFT_TEST_ONLY=false # When true, generate Swift code coverage SWIFT_COVERAGE=false SWIFT_COVERAGE_LCOV="${BUILD_DIR}/swift_coverage.lcov" +# When true, include the SCSI HBA: the ASFWSCSIControllerService personality in +# Info.plist and the restricted scsicontroller entitlement in the dext signature. +# Default OFF: the personality matches the FireWire card at boot, and with no +# powered SBP-2 device on the bus the HBA's probe INQUIRY stalls target-0 +# registration past watchdogd's 60 s boot quiesce (registry busy-timeout panic, +# IOService.cpp:5986) — regardless of SIP state. On AMFI-enforcing machines the +# ad-hoc signature carrying the restricted entitlement additionally gets the dext +# killed at launch, stranding the orphaned kernel stub in the same panic. +# See README "SCSI HBA — opt-in". +ENABLE_SCSI=false usage() { cat <&1 | tee "${RAW_LOG}" diff --git a/sign.sh b/sign.sh index ec98a24a..47555264 100755 --- a/sign.sh +++ b/sign.sh @@ -46,6 +46,22 @@ ok() { echo "[OK] $*"; } DEXT_PATH="$(find "${APP_PATH}/Contents/Library/SystemExtensions" -maxdepth 1 -name '*.dext' -print -quit 2>/dev/null || true)" [[ -n "${DEXT_PATH}" ]] || { err "No .dext found under ${APP_PATH}/Contents/Library/SystemExtensions"; exit 1; } +# The entitlement set must match what the build put in Info.plist: a dext whose +# signature carries the restricted scsicontroller entitlement is killed by AMFI +# on enforcing machines, and the orphaned kernel-side SCSI stub then panics the +# kernel at boot. Detect the SCSI personality (present only when the app was +# built with ASFW_ENABLE_SCSI=YES / ./build.sh --scsi) and pick the matching file +# — never sign the SCSI entitlement onto a build without the personality, or +# vice versa. +if /usr/libexec/PlistBuddy -c "Print :IOKitPersonalities:ASFWSCSIControllerService" \ + "${DEXT_PATH}/Info.plist" >/dev/null 2>&1; then + DEXT_ENTITLEMENTS="ASFWDriver/ASFWDriver+SCSI.entitlements" + log "SCSI HBA personality detected — signing with ${DEXT_ENTITLEMENTS}" + [[ -f "${DEXT_ENTITLEMENTS}" ]] || { err "Missing ${DEXT_ENTITLEMENTS}"; exit 1; } +else + log "No SCSI HBA personality — signing with ${DEXT_ENTITLEMENTS} (audio only)" +fi + # Sign inner-first: the dext, then the enclosing app. log "Signing dext: ${DEXT_PATH}" codesign --force --sign - --timestamp=none \