#pragma once #include #include #ifdef __cplusplus extern "C" { #endif // Returns the PHYSICAL address of the RSDP structure via *out_rsdp_address. uacpi_status uacpi_kernel_get_rsdp(uacpi_phys_addr *out_rsdp_address); /* * Map a physical memory range starting at 'addr' with length 'len', and return * a virtual address that can be used to access it. * * NOTE: 'addr' may be misaligned, in this case the host is expected to round it * down to the nearest page-aligned boundary and map that, while making * sure that at least 'len' bytes are still mapped starting at 'addr'. The * return value preserves the misaligned offset. * * Example for uacpi_kernel_map(0x1ABC, 0xF00): * 1. Round down the 'addr' we got to the nearest page boundary. * Considering a PAGE_SIZE of 4096 (or 0x1000), 0x1ABC rounded down * is 0x1000, offset within the page is 0x1ABC - 0x1000 => 0xABC * 2. Requested 'len' is 0xF00 bytes, but we just rounded the address * down by 0xABC bytes, so add those on top. 0xF00 + 0xABC => 0x19BC * 3. Round up the final 'len' to the nearest PAGE_SIZE boundary, in * this case 0x19BC is 0x2000 bytes (2 pages if PAGE_SIZE is 4096) * 4. Call the VMM to map the aligned address 0x1000 (from step 1) * with length 0x2000 (from step 3). Let's assume the returned * virtual address for the mapping is 0xF000. * 5. Add the original offset within page 0xABC (from step 1) to the * resulting virtual address 0xF000 + 0xABC => 0xFABC. Return it * to uACPI. */ void *uacpi_kernel_map(uacpi_phys_addr addr, uacpi_size len); /* * Unmap a virtual memory range at 'addr' with a length of 'len' bytes. * * NOTE: 'addr' may be misaligned, see the comment above 'uacpi_kernel_map'. * Similar steps to uacpi_kernel_map can be taken to retrieve the * virtual address originally returned by the VMM for this mapping * as well as its true length. */ void uacpi_kernel_unmap(void *addr, uacpi_size len); #ifndef UACPI_FORMATTED_LOGGING void uacpi_kernel_log(uacpi_log_level, const uacpi_char*); #else UACPI_PRINTF_DECL(2, 3) void uacpi_kernel_log(uacpi_log_level, const uacpi_char*, ...); void uacpi_kernel_vlog(uacpi_log_level, const uacpi_char*, uacpi_va_list); #endif /* * Only the above ^^^ API may be used by early table access and * UACPI_BAREBONES_MODE. */ #ifndef UACPI_BAREBONES_MODE /* * Convenience initialization/deinitialization hooks that will be called by * uACPI automatically when appropriate if compiled-in. */ #ifdef UACPI_KERNEL_INITIALIZATION /* * This API is invoked for each initialization level so that appropriate parts * of the host kernel and/or glue code can be initialized at different stages. * * uACPI API that triggers calls to uacpi_kernel_initialize and the respective * 'current_init_lvl' passed to the hook at that stage: * 1. uacpi_initialize() -> UACPI_INIT_LEVEL_EARLY * 2. uacpi_namespace_load() -> UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED * 3. (start of) uacpi_namespace_initialize() -> UACPI_INIT_LEVEL_NAMESPACE_LOADED * 4. (end of) uacpi_namespace_initialize() -> UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED */ uacpi_status uacpi_kernel_initialize(uacpi_init_level current_init_lvl); void uacpi_kernel_deinitialize(void); #endif /* * Open a PCI device at 'address' for reading & writing. * * Note that this must be able to open any arbitrary PCI device, not just those * detected during kernel PCI enumeration, since the following pattern is * relatively common in AML firmware: * Device (THC0) * { * // Device at 00:10.06 * Name (_ADR, 0x00100006) // _ADR: Address * * OperationRegion (THCR, PCI_Config, Zero, 0x0100) * Field (THCR, ByteAcc, NoLock, Preserve) * { * // Vendor ID field in the PCI configuration space * VDID, 32 * } * * // Check if the device at 00:10.06 actually exists, that is reading * // from its configuration space returns something other than 0xFFs. * If ((VDID != 0xFFFFFFFF)) * { * // Actually create the rest of the device's body if it's present * // in the system, otherwise skip it. * } * } * * The handle returned via 'out_handle' is used to perform IO on the * configuration space of the device. */ uacpi_status uacpi_kernel_pci_device_open( uacpi_pci_address address, uacpi_handle *out_handle ); void uacpi_kernel_pci_device_close(uacpi_handle); /* * Read & write the configuration space of a previously open PCI device. */ uacpi_status uacpi_kernel_pci_read8( uacpi_handle device, uacpi_size offset, uacpi_u8 *value ); uacpi_status uacpi_kernel_pci_read16( uacpi_handle device, uacpi_size offset, uacpi_u16 *value ); uacpi_status uacpi_kernel_pci_read32( uacpi_handle device, uacpi_size offset, uacpi_u32 *value ); uacpi_status uacpi_kernel_pci_write8( uacpi_handle device, uacpi_size offset, uacpi_u8 value ); uacpi_status uacpi_kernel_pci_write16( uacpi_handle device, uacpi_size offset, uacpi_u16 value ); uacpi_status uacpi_kernel_pci_write32( uacpi_handle device, uacpi_size offset, uacpi_u32 value ); /* * Map a SystemIO address at [base, base + len) and return a kernel-implemented * handle that can be used for reading and writing the IO range. * * NOTE: The x86 architecture uses the in/out family of instructions * to access the SystemIO address space. */ uacpi_status uacpi_kernel_io_map( uacpi_io_addr base, uacpi_size len, uacpi_handle *out_handle ); void uacpi_kernel_io_unmap(uacpi_handle handle); /* * Read/Write the IO range mapped via uacpi_kernel_io_map * at a 0-based 'offset' within the range. * * NOTE: * The x86 architecture uses the in/out family of instructions * to access the SystemIO address space. * * You are NOT allowed to break e.g. a 4-byte access into four 1-byte accesses. * Hardware ALWAYS expects accesses to be of the exact width. */ uacpi_status uacpi_kernel_io_read8( uacpi_handle, uacpi_size offset, uacpi_u8 *out_value ); uacpi_status uacpi_kernel_io_read16( uacpi_handle, uacpi_size offset, uacpi_u16 *out_value ); uacpi_status uacpi_kernel_io_read32( uacpi_handle, uacpi_size offset, uacpi_u32 *out_value ); uacpi_status uacpi_kernel_io_write8( uacpi_handle, uacpi_size offset, uacpi_u8 in_value ); uacpi_status uacpi_kernel_io_write16( uacpi_handle, uacpi_size offset, uacpi_u16 in_value ); uacpi_status uacpi_kernel_io_write32( uacpi_handle, uacpi_size offset, uacpi_u32 in_value ); /* * Allocate a block of memory of 'size' bytes. * The contents of the allocated memory are unspecified. */ void *uacpi_kernel_alloc(uacpi_size size); #ifdef UACPI_NATIVE_ALLOC_ZEROED /* * Allocate a block of memory of 'size' bytes. * The returned memory block is expected to be zero-filled. */ void *uacpi_kernel_alloc_zeroed(uacpi_size size); #endif /* * Free a previously allocated memory block. * * 'mem' might be a NULL pointer. In this case, the call is assumed to be a * no-op. * * An optionally enabled 'size_hint' parameter contains the size of the original * allocation. Note that in some scenarios this incurs additional cost to * calculate the object size. */ #ifndef UACPI_SIZED_FREES void uacpi_kernel_free(void *mem); #else void uacpi_kernel_free(void *mem, uacpi_size size_hint); #endif /* * Returns the number of nanosecond ticks elapsed since boot, * strictly monotonic. */ uacpi_u64 uacpi_kernel_get_nanoseconds_since_boot(void); /* * Spin for N microseconds. */ void uacpi_kernel_stall(uacpi_u8 usec); /* * Sleep for N milliseconds. */ void uacpi_kernel_sleep(uacpi_u64 msec); /* * Create/free an opaque non-recursive kernel mutex object. */ uacpi_handle uacpi_kernel_create_mutex(void); void uacpi_kernel_free_mutex(uacpi_handle); /* * Create/free an opaque kernel (semaphore-like) event object. */ uacpi_handle uacpi_kernel_create_event(void); void uacpi_kernel_free_event(uacpi_handle); /* * Returns a unique identifier of the currently executing thread. * * The returned thread id cannot be UACPI_THREAD_ID_NONE. */ uacpi_thread_id uacpi_kernel_get_thread_id(void); /* * Try to acquire the mutex with a millisecond timeout. * * The timeout value has the following meanings: * 0x0000 - Attempt to acquire the mutex once, in a non-blocking manner * 0x0001...0xFFFE - Attempt to acquire the mutex for at least 'timeout' * milliseconds * 0xFFFF - Infinite wait, block until the mutex is acquired * * The following are possible return values: * 1. UACPI_STATUS_OK - successful acquire operation * 2. UACPI_STATUS_TIMEOUT - timeout reached while attempting to acquire (or the * single attempt to acquire was not successful for * calls with timeout=0) * 3. Any other value - signifies a host internal error and is treated as such */ uacpi_status uacpi_kernel_acquire_mutex(uacpi_handle, uacpi_u16); void uacpi_kernel_release_mutex(uacpi_handle); /* * Try to wait for an event (counter > 0) with a millisecond timeout. * A timeout value of 0xFFFF implies infinite wait. * * The internal counter is decremented by 1 if wait was successful. * * A successful wait is indicated by returning UACPI_TRUE. */ uacpi_bool uacpi_kernel_wait_for_event(uacpi_handle, uacpi_u16); /* * Signal the event object by incrementing its internal counter by 1. * * This function may be used in interrupt contexts. */ void uacpi_kernel_signal_event(uacpi_handle); /* * Reset the event counter to 0. */ void uacpi_kernel_reset_event(uacpi_handle); /* * Handle a firmware request. * * Currently either a Breakpoint or Fatal operators. */ uacpi_status uacpi_kernel_handle_firmware_request(uacpi_firmware_request*); /* * Install an interrupt handler at 'irq', 'ctx' is passed to the provided * handler for every invocation. * * 'out_irq_handle' is set to a kernel-implemented value that can be used to * refer to this handler from other API. */ uacpi_status uacpi_kernel_install_interrupt_handler( uacpi_u32 irq, uacpi_interrupt_handler, uacpi_handle ctx, uacpi_handle *out_irq_handle ); /* * Uninstall an interrupt handler. 'irq_handle' is the value returned via * 'out_irq_handle' during installation. */ uacpi_status uacpi_kernel_uninstall_interrupt_handler( uacpi_interrupt_handler, uacpi_handle irq_handle ); /* * Create/free a kernel spinlock object. * * Unlike other types of locks, spinlocks may be used in interrupt contexts. */ uacpi_handle uacpi_kernel_create_spinlock(void); void uacpi_kernel_free_spinlock(uacpi_handle); /* * Lock/unlock helpers for spinlocks. * * These are expected to disable interrupts, returning the previous state of cpu * flags, that can be used to possibly re-enable interrupts if they were enabled * before. * * Note that lock is infalliable. */ uacpi_cpu_flags uacpi_kernel_lock_spinlock(uacpi_handle); void uacpi_kernel_unlock_spinlock(uacpi_handle, uacpi_cpu_flags); typedef enum uacpi_work_type { /* * Schedule a GPE handler method for execution. * This should be scheduled to run on CPU0 to avoid potential SMI-related * firmware bugs. */ UACPI_WORK_GPE_EXECUTION, /* * Schedule a Notify(device) firmware request for execution. * This can run on any CPU. */ UACPI_WORK_NOTIFICATION, } uacpi_work_type; typedef void (*uacpi_work_handler)(uacpi_handle); /* * Schedules deferred work for execution. * Might be invoked from an interrupt context. */ uacpi_status uacpi_kernel_schedule_work( uacpi_work_type, uacpi_work_handler, uacpi_handle ctx ); /* * Waits for two types of work to finish: * 1. All in-flight interrupts installed via uacpi_kernel_install_interrupt_handler * 2. All work scheduled via uacpi_kernel_schedule_work * * Note that the waits must be done in this order specifically. */ uacpi_status uacpi_kernel_wait_for_work_completion(void); #endif // !UACPI_BAREBONES_MODE #ifdef __cplusplus } #endif