#include <stand.h>
#include <sys/stdint.h>
#include <machine/stdarg.h>
#include <bootstrap.h>
#include <btxv86.h>
#include "libi386.h"
#include "platform/acfreebsd.h"
#include "acconfig.h"
#define ACPI_SYSTEM_XFACE
#include "actypes.h"
#include "actbl.h"
#include "actbl3.h"
ACPI_TABLE_RSDP *rsdp;
static ACPI_TABLE_RSDP *biosacpi_find_rsdp(void);
static ACPI_TABLE_RSDP *biosacpi_search_rsdp(vm_offset_t base, size_t length);
#define RSDP_CHECKSUM_LENGTH 20
static void
biosacpi_setup_spcr(ACPI_TABLE_SPCR *spcr)
{
unsigned baudrate;
const char *port;
char *name, *value;
if (spcr == NULL)
return;
switch (spcr->BaudRate) {
case 0:
baudrate = 0;
break;
case 3:
baudrate = 9600;
break;
case 4:
baudrate = 19200;
break;
case 6:
baudrate = 57600;
break;
case 7:
baudrate = 115200;
break;
default:
return;
}
port = NULL;
name = NULL;
value = NULL;
switch (spcr->SerialPort.SpaceId) {
case ACPI_ADR_SPACE_SYSTEM_IO:
if (baudrate == 0)
baudrate = comc_getspeed(spcr->SerialPort.Address);
if (asprintf(&value, "%u,8,N,1,-", baudrate) < 0)
return;
switch (spcr->SerialPort.Address) {
case 0x3F8:
port = "ttya";
break;
case 0x2F8:
port = "ttyb";
break;
case 0x3E8:
port = "ttyc";
break;
case 0x2E8:
port = "ttyd";
break;
default:
break;
}
break;
default:
break;
}
if (port != NULL) {
if (asprintf(&name, "%s,text", port) > 0) {
setenv("console", name, 1);
free(name);
}
if (asprintf(&name, "%s-mode", port) > 0) {
setenv(name, value, 1);
free(name);
}
if (asprintf(&name, "%s-spcr-mode", port) > 0) {
setenv(name, value, 1);
free(name);
}
}
free(value);
}
void
biosacpi_detect(void)
{
char buf[24];
int revision;
if ((rsdp = biosacpi_find_rsdp()) == NULL)
return;
sprintf(buf, "0x%08x", (unsigned int)VTOP(rsdp));
setenv("acpi.rsdp", buf, 1);
revision = rsdp->Revision;
if (revision == 0)
revision = 1;
snprintf(buf, sizeof (buf), "%d", revision);
setenv("acpi.revision", buf, 1);
strncpy(buf, rsdp->OemId, sizeof (rsdp->OemId));
buf[sizeof (rsdp->OemId)] = '\0';
setenv("acpi.oem", buf, 1);
snprintf(buf, sizeof (buf), "0x%08x", rsdp->RsdtPhysicalAddress);
setenv("acpi.rsdt", buf, 1);
if (revision >= 2) {
snprintf(buf, sizeof (buf), "0x%016llx",
(unsigned long long)rsdp->XsdtPhysicalAddress);
setenv("acpi.xsdt", buf, 1);
sprintf(buf, "%d", rsdp->Length);
setenv("acpi.xsdt_length", buf, 1);
}
biosacpi_setup_spcr(acpi_find_table(ACPI_SIG_SPCR));
}
static void *
acpi_map_sdt(vm_offset_t addr)
{
return ((void *)PTOV(addr));
}
static uint8_t
acpi_checksum(const void *p, size_t length)
{
const uint8_t *bp;
uint8_t sum;
bp = p;
sum = 0;
while (length--)
sum += *bp++;
return (sum);
}
static ACPI_TABLE_RSDP *
biosacpi_find_rsdp(void)
{
ACPI_TABLE_RSDP *rsdp;
uint16_t *addr;
addr = acpi_map_sdt(0x40E);
rsdp = biosacpi_search_rsdp((vm_offset_t)(*addr << 4), 0x400);
if (rsdp != NULL)
return (rsdp);
if ((rsdp = biosacpi_search_rsdp(0xe0000, 0x20000)) != NULL)
return (rsdp);
return (NULL);
}
static ACPI_TABLE_RSDP *
biosacpi_search_rsdp(vm_offset_t base, size_t length)
{
ACPI_TABLE_RSDP *rsdp;
size_t ofs, namelen;
namelen = strlen(ACPI_SIG_RSDP);
for (ofs = 0; ofs < length; ofs += 16) {
rsdp = acpi_map_sdt(base + ofs);
if (memcmp(rsdp->Signature, ACPI_SIG_RSDP, namelen) == 0) {
if (acpi_checksum(rsdp, RSDP_CHECKSUM_LENGTH))
continue;
return (rsdp);
}
}
return (NULL);
}
void *
acpi_find_table(const char *sig)
{
uint_t entries, i;
ACPI_TABLE_HEADER *sdp;
ACPI_TABLE_RSDT *rsdt;
ACPI_TABLE_XSDT *xsdt;
if (rsdp == NULL)
return (NULL);
if (rsdp->Revision >= 2 &&
rsdp->XsdtPhysicalAddress != 0 &&
rsdp->XsdtPhysicalAddress < UINTPTR_MAX) {
xsdt = acpi_map_sdt(rsdp->XsdtPhysicalAddress);
sdp = (ACPI_TABLE_HEADER *)xsdt;
if (sdp->Length < sizeof (ACPI_TABLE_HEADER)) {
entries = 0;
} else {
entries = sdp->Length - sizeof (ACPI_TABLE_HEADER);
entries /= ACPI_XSDT_ENTRY_SIZE;
}
for (i = 0; i < entries; i++) {
if (xsdt->TableOffsetEntry[i] == 0 ||
xsdt->TableOffsetEntry[i] >= UINTPTR_MAX)
continue;
sdp = acpi_map_sdt(xsdt->TableOffsetEntry[i]);
if (sdp->Length < sizeof (ACPI_TABLE_HEADER))
continue;
if (acpi_checksum(sdp, sdp->Length))
continue;
if (ACPI_COMPARE_NAME(sig, sdp->Signature))
return (sdp);
}
}
if (rsdp->RsdtPhysicalAddress != 0) {
rsdt = acpi_map_sdt(rsdp->RsdtPhysicalAddress);
sdp = (ACPI_TABLE_HEADER *)rsdt;
if (sdp->Length < sizeof (ACPI_TABLE_HEADER)) {
entries = 0;
} else {
entries = sdp->Length - sizeof (ACPI_TABLE_HEADER);
entries /= ACPI_RSDT_ENTRY_SIZE;
}
for (i = 0; i < entries; i++) {
if (rsdt->TableOffsetEntry[i] == 0)
continue;
sdp = acpi_map_sdt(rsdt->TableOffsetEntry[i]);
if (sdp->Length < sizeof (ACPI_TABLE_HEADER))
continue;
if (acpi_checksum(sdp, sdp->Length))
continue;
if (ACPI_COMPARE_NAME(sig, sdp->Signature))
return (sdp);
}
}
return (NULL);
}