xref: /freebsd/sys/dev/acpica/acpi_apei_bus.c (revision 9313f6b01485ad9a0b7cc59b459f5714533587c3)
1*9313f6b0SJohn Baldwin /*-
2*9313f6b0SJohn Baldwin  * SPDX-License-Identifier: BSD-2-Clause
3*9313f6b0SJohn Baldwin  *
4*9313f6b0SJohn Baldwin  * Copyright (c) 2020 Alexander Motin <mav@FreeBSD.org>
5*9313f6b0SJohn Baldwin  * Copyright (c) 2025-2026 Netflix, Inc.
6*9313f6b0SJohn Baldwin  *
7*9313f6b0SJohn Baldwin  * Redistribution and use in source and binary forms, with or without
8*9313f6b0SJohn Baldwin  * modification, are permitted provided that the following conditions
9*9313f6b0SJohn Baldwin  * are met:
10*9313f6b0SJohn Baldwin  * 1. Redistributions of source code must retain the above copyright
11*9313f6b0SJohn Baldwin  *    notice, this list of conditions and the following disclaimer.
12*9313f6b0SJohn Baldwin  * 2. Redistributions in binary form must reproduce the above copyright
13*9313f6b0SJohn Baldwin  *    notice, this list of conditions and the following disclaimer in the
14*9313f6b0SJohn Baldwin  *    documentation and/or other materials provided with the distribution.
15*9313f6b0SJohn Baldwin  *
16*9313f6b0SJohn Baldwin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17*9313f6b0SJohn Baldwin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*9313f6b0SJohn Baldwin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*9313f6b0SJohn Baldwin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20*9313f6b0SJohn Baldwin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*9313f6b0SJohn Baldwin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22*9313f6b0SJohn Baldwin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23*9313f6b0SJohn Baldwin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*9313f6b0SJohn Baldwin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*9313f6b0SJohn Baldwin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*9313f6b0SJohn Baldwin  * SUCH DAMAGE.
27*9313f6b0SJohn Baldwin  */
28*9313f6b0SJohn Baldwin 
29*9313f6b0SJohn Baldwin #include <sys/types.h>
30*9313f6b0SJohn Baldwin #include <sys/bus.h>
31*9313f6b0SJohn Baldwin #include <sys/kernel.h>
32*9313f6b0SJohn Baldwin #include <sys/malloc.h>
33*9313f6b0SJohn Baldwin #include <sys/module.h>
34*9313f6b0SJohn Baldwin #include <sys/rman.h>
35*9313f6b0SJohn Baldwin 
36*9313f6b0SJohn Baldwin #include <contrib/dev/acpica/include/acpi.h>
37*9313f6b0SJohn Baldwin 
38*9313f6b0SJohn Baldwin #include <dev/acpica/acpivar.h>
39*9313f6b0SJohn Baldwin #include <dev/acpica/apeivar.h>
40*9313f6b0SJohn Baldwin 
41*9313f6b0SJohn Baldwin /*
42*9313f6b0SJohn Baldwin  * Different APEI tables can reuse the same registers (and sometimes
43*9313f6b0SJohn Baldwin  * different views of the same register, e.g. 32- vs 64-bit mappings
44*9313f6b0SJohn Baldwin  * of the same register).  To enable this sharing, apei0 acts as a bus
45*9313f6b0SJohn Baldwin  * device managing a pool of allocated resources and hands out
46*9313f6b0SJohn Baldwin  * mappings to child devices.  Child devices handle individual tables.
47*9313f6b0SJohn Baldwin  */
48*9313f6b0SJohn Baldwin 
49*9313f6b0SJohn Baldwin struct apei_register {
50*9313f6b0SJohn Baldwin 	struct resource *res;
51*9313f6b0SJohn Baldwin 	TAILQ_ENTRY(apei_register) link;
52*9313f6b0SJohn Baldwin };
53*9313f6b0SJohn Baldwin 
54*9313f6b0SJohn Baldwin struct apei_register_mapping {
55*9313f6b0SJohn Baldwin 	struct resource_map map;
56*9313f6b0SJohn Baldwin 	struct apei_register *reg;
57*9313f6b0SJohn Baldwin 	TAILQ_ENTRY(apei_register_mapping) link;
58*9313f6b0SJohn Baldwin };
59*9313f6b0SJohn Baldwin 
60*9313f6b0SJohn Baldwin struct apei_ivars {
61*9313f6b0SJohn Baldwin 	TAILQ_HEAD(, apei_register_mapping) mappings;
62*9313f6b0SJohn Baldwin };
63*9313f6b0SJohn Baldwin 
64*9313f6b0SJohn Baldwin TAILQ_HEAD(apei_register_list, apei_register);
65*9313f6b0SJohn Baldwin 
66*9313f6b0SJohn Baldwin struct apei_softc {
67*9313f6b0SJohn Baldwin 	device_t dev;
68*9313f6b0SJohn Baldwin 	struct apei_register_list mem;
69*9313f6b0SJohn Baldwin 	struct apei_register_list io;
70*9313f6b0SJohn Baldwin };
71*9313f6b0SJohn Baldwin 
72*9313f6b0SJohn Baldwin static char *apei_ids[] = { "PNP0C33", NULL };
73*9313f6b0SJohn Baldwin 
74*9313f6b0SJohn Baldwin static MALLOC_DEFINE(M_APEI, "apei", "ACPI platform error interface");
75*9313f6b0SJohn Baldwin 
76*9313f6b0SJohn Baldwin static ACPI_STATUS
apei_find(ACPI_HANDLE handle,UINT32 level,void * context,void ** status)77*9313f6b0SJohn Baldwin apei_find(ACPI_HANDLE handle, UINT32 level, void *context,
78*9313f6b0SJohn Baldwin     void **status)
79*9313f6b0SJohn Baldwin {
80*9313f6b0SJohn Baldwin 	int *found = (int *)status;
81*9313f6b0SJohn Baldwin 	char **ids;
82*9313f6b0SJohn Baldwin 
83*9313f6b0SJohn Baldwin 	for (ids = apei_ids; *ids != NULL; ids++) {
84*9313f6b0SJohn Baldwin 		if (acpi_MatchHid(handle, *ids)) {
85*9313f6b0SJohn Baldwin 			*found = 1;
86*9313f6b0SJohn Baldwin 			break;
87*9313f6b0SJohn Baldwin 		}
88*9313f6b0SJohn Baldwin 	}
89*9313f6b0SJohn Baldwin 	return (AE_OK);
90*9313f6b0SJohn Baldwin }
91*9313f6b0SJohn Baldwin 
92*9313f6b0SJohn Baldwin static void
apei_identify(driver_t * driver,device_t parent)93*9313f6b0SJohn Baldwin apei_identify(driver_t *driver, device_t parent)
94*9313f6b0SJohn Baldwin {
95*9313f6b0SJohn Baldwin 	device_t	child;
96*9313f6b0SJohn Baldwin 	int		found;
97*9313f6b0SJohn Baldwin 
98*9313f6b0SJohn Baldwin 	if (acpi_disabled("apei"))
99*9313f6b0SJohn Baldwin 		return;
100*9313f6b0SJohn Baldwin 
101*9313f6b0SJohn Baldwin 	/* Only one APEI device can exist. */
102*9313f6b0SJohn Baldwin 	if (device_find_child(parent, "apei", DEVICE_UNIT_ANY) != NULL)
103*9313f6b0SJohn Baldwin 		return;
104*9313f6b0SJohn Baldwin 
105*9313f6b0SJohn Baldwin 	/* Search for ACPI error device to be used. */
106*9313f6b0SJohn Baldwin 	found = 0;
107*9313f6b0SJohn Baldwin 	AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
108*9313f6b0SJohn Baldwin 	    100, apei_find, NULL, NULL, (void *)&found);
109*9313f6b0SJohn Baldwin 	if (found)
110*9313f6b0SJohn Baldwin 		return;
111*9313f6b0SJohn Baldwin 
112*9313f6b0SJohn Baldwin 	/* If not found - create a fake one. */
113*9313f6b0SJohn Baldwin 	child = BUS_ADD_CHILD(parent, 2, "apei", 0);
114*9313f6b0SJohn Baldwin 	if (child == NULL)
115*9313f6b0SJohn Baldwin 		printf("%s: can't add child\n", __func__);
116*9313f6b0SJohn Baldwin }
117*9313f6b0SJohn Baldwin 
118*9313f6b0SJohn Baldwin static int
apei_probe(device_t dev)119*9313f6b0SJohn Baldwin apei_probe(device_t dev)
120*9313f6b0SJohn Baldwin {
121*9313f6b0SJohn Baldwin 	int rv;
122*9313f6b0SJohn Baldwin 
123*9313f6b0SJohn Baldwin 	if (acpi_disabled("apei"))
124*9313f6b0SJohn Baldwin 		return (ENXIO);
125*9313f6b0SJohn Baldwin 
126*9313f6b0SJohn Baldwin 	if (acpi_get_handle(dev) != NULL) {
127*9313f6b0SJohn Baldwin 		rv = ACPI_ID_PROBE(device_get_parent(dev), dev, apei_ids, NULL);
128*9313f6b0SJohn Baldwin 		if (rv > 0)
129*9313f6b0SJohn Baldwin 			return (rv);
130*9313f6b0SJohn Baldwin 	}
131*9313f6b0SJohn Baldwin 
132*9313f6b0SJohn Baldwin 	device_set_desc(dev, "ACPI Platform Error Interface");
133*9313f6b0SJohn Baldwin 	return (0);
134*9313f6b0SJohn Baldwin }
135*9313f6b0SJohn Baldwin 
136*9313f6b0SJohn Baldwin static int
apei_attach(device_t dev)137*9313f6b0SJohn Baldwin apei_attach(device_t dev)
138*9313f6b0SJohn Baldwin {
139*9313f6b0SJohn Baldwin 	struct apei_softc *sc = device_get_softc(dev);
140*9313f6b0SJohn Baldwin 
141*9313f6b0SJohn Baldwin 	sc->dev = dev;
142*9313f6b0SJohn Baldwin 	TAILQ_INIT(&sc->mem);
143*9313f6b0SJohn Baldwin 	TAILQ_INIT(&sc->io);
144*9313f6b0SJohn Baldwin 	bus_identify_children(dev);
145*9313f6b0SJohn Baldwin 	bus_attach_children(dev);
146*9313f6b0SJohn Baldwin 	return (0);
147*9313f6b0SJohn Baldwin }
148*9313f6b0SJohn Baldwin 
149*9313f6b0SJohn Baldwin static device_t
apei_add_child(device_t dev,u_int order,const char * name,int unit)150*9313f6b0SJohn Baldwin apei_add_child(device_t dev, u_int order, const char *name, int unit)
151*9313f6b0SJohn Baldwin {
152*9313f6b0SJohn Baldwin 	struct apei_ivars *ai;
153*9313f6b0SJohn Baldwin 	device_t child;
154*9313f6b0SJohn Baldwin 
155*9313f6b0SJohn Baldwin 	ai = malloc(sizeof(*ai), M_APEI, M_WAITOK);
156*9313f6b0SJohn Baldwin 	TAILQ_INIT(&ai->mappings);
157*9313f6b0SJohn Baldwin 	child = device_add_child_ordered(dev, order, name, unit);
158*9313f6b0SJohn Baldwin 	if (child != NULL)
159*9313f6b0SJohn Baldwin 		device_set_ivars(child, ai);
160*9313f6b0SJohn Baldwin 	else
161*9313f6b0SJohn Baldwin 		free(ai, M_APEI);
162*9313f6b0SJohn Baldwin 	return (child);
163*9313f6b0SJohn Baldwin }
164*9313f6b0SJohn Baldwin 
165*9313f6b0SJohn Baldwin static void
apei_child_deleted(device_t dev,device_t child)166*9313f6b0SJohn Baldwin apei_child_deleted(device_t dev, device_t child)
167*9313f6b0SJohn Baldwin {
168*9313f6b0SJohn Baldwin 	struct apei_ivars *ai = device_get_ivars(child);
169*9313f6b0SJohn Baldwin 
170*9313f6b0SJohn Baldwin 	MPASS(TAILQ_EMPTY(&ai->mappings));
171*9313f6b0SJohn Baldwin 	free(ai, M_APEI);
172*9313f6b0SJohn Baldwin }
173*9313f6b0SJohn Baldwin 
174*9313f6b0SJohn Baldwin static void
apei_child_detached(device_t dev,device_t child)175*9313f6b0SJohn Baldwin apei_child_detached(device_t dev, device_t child)
176*9313f6b0SJohn Baldwin {
177*9313f6b0SJohn Baldwin 	struct apei_ivars *ai = device_get_ivars(child);
178*9313f6b0SJohn Baldwin 
179*9313f6b0SJohn Baldwin 	while (!TAILQ_EMPTY(&ai->mappings)) {
180*9313f6b0SJohn Baldwin 		struct apei_register_mapping *m = TAILQ_FIRST(&ai->mappings);
181*9313f6b0SJohn Baldwin 
182*9313f6b0SJohn Baldwin 		device_printf(dev, "child leaked mapping of %s %#jx-%#jx\n",
183*9313f6b0SJohn Baldwin 		    rman_get_type(m->reg->res) == SYS_RES_MEMORY ? "iomem" :
184*9313f6b0SJohn Baldwin 		    "port", rman_get_start(m->reg->res),
185*9313f6b0SJohn Baldwin 		    rman_get_end(m->reg->res));
186*9313f6b0SJohn Baldwin 
187*9313f6b0SJohn Baldwin 		(void) apei_unmap_register(child, &m->map);
188*9313f6b0SJohn Baldwin 	}
189*9313f6b0SJohn Baldwin }
190*9313f6b0SJohn Baldwin 
191*9313f6b0SJohn Baldwin static int
apei_read_ivar(device_t dev,device_t child,int index,uintptr_t * result)192*9313f6b0SJohn Baldwin apei_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
193*9313f6b0SJohn Baldwin {
194*9313f6b0SJohn Baldwin 	switch (index) {
195*9313f6b0SJohn Baldwin 	case ACPI_IVAR_HANDLE:
196*9313f6b0SJohn Baldwin 		*(ACPI_HANDLE *)result = acpi_get_handle(dev);
197*9313f6b0SJohn Baldwin 		break;
198*9313f6b0SJohn Baldwin 	default:
199*9313f6b0SJohn Baldwin 		return (ENOENT);
200*9313f6b0SJohn Baldwin 	}
201*9313f6b0SJohn Baldwin 
202*9313f6b0SJohn Baldwin 	return (0);
203*9313f6b0SJohn Baldwin }
204*9313f6b0SJohn Baldwin 
205*9313f6b0SJohn Baldwin static struct apei_register *
apei_alloc_register(struct apei_softc * sc,int type,rman_res_t start,rman_res_t count)206*9313f6b0SJohn Baldwin apei_alloc_register(struct apei_softc *sc, int type, rman_res_t start,
207*9313f6b0SJohn Baldwin     rman_res_t count)
208*9313f6b0SJohn Baldwin {
209*9313f6b0SJohn Baldwin 	struct apei_register_list *list;
210*9313f6b0SJohn Baldwin 	struct apei_register *reg, *prev;
211*9313f6b0SJohn Baldwin 	rman_res_t end;
212*9313f6b0SJohn Baldwin 	const char *descr;
213*9313f6b0SJohn Baldwin 	int error, next_rid;
214*9313f6b0SJohn Baldwin 
215*9313f6b0SJohn Baldwin 	switch (type) {
216*9313f6b0SJohn Baldwin 	case SYS_RES_MEMORY:
217*9313f6b0SJohn Baldwin 		list = &sc->mem;
218*9313f6b0SJohn Baldwin 		descr = "iomem";
219*9313f6b0SJohn Baldwin 		break;
220*9313f6b0SJohn Baldwin 	case SYS_RES_IOPORT:
221*9313f6b0SJohn Baldwin 		list = &sc->io;
222*9313f6b0SJohn Baldwin 		descr = "port";
223*9313f6b0SJohn Baldwin 		break;
224*9313f6b0SJohn Baldwin 	default:
225*9313f6b0SJohn Baldwin 		return (NULL);
226*9313f6b0SJohn Baldwin 	}
227*9313f6b0SJohn Baldwin 
228*9313f6b0SJohn Baldwin 	end = start + (count - 1);
229*9313f6b0SJohn Baldwin 
230*9313f6b0SJohn Baldwin 	/* First, look for an existing resource. */
231*9313f6b0SJohn Baldwin 	prev = NULL;
232*9313f6b0SJohn Baldwin 	TAILQ_FOREACH(reg, list, link) {
233*9313f6b0SJohn Baldwin 		/* Does the existing register overlap with this request? */
234*9313f6b0SJohn Baldwin 		if (start > rman_get_end(reg->res) ||
235*9313f6b0SJohn Baldwin 		    end < rman_get_start(reg->res)) {
236*9313f6b0SJohn Baldwin 
237*9313f6b0SJohn Baldwin 			/* prev will be the location of the first free rid */
238*9313f6b0SJohn Baldwin 			if (prev == NULL)
239*9313f6b0SJohn Baldwin 				next_rid = 0;
240*9313f6b0SJohn Baldwin 			else
241*9313f6b0SJohn Baldwin 				next_rid = rman_get_rid(prev->res) + 1;
242*9313f6b0SJohn Baldwin 			if (rman_get_rid(reg->res) == next_rid)
243*9313f6b0SJohn Baldwin 				prev = reg;
244*9313f6b0SJohn Baldwin 			continue;
245*9313f6b0SJohn Baldwin 		}
246*9313f6b0SJohn Baldwin 
247*9313f6b0SJohn Baldwin 		/* Do we need to extend the existing register? */
248*9313f6b0SJohn Baldwin 		if (start < rman_get_start(reg->res) ||
249*9313f6b0SJohn Baldwin 		    end > rman_get_end(reg->res)) {
250*9313f6b0SJohn Baldwin 			rman_res_t new_end, new_start;
251*9313f6b0SJohn Baldwin 
252*9313f6b0SJohn Baldwin 			new_start = rman_get_start(reg->res);
253*9313f6b0SJohn Baldwin 			if (start < new_start)
254*9313f6b0SJohn Baldwin 				new_start = start;
255*9313f6b0SJohn Baldwin 			new_end = rman_get_end(reg->res);
256*9313f6b0SJohn Baldwin 			if (end > new_end)
257*9313f6b0SJohn Baldwin 				new_end = end;
258*9313f6b0SJohn Baldwin 			error = bus_adjust_resource(sc->dev, reg->res,
259*9313f6b0SJohn Baldwin 			    new_start, new_end);
260*9313f6b0SJohn Baldwin 			if (error != 0) {
261*9313f6b0SJohn Baldwin 				if (bootverbose)
262*9313f6b0SJohn Baldwin 					device_printf(sc->dev,
263*9313f6b0SJohn Baldwin 			    "failed to grow %s %#jx-%#jx for %#jx-%#jx: %d\n",
264*9313f6b0SJohn Baldwin 					    descr, rman_get_start(reg->res),
265*9313f6b0SJohn Baldwin 					    rman_get_end(reg->res), start, end,
266*9313f6b0SJohn Baldwin 					    error);
267*9313f6b0SJohn Baldwin 				return (NULL);
268*9313f6b0SJohn Baldwin 			}
269*9313f6b0SJohn Baldwin 		}
270*9313f6b0SJohn Baldwin 
271*9313f6b0SJohn Baldwin 		return (reg);
272*9313f6b0SJohn Baldwin 	}
273*9313f6b0SJohn Baldwin 
274*9313f6b0SJohn Baldwin 	/* Allocate a new resource. */
275*9313f6b0SJohn Baldwin 	if (prev == NULL)
276*9313f6b0SJohn Baldwin 		next_rid = 0;
277*9313f6b0SJohn Baldwin 	else
278*9313f6b0SJohn Baldwin 		next_rid = rman_get_rid(prev->res) + 1;
279*9313f6b0SJohn Baldwin 	error = bus_set_resource(sc->dev, type, next_rid, start, count);
280*9313f6b0SJohn Baldwin 	if (error != 0) {
281*9313f6b0SJohn Baldwin 		if (bootverbose)
282*9313f6b0SJohn Baldwin 			device_printf(sc->dev,
283*9313f6b0SJohn Baldwin 			    "failed to add %s %#jx-%#jx: %d\n", descr, start,
284*9313f6b0SJohn Baldwin 			    end, error);
285*9313f6b0SJohn Baldwin 		return (NULL);
286*9313f6b0SJohn Baldwin 	}
287*9313f6b0SJohn Baldwin 
288*9313f6b0SJohn Baldwin 	reg = malloc(sizeof(*reg), M_APEI, M_WAITOK);
289*9313f6b0SJohn Baldwin 	reg->res = bus_alloc_resource_any(sc->dev, type, next_rid, RF_ACTIVE |
290*9313f6b0SJohn Baldwin 	    RF_UNMAPPED);
291*9313f6b0SJohn Baldwin 	if (reg->res == NULL) {
292*9313f6b0SJohn Baldwin 		if (bootverbose)
293*9313f6b0SJohn Baldwin 			device_printf(sc->dev,
294*9313f6b0SJohn Baldwin 			    "failed to allocate %s %#jx-%#jx\n", descr, start,
295*9313f6b0SJohn Baldwin 			    end);
296*9313f6b0SJohn Baldwin 		free(reg, M_APEI);
297*9313f6b0SJohn Baldwin 		bus_delete_resource(sc->dev, type, next_rid);
298*9313f6b0SJohn Baldwin 		return (NULL);
299*9313f6b0SJohn Baldwin 	}
300*9313f6b0SJohn Baldwin 
301*9313f6b0SJohn Baldwin 	if (prev == NULL)
302*9313f6b0SJohn Baldwin 		TAILQ_INSERT_HEAD(list, reg, link);
303*9313f6b0SJohn Baldwin 	else
304*9313f6b0SJohn Baldwin 		TAILQ_INSERT_AFTER(list, prev, reg, link);
305*9313f6b0SJohn Baldwin 	return (reg);
306*9313f6b0SJohn Baldwin }
307*9313f6b0SJohn Baldwin 
308*9313f6b0SJohn Baldwin static struct resource_map *
apei_map_register_method(device_t dev,device_t child,int type,rman_res_t start,rman_res_t count)309*9313f6b0SJohn Baldwin apei_map_register_method(device_t dev, device_t child, int type,
310*9313f6b0SJohn Baldwin     rman_res_t start, rman_res_t count)
311*9313f6b0SJohn Baldwin {
312*9313f6b0SJohn Baldwin 	struct apei_softc *sc = device_get_softc(dev);
313*9313f6b0SJohn Baldwin 	struct apei_ivars *ai = device_get_ivars(child);
314*9313f6b0SJohn Baldwin 	struct apei_register_mapping *m;
315*9313f6b0SJohn Baldwin 	struct apei_register *reg;
316*9313f6b0SJohn Baldwin 	struct resource_map_request req;
317*9313f6b0SJohn Baldwin 	int error;
318*9313f6b0SJohn Baldwin 
319*9313f6b0SJohn Baldwin 	reg = apei_alloc_register(sc, type, start, count);
320*9313f6b0SJohn Baldwin 	if (reg == NULL)
321*9313f6b0SJohn Baldwin 		return (NULL);
322*9313f6b0SJohn Baldwin 
323*9313f6b0SJohn Baldwin 	m = malloc(sizeof(*m), M_APEI, M_WAITOK | M_ZERO);
324*9313f6b0SJohn Baldwin 	m->reg = reg;
325*9313f6b0SJohn Baldwin 	resource_init_map_request(&req);
326*9313f6b0SJohn Baldwin 	req.offset = start - rman_get_start(reg->res);
327*9313f6b0SJohn Baldwin 	req.length = count;
328*9313f6b0SJohn Baldwin 	error = bus_map_resource(dev, reg->res, &req, &m->map);
329*9313f6b0SJohn Baldwin 	if (error != 0) {
330*9313f6b0SJohn Baldwin 		if (bootverbose)
331*9313f6b0SJohn Baldwin 			device_printf(dev, "failed to map %s %#jx-%#jx: %d\n",
332*9313f6b0SJohn Baldwin 			    type == SYS_RES_MEMORY ? "iomem" : "port",
333*9313f6b0SJohn Baldwin 			    start,
334*9313f6b0SJohn Baldwin 			    start + (count - 1), error);
335*9313f6b0SJohn Baldwin 		free(m, M_APEI);
336*9313f6b0SJohn Baldwin 		return (NULL);
337*9313f6b0SJohn Baldwin 	}
338*9313f6b0SJohn Baldwin 	TAILQ_INSERT_TAIL(&ai->mappings, m, link);
339*9313f6b0SJohn Baldwin 	return (&m->map);
340*9313f6b0SJohn Baldwin }
341*9313f6b0SJohn Baldwin 
342*9313f6b0SJohn Baldwin static int
apei_unmap_register_method(device_t dev,device_t child,struct resource_map * map)343*9313f6b0SJohn Baldwin apei_unmap_register_method(device_t dev, device_t child,
344*9313f6b0SJohn Baldwin     struct resource_map *map)
345*9313f6b0SJohn Baldwin {
346*9313f6b0SJohn Baldwin 	struct apei_ivars *ai = device_get_ivars(child);
347*9313f6b0SJohn Baldwin 	struct apei_register_mapping *m;
348*9313f6b0SJohn Baldwin 	int error;
349*9313f6b0SJohn Baldwin 
350*9313f6b0SJohn Baldwin 	if (map == NULL)
351*9313f6b0SJohn Baldwin 		return (0);
352*9313f6b0SJohn Baldwin 
353*9313f6b0SJohn Baldwin 	TAILQ_FOREACH(m, &ai->mappings, link) {
354*9313f6b0SJohn Baldwin 		if (&m->map == map)
355*9313f6b0SJohn Baldwin 			break;
356*9313f6b0SJohn Baldwin 	}
357*9313f6b0SJohn Baldwin 
358*9313f6b0SJohn Baldwin 	if (m == NULL)
359*9313f6b0SJohn Baldwin 		return (EINVAL);
360*9313f6b0SJohn Baldwin 
361*9313f6b0SJohn Baldwin 	TAILQ_REMOVE(&ai->mappings, m, link);
362*9313f6b0SJohn Baldwin 	error = bus_unmap_resource(dev, m->reg->res, &m->map);
363*9313f6b0SJohn Baldwin 	if (error != 0 && bootverbose)
364*9313f6b0SJohn Baldwin 		device_printf(dev, "failed to unmap %s %#jx-%#jx: %d\n",
365*9313f6b0SJohn Baldwin 		    rman_get_type(m->reg->res) == SYS_RES_MEMORY ? "iomem" :
366*9313f6b0SJohn Baldwin 		    "port", rman_get_start(m->reg->res),
367*9313f6b0SJohn Baldwin 		    rman_get_end(m->reg->res), error);
368*9313f6b0SJohn Baldwin 	free(m, M_APEI);
369*9313f6b0SJohn Baldwin 	return (0);
370*9313f6b0SJohn Baldwin }
371*9313f6b0SJohn Baldwin 
372*9313f6b0SJohn Baldwin struct resource_map *
apei_map_register(device_t dev,ACPI_GENERIC_ADDRESS * gas)373*9313f6b0SJohn Baldwin apei_map_register(device_t dev, ACPI_GENERIC_ADDRESS *gas)
374*9313f6b0SJohn Baldwin {
375*9313f6b0SJohn Baldwin 	int type;
376*9313f6b0SJohn Baldwin 
377*9313f6b0SJohn Baldwin 	if (gas->Address == 0 || gas->BitWidth == 0 || gas->BitWidth % 8 != 0)
378*9313f6b0SJohn Baldwin 		return (NULL);
379*9313f6b0SJohn Baldwin 
380*9313f6b0SJohn Baldwin 	switch (gas->SpaceId) {
381*9313f6b0SJohn Baldwin 	case ACPI_ADR_SPACE_SYSTEM_MEMORY:
382*9313f6b0SJohn Baldwin 		type = SYS_RES_MEMORY;
383*9313f6b0SJohn Baldwin 		break;
384*9313f6b0SJohn Baldwin 	case ACPI_ADR_SPACE_SYSTEM_IO:
385*9313f6b0SJohn Baldwin 		type = SYS_RES_IOPORT;
386*9313f6b0SJohn Baldwin 		break;
387*9313f6b0SJohn Baldwin 	default:
388*9313f6b0SJohn Baldwin 		return (NULL);
389*9313f6b0SJohn Baldwin 	}
390*9313f6b0SJohn Baldwin 
391*9313f6b0SJohn Baldwin 	return (APEI_MAP_REGISTER(device_get_parent(dev), dev, type,
392*9313f6b0SJohn Baldwin 	    gas->Address, gas->BitWidth / 8));
393*9313f6b0SJohn Baldwin }
394*9313f6b0SJohn Baldwin 
395*9313f6b0SJohn Baldwin struct resource_map *
apei_map_memory(device_t dev,rman_res_t start,rman_res_t count)396*9313f6b0SJohn Baldwin apei_map_memory(device_t dev, rman_res_t start, rman_res_t count)
397*9313f6b0SJohn Baldwin {
398*9313f6b0SJohn Baldwin 	return (APEI_MAP_REGISTER(device_get_parent(dev), dev, SYS_RES_MEMORY,
399*9313f6b0SJohn Baldwin 	    start, count));
400*9313f6b0SJohn Baldwin }
401*9313f6b0SJohn Baldwin 
402*9313f6b0SJohn Baldwin int
apei_unmap_register(device_t dev,struct resource_map * map)403*9313f6b0SJohn Baldwin apei_unmap_register(device_t dev, struct resource_map *map)
404*9313f6b0SJohn Baldwin {
405*9313f6b0SJohn Baldwin 	return (APEI_UNMAP_REGISTER(device_get_parent(dev), dev, map));
406*9313f6b0SJohn Baldwin }
407*9313f6b0SJohn Baldwin 
408*9313f6b0SJohn Baldwin static device_method_t apei_methods[] = {
409*9313f6b0SJohn Baldwin 	/* Device interface */
410*9313f6b0SJohn Baldwin 	DEVMETHOD(device_identify,	apei_identify),
411*9313f6b0SJohn Baldwin 	DEVMETHOD(device_probe,		apei_probe),
412*9313f6b0SJohn Baldwin 	DEVMETHOD(device_attach,	apei_attach),
413*9313f6b0SJohn Baldwin 
414*9313f6b0SJohn Baldwin 	/* Bus interface */
415*9313f6b0SJohn Baldwin 	DEVMETHOD(bus_add_child,	apei_add_child),
416*9313f6b0SJohn Baldwin 	DEVMETHOD(bus_print_child,	bus_generic_print_child),
417*9313f6b0SJohn Baldwin 	DEVMETHOD(bus_child_deleted,	apei_child_deleted),
418*9313f6b0SJohn Baldwin 	DEVMETHOD(bus_child_detached,	apei_child_detached),
419*9313f6b0SJohn Baldwin 	DEVMETHOD(bus_read_ivar,	apei_read_ivar),
420*9313f6b0SJohn Baldwin 
421*9313f6b0SJohn Baldwin 	/* APEI interface */
422*9313f6b0SJohn Baldwin 	DEVMETHOD(apei_map_register,	apei_map_register_method),
423*9313f6b0SJohn Baldwin 	DEVMETHOD(apei_unmap_register,	apei_unmap_register_method),
424*9313f6b0SJohn Baldwin 
425*9313f6b0SJohn Baldwin 	DEVMETHOD_END
426*9313f6b0SJohn Baldwin };
427*9313f6b0SJohn Baldwin 
428*9313f6b0SJohn Baldwin static driver_t	apei_driver = {
429*9313f6b0SJohn Baldwin 	"apei",
430*9313f6b0SJohn Baldwin 	apei_methods,
431*9313f6b0SJohn Baldwin 	sizeof(struct apei_softc),
432*9313f6b0SJohn Baldwin };
433*9313f6b0SJohn Baldwin 
434*9313f6b0SJohn Baldwin DRIVER_MODULE(apei, acpi, apei_driver, NULL, NULL);
435*9313f6b0SJohn Baldwin MODULE_DEPEND(apei, acpi, 1, 1, 1);
436