λ

furix

cute os

settings

kernel/acpi.c

7262 bytes raw

#include <acpi.h>

#include <types.h>

/* ACPI 2.0 RSDP: eb9d2d30-2d88-11d3-9a16-0090273fc14d */
static const uint8_t guid_rsdp_v2[16] = {
  0x30, 0x2D, 0x9D, 0xEB, 0x88, 0x2D, 0xD3, 0x11,
  0x9A, 0x16, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D
};

/* ACPI 1.0 RSDP: 8868e871-e4f1-11d3-bc22-0080c73c8881 */
static const uint8_t guid_rsdp_v1[16] = {
  0x71, 0xE8, 0x68, 0x88, 0xF1, 0xE4, 0xD3, 0x11,
  0xBC, 0x22, 0x00, 0x80, 0xC7, 0x3C, 0x88, 0x81
};

#define MADT_TYPE_LOCAL_APIC 0
#define MADT_TYPE_IO_APIC 1
/* x2APIC: replaces type 0 when the firmware enabled x2APIC mode */
#define MADT_TYPE_X2APIC 9

/* signatures read little-endian via read_u32(): the first four ASCII bytes. */
#define ACPI_SIG_RSDP 0x20445352u /* "RSD " */
#define ACPI_SIG_RSDT 0x54445352u /* "RSDT" */
#define ACPI_SIG_MADT 0x43495041u /* "APIC" */

#define ACPI_SDT_HEADER_SIZE 36

/*
 * the MADT header is 8 bytes longer than a plain SDT header: it appends
 * LocalAPICAddress (4), PCATCompatibility (1), PCATCompatCount (1) and
 * Reserved (2). Structure entries therefore begin at 44, not 36.
 */
#define ACPI_MADT_HEADER_SIZE 44

#define ACPI_RSDT_ENTRY_SIZE 8

/* Local APIC structure: Type, Length, ACPI ID, APIC ID, Flags(4). */
#define MADT_LOCAL_APIC_FLAGS_OFFSET 4
#define MADT_LOCAL_APIC_ENABLED 0x1

/*
 * x2APIC structure: Type, Length, Reserved(2), ACPI UID(4), APIC ID(4),
 * Flags(4), ... so the APIC ID is a full 32-bit field at offset 8 and the
 * enabled flag sits at offset 12. the entry is 16 bytes minimum.
 */
#define MADT_X2APIC_ID_OFFSET 8
#define MADT_X2APIC_FLAGS_OFFSET 12
#define MADT_X2APIC_ENABLED 0x1
#define MADT_X2APIC_MIN_LEN 16

static uint32_t read_u32(const uint8_t *p) {
  return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
}

/* EFI_GUID is { u32, u16, u16, u8[8] } with no padding, so its memory image is
   exactly the 16-byte wire form we compare against. */
static bool guid_equal(const EFI_GUID *guid, const uint8_t *bytes) {
  const uint8_t *p = (const uint8_t *)guid;
  int i;

  for (i = 0; i < 16; i++) {
    if (p[i] != bytes[i]) {
      return false;
    }
  }
  return true;
}

/* an SDT's checksum is the sum of all its bytes, modulo 256, taken to zero. */
static bool checksum_ok(const uint8_t *table, uint32_t len) {
  uint8_t sum = 0;
  uint32_t i;

  for (i = 0; i < len; i++) {
    sum = (uint8_t)(sum + table[i]);
  }
  return sum == 0;
}

static void madt_parse(const uint8_t *madt, uint32_t madt_len, struct acpi_madt_info *out) {
  const uint8_t *entry = madt + ACPI_MADT_HEADER_SIZE;
  const uint8_t *end = madt + madt_len;

  while (entry + 2 <= end) {
    uint8_t type = entry[0];
    uint8_t len = entry[1];

    /* length 0 would not advance us and could not be a valid structure. */
    if (len < 2 || entry + len > end) {
      break;
    }

    if ((type == MADT_TYPE_LOCAL_APIC && len >= 8) ||
        (type == MADT_TYPE_X2APIC && len >= MADT_X2APIC_MIN_LEN)) {
      bool is_x2apic = (type == MADT_TYPE_X2APIC);
      uint32_t flags = read_u32(entry + (is_x2apic ? MADT_X2APIC_FLAGS_OFFSET
                                                  : MADT_LOCAL_APIC_FLAGS_OFFSET));
      uint32_t enabled_bit = is_x2apic ? MADT_X2APIC_ENABLED : MADT_LOCAL_APIC_ENABLED;

      /*
       * bit 0 of flags is "enabled"; skip CPUs the firmware marked unusable.
       *
       * the two entry types are handled together because a MADT uses one or the
       * other, never both, and reading a type 0 entry as type 9 would pick up a
       * 32-bit field spanning the flags and the following entry's header.
       */
      if ((flags & enabled_bit) != 0 && out->cpu_count < ACPI_MAX_APIC_IDS) {
        out->apic_ids[out->cpu_count] =
            is_x2apic ? read_u32(entry + MADT_X2APIC_ID_OFFSET) : (uint32_t)entry[3];

        /*
         * the slot is dense rather than the APIC ID, because an x2APIC ID may
         * exceed the per-CPU array we reserve. the BSP passes the slot to the
         * trampoline, so the AP never has to index anything by ID itself.
         */
        out->slots[out->cpu_count] = (uint8_t)out->cpu_count;
        out->cpu_count++;
      }
    } else if (type == MADT_TYPE_IO_APIC && len >= 12) {
      out->ioapic_count++;
    }

    entry += len;
  }
}

