Post

Replies

Boosts

Views

Activity

APIs dropped from MacCatalyst SDK without prior deprecation - is this to be expected?
We maintain a device driver for macOS. This consists of a bunch of components including a Launch Agent, a (Cocoa/AppKit) status bar item app, and a "main" windowed UI app. These all communicate with each other via XPC using the "low level" XPC APIs (libxpc). The launch agent registers a named Mach XPC service, and the two apps connect to it when they launch. Last year, we had an overhaul of the main UI app as we wanted to add a bunch of features. In the process we decided to switch it from Cocoa/AppKit to Mac Catalyst/UIKit as we were also contemplating porting the driver to iPadOS in future; plus, UIKit is a little simpler and I'm not a great UI programmer, so any simplification is welcome. This has worked OK so far, but in our attempt to upgrade our build toolchain from Xcode 15.2 to Xcode 15.4 we found that the Catalyst app would no longer build. Apparently the xpc_connection_create_mach_service() and xpc_connection_set_peer_code_signing_requirement() functions are no longer available as of the Mac Catalyst SDK included with Xcode 15.3. This makes it impossible for the app to connect to the launch agent's XPC service. A few things to note: These functions were never deprecated, so we did not have their impending removal on our radar. It seems they actually used to be available in the iOS SDK and were removed from that, so Mac Catalyst is effectively collateral damage. I don't think these would ever have been usable in apps on the iOS App Store, so perhaps they were removed from the iOS SDK ahead of support for notarised iOS app distribution. So it looks like they might have been removed from Mac Catalyst SDK by accident? I therefore filed a bug about this - FB13929309 - about 6 weeks ago. Reinstating the functions for Mac Catalyst would seem like a very straightforward fix, but I've not had a hint of feedback on the report. I guess my forum question comes down to this: Is Mac Catalyst considered a platform for building macOS apps in its own right? Or are we "holding it wrong" and should we only treat it as a way of tweaking Mac ports of iOS/iPad-first apps? Should we expect APIs to disappear from the Mac Catalyst SDK with zero notice? We can still build with Xcode 15.2 for the moment, and the app built this way runs fine up to and including the macOS 15 beta. But thanks to the limited forward and backward compatibility schedule for Xcode we can't stay on old Xcode for long We're also planning to make some feature changes to the app in the near future, and I don't want to be investing in an app built on a platform with no future. I'd rather port the app back to Cocoa/AppKit before adding features if that's the case.
5
0
792
Aug ’24
Problems integrating Hypervisor.framework APIC and IOAPIC
Introduction I'm trying to integrate support for the APIC implementation added to Hypervisor.framework back in macOS 12 into the open source Qemu VMM. Qemu contains a VMM-side software implementation of the APIC, but it shows up as a major performance constraint in profiling, so it'd be nice to use the in-kernel implementation. I've previously submitted DTS TSIs (case 3345863) for this and received some high level pointers, but I'm told the forums are now the focus for DTS. I've got things working to what feels like 95%, but I'm still tripping up on a few things. FreeBSD and macOS guests are successfully booting and running most of the time, but there are sporadic stalls which point towards undelivered interrupts. Linux fails early on. A number of key test cases are failing in the 'apic' and 'ioapic' test suites that are part of the open source 'kvm-unit-tests' project, and I've run out of ideas for workarounds. Broadly, I'm doing this: When calling hv_vm_create, I pass the HV_VM_ACCEL_APIC flag. The VM uses the newer hv_vcpu_run_until() API. After VM exits, query hv_vcpu_exit_info() in case there's anything else to do. Page fault VM exits in the APIC's MMIO range are forwarded to hv_vcpu_apic_write and hv_vcpu_apic_read respectively. (With hv_vcpu_exit_info check and post-processing if no_side_effect returns true.) Writes to the APICBASE MSR do some sanity checks (throw exception on invalid state transitions etc) and update the MMIO range via hv_vmx_vcpu_set_apic_address() if necessary. HVF seems to do its own additional handling for the actual APIC state changes. (Moving the MMIO and enabling the APIC at the same time fails: FB14021745) Various machinery and state handling around INIT and STARTUP IPIs for bringing up the other vCPUs. This was fiddly to get working but I think I've got it now. MSIs from virtual devices are delivered via hv_vm_lapic_msi. Reads and writes for PIC and ELCR I/O ports are forwarded to the hv_vm_atpic_port_write/hv_vm_atpic_port_read APIs. (In theory, interrupt levels on the PIC are controlled via hv_vm_atpic_assert_irq/hv_vm_atpic_deassert_irq but all modern OSes disable the PIC anyway.) Page faults for the IOAPIC's MMIO range are forwarded to hv_vm_ioapic_read/hv_vm_ioapic_write. Virtual devices deliver their interrupts using hv_vm_ioapic_assert_irq/hv_vm_ioapic_deassert_irq/hv_vm_ioapic_pulse_irq for level/edge-triggered interrupts respectively. Now for the parts where I'm stuck and I'm either doing something wrong, or there are bugs in HVF's implementation: Issues I'm running into IOAPIC: 1. Unmasking during raised interrupt level, test_ioapic_level_mask test case: Guest enables masking on a particular level-triggered interrupt line. (MMIO write to ioredtbl entry) The virtual device raises interrupt level to 1. VMM calls hv_vm_ioapic_assert_irq(). No interrupt, because masked, so far so good. The guest unmasks the interrupt via another write to the ioredtbl entry. At this point I would expect the interrupt to be delivered to the vCPU. This is not the case. Even another call to hv_vm_ioapic_assert_irq() after unmasking will have no effect. Only if we deassert and reassert does the guest receive anything. (This is my current workaround, but it is rather ugly because I essentially need to maintain shadow state to detect the situation.) 2. Retriggering, test case test_ioapic_level_retrigger: The vCPU enters a interrupts-disabled section (cli instruction) The virtual device asserts level-triggered interrupt. VMM calls hv_vm_ioapic_assert_irq(). The vCPU leaves the interrupts-disabled section (sti instruction) and starts executing other code (or halts, as in the test case) Interrupt is delivered to vCPU, runs interrupt handler. Interrupt handler signals EOI. Note that interrupt is still asserted. Outside the interrupt handler, the vCPU briefly disables interrupts again (cli) The vCPU once again re-enables interrupts (sti) and halts (hlt) Here we would expect the interrupt to be delivered again, but it is not. I don't currently have a workaround for this because none of these steps causes hv_vcpu_run_until exits where this condition could be detected. 3. Coalescing, test_ioapic_level_coalesce: The virtual device asserts a level-triggered interrupt line. The vCPU enters the corresponding handler. The device de-asserts the interrupt level. The device re-asserts the interrupt. The device once again de-asserts the interrupt. The interrupt handler sets EOI and returns. We would expect the interrupt handler to only run once in this sequence of events, but as it turns out, it runs a second time! This is less critical than the previous 2 unexpected behaviours, because spurious interrupts are usually only slightly detrimental to performance, whereas undelivered interrupts can cause system hangs. However, this doesn't exactly instill confidence in the implementation. I've submitted the above, as they look like bugs to me - either in the implementation, or lack of documentation - as FB14425412. APIC To work around the HVF IOAPIC problems mentioned above, I tried to use the HVF APIC implementation in isolation without the ATPIC and IOAPIC implementations. Instead, I provided (VMM side) software implementations of these controllers. However, the software IOAPIC needs to receive end-of-interrupt notifications from the APIC. This is what I understood the HV_APIC_CTRL_IOAPIC_EOI flag to be responsible for, so I passed it to hv_vcpu_apic_ctrl() during vCPU initialisation. The software IOAPIC implementation receives all the MMIO writes, maintains IOAPIC state, and calls hv_vm_send_ioapic_intr() whenever interrupts should be delivered to the VM. However, I have found that hv_vcpu_exit_info() never returns HV_VM_EXITINFO_IOAPIC_EOI. When the HVF APIC is in xAPIC mode, I can detect writes to offset 0xb0 in the MMIO write handler and query hv_vcpu_exit_ioapic_eoi() for the vector whose handler has run. However, once the APIC is in x2APIC mode, there are no exits for the x2APIC MSR accesses, so I can't see how I might get those EOI notifications. Am I interpreting the purpose of HV_APIC_CTRL_IOAPIC_EOI correctly? Do I need to do anything other than hv_vcpu_apic_ctrl to make it work? How should I be receiving the EOI notifications? I was expecting vCPU run exits, but this does not appear to be the case? Again, either a crucial step is missing from the documentation, or there's a bug in the implementation. I've submitted this as FB14425590. My Questions: Has anyone got the HVF APIC/IOAPIC working for the general purpose case, i.e. guest OS agnostic, all edge cases handled? The issues I've run into - are these bugs in HVF? Do I need extra support code/workarounds to make the edge cases work? Is using the APIC without the HVF's IOAPIC an intended supported use case or am I wasting my time on this "split" setup?
1
1
529
Jul ’24
Inconsistent threadExecutionWidth which doesn't always match SIMD group size in Metal Compute Shader?
I'm trying to use the SIMD group reduction/prefix functions in a series of reasonably complex compute kernels in a Mac app. I need to allocate some threadgroup memory for coordinating between SIMD groups in the same thread group. This array should therefore should have a capacity depending on [[simdgroups_per_threadgroup]], but that's not a compile time value, so it can't be used as an array dimension. Now, according to various WWDC session videos (e.g. WWDC2022 "Scale compute workloads acroos Apple GPUs), threadExecutionWidth on the pipeline object should return the SIMD group size, with which I could then allocate an appropriate amount of memory using setThreadgroupMemoryLength:atIndex: on the compute encoder. This works consistently on some hardware (e.g. Apple M1, threadExecutionWidth always seems to report 32) but I'm hitting configurations where threadExecutionWidth does not match apparent SIMD group size, causing runtime errors due to out of bounds access. (e.g. on Intel UHD Graphics 630, threadExecutionWidth = 16 for some complex kernels, although SIMD group size seems to be 32) Will the SIMD group size always be the same for all kernels on a device, so should I trust threadExecutionWidth only for the most trivial of kernels? Or should I submit a trivial kernel to the GPU which returns [[threads_per_simdgroup]]? I suspect the problem might occur in kernels where Metal offers an "odd" (non-pow2) maximum thread group sizes due to exhaustion of some resource (registers?), although in the case I'm encountering, the maximum threadgroup size is reported as 896, which is an integer multiple of 32, so it's not as if it's using the greatest common denominator between max threadgroup size and SIMD group size for threadExecutionWidth.
0
1
888
Jun ’22
DeviceDiscoveryExtension basics, can't get sample project to work
I'm trying to understand what exactly is made possible by Media Device Discovery Extensions, what responsibility the containing app has, and what exactly is made available to other apps or the system, if anything. I haven't been able to find any meaningful high level documentation, and WWDC 2022 session 10096 only mentions these new extensions in passing. The most comprehensive body of information I found is the example project: https://developer.apple.com/documentation/devicediscoveryextension/discovering_a_third-party_media-streaming_device?changes=latest_beta&language=objc However, I don't think it's working the way it should out of the box: I've got the Client target app built and running on an iPad Pro running iPadOS 16 Beta 2 I've got the MacServer target running on a Mac Mini with macOS 13 Ventura Beta 2 I've got the Server target running on an iPhone with iOS 15.5. (Non-beta) If I tap the AirPlay icon on the Client's video player, I can see the two servers, but selecting one just causes a spinner to show up next to its name. This keeps going for a while, eventually the spinner goes away again, but the device selection tick stays next to 'iPad'. The text "Select route" also doesn't change, which I think it's supposed to, judging by the code. I've tried a variety of combinations of settings on the servers - bluetooth only, bonjour only, different protocols, etc., but I'm always getting the same behaviour. Has anyone had any success in getting the example to work, and how? Is there any high level documentation available that I've missed? Can someone explain what exactly we can build with this in more detail than "implementations of custom A/V streaming protocols?" The WWDC session video talks about 3rd party SDKs, so do these extensions have to be embedded in every app that would be streaming the video, implying that it's not useful for mirroring?
4
0
1.6k
Jun ’22
How do I set DriverKit entitlements during development?
I'm trying to port an existing kext-based driver to DriverKit. My client has requested the relevant signing entitlements, but they haven't come through yet. Fortunately, the documentation says that we can develop and test DriverKit drivers without these entitlements if we disable SIP on the target machine. Right?Quoting https://developer.apple.com/documentation/driverkit/requesting_entitlements_for_driverkit_development?language=objc> Note> While waiting for Apple to grant your entitlement requests, you can continue to develop and test your drivers on your local systems. For information about how to disable the necessary security checks, see Debugging and Testing System Extensions."That referenced page, https://developer.apple.com/documentation/driverkit/debugging_and_testing_system_extensions?language=objc then goes on to say I need to disable SIP on the target system, along with a command for disabling the /Applications/ directory requirement.Well, I can't get it working. I've set up a Cocoa app, and embedded a DriverKit target within it. I'm signing the app using a Mac Developer certificate and an explicitly-created provisioning profile which has the system extension capability and which matches the app's bundle ID. I've wired up a button in the app to set up and submit a OSSystemExtensionRequest and implemented the delegate methods.Configuring the entitlements and signing of the .dext bundle is where I'm having trouble:If I don't set the com.apple.developer.driverkit entitlement, I can sign, build, and embed the dext, but submitting the system extension activation request fails with error code 9: "Invalid extension configuration in Info.plist and/or entitlements" (OSSystemExtensionErrorValidationFailed)If I do set the com.apple.developer.driverkit entitlement, I cannot find any combination of settings with which Xcode will even allow me to kick off the build (names have been anonymised):error: Automatic signing is unable to resolve an issue with the "DriverTarget" target's entitlements. Automatic signing can't add the com.apple.developer.driverkit entitlement to your provisioning profile. Switch to manual signing and resolve the issue by downloading a matching provisioning profile from the developer website. Alternatively, to continue using automatic signing, remove this entitlement from your entitlements file and its associated functionality from your code. (in target 'DriverTarget' from project 'DriverProject') error: Provisioning profile "Mac Team Provisioning Profile: *" doesn't include the com.apple.developer.driverkit entitlement. (in target 'DriverTarget' from project 'DriverProject')Both outcomes make logical sense:The system extension mechanism needs to know what kind of extension this is in order to install/activate it, and that appears to be done via the entitlements embedded in the code signature.You can't sign a binary with an entitlement for which your signing identity hasn't been whitelisted.The conclusion would therefore be that you can't build and test a DriverKit driver without explicitly being given permission by Apple. However:That appears to contradict the documentation.It doesn't make much logical sense why DriverKit is more restricted than kext development. Anyone curious about writing kexts can just turn off SIP and go wild, but only the chosen ones are allowed to tinker with DriverKit, the API that Apple is trying to push on everyone.So, have I missed something? What should I be doing to get this working?Incidentally, eskimo1's suggestion of setting the boot-arg here https://forums.developer.apple.com/thread/125048#394539 does not appear to make any difference, which makes sense as it's not actually amfid which is causing the failure.Full system log (### replacing non-public identifiers) for the relevant time frame:2020-04-16 12:19:20.992008+0200 0x1ef9 Default 0x3930 680 0 USB Display Device: installDriverButtonPushed: Submitted extension activation request 2020-04-16 12:19:20.994555+0200 0x1b65 Activity 0x3035 621 0 sysextd: (Security) SecTrustEvaluateIfNecessary 2020-04-16 12:19:20.995952+0200 0x1ded Default 0x3035 192 0 trustd: [com.apple.securityd:SecError] OCSPResponse: single response has extension(s). 2020-04-16 12:19:21.001309+0200 0x1b65 Default 0x3930 621 0 sysextd: attempting to realize extension with identifier ### 2020-04-16 12:19:21.002978+0200 0x1b65 Activity 0x3036 621 0 sysextd: (Security) SecTrustEvaluateIfNecessary 2020-04-16 12:19:21.004210+0200 0x1ded Default 0x3036 192 0 trustd: [com.apple.securityd:SecError] OCSPResponse: single response has extension(s). 2020-04-16 12:19:21.007341+0200 0x1b65 Default 0x3930 621 0 sysextd: Realizing target path: 2020-04-16 12:19:21.008133+0200 0x1b65 Default 0x3930 621 0 sysextd: Bundle class: UncachedBundle 2020-04-16 12:19:21.009911+0200 0x1b65 Activity 0x3037 621 0 sysextd: (Security) SecTrustEvaluateIfNecessary 2020-04-16 12:19:21.011170+0200 0x1ded Default 0x3037 192 0 trustd: [com.apple.securityd:SecError] OCSPResponse: single response has extension(s). 2020-04-16 12:19:21.013814+0200 0x1b65 Default 0x3930 621 0 sysextd: System extension does not appear to belong to any extension categories 2020-04-16 12:19:21.014108+0200 0x1ef9 Default 0x3930 680 0 USB Display Device: request:didFailWithError: Error Domain=OSSystemExtensionErrorDomain Code=9 "Invalid extension configuration in Info.plist and/or entitlements" UserInfo={NSLocalizedDescription=Invalid extension configuration in Info.plist and/or entitlements} 2020-04-16 12:19:21.014235+0200 0x1ef9 Default 0x3930 680 0 USB Display Device: Error code: 9
1
0
2.7k
Apr ’20
System Extension activation: OSSystemExtensionErrorExtensionNotFound
I'm trying to get to grips with the new System Extensions mechanism, as it looks like I'll need to port some of the kexts I help maintain to this new tech, at least once it reaches sufficient feature parity. I can't really start filing ERs until I've given it a try though. So I've been putting together a demo app with a system extension but have now run into a roadblock.Basically, my activation request is failing with OSSystemExtensionErrorExtensionNotFound and I don't understand why or how to fix it.I created a new Cocoa App Xcode project, then added an "App Proxy" type network extension target to the project. Xcode automatically set it up to be embedded in the .app bundle at Contents/Library/SystemExtensions/ - I ultimately need to use the Endpoint Security API, but there's no Xcode integration for that yet, it seems.I've got SIP disabled (so it shouldn't be an issue of missing entitlements in the signing cert), and the extension and app are both signed with a Mac Developer cert. I've added com.apple.developer.system-extension.install to the App's .entitlements.In the App, I have: OSSystemExtensionRequest* request = [OSSystemExtensionRequest activationRequestForExtension:@"my.rdns.prefix.EndpointSecurityDemo" queue:dispatch_get_main_queue()]; request.delegate = self; [[OSSystemExtensionManager sharedManager] submitRequest:request];I build the extension & app, then copy the app to /Applications to avoid OSSystemExtensionErrorUnsupportedParentBundleLocation.When run, the above code results in the request:didFailWithError: delegate method being called with an error code of OSSystemExtensionErrorExtensionNotFound.In Console.app, I can't see anything other than what my app logs, plus sysextd's "request contains no authorizationref" and "attempting to realize extension with identifier my.rdns.prefix.EndpointSecurityDemo". As there's no API for specifying an auth ref, I assume the first message isn't something I can do anything about.I've verified that the bundle identifier in the code matches the one in the system extension's plist once it's in the built app bundle. And the extension is definitely located at Contents/Library/SystemExtensions within the built .app.I'm at a loss as to why sysextd(?) can't find my extension. Is there any example code I can compare against? Any documentation I might have missed? The DriverKit WWDC session video is a nice conceptual intro but is very light on practicalities. Beyond that, I'm just using the OSSystemExtensions (and EndpointSecurity) headerdocs.Bonus question: In the headerdocs for OSSystemExtensionRequestDelegate I noticed a mention of "If the local system has System Extension developer mode enabled, this callback will always fire when an existing extension is found, regardless of version identifiers." What is this mode and how do I enable it?Update: For the heck of it, I tried with a DriverKit driver target (.dext). sysextd does find this one, and request:didFinishWithResult: returns 0 (OSSystemExtensionRequestCompleted) and I get a kext-like approval dialog. (I'm not convinced the dext actually ran after I approved it though, I certainly can't find the "Hello World" it's supposed to os_log anywhere, and it doesn't appear in ioreg.)
4
1
3.6k
Jun ’19