diff --git a/CHANGELOG.md b/CHANGELOG.md
index 302a04f..3a6d7da 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## 1.7.0 — 2026-08-07
+
+- Added a Big Sur-compatible direct MSI-to-MSI-X reallocation path while keeping a single Kext loadable on macOS 11–15.
+- Moved the MSI-X request to early PCI matching on every supported release so Recovery and second-stage Installer polled commands are covered.
+- Retained the macOS 14–15 interrupt-source route as a fallback.
+- Hardware-validated macOS 11–14 Recovery and a complete macOS 15.6.1 installation.
+- Verified PCIe x4 / 8.0 GT/s, TRIM support, S.M.A.R.T. status, and 2766.1/3005.9 MB/s measured sequential write/read performance on the tested PC711.
+
## 1.2.0 — 2026-08-07
- Added early, PC711-only MSI-X allocation for Darwin 20–22 without taking ownership away from Apple `IONVMeFamily`.
diff --git a/Driver/Info.plist b/Driver/Info.plist
index 6ad1790..fcc119b 100644
--- a/Driver/Info.plist
+++ b/Driver/Info.plist
@@ -15,9 +15,9 @@
CFBundlePackageType
KEXT
CFBundleShortVersionString
- 1.2.0
+ 1.7.0
CFBundleVersion
- 1.2.0
+ 1.7.0
IOKitPersonalities
PC711EarlyMSIX
diff --git a/Driver/PC711Probe.cpp b/Driver/PC711Probe.cpp
index 295c890..f7dcb3d 100644
--- a/Driver/PC711Probe.cpp
+++ b/Driver/PC711Probe.cpp
@@ -2,8 +2,11 @@
#include
#include
+#include
#include
#include
+#include
+#include
#include
#include
@@ -20,10 +23,62 @@ constexpr uint32_t kInterruptTypeMSIX {0x00020000U};
constexpr size_t kConfigureInterruptsVtableSlot {0x960 / sizeof(uintptr_t)};
constexpr size_t kControllerFlagsOffset {0x191};
constexpr uint8_t kLegacyMSIXPathFlag {0x10};
+constexpr uint8_t kBigSurMSIXMode {0x01};
+
+constexpr const char *kAllocateDeviceInterruptsSymbol {
+ "__ZN32IOPCIMessagedInterruptController24allocateDeviceInterruptsEP9IOServicejjPyPj"
+};
+constexpr const char *kDeallocateDeviceInterruptsSymbol {
+ "__ZN32IOPCIMessagedInterruptController26deallocateDeviceInterruptsEP9IOService"
+};
using ConfigureInterrupts = IOReturn (*)(IOPCIDevice *device,
uint32_t interruptType, uint32_t numRequired, uint32_t numRequested,
IOOptionBits options);
+using AllocateDeviceInterrupts = IOReturn (*)(void *controller,
+ IOService *device, uint32_t numVectors, uint32_t msiCapability,
+ uint64_t *msiAddress, uint32_t *msiData);
+using DeallocateDeviceInterrupts = IOReturn (*)(void *controller,
+ IOService *device);
+
+AllocateDeviceInterrupts bigSurAllocateDeviceInterrupts {nullptr};
+DeallocateDeviceInterrupts bigSurDeallocateDeviceInterrupts {nullptr};
+
+// Big Sur exports the five-argument allocator. Monterey and newer changed
+// its C++ signature, so weak linkage keeps one binary loadable across 11-15.
+extern "C" IOReturn directBigSurAllocateDeviceInterrupts(void *controller,
+ IOService *device, uint32_t numVectors, uint32_t msiCapability,
+ uint64_t *msiAddress, uint32_t *msiData)
+ __asm("__ZN32IOPCIMessagedInterruptController24allocateDeviceInterruptsEP9IOServicejjPyPj")
+ __attribute__((weak_import));
+extern "C" IOReturn directBigSurDeallocateDeviceInterrupts(void *controller,
+ IOService *device)
+ __asm("__ZN32IOPCIMessagedInterruptController26deallocateDeviceInterruptsEP9IOService")
+ __attribute__((weak_import));
+
+struct BigSurPCIMSIState {
+ uint8_t reserved0[28];
+ uint16_t msiCapability;
+ uint16_t msiControl;
+ uint16_t msiPhysVectorCount;
+ uint16_t msiVectorCount;
+ uint8_t msiMode;
+ uint8_t msiEnable;
+ uint8_t reserved1[2];
+ uint64_t msiTable;
+ uint64_t msiPBA;
+ void *msiVectors;
+};
+
+static_assert(offsetof(BigSurPCIMSIState, msiCapability) == 28,
+ "unexpected Big Sur PCI MSI state layout");
+static_assert(offsetof(BigSurPCIMSIState, msiTable) == 40,
+ "unexpected Big Sur PCI MSI table layout");
+
+class PC711PCIDeviceAccess : public IOPCIDevice {
+public:
+ void *compatReserved() { return reserved; }
+};
bool isPC711Device(IOPCIDevice *pci) {
if (!pci)
@@ -50,11 +105,119 @@ IOReturn configurePC711MSIX(IOPCIDevice *pci) {
kIOReturnUnsupported;
}
+IOReturn reallocateBigSurPC711MSIX(IOPCIDevice *pci) {
+ if (pci && pci->getProperty("PC711CompatBigSurMSIXReallocated"))
+ return kIOReturnSuccess;
+
+ auto allocate = bigSurAllocateDeviceInterrupts ?
+ bigSurAllocateDeviceInterrupts : directBigSurAllocateDeviceInterrupts;
+ auto deallocate = bigSurDeallocateDeviceInterrupts ?
+ bigSurDeallocateDeviceInterrupts : directBigSurDeallocateDeviceInterrupts;
+ if (!pci || !allocate || !deallocate)
+ return kIOReturnNotReady;
+
+ IOByteCount msixCapability {0};
+ pci->extendedFindPCICapability(kIOPCIMSIXCapability, &msixCapability);
+ if (!msixCapability)
+ return kIOReturnUnsupported;
+
+ auto controllers = OSDynamicCast(OSArray,
+ pci->getProperty(gIOInterruptControllersKey));
+ auto specifiers = OSDynamicCast(OSArray,
+ pci->getProperty(gIOInterruptSpecifiersKey));
+ if (!controllers || !specifiers ||
+ controllers->getCount() != specifiers->getCount())
+ return kIOReturnNotReady;
+
+ void *messagedController {nullptr};
+ const OSSymbol *messagedName {nullptr};
+ for (unsigned int index = 0; index < controllers->getCount(); index++) {
+ auto name = OSDynamicCast(OSSymbol, controllers->getObject(index));
+ if (!name)
+ continue;
+ auto controller = IOService::getPlatform()->lookUpInterruptController(name);
+ if (controller && controller->metaCast("IOPCIMessagedInterruptController")) {
+ messagedController = controller;
+ messagedName = name;
+ break;
+ }
+ }
+ if (!messagedController || !messagedName)
+ return kIOReturnNotReady;
+
+ auto state = reinterpret_cast(
+ reinterpret_cast(pci)->compatReserved());
+ if (!state || !state->msiCapability)
+ return kIOReturnNotReady;
+
+ const uint16_t oldCapability = state->msiCapability;
+ const uint8_t oldMode = state->msiMode;
+ const auto deallocateResult = deallocate(messagedController, pci);
+ if (deallocateResult != kIOReturnSuccess)
+ return deallocateResult;
+
+ auto retainedControllers = OSArray::withCapacity(controllers->getCount());
+ auto retainedSpecifiers = OSArray::withCapacity(specifiers->getCount());
+ if (!retainedControllers || !retainedSpecifiers) {
+ OSSafeReleaseNULL(retainedControllers);
+ OSSafeReleaseNULL(retainedSpecifiers);
+ return kIOReturnNoMemory;
+ }
+
+ for (unsigned int index = 0; index < controllers->getCount(); index++) {
+ auto controllerName = controllers->getObject(index);
+ auto specifier = specifiers->getObject(index);
+ if (!controllerName || !specifier || controllerName->isEqualTo(messagedName))
+ continue;
+ retainedControllers->setObject(controllerName);
+ retainedSpecifiers->setObject(specifier);
+ }
+ pci->setProperty(gIOInterruptControllersKey, retainedControllers);
+ pci->setProperty(gIOInterruptSpecifiersKey, retainedSpecifiers);
+ retainedControllers->release();
+ retainedSpecifiers->release();
+
+ state->msiCapability = static_cast(msixCapability);
+ state->msiControl = 0;
+ state->msiPhysVectorCount = 0;
+ state->msiVectorCount = 0;
+ state->msiMode = kBigSurMSIXMode;
+ state->msiEnable = 0;
+ state->msiTable = 0;
+ state->msiPBA = 0;
+ state->msiVectors = nullptr;
+
+ const auto allocateResult = allocate(
+ messagedController, pci, 0, static_cast(msixCapability),
+ nullptr, nullptr);
+ pci->setProperty("PC711CompatBigSurMSIXReallocationResult",
+ static_cast(static_cast(allocateResult)), 32);
+ if (allocateResult == kIOReturnSuccess) {
+ pci->setProperty("PC711CompatBigSurMSIXReallocated", true);
+ return allocateResult;
+ }
+
+ // Restore the original MSI allocation if MSI-X allocation failed.
+ state->msiCapability = oldCapability;
+ state->msiControl = 0;
+ state->msiPhysVectorCount = 0;
+ state->msiVectorCount = 0;
+ state->msiMode = oldMode;
+ state->msiEnable = 0;
+ state->msiTable = 0;
+ state->msiPBA = 0;
+ state->msiVectors = nullptr;
+ allocate(messagedController, pci, 0, oldCapability,
+ nullptr, nullptr);
+ return allocateResult;
+}
+
} // namespace
-// Darwin 20-22 may have resolved a different PCI interrupt allocation before
-// IONVMeFamily creates its event source. Request MSI-X while the PC711 PCI nub
-// is still being probed, then decline attachment so Apple's driver takes over.
+// Recovery and installer environments may issue polled NVMe commands before
+// IONVMeFamily creates its ordinary event source. Request MSI-X while the
+// PC711 PCI nub is still being probed, then decline attachment so Apple's
+// driver takes over on every supported macOS release.
class PC711EarlyMSIX : public IOService {
OSDeclareDefaultStructors(PC711EarlyMSIX)
@@ -65,14 +228,12 @@ class PC711EarlyMSIX : public IOService {
OSDefineMetaClassAndStructors(PC711EarlyMSIX, IOService)
IOService *PC711EarlyMSIX::probe(IOService *provider, SInt32 *) {
- if (getKernelVersion() > KernelVersion::Ventura)
- return nullptr;
-
auto pci = OSDynamicCast(IOPCIDevice, provider);
if (!isPC711Device(pci))
return nullptr;
- const auto result = configurePC711MSIX(pci);
+ const auto result = getKernelVersion() == KernelVersion::BigSur ?
+ reallocateBigSurPC711MSIX(pci) : configurePC711MSIX(pci);
pci->setProperty("PC711CompatEarlyMSIXRequested", true);
pci->setProperty("PC711CompatEarlyConfigureInterruptsResult",
static_cast(static_cast(result)), 32);
@@ -110,6 +271,8 @@ class PC711ProbePlugin {
IOService *provider);
static void processKext(void *context, KernelPatcher &patcher, size_t index,
mach_vm_address_t address, size_t size);
+ static void processPCIKext(void *context, KernelPatcher &patcher, size_t index,
+ mach_vm_address_t address, size_t size);
static IOFilterInterruptEventSource *wrapCreateDeviceInterrupt(void *controller,
IOInterruptEventAction action, IOFilterInterruptAction filter,
IOService *provider);
@@ -129,6 +292,19 @@ class PC711ProbePlugin {
{},
KernelPatcher::KextInfo::Unloaded
};
+
+ const char *pciKextPath {
+ "/System/Library/Extensions/IOPCIFamily.kext/Contents/MacOS/IOPCIFamily"
+ };
+
+ KernelPatcher::KextInfo pciKextInfo {
+ "com.apple.iokit.IOPCIFamily",
+ &pciKextPath,
+ 1,
+ {true},
+ {},
+ KernelPatcher::KextInfo::Unloaded
+ };
};
PC711ProbePlugin plugin;
@@ -162,7 +338,11 @@ IOFilterInterruptEventSource *PC711ProbePlugin::wrapCreateDeviceInterrupt(
filter, provider) : nullptr;
}
- const auto result = configurePC711MSIX(pci);
+ // On Big Sur the IOPCIFamily callback may not have resolved the private MSI
+ // allocator when the high-score PCI personality probes. Retry here, after
+ // IOPCIFamily is ready but before IONVMeFamily creates its event source.
+ const auto result = getKernelVersion() == KernelVersion::BigSur ?
+ reallocateBigSurPC711MSIX(pci) : configurePC711MSIX(pci);
pci->setProperty("PC711CompatMSIXRequested", true);
pci->setProperty("PC711CompatConfigureInterruptsResult",
static_cast(static_cast(result)), 32);
@@ -192,6 +372,25 @@ IOFilterInterruptEventSource *PC711ProbePlugin::wrapCreateDeviceInterrupt(
return eventSource;
}
+void PC711ProbePlugin::processPCIKext(void *context, KernelPatcher &patcher,
+ size_t index, mach_vm_address_t, size_t) {
+ auto instance = static_cast(context);
+ if (!instance || index != instance->pciKextInfo.loadIndex ||
+ getKernelVersion() != KernelVersion::BigSur)
+ return;
+
+ bigSurAllocateDeviceInterrupts = reinterpret_cast(
+ patcher.solveSymbol(index, kAllocateDeviceInterruptsSymbol));
+ bigSurDeallocateDeviceInterrupts = reinterpret_cast(
+ patcher.solveSymbol(index, kDeallocateDeviceInterruptsSymbol));
+ if (!bigSurAllocateDeviceInterrupts || !bigSurDeallocateDeviceInterrupts) {
+ SYSLOG("probe", "failed to resolve Big Sur MSI reallocation functions");
+ return;
+ }
+
+ SYSLOG("probe", "Big Sur PC711 MSI-X reallocation functions ready");
+}
+
void PC711ProbePlugin::processKext(void *context, KernelPatcher &patcher,
size_t index, mach_vm_address_t address, size_t size) {
auto instance = static_cast(context);
@@ -228,6 +427,10 @@ void PC711ProbePlugin::init() {
const auto error = lilu.onKextLoad(&kextInfo, 1, processKext, this);
if (error != LiluAPI::Error::NoError)
SYSLOG("probe", "failed to register IONVMeFamily load callback: %d", error);
+
+ const auto pciError = lilu.onKextLoad(&pciKextInfo, 1, processPCIKext, this);
+ if (pciError != LiluAPI::Error::NoError)
+ SYSLOG("probe", "failed to register IOPCIFamily load callback: %d", pciError);
}
const char *bootargOff[] {"-pc711poff"};
diff --git a/README.md b/README.md
index 8231bd2..829e51a 100644
--- a/README.md
+++ b/README.md
@@ -15,43 +15,43 @@ An automatic Lilu compatibility plugin that fixes an `IONVMeFamily` Identify-tim
## Verified result
-PC711Probe has passed hardware boot tests on the same PC711 with macOS 13.4.1 and macOS 15.6.1 Recovery. Disk Utility opened, the model and all five existing partitions were enumerated, and the former NVMe command-timeout panic after roughly 75 seconds did not recur. macOS 11.6 still panics and is not currently supported.
+PC711Probe 1.7.0 has booted the same physical PC711 across macOS 11–15. macOS 11–14 were verified in Recovery, while macOS 15.6.1 completed a full installation and booted from the PC711. The former Identify/command timeout panic after roughly 75 seconds did not recur.
-
+
| Item | Verified value |
|---|---|
| Controller | SK hynix `1C5C:174A`, NVMe class `01:08:02` |
| Model | `SKHynix_HFS512GDE9X084N` (PC711) |
| Firmware | `41010C22` |
-| v1.2.0 verified | macOS 13.4.1, build 22F82, Darwin 22.5.0 |
-| Previously verified | macOS 15.6.1, build 24G90, Darwin 24.6.0 |
-| Booted normally here | macOS 12.5.1 (21G83) and macOS 14.6.1 (23G93) Recovery |
-| Currently unsupported | macOS 11.6 (20G165), original NVMe timeout panic remains |
+| Recovery boot verified | macOS 11.6 (20G165), 12.5.1 (21G83), 13.4.1 (22F82), 14.6.1 (23G93) |
+| Full installation verified | macOS 15.6.1, build 24G90, Darwin 24.6.0 |
+| macOS 15 link/status | PCIe 3.0 x4, 8.0 GT/s, TRIM: Yes, S.M.A.R.T.: Verified |
+| macOS 15 measured result | 2766.1 MB/s write, 3005.9 MB/s read (Blackmagic Disk Speed Test) |
| Native OS | macOS 26.5.1, build 25F80, Darwin 25.5.0 |
| Boot environment | OpenCore 1.0.8, Lilu 1.7.3 |
## Automatic matching
-Version 1.2.0 requires no activation argument. Once enabled in OpenCore, it automatically patches only controllers matching:
+Version 1.7.0 requires no activation argument. Once enabled in OpenCore, it automatically patches only controllers matching:
- PCI Vendor/Device: `1C5C:174A`; and
- NVMe class: `01:08:02`.
The PC711 model string is not available until the first Identify succeeds, so the plugin uses its known PCI controller identity before that command. Different capacities and OEM model strings do not affect matching. NVMe controllers with other PCI IDs retain Apple's original behavior.
-The declared automatic range is Darwin 20–24 (macOS 11–15). The plugin does not load on Darwin 25/macOS 26. macOS 11 is within the load range but still panics on the tested machine.
+The declared automatic range is Darwin 20–24 (macOS 11–15). The plugin does not load on Darwin 25/macOS 26, where the tested PC711 works natively.
## How it works
-On macOS 15.6.1, the PC711 controller reaches Ready state (`CSTS=1`), but the first Identify Controller command never returns through the older interrupt completion path and eventually panics.
+Without the patch, the PC711 controller reaches Ready state (`CSTS=1`), but Identify or another early NVMe command may never complete through the older interrupt path and eventually panics after roughly 75 seconds.
Comparison of older Apple `IONVMeFamily` builds with macOS 26 showed that the newer OS requests one MSI-X vector before creating the interrupt source and removes an older MSI-X-specific path. For the matched PC711, PC711Probe:
-1. requests one MSI-X vector during early PCI matching on macOS 11–13, then declines attachment;
-2. leaves Apple `IONVMeFamily` as the actual NVMe driver;
-3. requests MSI-X and clears the old interrupt-path selector during interrupt-source creation on macOS 14–15; and
-4. leaves Identify, queues, namespaces, and storage I/O to Apple's driver.
+1. requests MSI-X during early PCI matching on macOS 11–15, before Recovery or Installer can issue sensitive polled commands;
+2. uses Big Sur's original PCI message-interrupt allocator on macOS 11 and `IOPCIDevice::configureInterrupts` on macOS 12–15;
+3. keeps the interrupt-source route as a fallback and clears the old selector on macOS 14–15; and
+4. declines attachment, leaving Identify, queues, namespaces, and all storage I/O to Apple `IONVMeFamily`.
[Read the concise development process](docs/DEVELOPMENT.en.md)
@@ -84,7 +84,7 @@ Output: `build/Debug/PC711Probe.kext`
## Current validation boundary
-Controller initialization, Identify, namespace discovery, and partition publication are verified in macOS 13/15 Recovery; macOS 12/14 Recovery booted normally here. macOS 11 remains unresolved. Full installation, sustained I/O, TRIM, sleep/wake, other firmware, and other platforms have not completed hardware validation. Keep a rollback EFI and data backup for the first test.
+Recovery boot is verified on macOS 11–14. A complete macOS 15.6.1 installation, normal system boot, namespace/partition publication, PCIe 3.0 x4 link, reported TRIM support, S.M.A.R.T. status, and a 2766.1/3005.9 MB/s write/read benchmark are verified on the tested PC711. Sleep/wake on macOS 11–15, long-duration stress, other firmware revisions, and other platforms remain outside the current validation boundary. Keep a rollback EFI and data backup for the first test.
## License
diff --git a/README_CN.md b/README_CN.md
index e015438..2b09060 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -15,43 +15,43 @@
## 已验证结果
-PC711Probe 已在同一块 PC711 上通过 macOS 13.4.1 与 macOS 15.6.1 Recovery 硬件启动验证:系统进入磁盘工具,型号及五个既有分区全部被枚举,原先约 75 秒后的 NVMe 命令超时 KP 不再出现。macOS 11.6 目前仍会 KP,尚未支持。
+PC711Probe 1.7.0 已让同一块实机 PC711 在 macOS 11–15 全部成功启动:macOS 11–14 完成 Recovery 验证,macOS 15.6.1 完成完整安装并从 PC711 进入系统。原先约 75 秒后的 Identify/命令超时 KP 不再出现。
-
+
| 项目 | 已验证值 |
|---|---|
| 控制器 | SK hynix `1C5C:174A`,NVMe class `01:08:02` |
| 型号 | `SKHynix_HFS512GDE9X084N`(PC711) |
| 固件 | `41010C22` |
-| v1.2.0 验证成功 | macOS 13.4.1,Build 22F82,Darwin 22.5.0 |
-| 既有验证成功 | macOS 15.6.1,Build 24G90,Darwin 24.6.0 |
-| 本机启动正常 | macOS 12.5.1(21G83)、macOS 14.6.1(23G93)Recovery |
-| 当前未支持 | macOS 11.6(20G165),仍发生原始 NVMe 超时 KP |
+| Recovery 启动验证 | macOS 11.6(20G165)、12.5.1(21G83)、13.4.1(22F82)、14.6.1(23G93) |
+| 完整安装验证 | macOS 15.6.1,Build 24G90,Darwin 24.6.0 |
+| macOS 15 链路/状态 | PCIe 3.0 x4、8.0 GT/s、TRIM:是、S.M.A.R.T.:已验证 |
+| macOS 15 实测成绩 | 写入 2766.1 MB/s、读取 3005.9 MB/s(Blackmagic Disk Speed Test) |
| 原生系统 | macOS 26.5.1,Build 25F80,Darwin 25.5.0 |
| 引导环境 | OpenCore 1.0.8,Lilu 1.7.3 |
## 自动匹配范围
-1.2.0 不需要任何启用参数。Kext 加入 OpenCore 后自动运行,只对以下控制器应用补丁:
+1.7.0 不需要任何启用参数。Kext 加入 OpenCore 后自动运行,只对以下控制器应用补丁:
- PCI Vendor/Device:`1C5C:174A`;
- NVMe class:`01:08:02`。
PC711 的型号字符串必须等第一次 Identify 成功后才能读取,因此插件使用其已知 PCI 控制器 ID 进行预先匹配;不同容量和 OEM 型号不依赖字符串判断。其他 PCI ID 的 NVMe 保持 Apple 原始行为。
-插件声明的自动运行范围为 Darwin 20–24(macOS 11–15)。Darwin 25/macOS 26 不加载插件。macOS 11 虽在加载范围内,但目前实测仍会 KP。
+插件声明的自动运行范围为 Darwin 20–24(macOS 11–15)。Darwin 25/macOS 26 不加载插件,实测 PC711 在该系统已原生免驱。
## 原理
-macOS 15.6.1 中,PC711 控制器已经 Ready(`CSTS=1`),但第一条 Identify Controller 命令无法通过旧中断完成路径返回,最终超时 KP。
+没有补丁时,PC711 控制器已经 Ready(`CSTS=1`),但 Identify 或其他早期 NVMe 命令可能无法通过旧中断路径完成,约 75 秒后触发超时 KP。
对比旧版与 macOS 26 的 Apple `IONVMeFamily` 后发现,新系统会在创建中断源前请求一个 MSI-X 向量,并移除了旧 MSI-X 特殊路径。PC711Probe 对匹配的 PC711:
-1. 在 macOS 11–13 的 PCI 匹配早期请求一个 MSI-X 向量,随后主动放弃设备绑定;
-2. Apple `IONVMeFamily` 继续作为真正的 NVMe 驱动接管设备;
-3. 在 macOS 14–15 创建中断源时请求 MSI-X,并清除旧中断路径选择位;
-4. 其余 Identify、队列、namespace 和存储 I/O 继续由 Apple 驱动完成。
+1. 在 macOS 11–15 的 PCI 匹配早期请求 MSI-X,早于 Recovery 或 Installer 的敏感轮询命令;
+2. macOS 11 使用 Big Sur 原始 PCI 消息中断分配器,macOS 12–15 使用 `IOPCIDevice::configureInterrupts`;
+3. 保留中断源兼容路由作为后备,并在 macOS 14–15 清除旧路径选择位;
+4. 随后主动放弃设备绑定,Identify、队列、namespace 和全部存储 I/O 仍由 Apple `IONVMeFamily` 完成。
[查看简明开发过程](docs/DEVELOPMENT.zh-CN.md)
@@ -84,7 +84,7 @@ cd PC711Probe
## 当前验证边界
-已验证 macOS 13/15 Recovery 中的控制器初始化、Identify、namespace 和分区发布;macOS 12/14 Recovery 在本机启动正常。macOS 11 尚未修复,完整安装、持续读写、TRIM、睡眠唤醒,以及其他固件和平台也未完成硬件验证。首次使用请保留回滚 EFI 和数据备份。
+macOS 11–14 Recovery 已通过启动验证;macOS 15.6.1 已完成完整安装、正常进入系统、namespace/分区发布、PCIe 3.0 x4 链路、TRIM 支持与 S.M.A.R.T. 状态确认,并实测写入 2766.1 MB/s、读取 3005.9 MB/s。macOS 11–15 的睡眠唤醒、长时间压力测试、其他固件版本和其他平台仍不在当前验证范围内。首次使用请保留回滚 EFI 和数据备份。
## 许可
diff --git a/RELEASE_NOTES_1.7.0.md b/RELEASE_NOTES_1.7.0.md
new file mode 100644
index 0000000..73a81f8
--- /dev/null
+++ b/RELEASE_NOTES_1.7.0.md
@@ -0,0 +1,20 @@
+# PC711Probe 1.7.0
+
+Complete macOS 11–15 PC711 compatibility update. / 完整覆盖 macOS 11–15 的 PC711 兼容性更新。
+
+## Changes / 变化
+
+- Keeps one automatic `PC711Probe.kext`, restricted to SK hynix `1C5C:174A` with NVMe class `01:08:02`. / 保持单个自动运行的 `PC711Probe.kext`,仅匹配 SK hynix `1C5C:174A` 与 NVMe class `01:08:02`。
+- Switches Big Sur's existing PC711 MSI allocation to MSI-X before Apple `IONVMeFamily` attaches. / 在 Apple `IONVMeFamily` 接管前,把 Big Sur 为 PC711 分配的 MSI 切换为 MSI-X。
+- Requests MSI-X during early PCI matching on macOS 12–15, covering Recovery and second-stage Installer polled commands. / 在 macOS 12–15 的 PCI 匹配早期请求 MSI-X,覆盖 Recovery 与安装第二阶段的轮询命令。
+- Retains the later macOS 14–15 interrupt-source route as a fallback. / 保留 macOS 14–15 的中断源兼容路由作为后备。
+- Does not load on macOS 26, where the tested PC711 works natively. / macOS 26 不加载本插件,实测 PC711 已原生免驱。
+
+## Hardware results / 实机结果
+
+- macOS 11.6, 12.5.1, 13.4.1, and 14.6.1 Recovery: booted successfully. / Recovery 启动成功。
+- macOS 15.6.1: completed installation, passed the second-stage `macOS Installer` boot, and entered the installed system from the PC711. / 完成安装、通过第二阶段 `macOS Installer` 启动,并从 PC711 进入系统。
+- Installed-system status: PCIe x4 at 8.0 GT/s, TRIM Yes, S.M.A.R.T. Verified. / 安装后状态:PCIe x4、8.0 GT/s、TRIM 是、S.M.A.R.T. 已验证。
+- Blackmagic Disk Speed Test: 2766.1 MB/s write and 3005.9 MB/s read. / 实测写入 2766.1 MB/s、读取 3005.9 MB/s。
+
+Back up EFI and data and keep a rollback-capable EFI for the first boot on other firmware or hardware. / 其他固件或硬件首次启动前,请备份 EFI 与数据并保留可回滚 EFI。
diff --git a/SHA256SUMS.txt b/SHA256SUMS.txt
index 0e6e2f5..1143f5a 100644
--- a/SHA256SUMS.txt
+++ b/SHA256SUMS.txt
@@ -1,2 +1,3 @@
d824eff15fb20f436fb522a5a9902ab06e249e1119b7afbe582647211e662da9 PC711Probe-1.0.0.zip
9321fa65e11ef49f2f396b76ba4f63a0c4eade983c53dfcd075363f2cf096304 PC711Probe-1.2.0.zip
+9e3c78cd55a301d85fb0237f7abb51be572b1e406a004459de4e096da5aa9cae PC711Probe-1.7.0.zip
diff --git a/Scripts/build.sh b/Scripts/build.sh
index da855bd..ead7fd4 100755
--- a/Scripts/build.sh
+++ b/Scripts/build.sh
@@ -29,7 +29,7 @@ plutil -lint "$project_dir/Driver/Info.plist"
mkdir -p "$binary_dir"
common_cxx_flags="-arch x86_64 -std=c++14 -fapple-kext -fno-builtin -fno-exceptions -fno-rtti -fno-asynchronous-unwind-tables"
-common_defines="-DKERNEL -DKERNEL_PRIVATE -D__KERNEL__ -DPRODUCT_NAME=PC711Probe -DMODULE_VERSION=1.2.0 -DMACH_ASSERT=1"
+common_defines="-DKERNEL -DKERNEL_PRIVATE -D__KERNEL__ -DPRODUCT_NAME=PC711Probe -DMODULE_VERSION=1.7.0 -DMACH_ASSERT=1"
xcrun clang++ $common_cxx_flags \
-c \
diff --git a/Scripts/verify.sh b/Scripts/verify.sh
index 0e84cae..668b791 100755
--- a/Scripts/verify.sh
+++ b/Scripts/verify.sh
@@ -10,7 +10,7 @@ license="$project_dir/LICENSE"
"$project_dir/Scripts/build.sh"
plutil -lint "$plist"
-test "$(/usr/libexec/PlistBuddy -c 'Print :CFBundleVersion' "$plist")" = "1.2.0"
+test "$(/usr/libexec/PlistBuddy -c 'Print :CFBundleVersion' "$plist")" = "1.7.0"
test "$(/usr/libexec/PlistBuddy -c 'Print :OSBundleLibraries:com.apple.iokit.IOPCIFamily' "$plist")" = "2.9"
file "$binary" | grep -q "Mach-O 64-bit kext bundle x86_64"
nm -g "$binary" | grep -q ' _kmod_info$'
@@ -35,6 +35,11 @@ grep -q 'configure(pci, kInterruptTypeMSIX, 1, 1, 0)' "$source"
grep -q 'class PC711EarlyMSIX' "$source"
grep -q 'PC711CompatEarlyMSIXRequested' "$source"
grep -q '0x174A1C5C' "$plist"
+grep -q 'reallocateBigSurPC711MSIX' "$source"
+grep -q 'PC711CompatBigSurMSIXReallocated' "$source"
+grep -q 'PC711CompatBigSurMSIXReallocationResult' "$source"
+grep -q 'kAllocateDeviceInterruptsSymbol' "$source"
+grep -q 'kDeallocateDeviceInterruptsSymbol' "$source"
grep -q 'kControllerFlagsOffset {0x191}' "$source"
grep -q 'PC711CompatConfigureInterruptsResult' "$source"
grep -q 'PC711CompatLegacyMSIXFlagAfter' "$source"
@@ -47,6 +52,7 @@ grep -q '^// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0$' "$source"
grep -q '^# PolyForm Noncommercial License 1.0.0$' "$license"
grep -q '^Required Notice: Copyright 2026 hrx114514x\.$' "$license"
grep -q 'PolyForm Noncommercial 1.0.0' "$plist"
+nm -m "$binary" | grep -q 'weak external __ZN32IOPCIMessagedInterruptController24allocateDeviceInterruptsEP9IOServicejjPyPj'
if grep -q -- '-pc711pcompat\|-pc711pstage' "$source"; then
echo "Manual activation or diagnostic-stage boot argument found" >&2
diff --git a/Support/KmodInfo.c b/Support/KmodInfo.c
index b76e116..7328691 100644
--- a/Support/KmodInfo.c
+++ b/Support/KmodInfo.c
@@ -16,7 +16,7 @@ extern kern_return_t _stop(kmod_info_t *info, void *data);
* setup (including OSKextGetCurrentIdentifier) and can leave the IOKit
* personality unable to instantiate.
*/
-KMOD_EXPLICIT_DECL(com.stationk9.driver.PC711Probe, "1.2.0", _start, _stop)
+KMOD_EXPLICIT_DECL(com.stationk9.driver.PC711Probe, "1.7.0", _start, _stop)
kmod_start_func_t *_realmain = PC711Probe_kern_start;
kmod_stop_func_t *_antimain = PC711Probe_kern_stop;
diff --git a/docs/DEVELOPMENT.en.md b/docs/DEVELOPMENT.en.md
index 0065883..16aa77e 100644
--- a/docs/DEVELOPMENT.en.md
+++ b/docs/DEVELOPMENT.en.md
@@ -27,16 +27,17 @@ IOPCIDevice::configureInterrupts(0x20000, 1, 1, 0);
`0x20000` requests MSI-X. Darwin 25 also removed the older MSI-X-specific path selected by bit `0x10` at controller offset `0x191`, consistently using the standard event-source path instead.
-Further comparison of Darwin 20–22 showed that `CreateDeviceInterrupt` is not exported in those kernel collections, and requesting MSI-X only when that function runs is already too late on Ventura. Older `IOPCIFamily` may have resolved a different interrupt allocation and refuse a second configuration request.
+Further comparison showed that requesting MSI-X only from `CreateDeviceInterrupt` is too late for some Recovery and Installer paths. Big Sur's `IOPCIFamily` also predates `IOPCIDevice::configureInterrupts` and initially chooses MSI when the PC711 exposes both MSI and MSI-X.
## 3. Implement the minimal compatibility patch
PC711Probe keeps two version-bounded compatibility entry points:
1. PCI identity `1C5C:174A` with NVMe class `01:08:02` is matched automatically;
-2. on Darwin 20–22, a high-score PCI probe requests one MSI-X vector early and returns null without claiming the device;
-3. on Darwin 23–24, `CreateDeviceInterrupt` is routed to request MSI-X and clear the old path-selector bit `0x10`; and
-4. Apple `IONVMeFamily` remains responsible for the actual device attachment and storage I/O.
+2. on Darwin 20, a high-score PCI probe switches the existing PC711 MSI allocation to MSI-X through Big Sur's exported message-interrupt allocator;
+3. on Darwin 21–24, the same early probe requests one MSI-X vector through `IOPCIDevice::configureInterrupts`;
+4. on Darwin 23–24, `CreateDeviceInterrupt` is also routed as a fallback to request MSI-X and clear the old path-selector bit `0x10`; and
+5. Apple `IONVMeFamily` remains responsible for the actual device attachment and storage I/O.
Identify, queues, namespaces, and storage I/O remain handled by Apple `IONVMeFamily`. Other PCI IDs retain Apple's original behavior. The tested PC711 works natively on macOS 26, so the plugin loads only through Darwin 24.
@@ -46,15 +47,16 @@ The project was built with pinned Lilu and MacKernelSDK revisions, followed by:
- static, architecture, and `Info.plist` checks;
- boot testing from an independent USB EFI;
-- controller, model, namespace, and five existing-partition enumeration on macOS 13.4.1 and 15.6.1;
-- normal Recovery boots on macOS 12.5.1 and 14.6.1;
-- an explicit unsupported result for macOS 11.6, where the original timeout panic remains; and
-- a return to macOS 26 to confirm PCIe x4 / 8.0 GT/s, verified SMART status, and no regression on the other NVMe drive.
+- normal Recovery boots on macOS 11.6, 12.5.1, 13.4.1, and 14.6.1;
+- a complete macOS 15.6.1 installation, including its second-stage `macOS Installer` boot;
+- controller, model, namespace, and partition publication in the installed macOS 15 system;
+- PCIe x4 / 8.0 GT/s, TRIM support reported as Yes, verified S.M.A.R.T. status, and a 2766.1/3005.9 MB/s write/read benchmark; and
+- a return to macOS 26 to confirm native PC711 operation and no regression on the other NVMe drive.
No existing PC711 partition was erased or modified during validation, and the repository redistributes no Apple binaries.
## 5. Current conclusion
-The combined patch removes the timeout and publishes the controller, namespace, and partitions in the verified macOS 13.4.1 and 15.6.1 hardware tests.
+The combined patch removes the timeout across the tested macOS 11–15 Recovery and Installer paths. macOS 15.6.1 completed installation and booted from the PC711 with normal storage publication and near-interface-limit sequential performance.
-macOS 11 remains unresolved. Other builds, firmware revisions, platforms, full installation, sustained I/O, TRIM, and sleep/wake also remain untested. PC711Probe is therefore a narrowly scoped, hardware-verified compatibility patch rather than a generic PC711 driver.
+Other builds, firmware revisions, platforms, long-duration stress, and macOS 11–15 sleep/wake remain untested. PC711Probe is therefore a narrowly scoped, hardware-verified compatibility patch rather than a generic PC711 driver.
diff --git a/docs/DEVELOPMENT.zh-CN.md b/docs/DEVELOPMENT.zh-CN.md
index 7bfc852..a941cfa 100644
--- a/docs/DEVELOPMENT.zh-CN.md
+++ b/docs/DEVELOPMENT.zh-CN.md
@@ -27,16 +27,17 @@ IOPCIDevice::configureInterrupts(0x20000, 1, 1, 0);
其中 `0x20000` 请求 MSI-X。Darwin 25 同时删除了 Darwin 24 中由控制器偏移 `0x191` 的 bit `0x10` 选择的旧 MSI-X 特殊路径,统一使用标准事件源路径。
-随后对 Darwin 20–22 继续对比发现:`CreateDeviceInterrupt` 符号在旧 Kernel Collection 中未导出,而且到该函数执行时再申请 MSI-X 对 Ventura 已经太晚。旧版 `IOPCIFamily` 可能已解析其他中断分配,并拒绝第二次配置。
+继续对比发现:部分 Recovery 与 Installer 路径在执行到 `CreateDeviceInterrupt` 之前就会发出敏感命令,此时再请求 MSI-X 已经太晚。Big Sur 的 `IOPCIFamily` 还早于 `IOPCIDevice::configureInterrupts`,当 PC711 同时提供 MSI 与 MSI-X 时会先选择 MSI。
## 3. 实现最小兼容补丁
PC711Probe 保留两个受版本限制的兼容入口:
1. 自动匹配 PCI 身份 `1C5C:174A` 和 NVMe class `01:08:02`;
-2. Darwin 20–22 通过高优先级 PCI probe 提前申请一个 MSI-X 向量,然后返回空值,不占用设备;
-3. Darwin 23–24 路由 `CreateDeviceInterrupt`,申请 MSI-X 并清除旧路径选择位 `0x10`;
-4. Apple 原始 `IONVMeFamily` 始终负责真正的设备绑定与存储 I/O。
+2. Darwin 20 通过高优先级 PCI probe,调用 Big Sur 导出的消息中断分配器,把 PC711 已有的 MSI 分配切换为 MSI-X;
+3. Darwin 21–24 由同一个早期 probe 通过 `IOPCIDevice::configureInterrupts` 申请一个 MSI-X 向量;
+4. Darwin 23–24 继续路由 `CreateDeviceInterrupt` 作为后备,申请 MSI-X 并清除旧路径选择位 `0x10`;
+5. Apple 原始 `IONVMeFamily` 始终负责真正的设备绑定与存储 I/O。
Identify、队列、namespace 和存储 I/O 仍由 Apple `IONVMeFamily` 完成。其他 PCI ID 保持 Apple 原始行为。macOS 26 已原生支持实测 PC711,因此插件最高只加载到 Darwin 24。
@@ -46,15 +47,16 @@ Identify、队列、namespace 和存储 I/O 仍由 Apple `IONVMeFamily` 完成
- 静态检查、架构检查和 `Info.plist` 校验;
- 独立 USB EFI 启动验证;
-- macOS 13.4.1 与 15.6.1 中控制器、型号、namespace 与五个既有分区枚举;
-- macOS 12.5.1 与 14.6.1 Recovery 启动正常;
-- macOS 11.6 仍复现原始超时 KP,明确标记为未支持;
-- 重启至 macOS 26,确认 PC711 仍为 PCIe x4 / 8.0 GT/s、SMART Verified,且另一块 NVMe 无回归。
+- macOS 11.6、12.5.1、13.4.1 与 14.6.1 Recovery 启动正常;
+- macOS 15.6.1 完成完整安装,包括第二阶段 `macOS Installer` 启动;
+- 安装后的 macOS 15 正常发布控制器、型号、namespace 和分区;
+- 确认 PCIe x4 / 8.0 GT/s、TRIM 支持为“是”、S.M.A.R.T. 已验证,并实测写入 2766.1 MB/s、读取 3005.9 MB/s;
+- 重启至 macOS 26,确认 PC711 原生工作且另一块 NVMe 无回归。
验证中没有抹除或修改 PC711 的现有分区,仓库也不分发任何 Apple 二进制。
## 5. 当前结论
-已证明该组合补丁可在 macOS 13.4.1 与 15.6.1 的上述实机环境中消除超时,并发布控制器、namespace 和分区。
+已证明该组合补丁可覆盖实测的 macOS 11–15 Recovery 与 Installer 路径。macOS 15.6.1 已完整安装并从 PC711 进入系统,存储发布正常,顺序性能接近接口上限。
-macOS 11 尚未修复;其他 build、固件或平台,以及完整安装、持续读写、TRIM 和睡眠唤醒也未覆盖。因此它是一个经过硬件验证的窄范围兼容补丁,不是通用 PC711 驱动。
+其他 build、固件或平台、长时间压力测试,以及 macOS 11–15 的睡眠唤醒尚未覆盖。因此它是一个经过硬件验证的窄范围兼容补丁,不是通用 PC711 驱动。
diff --git a/docs/INSTALL.en.md b/docs/INSTALL.en.md
index 4a2264f..f025bda 100644
--- a/docs/INSTALL.en.md
+++ b/docs/INSTALL.en.md
@@ -22,18 +22,18 @@ English | [简体中文](INSTALL.zh-CN.md)
| MinKernel | `20.0.0` |
| MaxKernel | `24.99.99` |
-3. Do not add an activation argument; version 1.2.0 automatically matches the `1C5C:174A` PC711.
+3. Do not add an activation argument; version 1.7.0 automatically matches the `1C5C:174A` PC711.
4. Disable AML/SSDT code that hides the PC711 through `_STA=0` or spoofed class/vendor/device values.
5. Temporarily disable NVMeFix for the first test so results are not mixed.
6. Validate the configuration with the `ocvalidate` matching the OpenCore version.
-> macOS 11 is within the kext load range, but the tested machine still hits the original NVMe timeout panic and must not be considered supported yet.
+> The tested PC711 has booted macOS 11–14 Recovery and completed a macOS 15.6.1 installation. Other firmware revisions and platforms still require a rollback-capable first test.
## First boot
1. Boot the older macOS release or Recovery through the test USB.
2. Confirm that the desktop or Disk Utility opens.
-3. Check only that the PC711 model and existing partitions appear; do not erase or write to the drive.
+3. For a read-only first test, check that the PC711 model and existing partitions appear before installing or writing data.
4. Confirm that other NVMe devices remain functional.
## Rollback
diff --git a/docs/INSTALL.zh-CN.md b/docs/INSTALL.zh-CN.md
index 9b69511..0e05fbe 100644
--- a/docs/INSTALL.zh-CN.md
+++ b/docs/INSTALL.zh-CN.md
@@ -22,18 +22,18 @@
| MinKernel | `20.0.0` |
| MaxKernel | `24.99.99` |
-3. 不要添加启用参数;1.2.0 会自动匹配 `1C5C:174A` PC711。
+3. 不要添加启用参数;1.7.0 会自动匹配 `1C5C:174A` PC711。
4. 停用隐藏 PC711 的 AML/SSDT,包括 `_STA=0` 或伪造 class/vendor/device 的规则。
5. 首次验证时暂时停用 NVMeFix,避免混淆结果。
6. 使用与 OpenCore 版本匹配的 `ocvalidate` 检查配置。
-> macOS 11 虽在 Kext 加载范围内,但目标实机仍会发生原始 NVMe 超时 KP,目前不应视为已支持。
+> 实测 PC711 已成功启动 macOS 11–14 Recovery,并完成 macOS 15.6.1 安装;其他固件版本和平台首次使用时仍需保留可回滚方案。
## 首次启动
1. 从测试 USB 启动旧版 macOS 或 Recovery。
2. 确认系统能够进入桌面或磁盘工具。
-3. 只检查 PC711 型号和既有分区是否出现,不要抹盘或写入。
+3. 首次只读验证时,先确认 PC711 型号和既有分区正常出现,再进行安装或写入。
4. 确认同机其他 NVMe 仍正常。
## 回滚
diff --git a/docs/images/macos15-installed-performance.png b/docs/images/macos15-installed-performance.png
new file mode 100644
index 0000000..ec51949
Binary files /dev/null and b/docs/images/macos15-installed-performance.png differ
diff --git a/docs/images/recovery-success.jpg b/docs/images/recovery-success.jpg
deleted file mode 100644
index 281a409..0000000
Binary files a/docs/images/recovery-success.jpg and /dev/null differ