1 // Copyright 2016 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6 
7 #include <assert.h>
8 #include <debug.h>
9 #include <err.h>
10 #include <stdio.h>
11 #include <trace.h>
12 #include <vm/vm_aspace.h>
13 #include <zircon/types.h>
14 
15 #if ARCH_X86
16 #include <platform/pc/bootloader.h>
17 #else
18 #error "Unsupported architecture"
19 #endif
20 
21 #include "acpi.h"
22 
23 #define _COMPONENT          ACPI_OS_SERVICES
24 ACPI_MODULE_NAME    ("oszircon")
25 
26 #define LOCAL_TRACE 0
27 
28 /**
29  * @brief Initialize the OSL subsystem.
30  *
31  * This function allows the OSL to initialize itself.  It is called during
32  * intiialization of the ACPICA subsystem.
33  *
34  * @return Initialization status
35  */
AcpiOsInitialize()36 ACPI_STATUS AcpiOsInitialize() {
37     return AE_OK;
38 }
39 
40 /**
41  * @brief Terminate the OSL subsystem.
42  *
43  * This function allows the OSL to cleanup and terminate.  It is called during
44  * termination of the ACPICA subsystem.
45  *
46  * @return Termination status
47  */
AcpiOsTerminate()48 ACPI_STATUS AcpiOsTerminate() {
49     return AE_OK;
50 }
51 
52 /**
53  * @brief Obtain the Root ACPI table pointer (RSDP).
54  *
55  * @return The physical address of the RSDP
56  */
AcpiOsGetRootPointer()57 ACPI_PHYSICAL_ADDRESS AcpiOsGetRootPointer() {
58     if (bootloader.acpi_rsdp) {
59         return bootloader.acpi_rsdp;
60     } else {
61         ACPI_PHYSICAL_ADDRESS TableAddress = 0;
62         ACPI_STATUS status = AcpiFindRootPointer(&TableAddress);
63 
64         if (status != AE_OK) {
65             return 0;
66         }
67         return TableAddress;
68     }
69 }
70 
71 /**
72  * @brief Allow the host OS to override a predefined ACPI object.
73  *
74  * @param PredefinedObject A pointer to a predefind object (name and initial
75  *        value)
76  * @param NewValue Where a new value for the predefined object is returned.
77  *        NULL if there is no override for this object.
78  *
79  * @return Exception code that indicates success or reason for failure.
80  */
AcpiOsPredefinedOverride(const ACPI_PREDEFINED_NAMES * PredefinedObject,ACPI_STRING * NewValue)81 ACPI_STATUS AcpiOsPredefinedOverride(
82         const ACPI_PREDEFINED_NAMES *PredefinedObject,
83         ACPI_STRING *NewValue) {
84     *NewValue = NULL;
85     return AE_OK;
86 }
87 
88 /**
89  * @brief Allow the host OS to override a firmware ACPI table via a logical
90  *        address.
91  *
92  * @param ExistingTable A pointer to the header of the existing ACPI table
93  * @param NewTable Where the pointer to the replacment table is returned.  The
94  *        OSL returns NULL if no replacement is provided.
95  *
96  * @return Exception code that indicates success or reason for failure.
97  */
AcpiOsTableOverride(ACPI_TABLE_HEADER * ExistingTable,ACPI_TABLE_HEADER ** NewTable)98 ACPI_STATUS AcpiOsTableOverride(
99         ACPI_TABLE_HEADER *ExistingTable,
100         ACPI_TABLE_HEADER **NewTable) {
101     *NewTable = NULL;
102     return AE_OK;
103 }
104 
105 /**
106  * @brief Allow the host OS to override a firmware ACPI table via a physical
107  *        address.
108  *
109  * @param ExistingTable A pointer to the header of the existing ACPI table
110  * @param NewAddress Where the physical address of the replacment table is
111  *        returned.  The OSL returns NULL if no replacement is provided.
112  * @param NewLength Where the length of the replacement table is returned.
113  *
114  * @return Exception code that indicates success or reason for failure.
115  */
AcpiOsPhysicalTableOverride(ACPI_TABLE_HEADER * ExistingTable,ACPI_PHYSICAL_ADDRESS * NewAddress,UINT32 * NewTableLength)116 ACPI_STATUS AcpiOsPhysicalTableOverride(
117         ACPI_TABLE_HEADER *ExistingTable,
118         ACPI_PHYSICAL_ADDRESS *NewAddress,
119         UINT32 *NewTableLength) {
120     *NewAddress = 0;
121     return AE_OK;
122 }
123 
124 /**
125  * @brief Map physical memory into the caller's address space.
126  *
127  * @param PhysicalAddress A full physical address of the memory to be mapped
128  *        into the caller's address space
129  * @param Length The amount of memory to mapped starting at the given physical
130  *        address
131  *
132  * @return Logical pointer to the mapped memory. A NULL pointer indicated failures.
133  */
AcpiOsMapMemory(ACPI_PHYSICAL_ADDRESS PhysicalAddress,ACPI_SIZE Length)134 void *AcpiOsMapMemory(
135         ACPI_PHYSICAL_ADDRESS PhysicalAddress,
136         ACPI_SIZE Length) {
137 
138     // Caution: PhysicalAddress might not be page-aligned, Length might not
139     // be a page multiple.
140 
141     ACPI_PHYSICAL_ADDRESS aligned_address = ROUNDDOWN(PhysicalAddress, PAGE_SIZE);
142     ACPI_PHYSICAL_ADDRESS end = ROUNDUP(PhysicalAddress + Length, PAGE_SIZE);
143 
144     void *vaddr = NULL;
145     zx_status_t status = VmAspace::kernel_aspace()->AllocPhysical(
146             "acpi_mapping",
147             end - aligned_address,
148             &vaddr,
149             PAGE_SIZE_SHIFT,
150             aligned_address,
151             0,
152             ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE);
153     if (status != ZX_OK) {
154         return NULL;
155     }
156     const uintptr_t real_addr =
157             reinterpret_cast<uintptr_t>(vaddr) + (PhysicalAddress - aligned_address);
158     return reinterpret_cast<void*>(real_addr);
159 }
160 
161 /**
162  * @brief Remove a physical to logical memory mapping.
163  *
164  * @param LogicalAddress The logical address that was returned from a previous
165  *        call to AcpiOsMapMemory.
166  * @param Length The amount of memory that was mapped. This value must be
167  *        identical to the value used in the call to AcpiOsMapMemory.
168  */
AcpiOsUnmapMemory(void * LogicalAddress,ACPI_SIZE Length)169 void AcpiOsUnmapMemory(void *LogicalAddress, ACPI_SIZE Length) {
170     zx_status_t status = VmAspace::kernel_aspace()->FreeRegion(
171             reinterpret_cast<vaddr_t>(LogicalAddress));
172     if (status != ZX_OK) {
173         TRACEF("WARNING: ACPI failed to free region %p, size %" PRIu64 "\n",
174                LogicalAddress, (uint64_t)Length);
175     }
176 }
177 
178 /**
179  * @brief Read a value from a memory location.
180  *
181  * @param Address Memory address to be read.
182  * @param Value A pointer to a location where the data is to be returned.
183  * @param Width The memory width in bits, either 8, 16, 32, or 64.
184  *
185  * @return Exception code that indicates success or reason for failure.
186  */
AcpiOsReadMemory(ACPI_PHYSICAL_ADDRESS Address,UINT64 * Value,UINT32 Width)187 ACPI_STATUS AcpiOsReadMemory(
188         ACPI_PHYSICAL_ADDRESS Address,
189         UINT64 *Value,
190         UINT32 Width) {
191 
192     PANIC_UNIMPLEMENTED;
193 }
194 
195 /**
196  * @brief Write a value to a memory location.
197  *
198  * @param Address Memory address where data is to be written.
199  * @param Value Data to be written to the memory location.
200  * @param Width The memory width in bits, either 8, 16, 32, or 64.
201  *
202  * @return Exception code that indicates success or reason for failure.
203  */
AcpiOsWriteMemory(ACPI_PHYSICAL_ADDRESS Address,UINT64 Value,UINT32 Width)204 ACPI_STATUS AcpiOsWriteMemory(
205         ACPI_PHYSICAL_ADDRESS Address,
206         UINT64 Value,
207         UINT32 Width) {
208 
209     PANIC_UNIMPLEMENTED;
210 }
211 
212 /**
213  * @brief Wait for a short amount of time (fine granularity).
214  *
215  * Execution of the running thread is not suspended for this time.
216  *
217  * @param Microseconds The amount of time to delay, in microseconds.
218  */
AcpiOsStall(UINT32 Microseconds)219 void AcpiOsStall(UINT32 Microseconds) {
220     spin(Microseconds);
221 }
222 
223 /**
224  * @brief Read a value from an input port.
225  *
226  * @param Address Hardware I/O port address to be read.
227  * @param Value A pointer to a location where the data is to be returned.
228  * @param Width The port width in bits, either 8, 16, or 32.
229  *
230  * @return Exception code that indicates success or reason for failure.
231  */
AcpiOsReadPort(ACPI_IO_ADDRESS Address,UINT32 * Value,UINT32 Width)232 ACPI_STATUS AcpiOsReadPort(
233         ACPI_IO_ADDRESS Address,
234         UINT32 *Value,
235         UINT32 Width) {
236     if (Address > 0xffff) {
237         return AE_BAD_PARAMETER;
238     }
239 
240     switch (Width) {
241         case 8:
242             *Value = inp((uint16_t)Address);
243             break;
244         case 16:
245             *Value = inpw((uint16_t)Address);
246             break;
247         case 32:
248             *Value = inpd((uint16_t)Address);
249             break;
250         default:
251             return AE_BAD_PARAMETER;
252     }
253     return AE_OK;
254 }
255 
256 /**
257  * @brief Write a value to an output port.
258  *
259  * @param Address Hardware I/O port address where data is to be written.
260  * @param Value The value to be written.
261  * @param Width The port width in bits, either 8, 16, or 32.
262  *
263  * @return Exception code that indicates success or reason for failure.
264  */
AcpiOsWritePort(ACPI_IO_ADDRESS Address,UINT32 Value,UINT32 Width)265 ACPI_STATUS AcpiOsWritePort(
266         ACPI_IO_ADDRESS Address,
267         UINT32 Value,
268         UINT32 Width) {
269     if (Address > 0xffff) {
270         return AE_BAD_PARAMETER;
271     }
272 
273     switch (Width) {
274         case 8:
275             outp((uint16_t)Address, (uint8_t)Value);
276             break;
277         case 16:
278             outpw((uint16_t)Address, (uint16_t)Value);
279             break;
280         case 32:
281             outpd((uint16_t)Address, (uint32_t)Value);
282             break;
283         default:
284             return AE_BAD_PARAMETER;
285     }
286     return AE_OK;
287 }
288 
289 /**
290  * @brief Allocate memory from the dynamic memory pool.
291  *
292  * @param Size Amount of memory to allocate.
293  *
294  * @return A pointer to the allocated memory. A NULL pointer is returned on
295  *         error.
296  */
AcpiOsAllocate(ACPI_SIZE Size)297 void *AcpiOsAllocate(ACPI_SIZE Size) {
298     return malloc(Size);
299 }
300 
301 /**
302  * @brief Free previously allocated memory.
303  *
304  * @param Memory A pointer to the memory to be freed.
305  */
AcpiOsFree(void * Memory)306 void AcpiOsFree(void *Memory) {
307     free(Memory);
308 }
309 
310 /**
311  * @brief Formatted stream output.
312  *
313  * @param Format A standard print format string.
314  * @param ...
315  */
AcpiOsPrintf(const char * Format,...)316 void ACPI_INTERNAL_VAR_XFACE AcpiOsPrintf(const char *Format, ...) {
317     va_list argp;
318     va_start(argp, Format);
319     AcpiOsVprintf(Format, argp);
320     va_end(argp);
321 }
322 
323 /**
324  * @brief Formatted stream output.
325  *
326  * @param Format A standard print format string.
327  * @param Args A variable parameter list
328  */
AcpiOsVprintf(const char * Format,va_list Args)329 void AcpiOsVprintf(const char *Format, va_list Args) {
330 }
331