Skip to content
5 changes: 5 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ when their work informed a change.
## Build and test

Build with `python3 tools/build.py`; run CPU tests with `python3 tools/test.py`.
The builder checks each generated Compat plist, including both DEBUG and RELEASE
in CI. Existing output can be checked with `python3 tools/check_compat_version.py`
and its build directory. Compat's release version and oldest compatible ABI are independent;
do not raise the compatibility floor during an ordinary version bump. An ABI
break requires coordinated Graphics client updates and final-pkg validation.
Lilu.kext and MacKernelSDK are local ignored dependencies. Optional native replay
requires the exact Apple drivers accepted by the tests. Do not commit generated
payloads, Apple driver files, credentials or local machine state.
2 changes: 1 addition & 1 deletion AppleIntelTGLCompat/AppleIntelTGLCompat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ static uint32_t read32(void *access, unsigned long offset) {
// it does not read plugin boot arguments or depend on framebuffer globals.
// The provider pins the immutable binding until reboot, like the donor owner.
extern "C" uint32_t xelpProbeCapabilities(uint32_t schema,uint32_t configABI) {
return schema==XELP::ProbeCapabilityABI && configABI==XELP::ProbeABI ? XELP::ProbeDisplay13ComboDP|XELP::ProbeDisplay13FixedTC|XELP::ProbeDisplay13FixedDP : 0;
return schema==XELP::ProbeCapabilityABI && configABI==XELP::ProbeABI ? XELP::ProbeDisplay13ComboDP|XELP::ProbeDisplay13FixedTC|XELP::ProbeDisplay13FixedDP|XELP::ProbeDisplay13Panel : 0;
}
extern "C" bool xelpConfigureDisplay(void *controller,const XELP::ProbeConfig *config) {
if(!controller || !config || !config->valid())return false;
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## 0.6.1

Use the latest [WhateverXe Graphics](https://github.com/b00t0x/WhateverXe-Graphics/releases) package.

### Added

- Add experimental internal display (eDP) and brightness support for Alder Lake-N / Twin Lake and Alder Lake-H/P/U / Raptor Lake-H/P/U GPUs. Not yet tested on hardware.

### Fixed

- Fix DEBUG boot freezes and restart loops caused by excessive kernel stack usage.

## 0.6.0

Use the latest [WhateverXe Graphics](https://github.com/b00t0x/WhateverXe-Graphics/releases) package.
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ for OS coverage and remaining problems.
- Tiger Lake-H45 (GT1): `9A60`, `9A68`, `9A70`.
- Other supported Alder Lake-N configurations and Twin Lake.
- Other Alder Lake-H/P/U and Raptor Lake-H/P/U configurations with supported fixed HDMI or fixed TC DisplayPort wiring.
- Experimental internal eDP on Alder Lake-N / Twin Lake and Alder Lake-H/P/U / Raptor Lake-H/P/U: a single firmware-described Combo A/B panel with PPS0 and PCH PWM brightness. This path has local CPU and donor replay coverage only; use the corresponding WhateverXe Graphics release with this plugin. See [display limits](docs/device-selection.md#display-limits).
- Other supported Alder Lake-S configurations, Alder Lake-HX, Raptor Lake-S and Raptor Lake-HX.

These devices use implemented support shared with the tested families. Their
Expand All @@ -62,8 +63,8 @@ limits are listed in [device selection](docs/device-selection.md).
### Further implementation required

- On Alder Lake-N / Twin Lake and Alder Lake-H/P/U / Raptor Lake-H/P/U,
internal eDP panels, Combo-PHY DisplayPort and Type-C-managed output need
further implementation. Accepting the GPU ID does not enable these outputs.
external Combo-PHY DisplayPort and Type-C-managed output need
further implementation. eDP panels using other power/backlight methods also need additional support. Accepting the GPU ID does not enable these outputs.
- Rocket Lake-S: its display power and clock handling differ from Tiger Lake.
It is not supported by this release, and no hardware validation is scheduled.

Expand Down Expand Up @@ -266,3 +267,5 @@ WhateverXe retains WhateverGreen's copyright notices and BSD-3-Clause license.
See [LICENSE.txt](LICENSE.txt). Lilu and MacKernelSDK are separate dependencies.

Thanks to [JulioNectar/XPS13Plus](https://github.com/JulioNectar/Hackintosh-XPS13Plus-IrisXe-Tahoe) for sharing its Xe graphics work. Its compiler adapter and LLVM bridge informed the OS-native AIR conversion used in WhateverXe Graphics.

Internal-panel work also draws on [WhateverXe-RaptorLakeP](https://github.com/samranahmad09/WhateverXe-RaptorLakeP), [X1Carbon-G11](https://github.com/dssoftx/Hackintosh-X1Carbon-G11-IrisXe-Tahoe), XPS13Plus and the [Linux i915 display sources](https://github.com/torvalds/linux/tree/v6.12/drivers/gpu/drm/i915/display) for hardware behavior and implementation comparisons.
9 changes: 7 additions & 2 deletions README_ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,13 @@ OS別の検証範囲と残る問題は[動作確認済みの機能と既知の
CPUモデル名や機種固有の設定は不要ですが、実機での動作確認は残っています。
受付IDの一覧と対応する資源構成は[デバイス選択](docs/device-selection.md)を参照してください。

実験的に、Alder Lake-N / Twin LakeとAlder Lake-H/P/U / Raptor Lake-H/P/Uの内蔵eDPを実装しています。対象はfirmwareで一意に記述されたCombo A/Bの単一パネルとPPS0・PCH PWMによる輝度制御です。CPU試験とdonor再生試験のみで、実機未検証です。対応するWhateverXe Graphicsリリースと組み合わせて使用してください。条件は[表示の制限](docs/device-selection.md#display-limits)を参照してください。

### 対応に追加改修が必要

- Alder Lake-N / Twin LakeとAlder Lake-H/P/U / Raptor Lake-H/P/Uの内蔵eDPパネル・
Combo PHY経由のDisplayPort・Type-C管理下の出力には追加改修が必要です。
- Alder Lake-N / Twin LakeとAlder Lake-H/P/U / Raptor Lake-H/P/Uの
Combo PHY経由の外部DisplayPort・Type-C管理下の出力には追加改修が必要です。
その他の電源・輝度制御方式を使うeDPパネルも追加対応が必要です。
GPUのIDを受け付けるだけで、これらの出力が有効になるわけではありません。
- Rocket Lake-S:Tiger Lakeとは表示の電源・クロック制御が異なります。
このリリースでは未対応で、実機検証の予定もありません。
Expand Down Expand Up @@ -196,3 +199,5 @@ WhateverXeとAppleIntelTGLCompatのkext、およびソースのmanifestは`build
WhateverXeはWhateverGreenの著作権表示とBSD-3-Clauseライセンスを継承しています。[LICENSE.txt](LICENSE.txt)を参照してください。LiluとMacKernelSDKは別の依存コンポーネントです。

Xeグラフィックスの取り組みを公開している[JulioNectar/XPS13Plus](https://github.com/JulioNectar/Hackintosh-XPS13Plus-IrisXe-Tahoe)に感謝します。同リポジトリのcompiler adapterとLLVM bridgeを、WhateverXe GraphicsのOS標準AIR変換の参考にしました。

内蔵パネルの対応では、[WhateverXe-RaptorLakeP](https://github.com/samranahmad09/WhateverXe-RaptorLakeP)、[X1Carbon-G11](https://github.com/dssoftx/Hackintosh-X1Carbon-G11-IrisXe-Tahoe)、XPS13Plus、[Linux i915の表示ソース](https://github.com/torvalds/linux/tree/v6.12/drivers/gpu/drm/i915/display)も、ハードウェアの挙動と実装の比較に参照しています。
47 changes: 29 additions & 18 deletions WhateverXe/kern_alderlake_n.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,14 @@ struct DisplayReadout {

// Read only. Check Display 13 PW_A/B/C before pipe-local registers; do not
// access TGL FIA/TC/Dekel addresses or issue selector/power/MMIO writes.
template<class IO> DisplayReadout readDisplay(IO &io, bool admitted) {
DisplayReadout r;
if (!admitted) return r;
template<class IO> void readDisplay(IO &io, bool admitted, DisplayReadout &r) {
// Reused storage must not retain pipe bits or registers from an earlier
// snapshot. Reset fields in place: aggregate assignment can itself reserve
// a full DisplayReadout temporary on the unoptimized kernel stack.
r.version=2;r.status=ReadoutStatus::NotAdmitted;
r.count=r.poweredPipes=r.activePipes=r.hdmiBPipes=0;
for(auto &reg:r.registers) {reg.offset=0;reg.value=0;}
if (!admitted) return;
const auto add = [&](uint32_t offset) {
if (r.count >= sizeof(r.registers) / sizeof(r.registers[0]) || !io.readable(offset)) {
r.status = ReadoutStatus::Unavailable;
Expand All @@ -146,7 +151,7 @@ template<class IO> DisplayReadout readDisplay(IO &io, bool admitted) {
};
r.status = ReadoutStatus::Complete;
const uint32_t power = add(0x45404);
if (r.status != ReadoutStatus::Complete) return r;
if (r.status != ReadoutStatus::Complete) return;
// Always-on / PW1 display state, including BIOS and DDI I/O ownership.
const uint32_t common[] {0x45400, 0x45408, 0x4540C, 0x45450, 0x45454, 0x4545C,
0x45420, 0x45424, 0x45444, 0x42000, 0x42084,
Expand Down Expand Up @@ -188,6 +193,12 @@ template<class IO> DisplayReadout readDisplay(IO &io, bool admitted) {
const auto after = add(0x45404);
if (r.status == ReadoutStatus::Complete && after != power)
r.status = ReadoutStatus::PowerChanged;
}
// Value-returning callers retain the existing API. Early bootstrap uses the
// in-place overload with one allocated snapshot instead of stacked copies.
template<class IO> DisplayReadout readDisplay(IO &io, bool admitted) {
DisplayReadout r;
readDisplay(io,admitted,r);
return r;
}

Expand All @@ -200,9 +211,9 @@ inline bool value(const DisplayReadout &r, uint32_t offset, uint32_t &out) {

// Source-side certificate for an already-running, uncompressed external SST
// GOP. This does not prove sink DPCD state, PLL rate or native AUX ownership.
inline bool dpSource(const DisplayReadout &boot,unsigned pipe,unsigned ddi,const Display13TC::Boot *tc=nullptr) {
inline bool dpSource(const DisplayReadout &boot,unsigned pipe,unsigned ddi,const Display13TC::Boot *tc=nullptr,bool panel=false) {
const bool typeC=ddi>=3 && ddi<=6;
if(pipe>=3 || (ddi>=2 && !typeC) || boot.status!=ReadoutStatus::Complete ||
if((panel && ddi>=2) || pipe>=3 || (ddi>=2 && !typeC) || boot.status!=ReadoutStatus::Complete ||
!(boot.activePipes&boot.poweredPipes&(1U<<pipe)))return false;
const auto bank=pipe*0x1000;
uint32_t trans=0,tp=0,buf=0,clock=0,ctl2=0,dss1=0,dss2=0,dm=0,dn=0,lm=0,ln=0;
Expand Down Expand Up @@ -234,21 +245,21 @@ inline bool dpSource(const DisplayReadout &boot,unsigned pipe,unsigned ddi,const
(buf&(typeC && tc->ownershipOptional?0x80000080U:0x800000C0U))==
(typeC && !tc->ownershipOptional?0x80000040U:0x80000000U) &&
(clock>>28)==ddi+(typeC?3:1) && (!typeC || clock==tc->transClock) &&
(tp&~(1U<<18))==0x80000300U &&
(tp&~((1U<<18)|(panel?1U<<6:0)))==0x80000300U &&
!(dss1&0xF0300000U) && !(dss2&0x80008000U) &&
!(dm&0x81000000U) && (dm&0xFFFFFF) &&
dn && !(dn&0xFF000000U) && lm && !(lm&0xFF000000U) && ln && !(ln&0xFF000000U) &&
(dm&0xFFFFFF)<=dn;
}
enum class GOPProtocol : uint32_t {TMDS,DP};
enum class GOPProtocol : uint32_t {TMDS,DP,EDP};
// Source and power must remain unchanged while only the plane is reinitialized.
// This is an early, serialized bootstrap transaction, not a modeset or link
// adoption certificate. A changed link stops further writes, including rollback.
template<class IO> bool dpSourceUnchanged(IO &io,const DisplayReadout &r,unsigned pipe,unsigned ddi,
uint32_t addedAUXRequest=0,const Display13TC::Boot *tc=nullptr) {
uint32_t addedAUXRequest=0,const Display13TC::Boot *tc=nullptr,bool panel=false) {
// addedAUXRequest is the existing Combo boot-lease transition only.
// TC lifetime uses Mode's own AUX lease, not the Combo MainLinkAUX slot.
if(!dpSource(r,pipe,ddi,tc) || (addedAUXRequest && (ddi>=2 || addedAUXRequest!=(2U<<(2*ddi)))))return false;
if(!dpSource(r,pipe,ddi,tc,panel) || (addedAUXRequest && (ddi>=2 || addedAUXRequest!=(2U<<(2*ddi)))))return false;
if(ddi>=3 && !tc->unchanged(io))return false;
const uint32_t bank=pipe*0x1000;
// Recheck power before touching pipe-local registers. A captured snapshot
Expand All @@ -271,17 +282,17 @@ template<class IO> bool dpSourceUnchanged(IO &io,const DisplayReadout &r,unsigne
// inventing a native transaction reference or claiming the firmware request.
// Caller holds the display owner lock throughout both proof and acquisition.
template<class IO> Display13Power::Result holdDPBootAUX(IO &io,const DisplayReadout &boot,
Display13Power::NativeBridge &bridge,Display13Power::MainLinkAUX &link,unsigned ddi) {
Display13Power::NativeBridge &bridge,Display13Power::MainLinkAUX &link,unsigned ddi,bool panel=false) {
using namespace Display13Power;Result r;
if(ddi>=2 || boot.activePipes!=1 || !dpSource(boot,0,ddi) || !dpSourceUnchanged(io,boot,0,ddi) ||
if(ddi>=2 || boot.activePipes!=1 || !dpSource(boot,0,ddi,nullptr,panel) || !dpSourceUnchanged(io,boot,0,ddi,0,nullptr,panel) ||
bridge.bootPort!=ddi || bridge.bootAdopted || !bridge.bootPGProtected || !bridge.bootDDIProtected ||
!bridge.outputHeld(io,ddi,0) || link.owner || link.bootPending || link.lease.owner)return r;
uint32_t aux=0;
if(!value(boot,AUX,aux) || !(aux&(1U<<(2*ddi))))return r; // No active-link cold power-up.
r=acquire(io,bridge.state,link.lease,auxDomain(ddi));
if(r.status!=Status::Complete)return r;
link.port=ddi;link.bootPending=true;
if(!dpSourceUnchanged(io,boot,0,ddi,2U<<(2*ddi)) || !held(io,bridge.state,link.lease.domains)) {
if(!dpSourceUnchanged(io,boot,0,ddi,2U<<(2*ddi),nullptr,panel) || !held(io,bridge.state,link.lease.domains)) {
// Do not release a newly acquired request under a still-active GOP.
bridge.state.faulted=true;r.status=Status::RequestLost;
}
Expand All @@ -291,9 +302,9 @@ template<class IO> Display13Power::Result holdDPBootAUX(IO &io,const DisplayRead
// The caller retains that object on success and then adopts the boot path.
// No hardware request or native AUX transaction counter changes at handoff.
template<class IO> Display13Power::Result adoptDPBootAUX(IO &io,const DisplayReadout &boot,
Display13Power::NativeBridge &bridge,Display13Power::MainLinkAUX &link,uintptr_t target,unsigned ddi) {
Display13Power::NativeBridge &bridge,Display13Power::MainLinkAUX &link,uintptr_t target,unsigned ddi,bool panel=false) {
using namespace Display13Power;Result r;
if(!target || ddi>=2 || boot.activePipes!=1 || !dpSource(boot,0,ddi) || !dpSourceUnchanged(io,boot,0,ddi) ||
if(!target || ddi>=2 || boot.activePipes!=1 || !dpSource(boot,0,ddi,nullptr,panel) || !dpSourceUnchanged(io,boot,0,ddi,0,nullptr,panel) ||
bridge.bootPort!=ddi || bridge.bootAdopted || !bridge.bootPGProtected || !bridge.bootDDIProtected ||
!bridge.outputHeld(io,ddi,0) || !link.bootPending || link.owner || link.port!=ddi ||
link.lease.owner!=&bridge.state || link.lease.domains!=dependencies(auxDomain(ddi)) ||
Expand Down Expand Up @@ -360,7 +371,7 @@ inline LinearStatus linearPlan(const DisplayReadout &r, LinearPlan &p,unsigned d
p={};
if (ddi>=7 || r.status!=ReadoutStatus::Complete || !r.activePipes || (r.activePipes&~7U) ||
(r.activePipes & (r.activePipes-1)) || (r.activePipes&~r.poweredPipes) ||
(protocol!=GOPProtocol::TMDS && protocol!=GOPProtocol::DP) ||
(protocol!=GOPProtocol::TMDS && protocol!=GOPProtocol::DP && protocol!=GOPProtocol::EDP) ||
(protocol==GOPProtocol::TMDS && ddi==1 && r.activePipes!=r.hdmiBPipes))
return LinearStatus::NotSingleHDMI;
while (!(r.activePipes & (1U<<p.pipe))) ++p.pipe;
Expand All @@ -380,7 +391,7 @@ inline LinearStatus linearPlan(const DisplayReadout &r, LinearPlan &p,unsigned d
if(!(trans&(1U<<31)) || ((trans>>27)&15)!=ddi+1 ||
(protocol==GOPProtocol::TMDS && ((trans>>24)&7)>1))
return LinearStatus::NotSingleHDMI;
if(protocol==GOPProtocol::DP && !dpSource(r,p.pipe,ddi,tc))return LinearStatus::InvalidDPSource;
if(protocol!=GOPProtocol::TMDS && !dpSource(r,p.pipe,ddi,tc,protocol==GOPProtocol::EDP))return LinearStatus::InvalidDPSource;
p.geometry=XELPGeometry::linear(p.control,p.stride,p.size,p.position,p.offset);
if(!p.geometry.valid || (p.surface&4095) || p.surface!=surfLive ||
((trans>>20)&7)!=0 || (pipeConf&0xC0000000)!=0xC0000000 ||
Expand Down Expand Up @@ -415,7 +426,7 @@ template<class IO> LinearResult reinitializeLinear(IO &io, const DisplayReadout
bool linkChanged=false;
const auto linkStable=[&] {
if(!bootGeometryUnchanged(io,r,p.pipe))linkChanged=true;
if(protocol==GOPProtocol::DP && !dpSourceUnchanged(io,r,p.pipe,ddi,0,tc))linkChanged=true;
if(protocol!=GOPProtocol::TMDS && !dpSourceUnchanged(io,r,p.pipe,ddi,0,tc,protocol==GOPProtocol::EDP))linkChanged=true;
if(ddi>=3 && (!tc || !tc->unchanged(io)))linkChanged=true;
return !linkChanged;
};
Expand Down
20 changes: 14 additions & 6 deletions WhateverXe/kern_display13_boot.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ template<class IO> bool reservedPrefix(IO &io,const AlderLakeN::DisplayReadout &
const auto stable=[&] {
return AlderLakeN::bootGeometryUnchanged(io,display,plan.pipe) &&
(ddi<3 || (tc && tc->unchanged(io))) &&
(protocol!=AlderLakeN::GOPProtocol::DP || AlderLakeN::dpSourceUnchanged(io,display,plan.pipe,ddi,0,tc));
(protocol==AlderLakeN::GOPProtocol::TMDS || AlderLakeN::dpSourceUnchanged(io,display,plan.pipe,ddi,0,tc,protocol==AlderLakeN::GOPProtocol::EDP));
};
if(!stable())return false;
for(unsigned i=0;i<memory.bytes/4096;++i)
Expand Down Expand Up @@ -104,16 +104,24 @@ template<class IO> UnusedTCResult releaseUnusedTC(IO &io,const AlderLakeN::Displ
r.status=Status::Complete;return r;
}

// Caller-owned storage keeps snapshots and per-port witnesses off the nested
// kernel bootstrap stack. The input remains live until the last peer check.
struct NormalizeScratch {
AlderLakeN::DisplayReadout after;
Display13TC::Boot tc[3];
};
template<class IO> Result normalize(IO &io,const AlderLakeN::DisplayReadout &boot,bool admitted,
const Display13HDMIWiring &w,const XELPMemory::Layout &memory) {
const Display13HDMIWiring &w,const XELPMemory::Layout &memory,
NormalizeScratch &scratch) {
Result r;r.before=boot.activePipes;
if(!admitted)return r;
if(!admitted || &boot==&scratch.after)return r;
r.status=Status::Topology;
const auto active=boot.activePipes;
if(!memory.valid || !Display13FBTopology::multiOutputWiring(w) || boot.status!=AlderLakeN::ReadoutStatus::Complete ||
if(w.panel || !memory.valid || !Display13FBTopology::multiOutputWiring(w) || boot.status!=AlderLakeN::ReadoutStatus::Complete ||
!(active&1) || active==1 || (active&~7U) || (active&~boot.poweredPipes))return r;
uint32_t ports[3] {},clocks[3] {},transport[3] {},used=0,dpPipes=0;
Display13TC::Boot tc[3];
auto &tc=scratch.tc;
for(auto &port:tc)port={};
uint32_t power=0,ddiPower=0,auxPower=0,gate=0;
if(!AlderLakeN::value(boot,0x45404,power) || !AlderLakeN::value(boot,0x45454,ddiPower) ||
!AlderLakeN::value(boot,0x45444,auxPower) ||
Expand Down Expand Up @@ -306,7 +314,7 @@ template<class IO> Result normalize(IO &io,const AlderLakeN::DisplayReadout &boo
parked|=1U<<p;
}
current=UINT32_MAX;
const auto after=AlderLakeN::readDisplay(io,true);r.after=after.activePipes;
auto &after=scratch.after;AlderLakeN::readDisplay(io,true,after);r.after=after.activePipes;
if(after.status!=AlderLakeN::ReadoutStatus::Complete || after.activePipes!=1 || !peer()) {r.status=Status::PeerChanged;return r;}
r.stage=7;r.status=Status::Complete;return r;
}
Expand Down
Loading
Loading