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 \