1*bc498427SJohn Baldwin /*-
2*bc498427SJohn Baldwin * SPDX-License-Identifier: BSD-2-Clause
3*bc498427SJohn Baldwin *
4*bc498427SJohn Baldwin * Copyright (c) 2025-2026 Netflix, Inc.
5*bc498427SJohn Baldwin * Written by: John Baldwin <jhb@FreeBSD.org>
6*bc498427SJohn Baldwin */
7*bc498427SJohn Baldwin
8*bc498427SJohn Baldwin #include <sys/param.h>
9*bc498427SJohn Baldwin #include <sys/bus.h>
10*bc498427SJohn Baldwin #include <sys/kernel.h>
11*bc498427SJohn Baldwin #include <sys/malloc.h>
12*bc498427SJohn Baldwin #include <sys/module.h>
13*bc498427SJohn Baldwin #include <sys/rman.h>
14*bc498427SJohn Baldwin
15*bc498427SJohn Baldwin #include <vm/vm.h>
16*bc498427SJohn Baldwin #include <vm/pmap.h>
17*bc498427SJohn Baldwin
18*bc498427SJohn Baldwin #include <contrib/dev/acpica/include/acpi.h>
19*bc498427SJohn Baldwin #include <contrib/dev/acpica/include/accommon.h>
20*bc498427SJohn Baldwin #include <contrib/dev/acpica/include/actables.h>
21*bc498427SJohn Baldwin
22*bc498427SJohn Baldwin #include <dev/acpica/acpiio.h>
23*bc498427SJohn Baldwin #include <dev/acpica/acpivar.h>
24*bc498427SJohn Baldwin #include <dev/acpica/apeivar.h>
25*bc498427SJohn Baldwin
26*bc498427SJohn Baldwin #define ACPI_EINJ_MAX_ACTION (ACPI_EINJV2_GET_ERROR_TYPE)
27*bc498427SJohn Baldwin
28*bc498427SJohn Baldwin struct einj_instruction {
29*bc498427SJohn Baldwin struct resource_map *res;
30*bc498427SJohn Baldwin u_int instruction;
31*bc498427SJohn Baldwin u_int flags;
32*bc498427SJohn Baldwin u_int size;
33*bc498427SJohn Baldwin uint64_t value;
34*bc498427SJohn Baldwin uint64_t mask;
35*bc498427SJohn Baldwin };
36*bc498427SJohn Baldwin
37*bc498427SJohn Baldwin struct einj_action {
38*bc498427SJohn Baldwin struct einj_instruction *instructions;
39*bc498427SJohn Baldwin u_int num_instructions;
40*bc498427SJohn Baldwin };
41*bc498427SJohn Baldwin
42*bc498427SJohn Baldwin struct einj_softc {
43*bc498427SJohn Baldwin device_t dev;
44*bc498427SJohn Baldwin ACPI_TABLE_EINJ *einj;
45*bc498427SJohn Baldwin struct einj_action actions[ACPI_EINJ_MAX_ACTION + 1];
46*bc498427SJohn Baldwin
47*bc498427SJohn Baldwin struct acpi_einj_info info;
48*bc498427SJohn Baldwin struct resource_map *address_res;
49*bc498427SJohn Baldwin struct resource_map *vendor_res;
50*bc498427SJohn Baldwin
51*bc498427SJohn Baldwin struct sx lock;
52*bc498427SJohn Baldwin };
53*bc498427SJohn Baldwin
54*bc498427SJohn Baldwin static MALLOC_DEFINE(M_EINJ, "einj", "ACPI error injection");
55*bc498427SJohn Baldwin
56*bc498427SJohn Baldwin static bool
einj_validate_instruction(u_int i,ACPI_EINJ_ENTRY * e)57*bc498427SJohn Baldwin einj_validate_instruction(u_int i, ACPI_EINJ_ENTRY *e)
58*bc498427SJohn Baldwin {
59*bc498427SJohn Baldwin ACPI_GENERIC_ADDRESS *gas;
60*bc498427SJohn Baldwin UINT8 valid_flags;
61*bc498427SJohn Baldwin
62*bc498427SJohn Baldwin valid_flags = 0;
63*bc498427SJohn Baldwin switch (e->WheaHeader.Instruction) {
64*bc498427SJohn Baldwin case ACPI_EINJ_WRITE_REGISTER:
65*bc498427SJohn Baldwin case ACPI_EINJ_WRITE_REGISTER_VALUE:
66*bc498427SJohn Baldwin valid_flags |= ACPI_EINJ_PRESERVE;
67*bc498427SJohn Baldwin break;
68*bc498427SJohn Baldwin case ACPI_EINJ_READ_REGISTER:
69*bc498427SJohn Baldwin case ACPI_EINJ_READ_REGISTER_VALUE:
70*bc498427SJohn Baldwin /* PRESERVE_REGISTER is ignored, but not invalid. */
71*bc498427SJohn Baldwin valid_flags |= ACPI_EINJ_PRESERVE;
72*bc498427SJohn Baldwin break;
73*bc498427SJohn Baldwin case ACPI_EINJ_NOOP:
74*bc498427SJohn Baldwin #if 0
75*bc498427SJohn Baldwin /* Not documented. */
76*bc498427SJohn Baldwin case ACPI_EINJ_FLUSH_CACHELINE:
77*bc498427SJohn Baldwin #endif
78*bc498427SJohn Baldwin break;
79*bc498427SJohn Baldwin default:
80*bc498427SJohn Baldwin if (bootverbose)
81*bc498427SJohn Baldwin printf("EINJ: Unknown instruction at index %u\n", i);
82*bc498427SJohn Baldwin return (false);
83*bc498427SJohn Baldwin }
84*bc498427SJohn Baldwin if ((e->WheaHeader.Flags & ~valid_flags) != 0) {
85*bc498427SJohn Baldwin if (bootverbose)
86*bc498427SJohn Baldwin printf("EINJ: Invalid instruction flag at index %u\n",
87*bc498427SJohn Baldwin i);
88*bc498427SJohn Baldwin return (false);
89*bc498427SJohn Baldwin }
90*bc498427SJohn Baldwin
91*bc498427SJohn Baldwin gas = &e->WheaHeader.RegisterRegion;
92*bc498427SJohn Baldwin switch (gas->SpaceId) {
93*bc498427SJohn Baldwin case ACPI_ADR_SPACE_SYSTEM_MEMORY:
94*bc498427SJohn Baldwin case ACPI_ADR_SPACE_SYSTEM_IO:
95*bc498427SJohn Baldwin break;
96*bc498427SJohn Baldwin default:
97*bc498427SJohn Baldwin if (bootverbose)
98*bc498427SJohn Baldwin printf("EINJ: Unsupported register address space at index %u\n",
99*bc498427SJohn Baldwin i);
100*bc498427SJohn Baldwin return (false);
101*bc498427SJohn Baldwin }
102*bc498427SJohn Baldwin
103*bc498427SJohn Baldwin /*
104*bc498427SJohn Baldwin * The spec seems to suggest sub-bit ranges of registers might
105*bc498427SJohn Baldwin * be valid, but punt on handling those until one is actually
106*bc498427SJohn Baldwin * encountered in the wild.
107*bc498427SJohn Baldwin */
108*bc498427SJohn Baldwin if (gas->BitOffset != 0) {
109*bc498427SJohn Baldwin if (bootverbose)
110*bc498427SJohn Baldwin printf("EINJ: Unsupported register offset at index %u\n",
111*bc498427SJohn Baldwin i);
112*bc498427SJohn Baldwin return (false);
113*bc498427SJohn Baldwin }
114*bc498427SJohn Baldwin if (!(gas->BitWidth == 8 || gas->BitWidth == 16 ||
115*bc498427SJohn Baldwin gas->BitWidth == 32 || gas->BitWidth == 64)) {
116*bc498427SJohn Baldwin if (bootverbose)
117*bc498427SJohn Baldwin printf("EINJ: Unsupported register width at index %u\n",
118*bc498427SJohn Baldwin i);
119*bc498427SJohn Baldwin return (false);
120*bc498427SJohn Baldwin }
121*bc498427SJohn Baldwin return (true);
122*bc498427SJohn Baldwin }
123*bc498427SJohn Baldwin
124*bc498427SJohn Baldwin static bool
einj_validate_table(ACPI_TABLE_EINJ * einj)125*bc498427SJohn Baldwin einj_validate_table(ACPI_TABLE_EINJ *einj)
126*bc498427SJohn Baldwin {
127*bc498427SJohn Baldwin ACPI_EINJ_ENTRY *e;
128*bc498427SJohn Baldwin u_int i;
129*bc498427SJohn Baldwin bool seen_set_error_type_with_address;
130*bc498427SJohn Baldwin
131*bc498427SJohn Baldwin if (einj->Header.Revision < 1 || einj->Header.Revision > 2) {
132*bc498427SJohn Baldwin if (bootverbose)
133*bc498427SJohn Baldwin printf("EINJ: Unsupported revision %u\n",
134*bc498427SJohn Baldwin einj->Header.Revision);
135*bc498427SJohn Baldwin return (false);
136*bc498427SJohn Baldwin }
137*bc498427SJohn Baldwin if (einj->HeaderLength != sizeof(*einj)) {
138*bc498427SJohn Baldwin if (bootverbose)
139*bc498427SJohn Baldwin printf("EINJ: Invalid Injection Interface Header Length\n");
140*bc498427SJohn Baldwin return (false);
141*bc498427SJohn Baldwin }
142*bc498427SJohn Baldwin if (einj->Entries == 0) {
143*bc498427SJohn Baldwin if (bootverbose)
144*bc498427SJohn Baldwin printf("EINJ: No actions\n");
145*bc498427SJohn Baldwin return (false);
146*bc498427SJohn Baldwin }
147*bc498427SJohn Baldwin if (einj->Header.Length != sizeof(*einj) + einj->Entries * sizeof(*e)) {
148*bc498427SJohn Baldwin if (bootverbose)
149*bc498427SJohn Baldwin printf("EINJ: Invalid table length\n");
150*bc498427SJohn Baldwin return (false);
151*bc498427SJohn Baldwin }
152*bc498427SJohn Baldwin
153*bc498427SJohn Baldwin e = (ACPI_EINJ_ENTRY *)(einj + 1);
154*bc498427SJohn Baldwin seen_set_error_type_with_address = false;
155*bc498427SJohn Baldwin for (i = 0; i < einj->Entries; i++, e++) {
156*bc498427SJohn Baldwin if (e->WheaHeader.Action > ACPI_EINJ_MAX_ACTION) {
157*bc498427SJohn Baldwin if (bootverbose)
158*bc498427SJohn Baldwin printf("EINJ: Invalid action at index %u\n", i);
159*bc498427SJohn Baldwin return (false);
160*bc498427SJohn Baldwin }
161*bc498427SJohn Baldwin
162*bc498427SJohn Baldwin if (e->WheaHeader.Action ==
163*bc498427SJohn Baldwin ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS) {
164*bc498427SJohn Baldwin if (seen_set_error_type_with_address) {
165*bc498427SJohn Baldwin if (bootverbose)
166*bc498427SJohn Baldwin printf("EINJ: Multiple SET_ERROR_TYPE_WITH_ADDRESS instructions\n");
167*bc498427SJohn Baldwin return (false);
168*bc498427SJohn Baldwin }
169*bc498427SJohn Baldwin seen_set_error_type_with_address = true;
170*bc498427SJohn Baldwin }
171*bc498427SJohn Baldwin
172*bc498427SJohn Baldwin if (!einj_validate_instruction(i, e))
173*bc498427SJohn Baldwin return (false);
174*bc498427SJohn Baldwin }
175*bc498427SJohn Baldwin return (true);
176*bc498427SJohn Baldwin }
177*bc498427SJohn Baldwin
178*bc498427SJohn Baldwin static uint64_t
einj_read_register(struct einj_instruction * inst)179*bc498427SJohn Baldwin einj_read_register(struct einj_instruction *inst)
180*bc498427SJohn Baldwin {
181*bc498427SJohn Baldwin switch (inst->size) {
182*bc498427SJohn Baldwin case 1:
183*bc498427SJohn Baldwin return (bus_read_1(inst->res, 0));
184*bc498427SJohn Baldwin case 2:
185*bc498427SJohn Baldwin return (bus_read_2(inst->res, 0));
186*bc498427SJohn Baldwin case 4:
187*bc498427SJohn Baldwin return (bus_read_4(inst->res, 0));
188*bc498427SJohn Baldwin case 8:
189*bc498427SJohn Baldwin return (bus_read_8(inst->res, 0));
190*bc498427SJohn Baldwin default:
191*bc498427SJohn Baldwin __assert_unreachable();
192*bc498427SJohn Baldwin }
193*bc498427SJohn Baldwin }
194*bc498427SJohn Baldwin
195*bc498427SJohn Baldwin static void
einj_write_register(struct einj_instruction * inst,uint64_t value)196*bc498427SJohn Baldwin einj_write_register(struct einj_instruction *inst, uint64_t value)
197*bc498427SJohn Baldwin {
198*bc498427SJohn Baldwin switch (inst->size) {
199*bc498427SJohn Baldwin case 1:
200*bc498427SJohn Baldwin bus_write_1(inst->res, 0, value);
201*bc498427SJohn Baldwin break;
202*bc498427SJohn Baldwin case 2:
203*bc498427SJohn Baldwin bus_write_2(inst->res, 0, value);
204*bc498427SJohn Baldwin break;
205*bc498427SJohn Baldwin case 4:
206*bc498427SJohn Baldwin bus_write_4(inst->res, 0, value);
207*bc498427SJohn Baldwin break;
208*bc498427SJohn Baldwin case 8:
209*bc498427SJohn Baldwin bus_write_8(inst->res, 0, value);
210*bc498427SJohn Baldwin break;
211*bc498427SJohn Baldwin default:
212*bc498427SJohn Baldwin __assert_unreachable();
213*bc498427SJohn Baldwin }
214*bc498427SJohn Baldwin }
215*bc498427SJohn Baldwin
216*bc498427SJohn Baldwin static uint64_t
einj_execute_instruction(struct einj_instruction * inst,uint64_t value)217*bc498427SJohn Baldwin einj_execute_instruction(struct einj_instruction *inst, uint64_t value)
218*bc498427SJohn Baldwin {
219*bc498427SJohn Baldwin uint64_t old;
220*bc498427SJohn Baldwin
221*bc498427SJohn Baldwin switch (inst->instruction) {
222*bc498427SJohn Baldwin case ACPI_EINJ_READ_REGISTER:
223*bc498427SJohn Baldwin case ACPI_EINJ_READ_REGISTER_VALUE:
224*bc498427SJohn Baldwin value = einj_read_register(inst);
225*bc498427SJohn Baldwin value &= inst->mask;
226*bc498427SJohn Baldwin if (inst->instruction == ACPI_EINJ_READ_REGISTER_VALUE)
227*bc498427SJohn Baldwin value = (value == inst->value);
228*bc498427SJohn Baldwin break;
229*bc498427SJohn Baldwin case ACPI_EINJ_WRITE_REGISTER_VALUE:
230*bc498427SJohn Baldwin value = inst->value;
231*bc498427SJohn Baldwin /* FALLTHROUGH */
232*bc498427SJohn Baldwin case ACPI_EINJ_WRITE_REGISTER:
233*bc498427SJohn Baldwin value &= inst->mask;
234*bc498427SJohn Baldwin if (inst->flags & ACPI_EINJ_PRESERVE) {
235*bc498427SJohn Baldwin old = einj_read_register(inst);
236*bc498427SJohn Baldwin value |= (old & ~inst->mask);
237*bc498427SJohn Baldwin }
238*bc498427SJohn Baldwin einj_write_register(inst, value);
239*bc498427SJohn Baldwin break;
240*bc498427SJohn Baldwin case ACPI_EINJ_NOOP:
241*bc498427SJohn Baldwin break;
242*bc498427SJohn Baldwin default:
243*bc498427SJohn Baldwin __assert_unreachable();
244*bc498427SJohn Baldwin }
245*bc498427SJohn Baldwin return (value);
246*bc498427SJohn Baldwin }
247*bc498427SJohn Baldwin
248*bc498427SJohn Baldwin static uint64_t
einj_execute_instructions(struct einj_instruction * inst,u_int count,uint64_t value)249*bc498427SJohn Baldwin einj_execute_instructions(struct einj_instruction *inst, u_int count,
250*bc498427SJohn Baldwin uint64_t value)
251*bc498427SJohn Baldwin {
252*bc498427SJohn Baldwin for (u_int i = 0; i < count; i++, inst++) {
253*bc498427SJohn Baldwin value = einj_execute_instruction(inst, value);
254*bc498427SJohn Baldwin }
255*bc498427SJohn Baldwin return (value);
256*bc498427SJohn Baldwin }
257*bc498427SJohn Baldwin
258*bc498427SJohn Baldwin static int
einj_execute_action(struct einj_softc * sc,u_int action,uint64_t * value)259*bc498427SJohn Baldwin einj_execute_action(struct einj_softc *sc, u_int action, uint64_t *value)
260*bc498427SJohn Baldwin {
261*bc498427SJohn Baldwin struct einj_action *ea;
262*bc498427SJohn Baldwin
263*bc498427SJohn Baldwin if (action >= nitems(sc->actions))
264*bc498427SJohn Baldwin return (ENOENT);
265*bc498427SJohn Baldwin ea = &sc->actions[action];
266*bc498427SJohn Baldwin if (ea->num_instructions == 0)
267*bc498427SJohn Baldwin return (ENOENT);
268*bc498427SJohn Baldwin
269*bc498427SJohn Baldwin *value = einj_execute_instructions(ea->instructions,
270*bc498427SJohn Baldwin ea->num_instructions, *value);
271*bc498427SJohn Baldwin return (0);
272*bc498427SJohn Baldwin }
273*bc498427SJohn Baldwin
274*bc498427SJohn Baldwin static int
einj_trigger_error(struct einj_softc * sc,vm_paddr_t table_pa)275*bc498427SJohn Baldwin einj_trigger_error(struct einj_softc *sc, vm_paddr_t table_pa)
276*bc498427SJohn Baldwin {
277*bc498427SJohn Baldwin ACPI_EINJ_TRIGGER *table;
278*bc498427SJohn Baldwin ACPI_EINJ_ENTRY *e;
279*bc498427SJohn Baldwin size_t table_len;
280*bc498427SJohn Baldwin uint64_t value;
281*bc498427SJohn Baldwin int error;
282*bc498427SJohn Baldwin
283*bc498427SJohn Baldwin /* Map the header to obtain the full length. */
284*bc498427SJohn Baldwin table_len = sizeof(*table);
285*bc498427SJohn Baldwin table = pmap_mapbios(table_pa, table_len);
286*bc498427SJohn Baldwin if (table == NULL) {
287*bc498427SJohn Baldwin device_printf(sc->dev, "failed to map trigger table header\n");
288*bc498427SJohn Baldwin return (ENXIO);
289*bc498427SJohn Baldwin }
290*bc498427SJohn Baldwin
291*bc498427SJohn Baldwin if (table->HeaderSize != sizeof(*table)) {
292*bc498427SJohn Baldwin device_printf(sc->dev, "invalid trigger table header size %u\n",
293*bc498427SJohn Baldwin table->HeaderSize);
294*bc498427SJohn Baldwin error = ENXIO;
295*bc498427SJohn Baldwin goto out;
296*bc498427SJohn Baldwin }
297*bc498427SJohn Baldwin
298*bc498427SJohn Baldwin if (table->TableSize <
299*bc498427SJohn Baldwin table->HeaderSize + sizeof(*e) * table->EntryCount) {
300*bc498427SJohn Baldwin device_printf(sc->dev,
301*bc498427SJohn Baldwin "trigger table too small (%u) for %u entries\n",
302*bc498427SJohn Baldwin table->TableSize, table->EntryCount);
303*bc498427SJohn Baldwin error = ENXIO;
304*bc498427SJohn Baldwin goto out;
305*bc498427SJohn Baldwin }
306*bc498427SJohn Baldwin
307*bc498427SJohn Baldwin if (table->EntryCount == 0) {
308*bc498427SJohn Baldwin error = 0;
309*bc498427SJohn Baldwin goto out;
310*bc498427SJohn Baldwin }
311*bc498427SJohn Baldwin
312*bc498427SJohn Baldwin /* Map the full table. */
313*bc498427SJohn Baldwin pmap_unmapbios(table, table_len);
314*bc498427SJohn Baldwin table_len = table->TableSize;
315*bc498427SJohn Baldwin table = pmap_mapbios(table_pa, table_len);
316*bc498427SJohn Baldwin if (table == NULL) {
317*bc498427SJohn Baldwin device_printf(sc->dev, "failed to map trigger table\n");
318*bc498427SJohn Baldwin return (ENXIO);
319*bc498427SJohn Baldwin }
320*bc498427SJohn Baldwin
321*bc498427SJohn Baldwin value = 0;
322*bc498427SJohn Baldwin e = (ACPI_EINJ_ENTRY *)(table + 1);
323*bc498427SJohn Baldwin for (u_int i = 0; i < table->EntryCount; i++, e++) {
324*bc498427SJohn Baldwin struct einj_instruction ei;
325*bc498427SJohn Baldwin
326*bc498427SJohn Baldwin if (e->WheaHeader.Action != ACPI_EINJ_TRIGGER_ERROR)
327*bc498427SJohn Baldwin device_printf(sc->dev,
328*bc498427SJohn Baldwin "invalid action %#x for trigger instruction %u\n",
329*bc498427SJohn Baldwin e->WheaHeader.Action, i);
330*bc498427SJohn Baldwin
331*bc498427SJohn Baldwin if (e->WheaHeader.Instruction == ACPI_EINJ_NOOP)
332*bc498427SJohn Baldwin continue;
333*bc498427SJohn Baldwin
334*bc498427SJohn Baldwin ei.res = apei_map_register(sc->dev,
335*bc498427SJohn Baldwin &e->WheaHeader.RegisterRegion);
336*bc498427SJohn Baldwin if (ei.res == NULL) {
337*bc498427SJohn Baldwin device_printf(sc->dev,
338*bc498427SJohn Baldwin "failed to map register for trigger entry %u\n", i);
339*bc498427SJohn Baldwin error = ENXIO;
340*bc498427SJohn Baldwin goto out;
341*bc498427SJohn Baldwin }
342*bc498427SJohn Baldwin
343*bc498427SJohn Baldwin ei.instruction = e->WheaHeader.Instruction;
344*bc498427SJohn Baldwin ei.flags = e->WheaHeader.Flags;
345*bc498427SJohn Baldwin ei.value = e->WheaHeader.Value;
346*bc498427SJohn Baldwin ei.mask = e->WheaHeader.Mask;
347*bc498427SJohn Baldwin ei.size = e->WheaHeader.RegisterRegion.BitWidth / 8;
348*bc498427SJohn Baldwin value = einj_execute_instruction(&ei, value);
349*bc498427SJohn Baldwin apei_unmap_register(sc->dev, ei.res);
350*bc498427SJohn Baldwin }
351*bc498427SJohn Baldwin error = 0;
352*bc498427SJohn Baldwin out:
353*bc498427SJohn Baldwin pmap_unmapbios(table, table_len);
354*bc498427SJohn Baldwin return (error);
355*bc498427SJohn Baldwin }
356*bc498427SJohn Baldwin
357*bc498427SJohn Baldwin static int
einj_set_error(struct einj_softc * sc,const struct acpi_einj_error * err)358*bc498427SJohn Baldwin einj_set_error(struct einj_softc *sc, const struct acpi_einj_error *err)
359*bc498427SJohn Baldwin {
360*bc498427SJohn Baldwin uint64_t value;
361*bc498427SJohn Baldwin int error;
362*bc498427SJohn Baldwin
363*bc498427SJohn Baldwin if ((err->address_flags & ~(ACPI_EINJ_APICID_VALID |
364*bc498427SJohn Baldwin ACPI_EINJ_MEMADDRESS_VALID | ACPI_EINJ_PCIE_VALID)) != 0)
365*bc498427SJohn Baldwin return (EINVAL);
366*bc498427SJohn Baldwin if (err->address_flags != 0 && sc->address_res == NULL)
367*bc498427SJohn Baldwin return (EOPNOTSUPP);
368*bc498427SJohn Baldwin
369*bc498427SJohn Baldwin value = 0;
370*bc498427SJohn Baldwin error = einj_execute_action(sc, ACPI_EINJ_BEGIN_OPERATION, &value);
371*bc498427SJohn Baldwin if (error != 0) {
372*bc498427SJohn Baldwin device_printf(sc->dev, "BEGIN_OPERATION failed\n");
373*bc498427SJohn Baldwin return (error);
374*bc498427SJohn Baldwin }
375*bc498427SJohn Baldwin
376*bc498427SJohn Baldwin value = err->error_type;
377*bc498427SJohn Baldwin if (sc->address_res != NULL) {
378*bc498427SJohn Baldwin error = einj_execute_action(sc,
379*bc498427SJohn Baldwin ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS, &value);
380*bc498427SJohn Baldwin if (error != 0) {
381*bc498427SJohn Baldwin device_printf(sc->dev,
382*bc498427SJohn Baldwin "SET_ERROR_TYPE_WITH_ADDRESS failed\n");
383*bc498427SJohn Baldwin goto out;
384*bc498427SJohn Baldwin }
385*bc498427SJohn Baldwin
386*bc498427SJohn Baldwin bus_write_4(sc->address_res,
387*bc498427SJohn Baldwin offsetof(ACPI_EINJ_ERROR_TYPE_WITH_ADDR, Flags),
388*bc498427SJohn Baldwin err->address_flags);
389*bc498427SJohn Baldwin bus_write_4(sc->address_res,
390*bc498427SJohn Baldwin offsetof(ACPI_EINJ_ERROR_TYPE_WITH_ADDR, ApicId),
391*bc498427SJohn Baldwin err->apic_id);
392*bc498427SJohn Baldwin bus_write_8(sc->address_res,
393*bc498427SJohn Baldwin offsetof(ACPI_EINJ_ERROR_TYPE_WITH_ADDR, Address),
394*bc498427SJohn Baldwin err->memory_address);
395*bc498427SJohn Baldwin bus_write_8(sc->address_res,
396*bc498427SJohn Baldwin offsetof(ACPI_EINJ_ERROR_TYPE_WITH_ADDR, Range),
397*bc498427SJohn Baldwin err->memory_range);
398*bc498427SJohn Baldwin bus_write_4(sc->address_res,
399*bc498427SJohn Baldwin offsetof(ACPI_EINJ_ERROR_TYPE_WITH_ADDR, PcieId),
400*bc498427SJohn Baldwin err->pcie_id);
401*bc498427SJohn Baldwin } else {
402*bc498427SJohn Baldwin error = einj_execute_action(sc, ACPI_EINJ_SET_ERROR_TYPE,
403*bc498427SJohn Baldwin &value);
404*bc498427SJohn Baldwin if (error != 0) {
405*bc498427SJohn Baldwin device_printf(sc->dev, "SET_ERROR_TYPE failed\n");
406*bc498427SJohn Baldwin goto out;
407*bc498427SJohn Baldwin }
408*bc498427SJohn Baldwin }
409*bc498427SJohn Baldwin
410*bc498427SJohn Baldwin value = 0;
411*bc498427SJohn Baldwin error = einj_execute_action(sc, ACPI_EINJ_EXECUTE_OPERATION, &value);
412*bc498427SJohn Baldwin if (error != 0) {
413*bc498427SJohn Baldwin device_printf(sc->dev, "EXECUTE_OPERATION failed\n");
414*bc498427SJohn Baldwin goto out;
415*bc498427SJohn Baldwin }
416*bc498427SJohn Baldwin
417*bc498427SJohn Baldwin for (;;) {
418*bc498427SJohn Baldwin value = 0;
419*bc498427SJohn Baldwin error = einj_execute_action(sc, ACPI_EINJ_CHECK_BUSY_STATUS,
420*bc498427SJohn Baldwin &value);
421*bc498427SJohn Baldwin if (error != 0) {
422*bc498427SJohn Baldwin device_printf(sc->dev, "CHECK_BUSY_STATUS failed\n");
423*bc498427SJohn Baldwin goto out;
424*bc498427SJohn Baldwin }
425*bc498427SJohn Baldwin
426*bc498427SJohn Baldwin if ((value & 1) == 0)
427*bc498427SJohn Baldwin break;
428*bc498427SJohn Baldwin DELAY(1000);
429*bc498427SJohn Baldwin }
430*bc498427SJohn Baldwin
431*bc498427SJohn Baldwin value = 0;
432*bc498427SJohn Baldwin error = einj_execute_action(sc, ACPI_EINJ_GET_COMMAND_STATUS, &value);
433*bc498427SJohn Baldwin if (error != 0) {
434*bc498427SJohn Baldwin device_printf(sc->dev, "GET_COMMAND_STATUS failed\n");
435*bc498427SJohn Baldwin goto out;
436*bc498427SJohn Baldwin }
437*bc498427SJohn Baldwin
438*bc498427SJohn Baldwin /* Bits 1:8 of the returned value contain the status code. */
439*bc498427SJohn Baldwin switch ((value >> 1) & 0xff) {
440*bc498427SJohn Baldwin case ACPI_EINJ_SUCCESS:
441*bc498427SJohn Baldwin break;
442*bc498427SJohn Baldwin case ACPI_EINJ_FAILURE:
443*bc498427SJohn Baldwin device_printf(sc->dev, "injection of error type %#x failed\n",
444*bc498427SJohn Baldwin err->error_type);
445*bc498427SJohn Baldwin error = ENXIO;
446*bc498427SJohn Baldwin goto out;
447*bc498427SJohn Baldwin case ACPI_EINJ_INVALID_ACCESS:
448*bc498427SJohn Baldwin device_printf(sc->dev,
449*bc498427SJohn Baldwin "invalid access while injecting error type %#x\n",
450*bc498427SJohn Baldwin err->error_type);
451*bc498427SJohn Baldwin error = ENXIO;
452*bc498427SJohn Baldwin goto out;
453*bc498427SJohn Baldwin default:
454*bc498427SJohn Baldwin device_printf(sc->dev,
455*bc498427SJohn Baldwin "unknown status %ju while injecting error type %#x\n",
456*bc498427SJohn Baldwin (uintmax_t)value, err->error_type);
457*bc498427SJohn Baldwin error = ENXIO;
458*bc498427SJohn Baldwin goto out;
459*bc498427SJohn Baldwin }
460*bc498427SJohn Baldwin
461*bc498427SJohn Baldwin value = 0;
462*bc498427SJohn Baldwin error = einj_execute_action(sc, ACPI_EINJ_GET_TRIGGER_TABLE, &value);
463*bc498427SJohn Baldwin if (error != 0) {
464*bc498427SJohn Baldwin device_printf(sc->dev, "GET_TRIGER_TABLE failed\n");
465*bc498427SJohn Baldwin goto out;
466*bc498427SJohn Baldwin }
467*bc498427SJohn Baldwin
468*bc498427SJohn Baldwin error = einj_trigger_error(sc, value);
469*bc498427SJohn Baldwin out:
470*bc498427SJohn Baldwin value = 0;
471*bc498427SJohn Baldwin (void)einj_execute_action(sc, ACPI_EINJ_END_OPERATION, &value);
472*bc498427SJohn Baldwin return (error);
473*bc498427SJohn Baldwin }
474*bc498427SJohn Baldwin
475*bc498427SJohn Baldwin static int
einj_ioctl(u_long cmd,caddr_t addr,void * arg)476*bc498427SJohn Baldwin einj_ioctl(u_long cmd, caddr_t addr, void *arg)
477*bc498427SJohn Baldwin {
478*bc498427SJohn Baldwin struct einj_softc *sc = arg;
479*bc498427SJohn Baldwin int error;
480*bc498427SJohn Baldwin
481*bc498427SJohn Baldwin error = 0;
482*bc498427SJohn Baldwin switch (cmd) {
483*bc498427SJohn Baldwin case ACPIIO_EINJ_GET_INFO:
484*bc498427SJohn Baldwin memcpy(addr, &sc->info, sizeof(sc->info));
485*bc498427SJohn Baldwin break;
486*bc498427SJohn Baldwin case ACPIIO_EINJ_GET_VENDOR: {
487*bc498427SJohn Baldwin struct acpi_einj_vendor_info *v = (void *)addr;
488*bc498427SJohn Baldwin void *buf;
489*bc498427SJohn Baldwin
490*bc498427SJohn Baldwin if (sc->vendor_res == NULL)
491*bc498427SJohn Baldwin return (ENXIO);
492*bc498427SJohn Baldwin if (v->len != sc->info.vendor_length)
493*bc498427SJohn Baldwin return (EINVAL);
494*bc498427SJohn Baldwin
495*bc498427SJohn Baldwin /*
496*bc498427SJohn Baldwin * This assumes the region can be safely read via
497*bc498427SJohn Baldwin * 4-byte reads.
498*bc498427SJohn Baldwin */
499*bc498427SJohn Baldwin buf = malloc(v->len, M_DEVBUF, M_WAITOK);
500*bc498427SJohn Baldwin bus_read_region_4(sc->vendor_res, 0, buf, v->len / 4);
501*bc498427SJohn Baldwin if (v->len % 4 != 0) {
502*bc498427SJohn Baldwin uint32_t offset;
503*bc498427SJohn Baldwin
504*bc498427SJohn Baldwin offset = rounddown2(v->len, 4);
505*bc498427SJohn Baldwin bus_read_region_1(sc->vendor_res, offset,
506*bc498427SJohn Baldwin (char *)buf + offset, v->len % 4);
507*bc498427SJohn Baldwin }
508*bc498427SJohn Baldwin
509*bc498427SJohn Baldwin error = copyout(buf, v->buf, v->len);
510*bc498427SJohn Baldwin free(buf, M_DEVBUF);
511*bc498427SJohn Baldwin break;
512*bc498427SJohn Baldwin }
513*bc498427SJohn Baldwin case ACPIIO_EINJ_SET_ERROR:
514*bc498427SJohn Baldwin sx_xlock(&sc->lock);
515*bc498427SJohn Baldwin error = einj_set_error(sc, (struct acpi_einj_error *)addr);
516*bc498427SJohn Baldwin sx_xunlock(&sc->lock);
517*bc498427SJohn Baldwin break;
518*bc498427SJohn Baldwin default:
519*bc498427SJohn Baldwin error = ENOIOCTL;
520*bc498427SJohn Baldwin break;
521*bc498427SJohn Baldwin }
522*bc498427SJohn Baldwin return (error);
523*bc498427SJohn Baldwin }
524*bc498427SJohn Baldwin
525*bc498427SJohn Baldwin static void
einj_identify(driver_t * driver,device_t parent)526*bc498427SJohn Baldwin einj_identify(driver_t *driver, device_t parent)
527*bc498427SJohn Baldwin {
528*bc498427SJohn Baldwin ACPI_TABLE_HEADER *einj;
529*bc498427SJohn Baldwin ACPI_STATUS status;
530*bc498427SJohn Baldwin bool valid;
531*bc498427SJohn Baldwin
532*bc498427SJohn Baldwin if (device_find_child(parent, "einj", DEVICE_UNIT_ANY) != NULL)
533*bc498427SJohn Baldwin return;
534*bc498427SJohn Baldwin
535*bc498427SJohn Baldwin status = AcpiGetTable(ACPI_SIG_EINJ, 0, &einj);
536*bc498427SJohn Baldwin if (ACPI_FAILURE(status))
537*bc498427SJohn Baldwin return;
538*bc498427SJohn Baldwin valid = einj_validate_table((ACPI_TABLE_EINJ *)einj);
539*bc498427SJohn Baldwin AcpiPutTable(einj);
540*bc498427SJohn Baldwin if (!valid)
541*bc498427SJohn Baldwin return;
542*bc498427SJohn Baldwin
543*bc498427SJohn Baldwin BUS_ADD_CHILD(parent, 1, "einj", DEVICE_UNIT_ANY);
544*bc498427SJohn Baldwin }
545*bc498427SJohn Baldwin
546*bc498427SJohn Baldwin static bool
einj_parse_table(struct einj_softc * sc,ACPI_GENERIC_ADDRESS * address_gas)547*bc498427SJohn Baldwin einj_parse_table(struct einj_softc *sc, ACPI_GENERIC_ADDRESS *address_gas)
548*bc498427SJohn Baldwin {
549*bc498427SJohn Baldwin ACPI_EINJ_ENTRY *e;
550*bc498427SJohn Baldwin struct resource_map *res;
551*bc498427SJohn Baldwin u_int i;
552*bc498427SJohn Baldwin
553*bc498427SJohn Baldwin e = (ACPI_EINJ_ENTRY *)(sc->einj + 1);
554*bc498427SJohn Baldwin for (i = 0; i < sc->einj->Entries; i++, e++) {
555*bc498427SJohn Baldwin struct einj_action *ea = &sc->actions[e->WheaHeader.Action];
556*bc498427SJohn Baldwin struct einj_instruction *ei;
557*bc498427SJohn Baldwin
558*bc498427SJohn Baldwin switch (e->WheaHeader.Instruction) {
559*bc498427SJohn Baldwin case ACPI_EINJ_NOOP:
560*bc498427SJohn Baldwin res = NULL;
561*bc498427SJohn Baldwin break;
562*bc498427SJohn Baldwin default:
563*bc498427SJohn Baldwin res = apei_map_register(sc->dev,
564*bc498427SJohn Baldwin &e->WheaHeader.RegisterRegion);
565*bc498427SJohn Baldwin if (res == NULL)
566*bc498427SJohn Baldwin return (false);
567*bc498427SJohn Baldwin }
568*bc498427SJohn Baldwin
569*bc498427SJohn Baldwin if (e->WheaHeader.Action ==
570*bc498427SJohn Baldwin ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS)
571*bc498427SJohn Baldwin *address_gas = e->WheaHeader.RegisterRegion;
572*bc498427SJohn Baldwin
573*bc498427SJohn Baldwin ea->instructions = realloc(ea->instructions,
574*bc498427SJohn Baldwin sizeof(*ea->instructions) * (ea->num_instructions + 1),
575*bc498427SJohn Baldwin M_EINJ, M_WAITOK);
576*bc498427SJohn Baldwin ei = &ea->instructions[ea->num_instructions];
577*bc498427SJohn Baldwin ei->res = res;
578*bc498427SJohn Baldwin ei->instruction = e->WheaHeader.Instruction;
579*bc498427SJohn Baldwin ei->flags = e->WheaHeader.Flags;
580*bc498427SJohn Baldwin ei->value = e->WheaHeader.Value;
581*bc498427SJohn Baldwin ei->mask = e->WheaHeader.Mask;
582*bc498427SJohn Baldwin ei->size = e->WheaHeader.RegisterRegion.BitWidth / 8;
583*bc498427SJohn Baldwin ea->num_instructions++;
584*bc498427SJohn Baldwin }
585*bc498427SJohn Baldwin return (true);
586*bc498427SJohn Baldwin }
587*bc498427SJohn Baldwin
588*bc498427SJohn Baldwin /*
589*bc498427SJohn Baldwin * SET_ERROR_TYPE_WITH_ADDRESS points to a data structure, but the
590*bc498427SJohn Baldwin * original action only points to the first word. Expand the mapping
591*bc498427SJohn Baldwin * to cover the entire structure and initialize the cached copy of the
592*bc498427SJohn Baldwin * structure.
593*bc498427SJohn Baldwin */
594*bc498427SJohn Baldwin static int
einj_parse_set_error_with_address(struct einj_softc * sc,const ACPI_GENERIC_ADDRESS * gas)595*bc498427SJohn Baldwin einj_parse_set_error_with_address(struct einj_softc *sc,
596*bc498427SJohn Baldwin const ACPI_GENERIC_ADDRESS *gas)
597*bc498427SJohn Baldwin {
598*bc498427SJohn Baldwin struct resource_map *res;
599*bc498427SJohn Baldwin struct einj_action *ea;
600*bc498427SJohn Baldwin uint32_t vendor_offset;
601*bc498427SJohn Baldwin
602*bc498427SJohn Baldwin ea = &sc->actions[ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS];
603*bc498427SJohn Baldwin if (ea->num_instructions == 0)
604*bc498427SJohn Baldwin return (0);
605*bc498427SJohn Baldwin
606*bc498427SJohn Baldwin /* Map the SET_ERROR_TYPE_WITH_ADDRESS structure. */
607*bc498427SJohn Baldwin res = apei_map_memory(sc->dev, gas->Address,
608*bc498427SJohn Baldwin sizeof(ACPI_EINJ_ERROR_TYPE_WITH_ADDR));
609*bc498427SJohn Baldwin if (res == NULL) {
610*bc498427SJohn Baldwin device_printf(sc->dev,
611*bc498427SJohn Baldwin "failed to map SET_ERROR_TYPE_WITH_ADDRESS structure\n");
612*bc498427SJohn Baldwin return (ENXIO);
613*bc498427SJohn Baldwin }
614*bc498427SJohn Baldwin sc->address_res = res;
615*bc498427SJohn Baldwin
616*bc498427SJohn Baldwin /* Map the Vendor extension structure if it exists. */
617*bc498427SJohn Baldwin if ((sc->info.error_type & ACPI_EINJ_VENDOR_DEFINED) == 0)
618*bc498427SJohn Baldwin return (0);
619*bc498427SJohn Baldwin
620*bc498427SJohn Baldwin vendor_offset = bus_read_4(res,
621*bc498427SJohn Baldwin offsetof(ACPI_EINJ_ERROR_TYPE_WITH_ADDR, VendorStructOffset));
622*bc498427SJohn Baldwin
623*bc498427SJohn Baldwin if (vendor_offset < sizeof(ACPI_EINJ_ERROR_TYPE_WITH_ADDR)) {
624*bc498427SJohn Baldwin device_printf(sc->dev,
625*bc498427SJohn Baldwin "Vendor Error extension overlaps base structure\n");
626*bc498427SJohn Baldwin return (0);
627*bc498427SJohn Baldwin }
628*bc498427SJohn Baldwin
629*bc498427SJohn Baldwin res = apei_map_memory(sc->dev, gas->Address + vendor_offset,
630*bc498427SJohn Baldwin sizeof(ACPI_EINJ_VENDOR));
631*bc498427SJohn Baldwin if (res == NULL) {
632*bc498427SJohn Baldwin device_printf(sc->dev,
633*bc498427SJohn Baldwin "failed to map Vendor Error extension structure\n");
634*bc498427SJohn Baldwin return (0);
635*bc498427SJohn Baldwin }
636*bc498427SJohn Baldwin
637*bc498427SJohn Baldwin sc->info.vendor_length =
638*bc498427SJohn Baldwin bus_read_4(res, offsetof(ACPI_EINJ_VENDOR, Length));
639*bc498427SJohn Baldwin
640*bc498427SJohn Baldwin if (sc->info.vendor_length < sizeof(ACPI_EINJ_VENDOR)) {
641*bc498427SJohn Baldwin if (bootverbose || sc->info.vendor_length != 0)
642*bc498427SJohn Baldwin device_printf(sc->dev,
643*bc498427SJohn Baldwin "invalid Vendor Error extension structure length\n");
644*bc498427SJohn Baldwin apei_unmap_register(sc->dev, res);
645*bc498427SJohn Baldwin return (0);
646*bc498427SJohn Baldwin }
647*bc498427SJohn Baldwin
648*bc498427SJohn Baldwin if (sc->info.vendor_length > sizeof(ACPI_EINJ_VENDOR)) {
649*bc498427SJohn Baldwin apei_unmap_register(sc->dev, res);
650*bc498427SJohn Baldwin res = apei_map_memory(sc->dev, gas->Address + vendor_offset,
651*bc498427SJohn Baldwin sc->info.vendor_length);
652*bc498427SJohn Baldwin if (res == NULL) {
653*bc498427SJohn Baldwin device_printf(sc->dev,
654*bc498427SJohn Baldwin "failed to map Vendor Error extension structure\n");
655*bc498427SJohn Baldwin return (0);
656*bc498427SJohn Baldwin }
657*bc498427SJohn Baldwin }
658*bc498427SJohn Baldwin sc->vendor_res = res;
659*bc498427SJohn Baldwin
660*bc498427SJohn Baldwin return (0);
661*bc498427SJohn Baldwin }
662*bc498427SJohn Baldwin
663*bc498427SJohn Baldwin static void
einj_cleanup(struct einj_softc * sc)664*bc498427SJohn Baldwin einj_cleanup(struct einj_softc *sc)
665*bc498427SJohn Baldwin {
666*bc498427SJohn Baldwin struct einj_action *ea;
667*bc498427SJohn Baldwin
668*bc498427SJohn Baldwin /* This is idempotent if the handler isn't registered. */
669*bc498427SJohn Baldwin acpi_deregister_ioctls(einj_ioctl);
670*bc498427SJohn Baldwin
671*bc498427SJohn Baldwin ea = sc->actions;
672*bc498427SJohn Baldwin for (u_int i = 0; i < nitems(sc->actions); i++, ea++) {
673*bc498427SJohn Baldwin struct einj_instruction *ei;
674*bc498427SJohn Baldwin
675*bc498427SJohn Baldwin ei = ea->instructions;
676*bc498427SJohn Baldwin for (u_int j = 0; j < ea->num_instructions; j++, ei++) {
677*bc498427SJohn Baldwin if (ei->res != NULL)
678*bc498427SJohn Baldwin apei_unmap_register(sc->dev, ei->res);
679*bc498427SJohn Baldwin }
680*bc498427SJohn Baldwin free(ea->instructions, M_EINJ);
681*bc498427SJohn Baldwin }
682*bc498427SJohn Baldwin
683*bc498427SJohn Baldwin if (sc->address_res)
684*bc498427SJohn Baldwin apei_unmap_register(sc->dev, sc->address_res);
685*bc498427SJohn Baldwin if (sc->vendor_res)
686*bc498427SJohn Baldwin apei_unmap_register(sc->dev, sc->vendor_res);
687*bc498427SJohn Baldwin AcpiPutTable((ACPI_TABLE_HEADER *)sc->einj);
688*bc498427SJohn Baldwin sx_destroy(&sc->lock);
689*bc498427SJohn Baldwin }
690*bc498427SJohn Baldwin
691*bc498427SJohn Baldwin static int
einj_probe(device_t dev)692*bc498427SJohn Baldwin einj_probe(device_t dev)
693*bc498427SJohn Baldwin {
694*bc498427SJohn Baldwin device_set_desc(dev, "ACPI Error Injection Interface");
695*bc498427SJohn Baldwin return (BUS_PROBE_GENERIC);
696*bc498427SJohn Baldwin }
697*bc498427SJohn Baldwin
698*bc498427SJohn Baldwin static int
einj_attach(device_t dev)699*bc498427SJohn Baldwin einj_attach(device_t dev)
700*bc498427SJohn Baldwin {
701*bc498427SJohn Baldwin struct einj_softc *sc = device_get_softc(dev);
702*bc498427SJohn Baldwin ACPI_TABLE_HEADER *hdr;
703*bc498427SJohn Baldwin ACPI_GENERIC_ADDRESS address_gas;
704*bc498427SJohn Baldwin ACPI_STATUS status;
705*bc498427SJohn Baldwin uint64_t value;
706*bc498427SJohn Baldwin int error;
707*bc498427SJohn Baldwin
708*bc498427SJohn Baldwin sc->dev = dev;
709*bc498427SJohn Baldwin status = AcpiGetTable(ACPI_SIG_EINJ, 0, &hdr);
710*bc498427SJohn Baldwin if (ACPI_FAILURE(status)) {
711*bc498427SJohn Baldwin device_printf(dev, "Failed to read " ACPI_SIG_EINJ " table\n");
712*bc498427SJohn Baldwin return (ENXIO);
713*bc498427SJohn Baldwin }
714*bc498427SJohn Baldwin sx_init(&sc->lock, "einj");
715*bc498427SJohn Baldwin sc->einj = (ACPI_TABLE_EINJ *)hdr;
716*bc498427SJohn Baldwin
717*bc498427SJohn Baldwin if (!einj_validate_table(sc->einj)) {
718*bc498427SJohn Baldwin device_printf(dev, "Invalid " ACPI_SIG_EINJ " table\n");
719*bc498427SJohn Baldwin goto out;
720*bc498427SJohn Baldwin }
721*bc498427SJohn Baldwin if (!einj_parse_table(sc, &address_gas))
722*bc498427SJohn Baldwin goto out;
723*bc498427SJohn Baldwin
724*bc498427SJohn Baldwin value = 0;
725*bc498427SJohn Baldwin error = einj_execute_action(sc, ACPI_EINJ_GET_ERROR_TYPE, &value);
726*bc498427SJohn Baldwin if (error != 0) {
727*bc498427SJohn Baldwin device_printf(dev, "Failed to fetch supported error types\n");
728*bc498427SJohn Baldwin goto out;
729*bc498427SJohn Baldwin }
730*bc498427SJohn Baldwin sc->info.error_type = value;
731*bc498427SJohn Baldwin
732*bc498427SJohn Baldwin error = einj_parse_set_error_with_address(sc, &address_gas);
733*bc498427SJohn Baldwin if (error != 0)
734*bc498427SJohn Baldwin goto out;
735*bc498427SJohn Baldwin
736*bc498427SJohn Baldwin error = acpi_register_ioctl(ACPIIO_EINJ_GET_INFO, einj_ioctl, sc);
737*bc498427SJohn Baldwin if (error == 0)
738*bc498427SJohn Baldwin error = acpi_register_ioctl(ACPIIO_EINJ_GET_VENDOR, einj_ioctl,
739*bc498427SJohn Baldwin sc);
740*bc498427SJohn Baldwin if (error == 0)
741*bc498427SJohn Baldwin error = acpi_register_ioctl(ACPIIO_EINJ_SET_ERROR, einj_ioctl,
742*bc498427SJohn Baldwin sc);
743*bc498427SJohn Baldwin if (error != 0) {
744*bc498427SJohn Baldwin device_printf(dev, "Failed to register ioctl handler\n");
745*bc498427SJohn Baldwin goto out;
746*bc498427SJohn Baldwin }
747*bc498427SJohn Baldwin return (0);
748*bc498427SJohn Baldwin out:
749*bc498427SJohn Baldwin einj_cleanup(sc);
750*bc498427SJohn Baldwin return (ENXIO);
751*bc498427SJohn Baldwin }
752*bc498427SJohn Baldwin
753*bc498427SJohn Baldwin static int
einj_detach(device_t dev)754*bc498427SJohn Baldwin einj_detach(device_t dev)
755*bc498427SJohn Baldwin {
756*bc498427SJohn Baldwin struct einj_softc *sc = device_get_softc(dev);
757*bc498427SJohn Baldwin
758*bc498427SJohn Baldwin einj_cleanup(sc);
759*bc498427SJohn Baldwin return (0);
760*bc498427SJohn Baldwin }
761*bc498427SJohn Baldwin
762*bc498427SJohn Baldwin static device_method_t einj_methods[] = {
763*bc498427SJohn Baldwin DEVMETHOD(device_identify, einj_identify),
764*bc498427SJohn Baldwin DEVMETHOD(device_probe, einj_probe),
765*bc498427SJohn Baldwin DEVMETHOD(device_attach, einj_attach),
766*bc498427SJohn Baldwin DEVMETHOD(device_detach, einj_detach),
767*bc498427SJohn Baldwin DEVMETHOD_END
768*bc498427SJohn Baldwin };
769*bc498427SJohn Baldwin
770*bc498427SJohn Baldwin static driver_t einj_driver = {
771*bc498427SJohn Baldwin "einj",
772*bc498427SJohn Baldwin einj_methods,
773*bc498427SJohn Baldwin sizeof(struct einj_softc)
774*bc498427SJohn Baldwin };
775*bc498427SJohn Baldwin
776*bc498427SJohn Baldwin DRIVER_MODULE(einj, apei, einj_driver, NULL, NULL);
777*bc498427SJohn Baldwin MODULE_DEPEND(einj, acpi, 1, 1, 1);
778