1 /*- 2 * Copyright (c) 2024 Rubicon Communications, LLC (Netgate) 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 #include "opt_acpi.h" 28 #include <sys/param.h> 29 #include <sys/conf.h> 30 #include <sys/uio.h> 31 #include <sys/proc.h> 32 #include <sys/kernel.h> 33 #include <sys/bus.h> 34 #include <sys/sbuf.h> 35 #include <sys/module.h> 36 #include <sys/sysctl.h> 37 38 #include <contrib/dev/acpica/include/acpi.h> 39 #include <contrib/dev/acpica/include/accommon.h> 40 #include <dev/acpica/acpivar.h> 41 #include "acpi_wmi_if.h" 42 43 #define _COMPONENT ACPI_OEM 44 ACPI_MODULE_NAME("SBL-FW-UPDATE-WMI") 45 ACPI_SERIAL_DECL(sbl_wmi, "SBL WMI device"); 46 47 #define ACPI_SBL_FW_UPDATE_WMI_GUID "44FADEB1-B204-40F2-8581-394BBDC1B651" 48 49 struct acpi_sbl_wmi_softc { 50 device_t dev; 51 device_t wmi_dev; 52 }; 53 54 static void 55 acpi_sbl_wmi_identify(driver_t *driver, device_t parent) 56 { 57 /* Don't do anything if driver is disabled. */ 58 if (acpi_disabled("sbl_wmi")) 59 return; 60 61 /* Add only a single device instance. */ 62 if (device_find_child(parent, "acpi_sbl_wmi", -1) != NULL) 63 return; 64 65 /* Check management GUID to see whether system is compatible. */ 66 if (!ACPI_WMI_PROVIDES_GUID_STRING(parent, 67 ACPI_SBL_FW_UPDATE_WMI_GUID)) 68 return; 69 70 if (BUS_ADD_CHILD(parent, 0, "acpi_sbl_wmi", -1) == NULL) 71 device_printf(parent, "add acpi_sbl_wmi child failed\n"); 72 } 73 74 static int 75 acpi_sbl_wmi_probe(device_t dev) 76 { 77 if (!ACPI_WMI_PROVIDES_GUID_STRING(device_get_parent(dev), 78 ACPI_SBL_FW_UPDATE_WMI_GUID)) 79 return (EINVAL); 80 device_set_desc(dev, "SBL Firmware Update WMI device"); 81 return (0); 82 } 83 84 static int 85 acpi_sbl_wmi_sysctl_get(struct acpi_sbl_wmi_softc *sc, int *val) 86 { 87 ACPI_OBJECT *obj; 88 ACPI_BUFFER out = { ACPI_ALLOCATE_BUFFER, NULL }; 89 int error = 0; 90 91 if (ACPI_FAILURE(ACPI_WMI_GET_BLOCK(sc->wmi_dev, 92 ACPI_SBL_FW_UPDATE_WMI_GUID, 0, &out))) { 93 error = EINVAL; 94 goto out; 95 } 96 97 obj = out.Pointer; 98 if (obj->Type != ACPI_TYPE_INTEGER) { 99 error = EINVAL; 100 goto out; 101 } 102 103 *val = obj->Integer.Value; 104 105 out: 106 if (out.Pointer) 107 AcpiOsFree(out.Pointer); 108 109 return (error); 110 } 111 112 static int 113 acpi_sbl_wmi_sysctl_set(struct acpi_sbl_wmi_softc *sc, int in) 114 { 115 ACPI_BUFFER input = { ACPI_ALLOCATE_BUFFER, NULL }; 116 uint32_t val; 117 118 val = in; 119 input.Length = sizeof(val); 120 input.Pointer = &val; 121 122 if (ACPI_FAILURE(ACPI_WMI_SET_BLOCK(sc->wmi_dev, 123 ACPI_SBL_FW_UPDATE_WMI_GUID, 0, &input))) 124 return (ENODEV); 125 126 return (0); 127 } 128 129 static int 130 acpi_sbl_wmi_fw_upgrade_sysctl(SYSCTL_HANDLER_ARGS) 131 { 132 struct acpi_sbl_wmi_softc *sc; 133 int arg; 134 int error = 0; 135 136 ACPI_SERIAL_BEGIN(sbl_wmi); 137 138 sc = (struct acpi_sbl_wmi_softc *)oidp->oid_arg1; 139 error = acpi_sbl_wmi_sysctl_get(sc, &arg); 140 if (error != 0) 141 goto out; 142 143 error = sysctl_handle_int(oidp, &arg, 0, req); 144 if (! error && req->newptr != NULL) 145 error = acpi_sbl_wmi_sysctl_set(sc, arg); 146 147 out: 148 ACPI_SERIAL_END(sbl_wmi); 149 150 return (error); 151 } 152 153 static int 154 acpi_sbl_wmi_attach(device_t dev) 155 { 156 struct acpi_sbl_wmi_softc *sc; 157 struct sysctl_ctx_list *sysctl_ctx; 158 struct sysctl_oid *sysctl_tree; 159 160 sc = device_get_softc(dev); 161 sc->dev = dev; 162 sc->wmi_dev = device_get_parent(dev); 163 164 sysctl_ctx = device_get_sysctl_ctx(dev); 165 sysctl_tree = device_get_sysctl_tree(dev); 166 167 SYSCTL_ADD_PROC(sysctl_ctx, 168 SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, 169 "firmware_update_request", 170 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, 171 sc, 0, acpi_sbl_wmi_fw_upgrade_sysctl, "I", 172 "Signal SBL that a firmware update is available"); 173 174 return (0); 175 } 176 177 static device_method_t acpi_sbl_wmi_methods[] = { 178 DEVMETHOD(device_identify, acpi_sbl_wmi_identify), 179 DEVMETHOD(device_probe, acpi_sbl_wmi_probe), 180 DEVMETHOD(device_attach, acpi_sbl_wmi_attach), 181 182 DEVMETHOD_END 183 }; 184 185 static driver_t acpi_sbl_wmi_driver = { 186 "acpi_sbl_wmi", 187 acpi_sbl_wmi_methods, 188 sizeof(struct acpi_sbl_wmi_softc), 189 }; 190 191 DRIVER_MODULE(acpi_sbl_wmi, acpi_wmi, acpi_sbl_wmi_driver, 0, 0); 192 MODULE_DEPEND(acpi_sbl_wmi, acpi_wmi, 1, 1, 1); 193 MODULE_DEPEND(acpi_sbl_wmi, acpi, 1, 1, 1); 194