diff --git a/probes/run.py b/probes/run.py index 4a6a175..6f59970 100755 --- a/probes/run.py +++ b/probes/run.py @@ -128,7 +128,8 @@ MULTI = {'pauth-address': ['probes/pauth-address.S','compat/pauth-native.S'], 'compat/adreno.c', 'compat/adreno-fill.c', 'compat/adreno-ib.c', 'compat/irq-forward.c', 'compat/ans.c', 'compat/ans-window.c', 'compat/nvme-host.c', 'compat/early-stop.c', 'compat/a16-thermal.c', 'probes/memory.S']} # Units linked only when their opt-in define is in FLAGS, after MULTI's, so a build without the define links exactly # the objects it always did and its ELF stays byte-identical. -OPTIONAL = {'xnuboot': (('-DSNAP_ADRENO_COHERENT=1', 'compat/adreno-coherent.c'),)} +OPTIONAL = {'xnuboot': (('-DSNAP_ADRENO_COHERENT=1', 'compat/adreno-coherent.c'), + ('-DSNAP_EDP_BACKLIGHT=1', 'compat/edp-backlight.c'))} def optional_sources(name, flags): diff --git a/probes/xnuboot.c b/probes/xnuboot.c index c5d1621..5ddbe51 100644 --- a/probes/xnuboot.c +++ b/probes/xnuboot.c @@ -870,6 +870,15 @@ struct guest_net_state { #define GUEST_NET_POLL2 4u /* q1n1 Q1N1_CALL_POLL2 */ #define GUEST_NET_CALL_E_OP UINT64_C(0xffffffffffffffff) /* Q1N1_CALL_E_OP: an older q1n1 */ #endif +/* SNAP_EDP_BACKLIGHT (compat/edp-backlight.h, agents/brightness): ADRENO_GUEST_BACKLIGHT sets the panel's DPCD + * brightness over the eDP controller's AUX channel for the kext's AdrenoBacklight, admitted like ADRENO_GUEST_STOP. + * Its state is one zeroed monitor page (setup.c). Off, the code is absent and compat/edp-backlight.c is not linked. */ +#ifndef SNAP_EDP_BACKLIGHT +#define SNAP_EDP_BACKLIGHT 0 +#endif +#if SNAP_EDP_BACKLIGHT +#include "../compat/edp-backlight.h" +#endif #include "../compat/fast-vector.h" _Static_assert(FAST_EXIT_CLASSES==FAST_TRAP_CLASSES-1+FAST_CROSS_CLASSES && FAST_EXIT_CROSS_SLOT(1)==FAST_TRAP_CLASSES-1, diff --git a/probes/xnuboot/context.c b/probes/xnuboot/context.c index cccd845..10e5f6d 100644 --- a/probes/xnuboot/context.c +++ b/probes/xnuboot/context.c @@ -455,6 +455,11 @@ struct context { * monitor arena pages taken at setup (owner 0, never released). */ struct guest_net_state *guest_net; #endif +#if SNAP_EDP_BACKLIGHT + /* compat/edp-backlight.h: ADRENO_GUEST_BACKLIGHT's state, one monitor + * arena page taken at setup (owner 0, never released). */ + struct edp_backlight_state *edp_backlight; +#endif #if SNAP_FAST_EL0_SYSREG || SNAP_SERVICE_SUBCLASS /* Appended only under these opt-in defines, inside the page before the * table arena (asserted below), so define-free layout readers keep every diff --git a/probes/xnuboot/kernel_views.c b/probes/xnuboot/kernel_views.c index 4bb3e35..c20109f 100644 --- a/probes/xnuboot/kernel_views.c +++ b/probes/xnuboot/kernel_views.c @@ -576,6 +576,10 @@ static int txn_publish_exclude(void *ctx,uint64_t page) #if SNAP_GUEST_NET /* The guest-network state, stored the same way (compat/guest-net-link.h). */ if(page>=(uint64_t)c->guest_net && page-(uint64_t)c->guest_netedp_backlight)return 1; #endif for(unsigned cpu=0;cpuvcpus[cpu].guard_count;g++) diff --git a/probes/xnuboot/mmio_dispatch.c b/probes/xnuboot/mmio_dispatch.c index c96bb52..bc08ef2 100644 --- a/probes/xnuboot/mmio_dispatch.c +++ b/probes/xnuboot/mmio_dispatch.c @@ -652,3 +652,46 @@ static void guest_net_hvc(struct context *c, uint64_t *frame, uint64_t elr, uint } #endif +#if SNAP_EDP_BACKLIGHT +/* compat/edp-backlight.c reaches the eDP controller and dispcc through these, physical addresses in q1n1's EL2 + * identity map (Device memory, as the thermal guard's reads). The engine admits only its allow-list + * (edp_bl_mmio_read_ok/edp_bl_mmio_write_ok, proven by tools/test-edp-backlight.c); the barrier orders each access + * before the next, as the controller's FIFO protocol needs. */ +static int edp_backlight_read(void *ctx, uint64_t pa, uint32_t *value) +{ + (void)ctx; + *value = *(const volatile uint32_t *)pa; + __asm__ volatile("dsb sy" ::: "memory"); + return 0; +} +static int edp_backlight_write(void *ctx, uint64_t pa, uint32_t value) +{ + (void)ctx; + *(volatile uint32_t *)pa = value; + __asm__ volatile("dsb sy" ::: "memory"); + return 0; +} +static uint64_t edp_backlight_clock(void *ctx) {(void)ctx; return service_profile_clock();} + +/* ADRENO_GUEST_BACKLIGHT (compat/edp-backlight.h): admitted from EL1 for the HVC the loaded AuxKC's own bytes hold + * (early_stop_guest_caller, as ADRENO_GUEST_STOP). Under the service lock: one AUX transaction at a time, each + * bounded by hz / EDP_BL_AUX_BUDGET_DIV. */ +static void edp_backlight_hvc(struct context *c, uint64_t *frame, uint64_t elr, uint64_t spsr) +{ + const struct params *p = c->params; + const struct edp_backlight_io io = {c, edp_backlight_read, edp_backlight_write, edp_backlight_clock, + p->clock_host_hz}; + uint64_t hvc = elr - 4, pa = guest_pa(hvc), r[4] = {frame[0], frame[1], frame[2], frame[3]}; + uint32_t word = 0; + if (pa && p->auxkc.pa_end > p->auxkc.pa_base + 4 && pa >= p->auxkc.pa_base && pa <= p->auxkc.pa_end - 4) + word = *(volatile uint32_t *)pa; + if (early_stop_guest_caller(&p->auxkc, hvc, pa, word, spsr)) { + r[0] = (uint64_t)(int64_t)EDP_BL_E_ORIGIN; + r[1] = r[2] = r[3] = 0; + } else + edp_backlight_call(c->edp_backlight, &io, r); + record(c, EV_EDP_BACKLIGHT, hvc, frame[1] << 32 | (uint32_t)frame[2], r[0] << 32 | (uint32_t)r[3], r[1]); + for (unsigned n = 0; n < 4; n++) frame[n] = r[n]; +} +#endif + diff --git a/probes/xnuboot/report_layout.c b/probes/xnuboot/report_layout.c index ff61405..24e956f 100644 --- a/probes/xnuboot/report_layout.c +++ b/probes/xnuboot/report_layout.c @@ -217,6 +217,11 @@ struct event { * control's PMU passes << 16, c = stale[0] << 32 | stale[1] (the controls' * stale words, each at most 1,024); probes/run.py decodes it */ #endif +#if SNAP_EDP_BACKLIGHT +#define EV_EDP_BACKLIGHT 60 /* ADRENO_GUEST_BACKLIGHT (compat/edp-backlight.h): a = sub-op << 32 | (u32)level asked, + * b = (u32)result << 32 | the panel's level after the call, c = the reply's x1 (ABI | + * state << 8 | detail << 16 | DPCD 0x700..0x703 << 24 | mode << 56); pc = the HVC */ +#endif #define EV_HANDOFF 7 /* a field of the handoff was read: a = offset */ #define EV_WATCHDOG 6 /* the guest ran too long and was taken back */ #define EV_DISPATCH_CMD 8 /* a call that is not an endpoint call: diff --git a/probes/xnuboot/setup.c b/probes/xnuboot/setup.c index e91c986..56f4c87 100644 --- a/probes/xnuboot/setup.c +++ b/probes/xnuboot/setup.c @@ -90,6 +90,14 @@ int xnuboot_setup(uint64_t work, uint64_t size, uint64_t params, uint64_t out, for (uint64_t n = 0; n < GUEST_NET_STATE_PAGES * 4096 / 8; n++) ((volatile uint64_t *)c->guest_net)[n] = 0; __asm__ volatile("" ::: "memory"); c->next_table += GUEST_NET_STATE_PAGES * 4096; +#endif +#if SNAP_EDP_BACKLIGHT + /* compat/edp-backlight.h: one page, zeroed (EDP_BL_STATE_UNPROBED), probed by the first guest call. */ + if (c->next_table + 4096 > c->work_end) return 1; + c->edp_backlight = (struct edp_backlight_state *)c->next_table; + for (uint64_t n = 0; n < 4096 / 8; n++) ((volatile uint64_t *)c->edp_backlight)[n] = 0; + __asm__ volatile("" ::: "memory"); + c->next_table += 4096; #endif if(c->params->early_stop.flags & EARLY_STOP_F_THERMAL) { if(c->next_table>c->work_end || c->work_end-c->next_table<4096)return 1; diff --git a/probes/xnuboot/trap_dispatch.c b/probes/xnuboot/trap_dispatch.c index 0d24be5..59327da 100644 --- a/probes/xnuboot/trap_dispatch.c +++ b/probes/xnuboot/trap_dispatch.c @@ -578,6 +578,12 @@ static int xnuboot_trap_locked(uint64_t *frame, uint64_t esr, uint64_t *elr) return 0; } #endif +#if SNAP_EDP_BACKLIGHT + if (frame[0] == ADRENO_GUEST_BACKLIGHT) { + edp_backlight_hvc(c, frame, *elr, physical_spsr); + return 0; + } +#endif #if SNAP_ADRENO_IB_ASYNC if (frame[0] == ADRENO_GUEST_IB_POLL) { gpu_ib_poll_hvc(c, frame, *elr);