#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: acpi_dev.c,v 1.4 2024/11/12 21:49:11 riastradh Exp $");
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/mman.h>
#include <dev/acpi/acpireg.h>
#include <dev/acpi/acpivar.h>
#define _COMPONENT ACPI_BUS_COMPONENT
ACPI_MODULE_NAME ("acpi_dev")
static dev_type_read(acpi_read);
const struct cdevsw acpi_cdevsw = {
.d_open = nullopen,
.d_close = nullclose,
.d_read = acpi_read,
.d_write = nowrite,
.d_ioctl = noioctl,
.d_stop = nostop,
.d_tty = notty,
.d_poll = nopoll,
.d_mmap = nommap,
.d_kqfilter = nokqfilter,
.d_discard = nodiscard,
.d_flag = D_OTHER | D_MPSAFE,
};
static bool
acpi_find_table_rsdp(ACPI_PHYSICAL_ADDRESS pa,
ACPI_PHYSICAL_ADDRESS *paddr, uint32_t *plen)
{
ACPI_PHYSICAL_ADDRESS table_pa;
table_pa = AcpiOsGetRootPointer();
if (table_pa == 0) {
return false;
}
if (pa >= table_pa && pa < table_pa + sizeof(ACPI_TABLE_RSDP)) {
*paddr = table_pa;
*plen = sizeof(ACPI_TABLE_RSDP);
return true;
}
return false;
}
static bool
acpi_find_table_sdt(ACPI_PHYSICAL_ADDRESS pa,
ACPI_PHYSICAL_ADDRESS *paddr, uint32_t *plen)
{
ACPI_PHYSICAL_ADDRESS table_pa;
ACPI_TABLE_RSDP *rsdp;
ACPI_TABLE_HEADER *thdr;
uint32_t table_len;
table_pa = AcpiOsGetRootPointer();
KASSERT(table_pa != 0);
rsdp = AcpiOsMapMemory(table_pa, sizeof(ACPI_TABLE_RSDP));
if (rsdp == NULL) {
return false;
}
if (rsdp->Revision > 1 && rsdp->XsdtPhysicalAddress) {
table_pa = rsdp->XsdtPhysicalAddress;
} else {
table_pa = rsdp->RsdtPhysicalAddress;
}
AcpiOsUnmapMemory(rsdp, sizeof(ACPI_TABLE_RSDP));
if (table_pa == 0) {
return false;
}
thdr = AcpiOsMapMemory(table_pa, sizeof(ACPI_TABLE_HEADER));
if (thdr == NULL) {
return false;
}
table_len = thdr->Length;
AcpiOsUnmapMemory(thdr, sizeof(ACPI_TABLE_HEADER));
if (pa >= table_pa && pa < table_pa + table_len) {
*paddr = table_pa;
*plen = table_len;
return true;
}
return false;
}
static bool
acpi_find_table(ACPI_PHYSICAL_ADDRESS pa,
ACPI_PHYSICAL_ADDRESS *paddr, uint32_t *plen)
{
ACPI_TABLE_DESC *tdesc;
ACPI_TABLE_TCPA_HDR *tcpa = NULL;
size_t tcpa_tdesc_len = 0;
bool found_table;
uint32_t i;
if (acpi_find_table_rsdp(pa, paddr, plen)) {
return true;
}
if (acpi_find_table_sdt(pa, paddr, plen)) {
return true;
}
found_table = false;
AcpiUtAcquireMutex(ACPI_MTX_TABLES);
for (i = 0; i < AcpiGbl_RootTableList.CurrentTableCount; i++) {
tdesc = &AcpiGbl_RootTableList.Tables[i];
if (pa >= tdesc->Address &&
pa < tdesc->Address + tdesc->Length) {
*paddr = tdesc->Address;
*plen = tdesc->Length;
found_table = true;
break;
} else if (memcmp(tdesc->Signature.Ascii, ACPI_SIG_TCPA, 4)
== 0) {
if (tcpa != NULL && tcpa_tdesc_len > 0)
AcpiOsUnmapMemory(tcpa, tcpa_tdesc_len);
tcpa = AcpiOsMapMemory(tdesc->Address, tdesc->Length);
if (tcpa != NULL)
tcpa_tdesc_len = tdesc->Length;
}
}
if (!found_table && tcpa != NULL) {
ACPI_PHYSICAL_ADDRESS tcpa_addr = 0;
uint32_t tcpa_len = 0;
if (tcpa->PlatformClass == ACPI_TCPA_CLIENT_TABLE) {
ACPI_TABLE_TCPA_CLIENT *t =
(ACPI_TABLE_TCPA_CLIENT *)(tcpa + 1);
tcpa_addr = t->LogAddress;
tcpa_len = t->MinimumLogLength;
} else if (tcpa->PlatformClass == ACPI_TCPA_SERVER_TABLE) {
ACPI_TABLE_TCPA_SERVER *t =
(ACPI_TABLE_TCPA_SERVER *)(tcpa + 1);
tcpa_addr = t->LogAddress;
tcpa_len = t->MinimumLogLength;
}
if (tcpa_len != 0 &&
pa >= tcpa_addr &&
pa < tcpa_addr + tcpa_len) {
*paddr = tcpa_addr;
*plen = tcpa_len;
found_table = true;
}
}
if (tcpa != NULL && tcpa_tdesc_len != 0)
AcpiOsUnmapMemory(tcpa, tcpa_tdesc_len);
AcpiUtReleaseMutex(ACPI_MTX_TABLES);
return found_table;
}
static int
acpi_read(dev_t dev, struct uio *uio, int flag)
{
ACPI_PHYSICAL_ADDRESS pa, table_pa;
uint32_t table_len;
uint8_t *data;
int error;
size_t len;
if (uio->uio_rw != UIO_READ) {
return EPERM;
}
if (!acpi_find_table(uio->uio_offset, &table_pa, &table_len)) {
return EIO;
}
pa = uio->uio_offset;
len = uimin(table_len - (pa - table_pa), uio->uio_resid);
data = AcpiOsMapMemory(pa, len);
if (data == NULL) {
return ENOMEM;
}
error = uiomove(data, len, uio);
AcpiOsUnmapMemory(data, len);
return error;
}