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