xref: /netbsd-src/usr.sbin/acpitools/acpidump/acpi_user.c (revision 161b30af53dfaf1375e7d54bc282eb25c6a2f685)
1*161b30afSjmcneill /* $NetBSD: acpi_user.c,v 1.6 2020/12/06 02:57:30 jmcneill Exp $ */
253e202c1Schristos 
353e202c1Schristos /*-
453e202c1Schristos  * Copyright (c) 1999 Doug Rabson
553e202c1Schristos  * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
653e202c1Schristos  * All rights reserved.
753e202c1Schristos  *
853e202c1Schristos  * Redistribution and use in source and binary forms, with or without
953e202c1Schristos  * modification, are permitted provided that the following conditions
1053e202c1Schristos  * are met:
1153e202c1Schristos  * 1. Redistributions of source code must retain the above copyright
1253e202c1Schristos  *    notice, this list of conditions and the following disclaimer.
1353e202c1Schristos  * 2. Redistributions in binary form must reproduce the above copyright
1453e202c1Schristos  *    notice, this list of conditions and the following disclaimer in the
1553e202c1Schristos  *    documentation and/or other materials provided with the distribution.
1653e202c1Schristos  *
1753e202c1Schristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1853e202c1Schristos  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1953e202c1Schristos  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2053e202c1Schristos  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2153e202c1Schristos  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2253e202c1Schristos  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2353e202c1Schristos  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2453e202c1Schristos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2553e202c1Schristos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2653e202c1Schristos  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2753e202c1Schristos  * SUCH DAMAGE.
2853e202c1Schristos  *
29c359a213Smsaitoh  *	$FreeBSD: head/usr.sbin/acpi/acpidump/acpi_user.c 251186 2013-05-31 17:23:38Z jkim $
3053e202c1Schristos  */
3109f2089bScegger 
3253e202c1Schristos #include <sys/cdefs.h>
33*161b30afSjmcneill __RCSID("$NetBSD: acpi_user.c,v 1.6 2020/12/06 02:57:30 jmcneill Exp $");
3453e202c1Schristos 
3553e202c1Schristos #include <sys/param.h>
3653e202c1Schristos #include <sys/mman.h>
3753e202c1Schristos #include <sys/queue.h>
3853e202c1Schristos #include <sys/stat.h>
3909f2089bScegger #include <sys/sysctl.h>
4053e202c1Schristos 
4153e202c1Schristos #include <err.h>
42*161b30afSjmcneill #include <errno.h>
4353e202c1Schristos #include <fcntl.h>
4453e202c1Schristos #include <stdlib.h>
4553e202c1Schristos #include <string.h>
4653e202c1Schristos #include <unistd.h>
4753e202c1Schristos 
4853e202c1Schristos #include "acpidump.h"
4953e202c1Schristos 
509b32f848Smsaitoh static char	machdep_acpi_root[] = "hw.acpi.root";
5153e202c1Schristos static int      acpi_mem_fd = -1;
52*161b30afSjmcneill static bool	acpi_mem_mmap = false;
5353e202c1Schristos 
5453e202c1Schristos struct acpi_user_mapping {
5553e202c1Schristos 	LIST_ENTRY(acpi_user_mapping) link;
5653e202c1Schristos 	vm_offset_t     pa;
5753e202c1Schristos 	caddr_t         va;
5853e202c1Schristos 	size_t          size;
5953e202c1Schristos };
6053e202c1Schristos 
61c359a213Smsaitoh static LIST_HEAD(acpi_user_mapping_list, acpi_user_mapping) maplist;
6253e202c1Schristos 
6353e202c1Schristos static void
acpi_user_init(void)6453e202c1Schristos acpi_user_init(void)
6553e202c1Schristos {
66*161b30afSjmcneill 	int saved_errno, use_devmem;
67*161b30afSjmcneill 	const char *s;
6853e202c1Schristos 
6953e202c1Schristos 	if (acpi_mem_fd == -1) {
70*161b30afSjmcneill 		s = getenv("ACPIDUMP_USE_DEVMEM");
71*161b30afSjmcneill 		use_devmem = s != NULL && *s == '1';
72*161b30afSjmcneill 		if (!use_devmem) {
73*161b30afSjmcneill 			acpi_mem_fd = open("/dev/acpi", O_RDONLY);
74*161b30afSjmcneill 		}
75*161b30afSjmcneill 		if (acpi_mem_fd == -1) {
76*161b30afSjmcneill 			saved_errno = errno;
7753e202c1Schristos 			acpi_mem_fd = open("/dev/mem", O_RDONLY);
78*161b30afSjmcneill 			if (acpi_mem_fd == -1) {
79*161b30afSjmcneill 				errno = saved_errno;
80*161b30afSjmcneill 			} else {
81*161b30afSjmcneill 				acpi_mem_mmap = true;
82*161b30afSjmcneill 			}
83*161b30afSjmcneill 		}
84*161b30afSjmcneill 		if (acpi_mem_fd == -1) {
85*161b30afSjmcneill 			err(EXIT_FAILURE, "opening /dev/acpi");
86*161b30afSjmcneill 		}
8753e202c1Schristos 		LIST_INIT(&maplist);
8853e202c1Schristos 	}
8953e202c1Schristos }
9053e202c1Schristos 
91*161b30afSjmcneill static void *
acpi_user_read_table(vm_offset_t pa,size_t psize)92*161b30afSjmcneill acpi_user_read_table(vm_offset_t pa, size_t psize)
93*161b30afSjmcneill {
94*161b30afSjmcneill 	void *data;
95*161b30afSjmcneill 	ssize_t len;
96*161b30afSjmcneill 
97*161b30afSjmcneill 	data = calloc(1, psize);
98*161b30afSjmcneill 	if (!data)
99*161b30afSjmcneill 		errx(EXIT_FAILURE, "out of memory");
100*161b30afSjmcneill 
101*161b30afSjmcneill 	len = pread(acpi_mem_fd, data, psize, pa);
102*161b30afSjmcneill 	if (len == -1)
103*161b30afSjmcneill 		errx(EXIT_FAILURE, "can't read table");
104*161b30afSjmcneill 
105*161b30afSjmcneill 	return data;
106*161b30afSjmcneill }
107*161b30afSjmcneill 
10853e202c1Schristos static struct acpi_user_mapping *
acpi_user_find_mapping(vm_offset_t pa,size_t size)10953e202c1Schristos acpi_user_find_mapping(vm_offset_t pa, size_t size)
11053e202c1Schristos {
11153e202c1Schristos 	struct	acpi_user_mapping *map;
11253e202c1Schristos 
11353e202c1Schristos 	/* First search for an existing mapping */
11453e202c1Schristos 	for (map = LIST_FIRST(&maplist); map; map = LIST_NEXT(map, link)) {
11553e202c1Schristos 		if (map->pa <= pa && map->size >= pa + size - map->pa)
11609f2089bScegger 			return map;
11753e202c1Schristos 	}
11853e202c1Schristos 
11953e202c1Schristos 	/* Then create a new one */
120*161b30afSjmcneill 	if (acpi_mem_mmap) {
12153e202c1Schristos 		size = round_page(pa + size) - trunc_page(pa);
12253e202c1Schristos 		pa = trunc_page(pa);
123*161b30afSjmcneill 	}
12453e202c1Schristos 	map = malloc(sizeof(struct acpi_user_mapping));
12553e202c1Schristos 	if (!map)
12609f2089bScegger 		errx(EXIT_FAILURE, "out of memory");
12753e202c1Schristos 	map->pa = pa;
128*161b30afSjmcneill 	if (acpi_mem_mmap) {
12953e202c1Schristos 		map->va = mmap(0, size, PROT_READ, MAP_SHARED, acpi_mem_fd, pa);
13053e202c1Schristos 		if ((intptr_t) map->va == -1)
13109f2089bScegger 			err(EXIT_FAILURE, "can't map address");
132*161b30afSjmcneill 	} else {
133*161b30afSjmcneill 		map->va = acpi_user_read_table(pa, size);
134*161b30afSjmcneill 	}
135*161b30afSjmcneill 	map->size = size;
13653e202c1Schristos 	LIST_INSERT_HEAD(&maplist, map, link);
13753e202c1Schristos 
13809f2089bScegger 	return map;
13909f2089bScegger }
14009f2089bScegger 
14109f2089bScegger static ACPI_TABLE_RSDP *
acpi_get_rsdp(u_long addr)14209f2089bScegger acpi_get_rsdp(u_long addr)
14309f2089bScegger {
14409f2089bScegger 	ACPI_TABLE_RSDP rsdp;
14509f2089bScegger 	size_t len;
14609f2089bScegger 
14709f2089bScegger 	/* Read in the table signature and check it. */
14809f2089bScegger 	pread(acpi_mem_fd, &rsdp, 8, addr);
14909f2089bScegger 	if (memcmp(rsdp.Signature, "RSD PTR ", 8))
15009f2089bScegger 		return (NULL);
15109f2089bScegger 
15209f2089bScegger 	/* Read the entire table. */
15309f2089bScegger 	pread(acpi_mem_fd, &rsdp, sizeof(rsdp), addr);
15409f2089bScegger 
15509f2089bScegger 	/* Check the standard checksum. */
15609f2089bScegger 	if (acpi_checksum(&rsdp, ACPI_RSDP_CHECKSUM_LENGTH) != 0)
15709f2089bScegger 		return (NULL);
15809f2089bScegger 
15909f2089bScegger 	/* Check extended checksum if table version >= 2. */
16009f2089bScegger 	if (rsdp.Revision >= 2 &&
16109f2089bScegger 	    acpi_checksum(&rsdp, ACPI_RSDP_XCHECKSUM_LENGTH) != 0)
16209f2089bScegger 		return (NULL);
16309f2089bScegger 
16409f2089bScegger 	/* If the revision is 0, assume a version 1 length. */
16509f2089bScegger 	if (rsdp.Revision == 0)
166c359a213Smsaitoh 		len = sizeof(ACPI_RSDP_COMMON);
16709f2089bScegger 	else
16809f2089bScegger 		len = rsdp.Length;
16909f2089bScegger 
17009f2089bScegger 	return (acpi_map_physical(addr, len));
17109f2089bScegger }
17209f2089bScegger 
17309f2089bScegger static ACPI_TABLE_RSDP *
acpi_scan_rsd_ptr(void)17409f2089bScegger acpi_scan_rsd_ptr(void)
17509f2089bScegger {
17609f2089bScegger #if defined(__amd64__) || defined(__i386__)
17709f2089bScegger 	ACPI_TABLE_RSDP *rsdp;
17809f2089bScegger 	u_long		addr, end;
17909f2089bScegger 
18009f2089bScegger 	/*
18109f2089bScegger 	 * On ia32, scan physical memory for the RSD PTR if above failed.
18209f2089bScegger 	 * According to section 5.2.2 of the ACPI spec, we only consider
18309f2089bScegger 	 * two regions for the base address:
18409f2089bScegger 	 * 1. EBDA (1 KB area addressed by the 16 bit pointer at 0x40E
18509f2089bScegger 	 * 2. High memory (0xE0000 - 0xFFFFF)
18609f2089bScegger 	 */
18709f2089bScegger 	addr = ACPI_EBDA_PTR_LOCATION;
18809f2089bScegger 	pread(acpi_mem_fd, &addr, sizeof(uint16_t), addr);
18909f2089bScegger 	addr <<= 4;
19009f2089bScegger 	end = addr + ACPI_EBDA_WINDOW_SIZE;
19109f2089bScegger 	for (; addr < end; addr += 16)
19209f2089bScegger 		if ((rsdp = acpi_get_rsdp(addr)) != NULL)
19309f2089bScegger 			return rsdp;
19409f2089bScegger 	addr = ACPI_HI_RSDP_WINDOW_BASE;
19509f2089bScegger 	end = addr + ACPI_HI_RSDP_WINDOW_SIZE;
19609f2089bScegger 	for (; addr < end; addr += 16)
19709f2089bScegger 		if ((rsdp = acpi_get_rsdp(addr)) != NULL)
19809f2089bScegger 			return rsdp;
19909f2089bScegger #endif /* __amd64__ || __i386__ */
20009f2089bScegger 	return NULL;
20153e202c1Schristos }
20253e202c1Schristos 
20353e202c1Schristos /*
20453e202c1Schristos  * Public interfaces
20553e202c1Schristos  */
20609f2089bScegger ACPI_TABLE_RSDP *
acpi_find_rsd_ptr(void)20709f2089bScegger acpi_find_rsd_ptr(void)
20853e202c1Schristos {
2099b32f848Smsaitoh 	ACPI_TABLE_RSDP	*rsdp;
2109b32f848Smsaitoh 	u_long		addr = 0;
2119b32f848Smsaitoh 	size_t		len = sizeof(addr);
21253e202c1Schristos 
21353e202c1Schristos 	acpi_user_init();
21453e202c1Schristos 
2159b32f848Smsaitoh 	if (sysctlbyname(machdep_acpi_root, &addr, &len, NULL, 0) != 0)
2169b32f848Smsaitoh 		addr = 0;
2179b32f848Smsaitoh 	if (addr != 0 && (rsdp = acpi_get_rsdp(addr)) != NULL)
2189b32f848Smsaitoh 		return rsdp;
21953e202c1Schristos 
22009f2089bScegger 	return acpi_scan_rsd_ptr();
22153e202c1Schristos }
22253e202c1Schristos 
22353e202c1Schristos void *
acpi_map_physical(vm_offset_t pa,size_t size)22453e202c1Schristos acpi_map_physical(vm_offset_t pa, size_t size)
22553e202c1Schristos {
22653e202c1Schristos 	struct	acpi_user_mapping *map;
22753e202c1Schristos 
22853e202c1Schristos 	map = acpi_user_find_mapping(pa, size);
22953e202c1Schristos 	return (map->va + (pa - map->pa));
23053e202c1Schristos }
23153e202c1Schristos 
23209f2089bScegger ACPI_TABLE_HEADER *
dsdt_load_file(char * infile)23309f2089bScegger dsdt_load_file(char *infile)
23453e202c1Schristos {
23509f2089bScegger 	ACPI_TABLE_HEADER *sdt;
23609f2089bScegger 	uint8_t		*dp;
23753e202c1Schristos 	struct stat	 sb;
23853e202c1Schristos 
23909f2089bScegger 	if ((acpi_mem_fd = open(infile, O_RDONLY)) == -1)
24009f2089bScegger 		errx(EXIT_FAILURE, "opening %s", infile);
24153e202c1Schristos 
24253e202c1Schristos 	LIST_INIT(&maplist);
24353e202c1Schristos 
24409f2089bScegger 	if (fstat(acpi_mem_fd, &sb) == -1)
24509f2089bScegger 		errx(EXIT_FAILURE, "fstat %s", infile);
24653e202c1Schristos 
24753e202c1Schristos 	dp = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, acpi_mem_fd, 0);
24825f11de3Sriastradh 	if (dp == MAP_FAILED)
24909f2089bScegger 		errx(EXIT_FAILURE, "mmap %s", infile);
25053e202c1Schristos 
25109f2089bScegger 	sdt = (ACPI_TABLE_HEADER *)dp;
25209f2089bScegger 	if (strncmp((char *)dp, ACPI_SIG_DSDT, 4) != 0)
25309f2089bScegger 	    errx(EXIT_FAILURE, "DSDT signature mismatch");
25453e202c1Schristos 
25509f2089bScegger 	if (sdt->Length > sb.st_size)
25609f2089bScegger 	    errx(EXIT_FAILURE,
25709f2089bScegger 		"corrupt DSDT: table size (%"PRIu32" bytes), file size "
25809f2089bScegger 		"(%"PRIu64" bytes)",
25909f2089bScegger 		sdt->Length, sb.st_size);
26009f2089bScegger 
26109f2089bScegger         if (acpi_checksum(sdt, sdt->Length) != 0)
26209f2089bScegger 	    errx(EXIT_FAILURE, "DSDT checksum mismatch");
26309f2089bScegger 
26409f2089bScegger 	return sdt;
26553e202c1Schristos }
266