1/*-
2 * Copyright (c) 2001 Mitsuru IWASAKI
3 * Copyright (c) 2015 The FreeBSD Foundation
4 * All rights reserved.
5 *
6 * This software was developed by Andrew Turner under
7 * sponsorship from the FreeBSD Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <sys/param.h>
32#include <sys/bus.h>
33#include <sys/kernel.h>
34
35#include <vm/vm.h>
36#include <vm/pmap.h>
37
38#include <machine/machdep.h>
39
40#include <contrib/dev/acpica/include/acpi.h>
41#include <contrib/dev/acpica/include/accommon.h>
42#include <contrib/dev/acpica/include/actables.h>
43
44#include <dev/acpica/acpivar.h>
45
46extern struct bus_space memmap_bus;
47
48int
49acpi_machdep_init(device_t dev)
50{
51
52	return (0);
53}
54
55int
56acpi_machdep_quirks(int *quirks)
57{
58
59	return (0);
60}
61
62static void *
63map_table(vm_paddr_t pa, const char *sig)
64{
65	ACPI_TABLE_HEADER *header;
66	vm_size_t length;
67	void *table;
68
69	header = pmap_mapbios(pa, sizeof(ACPI_TABLE_HEADER));
70	if (strncmp(header->Signature, sig, ACPI_NAMESEG_SIZE) != 0) {
71		pmap_unmapbios(header, sizeof(ACPI_TABLE_HEADER));
72		return (NULL);
73	}
74	length = header->Length;
75	pmap_unmapbios(header, sizeof(ACPI_TABLE_HEADER));
76
77	table = pmap_mapbios(pa, length);
78	if (ACPI_FAILURE(AcpiUtChecksum(table, length))) {
79		if (bootverbose)
80			printf("ACPI: Failed checksum for table %s\n", sig);
81#if (ACPI_CHECKSUM_ABORT)
82		pmap_unmapbios(table, length);
83		return (NULL);
84#endif
85	}
86	return (table);
87}
88
89/*
90 * See if a given ACPI table is the requested table.  Returns the
91 * length of the table if it matches or zero on failure.
92 */
93static int
94probe_table(vm_paddr_t address, const char *sig)
95{
96	ACPI_TABLE_HEADER *table;
97
98	table = pmap_mapbios(address, sizeof(ACPI_TABLE_HEADER));
99	if (table == NULL) {
100		if (bootverbose)
101			printf("ACPI: Failed to map table at 0x%jx\n",
102			    (uintmax_t)address);
103		return (0);
104	}
105
106	if (strncmp(table->Signature, sig, ACPI_NAMESEG_SIZE) != 0) {
107		pmap_unmapbios(table, sizeof(ACPI_TABLE_HEADER));
108		return (0);
109	}
110	pmap_unmapbios(table, sizeof(ACPI_TABLE_HEADER));
111	return (1);
112}
113
114/* Unmap a table previously mapped via acpi_map_table(). */
115void
116acpi_unmap_table(void *table)
117{
118	ACPI_TABLE_HEADER *header;
119
120	header = (ACPI_TABLE_HEADER *)table;
121	pmap_unmapbios(table, header->Length);
122}
123
124/*
125 * Try to map a table at a given physical address previously returned
126 * by acpi_find_table().
127 */
128void *
129acpi_map_table(vm_paddr_t pa, const char *sig)
130{
131
132	return (map_table(pa, sig));
133}
134
135/*
136 * Return the physical address of the requested table or zero if one
137 * is not found.
138 */
139vm_paddr_t
140acpi_find_table(const char *sig)
141{
142	ACPI_PHYSICAL_ADDRESS rsdp_ptr;
143	ACPI_TABLE_RSDP *rsdp;
144	ACPI_TABLE_XSDT *xsdt;
145	ACPI_TABLE_HEADER *table;
146	vm_paddr_t addr;
147	int i, count;
148
149	if (resource_disabled("acpi", 0))
150		return (0);
151
152	/*
153	 * Map in the RSDP.  Since ACPI uses AcpiOsMapMemory() which in turn
154	 * calls pmap_mapbios() to find the RSDP, we assume that we can use
155	 * pmap_mapbios() to map the RSDP.
156	 */
157	if ((rsdp_ptr = AcpiOsGetRootPointer()) == 0)
158		return (0);
159	rsdp = pmap_mapbios(rsdp_ptr, sizeof(ACPI_TABLE_RSDP));
160	if (rsdp == NULL) {
161		printf("ACPI: Failed to map RSDP\n");
162		return (0);
163	}
164
165	addr = 0;
166	if (rsdp->Revision >= 2 && rsdp->XsdtPhysicalAddress != 0) {
167		/*
168		 * AcpiOsGetRootPointer only verifies the checksum for
169		 * the version 1.0 portion of the RSDP.  Version 2.0 has
170		 * an additional checksum that we verify first.
171		 */
172		if (AcpiUtChecksum((UINT8 *)rsdp, ACPI_RSDP_XCHECKSUM_LENGTH)) {
173			printf("ACPI: RSDP failed extended checksum\n");
174			pmap_unmapbios(rsdp, sizeof(ACPI_TABLE_RSDP));
175			return (0);
176		}
177		xsdt = map_table(rsdp->XsdtPhysicalAddress, ACPI_SIG_XSDT);
178		if (xsdt == NULL) {
179			printf("ACPI: Failed to map XSDT\n");
180			pmap_unmapbios(rsdp, sizeof(ACPI_TABLE_RSDP));
181			return (0);
182		}
183		count = (xsdt->Header.Length - sizeof(ACPI_TABLE_HEADER)) /
184		    sizeof(UINT64);
185		for (i = 0; i < count; i++)
186			if (probe_table(xsdt->TableOffsetEntry[i], sig)) {
187				addr = xsdt->TableOffsetEntry[i];
188				break;
189			}
190		acpi_unmap_table(xsdt);
191	} else {
192		printf("ACPI: Unsupported RSDP version %d and XSDT %#lx\n",
193		    rsdp->Revision, rsdp->XsdtPhysicalAddress);
194	}
195	pmap_unmapbios(rsdp, sizeof(ACPI_TABLE_RSDP));
196
197	if (addr == 0)
198		return (0);
199
200	/*
201	 * Verify that we can map the full table and that its checksum is
202	 * correct, etc.
203	 */
204	table = map_table(addr, sig);
205	if (table == NULL)
206		return (0);
207	acpi_unmap_table(table);
208
209	return (addr);
210}
211
212int
213acpi_map_addr(struct acpi_generic_address *addr, bus_space_tag_t *tag,
214    bus_space_handle_t *handle, bus_size_t size)
215{
216	bus_addr_t phys;
217
218	/* Check if the device is Memory mapped */
219	if (addr->SpaceId != 0)
220		return (ENXIO);
221
222	phys = addr->Address;
223	*tag = &memmap_bus;
224
225	return (bus_space_map(*tag, phys, size, 0, handle));
226}
227
228#if MAXMEMDOM > 1
229static void
230parse_pxm_tables(void *dummy)
231{
232	uint64_t mmfr0, parange;
233
234	/* Only parse ACPI tables when booting via ACPI */
235	if (arm64_bus_method != ARM64_BUS_ACPI)
236		return;
237
238	if (!get_kernel_reg(ID_AA64MMFR0_EL1, &mmfr0)) {
239		/* chosen arbitrarily */
240		mmfr0 = ID_AA64MMFR0_PARange_1T;
241	}
242
243	switch (ID_AA64MMFR0_PARange_VAL(mmfr0)) {
244	case ID_AA64MMFR0_PARange_4G:
245		parange = (vm_paddr_t)4 << 30 /* GiB */;
246		break;
247	case ID_AA64MMFR0_PARange_64G:
248		parange = (vm_paddr_t)64 << 30 /* GiB */;
249		break;
250	case ID_AA64MMFR0_PARange_1T:
251		parange = (vm_paddr_t)1 << 40 /* TiB */;
252		break;
253	case ID_AA64MMFR0_PARange_4T:
254		parange = (vm_paddr_t)4 << 40 /* TiB */;
255		break;
256	case ID_AA64MMFR0_PARange_16T:
257		parange = (vm_paddr_t)16 << 40 /* TiB */;
258		break;
259	case ID_AA64MMFR0_PARange_256T:
260		parange = (vm_paddr_t)256 << 40 /* TiB */;
261		break;
262	case ID_AA64MMFR0_PARange_4P:
263		parange = (vm_paddr_t)4 << 50 /* PiB */;
264		break;
265	default:
266		/* chosen arbitrarily */
267		parange = (vm_paddr_t)1 << 40 /* TiB */;
268		printf("Unknown value for PARange in mmfr0 (%#lx)\n", mmfr0);
269		break;
270	}
271
272	acpi_pxm_init(MAXCPU, parange);
273	acpi_pxm_parse_tables();
274	acpi_pxm_set_mem_locality();
275}
276SYSINIT(parse_pxm_tables, SI_SUB_VM - 1, SI_ORDER_FIRST, parse_pxm_tables,
277    NULL);
278#endif
279