/*
 * locate a table by the signature of the table itself rather than by the
 * Type byte recorded in the RSDT entry. it costs one extra read and needs no
 * trust in the entry metadata, which some firmware images get wrong.
 */
static const uint8_t *rsdt_find_table(const uint8_t *rsdt, uint32_t rsdt_len, uint32_t sig) {
  const uint8_t *entry = rsdt + ACPI_SDT_HEADER_SIZE;
  const uint8_t *end = rsdt + rsdt_len;

  while (entry + ACPI_RSDT_ENTRY_SIZE <= end) {
    const uint8_t *t = (const uint8_t *)(uint64_t)read_u32(entry + 4);

    if (t != NULL && read_u32(t) == sig) {
      return t;
    }
    entry += ACPI_RSDT_ENTRY_SIZE;
  }
  return NULL;
}

bool acpi_madt_parse(const EFI_SYSTEM_TABLE *st, struct acpi_madt_info *out) {
  const uint8_t *rsdp = NULL;
  const uint8_t *rsdt;
  const uint8_t *madt;
  uint32_t rsdt_len;
  uint32_t madt_len;
  UINTN i;

  out->cpu_count = 0;
  out->ioapic_count = 0;

  /* some firmware installs the MADT directly in the UEFI configuration table. */
  for (i = 0; i < st->NumberOfTableEntries; i++) {
    const uint8_t *t = (const uint8_t *)st->ConfigurationTable[i].VendorTable;

    if (t != NULL && read_u32(t) == ACPI_SIG_MADT) {
      madt_len = read_u32(t + 4);
      if (madt_len >= ACPI_MADT_HEADER_SIZE && checksum_ok(t, madt_len)) {
        madt_parse(t, madt_len, out);
        return out->cpu_count > 0;
      }
    }
  }

  for (i = 0; i < st->NumberOfTableEntries; i++) {
    const EFI_CONFIGURATION_TABLE *ct = &st->ConfigurationTable[i];

    if (guid_equal(&ct->VendorGuid, guid_rsdp_v2) || guid_equal(&ct->VendorGuid, guid_rsdp_v1)) {
      rsdp = (const uint8_t *)ct->VendorTable;
      break;
    }
  }

  if (rsdp == NULL || read_u32(rsdp) != ACPI_SIG_RSDP) {
    return false;
  }
  /* signature is 8 bytes: "RSD PTR " -> R(0) S(1) D(2) ' '(3) P(4) T(5) R(6) ' '(7). */
  if (rsdp[4] != 'P' || rsdp[5] != 'T' || rsdp[6] != 'R' || rsdp[7] != ' ') {
    return false;
  }

  /* RsdtPhysicalAddress is a 32-bit field at offset 16. */
  rsdt = (const uint8_t *)(uint64_t)read_u32(rsdp + 16);
  if (rsdt == NULL || read_u32(rsdt) != ACPI_SIG_RSDT) {
    return false;
  }

  rsdt_len = read_u32(rsdt + 4);
  if (rsdt_len < ACPI_SDT_HEADER_SIZE || !checksum_ok(rsdt, rsdt_len)) {
    return false;
  }

  madt = rsdt_find_table(rsdt, rsdt_len, ACPI_SIG_MADT);
  if (madt == NULL) {
    return false;
  }

  madt_len = read_u32(madt + 4);
  if (madt_len < ACPI_MADT_HEADER_SIZE || !checksum_ok(madt, madt_len)) {
    return false;
  }

  madt_parse(madt, madt_len, out);
  return out->cpu_count > 0;
}

uint32_t acpi_madt_ap_count(const struct acpi_madt_info *madt) {
  /*
   * one of the listed processors is the BSP, which is already running. the
   * subtraction is guarded rather than written as `cpu_count - 1`: a MADT with
   * no enabled processor entries would otherwise wrap to UINT32_MAX and send
   * the bring-up loop looking for four billion APs.
   */
  return (madt->cpu_count > 0) ? (madt->cpu_count - 1) : 0;
}