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