MonkOS  v0.1
A simple 64-bit operating system (x86_64)
acpi.c
Go to the documentation of this file.
1 //============================================================================
2 /// @file acpi.c
3 /// @brief Advanced configuration and power interface (ACPI) tables.
4 //
5 // Copyright 2016 Brett Vickers.
6 // Use of this source code is governed by a BSD-style license
7 // that can be found in the MonkOS LICENSE file.
8 //============================================================================
9 
10 #include <core.h>
11 #include <libc/string.h>
12 #include <kernel/debug/log.h>
13 #include <kernel/mem/acpi.h>
14 #include <kernel/mem/kmem.h>
15 #include <kernel/mem/paging.h>
16 #include <kernel/mem/pmap.h>
17 #include <kernel/x86/cpu.h>
18 
19 #define SIGNATURE_RSDP 0x2052545020445352ll // "RSD PTR "
20 #define SIGNATURE_MADT 0x43495041 // "APIC"
21 #define SIGNATURE_BOOT 0x544f4f42 // "BOOT"
22 #define SIGNATURE_FADT 0x50434146 // "FACP"
23 #define SIGNATURE_HPET 0x54455048 // "HPET"
24 #define SIGNATURE_MCFG 0x4746434d // "MCFG"
25 #define SIGNATURE_SRAT 0x54415253 // "SRAT"
26 #define SIGNATURE_SSDT 0x54445353 // "SSDT"
27 #define SIGNATURE_WAET 0x54454157 // "WAET"
28 
29 // Page alignment macros
30 #define PAGE_ALIGN_DOWN(a) ((a) & ~(PAGE_SIZE - 1))
31 #define PAGE_ALIGN_UP(a) (((a) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))
32 
33 /// A structure used to track the state of the temporary page table generated
34 /// by the boot loader. The ACPI code updates it to access the memory stored
35 /// in ACPI tables.
36 typedef struct btable
37 {
38  page_t *root; ///< The top-level (PML4) page table
39  page_t *next_page; ///< The next page to use when allocating
40  page_t *term_page; ///< Just beyond the last available page
41 } btable_t;
42 
43 struct acpi_rsdp
44 {
45  char signature[8]; ///< Contains "RSD PTR "
46  uint8_t checksum; ///< Covers up to (and including) ptr_rsdt
47  char oemid[6]; ///< Supplied by the OEM
48  uint8_t revision; ///< 0=1.0, 1=2.0, 2=3.0
49  uint32_t ptr_rsdt; ///< 32-bit pointer to RSDT table
50 
51  // The following fields do not exist in ACPI1.0
52  uint32_t length; ///< RSDT table length, including header
53  uint64_t ptr_xsdt; ///< 64-bit pointer to XSDT table
54  uint8_t checksum_ex; ///< Covers entire rsdp structure
55  uint8_t reserved[3];
56 } PACKSTRUCT;
57 
58 struct acpi_rsdt
59 {
60  struct acpi_hdr hdr;
61  uint32_t ptr_table[1]; ///< Pointers to other ACPI tables
62 } PACKSTRUCT;
63 
64 struct acpi_xsdt
65 {
66  struct acpi_hdr hdr;
67  uint64_t ptr_table[1]; ///< Pointers to other ACPI tables
68 } PACKSTRUCT;
69 
70 struct acpi
71 {
72  int version; // ACPI version (1, 2 or 3)
73  const struct acpi_rsdp *rsdp;
74  const struct acpi_rsdt *rsdt;
75  const struct acpi_xsdt *xsdt;
76  const struct acpi_fadt *fadt;
77  const struct acpi_madt *madt;
78  const struct acpi_mcfg *mcfg;
79 };
80 
81 static struct acpi acpi;
82 
83 static void
84 read_fadt(const struct acpi_hdr *hdr)
85 {
86  const struct acpi_fadt *fadt = (const struct acpi_fadt *)hdr;
87  acpi.fadt = fadt;
88 }
89 
90 static void
91 read_madt(const struct acpi_hdr *hdr)
92 {
93  const struct acpi_madt *madt = (const struct acpi_madt *)hdr;
94  acpi.madt = madt;
95 }
96 
97 static void
98 read_mcfg(const struct acpi_hdr *hdr)
99 {
100  const struct acpi_mcfg *mcfg = (const struct acpi_mcfg *)hdr;
101  acpi.mcfg = mcfg;
102 }
103 
104 static void
105 read_table(const struct acpi_hdr *hdr)
106 {
107  switch (hdr->signature.dword)
108  {
109  case SIGNATURE_FADT:
110  read_fadt(hdr); break;
111 
112  case SIGNATURE_MADT:
113  read_madt(hdr); break;
114 
115  case SIGNATURE_MCFG:
116  read_mcfg(hdr); break;
117 
118  default:
119  break;
120  }
121 }
122 
123 static bool
124 is_mapped(btable_t *btable, uint64_t addr)
125 {
126  uint64_t pml4te = PML4E(addr);
127  uint64_t pdpte = PDPTE(addr);
128  uint64_t pde = PDE(addr);
129  uint64_t pte = PTE(addr);
130 
131  page_t *pml4t = btable->root;
132  if (pml4t->entry[pml4te] == 0)
133  return false;
134 
135  page_t *pdpt = PGPTR(pml4t->entry[pml4te]);
136  if (pdpt->entry[pdpte] == 0)
137  return false;
138  if (pdpt->entry[pdpte] & PF_PS)
139  return true;
140 
141  page_t *pdt = PGPTR(pdpt->entry[pdpte]);
142  if (pdt->entry[pde] == 0)
143  return false;
144  if (pdt->entry[pde] & PF_PS)
145  return true;
146 
147  page_t *pt = PGPTR(pdt->entry[pde]);
148  return pt->entry[pte] != 0;
149 }
150 
151 static uint64_t
153 {
154  if (btable->next_page == btable->term_page)
155  fatal();
156 
157  page_t *page = btable->next_page++;
158  memzero(page, sizeof(page_t));
159  return (uint64_t)page | PF_PRESENT | PF_RW;
160 }
161 
162 static void
163 create_page(btable_t *btable, uint64_t addr, uint64_t flags)
164 {
165  uint64_t pml4te = PML4E(addr);
166  uint64_t pdpte = PDPTE(addr);
167  uint64_t pde = PDE(addr);
168  uint64_t pte = PTE(addr);
169 
170  page_t *pml4t = btable->root;
171  if (pml4t->entry[pml4te] == 0)
172  pml4t->entry[pml4te] = alloc_page(btable);
173 
174  page_t *pdpt = PGPTR(pml4t->entry[pml4te]);
175  if (pdpt->entry[pdpte] == 0)
176  pdpt->entry[pdpte] = alloc_page(btable);
177 
178  page_t *pdt = PGPTR(pdpt->entry[pdpte]);
179  if (pdt->entry[pde] == 0)
180  pdt->entry[pde] = alloc_page(btable);
181 
182  page_t *pt = PGPTR(pdt->entry[pde]);
183  pt->entry[pte] = addr | flags;
184 }
185 
186 static void
187 map_range(btable_t *btable, uint64_t addr, uint64_t size, uint64_t flags)
188 {
189  // Calculate the page-aligned extents of the block of memory.
190  uint64_t begin = PAGE_ALIGN_DOWN(addr);
191  uint64_t term = PAGE_ALIGN_UP(addr + size);
192 
193  // If necessary, create new pages in the boot page table to cover the
194  // address range.
195  for (uint64_t addr = begin; addr < term; addr += PAGE_SIZE) {
196  if (!is_mapped(btable, addr))
197  create_page(btable, addr, flags);
198  }
199 }
200 
201 static void
202 map_table(btable_t *btable, const struct acpi_hdr *hdr)
203 {
204  uint64_t addr = (uint64_t)hdr;
205  uint64_t flags = PF_PRESENT | PF_RW;
206 
207  // First map the header itself, since we can't read its length until
208  // it's mapped.
209  map_range(btable, addr, sizeof(struct acpi_hdr), flags);
210 
211  // Now that we can read the header's length, map the entire ACPI table.
212  uint64_t size = hdr->length;
213  map_range(btable, addr, size, flags);
214 
215  // Calculate the page-aligned extents of the ACPI table, and add them to
216  // the BIOS-generated memory table.
218  PAGE_ALIGN_UP(addr + hdr->length) - PAGE_ALIGN_DOWN(addr),
219  PMEMTYPE_ACPI);
220 }
221 
222 static void
224 {
225  const struct acpi_xsdt *xsdt = acpi.xsdt;
226  const struct acpi_hdr *xhdr = &xsdt->hdr;
227 
228  logf(LOG_INFO,
229  "[acpi] oem='%.6s' tbl='%.8s' rev=%#x creator='%.4s'",
230  xhdr->oemid, xhdr->oemtableid, xhdr->oemrevision, xhdr->creatorid);
231 
232  // Read each of the tables referenced by the XSDT table.
233  int tables = (int)(xhdr->length - sizeof(*xhdr)) / sizeof(uint64_t);
234  for (int i = 0; i < tables; i++) {
235  const struct acpi_hdr *hdr =
236  (const struct acpi_hdr *)xsdt->ptr_table[i];
237  map_table(btable, hdr);
238  logf(LOG_INFO, "[acpi] Found %.4s table at %#lx.",
239  hdr->signature.bytes, (uint64_t)hdr);
240  read_table(hdr);
241  }
242 }
243 
244 static void
246 {
247  const struct acpi_rsdt *rsdt = acpi.rsdt;
248  const struct acpi_hdr *rhdr = &rsdt->hdr;
249 
250  logf(LOG_INFO,
251  "[acpi] oem='%.6s' tbl='%.8s' rev=%#x creator='%.4s'",
252  rhdr->oemid, rhdr->oemtableid, rhdr->oemrevision, rhdr->creatorid);
253 
254  // Read each of the tables referenced by the RSDT table.
255  int tables = (int)(rhdr->length - sizeof(*rhdr)) / sizeof(uint32_t);
256  for (int i = 0; i < tables; i++) {
257  const struct acpi_hdr *hdr =
258  (const struct acpi_hdr *)(uintptr_t)rsdt->ptr_table[i];
259  map_table(btable, hdr);
260  logf(LOG_INFO, "[acpi] Found %.4s table at %#lx.",
261  hdr->signature.bytes, (uint64_t)hdr);
262  read_table(hdr);
263  }
264 }
265 
266 static const struct acpi_rsdp *
267 find_rsdp(uint64_t addr, uint64_t size)
268 {
269  // Scan memory for the 8-byte RSDP signature. It's guaranteed to be
270  // aligned on a 16-byte boundary.
271  const uint64_t *ptr = (const uint64_t *)addr;
272  const uint64_t *term = (const uint64_t *)(addr + size);
273  for (; ptr < term; ptr += 2) {
274  if (*ptr == SIGNATURE_RSDP)
275  return (const struct acpi_rsdp *)ptr;
276  }
277  return NULL;
278 }
279 
280 void
282 {
283  // Initialize the state of the temporary page table generated by the boot
284  // loader. We'll be updating it as we scan ACPI tables.
285  btable_t btable =
286  {
288  .next_page = (page_t *)KMEM_BOOT_PAGETABLE_LOADED,
289  .term_page = (page_t *)KMEM_BOOT_PAGETABLE_END,
290  };
291 
292  // Scan the extended BIOS and system ROM memory regions for the ACPI RSDP
293  // table.
295  if (acpi.rsdp == NULL)
297 
298  // Fatal out if the ACPI tables could not be found.
299  if (acpi.rsdp == NULL) {
300  logf(LOG_CRIT, "[acpi] No ACPI tables found.");
301  fatal();
302  }
303 
304  acpi.version = acpi.rsdp->revision + 1;
305  logf(LOG_INFO, "[acpi] ACPI %d.0 RSDP table found at %#lx.",
306  acpi.version, (uintptr_t)acpi.rsdp);
307 
308  // Prefer the ACPI2.0 XSDT table for finding all other tables.
309  if (acpi.version > 1) {
310  acpi.xsdt = (const struct acpi_xsdt *)acpi.rsdp->ptr_xsdt;
311  if (acpi.xsdt == NULL) {
312  logf(LOG_INFO, "[acpi] No XSDT table found.");
313  }
314  else {
315  logf(LOG_INFO, "[acpi] Found XSDT table at %#lx.",
316  (uintptr_t)acpi.xsdt);
317  map_table(&btable, &acpi.xsdt->hdr);
318  read_xsdt(&btable);
319  }
320  }
321 
322  // Fall back to the ACPI1.0 RSDT table if XSDT isn't available.
323  if (acpi.xsdt == NULL) {
324  acpi.rsdt = (const struct acpi_rsdt *)(uintptr_t)acpi.rsdp->ptr_rsdt;
325  if (acpi.rsdt == NULL) {
326  logf(LOG_CRIT, "[acpi] No RSDT table found.");
327  fatal();
328  }
329  else {
330  logf(LOG_INFO, "[acpi] Found RSDT table at %#lx.",
331  (uintptr_t)acpi.rsdt);
332  map_table(&btable, &acpi.rsdt->hdr);
333  read_rsdt(&btable);
334  }
335  }
336 
337  // Reserve local APIC memory-mapped I/O addresses.
338  if (acpi.madt != NULL) {
341  }
342 
343  // Reserve I/O APIC memory-mapped I/O addresses.
344  const struct acpi_madt_io_apic *io = NULL;
345  while ((io = acpi_next_io_apic(io)) != NULL) {
348  }
349 }
350 
351 int
353 {
354  return acpi.version;
355 }
356 
357 const struct acpi_fadt *
359 {
360  return acpi.fadt;
361 }
362 
363 const struct acpi_madt *
365 {
366  return acpi.madt;
367 }
368 
369 static const void *
370 madt_find(enum acpi_madt_type type, const void *prev)
371 {
372  const struct acpi_madt *madt = acpi.madt;
373  if (madt == NULL)
374  return NULL;
375 
376  const void *term = (const uint8_t *)madt + madt->hdr.length;
377 
378  const void *ptr;
379  if (prev == NULL) {
380  ptr = madt + 1;
381  }
382  else {
383  ptr = (const uint8_t *)prev +
384  ((const struct acpi_madt_hdr *)prev)->length;
385  }
386 
387  while (ptr < term) {
388  const struct acpi_madt_hdr *hdr = (const struct acpi_madt_hdr *)ptr;
389  if (hdr->type == type)
390  return hdr;
391  ptr = (const uint8_t *)hdr + hdr->length;
392  }
393 
394  return NULL;
395 }
396 
397 const struct acpi_madt_local_apic *
399 {
400  return (const struct acpi_madt_local_apic *)madt_find(
401  ACPI_MADT_LOCAL_APIC, prev);
402 }
403 
404 const struct acpi_madt_io_apic *
406 {
407  return (const struct acpi_madt_io_apic *)madt_find(
408  ACPI_MADT_IO_APIC, prev);
409 }
410 
411 const struct acpi_madt_iso *
412 acpi_next_iso(const struct acpi_madt_iso *prev)
413 {
414  return (const struct acpi_madt_iso *)madt_find(
415  ACPI_MADT_ISO, prev);
416 }
417 
418 const struct acpi_mcfg_addr *
420 {
421  const struct acpi_mcfg *mcfg = acpi.mcfg;
422  if (mcfg == NULL)
423  return NULL;
424 
425  const struct acpi_mcfg_addr *ptr;
426  if (prev == NULL)
427  ptr = (const struct acpi_mcfg_addr *)(mcfg + 1);
428  else
429  ptr = prev + 1;
430 
431  const uint8_t *term = (const uint8_t *)mcfg + mcfg->hdr.length;
432  if ((const uint8_t *)ptr < term)
433  return ptr;
434  else
435  return NULL;
436 }
MADT I/O APIC entry.
Definition: acpi.h:168
#define SIGNATURE_MADT
Definition: acpi.c:20
#define KMEM_BOOT_PAGETABLE
Definition: kmem.h:19
struct acpi_rsdp PACKSTRUCT
uint32_t length
Length of this table including header.
Definition: acpi.h:25
MADT local APIC entry.
Definition: acpi.h:155
uint32_t ptr_io_apic
I/O APIC address.
Definition: acpi.h:174
Physical memory map describing usable and reserved regions of physical memory.
char oemid[6]
Supplied by the OEM.
Definition: acpi.h:28
const struct acpi_xsdt * xsdt
Definition: acpi.c:75
Header attached to the front of all ACPI tables.
Definition: acpi.h:18
union acpi_hdr::@1 signature
Advanced configuration and power interface (ACPI) tables.
String and memory operations.
uint8_t checksum_ex
Covers entire rsdp structure.
Definition: acpi.c:54
#define KMEM_BOOT_PAGETABLE_LOADED
Definition: kmem.h:20
#define KMEM_BOOT_PAGETABLE_END
Definition: kmem.h:21
uint32_t ptr_rsdt
32-bit pointer to RSDT table
Definition: acpi.c:49
uint32_t ptr_table[1]
Pointers to other ACPI tables.
Definition: acpi.c:61
uint32_t length
RSDT table length, including header.
Definition: acpi.c:52
#define PF_PRESENT
Definition: paging.h:20
static void create_page(btable_t *btable, uint64_t addr, uint64_t flags)
Definition: acpi.c:163
#define SIGNATURE_FADT
Definition: acpi.c:22
#define SIGNATURE_MCFG
Definition: acpi.c:24
#define PTE(a)
Definition: paging.h:43
Core include file.
char creatorid[4]
Vendor id.
Definition: acpi.h:31
#define KMEM_EXTENDED_BIOS_SIZE
Definition: kmem.h:42
Kernel physical (and virtual) memory map.
Interrupt Source Override.
Definition: acpi.h:128
#define KMEM_EXTENDED_BIOS
Definition: kmem.h:31
uint8_t length
Length of IC structure including header.
Definition: acpi.h:148
struct acpi_hdr hdr
Definition: acpi.h:41
Informational message.
Definition: log.h:24
Marked as uncacheable, usually for I/O.
Definition: pmap.h:28
static bool is_mapped(btable_t *btable, uint64_t addr)
Definition: acpi.c:124
static void map_range(btable_t *btable, uint64_t addr, uint64_t size, uint64_t flags)
Definition: acpi.c:187
#define PML4E(a)
Definition: paging.h:40
struct acpi_hdr hdr
Definition: acpi.h:114
const struct acpi_madt_local_apic * acpi_next_local_apic(const struct acpi_madt_local_apic *prev)
Return a pointer to the next Local APIC structure entry in the MADT table.
Definition: acpi.c:398
static void read_mcfg(const struct acpi_hdr *hdr)
Definition: acpi.c:98
uint32_t ptr_local_apic
Local APIC address.
Definition: acpi.h:116
Used for ACPI tables or code.
Definition: pmap.h:25
static void read_madt(const struct acpi_hdr *hdr)
Definition: acpi.c:91
static void map_table(btable_t *btable, const struct acpi_hdr *hdr)
Definition: acpi.c:202
MCFG entry, one or more of which appears at the tail of the acpi_mcfg struct.
Definition: acpi.h:99
Processor Local APIC.
Definition: acpi.h:126
static void read_rsdt(btable_t *btable)
Definition: acpi.c:245
char oemtableid[8]
Supplied by the OEM.
Definition: acpi.h:29
__forceinline void fatal()
Definition: cpu_inl.h:158
PCI express Mapped Configuration (MCFG) table.
Definition: acpi.h:87
uint64_t ptr_xsdt
64-bit pointer to XSDT table
Definition: acpi.c:53
const struct acpi_rsdp * rsdp
Definition: acpi.c:73
const struct acpi_rsdt * rsdt
Definition: acpi.c:74
uint32_t oemrevision
Supplied by the OEM.
Definition: acpi.h:30
page_t * next_page
The next page to use when allocating.
Definition: acpi.c:39
#define PAGE_ALIGN_UP(a)
Definition: acpi.c:31
Definition: acpi.c:64
A pagetable page record.
Definition: paging.h:54
Paged memory management.
void acpi_init()
Find and parse all available ACPI tables.
Definition: acpi.c:281
Fixed ACPI Description Table (FADT)
Definition: acpi.h:39
const struct acpi_madt * madt
Definition: acpi.c:77
#define PF_PS
Definition: paging.h:27
#define PAGE_SIZE
Definition: paging.h:15
#define PAGE_ALIGN_DOWN(a)
Definition: acpi.c:30
int version
Definition: acpi.c:72
uint8_t revision
0=1.0, 1=2.0, 2=3.0
Definition: acpi.c:48
void pmap_add(uint64_t addr, uint64_t size, enum pmemtype type)
Add a region of memory to the physical memory map.
Definition: pmap.c:322
const struct acpi_mcfg_addr * acpi_next_mcfg_addr(const struct acpi_mcfg_addr *prev)
Return a pointer to the next Mapped Configuration (MCFG) address entry, used for PCIe.
Definition: acpi.c:419
uint64_t entry[PAGE_SIZE/sizeof(uint64_t)]
Definition: paging.h:56
MADT entry header.
Definition: acpi.h:145
static const void * madt_find(enum acpi_madt_type type, const void *prev)
Definition: acpi.c:370
A structure used to track the state of the temporary page table generated by the boot loader...
Definition: acpi.c:36
void logf(loglevel_t level, const char *format,...)
Log a printf-formatted message to the kernel log buffer.
Definition: log.c:195
x86 CPU-specific function implementations.
uint8_t type
acpi_madt_type
Definition: acpi.h:147
struct acpi_hdr hdr
Definition: acpi.c:66
MADT Interrupt Source Override (ISO) entry.
Definition: acpi.h:182
uint64_t ptr_table[1]
Pointers to other ACPI tables.
Definition: acpi.c:67
Critical error, occurs just prior to crashing.
Definition: log.h:21
static const struct acpi_rsdp * find_rsdp(uint64_t addr, uint64_t size)
Definition: acpi.c:267
Definition: acpi.c:43
Definition: acpi.c:58
page_t * root
The top-level (PML4) page table.
Definition: acpi.c:38
const struct acpi_madt * acpi_madt()
Return a pointer to the ACPI multiple APIC description table (MADT).
Definition: acpi.c:364
const struct acpi_madt_io_apic * acpi_next_io_apic(const struct acpi_madt_io_apic *prev)
Return a pointer to the next I/O APIC structure entry in the MADT table.
Definition: acpi.c:405
uint8_t checksum
Covers up to (and including) ptr_rsdt.
Definition: acpi.c:46
#define PF_RW
Definition: paging.h:21
const struct acpi_mcfg * mcfg
Definition: acpi.c:78
int acpi_version()
Return the ACPI version number.
Definition: acpi.c:352
#define PDPTE(a)
Definition: paging.h:41
Multiple APIC description table (MADT).
Definition: acpi.h:112
const struct acpi_fadt * acpi_fadt()
Return a pointer to the ACPI fixed ACPI description table.
Definition: acpi.c:358
const struct acpi_fadt * fadt
Definition: acpi.c:76
void * memzero(void *dst, size_t num)
Fill a region of memory with zeroes.
I/O APIC.
Definition: acpi.h:127
const struct acpi_madt_iso * acpi_next_iso(const struct acpi_madt_iso *prev)
Return a pointer to the next Interrupt Source Override (ISO) structure entry in the MADT table...
Definition: acpi.c:412
static uint64_t alloc_page(btable_t *btable)
Definition: acpi.c:152
static void read_fadt(const struct acpi_hdr *hdr)
Definition: acpi.c:84
static void read_table(const struct acpi_hdr *hdr)
Definition: acpi.c:105
#define KMEM_SYSTEM_ROM_SIZE
Definition: kmem.h:44
#define KMEM_SYSTEM_ROM
Definition: kmem.h:33
struct acpi_hdr hdr
Definition: acpi.c:60
struct acpi_hdr hdr
Definition: acpi.h:89
Definition: acpi.c:70
acpi_madt_type
MADT entry types.
Definition: acpi.h:124
#define SIGNATURE_RSDP
Definition: acpi.c:19
static void read_xsdt(btable_t *btable)
Definition: acpi.c:223
#define PDE(a)
Definition: paging.h:42
#define PGPTR(pte)
Definition: paging.h:46
page_t * term_page
Just beyond the last available page.
Definition: acpi.c:40