diff options
author | Tom Rini <trini@konsulko.com> | 2020-04-30 13:00:20 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2020-04-30 13:00:20 -0400 |
commit | 9f0a6df3a57469061582c6b27fc869829681beca (patch) | |
tree | 80b1a3707a5a4f6fd1d9db03ce24e12e0d47b781 | |
parent | 6d7dacf726ca043a3f5487549bbfa506c990c813 (diff) | |
parent | 249154672d43db6c7978fd9b67d224e9dec09867 (diff) |
Merge https://gitlab.denx.de/u-boot/custodians/u-boot-x86
- DM ACPI support (Part A)
- Improve support for chain-loading x86 U-Boot
30 files changed, 983 insertions, 232 deletions
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index df9f1835c9..4bccfbe6e1 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 f4ce72d566..f95ddb058a 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 17cf1682ad..7ef169b147 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 37e0424b5e..0f04c4f8e9 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 cec04b481b..8526e856d7 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 c8da7f10e9..0312a26bbb 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 4c7e9ea215..e67a116ac1 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 22cab2dd6c..905c825cdc 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 61de0077d7..268284f43c 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 f4c1839104..4aee2f3e8c 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 9346e165d8..13f1409de8 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 9ce0ddf0d3..15e82de2fe 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 226c7e66b3..98b762209f 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 c8f2c09b6a..3869c53c5f 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 da9bd6b45c..c7dc2ea257 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 5bb55e256f..d906b528b3 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 6ce9e5521c..157a33081f 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 6692ed96c6..974ad48b0a 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 0000000000..203bd93bd5 --- /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 a441738ad1..c6b70ce61a 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 0000000000..27bd3978d9 --- /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 ba50d688fe..e09905cf2a 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 d2e10d6868..7f46e901fb 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 194be9aa58..3681c5c8ed 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 d9e220cfe3..8c78792cc9 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 f3bc8ca24a..d915f9426d 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 49257914ff..7563a4c60a 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 9ef88c966b..b5a167b6ed 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 4633dcb948..1c253af3bf 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 e7b8abd556..176d207a55 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); |