mirror of
https://github.com/u-boot/u-boot.git
synced 2024-11-26 21:54:37 +08:00
- DM ACPI support (Part A) - Improve support for chain-loading x86 U-Boot
This commit is contained in:
commit
9f0a6df3a5
@ -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";
|
||||
|
@ -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>
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
debug("current = %x\n", current);
|
||||
acpi_write_dev_tables(ctx);
|
||||
|
||||
acpi_rsdp_addr = (unsigned long)rsdp;
|
||||
addr = map_to_sysmem(ctx->current);
|
||||
debug("current = %lx\n", addr);
|
||||
|
||||
acpi_rsdp_addr = (unsigned long)ctx->rsdp;
|
||||
debug("ACPI: done\n");
|
||||
|
||||
return current;
|
||||
return addr;
|
||||
}
|
||||
|
||||
ulong acpi_get_rsdp_addr(void)
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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();
|
||||
}
|
||||
|
14
cmd/Kconfig
14
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
|
||||
|
@ -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
|
||||
|
186
cmd/acpi.c
Normal file
186
cmd/acpi.c
Normal file
@ -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));
|
@ -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
|
||||
|
36
doc/device-tree-bindings/device.txt
Normal file
36
doc/device-tree-bindings/device.txt
Normal file
@ -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;
|
||||
};
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -24,6 +24,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
|
||||
*/
|
||||
@ -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
|
||||
|
@ -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
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
}
|
||||
|
238
test/dm/acpi.c
238
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);
|
||||
|
Loading…
Reference in New Issue
Block a user