sys/dev/apple/fw/apple_fw.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | /* * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2026 Abdelkader Boudih <dragonflybsd@seuros.com> * * Apple EFI Firmware Properties driver for DragonFly BSD. * * Discovers Apple-specific ACPI structures on Macs: * - Enumerates Apple SSDTs (SmcDppt, PcieTbt, Sdxc, etc.) * - Evaluates OSDW (macOS detection flag) * - Walks namespace for _DSM methods using Apple's DTGP UUID * - Extracts device properties and exposes via sysctl */ #include "apple_fw.h" #include <contrib/dev/acpica/source/include/acnamesp.h> #define _COMPONENT ACPI_OEM ACPI_MODULE_NAME("APPLE_FW") MALLOC_DEFINE(M_APPLEFW, "apple_fw", "Apple EFI firmware driver"); static int apple_fw_identify(driver_t *, device_t); static int apple_fw_probe(device_t); static int apple_fw_attach(device_t); static int apple_fw_detach(device_t); static void apple_fw_enumerate_ssdts(device_t); static void apple_fw_eval_osdw(device_t); static void apple_fw_fixup_osys(device_t); static device_method_t apple_fw_methods[] = { DEVMETHOD(device_identify, apple_fw_identify), DEVMETHOD(device_probe, apple_fw_probe), DEVMETHOD(device_attach, apple_fw_attach), DEVMETHOD(device_detach, apple_fw_detach), DEVMETHOD_END }; static driver_t apple_fw_driver = { "apple_fw", apple_fw_methods, sizeof(struct apple_fw_softc), .gpri = KOBJ_GPRI_ACPI }; static devclass_t apple_fw_devclass; DRIVER_MODULE(apple_fw, acpi, apple_fw_driver, apple_fw_devclass, NULL, NULL); MODULE_DEPEND(apple_fw, acpi, 1, 1, 1); MODULE_VERSION(apple_fw, 1); /* * Check if running on Apple EFI firmware by examining the FADT OEM ID. */ static int apple_fw_is_apple(void) { ACPI_TABLE_HEADER hdr; ACPI_STATUS status; status = AcpiGetTableHeader(ACPI_SIG_FADT, 0, &hdr); if (ACPI_FAILURE(status)) return (0); return (strncmp(hdr.OemId, "APPLE ", ACPI_OEM_ID_SIZE) == 0); } /* * Tag value for our synthetic child -- used by probe to distinguish * the device we create from real ACPI-enumerated devices. */ #define DEV_APPLE_FW(x) \ (acpi_get_magic(x) == (uintptr_t)&apple_fw_devclass) static int apple_fw_identify(driver_t *driver, device_t parent) { device_t child; /* Don't re-add on bus rescan */ if (device_find_child(parent, "apple_fw", -1) != NULL) return (0); if (!apple_fw_is_apple()) return (ENXIO); child = BUS_ADD_CHILD(parent, parent, 0, "apple_fw", -1); if (child != NULL) acpi_set_magic(child, (uintptr_t)&apple_fw_devclass); return (0); } static int apple_fw_probe(device_t dev) { char *product, *biosver; char desc[128]; if (acpi_disabled("apple_fw")) return (ENXIO); /* Only match the synthetic child we tagged in identify */ if (!DEV_APPLE_FW(dev)) return (ENXIO); product = kgetenv("smbios.system.product"); biosver = kgetenv("smbios.bios.version"); ksnprintf(desc, sizeof(desc), "Apple %s EFI Firmware %s", product, biosver); device_set_desc_copy(dev, desc); kfreeenv(product); kfreeenv(biosver); return (0); } static int apple_fw_attach(device_t dev) { struct apple_fw_softc *sc = device_get_softc(dev); int darwin_osi = 1; sc->sc_dev = dev; lockinit(&sc->sc_lock, "apple_fw", 0, LK_CANRECURSE); /* Enumerate Apple-specific SSDTs */ apple_fw_enumerate_ssdts(dev); /* * If the Darwin OSI tunable is enabled but the early path * didn't run (module loaded late), fix up OSYS ourselves. * If the tunable is disabled, the user opted out -- skip. */ TUNABLE_INT_FETCH("hw.acpi.apple_darwin_osi", &darwin_osi); apple_fw_eval_osdw(dev); if (darwin_osi && sc->sc_osdw == 0) apple_fw_fixup_osys(dev); /* Discover _DSM device properties via DTGP */ apple_fw_discover_dsm_devices(dev); /* Register sysctl tree */ apple_fw_sysctl_init(dev); return (0); } static int apple_fw_detach(device_t dev) { struct apple_fw_softc *sc = device_get_softc(dev); lockuninit(&sc->sc_lock); return (0); } /* * Fix up OSYS when Darwin OSI was not installed before AcpiLoadTables. * * Install the Darwin interface (affects future _OSI queries), then * re-evaluate PINI which re-checks _OSI and sets OSYS=0x2710. * If PINI is not present, write OSYS directly via the namespace. */ #define APPLE_OSYS_DARWIN 0x2710 static void apple_fw_fixup_osys(device_t dev) { struct apple_fw_softc *sc = device_get_softc(dev); ACPI_STATUS status; /* Install Darwin so future _OSI("Darwin") calls return true */ AcpiInstallInterface("Darwin"); /* Try re-evaluating PINI (Apple's _OSI->OSYS dispatcher) */ status = AcpiEvaluateObject(NULL, "\\PINI", NULL, NULL); if (ACPI_SUCCESS(status)) { apple_fw_eval_osdw(dev); if (sc->sc_osdw != 0) { device_printf(dev, "Darwin OSI fixup via PINI\n"); return; } } /* PINI missing or didn't help -- write OSYS directly */ { ACPI_HANDLE handle; ACPI_NAMESPACE_NODE *node; ACPI_OPERAND_OBJECT *obj; status = AcpiGetHandle(NULL, "\\OSYS", &handle); if (ACPI_FAILURE(status)) { device_printf(dev, "Darwin OSI fixup failed: no \\OSYS\n"); return; } node = AcpiNsValidateHandle(handle); if (node == NULL) return; obj = AcpiNsGetAttachedObject(node); if (obj != NULL && obj->Common.Type == ACPI_TYPE_INTEGER) { obj->Integer.Value = APPLE_OSYS_DARWIN; apple_fw_eval_osdw(dev); device_printf(dev, "Darwin OSI fixup via OSYS poke\n"); } } } /* * Enumerate all SSDT tables with OEM ID "APPLE". */ static void apple_fw_enumerate_ssdts(device_t dev) { struct apple_fw_softc *sc = device_get_softc(dev); ACPI_TABLE_HEADER *table; ACPI_STATUS status; uint32_t instance; int n; sc->sc_ssdt_count = 0; for (instance = 1; instance <= 128; instance++) { status = AcpiGetTable(ACPI_SIG_SSDT, instance, &table); if (ACPI_FAILURE(status)) break; /* Only catalog Apple SSDTs */ if (strncmp(table->OemId, "APPLE ", ACPI_OEM_ID_SIZE) != 0) continue; n = sc->sc_ssdt_count; if (n >= APPLE_FW_MAX_SSDTS) break; memcpy(sc->sc_ssdts[n].oem_table_id, table->OemTableId, ACPI_OEM_TABLE_ID_SIZE); sc->sc_ssdts[n].oem_table_id[ACPI_OEM_TABLE_ID_SIZE] = '\0'; sc->sc_ssdt_count++; } } /* * Evaluate the OSDW method. Returns 1 if the firmware thinks macOS is * running (OSYS == 0x2710), 0 otherwise. */ static void apple_fw_eval_osdw(device_t dev) { struct apple_fw_softc *sc = device_get_softc(dev); UINT32 val; sc->sc_osdw = 0; if (ACPI_SUCCESS(acpi_GetInteger(NULL, "OSDW", &val))) sc->sc_osdw = (int)val; } |