aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2020-04-30 13:00:20 -0400
committerTom Rini <trini@konsulko.com>2020-04-30 13:00:20 -0400
commit9f0a6df3a57469061582c6b27fc869829681beca (patch)
tree80b1a3707a5a4f6fd1d9db03ce24e12e0d47b781
parent6d7dacf726ca043a3f5487549bbfa506c990c813 (diff)
parent249154672d43db6c7978fd9b67d224e9dec09867 (diff)
downloadu-boot-9f0a6df3a57469061582c6b27fc869829681beca.zip
u-boot-9f0a6df3a57469061582c6b27fc869829681beca.tar.gz
u-boot-9f0a6df3a57469061582c6b27fc869829681beca.tar.bz2
Merge https://gitlab.denx.de/u-boot/custodians/u-boot-x86
- DM ACPI support (Part A) - Improve support for chain-loading x86 U-Boot
-rw-r--r--arch/sandbox/dts/test.dts4
-rw-r--r--arch/sandbox/include/asm/global_data.h1
-rw-r--r--arch/x86/cpu/apollolake/fsp_s.c2
-rw-r--r--arch/x86/cpu/coreboot/tables.c24
-rw-r--r--arch/x86/cpu/cpu.c4
-rw-r--r--arch/x86/cpu/i386/cpu.c27
-rw-r--r--arch/x86/cpu/i386/interrupt.c6
-rw-r--r--arch/x86/cpu/start_from_spl.S16
-rw-r--r--arch/x86/include/asm/coreboot_tables.h7
-rw-r--r--arch/x86/include/asm/global_data.h1
-rw-r--r--arch/x86/lib/acpi_table.c242
-rw-r--r--arch/x86/lib/fsp/fsp_dram.c8
-rw-r--r--arch/x86/lib/fsp/fsp_graphics.c3
-rw-r--r--arch/x86/lib/fsp2/fsp_dram.c10
-rw-r--r--arch/x86/lib/fsp2/fsp_init.c2
-rw-r--r--arch/x86/lib/init_helpers.c3
-rw-r--r--cmd/Kconfig14
-rw-r--r--cmd/Makefile1
-rw-r--r--cmd/acpi.c186
-rw-r--r--doc/arch/x86.rst28
-rw-r--r--doc/device-tree-bindings/device.txt36
-rw-r--r--drivers/core/acpi.c62
-rw-r--r--drivers/pci/pci-uclass.c4
-rw-r--r--include/acpi/acpi_table.h65
-rw-r--r--include/asm-generic/global_data.h1
-rw-r--r--include/cbfs.h2
-rw-r--r--include/dm/acpi.h37
-rw-r--r--include/init.h2
-rw-r--r--lib/acpi/acpi_table.c179
-rw-r--r--test/dm/acpi.c238
30 files changed, 983 insertions, 232 deletions
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index df9f183..4bccfbe 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -226,6 +226,10 @@
compatible = "denx,u-boot-acpi-test";
};
+ acpi-test2 {
+ compatible = "denx,u-boot-acpi-test";
+ };
+
clocks {
clk_fixed: clk-fixed {
compatible = "fixed-clock";
diff --git a/arch/sandbox/include/asm/global_data.h b/arch/sandbox/include/asm/global_data.h
index f4ce72d..f95ddb0 100644
--- a/arch/sandbox/include/asm/global_data.h
+++ b/arch/sandbox/include/asm/global_data.h
@@ -13,6 +13,7 @@
struct arch_global_data {
uint8_t *ram_buf; /* emulated RAM buffer */
void *text_base; /* pointer to base of text region */
+ ulong acpi_start; /* Start address of ACPI tables */
};
#include <asm-generic/global_data.h>
diff --git a/arch/x86/cpu/apollolake/fsp_s.c b/arch/x86/cpu/apollolake/fsp_s.c
index 17cf168..7ef169b 100644
--- a/arch/x86/cpu/apollolake/fsp_s.c
+++ b/arch/x86/cpu/apollolake/fsp_s.c
@@ -566,6 +566,8 @@ int arch_fsp_init_r(void)
struct udevice *dev, *itss;
int ret;
+ if (!ll_boot_init())
+ return 0;
/*
* This must be called before any devices are probed. Put any probing
* into arch_fsps_preinit() above.
diff --git a/arch/x86/cpu/coreboot/tables.c b/arch/x86/cpu/coreboot/tables.c
index 37e0424..0f04c4f 100644
--- a/arch/x86/cpu/coreboot/tables.c
+++ b/arch/x86/cpu/coreboot/tables.c
@@ -115,20 +115,11 @@ __weak void cb_parse_unhandled(u32 tag, unsigned char *ptr)
static int cb_parse_header(void *addr, int len, struct sysinfo_t *info)
{
+ unsigned char *ptr = addr;
struct cb_header *header;
- unsigned char *ptr = (unsigned char *)addr;
int i;
- for (i = 0; i < len; i += 16, ptr += 16) {
- header = (struct cb_header *)ptr;
- if (!strncmp((const char *)header->signature, "LBIO", 4))
- break;
- }
-
- /* We walked the entire space and didn't find anything. */
- if (i >= len)
- return -1;
-
+ header = (struct cb_header *)ptr;
if (!header->table_bytes)
return 0;
@@ -231,10 +222,13 @@ static int cb_parse_header(void *addr, int len, struct sysinfo_t *info)
int get_coreboot_info(struct sysinfo_t *info)
{
- int ret = cb_parse_header((void *)0x00000000, 0x1000, info);
+ long addr;
+ int ret;
- if (ret != 1)
- ret = cb_parse_header((void *)0x000f0000, 0x1000, info);
+ addr = locate_coreboot_table();
+ if (addr < 0)
+ return addr;
+ ret = cb_parse_header((void *)addr, 0x1000, info);
- return (ret == 1) ? 0 : -1;
+ return ret == 1 ? 0 : -ENOENT;
}
diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c
index cec04b4..8526e85 100644
--- a/arch/x86/cpu/cpu.c
+++ b/arch/x86/cpu/cpu.c
@@ -239,8 +239,10 @@ int cpu_init_r(void)
struct udevice *dev;
int ret;
- if (!ll_boot_init())
+ if (!ll_boot_init()) {
+ uclass_first_device(UCLASS_PCI, &dev);
return 0;
+ }
ret = x86_init_cpus();
if (ret)
diff --git a/arch/x86/cpu/i386/cpu.c b/arch/x86/cpu/i386/cpu.c
index c8da7f1..0312a26 100644
--- a/arch/x86/cpu/i386/cpu.c
+++ b/arch/x86/cpu/i386/cpu.c
@@ -447,10 +447,37 @@ int x86_cpu_init_f(void)
return 0;
}
+long detect_coreboot_table_at(ulong start, ulong size)
+{
+ u32 *ptr, *end;
+
+ size /= 4;
+ for (ptr = (void *)start, end = ptr + size; ptr < end; ptr += 4) {
+ if (*ptr == 0x4f49424c) /* "LBIO" */
+ return (long)ptr;
+ }
+
+ return -ENOENT;
+}
+
+long locate_coreboot_table(void)
+{
+ long addr;
+
+ /* We look for LBIO in the first 4K of RAM and again at 960KB */
+ addr = detect_coreboot_table_at(0x0, 0x1000);
+ if (addr < 0)
+ addr = detect_coreboot_table_at(0xf0000, 0x1000);
+
+ return addr;
+}
+
int x86_cpu_reinit_f(void)
{
setup_identity();
setup_pci_ram_top();
+ if (locate_coreboot_table() >= 0)
+ gd->flags |= GD_FLG_SKIP_LL_INIT;
return 0;
}
diff --git a/arch/x86/cpu/i386/interrupt.c b/arch/x86/cpu/i386/interrupt.c
index 4c7e9ea..e67a116 100644
--- a/arch/x86/cpu/i386/interrupt.c
+++ b/arch/x86/cpu/i386/interrupt.c
@@ -264,6 +264,9 @@ int interrupt_init(void)
struct udevice *dev;
int ret;
+ if (!ll_boot_init())
+ return 0;
+
/* Try to set up the interrupt router, but don't require one */
ret = irq_first_device_type(X86_IRQT_BASE, &dev);
if (ret && ret != -ENODEV)
@@ -295,8 +298,7 @@ int interrupt_init(void)
* TODO(sjg@chromium.org): But we don't handle these correctly when
* booted from EFI.
*/
- if (ll_boot_init())
- enable_interrupts();
+ enable_interrupts();
#endif
return 0;
diff --git a/arch/x86/cpu/start_from_spl.S b/arch/x86/cpu/start_from_spl.S
index 22cab2d..905c825 100644
--- a/arch/x86/cpu/start_from_spl.S
+++ b/arch/x86/cpu/start_from_spl.S
@@ -14,18 +14,30 @@
.globl _start
.type _start, @function
_start:
- /* Set up memory using the existing stack */
+ /*
+ * If running from coreboot, CAR is no-longer available. Use the
+ * existing stack, which is large enough.
+ */
+ call locate_coreboot_table
+ cmp $0, %eax
+ jge use_existing_stack
+
movl $(CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE - 4), %eax
#ifdef CONFIG_DCACHE_RAM_MRC_VAR_SIZE
subl $CONFIG_DCACHE_RAM_MRC_VAR_SIZE, %eax
#endif
+ jmp 2f
/*
- * We don't subject CONFIG_DCACHE_RAM_MRC_VAR_SIZE since memory is
+ * We don't subtract CONFIG_DCACHE_RAM_MRC_VAR_SIZE since memory is
* already set up. This has the happy side-effect of putting gd in a
* new place separate from SPL, so the memset() in
* board_init_f_init_reserve() does not cause any problems (otherwise
* it would zero out the gd and crash)
*/
+ /* Set up memory using the existing stack */
+use_existing_stack:
+ mov %esp, %eax
+2:
call board_init_f_alloc_reserve
mov %eax, %esp
diff --git a/arch/x86/include/asm/coreboot_tables.h b/arch/x86/include/asm/coreboot_tables.h
index 61de0077..268284f 100644
--- a/arch/x86/include/asm/coreboot_tables.h
+++ b/arch/x86/include/asm/coreboot_tables.h
@@ -343,4 +343,11 @@ void *high_table_malloc(size_t bytes);
*/
void write_coreboot_table(u32 addr, struct memory_area *cfg_tables);
+/**
+ * locate_coreboot_table() - Try to find coreboot tables at standard locations
+ *
+ * @return address of table that was found, or -ve error number
+ */
+long locate_coreboot_table(void);
+
#endif
diff --git a/arch/x86/include/asm/global_data.h b/arch/x86/include/asm/global_data.h
index f4c1839..4aee2f3 100644
--- a/arch/x86/include/asm/global_data.h
+++ b/arch/x86/include/asm/global_data.h
@@ -123,6 +123,7 @@ struct arch_global_data {
#ifdef CONFIG_FSP_VERSION2
struct fsp_header *fsp_s_hdr; /* Pointer to FSP-S header */
#endif
+ ulong acpi_start; /* Start address of ACPI tables */
};
#endif
diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c
index 9346e16..13f1409 100644
--- a/arch/x86/lib/acpi_table.c
+++ b/arch/x86/lib/acpi_table.c
@@ -10,6 +10,7 @@
#include <cpu.h>
#include <dm.h>
#include <dm/uclass-internal.h>
+#include <mapmem.h>
#include <serial.h>
#include <version.h>
#include <acpi/acpi_table.h>
@@ -19,6 +20,7 @@
#include <asm/mpspec.h>
#include <asm/tables.h>
#include <asm/arch/global_nvs.h>
+#include <dm/acpi.h>
/*
* IASL compiles the dsdt entries and writes the hex values
@@ -29,139 +31,6 @@ extern const unsigned char AmlCode[];
/* ACPI RSDP address to be used in boot parameters */
static ulong acpi_rsdp_addr;
-static void acpi_write_rsdp(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt,
- struct acpi_xsdt *xsdt)
-{
- memset(rsdp, 0, sizeof(struct acpi_rsdp));
-
- memcpy(rsdp->signature, RSDP_SIG, 8);
- memcpy(rsdp->oem_id, OEM_ID, 6);
-
- rsdp->length = sizeof(struct acpi_rsdp);
- rsdp->rsdt_address = (u32)rsdt;
-
- /*
- * Revision: ACPI 1.0: 0, ACPI 2.0/3.0/4.0: 2
- *
- * Some OSes expect an XSDT to be present for RSD PTR revisions >= 2.
- * If we don't have an ACPI XSDT, force ACPI 1.0 (and thus RSD PTR
- * revision 0)
- */
- if (xsdt == NULL) {
- rsdp->revision = ACPI_RSDP_REV_ACPI_1_0;
- } else {
- rsdp->xsdt_address = (u64)(u32)xsdt;
- rsdp->revision = ACPI_RSDP_REV_ACPI_2_0;
- }
-
- /* Calculate checksums */
- rsdp->checksum = table_compute_checksum((void *)rsdp, 20);
- rsdp->ext_checksum = table_compute_checksum((void *)rsdp,
- sizeof(struct acpi_rsdp));
-}
-
-void acpi_fill_header(struct acpi_table_header *header, char *signature)
-{
- memcpy(header->signature, signature, 4);
- memcpy(header->oem_id, OEM_ID, 6);
- memcpy(header->oem_table_id, OEM_TABLE_ID, 8);
- header->oem_revision = U_BOOT_BUILD_DATE;
- memcpy(header->aslc_id, ASLC_ID, 4);
-}
-
-static void acpi_write_rsdt(struct acpi_rsdt *rsdt)
-{
- struct acpi_table_header *header = &(rsdt->header);
-
- /* Fill out header fields */
- acpi_fill_header(header, "RSDT");
- header->length = sizeof(struct acpi_rsdt);
- header->revision = 1;
-
- /* Entries are filled in later, we come with an empty set */
-
- /* Fix checksum */
- header->checksum = table_compute_checksum((void *)rsdt,
- sizeof(struct acpi_rsdt));
-}
-
-static void acpi_write_xsdt(struct acpi_xsdt *xsdt)
-{
- struct acpi_table_header *header = &(xsdt->header);
-
- /* Fill out header fields */
- acpi_fill_header(header, "XSDT");
- header->length = sizeof(struct acpi_xsdt);
- header->revision = 1;
-
- /* Entries are filled in later, we come with an empty set */
-
- /* Fix checksum */
- header->checksum = table_compute_checksum((void *)xsdt,
- sizeof(struct acpi_xsdt));
-}
-
-/**
- * Add an ACPI table to the RSDT (and XSDT) structure, recalculate length
- * and checksum.
- */
-static void acpi_add_table(struct acpi_rsdp *rsdp, void *table)
-{
- int i, entries_num;
- struct acpi_rsdt *rsdt;
- struct acpi_xsdt *xsdt;
-
- /* The RSDT is mandatory while the XSDT is not */
- rsdt = (struct acpi_rsdt *)rsdp->rsdt_address;
-
- /* This should always be MAX_ACPI_TABLES */
- entries_num = ARRAY_SIZE(rsdt->entry);
-
- for (i = 0; i < entries_num; i++) {
- if (rsdt->entry[i] == 0)
- break;
- }
-
- if (i >= entries_num) {
- debug("ACPI: Error: too many tables\n");
- return;
- }
-
- /* Add table to the RSDT */
- rsdt->entry[i] = (u32)table;
-
- /* Fix RSDT length or the kernel will assume invalid entries */
- rsdt->header.length = sizeof(struct acpi_table_header) +
- sizeof(u32) * (i + 1);
-
- /* Re-calculate checksum */
- rsdt->header.checksum = 0;
- rsdt->header.checksum = table_compute_checksum((u8 *)rsdt,
- rsdt->header.length);
-
- /* The RSDT is mandatory while the XSDT is not */
- if (!rsdp->xsdt_address)
- return;
-
- /*
- * And now the same thing for the XSDT. We use the same index as for
- * now we want the XSDT and RSDT to always be in sync in U-Boot
- */
- xsdt = (struct acpi_xsdt *)((u32)rsdp->xsdt_address);
-
- /* Add table to the XSDT */
- xsdt->entry[i] = (u64)(u32)table;
-
- /* Fix XSDT length */
- xsdt->header.length = sizeof(struct acpi_table_header) +
- sizeof(u64) * (i + 1);
-
- /* Re-calculate checksum */
- xsdt->header.checksum = 0;
- xsdt->header.checksum = table_compute_checksum((u8 *)xsdt,
- xsdt->header.length);
-}
-
static void acpi_create_facs(struct acpi_facs *facs)
{
memset((void *)facs, 0, sizeof(struct acpi_facs));
@@ -487,12 +356,9 @@ static void acpi_create_spcr(struct acpi_spcr *spcr)
/*
* QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c
*/
-ulong write_acpi_tables(ulong start)
+ulong write_acpi_tables(ulong start_addr)
{
- u32 current;
- struct acpi_rsdp *rsdp;
- struct acpi_rsdt *rsdt;
- struct acpi_xsdt *xsdt;
+ struct acpi_ctx sctx, *ctx = &sctx;
struct acpi_facs *facs;
struct acpi_table_header *dsdt;
struct acpi_fadt *fadt;
@@ -500,60 +366,39 @@ ulong write_acpi_tables(ulong start)
struct acpi_madt *madt;
struct acpi_csrt *csrt;
struct acpi_spcr *spcr;
+ void *start;
+ ulong addr;
int i;
- current = start;
+ start = map_sysmem(start_addr, 0);
- /* Align ACPI tables to 16 byte */
- current = ALIGN(current, 16);
+ debug("ACPI: Writing ACPI tables at %lx\n", start_addr);
- debug("ACPI: Writing ACPI tables at %lx\n", start);
-
- /* We need at least an RSDP and an RSDT Table */
- rsdp = (struct acpi_rsdp *)current;
- current += sizeof(struct acpi_rsdp);
- current = ALIGN(current, 16);
- rsdt = (struct acpi_rsdt *)current;
- current += sizeof(struct acpi_rsdt);
- current = ALIGN(current, 16);
- xsdt = (struct acpi_xsdt *)current;
- current += sizeof(struct acpi_xsdt);
- /*
- * Per ACPI spec, the FACS table address must be aligned to a 64 byte
- * boundary (Windows checks this, but Linux does not).
- */
- current = ALIGN(current, 64);
-
- /* clear all table memory */
- memset((void *)start, 0, current - start);
-
- acpi_write_rsdp(rsdp, rsdt, xsdt);
- acpi_write_rsdt(rsdt);
- acpi_write_xsdt(xsdt);
+ acpi_setup_base_tables(ctx, start);
debug("ACPI: * FACS\n");
- facs = (struct acpi_facs *)current;
- current += sizeof(struct acpi_facs);
- current = ALIGN(current, 16);
+ facs = ctx->current;
+ acpi_inc_align(ctx, sizeof(struct acpi_facs));
acpi_create_facs(facs);
debug("ACPI: * DSDT\n");
- dsdt = (struct acpi_table_header *)current;
+ dsdt = ctx->current;
memcpy(dsdt, &AmlCode, sizeof(struct acpi_table_header));
- current += sizeof(struct acpi_table_header);
- memcpy((char *)current,
+ acpi_inc(ctx, sizeof(struct acpi_table_header));
+ memcpy(ctx->current,
(char *)&AmlCode + sizeof(struct acpi_table_header),
dsdt->length - sizeof(struct acpi_table_header));
- current += dsdt->length - sizeof(struct acpi_table_header);
- current = ALIGN(current, 16);
+ acpi_inc_align(ctx, dsdt->length - sizeof(struct acpi_table_header));
/* Pack GNVS into the ACPI table area */
for (i = 0; i < dsdt->length; i++) {
u32 *gnvs = (u32 *)((u32)dsdt + i);
if (*gnvs == ACPI_GNVS_ADDR) {
- debug("Fix up global NVS in DSDT to 0x%08x\n", current);
- *gnvs = current;
+ ulong addr = (ulong)map_to_sysmem(ctx->current);
+
+ debug("Fix up global NVS in DSDT to %#08lx\n", addr);
+ *gnvs = addr;
break;
}
}
@@ -563,51 +408,48 @@ ulong write_acpi_tables(ulong start)
dsdt->checksum = table_compute_checksum((void *)dsdt, dsdt->length);
/* Fill in platform-specific global NVS variables */
- acpi_create_gnvs((struct acpi_global_nvs *)current);
- current += sizeof(struct acpi_global_nvs);
- current = ALIGN(current, 16);
+ acpi_create_gnvs(ctx->current);
+ acpi_inc_align(ctx, sizeof(struct acpi_global_nvs));
debug("ACPI: * FADT\n");
- fadt = (struct acpi_fadt *)current;
- current += sizeof(struct acpi_fadt);
- current = ALIGN(current, 16);
+ fadt = ctx->current;
+ acpi_inc_align(ctx, sizeof(struct acpi_fadt));
acpi_create_fadt(fadt, facs, dsdt);
- acpi_add_table(rsdp, fadt);
+ acpi_add_table(ctx, fadt);
debug("ACPI: * MADT\n");
- madt = (struct acpi_madt *)current;
+ madt = ctx->current;
acpi_create_madt(madt);
- current += madt->header.length;
- acpi_add_table(rsdp, madt);
- current = ALIGN(current, 16);
+ acpi_inc_align(ctx, madt->header.length);
+ acpi_add_table(ctx, madt);
debug("ACPI: * MCFG\n");
- mcfg = (struct acpi_mcfg *)current;
+ mcfg = ctx->current;
acpi_create_mcfg(mcfg);
- current += mcfg->header.length;
- acpi_add_table(rsdp, mcfg);
- current = ALIGN(current, 16);
+ acpi_inc_align(ctx, mcfg->header.length);
+ acpi_add_table(ctx, mcfg);
debug("ACPI: * CSRT\n");
- csrt = (struct acpi_csrt *)current;
+ csrt = ctx->current;
acpi_create_csrt(csrt);
- current += csrt->header.length;
- acpi_add_table(rsdp, csrt);
- current = ALIGN(current, 16);
+ acpi_inc_align(ctx, csrt->header.length);
+ acpi_add_table(ctx, csrt);
debug("ACPI: * SPCR\n");
- spcr = (struct acpi_spcr *)current;
+ spcr = ctx->current;
acpi_create_spcr(spcr);
- current += spcr->header.length;
- acpi_add_table(rsdp, spcr);
- current = ALIGN(current, 16);
+ acpi_inc_align(ctx, spcr->header.length);
+ acpi_add_table(ctx, spcr);
+
+ acpi_write_dev_tables(ctx);
- debug("current = %x\n", current);
+ addr = map_to_sysmem(ctx->current);
+ debug("current = %lx\n", addr);
- acpi_rsdp_addr = (unsigned long)rsdp;
+ acpi_rsdp_addr = (unsigned long)ctx->rsdp;
debug("ACPI: done\n");
- return current;
+ return addr;
}
ulong acpi_get_rsdp_addr(void)
diff --git a/arch/x86/lib/fsp/fsp_dram.c b/arch/x86/lib/fsp/fsp_dram.c
index 9ce0ddf..15e82de 100644
--- a/arch/x86/lib/fsp/fsp_dram.c
+++ b/arch/x86/lib/fsp/fsp_dram.c
@@ -44,6 +44,14 @@ int dram_init_banksize(void)
phys_addr_t low_end;
uint bank;
+ if (!ll_boot_init()) {
+ gd->bd->bi_dram[0].start = 0;
+ gd->bd->bi_dram[0].size = gd->ram_size;
+
+ mtrr_add_request(MTRR_TYPE_WRBACK, 0, gd->ram_size);
+ return 0;
+ }
+
low_end = 0;
for (bank = 1, hdr = gd->arch.hob_list;
bank < CONFIG_NR_DRAM_BANKS && !end_of_hob(hdr);
diff --git a/arch/x86/lib/fsp/fsp_graphics.c b/arch/x86/lib/fsp/fsp_graphics.c
index 226c7e6..98b7622 100644
--- a/arch/x86/lib/fsp/fsp_graphics.c
+++ b/arch/x86/lib/fsp/fsp_graphics.c
@@ -78,6 +78,9 @@ static int fsp_video_probe(struct udevice *dev)
struct vesa_mode_info *vesa = &mode_info.vesa;
int ret;
+ if (!ll_boot_init())
+ return 0;
+
printf("Video: ");
/* Initialize vesa_mode_info structure */
diff --git a/arch/x86/lib/fsp2/fsp_dram.c b/arch/x86/lib/fsp2/fsp_dram.c
index c8f2c09..3869c53 100644
--- a/arch/x86/lib/fsp2/fsp_dram.c
+++ b/arch/x86/lib/fsp2/fsp_dram.c
@@ -12,11 +12,18 @@
#include <asm/fsp/fsp_support.h>
#include <asm/fsp2/fsp_api.h>
#include <asm/fsp2/fsp_internal.h>
+#include <linux/sizes.h>
int dram_init(void)
{
int ret;
+ if (!ll_boot_init()) {
+ /* Use a small and safe amount of 1GB */
+ gd->ram_size = SZ_1G;
+
+ return 0;
+ }
if (spl_phase() == PHASE_SPL) {
#ifdef CONFIG_HAVE_ACPI_RESUME
bool s3wake = gd->arch.prev_sleep_state == ACPI_S3;
@@ -68,6 +75,9 @@ int dram_init(void)
ulong board_get_usable_ram_top(ulong total_size)
{
+ if (!ll_boot_init())
+ return gd->ram_size;
+
#if CONFIG_IS_ENABLED(HANDOFF)
struct spl_handoff *ho = gd->spl_handoff;
diff --git a/arch/x86/lib/fsp2/fsp_init.c b/arch/x86/lib/fsp2/fsp_init.c
index da9bd6b..c7dc2ea 100644
--- a/arch/x86/lib/fsp2/fsp_init.c
+++ b/arch/x86/lib/fsp2/fsp_init.c
@@ -23,7 +23,7 @@ int arch_cpu_init_dm(void)
int ret;
/* Make sure pads are set up early in U-Boot */
- if (spl_phase() != PHASE_BOARD_F)
+ if (!ll_boot_init() || spl_phase() != PHASE_BOARD_F)
return 0;
/* Probe all pinctrl devices to set up the pads */
diff --git a/arch/x86/lib/init_helpers.c b/arch/x86/lib/init_helpers.c
index 5bb55e2..d906b52 100644
--- a/arch/x86/lib/init_helpers.c
+++ b/arch/x86/lib/init_helpers.c
@@ -30,6 +30,9 @@ int init_cache_f_r(void)
return ret;
}
+ if (!ll_boot_init())
+ return 0;
+
/* Initialise the CPU cache(s) */
return init_cache();
}
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 6ce9e55..157a330 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -190,6 +190,20 @@ comment "Commands"
menu "Info commands"
+config CMD_ACPI
+ bool "acpi"
+ default y if ACPIGEN
+ help
+ List and dump ACPI tables. ACPI (Advanced Configuration and Power
+ Interface) is used mostly on x86 for providing information to the
+ Operating System about devices in the system. The tables are set up
+ by the firmware, typically U-Boot but possibly an earlier firmware
+ module, if U-Boot is chain-loaded from something else. ACPI tables
+ can also include code, to perform hardware-specific tasks required
+ by the Operating Systems. This allows some amount of separation
+ between the firmware and OS, and is particularly useful when you
+ want to make hardware changes without the OS needing to be adjusted.
+
config CMD_BDI
bool "bdinfo"
default y
diff --git a/cmd/Makefile b/cmd/Makefile
index 6692ed9..974ad48 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -11,6 +11,7 @@ obj-y += help.o
obj-y += version.o
# command
+obj-$(CONFIG_CMD_ACPI) += acpi.o
obj-$(CONFIG_CMD_AES) += aes.o
obj-$(CONFIG_CMD_AB_SELECT) += ab_select.o
obj-$(CONFIG_CMD_ADC) += adc.o
diff --git a/cmd/acpi.c b/cmd/acpi.c
new file mode 100644
index 0000000..203bd93
--- /dev/null
+++ b/cmd/acpi.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2019 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+#include <common.h>
+#include <command.h>
+#include <mapmem.h>
+#include <acpi/acpi_table.h>
+#include <asm/acpi_table.h>
+#include <dm/acpi.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/**
+ * dump_hdr() - Dump an ACPI header
+ *
+ * If the header is for FACS then it shows the revision information as well
+ *
+ * @hdr: ACPI header to dump
+ */
+static void dump_hdr(struct acpi_table_header *hdr)
+{
+ bool has_hdr = memcmp(hdr->signature, "FACS", ACPI_NAME_LEN);
+
+ printf("%.*s %08lx %06x", ACPI_NAME_LEN, hdr->signature,
+ (ulong)map_to_sysmem(hdr), hdr->length);
+ if (has_hdr) {
+ printf(" (v%02d %.6s %.8s %u %.4s %d)\n", hdr->revision,
+ hdr->oem_id, hdr->oem_table_id, hdr->oem_revision,
+ hdr->aslc_id, hdr->aslc_revision);
+ } else {
+ printf("\n");
+ }
+}
+
+/**
+ * find_table() - Look up an ACPI table
+ *
+ * @sig: Signature of table (4 characters, upper case)
+ * @return pointer to table header, or NULL if not found
+ */
+struct acpi_table_header *find_table(const char *sig)
+{
+ struct acpi_rsdp *rsdp;
+ struct acpi_rsdt *rsdt;
+ int len, i, count;
+
+ rsdp = map_sysmem(gd->arch.acpi_start, 0);
+ if (!rsdp)
+ return NULL;
+ rsdt = map_sysmem(rsdp->rsdt_address, 0);
+ len = rsdt->header.length - sizeof(rsdt->header);
+ count = len / sizeof(u32);
+ for (i = 0; i < count; i++) {
+ struct acpi_table_header *hdr;
+
+ hdr = map_sysmem(rsdt->entry[i], 0);
+ if (!memcmp(hdr->signature, sig, ACPI_NAME_LEN))
+ return hdr;
+ if (!memcmp(hdr->signature, "FACP", ACPI_NAME_LEN)) {
+ struct acpi_fadt *fadt = (struct acpi_fadt *)hdr;
+
+ if (!memcmp(sig, "DSDT", ACPI_NAME_LEN) && fadt->dsdt)
+ return map_sysmem(fadt->dsdt, 0);
+ if (!memcmp(sig, "FACS", ACPI_NAME_LEN) &&
+ fadt->firmware_ctrl)
+ return map_sysmem(fadt->firmware_ctrl, 0);
+ }
+ }
+
+ return NULL;
+}
+
+static int dump_table_name(const char *sig)
+{
+ struct acpi_table_header *hdr;
+
+ hdr = find_table(sig);
+ if (!hdr)
+ return -ENOENT;
+ printf("%.*s @ %08lx\n", ACPI_NAME_LEN, hdr->signature,
+ (ulong)map_to_sysmem(hdr));
+ print_buffer(0, hdr, 1, hdr->length, 0);
+
+ return 0;
+}
+
+static void list_fadt(struct acpi_fadt *fadt)
+{
+ if (fadt->dsdt)
+ dump_hdr(map_sysmem(fadt->dsdt, 0));
+ if (fadt->firmware_ctrl)
+ dump_hdr(map_sysmem(fadt->firmware_ctrl, 0));
+}
+
+static int list_rsdt(struct acpi_rsdt *rsdt, struct acpi_xsdt *xsdt)
+{
+ int len, i, count;
+
+ dump_hdr(&rsdt->header);
+ if (xsdt)
+ dump_hdr(&xsdt->header);
+ len = rsdt->header.length - sizeof(rsdt->header);
+ count = len / sizeof(u32);
+ for (i = 0; i < count; i++) {
+ struct acpi_table_header *hdr;
+
+ if (!rsdt->entry[i])
+ break;
+ hdr = map_sysmem(rsdt->entry[i], 0);
+ dump_hdr(hdr);
+ if (!memcmp(hdr->signature, "FACP", ACPI_NAME_LEN))
+ list_fadt((struct acpi_fadt *)hdr);
+ if (xsdt) {
+ if (xsdt->entry[i] != rsdt->entry[i]) {
+ printf(" (xsdt mismatch %llx)\n",
+ xsdt->entry[i]);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int list_rsdp(struct acpi_rsdp *rsdp)
+{
+ struct acpi_rsdt *rsdt;
+ struct acpi_xsdt *xsdt;
+
+ printf("RSDP %08lx %06x (v%02d %.6s)\n", (ulong)map_to_sysmem(rsdp),
+ rsdp->length, rsdp->revision, rsdp->oem_id);
+ rsdt = map_sysmem(rsdp->rsdt_address, 0);
+ xsdt = map_sysmem(rsdp->xsdt_address, 0);
+ list_rsdt(rsdt, xsdt);
+
+ return 0;
+}
+
+static int do_acpi_list(cmd_tbl_t *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct acpi_rsdp *rsdp;
+
+ rsdp = map_sysmem(gd->arch.acpi_start, 0);
+ if (!rsdp) {
+ printf("No ACPI tables present\n");
+ return 0;
+ }
+ printf("ACPI tables start at %lx\n", gd->arch.acpi_start);
+ list_rsdp(rsdp);
+
+ return 0;
+}
+
+static int do_acpi_dump(cmd_tbl_t *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ const char *name;
+ char sig[ACPI_NAME_LEN];
+ int ret;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+ name = argv[1];
+ if (strlen(name) != ACPI_NAME_LEN) {
+ printf("Table name '%s' must be four characters\n", name);
+ return CMD_RET_FAILURE;
+ }
+ str_to_upper(name, sig, -1);
+ ret = dump_table_name(sig);
+ if (ret) {
+ printf("Table '%.*s' not found\n", ACPI_NAME_LEN, sig);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+static char acpi_help_text[] =
+ "list - list ACPI tables\n"
+ "acpi dump <name> - Dump ACPI table";
+
+U_BOOT_CMD_WITH_SUBCMDS(acpi, "ACPI tables", acpi_help_text,
+ U_BOOT_SUBCMD_MKENT(list, 1, 1, do_acpi_list),
+ U_BOOT_SUBCMD_MKENT(dump, 2, 1, do_acpi_dump));
diff --git a/doc/arch/x86.rst b/doc/arch/x86.rst
index a441738..c6b70ce 100644
--- a/doc/arch/x86.rst
+++ b/doc/arch/x86.rst
@@ -712,6 +712,34 @@ to load a 'u-boot-payload.efi', see below test logs on QEMU.
See :doc:`../uefi/u-boot_on_efi` and :doc:`../uefi/uefi` for details of
EFI support in U-Boot.
+Chain-loading
+-------------
+U-Boot can be chain-loaded from another bootloader, such as coreboot or
+Slim Bootloader. Typically this is done by building for targets 'coreboot' or
+'slimbootloader'.
+
+For example, at present we have a 'coreboot' target but this runs very
+different code from the bare-metal targets, such as coral. There is very little
+in common between them.
+
+It is useful to be able to boot the same U-Boot on a device, with or without a
+first-stage bootloader. For example, with chromebook_coral, it is helpful for
+testing to be able to boot the same U-Boot (complete with FSP) on bare metal
+and from coreboot. It allows checking of things like CPU speed, comparing
+registers, ACPI tables and the like.
+
+To do this you can use ll_boot_init() in appropriate places to skip init that
+has already been done by the previous stage. This works by setting a
+GD_FLG_NO_LL_INIT flag when U-Boot detects that it is running from another
+bootloader.
+
+With this feature, you can build a bare-metal target and boot it from
+coreboot, for example.
+
+Note that this is a development feature only. It is not intended for use in
+production environments. Also it is not currently part of the automated tests
+so may break in the future.
+
TODO List
---------
- Audio
diff --git a/doc/device-tree-bindings/device.txt b/doc/device-tree-bindings/device.txt
new file mode 100644
index 0000000..27bd397
--- /dev/null
+++ b/doc/device-tree-bindings/device.txt
@@ -0,0 +1,36 @@
+Devices
+=======
+
+Device bindings are described by their own individual binding files.
+
+U-Boot provides for some optional properties which are documented here. See
+also hid-over-i2c.txt which describes HID devices. See also
+Documentation/firmware-guide/acpi/enumeration.rst in the Linux kernel for
+the acpi,compatible property.
+
+ - acpi,has-power-resource : (boolean) true if this device has a power resource.
+ This causes an ACPI PowerResource to be written containing the properties
+ provided by this binding, to describe how to handle powering the device up
+ and down using GPIOs
+ - acpi,compatible : compatible string to report
+ - acpi,ddn : Contains the string to use as the _DDN (DOS (Disk Operating
+ System) Device Name)
+ - acpi,hid : Contains the string to use as the HID (Hardware ID)
+ identifier _HID
+ - acpi,uid : _UID value for device
+ - linux,probed : Tells U-Boot to add 'linux,probed' to the ACPI tables so that
+ Linux will only load the driver if the device can be detected (e.g. on I2C
+ bus). Note that this is an out-of-tree Linux feature.
+
+
+Example
+-------
+
+elan_touchscreen: elan-touchscreen@10 {
+ compatible = "i2c-chip";
+ reg = <0x10>;
+ acpi,hid = "ELAN0001";
+ acpi,ddn = "ELAN Touchscreen";
+ interrupts-extended = <&acpi_gpe GPIO_21_IRQ IRQ_TYPE_EDGE_FALLING>;
+ linux,probed;
+};
diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c
index ba50d68..e09905c 100644
--- a/drivers/core/acpi.c
+++ b/drivers/core/acpi.c
@@ -11,8 +11,17 @@
#include <common.h>
#include <dm.h>
#include <dm/acpi.h>
+#include <dm/device-internal.h>
#include <dm/root.h>
+/* Type of method to call */
+enum method_t {
+ METHOD_WRITE_TABLES,
+};
+
+/* Prototype for all methods */
+typedef int (*acpi_method)(const struct udevice *dev, struct acpi_ctx *ctx);
+
int acpi_copy_name(char *out_name, const char *name)
{
strncpy(out_name, name, ACPI_NAME_LEN);
@@ -31,3 +40,56 @@ int acpi_get_name(const struct udevice *dev, char *out_name)
return -ENOSYS;
}
+
+acpi_method acpi_get_method(struct udevice *dev, enum method_t method)
+{
+ struct acpi_ops *aops;
+
+ aops = device_get_acpi_ops(dev);
+ if (aops) {
+ switch (method) {
+ case METHOD_WRITE_TABLES:
+ return aops->write_tables;
+ }
+ }
+
+ return NULL;
+}
+
+int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent,
+ enum method_t method)
+{
+ struct udevice *dev;
+ acpi_method func;
+ int ret;
+
+ func = acpi_get_method(parent, method);
+ if (func) {
+ log_debug("\n");
+ log_debug("- %s %p\n", parent->name, func);
+ ret = device_ofdata_to_platdata(parent);
+ if (ret)
+ return log_msg_ret("ofdata", ret);
+ ret = func(parent, ctx);
+ if (ret)
+ return log_msg_ret("func", ret);
+ }
+ device_foreach_child(dev, parent) {
+ ret = acpi_recurse_method(ctx, dev, method);
+ if (ret)
+ return log_msg_ret("recurse", ret);
+ }
+
+ return 0;
+}
+
+int acpi_write_dev_tables(struct acpi_ctx *ctx)
+{
+ int ret;
+
+ log_debug("Writing device tables\n");
+ ret = acpi_recurse_method(ctx, dm_root(), METHOD_WRITE_TABLES);
+ log_debug("Writing finished, err=%d\n", ret);
+
+ return ret;
+}
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c
index d2e10d6..7f46e90 100644
--- a/drivers/pci/pci-uclass.c
+++ b/drivers/pci/pci-uclass.c
@@ -1009,7 +1009,7 @@ static int pci_uclass_post_probe(struct udevice *bus)
if (ret)
return ret;
- if (CONFIG_IS_ENABLED(PCI_PNP) &&
+ if (CONFIG_IS_ENABLED(PCI_PNP) && ll_boot_init() &&
(!hose->skip_auto_config_until_reloc ||
(gd->flags & GD_FLG_RELOC))) {
ret = pci_auto_config_devices(bus);
@@ -1031,7 +1031,7 @@ static int pci_uclass_post_probe(struct udevice *bus)
* Note we only call this 1) after U-Boot is relocated, and 2)
* root bus has finished probing.
*/
- if ((gd->flags & GD_FLG_RELOC) && (bus->seq == 0)) {
+ if ((gd->flags & GD_FLG_RELOC) && bus->seq == 0 && ll_boot_init()) {
ret = fsp_init_phase_pci();
if (ret)
return ret;
diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h
index 194be9a..3681c5c 100644
--- a/include/acpi/acpi_table.h
+++ b/include/acpi/acpi_table.h
@@ -23,6 +23,8 @@
#if !defined(__ACPI__)
+struct acpi_ctx;
+
/*
* RSDP (Root System Description Pointer)
* Note: ACPI 1.0 didn't have length, xsdt_address, and ext_checksum
@@ -505,6 +507,69 @@ int acpi_get_table_revision(enum acpi_tables table);
*/
int acpi_create_dmar(struct acpi_dmar *dmar, enum dmar_flags flags);
+/**
+ * acpi_fill_header() - Set up a new table header
+ *
+ * This sets all fields except length, revision, checksum and aslc_revision
+ *
+ * @header: ACPI header to update
+ * @signature: Table signature to use (4 characters)
+ */
+void acpi_fill_header(struct acpi_table_header *header, char *signature);
+
+/**
+ * acpi_align() - Align the ACPI output pointer to a 16-byte boundary
+ *
+ * @ctx: ACPI context
+ */
+void acpi_align(struct acpi_ctx *ctx);
+
+/**
+ * acpi_align64() - Align the ACPI output pointer to a 64-byte boundary
+ *
+ * @ctx: ACPI context
+ */
+void acpi_align64(struct acpi_ctx *ctx);
+
+/**
+ * acpi_inc() - Increment the ACPI output pointer by a bit
+ *
+ * The pointer is NOT aligned afterwards.
+ *
+ * @ctx: ACPI context
+ * @amount: Amount to increment by
+ */
+void acpi_inc(struct acpi_ctx *ctx, uint amount);
+
+/**
+ * acpi_inc_align() - Increment the ACPI output pointer by a bit and align
+ *
+ * The pointer is aligned afterwards to a 16-byte boundary
+ *
+ * @ctx: ACPI context
+ * @amount: Amount to increment by
+ */
+void acpi_inc_align(struct acpi_ctx *ctx, uint amount);
+
+/**
+ * acpi_add_table() - Add a new table to the RSDP and XSDT
+ *
+ * @ctx: ACPI context
+ * @table: Table to add
+ * @return 0 if OK, -E2BIG if too many tables
+ */
+int acpi_add_table(struct acpi_ctx *ctx, void *table);
+
+/**
+ * acpi_setup_base_tables() - Set up context along with RSDP, RSDT and XSDT
+ *
+ * Set up the context with the given start position. Some basic tables are
+ * always needed, so set them up as well.
+ *
+ * @ctx: Context to set up
+ */
+void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start);
+
#endif /* !__ACPI__*/
#include <asm/acpi_table.h>
diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h
index d9e220c..8c78792 100644
--- a/include/asm-generic/global_data.h
+++ b/include/asm-generic/global_data.h
@@ -166,5 +166,6 @@ typedef struct global_data {
#define GD_FLG_SPL_EARLY_INIT 0x04000 /* Early SPL init is done */
#define GD_FLG_LOG_READY 0x08000 /* Log system is ready for use */
#define GD_FLG_WDT_READY 0x10000 /* Watchdog is ready for use */
+#define GD_FLG_SKIP_LL_INIT 0x20000 /* Don't perform low-level init */
#endif /* __ASM_GENERIC_GBL_DATA_H */
diff --git a/include/cbfs.h b/include/cbfs.h
index f3bc8ca..d915f94 100644
--- a/include/cbfs.h
+++ b/include/cbfs.h
@@ -135,7 +135,7 @@ void file_cbfs_get_next(const struct cbfs_cachenode **file);
*/
const struct cbfs_cachenode *file_cbfs_find(const char *name);
-struct cbfs_priv *priv;
+struct cbfs_priv;
/**
* cbfs_find_file() - Find a file in a given CBFS
diff --git a/include/dm/acpi.h b/include/dm/acpi.h
index 4925791..7563a4c 100644
--- a/include/dm/acpi.h
+++ b/include/dm/acpi.h
@@ -25,6 +25,24 @@
#if !defined(__ACPI__)
/**
+ * struct acpi_ctx - Context used for writing ACPI tables
+ *
+ * This contains a few useful pieces of information used when writing
+ *
+ * @current: Current address for writing
+ * @rsdp: Pointer to the Root System Description Pointer, typically used when
+ * adding a new table. The RSDP holds pointers to the RSDT and XSDT.
+ * @rsdt: Pointer to the Root System Description Table
+ * @xsdt: Pointer to the Extended System Description Table
+ */
+struct acpi_ctx {
+ void *current;
+ struct acpi_rsdp *rsdp;
+ struct acpi_rsdt *rsdt;
+ struct acpi_xsdt *xsdt;
+};
+
+/**
* struct acpi_ops - ACPI operations supported by driver model
*/
struct acpi_ops {
@@ -38,6 +56,15 @@ struct acpi_ops {
* other error
*/
int (*get_name)(const struct udevice *dev, char *out_name);
+
+ /**
+ * write_tables() - Write out any tables required by this device
+ *
+ * @dev: Device to write
+ * @ctx: ACPI context to use
+ * @return 0 if OK, -ve on error
+ */
+ int (*write_tables)(const struct udevice *dev, struct acpi_ctx *ctx);
};
#define device_get_acpi_ops(dev) ((dev)->driver->acpi_ops)
@@ -72,6 +99,16 @@ int acpi_get_name(const struct udevice *dev, char *out_name);
*/
int acpi_copy_name(char *out_name, const char *name);
+/**
+ * acpi_write_dev_tables() - Write ACPI tables required by devices
+ *
+ * This scans through all devices and tells them to write any tables they want
+ * to write.
+ *
+ * @return 0 if OK, -ve if any device returned an error
+ */
+int acpi_write_dev_tables(struct acpi_ctx *ctx);
+
#endif /* __ACPI__ */
#endif
diff --git a/include/init.h b/include/init.h
index 9ef88c9..b5a167b 100644
--- a/include/init.h
+++ b/include/init.h
@@ -20,7 +20,7 @@ struct global_data;
#ifdef CONFIG_EFI_STUB
#define ll_boot_init() false
#else
-#define ll_boot_init() true
+#define ll_boot_init() (!(gd->flags & GD_FLG_SKIP_LL_INIT))
#endif
/*
diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c
index 4633dcb..1c253af 100644
--- a/lib/acpi/acpi_table.c
+++ b/lib/acpi/acpi_table.c
@@ -6,12 +6,14 @@
*/
#include <common.h>
-#include <acpi/acpi_table.h>
#include <dm.h>
#include <cpu.h>
+#include <mapmem.h>
+#include <tables_csum.h>
+#include <version.h>
+#include <acpi/acpi_table.h>
+#include <dm/acpi.h>
-/* Temporary change to ensure bisectability */
-#ifndef CONFIG_SANDBOX
int acpi_create_dmar(struct acpi_dmar *dmar, enum dmar_flags flags)
{
struct acpi_table_header *header = &dmar->header;
@@ -37,7 +39,6 @@ int acpi_create_dmar(struct acpi_dmar *dmar, enum dmar_flags flags)
return 0;
}
-#endif
int acpi_get_table_revision(enum acpi_tables table)
{
@@ -91,3 +92,173 @@ int acpi_get_table_revision(enum acpi_tables table)
return -EINVAL;
}
}
+
+void acpi_fill_header(struct acpi_table_header *header, char *signature)
+{
+ memcpy(header->signature, signature, 4);
+ memcpy(header->oem_id, OEM_ID, 6);
+ memcpy(header->oem_table_id, OEM_TABLE_ID, 8);
+ header->oem_revision = U_BOOT_BUILD_DATE;
+ memcpy(header->aslc_id, ASLC_ID, 4);
+}
+
+void acpi_align(struct acpi_ctx *ctx)
+{
+ ctx->current = (void *)ALIGN((ulong)ctx->current, 16);
+}
+
+void acpi_align64(struct acpi_ctx *ctx)
+{
+ ctx->current = (void *)ALIGN((ulong)ctx->current, 64);
+}
+
+void acpi_inc(struct acpi_ctx *ctx, uint amount)
+{
+ ctx->current += amount;
+}
+
+void acpi_inc_align(struct acpi_ctx *ctx, uint amount)
+{
+ ctx->current += amount;
+ acpi_align(ctx);
+}
+
+/**
+ * Add an ACPI table to the RSDT (and XSDT) structure, recalculate length
+ * and checksum.
+ */
+int acpi_add_table(struct acpi_ctx *ctx, void *table)
+{
+ int i, entries_num;
+ struct acpi_rsdt *rsdt;
+ struct acpi_xsdt *xsdt;
+
+ /* The RSDT is mandatory while the XSDT is not */
+ rsdt = ctx->rsdt;
+
+ /* This should always be MAX_ACPI_TABLES */
+ entries_num = ARRAY_SIZE(rsdt->entry);
+
+ for (i = 0; i < entries_num; i++) {
+ if (rsdt->entry[i] == 0)
+ break;
+ }
+
+ if (i >= entries_num) {
+ log_err("ACPI: Error: too many tables\n");
+ return -E2BIG;
+ }
+
+ /* Add table to the RSDT */
+ rsdt->entry[i] = map_to_sysmem(table);
+
+ /* Fix RSDT length or the kernel will assume invalid entries */
+ rsdt->header.length = sizeof(struct acpi_table_header) +
+ (sizeof(u32) * (i + 1));
+
+ /* Re-calculate checksum */
+ rsdt->header.checksum = 0;
+ rsdt->header.checksum = table_compute_checksum((u8 *)rsdt,
+ rsdt->header.length);
+
+ /*
+ * And now the same thing for the XSDT. We use the same index as for
+ * now we want the XSDT and RSDT to always be in sync in U-Boot
+ */
+ xsdt = ctx->xsdt;
+
+ /* Add table to the XSDT */
+ xsdt->entry[i] = map_to_sysmem(table);
+
+ /* Fix XSDT length */
+ xsdt->header.length = sizeof(struct acpi_table_header) +
+ (sizeof(u64) * (i + 1));
+
+ /* Re-calculate checksum */
+ xsdt->header.checksum = 0;
+ xsdt->header.checksum = table_compute_checksum((u8 *)xsdt,
+ xsdt->header.length);
+
+ return 0;
+}
+
+static void acpi_write_rsdp(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt,
+ struct acpi_xsdt *xsdt)
+{
+ memset(rsdp, 0, sizeof(struct acpi_rsdp));
+
+ memcpy(rsdp->signature, RSDP_SIG, 8);
+ memcpy(rsdp->oem_id, OEM_ID, 6);
+
+ rsdp->length = sizeof(struct acpi_rsdp);
+ rsdp->rsdt_address = map_to_sysmem(rsdt);
+
+ rsdp->xsdt_address = map_to_sysmem(xsdt);
+ rsdp->revision = ACPI_RSDP_REV_ACPI_2_0;
+
+ /* Calculate checksums */
+ rsdp->checksum = table_compute_checksum(rsdp, 20);
+ rsdp->ext_checksum = table_compute_checksum(rsdp,
+ sizeof(struct acpi_rsdp));
+}
+
+static void acpi_write_rsdt(struct acpi_rsdt *rsdt)
+{
+ struct acpi_table_header *header = &rsdt->header;
+
+ /* Fill out header fields */
+ acpi_fill_header(header, "RSDT");
+ header->length = sizeof(struct acpi_rsdt);
+ header->revision = 1;
+
+ /* Entries are filled in later, we come with an empty set */
+
+ /* Fix checksum */
+ header->checksum = table_compute_checksum(rsdt,
+ sizeof(struct acpi_rsdt));
+}
+
+static void acpi_write_xsdt(struct acpi_xsdt *xsdt)
+{
+ struct acpi_table_header *header = &xsdt->header;
+
+ /* Fill out header fields */
+ acpi_fill_header(header, "XSDT");
+ header->length = sizeof(struct acpi_xsdt);
+ header->revision = 1;
+
+ /* Entries are filled in later, we come with an empty set */
+
+ /* Fix checksum */
+ header->checksum = table_compute_checksum(xsdt,
+ sizeof(struct acpi_xsdt));
+}
+
+void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start)
+{
+ ctx->current = start;
+
+ /* Align ACPI tables to 16 byte */
+ acpi_align(ctx);
+ gd->arch.acpi_start = map_to_sysmem(ctx->current);
+
+ /* We need at least an RSDP and an RSDT Table */
+ ctx->rsdp = ctx->current;
+ acpi_inc_align(ctx, sizeof(struct acpi_rsdp));
+ ctx->rsdt = ctx->current;
+ acpi_inc_align(ctx, sizeof(struct acpi_rsdt));
+ ctx->xsdt = ctx->current;
+ acpi_inc_align(ctx, sizeof(struct acpi_xsdt));
+
+ /* clear all table memory */
+ memset((void *)start, '\0', ctx->current - start);
+
+ acpi_write_rsdp(ctx->rsdp, ctx->rsdt, ctx->xsdt);
+ acpi_write_rsdt(ctx->rsdt);
+ acpi_write_xsdt(ctx->xsdt);
+ /*
+ * Per ACPI spec, the FACS table address must be aligned to a 64 byte
+ * boundary (Windows checks this, but Linux does not).
+ */
+ acpi_align64(ctx);
+}
diff --git a/test/dm/acpi.c b/test/dm/acpi.c
index e7b8abd..176d207 100644
--- a/test/dm/acpi.c
+++ b/test/dm/acpi.c
@@ -7,13 +7,36 @@
*/
#include <common.h>
+#include <console.h>
#include <dm.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <version.h>
+#include <tables_csum.h>
+#include <version.h>
#include <acpi/acpi_table.h>
#include <dm/acpi.h>
#include <dm/test.h>
#include <test/ut.h>
#define ACPI_TEST_DEV_NAME "ABCD"
+#define BUF_SIZE 4096
+
+static int testacpi_write_tables(const struct udevice *dev,
+ struct acpi_ctx *ctx)
+{
+ struct acpi_dmar *dmar;
+ int ret;
+
+ dmar = (struct acpi_dmar *)ctx->current;
+ acpi_create_dmar(dmar, DMAR_INTR_REMAP);
+ ctx->current += sizeof(struct acpi_dmar);
+ ret = acpi_add_table(ctx, dmar);
+ if (ret)
+ return log_msg_ret("add", ret);
+
+ return 0;
+}
static int testacpi_get_name(const struct udevice *dev, char *out_name)
{
@@ -22,6 +45,7 @@ static int testacpi_get_name(const struct udevice *dev, char *out_name)
struct acpi_ops testacpi_ops = {
.get_name = testacpi_get_name,
+ .write_tables = testacpi_write_tables,
};
static const struct udevice_id testacpi_ids[] = {
@@ -68,8 +92,6 @@ static int dm_test_acpi_get_table_revision(struct unit_test_state *uts)
DM_TEST(dm_test_acpi_get_table_revision,
DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
-/* Temporary change to ensure bisectability */
-#ifndef CONFIG_SANDBOX
/* Test acpi_create_dmar() */
static int dm_test_acpi_create_dmar(struct unit_test_state *uts)
{
@@ -82,4 +104,214 @@ static int dm_test_acpi_create_dmar(struct unit_test_state *uts)
return 0;
}
DM_TEST(dm_test_acpi_create_dmar, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
-#endif
+
+/* Test acpi_fill_header() */
+static int dm_test_acpi_fill_header(struct unit_test_state *uts)
+{
+ struct acpi_table_header hdr;
+
+ /* Make sure these 5 fields are not changed */
+ hdr.length = 0x11;
+ hdr.revision = 0x22;
+ hdr.checksum = 0x33;
+ hdr.aslc_revision = 0x44;
+ acpi_fill_header(&hdr, "ABCD");
+
+ ut_asserteq_mem("ABCD", hdr.signature, sizeof(hdr.signature));
+ ut_asserteq(0x11, hdr.length);
+ ut_asserteq(0x22, hdr.revision);
+ ut_asserteq(0x33, hdr.checksum);
+ ut_asserteq_mem(OEM_ID, hdr.oem_id, sizeof(hdr.oem_id));
+ ut_asserteq_mem(OEM_TABLE_ID, hdr.oem_table_id,
+ sizeof(hdr.oem_table_id));
+ ut_asserteq(U_BOOT_BUILD_DATE, hdr.oem_revision);
+ ut_asserteq_mem(ASLC_ID, hdr.aslc_id, sizeof(hdr.aslc_id));
+ ut_asserteq(0x44, hdr.aslc_revision);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_fill_header, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test ACPI write_tables() */
+static int dm_test_acpi_write_tables(struct unit_test_state *uts)
+{
+ struct acpi_dmar *dmar;
+ struct acpi_ctx ctx;
+ void *buf;
+
+ buf = malloc(BUF_SIZE);
+ ut_assertnonnull(buf);
+
+ acpi_setup_base_tables(&ctx, buf);
+ dmar = ctx.current;
+ ut_assertok(acpi_write_dev_tables(&ctx));
+
+ /*
+ * We should have two dmar tables, one for each "denx,u-boot-acpi-test"
+ * device
+ */
+ ut_asserteq_ptr(dmar + 2, ctx.current);
+ ut_asserteq(DMAR_INTR_REMAP, dmar->flags);
+ ut_asserteq(32 - 1, dmar->host_address_width);
+
+ ut_asserteq(DMAR_INTR_REMAP, dmar[1].flags);
+ ut_asserteq(32 - 1, dmar[1].host_address_width);
+
+ /* Check that the pointers were added correctly */
+ ut_asserteq(map_to_sysmem(dmar), ctx.rsdt->entry[0]);
+ ut_asserteq(map_to_sysmem(dmar + 1), ctx.rsdt->entry[1]);
+ ut_asserteq(0, ctx.rsdt->entry[2]);
+
+ ut_asserteq(map_to_sysmem(dmar), ctx.xsdt->entry[0]);
+ ut_asserteq(map_to_sysmem(dmar + 1), ctx.xsdt->entry[1]);
+ ut_asserteq(0, ctx.xsdt->entry[2]);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_write_tables, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test basic ACPI functions */
+static int dm_test_acpi_basic(struct unit_test_state *uts)
+{
+ struct acpi_ctx ctx;
+
+ /* Check align works */
+ ctx.current = (void *)5;
+ acpi_align(&ctx);
+ ut_asserteq_ptr((void *)16, ctx.current);
+
+ /* Check that align does nothing if already aligned */
+ acpi_align(&ctx);
+ ut_asserteq_ptr((void *)16, ctx.current);
+ acpi_align64(&ctx);
+ ut_asserteq_ptr((void *)64, ctx.current);
+ acpi_align64(&ctx);
+ ut_asserteq_ptr((void *)64, ctx.current);
+
+ /* Check incrementing */
+ acpi_inc(&ctx, 3);
+ ut_asserteq_ptr((void *)67, ctx.current);
+ acpi_inc_align(&ctx, 3);
+ ut_asserteq_ptr((void *)80, ctx.current);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_basic, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test acpi_setup_base_tables */
+static int dm_test_acpi_setup_base_tables(struct unit_test_state *uts)
+{
+ struct acpi_rsdp *rsdp;
+ struct acpi_rsdt *rsdt;
+ struct acpi_xsdt *xsdt;
+ struct acpi_ctx ctx;
+ void *buf, *end;
+
+ /*
+ * Use an unaligned address deliberately, by allocating an aligned
+ * address and then adding 4 to it
+ */
+ buf = memalign(64, BUF_SIZE);
+ ut_assertnonnull(buf);
+ acpi_setup_base_tables(&ctx, buf + 4);
+ ut_asserteq(map_to_sysmem(PTR_ALIGN(buf + 4, 16)), gd->arch.acpi_start);
+
+ rsdp = buf + 16;
+ ut_asserteq_ptr(rsdp, ctx.rsdp);
+ ut_assertok(memcmp(RSDP_SIG, rsdp->signature, sizeof(rsdp->signature)));
+ ut_asserteq(sizeof(*rsdp), rsdp->length);
+ ut_assertok(table_compute_checksum(rsdp, 20));
+ ut_assertok(table_compute_checksum(rsdp, sizeof(*rsdp)));
+
+ rsdt = PTR_ALIGN((void *)rsdp + sizeof(*rsdp), 16);
+ ut_asserteq_ptr(rsdt, ctx.rsdt);
+ ut_assertok(memcmp("RSDT", rsdt->header.signature, ACPI_NAME_LEN));
+ ut_asserteq(sizeof(*rsdt), rsdt->header.length);
+ ut_assertok(table_compute_checksum(rsdt, sizeof(*rsdt)));
+
+ xsdt = PTR_ALIGN((void *)rsdt + sizeof(*rsdt), 16);
+ ut_asserteq_ptr(xsdt, ctx.xsdt);
+ ut_assertok(memcmp("XSDT", xsdt->header.signature, ACPI_NAME_LEN));
+ ut_asserteq(sizeof(*xsdt), xsdt->header.length);
+ ut_assertok(table_compute_checksum(xsdt, sizeof(*xsdt)));
+
+ end = PTR_ALIGN((void *)xsdt + sizeof(*xsdt), 64);
+ ut_asserteq_ptr(end, ctx.current);
+
+ ut_asserteq(map_to_sysmem(rsdt), rsdp->rsdt_address);
+ ut_asserteq(map_to_sysmem(xsdt), rsdp->xsdt_address);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_setup_base_tables,
+ DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test 'acpi list' command */
+static int dm_test_acpi_cmd_list(struct unit_test_state *uts)
+{
+ struct acpi_ctx ctx;
+ ulong addr;
+ void *buf;
+
+ buf = memalign(16, BUF_SIZE);
+ ut_assertnonnull(buf);
+ acpi_setup_base_tables(&ctx, buf);
+
+ ut_assertok(acpi_write_dev_tables(&ctx));
+
+ console_record_reset();
+ run_command("acpi list", 0);
+ addr = (ulong)map_to_sysmem(buf);
+ ut_assert_nextline("ACPI tables start at %lx", addr);
+ ut_assert_nextline("RSDP %08lx %06lx (v02 U-BOOT)", addr,
+ sizeof(struct acpi_rsdp));
+ addr = ALIGN(addr + sizeof(struct acpi_rsdp), 16);
+ ut_assert_nextline("RSDT %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
+ addr, sizeof(struct acpi_table_header) +
+ 2 * sizeof(u32), U_BOOT_BUILD_DATE);
+ addr = ALIGN(addr + sizeof(struct acpi_rsdt), 16);
+ ut_assert_nextline("XSDT %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
+ addr, sizeof(struct acpi_table_header) +
+ 2 * sizeof(u64), U_BOOT_BUILD_DATE);
+ addr = ALIGN(addr + sizeof(struct acpi_xsdt), 64);
+ ut_assert_nextline("DMAR %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
+ addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE);
+ addr = ALIGN(addr + sizeof(struct acpi_dmar), 16);
+ ut_assert_nextline("DMAR %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
+ addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE);
+ ut_assert_console_end();
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_cmd_list, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test 'acpi dump' command */
+static int dm_test_acpi_cmd_dump(struct unit_test_state *uts)
+{
+ struct acpi_ctx ctx;
+ ulong addr;
+ void *buf;
+
+ buf = memalign(16, BUF_SIZE);
+ ut_assertnonnull(buf);
+ acpi_setup_base_tables(&ctx, buf);
+
+ ut_assertok(acpi_write_dev_tables(&ctx));
+
+ /* First search for a non-existent table */
+ console_record_reset();
+ run_command("acpi dump rdst", 0);
+ ut_assert_nextline("Table 'RDST' not found");
+ ut_assert_console_end();
+
+ /* Now a real table */
+ console_record_reset();
+ run_command("acpi dump dmar", 0);
+ addr = ALIGN(map_to_sysmem(ctx.xsdt) + sizeof(struct acpi_xsdt), 64);
+ ut_assert_nextline("DMAR @ %08lx", addr);
+ ut_assert_nextlines_are_dump(0x30);
+ ut_assert_console_end();
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_cmd_dump, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);