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