#include "NativeLegacyRequest.h" // SPDX-License-Identifier: BSD-2-Clause-Patent // Reuse the unchanged original generic framebuffer implementation. Unknown // backend leaves stop; this class is not yet a published system display. #include "OriginalLegacyFramebufferABI.h" #include #include #include #include #include #include extern "C" __attribute__((noreturn)) void native_legacy_backend_guard(unsigned,void *,void *); extern "C" IOService *native_legacy_display_allocate(void); extern "C" bool native_legacy_gop_mapping_observed(IOService *,IOMemoryMap *,uint32_t,uint32_t); extern "C" void native_legacy_registration_observed(IOService *); extern "C" void native_legacy_client_enter(task_t,uint32_t); extern "C" void native_legacy_client_observed(IOReturn,IOUserClient *); extern "C" void native_legacy_pm_observed(unsigned,IOReturn,unsigned long); extern "C" IOMemoryDescriptor *native_legacy_surface_descriptor(OSObject *); extern "C" uint64_t native_legacy_surface_width(OSObject *),native_legacy_surface_height(OSObject *); extern "C" uint64_t native_legacy_surface_stride(OSObject *),native_legacy_surface_allocation(OSObject *); extern "C" uint32_t native_legacy_surface_format(OSObject *),native_legacy_surface_planes(OSObject *); extern "C" void native_legacy_frame_observed(unsigned kind,uint64_t count,uint64_t minx,uint64_t miny,uint64_t maxx,uint64_t maxy); extern "C" void native_legacy_frame_colors_observed(const uint32_t *colors,const uint64_t *counts,uint64_t other); extern "C" void native_legacy_swap_validation_observed(bool,unsigned,const void *,const void *); extern "C" bool native_legacy_prepare_scanout(void); extern "C" void native_legacy_transfer_observed(IOReturn,const void *,uint64_t); extern "C" void native_legacy_copy_timing_observed(uint64_t ticks,uint64_t bytes); extern "C" void native_legacy_retirement_observed(IOService *); class NativeLegacyDisplay : public IOMobileFramebufferLegacy { OSDeclareDefaultStructors(NativeLegacyDisplay) IOMemoryMap *bootMapping=nullptr; uint32_t scanBudget=32; void *shadow=nullptr;uint64_t shadowBytes=0;bool shadowValid=false; bool primary=false; bool logicalPM=false; bool transferred=false; const OriginalLegacySwapRequest *validatedRequest=nullptr; public: void free() override {if(logicalPM){PMstop();logicalPM=false;}primary=false;OSSafeReleaseNULL(bootMapping);IOMobileFramebufferLegacy::free();} void stop(IOService *provider) override { transferred=false;validatedRequest=nullptr; if(logicalPM){PMstop();logicalPM=false;} primary=false;IOMobileFramebufferLegacy::stop(provider);OSSafeReleaseNULL(bootMapping); } bool start(IOService *provider) override { if(!IOMobileFramebufferLegacy::start(provider))return false; unsigned enabled=0;PE_parse_boot_argn("offline-legacy-pm",&enabled,sizeof(enabled)); if(!enabled)return true; unsigned phase=0;PE_parse_boot_argn("offline-legacy-phase",&phase,sizeof(phase)); if(enabled!=1 || phase!=4 || !provider || !bootMapping || !primary) { native_legacy_pm_observed(1,kIOReturnNotReady,0);IOMobileFramebufferLegacy::stop(provider);return false; } // Actual IOKit PM lifecycle for logical scanout scheduling only. GOP // provides no panel power switch: neither state advertises physical // power output, power control or a hardware power transition. static IOPMPowerState states[2]={}; states[0].version=states[1].version=1; states[1].capabilityFlags=kIOPMDeviceUsable; PMinit();logicalPM=initialized; if(!logicalPM){native_legacy_pm_observed(1,kIOReturnNotReady,0);IOMobileFramebufferLegacy::stop(provider);return false;} provider->joinPMtree(this); IOReturn status=registerPowerDriver(this,states,2);native_legacy_pm_observed(1,status,0); if(status==kIOReturnSuccess){status=changePowerStateToPriv(1);native_legacy_pm_observed(2,status,1);} if(status!=kIOReturnSuccess){PMstop();logicalPM=false;IOMobileFramebufferLegacy::stop(provider);return false;} return true; } IOReturn setPowerState(unsigned long ordinal,IOService *device) override { if(!logicalPM)return IOMobileFramebufferLegacy::setPowerState(ordinal,device); if(ordinal>1 || !bootMapping){native_legacy_pm_observed(3,kIOReturnUnsupported,ordinal);return kIOReturnUnsupported;} native_legacy_pm_observed(3,kIOReturnSuccess,ordinal); IOReturn status=IOMobileFramebufferLegacy::setPowerState(ordinal,device); native_legacy_pm_observed(4,status,ordinal);return status; } // The explicit offline system-client experiment publishes only the real // GOP mode. Unknown backend calls retain their stop-with-caller guards. void registerService(IOOptionBits options=0) override { unsigned enabled=0;PE_parse_boot_argn("offline-legacy-register",&enabled,sizeof(enabled)); if(enabled!=1)native_legacy_backend_guard(84,this,__builtin_return_address(0)); IOService::registerService(options);native_legacy_registration_observed(this); } bool isMainDisplay() override {return primary;} void kickSwapQueue() override { unsigned enabled=0;PE_parse_boot_argn("offline-legacy-transfer",&enabled,sizeof(enabled)); if(enabled!=1 || transferred || !logicalPM || !bootMapping || !primary)return; const OriginalLegacySwapRequest *request=pendingSwapRequest(); if(!request || request!=validatedRequest)return; // This bounded first-frame transfer preserves the original queue's // resource ownership. Separately gated retirement follows the copy; // neither path signals a hardware fence, announces a vblank or // claims general producer synchronization. // The owned GPU instrument completes its work before submission. // The separate original software-renderer experiment uses completed // synchronous CPU surfaces, with no GPU or display fence fabricated. OSObject *surface=nullptr; memcpy(&surface,reinterpret_cast(request)+2624,sizeof(surface)); if(!surface) { // Validated blank swap (all layers disabled over black): solid-fill the actual GOP. PE_Video blank={}; uint32_t bw=0,bh=0;getDisplayDimensions(&bw,&bh); uint64_t blankLength=(uint64_t)bw*bh*4; if(PE_current_console(&blank) || blank.v_baseAddr!=bootMapping->getPhysicalAddress() || blank.v_width!=bw || blank.v_height!=bh || blank.v_depth!=32 || blank.v_rotate || !native_legacy_surface_bounds(bw,bh,blank.v_rowBytes,bootMapping->getLength(),bootMapping->getLength())) { native_legacy_transfer_observed(kIOReturnNotReady,request,0);return; } if(!native_legacy_prepare_scanout()) {native_legacy_transfer_observed(kIOReturnNotReady,request,0);return;} if(!native_legacy_fill_pixels(reinterpret_cast(bootMapping->getVirtualAddress()), bootMapping->getLength(),blank.v_rowBytes,bw,bh,0xff000000U)) { native_legacy_transfer_observed(kIOReturnBadArgument,request,0);return; } __asm__ volatile("dsb sy" ::: "memory"); shadowValid=false; transferred=true; native_legacy_frame_observed(1,0,0,0,0,0); {const uint32_t colors[4]={0xff000000U,0,0,0};const uint64_t counts[4]={(uint64_t)bw*bh,0,0,0};native_legacy_frame_colors_observed(colors,counts,0);} native_legacy_transfer_observed(kIOReturnSuccess,request,blankLength); finishTransfer(request); return; } if(!surface->metaCast("IOSurface"))return; IOMemoryDescriptor *memory=native_legacy_surface_descriptor(surface); PE_Video video={}; uint32_t width=0,height=0;getDisplayDimensions(&width,&height); uint64_t length=(uint64_t)width*height*4; uint64_t sourceStride=native_legacy_surface_stride(surface); uint64_t allocation=native_legacy_surface_allocation(surface); if(!memory || !memory->metaCast("IOMemoryDescriptor") || !native_legacy_surface_bounds(width,height,sourceStride,allocation,memory->getLength()) || native_legacy_surface_width(surface)!=width || native_legacy_surface_height(surface)!=height || native_legacy_surface_format(surface)!=0x42475241 || native_legacy_surface_planes(surface) || PE_current_console(&video) || video.v_baseAddr!=bootMapping->getPhysicalAddress() || video.v_width!=width || video.v_height!=height || video.v_depth!=32 || video.v_rotate || !native_legacy_surface_bounds(width,height,video.v_rowBytes,bootMapping->getLength(),bootMapping->getLength())) { native_legacy_transfer_observed(kIOReturnNotReady,request,0);return; } IOMemoryMap *source=memory->map(kIOMapAnywhere|kIOMapReadOnly); if(!source || !source->getVirtualAddress() || source->getLength()(bootMapping->getVirtualAddress()), bootMapping->getLength(),video.v_rowBytes,reinterpret_cast(source->getVirtualAddress()), source->getLength(),sourceStride,width,height)) { OSSafeReleaseNULL(source);native_legacy_transfer_observed(kIOReturnBadArgument,request,0);return; } // The two content scans read every pixel again; they only describe the first frames (the probe keeps 32). if(scanBudget) { --scanBudget; uint64_t scan[5]; if(native_legacy_frame_scan(reinterpret_cast(source->getVirtualAddress()),source->getLength(),sourceStride,width,height,scan)) native_legacy_frame_observed(0,scan[0],scan[1],scan[2],scan[3],scan[4]); uint32_t colors[4];uint64_t counts[4],other=0; if(native_legacy_frame_colors(reinterpret_cast(source->getVirtualAddress()),source->getLength(),sourceStride,width,height,colors,counts,&other)) native_legacy_frame_colors_observed(colors,counts,other); } if(!native_legacy_prepare_scanout()) { OSSafeReleaseNULL(source);native_legacy_transfer_observed(kIOReturnNotReady,request,0);return; } uint64_t copyStart;__asm__ volatile("isb\n\tmrs %0, cntpct_el0":"=r"(copyStart)); uint64_t stored=UINT64_MAX; { // Store only what changed since the previous frame (the scanout memory is slow device memory). uint64_t need=(uint64_t)width*height*4; // Differential copy is opt-in (sheng-fb-diff=1): on hardware it reads the whole source every frame and was not faster // than the plain copy (143-285 ms per swap either way), and the first boots with it were followed by an unresponsive // lock screen that disappeared with the plain copy (int-005). static int diffOff=-1; if(diffOff<0){unsigned v=0;PE_parse_boot_argn("sheng-fb-diff",&v,sizeof v);diffOff=(v!=1);} if(!diffOff && !shadow && need<=(64ull<<20)){shadow=IOMallocAligned((vm_size_t)need,16384);if(shadow){shadowBytes=need;shadowValid=false;}} if(!diffOff && shadow && shadowBytes>=need) stored=native_legacy_copy_diff(reinterpret_cast(bootMapping->getVirtualAddress()), bootMapping->getLength(),video.v_rowBytes,reinterpret_cast(source->getVirtualAddress()), source->getLength(),sourceStride,width,height,shadow,shadowValid); if(stored!=UINT64_MAX)shadowValid=true; } if(stored==UINT64_MAX) { shadowValid=false; if(!native_legacy_copy_pixels(reinterpret_cast(bootMapping->getVirtualAddress()), bootMapping->getLength(),video.v_rowBytes,reinterpret_cast(source->getVirtualAddress()), source->getLength(),sourceStride,width,height)) { OSSafeReleaseNULL(source);native_legacy_transfer_observed(kIOReturnBadArgument,request,0);return; } stored=length; } __asm__ volatile("dsb sy" ::: "memory"); {uint64_t copyEnd;__asm__ volatile("isb\n\tmrs %0, cntpct_el0":"=r"(copyEnd));native_legacy_copy_timing_observed(copyEnd-copyStart,stored);} OSSafeReleaseNULL(source);transferred=true; native_legacy_transfer_observed(kIOReturnSuccess,request,length); finishTransfer(request); } // Separately gated retirement after a real scanout store, shared by copied and blank frames. void finishTransfer(const OriginalLegacySwapRequest *request) { unsigned retire=0,cpuPresent=0; PE_parse_boot_argn("offline-legacy-retire",&retire,sizeof(retire)); PE_parse_boot_argn("offline-legacy-cpu-present",&cpuPresent,sizeof(cpuPresent)); if(retire==1 || cpuPresent==1) { // Only after the real copy and store barrier: select the actual // pending head and let the unchanged family release it and wake // waiters. This reports memory scanout completion, not a panel // vblank or hardware fence. Producer completion must precede // submission; this does not implement asynchronous GPU sync. if(!activateOnlyPendingSwap(request))return; validatedRequest=nullptr; IOMobileFramebufferLegacy::completeCurrentSwap(); native_legacy_retirement_observed(this); // The unchanged family has retired this generation and released // its current reference. Permit the next independently validated // single pending frame. Keep the latch set while completion can // reenter this method, and whenever retirement remains disabled. transferred=false; } } IOReturn collectCompletionReports(uint32_t,void *,uint32_t *count,uint32_t) override { // The original completion helper passes null optional outputs and // ignores this IOReturn. GOP exposes no hardware completion telemetry. if(count)*count=0; return kIOReturnUnsupported; } bool validateSwap(const OriginalLegacySwapRequest *request) override { // Qualified original request fields are packed; use memcpy for each // access. Accept only one full-frame, untransformed, nonplanar BGRA // surface with real original backing memory. No generic capability // or success is supplied for other layer/format/geometry contracts. auto bytes=reinterpret_cast(request); auto word=[&](unsigned offset){uint32_t value=0;memcpy(&value,bytes+offset,4);return value;}; OSObject *surface=nullptr;unsigned reason=1;bool accepted=false; unsigned enabled=0;PE_parse_boot_argn("offline-legacy-validate",&enabled,sizeof(enabled)); if(enabled==1 && logicalPM && primary && bootMapping && request) { reason=2;OSObject *surfaces[4]={};memcpy(surfaces,bytes+2624,sizeof(surfaces));surface=surfaces[0]; uint32_t width=0,height=0;getDisplayDimensions(&width,&height); if(surface && surface->metaCast("IOSurface") && !surfaces[1] && !surfaces[2] && !surfaces[3] && native_legacy_single_layer_state(word(792),word(796),word(800))) { reason=3;bool rectangles=true; const unsigned rectOffsets[]={17,81}; for(unsigned offset: rectOffsets) { uint32_t rect[4]={};memcpy(rect,bytes+offset,sizeof(rect)); rectangles=rectangles && rect[0]==0 && rect[1]==0 && rect[2]==width && rect[3]==height; } bool options=true;for(unsigned offset=804;offset<=812;offset+=4)options=options && word(offset)==0; if(width && height && rectangles && options && word(816)==width && word(820)==height) { reason=4; IOMemoryDescriptor *memory=native_legacy_surface_descriptor(surface); if(native_legacy_surface_width(surface)==width && native_legacy_surface_height(surface)==height && native_legacy_surface_format(surface)==0x42475241 && !native_legacy_surface_planes(surface) && memory && memory->metaCast("IOMemoryDescriptor") && native_legacy_surface_bounds(width,height,native_legacy_surface_stride(surface), native_legacy_surface_allocation(surface),memory->getLength())) { reason=0;accepted=true; } } } } if(!accepted && reason==2 && !surface && enabled==1) { // No surface in any slot and every updated layer disabled over black: present a blank screen. OSObject *none[4]={};memcpy(none,bytes+2624,sizeof(none)); uint32_t width=0,height=0;getDisplayDimensions(&width,&height); bool options=true;for(unsigned offset=804;offset<=812;offset+=4)options=options && word(offset)==0; if(!none[0] && !none[1] && !none[2] && !none[3] && width && height && options && word(816)==width && word(820)==height && native_legacy_blank_state(word(792),word(796),word(800))) { reason=5;accepted=true; } } validatedRequest=accepted?request:nullptr; native_legacy_swap_validation_observed(accepted,reason,request,surface);return accepted; } // The original slot-188 caller passes a uint32_t output and consumes an // IOReturn. Its implementation assumes an AppleARMIODevice provider. // Query our actual generic provider instead; GOP supplies no dot pitch. IOReturn getDotPitch(uint32_t *pitch) override { if(!pitch)return kIOReturnBadArgument; *pitch=0;IOService *provider=getProvider();if(!provider)return kIOReturnNotReady; OSObject *property=provider->getProperty("dot-pitch"); if(!property)return kIOReturnNotFound; OSData *data=OSDynamicCast(OSData,property); if(!data || data->getLength()!=sizeof(*pitch))return kIOReturnBadArgument; memcpy(pitch,data->getBytesNoCopy(),sizeof(*pitch));return kIOReturnSuccess; } // Selector 74 enumerates optional frame-info trace keys, not display // modes. This backend implements no trace-key subscription contract. IOReturn supportedFrameInfo(uint32_t,uint32_t *count,char *key,uint32_t *mask) override { if(!count || !key || !mask)return kIOReturnBadArgument; *count=0;*mask=0;bzero(key,64);return kIOReturnUnsupported; } IOReturn newUserClient(task_t task,void *security,UInt32 type,IOUserClient **client) override { native_legacy_client_enter(task,type); IOReturn status=IOMobileFramebufferLegacy::newUserClient(task,security,type,client); native_legacy_client_observed(status,status==kIOReturnSuccess && client?*client:nullptr);return status; } bool startHardware() override { unsigned enabled=0; PE_parse_boot_argn("offline-legacy-gop",&enabled,sizeof(enabled)); if(enabled!=1)return false; PE_Video video={};IOService *provider=getProvider(); if(!provider || PE_current_console(&video) || !video.v_baseAddr || !video.v_width || !video.v_height || video.v_depth!=32 || video.v_rotate || video.v_width>16384 || video.v_height>16384 || video.v_rowBytesUINT64_MAX/video.v_height)return false; IOMemoryMap *mapping=provider->mapDeviceMemoryWithIndex(0,kIOMapInhibitCache); if(!mapping)return false; bool valid=mapping->getVirtualAddress() && mapping->getPhysicalAddress()==video.v_baseAddr && mapping->getLength()==video.v_rowBytes*video.v_height; if(valid) { installBootDimensions((uint32_t)video.v_width,(uint32_t)video.v_height); uint32_t width=0,height=0;getDisplayDimensions(&width,&height); valid=width==video.v_width && height==video.v_height; if(valid) { OSSafeReleaseNULL(bootMapping);bootMapping=mapping;mapping=nullptr;primary=video.v_display==1; valid=native_legacy_gop_mapping_observed(this,bootMapping,width,height); } } OSSafeReleaseNULL(mapping);return valid; } #define LEAF(n) __attribute__((noreturn,noinline)) void opaque##n() override { native_legacy_backend_guard(n,this,__builtin_return_address(0)); } LEAF(211) LEAF(245) LEAF(246) LEAF(306) LEAF(307) LEAF(338) LEAF(345) #undef LEAF }; OSDefineMetaClassAndStructors(NativeLegacyDisplay,IOMobileFramebufferLegacy) IOService *native_legacy_display_allocate(void) {return new NativeLegacyDisplay;}