xref: /freebsd/sys/dev/acpica/acpi_einj.c (revision bc49842769bd3657a5a1e7de878c0ce8780a2f86)
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