GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -0,0 +1,95 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2002-2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
ifndef CONFIG_X86_64
obj-y += bios.o
obj-y += bios_asm.o
obj-y += bios_interrupts.o
obj-y += string.o
endif
ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_CMD_BOOTM) += bootm.o
endif
obj-y += cmd_boot.o
obj-$(CONFIG_SEABIOS) += coreboot_table.o
obj-y += early_cmos.o
obj-y += e820.o
obj-y += init_helpers.o
obj-y += interrupts.o
obj-y += lpc-uclass.o
obj-y += mpspec.o
obj-$(CONFIG_ENABLE_MRC_CACHE) += mrccache.o
obj-y += northbridge-uclass.o
obj-$(CONFIG_I8259_PIC) += i8259.o
obj-$(CONFIG_I8254_TIMER) += i8254.o
obj-$(CONFIG_PINCTRL_ICH6) += pinctrl_ich6.o
obj-y += pirq_routing.o
obj-y += relocate.o
obj-y += physmem.o
obj-$(CONFIG_INTEL_MID) += pmu.o
obj-$(CONFIG_X86_RAMTEST) += ramtest.o
obj-$(CONFIG_INTEL_MID) += scu.o
obj-y += sections.o
obj-y += sfi.o
obj-y += acpi.o
obj-$(CONFIG_HAVE_ACPI_RESUME) += acpi_s3.o
ifndef CONFIG_QEMU
obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi_table.o
endif
obj-y += tables.o
ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_CMD_ZBOOT) += zimage.o
endif
obj-$(CONFIG_USE_HOB) += hob.o
obj-$(CONFIG_HAVE_FSP) += fsp/
obj-$(CONFIG_FSP_VERSION1) += fsp1/
obj-$(CONFIG_FSP_VERSION2) += fsp2/
ifdef CONFIG_SPL_BUILD
ifdef CONFIG_TPL_BUILD
obj-y += tpl.o
else
obj-y += spl.o
endif
endif
lib-$(CONFIG_USE_PRIVATE_LIBGCC) += div64.o
ifeq ($(CONFIG_$(SPL_)X86_64),)
obj-$(CONFIG_EFI_APP) += crt0_ia32_efi.o reloc_ia32_efi.o
endif
ifneq ($(CONFIG_EFI_STUB),)
CFLAGS_REMOVE_reloc_ia32_efi.o += -mregparm=3
CFLAGS_reloc_ia32_efi.o += -fpic -fshort-wchar
# When building for 64-bit we must remove the i386-specific flags
CFLAGS_REMOVE_reloc_x86_64_efi.o += -mregparm=3 -march=i386 -m32
CFLAGS_reloc_x86_64_efi.o += -fpic -fshort-wchar -m64
AFLAGS_REMOVE_crt0_x86_64_efi.o += -mregparm=3 -march=i386 -m32
AFLAGS_crt0_x86_64_efi.o += -fpic -fshort-wchar -m64
extra-$(CONFIG_EFI_STUB_32BIT) += crt0_ia32_efi.o reloc_ia32_efi.o
extra-$(CONFIG_EFI_STUB_64BIT) += crt0_x86_64_efi.o reloc_x86_64_efi.o
endif
ifdef CONFIG_EFI_STUB
ifeq ($(CONFIG_$(SPL_)X86_64),)
extra-y += $(EFI_CRT0) $(EFI_RELOC)
endif
else
ifndef CONFIG_SPL_BUILD
ifneq ($(CONFIG_CMD_BOOTEFI_SELFTEST)$(CONFIG_CMD_BOOTEFI_HELLO_COMPILE),)
extra-y += $(EFI_CRT0) $(EFI_RELOC)
endif
endif
endif
@@ -0,0 +1,108 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <asm/acpi_table.h>
#include <asm/io.h>
#include <asm/tables.h>
static struct acpi_rsdp *acpi_valid_rsdp(struct acpi_rsdp *rsdp)
{
if (strncmp((char *)rsdp, RSDP_SIG, sizeof(RSDP_SIG) - 1) != 0)
return NULL;
debug("Looking on %p for valid checksum\n", rsdp);
if (table_compute_checksum((void *)rsdp, 20) != 0)
return NULL;
debug("acpi rsdp checksum 1 passed\n");
if ((rsdp->revision > 1) &&
(table_compute_checksum((void *)rsdp, rsdp->length) != 0))
return NULL;
debug("acpi rsdp checksum 2 passed\n");
return rsdp;
}
struct acpi_fadt *acpi_find_fadt(void)
{
char *p, *end;
struct acpi_rsdp *rsdp = NULL;
struct acpi_rsdt *rsdt;
struct acpi_fadt *fadt = NULL;
int i;
/* Find RSDP */
for (p = (char *)ROM_TABLE_ADDR; p < (char *)ROM_TABLE_END; p += 16) {
rsdp = acpi_valid_rsdp((struct acpi_rsdp *)p);
if (rsdp)
break;
}
if (!rsdp)
return NULL;
debug("RSDP found at %p\n", rsdp);
rsdt = (struct acpi_rsdt *)(uintptr_t)rsdp->rsdt_address;
end = (char *)rsdt + rsdt->header.length;
debug("RSDT found at %p ends at %p\n", rsdt, end);
for (i = 0; ((char *)&rsdt->entry[i]) < end; i++) {
fadt = (struct acpi_fadt *)(uintptr_t)rsdt->entry[i];
if (strncmp((char *)fadt, "FACP", 4) == 0)
break;
fadt = NULL;
}
if (!fadt)
return NULL;
debug("FADT found at %p\n", fadt);
return fadt;
}
void *acpi_find_wakeup_vector(struct acpi_fadt *fadt)
{
struct acpi_facs *facs;
void *wake_vec;
debug("Trying to find the wakeup vector...\n");
facs = (struct acpi_facs *)(uintptr_t)fadt->firmware_ctrl;
if (!facs) {
debug("No FACS found, wake up from S3 not possible.\n");
return NULL;
}
debug("FACS found at %p\n", facs);
wake_vec = (void *)(uintptr_t)facs->firmware_waking_vector;
debug("OS waking vector is %p\n", wake_vec);
return wake_vec;
}
void enter_acpi_mode(int pm1_cnt)
{
u16 val = inw(pm1_cnt);
/*
* PM1_CNT register bit0 selects the power management event to be
* either an SCI or SMI interrupt. When this bit is set, then power
* management events will generate an SCI interrupt. When this bit
* is reset power management events will generate an SMI interrupt.
*
* Per ACPI spec, it is the responsibility of the hardware to set
* or reset this bit. OSPM always preserves this bit position.
*
* U-Boot does not support SMI. And we don't have plan to support
* anything running in SMM within U-Boot. To create a legacy-free
* system, and expose ourselves to OSPM as working under ACPI mode
* already, turn this bit on.
*/
outw(val | PM1_CNT_SCI_EN, pm1_cnt);
}
@@ -0,0 +1,83 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <acpi_s3.h>
#include <asm/acpi.h>
#include <asm/acpi_table.h>
#include <asm/post.h>
#include <linux/linkage.h>
DECLARE_GLOBAL_DATA_PTR;
static void asmlinkage (*acpi_do_wakeup)(void *vector) = (void *)WAKEUP_BASE;
static void acpi_jump_to_wakeup(void *vector)
{
/* Copy wakeup trampoline in place */
memcpy((void *)WAKEUP_BASE, __wakeup, __wakeup_size);
printf("Jumping to OS waking vector %p\n", vector);
acpi_do_wakeup(vector);
}
void acpi_resume(struct acpi_fadt *fadt)
{
void *wake_vec;
/* Turn on ACPI mode for S3 */
enter_acpi_mode(fadt->pm1a_cnt_blk);
wake_vec = acpi_find_wakeup_vector(fadt);
/*
* Restore the memory content starting from address 0x1000 which is
* used for the real mode interrupt handler stubs.
*/
memcpy((void *)0x1000, (const void *)gd->arch.backup_mem,
S3_RESERVE_SIZE);
post_code(POST_OS_RESUME);
acpi_jump_to_wakeup(wake_vec);
}
int acpi_s3_reserve(void)
{
/* adjust stack pointer for ACPI S3 resume backup memory */
gd->start_addr_sp -= S3_RESERVE_SIZE;
gd->arch.backup_mem = gd->start_addr_sp;
gd->start_addr_sp &= ~0xf;
/*
* U-Boot sets up the real mode interrupt handler stubs starting from
* address 0x1000. In most cases, the first 640K (0x00000 - 0x9ffff)
* system memory is reported as system RAM in E820 table to the OS.
* (see install_e820_map() implementation for each platform). So OS
* can use these memories whatever it wants.
*
* If U-Boot is in an S3 resume path, care must be taken not to corrupt
* these memorie otherwise OS data gets lost. Testing shows that, on
* Microsoft Windows 10 on Intel Baytrail its wake up vector happens to
* be installed at the same address 0x1000. While on Linux its wake up
* vector does not overlap this memory range, but after resume kernel
* checks low memory range per config option CONFIG_X86_RESERVE_LOW
* which is 64K by default to see whether a memory corruption occurs
* during the suspend/resume (it's harmless, but warnings are shown
* in the kernel dmesg logs).
*
* We cannot simply mark the these memory as reserved in E820 table
* because such configuration makes GRUB complain: unable to allocate
* real mode page. Hence we choose to back up these memories to the
* place where we reserved on our stack for our S3 resume work.
* Before jumping to OS wake up vector, we need restore the original
* content there (see acpi_resume() above).
*/
if (gd->arch.prev_sleep_state == ACPI_S3)
memcpy((void *)gd->arch.backup_mem, (const void *)0x1000,
S3_RESERVE_SIZE);
return 0;
}
@@ -0,0 +1,606 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Based on acpi.c from coreboot
*
* Copyright (C) 2015, Saket Sinha <saket.sinha89@gmail.com>
* Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <cpu.h>
#include <dm.h>
#include <dm/uclass-internal.h>
#include <serial.h>
#include <version.h>
#include <asm/acpi/global_nvs.h>
#include <asm/acpi_table.h>
#include <asm/ioapic.h>
#include <asm/lapic.h>
#include <asm/mpspec.h>
#include <asm/tables.h>
#include <asm/arch/global_nvs.h>
/*
* IASL compiles the dsdt entries and writes the hex values
* to a C array AmlCode[] (see dsdt.c).
*/
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 = NULL;
/* The RSDT is mandatory while the XSDT is not */
rsdt = (struct acpi_rsdt *)rsdp->rsdt_address;
if (rsdp->xsdt_address)
xsdt = (struct acpi_xsdt *)((u32)rsdp->xsdt_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);
/*
* 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
*/
if (xsdt) {
/* 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));
memcpy(facs->signature, "FACS", 4);
facs->length = sizeof(struct acpi_facs);
facs->hardware_signature = 0;
facs->firmware_waking_vector = 0;
facs->global_lock = 0;
facs->flags = 0;
facs->x_firmware_waking_vector_l = 0;
facs->x_firmware_waking_vector_h = 0;
facs->version = 1;
}
static int acpi_create_madt_lapic(struct acpi_madt_lapic *lapic,
u8 cpu, u8 apic)
{
lapic->type = ACPI_APIC_LAPIC;
lapic->length = sizeof(struct acpi_madt_lapic);
lapic->flags = LOCAL_APIC_FLAG_ENABLED;
lapic->processor_id = cpu;
lapic->apic_id = apic;
return lapic->length;
}
int acpi_create_madt_lapics(u32 current)
{
struct udevice *dev;
int total_length = 0;
for (uclass_find_first_device(UCLASS_CPU, &dev);
dev;
uclass_find_next_device(&dev)) {
struct cpu_platdata *plat = dev_get_parent_platdata(dev);
int length = acpi_create_madt_lapic(
(struct acpi_madt_lapic *)current,
plat->cpu_id, plat->cpu_id);
current += length;
total_length += length;
}
return total_length;
}
int acpi_create_madt_ioapic(struct acpi_madt_ioapic *ioapic, u8 id,
u32 addr, u32 gsi_base)
{
ioapic->type = ACPI_APIC_IOAPIC;
ioapic->length = sizeof(struct acpi_madt_ioapic);
ioapic->reserved = 0x00;
ioapic->gsi_base = gsi_base;
ioapic->ioapic_id = id;
ioapic->ioapic_addr = addr;
return ioapic->length;
}
int acpi_create_madt_irqoverride(struct acpi_madt_irqoverride *irqoverride,
u8 bus, u8 source, u32 gsirq, u16 flags)
{
irqoverride->type = ACPI_APIC_IRQ_SRC_OVERRIDE;
irqoverride->length = sizeof(struct acpi_madt_irqoverride);
irqoverride->bus = bus;
irqoverride->source = source;
irqoverride->gsirq = gsirq;
irqoverride->flags = flags;
return irqoverride->length;
}
int acpi_create_madt_lapic_nmi(struct acpi_madt_lapic_nmi *lapic_nmi,
u8 cpu, u16 flags, u8 lint)
{
lapic_nmi->type = ACPI_APIC_LAPIC_NMI;
lapic_nmi->length = sizeof(struct acpi_madt_lapic_nmi);
lapic_nmi->flags = flags;
lapic_nmi->processor_id = cpu;
lapic_nmi->lint = lint;
return lapic_nmi->length;
}
static int acpi_create_madt_irq_overrides(u32 current)
{
struct acpi_madt_irqoverride *irqovr;
u16 sci_flags = MP_IRQ_TRIGGER_LEVEL | MP_IRQ_POLARITY_HIGH;
int length = 0;
irqovr = (void *)current;
length += acpi_create_madt_irqoverride(irqovr, 0, 0, 2, 0);
irqovr = (void *)(current + length);
length += acpi_create_madt_irqoverride(irqovr, 0, 9, 9, sci_flags);
return length;
}
__weak u32 acpi_fill_madt(u32 current)
{
current += acpi_create_madt_lapics(current);
current += acpi_create_madt_ioapic((struct acpi_madt_ioapic *)current,
io_apic_read(IO_APIC_ID) >> 24, IO_APIC_ADDR, 0);
current += acpi_create_madt_irq_overrides(current);
return current;
}
static void acpi_create_madt(struct acpi_madt *madt)
{
struct acpi_table_header *header = &(madt->header);
u32 current = (u32)madt + sizeof(struct acpi_madt);
memset((void *)madt, 0, sizeof(struct acpi_madt));
/* Fill out header fields */
acpi_fill_header(header, "APIC");
header->length = sizeof(struct acpi_madt);
header->revision = 4;
madt->lapic_addr = LAPIC_DEFAULT_BASE;
madt->flags = ACPI_MADT_PCAT_COMPAT;
current = acpi_fill_madt(current);
/* (Re)calculate length and checksum */
header->length = current - (u32)madt;
header->checksum = table_compute_checksum((void *)madt, header->length);
}
int acpi_create_mcfg_mmconfig(struct acpi_mcfg_mmconfig *mmconfig, u32 base,
u16 seg_nr, u8 start, u8 end)
{
memset(mmconfig, 0, sizeof(*mmconfig));
mmconfig->base_address_l = base;
mmconfig->base_address_h = 0;
mmconfig->pci_segment_group_number = seg_nr;
mmconfig->start_bus_number = start;
mmconfig->end_bus_number = end;
return sizeof(struct acpi_mcfg_mmconfig);
}
__weak u32 acpi_fill_mcfg(u32 current)
{
current += acpi_create_mcfg_mmconfig
((struct acpi_mcfg_mmconfig *)current,
CONFIG_PCIE_ECAM_BASE, 0x0, 0x0, 255);
return current;
}
/* MCFG is defined in the PCI Firmware Specification 3.0 */
static void acpi_create_mcfg(struct acpi_mcfg *mcfg)
{
struct acpi_table_header *header = &(mcfg->header);
u32 current = (u32)mcfg + sizeof(struct acpi_mcfg);
memset((void *)mcfg, 0, sizeof(struct acpi_mcfg));
/* Fill out header fields */
acpi_fill_header(header, "MCFG");
header->length = sizeof(struct acpi_mcfg);
header->revision = 1;
current = acpi_fill_mcfg(current);
/* (Re)calculate length and checksum */
header->length = current - (u32)mcfg;
header->checksum = table_compute_checksum((void *)mcfg, header->length);
}
__weak u32 acpi_fill_csrt(u32 current)
{
return current;
}
static void acpi_create_csrt(struct acpi_csrt *csrt)
{
struct acpi_table_header *header = &(csrt->header);
u32 current = (u32)csrt + sizeof(struct acpi_csrt);
memset((void *)csrt, 0, sizeof(struct acpi_csrt));
/* Fill out header fields */
acpi_fill_header(header, "CSRT");
header->length = sizeof(struct acpi_csrt);
header->revision = 0;
current = acpi_fill_csrt(current);
/* (Re)calculate length and checksum */
header->length = current - (u32)csrt;
header->checksum = table_compute_checksum((void *)csrt, header->length);
}
static void acpi_create_spcr(struct acpi_spcr *spcr)
{
struct acpi_table_header *header = &(spcr->header);
struct serial_device_info serial_info = {0};
ulong serial_address, serial_offset;
struct udevice *dev;
uint serial_config;
uint serial_width;
int access_size;
int space_id;
int ret = -ENODEV;
/* Fill out header fields */
acpi_fill_header(header, "SPCR");
header->length = sizeof(struct acpi_spcr);
header->revision = 2;
/* Read the device once, here. It is reused below */
dev = gd->cur_serial_dev;
if (dev)
ret = serial_getinfo(dev, &serial_info);
if (ret)
serial_info.type = SERIAL_CHIP_UNKNOWN;
/* Encode chip type */
switch (serial_info.type) {
case SERIAL_CHIP_16550_COMPATIBLE:
spcr->interface_type = ACPI_DBG2_16550_COMPATIBLE;
break;
case SERIAL_CHIP_UNKNOWN:
default:
spcr->interface_type = ACPI_DBG2_UNKNOWN;
break;
}
/* Encode address space */
switch (serial_info.addr_space) {
case SERIAL_ADDRESS_SPACE_MEMORY:
space_id = ACPI_ADDRESS_SPACE_MEMORY;
break;
case SERIAL_ADDRESS_SPACE_IO:
default:
space_id = ACPI_ADDRESS_SPACE_IO;
break;
}
serial_width = serial_info.reg_width * 8;
serial_offset = serial_info.reg_offset << serial_info.reg_shift;
serial_address = serial_info.addr + serial_offset;
/* Encode register access size */
switch (serial_info.reg_shift) {
case 0:
access_size = ACPI_ACCESS_SIZE_BYTE_ACCESS;
break;
case 1:
access_size = ACPI_ACCESS_SIZE_WORD_ACCESS;
break;
case 2:
access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS;
break;
case 3:
access_size = ACPI_ACCESS_SIZE_QWORD_ACCESS;
break;
default:
access_size = ACPI_ACCESS_SIZE_UNDEFINED;
break;
}
debug("UART type %u @ %lx\n", spcr->interface_type, serial_address);
/* Fill GAS */
spcr->serial_port.space_id = space_id;
spcr->serial_port.bit_width = serial_width;
spcr->serial_port.bit_offset = 0;
spcr->serial_port.access_size = access_size;
spcr->serial_port.addrl = lower_32_bits(serial_address);
spcr->serial_port.addrh = upper_32_bits(serial_address);
/* Encode baud rate */
switch (serial_info.baudrate) {
case 9600:
spcr->baud_rate = 3;
break;
case 19200:
spcr->baud_rate = 4;
break;
case 57600:
spcr->baud_rate = 6;
break;
case 115200:
spcr->baud_rate = 7;
break;
default:
spcr->baud_rate = 0;
break;
}
serial_config = SERIAL_DEFAULT_CONFIG;
if (dev)
ret = serial_getconfig(dev, &serial_config);
spcr->parity = SERIAL_GET_PARITY(serial_config);
spcr->stop_bits = SERIAL_GET_STOP(serial_config);
/* No PCI devices for now */
spcr->pci_device_id = 0xffff;
spcr->pci_vendor_id = 0xffff;
/* Fix checksum */
header->checksum = table_compute_checksum((void *)spcr, header->length);
}
/*
* QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c
*/
ulong write_acpi_tables(ulong start)
{
u32 current;
struct acpi_rsdp *rsdp;
struct acpi_rsdt *rsdt;
struct acpi_xsdt *xsdt;
struct acpi_facs *facs;
struct acpi_table_header *dsdt;
struct acpi_fadt *fadt;
struct acpi_mcfg *mcfg;
struct acpi_madt *madt;
struct acpi_csrt *csrt;
struct acpi_spcr *spcr;
int i;
current = start;
/* Align ACPI tables to 16 byte */
current = ALIGN(current, 16);
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);
debug("ACPI: * FACS\n");
facs = (struct acpi_facs *)current;
current += sizeof(struct acpi_facs);
current = ALIGN(current, 16);
acpi_create_facs(facs);
debug("ACPI: * DSDT\n");
dsdt = (struct acpi_table_header *)current;
memcpy(dsdt, &AmlCode, sizeof(struct acpi_table_header));
current += sizeof(struct acpi_table_header);
memcpy((char *)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);
/* 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;
break;
}
}
/* Update DSDT checksum since we patched the GNVS address */
dsdt->checksum = 0;
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);
debug("ACPI: * FADT\n");
fadt = (struct acpi_fadt *)current;
current += sizeof(struct acpi_fadt);
current = ALIGN(current, 16);
acpi_create_fadt(fadt, facs, dsdt);
acpi_add_table(rsdp, fadt);
debug("ACPI: * MADT\n");
madt = (struct acpi_madt *)current;
acpi_create_madt(madt);
current += madt->header.length;
acpi_add_table(rsdp, madt);
current = ALIGN(current, 16);
debug("ACPI: * MCFG\n");
mcfg = (struct acpi_mcfg *)current;
acpi_create_mcfg(mcfg);
current += mcfg->header.length;
acpi_add_table(rsdp, mcfg);
current = ALIGN(current, 16);
debug("ACPI: * CSRT\n");
csrt = (struct acpi_csrt *)current;
acpi_create_csrt(csrt);
current += csrt->header.length;
acpi_add_table(rsdp, csrt);
current = ALIGN(current, 16);
debug("ACPI: * SPCR\n");
spcr = (struct acpi_spcr *)current;
acpi_create_spcr(spcr);
current += spcr->header.length;
acpi_add_table(rsdp, spcr);
current = ALIGN(current, 16);
debug("current = %x\n", current);
acpi_rsdp_addr = (unsigned long)rsdp;
debug("ACPI: done\n");
return current;
}
ulong acpi_get_rsdp_addr(void)
{
return acpi_rsdp_addr;
}
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Adapted from Linux v2.6.36 kernel: arch/powerpc/kernel/asm-offsets.c
*
* This program is used to generate definitions needed by
* assembly language modules.
*
* We use the technique used in the OSF Mach kernel code:
* generate asm statements containing #defines,
* compile this file to assembler, and then extract the
* #defines from the assembly-language output.
*/
#include <common.h>
#include <linux/kbuild.h>
int main(void)
{
DEFINE(GD_BIST, offsetof(gd_t, arch.bist));
#ifdef CONFIG_USE_HOB
DEFINE(GD_HOB_LIST, offsetof(gd_t, arch.hob_list));
#endif
DEFINE(GD_TABLE, offsetof(gd_t, arch.table));
return 0;
}
@@ -0,0 +1,355 @@
// SPDX-License-Identifier: GPL-2.0
/*
* From Coreboot file device/oprom/realmode/x86.c
*
* Copyright (C) 2007 Advanced Micro Devices, Inc.
* Copyright (C) 2009-2010 coresystems GmbH
*/
#include <common.h>
#include <bios_emul.h>
#include <irq_func.h>
#include <vbe.h>
#include <linux/linkage.h>
#include <asm/cache.h>
#include <asm/processor.h>
#include <asm/i8259.h>
#include <asm/io.h>
#include <asm/post.h>
#include "bios.h"
/* Interrupt handlers for each interrupt the ROM can call */
static int (*int_handler[256])(void);
/* to have a common register file for interrupt handlers */
X86EMU_sysEnv _X86EMU_env;
asmlinkage void (*realmode_call)(u32 addr, u32 eax, u32 ebx, u32 ecx, u32 edx,
u32 esi, u32 edi);
asmlinkage void (*realmode_interrupt)(u32 intno, u32 eax, u32 ebx, u32 ecx,
u32 edx, u32 esi, u32 edi);
static void setup_realmode_code(void)
{
memcpy((void *)REALMODE_BASE, &asm_realmode_code,
asm_realmode_code_size);
/* Ensure the global pointers are relocated properly. */
realmode_call = PTR_TO_REAL_MODE(asm_realmode_call);
realmode_interrupt = PTR_TO_REAL_MODE(__realmode_interrupt);
debug("Real mode stub @%x: %d bytes\n", REALMODE_BASE,
asm_realmode_code_size);
}
static void setup_rombios(void)
{
const char date[] = "06/11/99";
memcpy((void *)0xffff5, &date, 8);
const char ident[] = "PCI_ISA";
memcpy((void *)0xfffd9, &ident, 7);
/* system model: IBM-AT */
writeb(0xfc, 0xffffe);
}
static int int_exception_handler(void)
{
/* compatibility shim */
struct eregs reg_info = {
.eax = M.x86.R_EAX,
.ecx = M.x86.R_ECX,
.edx = M.x86.R_EDX,
.ebx = M.x86.R_EBX,
.esp = M.x86.R_ESP,
.ebp = M.x86.R_EBP,
.esi = M.x86.R_ESI,
.edi = M.x86.R_EDI,
.vector = M.x86.intno,
.error_code = 0,
.eip = M.x86.R_EIP,
.cs = M.x86.R_CS,
.eflags = M.x86.R_EFLG
};
struct eregs *regs = &reg_info;
debug("Oops, exception %d while executing option rom\n", regs->vector);
cpu_hlt();
return 0;
}
static int int_unknown_handler(void)
{
debug("Unsupported software interrupt #0x%x eax 0x%x\n",
M.x86.intno, M.x86.R_EAX);
return -1;
}
/* setup interrupt handlers for mainboard */
void bios_set_interrupt_handler(int intnum, int (*int_func)(void))
{
int_handler[intnum] = int_func;
}
static void setup_interrupt_handlers(void)
{
int i;
/*
* The first 16 int_handler functions are not BIOS services,
* but the CPU-generated exceptions ("hardware interrupts")
*/
for (i = 0; i < 0x10; i++)
int_handler[i] = &int_exception_handler;
/* Mark all other int_handler calls as unknown first */
for (i = 0x10; i < 0x100; i++) {
/* Skip if bios_set_interrupt_handler() isn't called first */
if (int_handler[i])
continue;
/*
* Now set the default functions that are actually needed
* to initialize the option roms. The board may override
* these with bios_set_interrupt_handler()
*/
switch (i) {
case 0x10:
int_handler[0x10] = &int10_handler;
break;
case 0x12:
int_handler[0x12] = &int12_handler;
break;
case 0x16:
int_handler[0x16] = &int16_handler;
break;
case 0x1a:
int_handler[0x1a] = &int1a_handler;
break;
default:
int_handler[i] = &int_unknown_handler;
break;
}
}
}
static void write_idt_stub(void *target, u8 intnum)
{
unsigned char *codeptr;
codeptr = (unsigned char *)target;
memcpy(codeptr, &__idt_handler, __idt_handler_size);
codeptr[3] = intnum; /* modify int# in the code stub. */
}
static void setup_realmode_idt(void)
{
struct realmode_idt *idts = NULL;
int i;
/*
* Copy IDT stub code for each interrupt. This might seem wasteful
* but it is really simple
*/
for (i = 0; i < 256; i++) {
idts[i].cs = 0;
idts[i].offset = 0x1000 + (i * __idt_handler_size);
write_idt_stub((void *)((ulong)idts[i].offset), i);
}
/*
* Many option ROMs use the hard coded interrupt entry points in the
* system bios. So install them at the known locations.
*/
/* int42 is the relocated int10 */
write_idt_stub((void *)0xff065, 0x42);
/* BIOS Int 11 Handler F000:F84D */
write_idt_stub((void *)0xff84d, 0x11);
/* BIOS Int 12 Handler F000:F841 */
write_idt_stub((void *)0xff841, 0x12);
/* BIOS Int 13 Handler F000:EC59 */
write_idt_stub((void *)0xfec59, 0x13);
/* BIOS Int 14 Handler F000:E739 */
write_idt_stub((void *)0xfe739, 0x14);
/* BIOS Int 15 Handler F000:F859 */
write_idt_stub((void *)0xff859, 0x15);
/* BIOS Int 16 Handler F000:E82E */
write_idt_stub((void *)0xfe82e, 0x16);
/* BIOS Int 17 Handler F000:EFD2 */
write_idt_stub((void *)0xfefd2, 0x17);
/* ROM BIOS Int 1A Handler F000:FE6E */
write_idt_stub((void *)0xffe6e, 0x1a);
}
#ifdef CONFIG_FRAMEBUFFER_SET_VESA_MODE
static u8 vbe_get_mode_info(struct vbe_mode_info *mi)
{
u16 buffer_seg;
u16 buffer_adr;
char *buffer;
debug("VBE: Getting information about VESA mode %04x\n",
mi->video_mode);
buffer = PTR_TO_REAL_MODE(asm_realmode_buffer);
buffer_seg = (((unsigned long)buffer) >> 4) & 0xff00;
buffer_adr = ((unsigned long)buffer) & 0xffff;
realmode_interrupt(0x10, VESA_GET_MODE_INFO, 0x0000, mi->video_mode,
0x0000, buffer_seg, buffer_adr);
memcpy(mi->mode_info_block, buffer, sizeof(struct vbe_mode_info));
mi->valid = true;
return 0;
}
static u8 vbe_set_mode(struct vbe_mode_info *mi)
{
int video_mode = mi->video_mode;
debug("VBE: Setting VESA mode %#04x\n", video_mode);
/* request linear framebuffer mode */
video_mode |= (1 << 14);
/* don't clear the framebuffer, we do that later */
video_mode |= (1 << 15);
realmode_interrupt(0x10, VESA_SET_MODE, video_mode,
0x0000, 0x0000, 0x0000, 0x0000);
return 0;
}
static void vbe_set_graphics(int vesa_mode, struct vbe_mode_info *mode_info)
{
unsigned char *framebuffer;
mode_info->video_mode = (1 << 14) | vesa_mode;
vbe_get_mode_info(mode_info);
framebuffer = (unsigned char *)(ulong)mode_info->vesa.phys_base_ptr;
debug("VBE: resolution: %dx%d@%d\n",
le16_to_cpu(mode_info->vesa.x_resolution),
le16_to_cpu(mode_info->vesa.y_resolution),
mode_info->vesa.bits_per_pixel);
debug("VBE: framebuffer: %p\n", framebuffer);
if (!framebuffer) {
debug("VBE: Mode does not support linear framebuffer\n");
return;
}
mode_info->video_mode &= 0x3ff;
vbe_set_mode(mode_info);
}
#endif /* CONFIG_FRAMEBUFFER_SET_VESA_MODE */
void bios_run_on_x86(struct udevice *dev, unsigned long addr, int vesa_mode,
struct vbe_mode_info *mode_info)
{
pci_dev_t pcidev = dm_pci_get_bdf(dev);
u32 num_dev;
num_dev = PCI_BUS(pcidev) << 8 | PCI_DEV(pcidev) << 3 |
PCI_FUNC(pcidev);
/* Needed to avoid exceptions in some ROMs */
interrupt_init();
/* Set up some legacy information in the F segment */
setup_rombios();
/* Set up C interrupt handlers */
setup_interrupt_handlers();
/* Set up real-mode IDT */
setup_realmode_idt();
/* Make sure the code is placed. */
setup_realmode_code();
debug("Calling Option ROM at %lx, pci device %#x...", addr, num_dev);
/* Option ROM entry point is at OPROM start + 3 */
realmode_call(addr + 0x0003, num_dev, 0xffff, 0x0000, 0xffff, 0x0,
0x0);
debug("done\n");
#ifdef CONFIG_FRAMEBUFFER_SET_VESA_MODE
if (vesa_mode != -1)
vbe_set_graphics(vesa_mode, mode_info);
#endif
}
asmlinkage int interrupt_handler(u32 intnumber, u32 gsfs, u32 dses,
u32 edi, u32 esi, u32 ebp, u32 esp,
u32 ebx, u32 edx, u32 ecx, u32 eax,
u32 cs_ip, u16 stackflags)
{
u32 ip;
u32 cs;
u32 flags;
int ret = 0;
ip = cs_ip & 0xffff;
cs = cs_ip >> 16;
flags = stackflags;
#ifdef CONFIG_REALMODE_DEBUG
debug("oprom: INT# 0x%x\n", intnumber);
debug("oprom: eax: %08x ebx: %08x ecx: %08x edx: %08x\n",
eax, ebx, ecx, edx);
debug("oprom: ebp: %08x esp: %08x edi: %08x esi: %08x\n",
ebp, esp, edi, esi);
debug("oprom: ip: %04x cs: %04x flags: %08x\n",
ip, cs, flags);
debug("oprom: stackflags = %04x\n", stackflags);
#endif
/*
* Fetch arguments from the stack and put them to a place
* suitable for the interrupt handlers
*/
M.x86.R_EAX = eax;
M.x86.R_ECX = ecx;
M.x86.R_EDX = edx;
M.x86.R_EBX = ebx;
M.x86.R_ESP = esp;
M.x86.R_EBP = ebp;
M.x86.R_ESI = esi;
M.x86.R_EDI = edi;
M.x86.intno = intnumber;
M.x86.R_EIP = ip;
M.x86.R_CS = cs;
M.x86.R_EFLG = flags;
/* Call the interrupt handler for this interrupt number */
ret = int_handler[intnumber]();
/*
* This code is quite strange...
*
* Put registers back on the stack. The assembler code will pop them
* later. We force (volatile!) changing the values of the parameters
* of this function. We know that they stay alive on the stack after
* we leave this function.
*/
*(volatile u32 *)&eax = M.x86.R_EAX;
*(volatile u32 *)&ecx = M.x86.R_ECX;
*(volatile u32 *)&edx = M.x86.R_EDX;
*(volatile u32 *)&ebx = M.x86.R_EBX;
*(volatile u32 *)&esi = M.x86.R_ESI;
*(volatile u32 *)&edi = M.x86.R_EDI;
flags = M.x86.R_EFLG;
/* Pass success or error back to our caller via the CARRY flag */
if (ret) {
flags &= ~1; /* no error: clear carry */
} else {
debug("int%02x call returned error\n", intnumber);
flags |= 1; /* error: set carry */
}
*(volatile u16 *)&stackflags = flags;
return ret;
}
@@ -0,0 +1,99 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* From Coreboot file device/oprom/realmode/x86.h
*
* Copyright (C) 2007 Advanced Micro Devices, Inc.
* Copyright (C) 2009-2010 coresystems GmbH
*/
#ifndef _X86_LIB_BIOS_H
#define _X86_LIB_BIOS_H
#include <linux/linkage.h>
#define REALMODE_BASE 0x600
#ifdef __ASSEMBLY__
#define PTR_TO_REAL_MODE(x) (x - asm_realmode_code + REALMODE_BASE)
#else
/* Convert a symbol address to our real mode area */
#define PTR_TO_REAL_MODE(sym)\
(void *)(REALMODE_BASE + ((char *)&(sym) - (char *)&asm_realmode_code))
/*
* The following symbols cannot be used directly. They need to be fixed up
* to point to the correct address location after the code has been copied
* to REALMODE_BASE. Absolute symbols are not used because those symbols are
* relocated by U-Boot.
*/
extern unsigned char asm_realmode_call, __realmode_interrupt;
extern unsigned char asm_realmode_buffer;
#define DOWNTO8(A) \
union { \
struct { \
union { \
struct { \
uint8_t A##l; \
uint8_t A##h; \
} __packed; \
uint16_t A##x; \
} __packed; \
uint16_t h##A##x; \
} __packed; \
uint32_t e##A##x; \
} __packed;
#define DOWNTO16(A) \
union { \
struct { \
uint16_t A; \
uint16_t h##A; \
} __packed; \
uint32_t e##A; \
} __packed;
struct eregs {
DOWNTO8(a);
DOWNTO8(c);
DOWNTO8(d);
DOWNTO8(b);
DOWNTO16(sp);
DOWNTO16(bp);
DOWNTO16(si);
DOWNTO16(di);
uint32_t vector;
uint32_t error_code;
uint32_t eip;
uint32_t cs;
uint32_t eflags;
};
struct realmode_idt {
u16 offset, cs;
};
void x86_exception(struct eregs *info);
/* From x86_asm.S */
extern unsigned char __idt_handler;
extern unsigned int __idt_handler_size;
extern unsigned char asm_realmode_code;
extern unsigned int asm_realmode_code_size;
asmlinkage void (*realmode_call)(u32 addr, u32 eax, u32 ebx, u32 ecx, u32 edx,
u32 esi, u32 edi);
asmlinkage void (*realmode_interrupt)(u32 intno, u32 eax, u32 ebx, u32 ecx,
u32 edx, u32 esi, u32 edi);
int int10_handler(void);
int int12_handler(void);
int int16_handler(void);
int int1a_handler(void);
#endif /*__ASSEMBLY__ */
#endif
@@ -0,0 +1,303 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* From coreboot x86_asm.S, cleaned up substantially
*
* Copyright (C) 2009-2010 coresystems GmbH
*/
#include <asm/processor.h>
#include <asm/processor-flags.h>
#include "bios.h"
#define SEG(segment) $segment * X86_GDT_ENTRY_SIZE
/*
* This is the interrupt handler stub code. It gets copied to the IDT and
* to some fixed addresses in the F segment. Before the code can used,
* it gets patched up by the C function copying it: byte 3 (the $0 in
* movb $0, %al) is overwritten with the interrupt numbers.
*/
.code16
.globl __idt_handler
__idt_handler:
pushal
movb $0, %al /* This instruction gets modified */
ljmp $0, $__interrupt_handler_16bit
.globl __idt_handler_size
__idt_handler_size:
.long . - __idt_handler
.macro setup_registers
/* initial register values */
movl 44(%ebp), %eax
movl %eax, __registers + 0 /* eax */
movl 48(%ebp), %eax
movl %eax, __registers + 4 /* ebx */
movl 52(%ebp), %eax
movl %eax, __registers + 8 /* ecx */
movl 56(%ebp), %eax
movl %eax, __registers + 12 /* edx */
movl 60(%ebp), %eax
movl %eax, __registers + 16 /* esi */
movl 64(%ebp), %eax
movl %eax, __registers + 20 /* edi */
.endm
.macro enter_real_mode
/* Activate the right segment descriptor real mode. */
ljmp SEG(X86_GDT_ENTRY_16BIT_CS), $PTR_TO_REAL_MODE(1f)
1:
.code16
/*
* Load the segment registers with properly configured segment
* descriptors. They will retain these configurations (limits,
* writability, etc.) once protected mode is turned off.
*/
mov SEG(X86_GDT_ENTRY_16BIT_DS), %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss
/* Turn off protection */
movl %cr0, %eax
andl $~X86_CR0_PE, %eax
movl %eax, %cr0
/* Now really going into real mode */
ljmp $0, $PTR_TO_REAL_MODE(1f)
1:
/*
* Set up a stack: Put the stack at the end of page zero. That way
* we can easily share it between real and protected, since the
* 16-bit ESP at segment 0 will work for any case.
*/
mov $0x0, %ax
mov %ax, %ss
/* Load 16 bit IDT */
xor %ax, %ax
mov %ax, %ds
lidt __realmode_idt
.endm
.macro prepare_for_irom
movl $0x1000, %eax
movl %eax, %esp
/* Initialise registers for option rom lcall */
movl __registers + 0, %eax
movl __registers + 4, %ebx
movl __registers + 8, %ecx
movl __registers + 12, %edx
movl __registers + 16, %esi
movl __registers + 20, %edi
/* Set all segments to 0x0000, ds to 0x0040 */
push %ax
xor %ax, %ax
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov SEG(X86_GDT_ENTRY_16BIT_FLAT_DS), %ax
mov %ax, %ds
pop %ax
.endm
.macro enter_protected_mode
/* Go back to protected mode */
movl %cr0, %eax
orl $X86_CR0_PE, %eax
movl %eax, %cr0
/* Now that we are in protected mode jump to a 32 bit code segment */
data32 ljmp SEG(X86_GDT_ENTRY_32BIT_CS), $PTR_TO_REAL_MODE(1f)
1:
.code32
mov SEG(X86_GDT_ENTRY_32BIT_DS), %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %gs
mov %ax, %ss
mov SEG(X86_GDT_ENTRY_32BIT_FS), %ax
mov %ax, %fs
/* restore proper idt */
lidt idt_ptr
.endm
/*
* In order to be independent of U-Boot's position in RAM we relocate a part
* of the code to the first megabyte of RAM, so the CPU can use it in
* real-mode. This code lives at asm_realmode_code.
*/
.globl asm_realmode_code
asm_realmode_code:
/* Realmode IDT pointer structure. */
__realmode_idt = PTR_TO_REAL_MODE(.)
.word 1023 /* 16 bit limit */
.long 0 /* 24 bit base */
.word 0
/* Preserve old stack */
__stack = PTR_TO_REAL_MODE(.)
.long 0
/* Register store for realmode_call and realmode_interrupt */
__registers = PTR_TO_REAL_MODE(.)
.long 0 /* 0 - EAX */
.long 0 /* 4 - EBX */
.long 0 /* 8 - ECX */
.long 0 /* 12 - EDX */
.long 0 /* 16 - ESI */
.long 0 /* 20 - EDI */
/* 256 byte buffer, used by int10 */
.globl asm_realmode_buffer
asm_realmode_buffer:
.skip 256
.code32
.globl asm_realmode_call
asm_realmode_call:
/* save all registers to the stack */
pusha
pushf
movl %esp, __stack
movl %esp, %ebp
/*
* This function is called with regparm=0 and we have to skip the
* 36 bytes from pushf+pusha. Hence start at 40.
* Set up our call instruction.
*/
movl 40(%ebp), %eax
mov %ax, __lcall_instr + 1
andl $0xffff0000, %eax
shrl $4, %eax
mov %ax, __lcall_instr + 3
wbinvd
setup_registers
enter_real_mode
prepare_for_irom
__lcall_instr = PTR_TO_REAL_MODE(.)
.byte 0x9a
.word 0x0000, 0x0000
enter_protected_mode
/* restore stack pointer, eflags and register values and exit */
movl __stack, %esp
popf
popa
ret
.globl __realmode_interrupt
__realmode_interrupt:
/* save all registers to the stack and store the stack pointer */
pusha
pushf
movl %esp, __stack
movl %esp, %ebp
/*
* This function is called with regparm=0 and we have to skip the
* 36 bytes from pushf+pusha. Hence start at 40.
* Prepare interrupt calling code.
*/
movl 40(%ebp), %eax
movb %al, __intXX_instr + 1 /* intno */
setup_registers
enter_real_mode
prepare_for_irom
__intXX_instr = PTR_TO_REAL_MODE(.)
.byte 0xcd, 0x00 /* This becomes intXX */
enter_protected_mode
/* restore stack pointer, eflags and register values and exit */
movl __stack, %esp
popf
popa
ret
/*
* This is the 16-bit interrupt entry point called by the IDT stub code.
*
* Before this code code is called, %eax is pushed to the stack, and the
* interrupt number is loaded into %al. On return this function cleans up
* for its caller.
*/
.code16
__interrupt_handler_16bit = PTR_TO_REAL_MODE(.)
push %ds
push %es
push %fs
push %gs
/* Save real mode SS */
movw %ss, %cs:__realmode_ss
/* Clear DF to not break ABI assumptions */
cld
/*
* Clean up the interrupt number. We could do this in the stub, but
* it would cost two more bytes per stub entry.
*/
andl $0xff, %eax
pushl %eax /* ... and make it the first parameter */
enter_protected_mode
/*
* Now we are in protected mode. We need compute the right ESP based
* on saved real mode SS otherwise interrupt_handler() won't get
* correct parameters from the stack.
*/
movzwl %cs:__realmode_ss, %ecx
shll $4, %ecx
addl %ecx, %esp
/* Call the C interrupt handler */
movl $interrupt_handler, %eax
call *%eax
/* Restore real mode ESP based on saved SS */
movzwl %cs:__realmode_ss, %ecx
shll $4, %ecx
subl %ecx, %esp
enter_real_mode
/* Restore real mode SS */
movw %cs:__realmode_ss, %ss
/*
* Restore all registers, including those manipulated by the C
* handler
*/
popl %eax
pop %gs
pop %fs
pop %es
pop %ds
popal
iret
__realmode_ss = PTR_TO_REAL_MODE(.)
.word 0
.globl asm_realmode_code_size
asm_realmode_code_size:
.long . - asm_realmode_code
@@ -0,0 +1,218 @@
// SPDX-License-Identifier: GPL-2.0
/*
* From Coreboot
*
* Copyright (C) 2001 Ronald G. Minnich
* Copyright (C) 2005 Nick.Barker9@btinternet.com
* Copyright (C) 2007-2009 coresystems GmbH
*/
#include <common.h>
#include <asm/pci.h>
#include "bios_emul.h"
/* errors go in AH. Just set these up so that word assigns will work */
enum {
PCIBIOS_SUCCESSFUL = 0x0000,
PCIBIOS_UNSUPPORTED = 0x8100,
PCIBIOS_BADVENDOR = 0x8300,
PCIBIOS_NODEV = 0x8600,
PCIBIOS_BADREG = 0x8700
};
int int10_handler(void)
{
static u8 cursor_row, cursor_col;
int res = 0;
switch ((M.x86.R_EAX & 0xff00) >> 8) {
case 0x01: /* Set cursor shape */
res = 1;
break;
case 0x02: /* Set cursor position */
if (cursor_row != ((M.x86.R_EDX >> 8) & 0xff) ||
cursor_col >= (M.x86.R_EDX & 0xff)) {
debug("\n");
}
cursor_row = (M.x86.R_EDX >> 8) & 0xff;
cursor_col = M.x86.R_EDX & 0xff;
res = 1;
break;
case 0x03: /* Get cursor position */
M.x86.R_EAX &= 0x00ff;
M.x86.R_ECX = 0x0607;
M.x86.R_EDX = (cursor_row << 8) | cursor_col;
res = 1;
break;
case 0x06: /* Scroll up */
debug("\n");
res = 1;
break;
case 0x08: /* Get Character and Mode at Cursor Position */
M.x86.R_EAX = 0x0f00 | 'A'; /* White on black 'A' */
res = 1;
break;
case 0x09: /* Write Character and attribute */
case 0x0e: /* Write Character */
debug("%c", M.x86.R_EAX & 0xff);
res = 1;
break;
case 0x0f: /* Get video mode */
M.x86.R_EAX = 0x5002; /*80 x 25 */
M.x86.R_EBX &= 0x00ff;
res = 1;
break;
default:
printf("Unknown INT10 function %04x\n", M.x86.R_EAX & 0xffff);
break;
}
return res;
}
int int12_handler(void)
{
M.x86.R_EAX = 64 * 1024;
return 1;
}
int int16_handler(void)
{
int res = 0;
switch ((M.x86.R_EAX & 0xff00) >> 8) {
case 0x00: /* Check for Keystroke */
M.x86.R_EAX = 0x6120; /* Space Bar, Space */
res = 1;
break;
case 0x01: /* Check for Keystroke */
M.x86.R_EFLG |= 1 << 6; /* Zero Flag set (no key available) */
res = 1;
break;
default:
printf("Unknown INT16 function %04x\n", M.x86.R_EAX & 0xffff);
break;
}
return res;
}
#define PCI_CONFIG_SPACE_TYPE1 (1 << 0)
#define PCI_SPECIAL_CYCLE_TYPE1 (1 << 4)
int int1a_handler(void)
{
unsigned short func = (unsigned short)M.x86.R_EAX;
int retval = 1;
unsigned short devid, vendorid, devfn;
struct udevice *dev;
/* Use short to get rid of gabage in upper half of 32-bit register */
short devindex;
unsigned char bus;
pci_dev_t bdf;
u32 dword;
u16 word;
u8 byte, reg;
int ret;
switch (func) {
case 0xb101: /* PCIBIOS Check */
M.x86.R_EDX = 0x20494350; /* ' ICP' */
M.x86.R_EAX &= 0xffff0000; /* Clear AH / AL */
M.x86.R_EAX |= PCI_CONFIG_SPACE_TYPE1 |
PCI_SPECIAL_CYCLE_TYPE1;
/*
* last bus in the system. Hard code to 255 for now.
* dev_enumerate() does not seem to tell us (publically)
*/
M.x86.R_ECX = 0xff;
M.x86.R_EDI = 0x00000000; /* protected mode entry */
retval = 1;
break;
case 0xb102: /* Find Device */
devid = M.x86.R_ECX;
vendorid = M.x86.R_EDX;
devindex = M.x86.R_ESI;
bdf = -1;
ret = dm_pci_find_device(vendorid, devid, devindex, &dev);
if (!ret) {
unsigned short busdevfn;
bdf = dm_pci_get_bdf(dev);
M.x86.R_EAX &= 0xffff00ff; /* Clear AH */
M.x86.R_EAX |= PCIBIOS_SUCCESSFUL;
/*
* busnum is an unsigned char;
* devfn is an int, so we mask it off.
*/
busdevfn = (PCI_BUS(bdf) << 8) | PCI_DEV(bdf) << 3 |
PCI_FUNC(bdf);
debug("0x%x: return 0x%x\n", func, busdevfn);
M.x86.R_EBX = busdevfn;
retval = 1;
} else {
M.x86.R_EAX &= 0xffff00ff; /* Clear AH */
M.x86.R_EAX |= PCIBIOS_NODEV;
retval = 0;
}
break;
case 0xb10a: /* Read Config Dword */
case 0xb109: /* Read Config Word */
case 0xb108: /* Read Config Byte */
case 0xb10d: /* Write Config Dword */
case 0xb10c: /* Write Config Word */
case 0xb10b: /* Write Config Byte */
devfn = M.x86.R_EBX & 0xff;
bus = M.x86.R_EBX >> 8;
reg = M.x86.R_EDI;
bdf = PCI_BDF(bus, devfn >> 3, devfn & 7);
ret = dm_pci_bus_find_bdf(bdf, &dev);
if (ret) {
debug("%s: Device %x not found\n", __func__, bdf);
break;
}
switch (func) {
case 0xb108: /* Read Config Byte */
dm_pci_read_config8(dev, reg, &byte);
M.x86.R_ECX = byte;
break;
case 0xb109: /* Read Config Word */
dm_pci_read_config16(dev, reg, &word);
M.x86.R_ECX = word;
break;
case 0xb10a: /* Read Config Dword */
dm_pci_read_config32(dev, reg, &dword);
M.x86.R_ECX = dword;
break;
case 0xb10b: /* Write Config Byte */
byte = M.x86.R_ECX;
dm_pci_write_config8(dev, reg, byte);
break;
case 0xb10c: /* Write Config Word */
word = M.x86.R_ECX;
dm_pci_write_config16(dev, reg, word);
break;
case 0xb10d: /* Write Config Dword */
dword = M.x86.R_ECX;
dm_pci_write_config32(dev, reg, dword);
break;
}
#ifdef CONFIG_REALMODE_DEBUG
debug("0x%x: bus %d devfn 0x%x reg 0x%x val 0x%x\n", func,
bus, devfn, reg, M.x86.R_ECX);
#endif
M.x86.R_EAX &= 0xffff00ff; /* Clear AH */
M.x86.R_EAX |= PCIBIOS_SUCCESSFUL;
retval = 1;
break;
default:
printf("UNSUPPORTED PCIBIOS FUNCTION 0x%x\n", func);
M.x86.R_EAX &= 0xffff00ff; /* Clear AH */
M.x86.R_EAX |= PCIBIOS_UNSUPPORTED;
retval = 0;
break;
}
return retval;
}
@@ -0,0 +1,221 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2002
* Sysgo Real-Time Solutions, GmbH <www.elinos.com>
* Marius Groeger <mgroeger@sysgo.de>
*
* Copyright (C) 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl)
*/
#include <common.h>
#include <command.h>
#include <dm/device.h>
#include <dm/root.h>
#include <errno.h>
#include <fdt_support.h>
#include <image.h>
#include <u-boot/zlib.h>
#include <asm/bootparam.h>
#include <asm/cpu.h>
#include <asm/byteorder.h>
#include <asm/zimage.h>
#ifdef CONFIG_SYS_COREBOOT
#include <asm/arch/timestamp.h>
#endif
DECLARE_GLOBAL_DATA_PTR;
#define COMMAND_LINE_OFFSET 0x9000
void bootm_announce_and_cleanup(void)
{
printf("\nStarting kernel ...\n\n");
#ifdef CONFIG_SYS_COREBOOT
timestamp_add_now(TS_U_BOOT_START_KERNEL);
#endif
bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
#if CONFIG_IS_ENABLED(BOOTSTAGE_REPORT)
bootstage_report();
#endif
/*
* Call remove function of all devices with a removal flag set.
* This may be useful for last-stage operations, like cancelling
* of DMA operation or releasing device internal buffers.
*/
dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
}
#if defined(CONFIG_OF_LIBFDT) && !defined(CONFIG_OF_NO_KERNEL)
int arch_fixup_memory_node(void *blob)
{
bd_t *bd = gd->bd;
int bank;
u64 start[CONFIG_NR_DRAM_BANKS];
u64 size[CONFIG_NR_DRAM_BANKS];
for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) {
start[bank] = bd->bi_dram[bank].start;
size[bank] = bd->bi_dram[bank].size;
}
return fdt_fixup_memory_banks(blob, start, size, CONFIG_NR_DRAM_BANKS);
}
#endif
/* Subcommand: PREP */
static int boot_prep_linux(bootm_headers_t *images)
{
char *cmd_line_dest = NULL;
image_header_t *hdr;
int is_zimage = 0;
void *data = NULL;
size_t len;
int ret;
#ifdef CONFIG_OF_LIBFDT
if (images->ft_len) {
debug("using: FDT\n");
if (image_setup_linux(images)) {
puts("FDT creation failed! hanging...");
hang();
}
}
#endif
if (images->legacy_hdr_valid) {
hdr = images->legacy_hdr_os;
if (image_check_type(hdr, IH_TYPE_MULTI)) {
ulong os_data, os_len;
/* if multi-part image, we need to get first subimage */
image_multi_getimg(hdr, 0, &os_data, &os_len);
data = (void *)os_data;
len = os_len;
} else {
/* otherwise get image data */
data = (void *)image_get_data(hdr);
len = image_get_data_size(hdr);
}
is_zimage = 1;
#if defined(CONFIG_FIT)
} else if (images->fit_uname_os && is_zimage) {
ret = fit_image_get_data(images->fit_hdr_os,
images->fit_noffset_os,
(const void **)&data, &len);
if (ret) {
puts("Can't get image data/size!\n");
goto error;
}
is_zimage = 1;
#endif
}
if (is_zimage) {
ulong load_address;
char *base_ptr;
base_ptr = (char *)load_zimage(data, len, &load_address);
if (!base_ptr) {
puts("## Kernel loading failed ...\n");
goto error;
}
images->os.load = load_address;
cmd_line_dest = base_ptr + COMMAND_LINE_OFFSET;
images->ep = (ulong)base_ptr;
} else if (images->ep) {
cmd_line_dest = (void *)images->ep + COMMAND_LINE_OFFSET;
} else {
printf("## Kernel loading failed (missing x86 kernel setup) ...\n");
goto error;
}
printf("Setup at %#08lx\n", images->ep);
ret = setup_zimage((void *)images->ep, cmd_line_dest,
0, images->rd_start,
images->rd_end - images->rd_start);
if (ret) {
printf("## Setting up boot parameters failed ...\n");
return 1;
}
return 0;
error:
return 1;
}
int boot_linux_kernel(ulong setup_base, ulong load_address, bool image_64bit)
{
bootm_announce_and_cleanup();
#ifdef CONFIG_SYS_COREBOOT
timestamp_add_now(TS_U_BOOT_START_KERNEL);
#endif
if (image_64bit) {
if (!cpu_has_64bit()) {
puts("Cannot boot 64-bit kernel on 32-bit machine\n");
return -EFAULT;
}
/* At present 64-bit U-Boot does not support booting a
* kernel.
* TODO(sjg@chromium.org): Support booting both 32-bit and
* 64-bit kernels from 64-bit U-Boot.
*/
#if !CONFIG_IS_ENABLED(X86_64)
return cpu_jump_to_64bit(setup_base, load_address);
#endif
} else {
/*
* Set %ebx, %ebp, and %edi to 0, %esi to point to the
* boot_params structure, and then jump to the kernel. We
* assume that %cs is 0x10, 4GB flat, and read/execute, and
* the data segments are 0x18, 4GB flat, and read/write.
* U-Boot is setting them up that way for itself in
* arch/i386/cpu/cpu.c.
*
* Note that we cannot currently boot a kernel while running as
* an EFI application. Please use the payload option for that.
*/
#ifndef CONFIG_EFI_APP
__asm__ __volatile__ (
"movl $0, %%ebp\n"
"cli\n"
"jmp *%[kernel_entry]\n"
:: [kernel_entry]"a"(load_address),
[boot_params] "S"(setup_base),
"b"(0), "D"(0)
);
#endif
}
/* We can't get to here */
return -EFAULT;
}
/* Subcommand: GO */
static int boot_jump_linux(bootm_headers_t *images)
{
debug("## Transferring control to Linux (at address %08lx, kernel %08lx) ...\n",
images->ep, images->os.load);
return boot_linux_kernel(images->ep, images->os.load,
images->os.arch == IH_ARCH_X86_64);
}
int do_bootm_linux(int flag, int argc, char * const argv[],
bootm_headers_t *images)
{
/* No need for those on x86 */
if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
return -1;
if (flag & BOOTM_STATE_OS_PREP)
return boot_prep_linux(images);
if (flag & BOOTM_STATE_OS_GO)
return boot_jump_linux(images);
return boot_jump_linux(images);
}
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2008-2011
* Graeme Russ, <graeme.russ@gmail.com>
*
* (C) Copyright 2002
* Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
*
* (C) Copyright 2002
* Wolfgang Denk, DENX Software Engineering, <wd@denx.de>
*
* (C) Copyright 2002
* Sysgo Real-Time Solutions, GmbH <www.elinos.com>
* Marius Groeger <mgroeger@sysgo.de>
*/
#include <common.h>
#include <command.h>
#include <malloc.h>
#include <asm/u-boot-x86.h>
DECLARE_GLOBAL_DATA_PTR;
unsigned long do_go_exec(ulong (*entry)(int, char * const []),
int argc, char * const argv[])
{
unsigned long ret = 0;
char **argv_tmp;
/*
* x86 does not use a dedicated register to pass the pointer to
* the global_data, so it is instead passed as argv[-1]. By using
* argv[-1], the called 'Application' can use the contents of
* argv natively. However, to safely use argv[-1] a new copy of
* argv is needed with the extra element
*/
argv_tmp = malloc(sizeof(char *) * (argc + 1));
if (argv_tmp) {
argv_tmp[0] = (char *)gd;
memcpy(&argv_tmp[1], argv, (size_t)(sizeof(char *) * argc));
ret = (entry) (argc, &argv_tmp[1]);
free(argv_tmp);
}
return ret;
}
@@ -0,0 +1,171 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <acpi_s3.h>
#include <vbe.h>
#include <asm/coreboot_tables.h>
#include <asm/e820.h>
DECLARE_GLOBAL_DATA_PTR;
int high_table_reserve(void)
{
/* adjust stack pointer to reserve space for configuration tables */
gd->arch.high_table_limit = gd->start_addr_sp;
gd->start_addr_sp -= CONFIG_HIGH_TABLE_SIZE;
gd->arch.high_table_ptr = gd->start_addr_sp;
/* clear the memory */
#ifdef CONFIG_HAVE_ACPI_RESUME
if (gd->arch.prev_sleep_state != ACPI_S3)
#endif
memset((void *)gd->arch.high_table_ptr, 0,
CONFIG_HIGH_TABLE_SIZE);
gd->start_addr_sp &= ~0xf;
return 0;
}
void *high_table_malloc(size_t bytes)
{
u32 new_ptr;
void *ptr;
new_ptr = gd->arch.high_table_ptr + bytes;
if (new_ptr >= gd->arch.high_table_limit)
return NULL;
ptr = (void *)gd->arch.high_table_ptr;
gd->arch.high_table_ptr = new_ptr;
return ptr;
}
/**
* cb_table_init() - initialize a coreboot table header
*
* This fills in the coreboot table header signature and the header bytes.
* Other fields are set to zero.
*
* @cbh: coreboot table header address
*/
static void cb_table_init(struct cb_header *cbh)
{
memset(cbh, 0, sizeof(struct cb_header));
memcpy(cbh->signature, "LBIO", 4);
cbh->header_bytes = sizeof(struct cb_header);
}
/**
* cb_table_add_entry() - add a coreboot table entry
*
* This increases the coreboot table entry size with added table entry length
* and increases entry count by 1.
*
* @cbh: coreboot table header address
* @cbr: to be added table entry address
* @return: pointer to next table entry address
*/
static u32 cb_table_add_entry(struct cb_header *cbh, struct cb_record *cbr)
{
cbh->table_bytes += cbr->size;
cbh->table_entries++;
return (u32)cbr + cbr->size;
}
/**
* cb_table_finalize() - finalize the coreboot table
*
* This calculates the checksum for all coreboot table entries as well as
* the checksum for the coreboot header itself.
*
* @cbh: coreboot table header address
*/
static void cb_table_finalize(struct cb_header *cbh)
{
struct cb_record *cbr = (struct cb_record *)(cbh + 1);
cbh->table_checksum = compute_ip_checksum(cbr, cbh->table_bytes);
cbh->header_checksum = compute_ip_checksum(cbh, cbh->header_bytes);
}
void write_coreboot_table(u32 addr, struct memory_area *cfg_tables)
{
struct cb_header *cbh = (struct cb_header *)addr;
struct cb_record *cbr;
struct cb_memory *mem;
struct cb_memory_range *map;
struct e820_entry e820[32];
struct cb_framebuffer *fb;
struct vesa_mode_info *vesa;
int i, num;
cb_table_init(cbh);
cbr = (struct cb_record *)(cbh + 1);
/*
* Two type of coreboot table entries are generated by us.
* They are 'struct cb_memory' and 'struct cb_framebuffer'.
*/
/* populate memory map table */
mem = (struct cb_memory *)cbr;
mem->tag = CB_TAG_MEMORY;
map = mem->map;
/* first install e820 defined memory maps */
num = install_e820_map(ARRAY_SIZE(e820), e820);
for (i = 0; i < num; i++) {
map->start.lo = e820[i].addr & 0xffffffff;
map->start.hi = e820[i].addr >> 32;
map->size.lo = e820[i].size & 0xffffffff;
map->size.hi = e820[i].size >> 32;
map->type = e820[i].type;
map++;
}
/* then install all configuration tables */
while (cfg_tables->size) {
map->start.lo = cfg_tables->start & 0xffffffff;
map->start.hi = cfg_tables->start >> 32;
map->size.lo = cfg_tables->size & 0xffffffff;
map->size.hi = cfg_tables->size >> 32;
map->type = CB_MEM_TABLE;
map++;
num++;
cfg_tables++;
}
mem->size = num * sizeof(struct cb_memory_range) +
sizeof(struct cb_record);
cbr = (struct cb_record *)cb_table_add_entry(cbh, cbr);
/* populate framebuffer table if we have sane vesa info */
vesa = &mode_info.vesa;
if (vesa->x_resolution && vesa->y_resolution) {
fb = (struct cb_framebuffer *)cbr;
fb->tag = CB_TAG_FRAMEBUFFER;
fb->size = sizeof(struct cb_framebuffer);
fb->x_resolution = vesa->x_resolution;
fb->y_resolution = vesa->y_resolution;
fb->bits_per_pixel = vesa->bits_per_pixel;
fb->bytes_per_line = vesa->bytes_per_scanline;
fb->physical_address = vesa->phys_base_ptr;
fb->red_mask_size = vesa->red_mask_size;
fb->red_mask_pos = vesa->red_mask_pos;
fb->green_mask_size = vesa->green_mask_size;
fb->green_mask_pos = vesa->green_mask_pos;
fb->blue_mask_size = vesa->blue_mask_size;
fb->blue_mask_pos = vesa->blue_mask_pos;
fb->reserved_mask_size = vesa->reserved_mask_size;
fb->reserved_mask_pos = vesa->reserved_mask_pos;
cbr = (struct cb_record *)cb_table_add_entry(cbh, cbr);
}
cb_table_finalize(cbh);
}
@@ -0,0 +1,51 @@
/* SPDX-License-Identifier: BSD-3-Clause */
/*
* crt0-efi-ia32.S - x86 EFI startup code.
*
* Copyright (C) 1999 Hewlett-Packard Co.
* Contributed by David Mosberger <davidm@hpl.hp.com>.
* All rights reserved.
*/
.text
.align 4
.globl _start
_start:
pushl %ebp
movl %esp,%ebp
pushl 12(%ebp) # copy "image" argument
pushl 8(%ebp) # copy "systab" argument
call 0f
0: popl %eax
movl %eax,%ebx
addl $image_base-0b,%eax # %eax = ldbase
addl $_DYNAMIC-0b,%ebx # %ebx = _DYNAMIC
pushl %ebx # pass _DYNAMIC as second argument
pushl %eax # pass ldbase as first argument
call _relocate
popl %ebx
popl %ebx
testl %eax,%eax
jne .exit
call efi_main # call app with "image" and "systab" argument
.exit: leave
ret
/*
* hand-craft a dummy .reloc section so EFI knows it's a relocatable
* executable:
*/
.data
dummy: .long 0
#define IMAGE_REL_ABSOLUTE 0
.section .reloc
.long dummy /* Page RVA */
.long 10 /* Block Size (2*4+2) */
.word (IMAGE_REL_ABSOLUTE << 12) + 0 /* reloc for dummy */
@@ -0,0 +1,50 @@
/* SPDX-License-Identifier: BSD-3-Clause */
/*
* crt0-efi-x86_64.S - x86_64 EFI startup code.
* Copyright (C) 1999 Hewlett-Packard Co.
* Contributed by David Mosberger <davidm@hpl.hp.com>.
* Copyright (C) 2005 Intel Corporation
* Contributed by Fenghua Yu <fenghua.yu@intel.com>.
*
* All rights reserved.
*/
.text
.align 4
.globl _start
_start:
subq $8, %rsp
pushq %rcx
pushq %rdx
lea image_base(%rip), %rcx
lea _DYNAMIC(%rip), %rdx
call _relocate
popq %rdx
popq %rcx
testq %rax, %rax
jnz .exit
call efi_main
.exit:
addq $8, %rsp
ret
/*
* hand-craft a dummy .reloc section so EFI knows it's a relocatable
* executable:
*/
.data
dummy: .long 0
#define IMAGE_REL_ABSOLUTE 0
.section .reloc, "a"
label1:
.long dummy-label1 /* Page RVA */
.long 10 /* Block Size (2*4+2) */
.word (IMAGE_REL_ABSOLUTE << 12) + 0 /* reloc for dummy */
@@ -0,0 +1,112 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
* This file is copied from the coreboot repository as part of
* the libpayload project:
*
* Copyright 2014 Google Inc.
*/
#include <common.h>
union overlay64 {
u64 longw;
struct {
u32 lower;
u32 higher;
} words;
};
u64 __ashldi3(u64 num, unsigned int shift)
{
union overlay64 output;
output.longw = num;
if (shift >= 32) {
output.words.higher = output.words.lower << (shift - 32);
output.words.lower = 0;
} else {
if (!shift)
return num;
output.words.higher = (output.words.higher << shift) |
(output.words.lower >> (32 - shift));
output.words.lower = output.words.lower << shift;
}
return output.longw;
}
u64 __lshrdi3(u64 num, unsigned int shift)
{
union overlay64 output;
output.longw = num;
if (shift >= 32) {
output.words.lower = output.words.higher >> (shift - 32);
output.words.higher = 0;
} else {
if (!shift)
return num;
output.words.lower = output.words.lower >> shift |
(output.words.higher << (32 - shift));
output.words.higher = output.words.higher >> shift;
}
return output.longw;
}
#define MAX_32BIT_UINT ((((u64)1) << 32) - 1)
static u64 _64bit_divide(u64 dividend, u64 divider, u64 *rem_p)
{
u64 result = 0;
/*
* If divider is zero - let the rest of the system care about the
* exception.
*/
if (!divider)
return 1 / (u32)divider;
/* As an optimization, let's not use 64 bit division unless we must. */
if (dividend <= MAX_32BIT_UINT) {
if (divider > MAX_32BIT_UINT) {
result = 0;
if (rem_p)
*rem_p = divider;
} else {
result = (u32)dividend / (u32)divider;
if (rem_p)
*rem_p = (u32)dividend % (u32)divider;
}
return result;
}
while (divider <= dividend) {
u64 locald = divider;
u64 limit = __lshrdi3(dividend, 1);
int shifts = 0;
while (locald <= limit) {
shifts++;
locald = locald + locald;
}
result |= __ashldi3(1, shifts);
dividend -= locald;
}
if (rem_p)
*rem_p = dividend;
return result;
}
u64 __udivdi3(u64 num, u64 den)
{
return _64bit_divide(num, den, NULL);
}
u64 __umoddi3(u64 num, u64 den)
{
u64 v = 0;
_64bit_divide(num, den, &v);
return v;
}
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <efi_loader.h>
#include <asm/e820.h>
DECLARE_GLOBAL_DATA_PTR;
/*
* Install a default e820 table with 4 entries as follows:
*
* 0x000000-0x0a0000 Useable RAM
* 0x0a0000-0x100000 Reserved for ISA
* 0x100000-gd->ram_size Useable RAM
* CONFIG_PCIE_ECAM_BASE PCIe ECAM
*/
__weak unsigned int install_e820_map(unsigned int max_entries,
struct e820_entry *entries)
{
entries[0].addr = 0;
entries[0].size = ISA_START_ADDRESS;
entries[0].type = E820_RAM;
entries[1].addr = ISA_START_ADDRESS;
entries[1].size = ISA_END_ADDRESS - ISA_START_ADDRESS;
entries[1].type = E820_RESERVED;
entries[2].addr = ISA_END_ADDRESS;
entries[2].size = gd->ram_size - ISA_END_ADDRESS;
entries[2].type = E820_RAM;
entries[3].addr = CONFIG_PCIE_ECAM_BASE;
entries[3].size = CONFIG_PCIE_ECAM_SIZE;
entries[3].type = E820_RESERVED;
return 4;
}
#if CONFIG_IS_ENABLED(EFI_LOADER)
void efi_add_known_memory(void)
{
struct e820_entry e820[E820MAX];
unsigned int i, num;
u64 start, pages, ram_top;
int type;
num = install_e820_map(ARRAY_SIZE(e820), e820);
ram_top = (u64)gd->ram_top & ~EFI_PAGE_MASK;
if (!ram_top)
ram_top = 0x100000000ULL;
for (i = 0; i < num; ++i) {
start = e820[i].addr;
switch (e820[i].type) {
case E820_RAM:
type = EFI_CONVENTIONAL_MEMORY;
break;
case E820_RESERVED:
type = EFI_RESERVED_MEMORY_TYPE;
break;
case E820_ACPI:
type = EFI_ACPI_RECLAIM_MEMORY;
break;
case E820_NVS:
type = EFI_ACPI_MEMORY_NVS;
break;
case E820_UNUSABLE:
default:
type = EFI_UNUSABLE_MEMORY;
break;
}
if (type == EFI_CONVENTIONAL_MEMORY) {
efi_add_conventional_memory_map(start,
start + e820[i].size,
ram_top);
} else {
pages = ALIGN(e820[i].size, EFI_PAGE_SIZE)
>> EFI_PAGE_SHIFT;
efi_add_memory_map(start, pages, type, false);
}
}
}
#endif /* CONFIG_IS_ENABLED(EFI_LOADER) */
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
*/
/*
* This library provides CMOS (inside RTC SRAM) access routines at a very
* early stage when driver model is not available yet. Only read access is
* provided. The 16-bit/32-bit read are compatible with driver model RTC
* uclass write ops, that data is stored in little-endian mode.
*/
#include <common.h>
#include <asm/early_cmos.h>
#include <asm/io.h>
u8 cmos_read8(u8 addr)
{
outb(addr, CMOS_IO_PORT);
return inb(CMOS_IO_PORT + 1);
}
u16 cmos_read16(u8 addr)
{
u16 value = 0;
u16 data;
int i;
for (i = 0; i < sizeof(value); i++) {
data = cmos_read8(addr + i);
value |= data << (i << 3);
}
return value;
}
u32 cmos_read32(u8 addr)
{
u32 value = 0;
u32 data;
int i;
for (i = 0; i < sizeof(value); i++) {
data = cmos_read8(addr + i);
value |= data << (i << 3);
}
return value;
}
@@ -0,0 +1,91 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* U-Boot EFI linker script
*
* Modified from usr/lib32/elf_ia32_efi.lds in gnu-efi
*/
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SECTIONS
{
image_base = .;
.hash : { *(.hash) } /* this MUST come first, EFI expects it */
. = ALIGN(4096);
.text :
{
*(.text)
*(.text.*)
*(.gnu.linkonce.t.*)
}
. = ALIGN(4096);
.sdata :
{
*(.got.plt)
*(.got)
*(.srodata)
*(.sdata)
*(.sbss)
*(.scommon)
}
. = ALIGN(4096);
.data :
{
*(.rodata*)
*(.data)
*(.data1)
*(.data.*)
*(.sdata)
*(.got.plt)
*(.got)
/*
* the EFI loader doesn't seem to like a .bss section, so we
* stick it all into .data:
*/
*(.sbss)
*(.scommon)
*(.dynbss)
*(.bss*)
*(COMMON)
/* U-Boot lists and device tree */
. = ALIGN(8);
*(SORT(.u_boot_list*));
. = ALIGN(8);
*(.dtb*);
}
. = ALIGN(4096);
.dynamic : { *(.dynamic) }
. = ALIGN(4096);
.rel :
{
*(.rel.data)
*(.rel.data.*)
*(.rel.got)
*(.rel.stab)
*(.data.rel.ro.local)
*(.data.rel.local)
*(.data.rel.ro)
*(.data.rel*)
*(.rel.u_boot_list*)
}
. = ALIGN(4096);
.reloc : /* This is the PECOFF .reloc section! */
{
*(.reloc)
}
. = ALIGN(4096);
.dynsym : { *(.dynsym) }
. = ALIGN(4096);
.dynstr : { *(.dynstr) }
. = ALIGN(4096);
/DISCARD/ :
{
*(.rel.reloc)
*(.eh_frame)
*(.note.GNU-stack)
}
.comment 0 : { *(.comment) }
}
@@ -0,0 +1,80 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* U-Boot EFI linker script
*
* Modified from usr/lib32/elf_x86_64_efi.lds in gnu-efi
*/
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SECTIONS
{
image_base = .;
.hash : { *(.hash) } /* this MUST come first, EFI expects it */
. = ALIGN(4096);
.eh_frame : {
*(.eh_frame)
}
. = ALIGN(4096);
.text : {
*(.text)
*(.text.*)
*(.gnu.linkonce.t.*)
}
. = ALIGN(4096);
.reloc : {
*(.reloc)
}
. = ALIGN(4096);
.data : {
*(.rodata*)
*(.got.plt)
*(.got)
*(.data*)
*(.sdata)
/* the EFI loader doesn't seem to like a .bss section, so we stick
* it all into .data: */
*(.sbss)
*(.scommon)
*(.dynbss)
*(.bss*)
*(COMMON)
*(.rel.local)
/* U-Boot lists and device tree */
. = ALIGN(8);
*(SORT(.u_boot_list*));
. = ALIGN(8);
*(.dtb*);
}
. = ALIGN(4096);
.dynamic : { *(.dynamic) }
. = ALIGN(4096);
.rela : {
*(.rela.data*)
*(.rela.got)
*(.rela.stab)
}
. = ALIGN(4096);
.dynsym : { *(.dynsym) }
. = ALIGN(4096);
.dynstr : { *(.dynstr) }
. = ALIGN(4096);
.ignored.reloc : {
*(.rela.reloc)
*(.eh_frame)
*(.note.GNU-stack)
}
.comment 0 : { *(.comment) }
}
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright 2019 Google LLC
obj-y += fsp_common.o
obj-y += fsp_dram.o
obj-y += fsp_support.o
@@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <acpi_s3.h>
#include <cpu_func.h>
#include <dm.h>
#include <errno.h>
#include <rtc.h>
#include <asm/cmos_layout.h>
#include <asm/early_cmos.h>
#include <asm/io.h>
#include <asm/mrccache.h>
#include <asm/post.h>
#include <asm/processor.h>
#include <asm/fsp/fsp_support.h>
DECLARE_GLOBAL_DATA_PTR;
int checkcpu(void)
{
return 0;
}
int print_cpuinfo(void)
{
post_code(POST_CPU_INFO);
return default_print_cpuinfo();
}
int fsp_init_phase_pci(void)
{
u32 status;
/* call into FspNotify */
debug("Calling into FSP (notify phase INIT_PHASE_PCI): ");
status = fsp_notify(NULL, INIT_PHASE_PCI);
if (status)
debug("fail, error code %x\n", status);
else
debug("OK\n");
return status ? -EPERM : 0;
}
void board_final_cleanup(void)
{
u32 status;
/* call into FspNotify */
debug("Calling into FSP (notify phase INIT_PHASE_BOOT): ");
status = fsp_notify(NULL, INIT_PHASE_BOOT);
if (status)
debug("fail, error code %x\n", status);
else
debug("OK\n");
}
void *fsp_prepare_mrc_cache(void)
{
struct mrc_data_container *cache;
struct mrc_region entry;
int ret;
ret = mrccache_get_region(NULL, &entry);
if (ret)
return NULL;
cache = mrccache_find_current(&entry);
if (!cache)
return NULL;
debug("%s: mrc cache at %p, size %x checksum %04x\n", __func__,
cache->data, cache->data_size, cache->checksum);
return cache->data;
}
#ifdef CONFIG_HAVE_ACPI_RESUME
int fsp_save_s3_stack(void)
{
struct udevice *dev;
int ret;
if (gd->arch.prev_sleep_state == ACPI_S3)
return 0;
ret = uclass_get_device(UCLASS_RTC, 0, &dev);
if (ret) {
debug("Cannot find RTC: err=%d\n", ret);
return -ENODEV;
}
/* Save the stack address to CMOS */
ret = rtc_write32(dev, CMOS_FSP_STACK_ADDR, gd->start_addr_sp);
if (ret) {
debug("Save stack address to CMOS: err=%d\n", ret);
return -EIO;
}
return 0;
}
#endif
@@ -0,0 +1,101 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <handoff.h>
#include <init.h>
#include <asm/fsp/fsp_support.h>
#include <asm/e820.h>
#include <asm/mrccache.h>
#include <asm/post.h>
DECLARE_GLOBAL_DATA_PTR;
int fsp_scan_for_ram_size(void)
{
phys_size_t ram_size = 0;
const struct hob_header *hdr;
struct hob_res_desc *res_desc;
hdr = gd->arch.hob_list;
while (!end_of_hob(hdr)) {
if (hdr->type == HOB_TYPE_RES_DESC) {
res_desc = (struct hob_res_desc *)hdr;
if (res_desc->type == RES_SYS_MEM ||
res_desc->type == RES_MEM_RESERVED)
ram_size += res_desc->len;
}
hdr = get_next_hob(hdr);
}
gd->ram_size = ram_size;
post_code(POST_DRAM);
return 0;
};
int dram_init_banksize(void)
{
gd->bd->bi_dram[0].start = 0;
gd->bd->bi_dram[0].size = gd->ram_size;
return 0;
}
unsigned int install_e820_map(unsigned int max_entries,
struct e820_entry *entries)
{
unsigned int num_entries = 0;
const struct hob_header *hdr;
struct hob_res_desc *res_desc;
hdr = gd->arch.hob_list;
while (!end_of_hob(hdr)) {
if (hdr->type == HOB_TYPE_RES_DESC) {
res_desc = (struct hob_res_desc *)hdr;
entries[num_entries].addr = res_desc->phys_start;
entries[num_entries].size = res_desc->len;
if (res_desc->type == RES_SYS_MEM)
entries[num_entries].type = E820_RAM;
else if (res_desc->type == RES_MEM_RESERVED)
entries[num_entries].type = E820_RESERVED;
num_entries++;
}
hdr = get_next_hob(hdr);
}
/* Mark PCIe ECAM address range as reserved */
entries[num_entries].addr = CONFIG_PCIE_ECAM_BASE;
entries[num_entries].size = CONFIG_PCIE_ECAM_SIZE;
entries[num_entries].type = E820_RESERVED;
num_entries++;
#ifdef CONFIG_HAVE_ACPI_RESUME
/*
* Everything between U-Boot's stack and ram top needs to be
* reserved in order for ACPI S3 resume to work.
*/
entries[num_entries].addr = gd->start_addr_sp - CONFIG_STACK_SIZE;
entries[num_entries].size = gd->ram_top - gd->start_addr_sp +
CONFIG_STACK_SIZE;
entries[num_entries].type = E820_RESERVED;
num_entries++;
#endif
return num_entries;
}
#if CONFIG_IS_ENABLED(HANDOFF) && IS_ENABLED(CONFIG_USE_HOB)
int handoff_arch_save(struct spl_handoff *ho)
{
ho->arch.usable_ram_top = fsp_get_usable_lowmem_top(gd->arch.hob_list);
ho->arch.hob_list = gd->arch.hob_list;
return 0;
}
#endif
@@ -0,0 +1,183 @@
// SPDX-License-Identifier: Intel
/*
* Copyright (C) 2013, Intel Corporation
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <asm/fsp1/fsp_support.h>
#include <asm/post.h>
u32 fsp_get_usable_lowmem_top(const void *hob_list)
{
const struct hob_header *hdr;
struct hob_res_desc *res_desc;
phys_addr_t phys_start;
u32 top;
#ifdef CONFIG_FSP_BROKEN_HOB
struct hob_mem_alloc *res_mem;
phys_addr_t mem_base = 0;
#endif
/* Get the HOB list for processing */
hdr = hob_list;
/* * Collect memory ranges */
top = FSP_LOWMEM_BASE;
while (!end_of_hob(hdr)) {
if (hdr->type == HOB_TYPE_RES_DESC) {
res_desc = (struct hob_res_desc *)hdr;
if (res_desc->type == RES_SYS_MEM) {
phys_start = res_desc->phys_start;
/* Need memory above 1MB to be collected here */
if (phys_start >= FSP_LOWMEM_BASE &&
phys_start < (phys_addr_t)FSP_HIGHMEM_BASE)
top += (u32)(res_desc->len);
}
}
#ifdef CONFIG_FSP_BROKEN_HOB
/*
* Find out the lowest memory base address allocated by FSP
* for the boot service data
*/
if (hdr->type == HOB_TYPE_MEM_ALLOC) {
res_mem = (struct hob_mem_alloc *)hdr;
if (!mem_base)
mem_base = res_mem->mem_base;
if (res_mem->mem_base < mem_base)
mem_base = res_mem->mem_base;
}
#endif
hdr = get_next_hob(hdr);
}
#ifdef CONFIG_FSP_BROKEN_HOB
/*
* Check whether the memory top address is below the FSP HOB list.
* If not, use the lowest memory base address allocated by FSP as
* the memory top address. This is to prevent U-Boot relocation
* overwrites the important boot service data which is used by FSP,
* otherwise the subsequent call to fsp_notify() will fail.
*/
if (top > (u32)hob_list) {
debug("Adjust memory top address due to a buggy FSP\n");
top = (u32)mem_base;
}
#endif
return top;
}
u64 fsp_get_usable_highmem_top(const void *hob_list)
{
const struct hob_header *hdr;
struct hob_res_desc *res_desc;
phys_addr_t phys_start;
u64 top;
/* Get the HOB list for processing */
hdr = hob_list;
/* Collect memory ranges */
top = FSP_HIGHMEM_BASE;
while (!end_of_hob(hdr)) {
if (hdr->type == HOB_TYPE_RES_DESC) {
res_desc = (struct hob_res_desc *)hdr;
if (res_desc->type == RES_SYS_MEM) {
phys_start = res_desc->phys_start;
/* Need memory above 4GB to be collected here */
if (phys_start >= (phys_addr_t)FSP_HIGHMEM_BASE)
top += (u32)(res_desc->len);
}
}
hdr = get_next_hob(hdr);
}
return top;
}
u64 fsp_get_reserved_mem_from_guid(const void *hob_list, u64 *len,
const efi_guid_t *guid)
{
const struct hob_header *hdr;
struct hob_res_desc *res_desc;
/* Get the HOB list for processing */
hdr = hob_list;
/* Collect memory ranges */
while (!end_of_hob(hdr)) {
if (hdr->type == HOB_TYPE_RES_DESC) {
res_desc = (struct hob_res_desc *)hdr;
if (res_desc->type == RES_MEM_RESERVED) {
if (!guidcmp(&res_desc->owner, guid)) {
if (len)
*len = (u32)(res_desc->len);
return (u64)(res_desc->phys_start);
}
}
}
hdr = get_next_hob(hdr);
}
return 0;
}
u32 fsp_get_fsp_reserved_mem(const void *hob_list, u32 *len)
{
const efi_guid_t guid = FSP_HOB_RESOURCE_OWNER_FSP_GUID;
u64 length;
u32 base;
base = (u32)fsp_get_reserved_mem_from_guid(hob_list,
&length, &guid);
if (len && base)
*len = (u32)length;
return base;
}
u32 fsp_get_tseg_reserved_mem(const void *hob_list, u32 *len)
{
const efi_guid_t guid = FSP_HOB_RESOURCE_OWNER_TSEG_GUID;
u64 length;
u32 base;
base = (u32)fsp_get_reserved_mem_from_guid(hob_list,
&length, &guid);
if (len && base)
*len = (u32)length;
return base;
}
void *fsp_get_nvs_data(const void *hob_list, u32 *len)
{
const efi_guid_t guid = FSP_NON_VOLATILE_STORAGE_HOB_GUID;
return hob_get_guid_hob_data(hob_list, len, &guid);
}
void *fsp_get_var_nvs_data(const void *hob_list, u32 *len)
{
const efi_guid_t guid = FSP_VARIABLE_NV_DATA_HOB_GUID;
return hob_get_guid_hob_data(hob_list, len, &guid);
}
void *fsp_get_bootloader_tmp_mem(const void *hob_list, u32 *len)
{
const efi_guid_t guid = FSP_BOOTLOADER_TEMP_MEM_HOB_GUID;
return hob_get_guid_hob_data(hob_list, len, &guid);
}
void *fsp_get_graphics_info(const void *hob_list, u32 *len)
{
const efi_guid_t guid = FSP_GRAPHICS_INFO_HOB_GUID;
return hob_get_guid_hob_data(hob_list, len, &guid);
}
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (C) 2015 Google, Inc
obj-y += fsp_car.o
obj-y += fsp_common.o
obj-y += fsp_dram.o
obj-$(CONFIG_VIDEO_FSP) += fsp_graphics.o
obj-y += fsp_support.o
@@ -0,0 +1,111 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
*/
#include <config.h>
#include <asm/post.h>
.globl car_init
car_init:
/*
* Note: ebp holds the BIST value (built-in self test) so far, but ebp
* will be destroyed through the FSP call, thus we have to test the
* BIST value here before we call into FSP.
*/
test %ebp, %ebp
jz car_init_start
post_code(POST_BIST_FAILURE)
jmp die
car_init_start:
post_code(POST_CAR_START)
lea fsp_find_header_romstack, %esp
jmp fsp_find_header
fsp_find_header_ret:
/* EAX points to FSP_INFO_HEADER */
mov %eax, %ebp
/* sanity test */
cmp $CONFIG_FSP_ADDR, %eax
jb die
/* calculate TempRamInitEntry address */
mov 0x30(%ebp), %eax
add 0x1c(%ebp), %eax
/* call FSP TempRamInitEntry to setup temporary stack */
lea temp_ram_init_romstack, %esp
jmp *%eax
temp_ram_init_ret:
addl $4, %esp
cmp $0, %eax
jnz car_init_fail
post_code(POST_CAR_CPU_CACHE)
/*
* The FSP TempRamInit initializes the ecx and edx registers to
* point to a temporary but writable memory range (Cache-As-RAM).
* ecx: the start of this temporary memory range,
* edx: the end of this range.
*/
/* stack grows down from top of CAR */
movl %edx, %esp
subl $4, %esp
xor %esi, %esi
jmp car_init_done
.global fsp_init_done
fsp_init_done:
/*
* We come here from fsp_continue() with eax pointing to the HOB list.
* Save eax to esi temporarily.
*/
movl %eax, %esi
car_init_done:
/*
* Re-initialize the ebp (BIST) to zero, as we already reach here
* which means we passed BIST testing before.
*/
xorl %ebp, %ebp
jmp car_init_ret
car_init_fail:
post_code(POST_CAR_FAILURE)
die:
hlt
jmp die
hlt
/*
* The function call before CAR initialization is tricky. It cannot
* be called using the 'call' instruction but only the 'jmp' with
* the help of a handcrafted stack in the ROM. The stack needs to
* contain the function return address as well as the parameters.
*/
.balign 4
fsp_find_header_romstack:
.long fsp_find_header_ret
.balign 4
temp_ram_init_romstack:
.long temp_ram_init_ret
.long temp_ram_init_params
temp_ram_init_params:
_dt_ucode_base_size:
/* These next two fields are filled in by binman */
.globl ucode_base
ucode_base: /* Declared in microcode.h */
.long 0 /* microcode base */
.globl ucode_size
ucode_size: /* Declared in microcode.h */
.long 0 /* microcode size */
.long CONFIG_SYS_MONITOR_BASE /* code region base */
.long CONFIG_SYS_MONITOR_LEN /* code region size */
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <acpi_s3.h>
#include <dm.h>
#include <errno.h>
#include <rtc.h>
#include <asm/cmos_layout.h>
#include <asm/early_cmos.h>
#include <asm/io.h>
#include <asm/mrccache.h>
#include <asm/post.h>
#include <asm/processor.h>
#include <asm/fsp1/fsp_support.h>
DECLARE_GLOBAL_DATA_PTR;
int arch_fsp_init(void)
{
void *nvs;
int stack = CONFIG_FSP_TEMP_RAM_ADDR;
int boot_mode = BOOT_FULL_CONFIG;
#ifdef CONFIG_HAVE_ACPI_RESUME
int prev_sleep_state = chipset_prev_sleep_state();
gd->arch.prev_sleep_state = prev_sleep_state;
#endif
if (!gd->arch.hob_list) {
if (IS_ENABLED(CONFIG_ENABLE_MRC_CACHE))
nvs = fsp_prepare_mrc_cache();
else
nvs = NULL;
#ifdef CONFIG_HAVE_ACPI_RESUME
if (prev_sleep_state == ACPI_S3) {
if (nvs == NULL) {
/* If waking from S3 and no cache then */
debug("No MRC cache found in S3 resume path\n");
post_code(POST_RESUME_FAILURE);
/* Clear Sleep Type */
chipset_clear_sleep_state();
/* Reboot */
debug("Rebooting..\n");
outb(SYS_RST | RST_CPU, IO_PORT_RESET);
/* Should not reach here.. */
panic("Reboot System");
}
/*
* DM is not available yet at this point, hence call
* CMOS access library which does not depend on DM.
*/
stack = cmos_read32(CMOS_FSP_STACK_ADDR);
boot_mode = BOOT_ON_S3_RESUME;
}
#endif
/*
* The first time we enter here, call fsp_init().
* Note the execution does not return to this function,
* instead it jumps to fsp_continue().
*/
fsp_init(stack, boot_mode, nvs);
} else {
/*
* The second time we enter here, adjust the size of malloc()
* pool before relocation. Given gd->malloc_base was adjusted
* after the call to board_init_f_init_reserve() in arch/x86/
* cpu/start.S, we should fix up gd->malloc_limit here.
*/
gd->malloc_limit += CONFIG_FSP_SYS_MALLOC_F_LEN;
}
return 0;
}
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <asm/fsp/fsp_support.h>
int dram_init(void)
{
int ret;
/* The FSP has already set up DRAM, so grab the info we need */
ret = fsp_scan_for_ram_size();
if (ret)
return ret;
if (IS_ENABLED(CONFIG_ENABLE_MRC_CACHE))
gd->arch.mrc_output = fsp_get_nvs_data(gd->arch.hob_list,
&gd->arch.mrc_output_len);
return 0;
}
/*
* This function looks for the highest region of memory lower than 4GB which
* has enough space for U-Boot where U-Boot is aligned on a page boundary.
* It overrides the default implementation found elsewhere which simply
* picks the end of ram, wherever that may be. The location of the stack,
* the relocation address, and how far U-Boot is moved by relocation are
* set in the global data structure.
*/
ulong board_get_usable_ram_top(ulong total_size)
{
return fsp_get_usable_lowmem_top(gd->arch.hob_list);
}
@@ -0,0 +1,127 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <dm.h>
#include <vbe.h>
#include <video.h>
#include <asm/fsp1/fsp_support.h>
DECLARE_GLOBAL_DATA_PTR;
struct pixel {
u8 pos;
u8 size;
};
static const struct fsp_framebuffer {
struct pixel red;
struct pixel green;
struct pixel blue;
struct pixel rsvd;
} fsp_framebuffer_format_map[] = {
[pixel_rgbx_8bpc] = { {0, 8}, {8, 8}, {16, 8}, {24, 8} },
[pixel_bgrx_8bpc] = { {16, 8}, {8, 8}, {0, 8}, {24, 8} },
};
static int save_vesa_mode(struct vesa_mode_info *vesa)
{
const struct hob_graphics_info *ginfo;
const struct fsp_framebuffer *fbinfo;
ginfo = fsp_get_graphics_info(gd->arch.hob_list, NULL);
/*
* If there is no graphics info structure, bail out and keep
* running on the serial console.
*
* Note: on some platforms (eg: Braswell), the FSP will not produce
* the graphics info HOB unless you plug some cables to the display
* interface (eg: HDMI) on the board.
*/
if (!ginfo) {
debug("FSP graphics hand-off block not found\n");
return -ENXIO;
}
vesa->x_resolution = ginfo->width;
vesa->y_resolution = ginfo->height;
vesa->bits_per_pixel = 32;
vesa->bytes_per_scanline = ginfo->pixels_per_scanline * 4;
vesa->phys_base_ptr = ginfo->fb_base;
if (ginfo->pixel_format >= pixel_bitmask) {
debug("FSP set unknown framebuffer format: %d\n",
ginfo->pixel_format);
return -EINVAL;
}
fbinfo = &fsp_framebuffer_format_map[ginfo->pixel_format];
vesa->red_mask_size = fbinfo->red.size;
vesa->red_mask_pos = fbinfo->red.pos;
vesa->green_mask_size = fbinfo->green.size;
vesa->green_mask_pos = fbinfo->green.pos;
vesa->blue_mask_size = fbinfo->blue.size;
vesa->blue_mask_pos = fbinfo->blue.pos;
vesa->reserved_mask_size = fbinfo->rsvd.size;
vesa->reserved_mask_pos = fbinfo->rsvd.pos;
return 0;
}
static int fsp_video_probe(struct udevice *dev)
{
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
struct vesa_mode_info *vesa = &mode_info.vesa;
int ret;
printf("Video: ");
/* Initialize vesa_mode_info structure */
ret = save_vesa_mode(vesa);
if (ret)
goto err;
/*
* The framebuffer base address in the FSP graphics info HOB reflects
* the value assigned by the FSP. After PCI enumeration the framebuffer
* base address may be relocated. Let's get the updated one from device.
*
* For IGD, it seems to be always on BAR2.
*/
vesa->phys_base_ptr = dm_pci_read_bar32(dev, 2);
ret = vbe_setup_video_priv(vesa, uc_priv, plat);
if (ret)
goto err;
printf("%dx%dx%d\n", uc_priv->xsize, uc_priv->ysize,
vesa->bits_per_pixel);
return 0;
err:
printf("No video mode configured in FSP!\n");
return ret;
}
static const struct udevice_id fsp_video_ids[] = {
{ .compatible = "fsp-fb" },
{ }
};
U_BOOT_DRIVER(fsp_video) = {
.name = "fsp_video",
.id = UCLASS_VIDEO,
.of_match = fsp_video_ids,
.probe = fsp_video_probe,
};
static struct pci_device_id fsp_video_supported[] = {
{ PCI_DEVICE_CLASS(PCI_CLASS_DISPLAY_VGA << 8, 0xffff00) },
{ },
};
U_BOOT_PCI_DEVICE(fsp_video, fsp_video_supported);
@@ -0,0 +1,199 @@
// SPDX-License-Identifier: Intel
/*
* Copyright (C) 2013, Intel Corporation
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <asm/fsp1/fsp_support.h>
#include <asm/post.h>
struct fsp_header *__attribute__((optimize("O0"))) fsp_find_header(void)
{
/*
* This function may be called before the a stack is established,
* so special care must be taken. First, it cannot declare any local
* variable using stack. Only register variable can be used here.
* Secondly, some compiler version will add prolog or epilog code
* for the C function. If so the function call may not work before
* stack is ready.
*
* GCC 4.8.1 has been verified to be working for the following codes.
*/
volatile register u8 *fsp asm("eax");
/* Initalize the FSP base */
fsp = (u8 *)CONFIG_FSP_ADDR;
/* Check the FV signature, _FVH */
if (((struct fv_header *)fsp)->sign == EFI_FVH_SIGNATURE) {
/* Go to the end of the FV header and align the address */
fsp += ((struct fv_header *)fsp)->ext_hdr_off;
fsp += ((struct fv_ext_header *)fsp)->ext_hdr_size;
fsp = (u8 *)(((u32)fsp + 7) & 0xFFFFFFF8);
} else {
fsp = 0;
}
/* Check the FFS GUID */
if (fsp &&
((struct ffs_file_header *)fsp)->name.b[0] == FSP_GUID_BYTE0 &&
((struct ffs_file_header *)fsp)->name.b[1] == FSP_GUID_BYTE1 &&
((struct ffs_file_header *)fsp)->name.b[2] == FSP_GUID_BYTE2 &&
((struct ffs_file_header *)fsp)->name.b[3] == FSP_GUID_BYTE3 &&
((struct ffs_file_header *)fsp)->name.b[4] == FSP_GUID_BYTE4 &&
((struct ffs_file_header *)fsp)->name.b[5] == FSP_GUID_BYTE5 &&
((struct ffs_file_header *)fsp)->name.b[6] == FSP_GUID_BYTE6 &&
((struct ffs_file_header *)fsp)->name.b[7] == FSP_GUID_BYTE7 &&
((struct ffs_file_header *)fsp)->name.b[8] == FSP_GUID_BYTE8 &&
((struct ffs_file_header *)fsp)->name.b[9] == FSP_GUID_BYTE9 &&
((struct ffs_file_header *)fsp)->name.b[10] == FSP_GUID_BYTE10 &&
((struct ffs_file_header *)fsp)->name.b[11] == FSP_GUID_BYTE11 &&
((struct ffs_file_header *)fsp)->name.b[12] == FSP_GUID_BYTE12 &&
((struct ffs_file_header *)fsp)->name.b[13] == FSP_GUID_BYTE13 &&
((struct ffs_file_header *)fsp)->name.b[14] == FSP_GUID_BYTE14 &&
((struct ffs_file_header *)fsp)->name.b[15] == FSP_GUID_BYTE15) {
/* Add the FFS header size to find the raw section header */
fsp += sizeof(struct ffs_file_header);
} else {
fsp = 0;
}
if (fsp &&
((struct raw_section *)fsp)->type == EFI_SECTION_RAW) {
/* Add the raw section header size to find the FSP header */
fsp += sizeof(struct raw_section);
} else {
fsp = 0;
}
return (struct fsp_header *)fsp;
}
void fsp_continue(u32 status, void *hob_list)
{
post_code(POST_MRC);
assert(status == 0);
/* The boot loader main function entry */
fsp_init_done(hob_list);
}
void fsp_init(u32 stack_top, u32 boot_mode, void *nvs_buf)
{
struct fsp_config_data config_data;
fsp_init_f init;
struct fsp_init_params params;
struct fspinit_rtbuf rt_buf;
struct fsp_header *fsp_hdr;
struct fsp_init_params *params_ptr;
#ifdef CONFIG_FSP_USE_UPD
struct vpd_region *fsp_vpd;
struct upd_region *fsp_upd;
#endif
fsp_hdr = fsp_find_header();
if (fsp_hdr == NULL) {
/* No valid FSP info header was found */
panic("Invalid FSP header");
}
config_data.common.fsp_hdr = fsp_hdr;
config_data.common.stack_top = stack_top;
config_data.common.boot_mode = boot_mode;
#ifdef CONFIG_FSP_USE_UPD
/* Get VPD region start */
fsp_vpd = (struct vpd_region *)(fsp_hdr->img_base +
fsp_hdr->cfg_region_off);
/* Verify the VPD data region is valid */
assert(fsp_vpd->sign == VPD_IMAGE_ID);
fsp_upd = &config_data.fsp_upd;
/* Copy default data from Flash */
memcpy(fsp_upd, (void *)(fsp_hdr->img_base + fsp_vpd->upd_offset),
sizeof(struct upd_region));
/* Verify the UPD data region is valid */
assert(fsp_upd->terminator == UPD_TERMINATOR);
#endif
memset(&rt_buf, 0, sizeof(struct fspinit_rtbuf));
/* Override any configuration if required */
fsp_update_configs(&config_data, &rt_buf);
memset(&params, 0, sizeof(struct fsp_init_params));
params.nvs_buf = nvs_buf;
params.rt_buf = (struct fspinit_rtbuf *)&rt_buf;
params.continuation = (fsp_continuation_f)fsp_asm_continuation;
init = (fsp_init_f)(fsp_hdr->img_base + fsp_hdr->fsp_init);
params_ptr = &params;
post_code(POST_PRE_MRC);
/* Load GDT for FSP */
setup_fsp_gdt();
/*
* Use ASM code to ensure the register value in EAX & EDX
* will be passed into fsp_continue
*/
asm volatile (
"pushl %0;"
"call *%%eax;"
".global fsp_asm_continuation;"
"fsp_asm_continuation:;"
"movl 4(%%esp), %%eax;" /* status */
"movl 8(%%esp), %%edx;" /* hob_list */
"jmp fsp_continue;"
: : "m"(params_ptr), "a"(init)
);
/*
* Should never get here.
* Control will continue from fsp_continue.
* This line below is to prevent the compiler from optimizing
* structure intialization.
*
* DO NOT REMOVE!
*/
init(&params);
}
u32 fsp_notify(struct fsp_header *fsp_hdr, u32 phase)
{
fsp_notify_f notify;
struct fsp_notify_params params;
struct fsp_notify_params *params_ptr;
u32 status;
if (!fsp_hdr)
fsp_hdr = (struct fsp_header *)fsp_find_header();
if (fsp_hdr == NULL) {
/* No valid FSP info header */
panic("Invalid FSP header");
}
notify = (fsp_notify_f)(fsp_hdr->img_base + fsp_hdr->fsp_notify);
params.phase = phase;
params_ptr = &params;
/*
* Use ASM code to ensure correct parameter is on the stack for
* FspNotify as U-Boot is using different ABI from FSP
*/
asm volatile (
"pushl %1;" /* push notify phase */
"call *%%eax;" /* call FspNotify */
"addl $4, %%esp;" /* clean up the stack */
: "=a"(status) : "m"(params_ptr), "a"(notify), "m"(*params_ptr)
);
return status;
}
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: Intel
/*
* Copyright (C) 2013, Intel Corporation
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <asm/hob.h>
/**
* Returns the next instance of a HOB type from the starting HOB.
*
* @type: HOB type to search
* @hob_list: A pointer to the HOB list
*
* @return A HOB object with matching type; Otherwise NULL.
*/
const struct hob_header *hob_get_next_hob(uint type, const void *hob_list)
{
const struct hob_header *hdr;
hdr = hob_list;
/* Parse the HOB list until end of list or matching type is found */
while (!end_of_hob(hdr)) {
if (hdr->type == type)
return hdr;
hdr = get_next_hob(hdr);
}
return NULL;
}
/**
* Returns the next instance of the matched GUID HOB from the starting HOB.
*
* @guid: GUID to search
* @hob_list: A pointer to the HOB list
*
* @return A HOB object with matching GUID; Otherwise NULL.
*/
const struct hob_header *hob_get_next_guid_hob(const efi_guid_t *guid,
const void *hob_list)
{
const struct hob_header *hdr;
struct hob_guid *guid_hob;
hdr = hob_list;
while ((hdr = hob_get_next_hob(HOB_TYPE_GUID_EXT, hdr))) {
guid_hob = (struct hob_guid *)hdr;
if (!guidcmp(guid, &guid_hob->name))
break;
hdr = get_next_hob(hdr);
}
return hdr;
}
/**
* This function retrieves a GUID HOB data buffer and size.
*
* @hob_list: A HOB list pointer.
* @len: A pointer to the GUID HOB data buffer length.
* If the GUID HOB is located, the length will be updated.
* @guid A pointer to HOB GUID.
*
* @return NULL: Failed to find the GUID HOB.
* @return others: GUID HOB data buffer pointer.
*/
void *hob_get_guid_hob_data(const void *hob_list, u32 *len,
const efi_guid_t *guid)
{
const struct hob_header *guid_hob;
guid_hob = hob_get_next_guid_hob(guid, hob_list);
if (!guid_hob)
return NULL;
if (len)
*len = get_guid_hob_data_size(guid_hob);
return get_guid_hob_data(guid_hob);
}
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2002
* Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
*/
#include <common.h>
#include <asm/io.h>
#include <asm/i8254.h>
#define TIMER1_VALUE 18 /* 15.6us */
#define BEEP_FREQUENCY_HZ 440
#define SYSCTL_PORTB 0x61
#define PORTB_BEEP_ENABLE 0x3
static void i8254_set_beep_freq(uint frequency_hz)
{
uint countdown;
countdown = PIT_TICK_RATE / frequency_hz;
outb(countdown & 0xff, PIT_BASE + PIT_T2);
outb((countdown >> 8) & 0xff, PIT_BASE + PIT_T2);
}
int i8254_init(void)
{
/*
* Initialize counter 1, used to refresh request signal.
* This is required for legacy purpose as some codes like
* vgabios utilizes counter 1 to provide delay functionality.
*/
outb(PIT_CMD_CTR1 | PIT_CMD_LOW | PIT_CMD_MODE2,
PIT_BASE + PIT_COMMAND);
outb(TIMER1_VALUE, PIT_BASE + PIT_T1);
/*
* Initialize counter 2, used to drive the speaker.
* To start a beep, set both bit0 and bit1 of port 0x61.
* To stop it, clear both bit0 and bit1 of port 0x61.
*/
outb(PIT_CMD_CTR2 | PIT_CMD_BOTH | PIT_CMD_MODE3,
PIT_BASE + PIT_COMMAND);
i8254_set_beep_freq(BEEP_FREQUENCY_HZ);
return 0;
}
int i8254_enable_beep(uint frequency_hz)
{
if (!frequency_hz)
return -EINVAL;
/* make sure i8254 is setup correctly before generating beeps */
outb(PIT_CMD_CTR2 | PIT_CMD_BOTH | PIT_CMD_MODE3,
PIT_BASE + PIT_COMMAND);
i8254_set_beep_freq(frequency_hz);
setio_8(SYSCTL_PORTB, PORTB_BEEP_ENABLE);
return 0;
}
void i8254_disable_beep(void)
{
clrio_8(SYSCTL_PORTB, PORTB_BEEP_ENABLE);
}
@@ -0,0 +1,128 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2009
* Graeme Russ, <graeme.russ@gmail.com>
*
* (C) Copyright 2002
* Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
*/
/*
* This file provides the interrupt handling functionality for systems
* based on the standard PC/AT architecture using two cascaded i8259
* Programmable Interrupt Controllers.
*/
#include <common.h>
#include <asm/io.h>
#include <asm/i8259.h>
#include <asm/ibmpc.h>
#include <asm/interrupt.h>
int i8259_init(void)
{
u8 i;
/* Mask all interrupts */
outb(0xff, MASTER_PIC + IMR);
outb(0xff, SLAVE_PIC + IMR);
/*
* Master PIC
* Place master PIC interrupts at INT20
*/
outb(ICW1_SEL | ICW1_EICW4, MASTER_PIC + ICW1);
outb(0x20, MASTER_PIC + ICW2);
outb(IR2, MASTER_PIC + ICW3);
outb(ICW4_PM, MASTER_PIC + ICW4);
for (i = 0; i < 8; i++)
outb(OCW2_SEOI | i, MASTER_PIC + OCW2);
/*
* Slave PIC
* Place slave PIC interrupts at INT28
*/
outb(ICW1_SEL | ICW1_EICW4, SLAVE_PIC + ICW1);
outb(0x28, SLAVE_PIC + ICW2);
outb(0x02, SLAVE_PIC + ICW3);
outb(ICW4_PM, SLAVE_PIC + ICW4);
for (i = 0; i < 8; i++)
outb(OCW2_SEOI | i, SLAVE_PIC + OCW2);
/*
* Enable cascaded interrupts by unmasking the cascade IRQ pin of
* the master PIC
*/
unmask_irq(2);
/* Interrupt 9 should be level triggered (SCI). The OS might do this */
configure_irq_trigger(9, true);
return 0;
}
void mask_irq(int irq)
{
int imr_port;
if (irq >= SYS_NUM_IRQS)
return;
if (irq > 7)
imr_port = SLAVE_PIC + IMR;
else
imr_port = MASTER_PIC + IMR;
outb(inb(imr_port) | (1 << (irq & 7)), imr_port);
}
void unmask_irq(int irq)
{
int imr_port;
if (irq >= SYS_NUM_IRQS)
return;
if (irq > 7)
imr_port = SLAVE_PIC + IMR;
else
imr_port = MASTER_PIC + IMR;
outb(inb(imr_port) & ~(1 << (irq & 7)), imr_port);
}
void specific_eoi(int irq)
{
if (irq >= SYS_NUM_IRQS)
return;
if (irq > 7) {
/*
* IRQ is on the slave - Issue a corresponding EOI to the
* slave PIC and an EOI for IRQ2 (the cascade interrupt)
* on the master PIC
*/
outb(OCW2_SEOI | (irq & 7), SLAVE_PIC + OCW2);
irq = SEOI_IR2;
}
outb(OCW2_SEOI | irq, MASTER_PIC + OCW2);
}
void configure_irq_trigger(int int_num, bool is_level_triggered)
{
u16 int_bits = inb(ELCR1) | (((u16)inb(ELCR2)) << 8);
debug("%s: current interrupts are 0x%x\n", __func__, int_bits);
if (is_level_triggered)
int_bits |= (1 << int_num);
else
int_bits &= ~(1 << int_num);
/* Write new values */
debug("%s: try to set interrupts 0x%x\n", __func__, int_bits);
outb((u8)(int_bits & 0xff), ELCR1);
outb((u8)(int_bits >> 8), ELCR2);
}
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2011
* Graeme Russ, <graeme.russ@gmail.com>
*/
#include <common.h>
#include <init.h>
#include <linux/errno.h>
#include <asm/mtrr.h>
DECLARE_GLOBAL_DATA_PTR;
int init_cache_f_r(void)
{
bool do_mtrr = CONFIG_IS_ENABLED(X86_32BIT_INIT) ||
IS_ENABLED(CONFIG_FSP_VERSION2);
int ret;
do_mtrr &= !IS_ENABLED(CONFIG_FSP_VERSION1) &&
!IS_ENABLED(CONFIG_SYS_SLIMBOOTLOADER);
if (do_mtrr) {
ret = mtrr_commit(false);
/*
* If MTRR MSR is not implemented by the processor, just ignore
* it
*/
if (ret && ret != -ENOSYS)
return ret;
}
/* Initialise the CPU cache(s) */
return init_cache();
}
@@ -0,0 +1,154 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2009
* Graeme Russ, <graeme.russ@gmail.com>
*
* (C) Copyright 2007
* Daniel Hellstrom, Gaisler Research, <daniel@gaisler.com>
*
* (C) Copyright 2006
* Detlev Zundel, DENX Software Engineering, <dzu@denx.de>
*
* (C) Copyright -2003
* Wolfgang Denk, DENX Software Engineering, <wd@denx.de>
*
* (C) Copyright 2002
* Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
*
* (C) Copyright 2001
* Josh Huber, Mission Critical Linux, Inc, <huber@mclx.com>
*/
/*
* This file contains the high-level API for the interrupt sub-system
* of the x86 port of U-Boot. Most of the functionality has been
* shamelessly stolen from the leon2 / leon3 ports of U-Boot.
* Daniel Hellstrom, Detlev Zundel, Wolfgang Denk and Josh Huber are
* credited for the corresponding work on those ports. The original
* interrupt handling routines for the x86 port were written by
* Daniel Engström
*/
#include <common.h>
#include <irq_func.h>
#include <asm/interrupt.h>
#if !CONFIG_IS_ENABLED(X86_64)
struct irq_action {
interrupt_handler_t *handler;
void *arg;
unsigned int count;
};
static struct irq_action irq_handlers[SYS_NUM_IRQS] = { {0} };
static int spurious_irq_cnt;
static int spurious_irq;
void irq_install_handler(int irq, interrupt_handler_t *handler, void *arg)
{
int status;
if (irq < 0 || irq >= SYS_NUM_IRQS) {
printf("irq_install_handler: bad irq number %d\n", irq);
return;
}
if (irq_handlers[irq].handler != NULL)
printf("irq_install_handler: 0x%08lx replacing 0x%08lx\n",
(ulong) handler,
(ulong) irq_handlers[irq].handler);
status = disable_interrupts();
irq_handlers[irq].handler = handler;
irq_handlers[irq].arg = arg;
irq_handlers[irq].count = 0;
if (CONFIG_IS_ENABLED(I8259_PIC))
unmask_irq(irq);
if (status)
enable_interrupts();
return;
}
void irq_free_handler(int irq)
{
int status;
if (irq < 0 || irq >= SYS_NUM_IRQS) {
printf("irq_free_handler: bad irq number %d\n", irq);
return;
}
status = disable_interrupts();
if (CONFIG_IS_ENABLED(I8259_PIC))
mask_irq(irq);
irq_handlers[irq].handler = NULL;
irq_handlers[irq].arg = NULL;
if (status)
enable_interrupts();
return;
}
void do_irq(int hw_irq)
{
int irq = hw_irq - 0x20;
if (irq < 0 || irq >= SYS_NUM_IRQS) {
printf("do_irq: bad irq number %d\n", irq);
return;
}
if (irq_handlers[irq].handler) {
if (CONFIG_IS_ENABLED(I8259_PIC))
mask_irq(irq);
irq_handlers[irq].handler(irq_handlers[irq].arg);
irq_handlers[irq].count++;
if (CONFIG_IS_ENABLED(I8259_PIC)) {
unmask_irq(irq);
specific_eoi(irq);
}
} else {
if ((irq & 7) != 7) {
spurious_irq_cnt++;
spurious_irq = irq;
}
}
}
#endif
#if defined(CONFIG_CMD_IRQ)
int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
#if !CONFIG_IS_ENABLED(X86_64)
int irq;
printf("Spurious IRQ: %u, last unknown IRQ: %d\n",
spurious_irq_cnt, spurious_irq);
printf("Interrupt-Information:\n");
printf("Nr Routine Arg Count\n");
for (irq = 0; irq < SYS_NUM_IRQS; irq++) {
if (irq_handlers[irq].handler != NULL) {
printf("%02d %08lx %08lx %d\n",
irq,
(ulong)irq_handlers[irq].handler,
(ulong)irq_handlers[irq].arg,
irq_handlers[irq].count);
}
}
#endif
return 0;
}
#endif
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#include <common.h>
#include <dm.h>
UCLASS_DRIVER(lpc) = {
.id = UCLASS_LPC,
.name = "lpc",
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
.post_bind = dm_scan_fdt_dev,
#endif
};
@@ -0,0 +1,408 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*
* Adapted from coreboot src/arch/x86/boot/mpspec.c
*/
#include <common.h>
#include <cpu.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <asm/cpu.h>
#include <asm/irq.h>
#include <asm/ioapic.h>
#include <asm/lapic.h>
#include <asm/mpspec.h>
#include <asm/tables.h>
#include <dm/uclass-internal.h>
DECLARE_GLOBAL_DATA_PTR;
static bool isa_irq_occupied[16];
struct mp_config_table *mp_write_floating_table(struct mp_floating_table *mf)
{
ulong mc;
memcpy(mf->mpf_signature, MPF_SIGNATURE, 4);
mf->mpf_physptr = (ulong)mf + sizeof(struct mp_floating_table);
mf->mpf_length = 1;
mf->mpf_spec = MPSPEC_V14;
mf->mpf_checksum = 0;
/* We don't use the default configuration table */
mf->mpf_feature1 = 0;
/* Indicate that virtual wire mode is always implemented */
mf->mpf_feature2 = 0;
mf->mpf_feature3 = 0;
mf->mpf_feature4 = 0;
mf->mpf_feature5 = 0;
mf->mpf_checksum = table_compute_checksum(mf, mf->mpf_length * 16);
mc = (ulong)mf + sizeof(struct mp_floating_table);
return (struct mp_config_table *)mc;
}
void mp_config_table_init(struct mp_config_table *mc)
{
memcpy(mc->mpc_signature, MPC_SIGNATURE, 4);
mc->mpc_length = sizeof(struct mp_config_table);
mc->mpc_spec = MPSPEC_V14;
mc->mpc_checksum = 0;
mc->mpc_oemptr = 0;
mc->mpc_oemsize = 0;
mc->mpc_entry_count = 0;
mc->mpc_lapic = LAPIC_DEFAULT_BASE;
mc->mpe_length = 0;
mc->mpe_checksum = 0;
mc->reserved = 0;
/* The oem/product id fields are exactly 8/12 bytes long */
table_fill_string(mc->mpc_oem, CONFIG_SYS_VENDOR, 8, ' ');
table_fill_string(mc->mpc_product, CONFIG_SYS_BOARD, 12, ' ');
}
void mp_write_processor(struct mp_config_table *mc)
{
struct mpc_config_processor *mpc;
struct udevice *dev;
u8 boot_apicid, apicver;
u32 cpusignature, cpufeature;
struct cpuid_result result;
boot_apicid = lapicid();
apicver = lapic_read(LAPIC_LVR) & 0xff;
result = cpuid(1);
cpusignature = result.eax;
cpufeature = result.edx;
for (uclass_find_first_device(UCLASS_CPU, &dev);
dev;
uclass_find_next_device(&dev)) {
struct cpu_platdata *plat = dev_get_parent_platdata(dev);
u8 cpuflag = MPC_CPU_EN;
if (!device_active(dev))
continue;
mpc = (struct mpc_config_processor *)mp_next_mpc_entry(mc);
mpc->mpc_type = MP_PROCESSOR;
mpc->mpc_apicid = plat->cpu_id;
mpc->mpc_apicver = apicver;
if (boot_apicid == plat->cpu_id)
cpuflag |= MPC_CPU_BP;
mpc->mpc_cpuflag = cpuflag;
mpc->mpc_cpusignature = cpusignature;
mpc->mpc_cpufeature = cpufeature;
mpc->mpc_reserved[0] = 0;
mpc->mpc_reserved[1] = 0;
mp_add_mpc_entry(mc, sizeof(*mpc));
}
}
void mp_write_bus(struct mp_config_table *mc, int id, const char *bustype)
{
struct mpc_config_bus *mpc;
mpc = (struct mpc_config_bus *)mp_next_mpc_entry(mc);
mpc->mpc_type = MP_BUS;
mpc->mpc_busid = id;
memcpy(mpc->mpc_bustype, bustype, 6);
mp_add_mpc_entry(mc, sizeof(*mpc));
}
void mp_write_ioapic(struct mp_config_table *mc, int id, int ver, u32 apicaddr)
{
struct mpc_config_ioapic *mpc;
mpc = (struct mpc_config_ioapic *)mp_next_mpc_entry(mc);
mpc->mpc_type = MP_IOAPIC;
mpc->mpc_apicid = id;
mpc->mpc_apicver = ver;
mpc->mpc_flags = MPC_APIC_USABLE;
mpc->mpc_apicaddr = apicaddr;
mp_add_mpc_entry(mc, sizeof(*mpc));
}
void mp_write_intsrc(struct mp_config_table *mc, int irqtype, int irqflag,
int srcbus, int srcbusirq, int dstapic, int dstirq)
{
struct mpc_config_intsrc *mpc;
mpc = (struct mpc_config_intsrc *)mp_next_mpc_entry(mc);
mpc->mpc_type = MP_INTSRC;
mpc->mpc_irqtype = irqtype;
mpc->mpc_irqflag = irqflag;
mpc->mpc_srcbus = srcbus;
mpc->mpc_srcbusirq = srcbusirq;
mpc->mpc_dstapic = dstapic;
mpc->mpc_dstirq = dstirq;
mp_add_mpc_entry(mc, sizeof(*mpc));
}
void mp_write_pci_intsrc(struct mp_config_table *mc, int irqtype,
int srcbus, int dev, int pin, int dstapic, int dstirq)
{
u8 srcbusirq = (dev << 2) | (pin - 1);
mp_write_intsrc(mc, irqtype, MP_IRQ_TRIGGER_LEVEL | MP_IRQ_POLARITY_LOW,
srcbus, srcbusirq, dstapic, dstirq);
}
void mp_write_lintsrc(struct mp_config_table *mc, int irqtype, int irqflag,
int srcbus, int srcbusirq, int destapic, int destlint)
{
struct mpc_config_lintsrc *mpc;
mpc = (struct mpc_config_lintsrc *)mp_next_mpc_entry(mc);
mpc->mpc_type = MP_LINTSRC;
mpc->mpc_irqtype = irqtype;
mpc->mpc_irqflag = irqflag;
mpc->mpc_srcbusid = srcbus;
mpc->mpc_srcbusirq = srcbusirq;
mpc->mpc_destapic = destapic;
mpc->mpc_destlint = destlint;
mp_add_mpc_entry(mc, sizeof(*mpc));
}
void mp_write_address_space(struct mp_config_table *mc,
int busid, int addr_type,
u32 addr_base_low, u32 addr_base_high,
u32 addr_length_low, u32 addr_length_high)
{
struct mp_ext_system_address_space *mpe;
mpe = (struct mp_ext_system_address_space *)mp_next_mpe_entry(mc);
mpe->mpe_type = MPE_SYSTEM_ADDRESS_SPACE;
mpe->mpe_length = sizeof(*mpe);
mpe->mpe_busid = busid;
mpe->mpe_addr_type = addr_type;
mpe->mpe_addr_base_low = addr_base_low;
mpe->mpe_addr_base_high = addr_base_high;
mpe->mpe_addr_length_low = addr_length_low;
mpe->mpe_addr_length_high = addr_length_high;
mp_add_mpe_entry(mc, (struct mp_ext_config *)mpe);
}
void mp_write_bus_hierarchy(struct mp_config_table *mc,
int busid, int bus_info, int parent_busid)
{
struct mp_ext_bus_hierarchy *mpe;
mpe = (struct mp_ext_bus_hierarchy *)mp_next_mpe_entry(mc);
mpe->mpe_type = MPE_BUS_HIERARCHY;
mpe->mpe_length = sizeof(*mpe);
mpe->mpe_busid = busid;
mpe->mpe_bus_info = bus_info;
mpe->mpe_parent_busid = parent_busid;
mpe->reserved[0] = 0;
mpe->reserved[1] = 0;
mpe->reserved[2] = 0;
mp_add_mpe_entry(mc, (struct mp_ext_config *)mpe);
}
void mp_write_compat_address_space(struct mp_config_table *mc, int busid,
int addr_modifier, u32 range_list)
{
struct mp_ext_compat_address_space *mpe;
mpe = (struct mp_ext_compat_address_space *)mp_next_mpe_entry(mc);
mpe->mpe_type = MPE_COMPAT_ADDRESS_SPACE;
mpe->mpe_length = sizeof(*mpe);
mpe->mpe_busid = busid;
mpe->mpe_addr_modifier = addr_modifier;
mpe->mpe_range_list = range_list;
mp_add_mpe_entry(mc, (struct mp_ext_config *)mpe);
}
u32 mptable_finalize(struct mp_config_table *mc)
{
ulong end;
mc->mpe_checksum = table_compute_checksum((void *)mp_next_mpc_entry(mc),
mc->mpe_length);
mc->mpc_checksum = table_compute_checksum(mc, mc->mpc_length);
end = mp_next_mpe_entry(mc);
debug("Write the MP table at: %lx - %lx\n", (ulong)mc, end);
return end;
}
static void mptable_add_isa_interrupts(struct mp_config_table *mc, int bus_isa,
int apicid, int external_int2)
{
int i;
mp_write_intsrc(mc, external_int2 ? MP_INT : MP_EXTINT,
MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
bus_isa, 0, apicid, 0);
mp_write_intsrc(mc, MP_INT, MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
bus_isa, 1, apicid, 1);
mp_write_intsrc(mc, external_int2 ? MP_EXTINT : MP_INT,
MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
bus_isa, 0, apicid, 2);
for (i = 3; i < 16; i++) {
/*
* Do not write ISA interrupt entry if it is already occupied
* by the platform devices.
*/
if (isa_irq_occupied[i])
continue;
mp_write_intsrc(mc, MP_INT,
MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
bus_isa, i, apicid, i);
}
}
/*
* Check duplicated I/O interrupt assignment table entry, to make sure
* there is only one entry with the given bus, device and interrupt pin.
*/
static bool check_dup_entry(struct mpc_config_intsrc *intsrc_base,
int entry_num, int bus, int device, int pin)
{
struct mpc_config_intsrc *intsrc = intsrc_base;
int i;
for (i = 0; i < entry_num; i++) {
if (intsrc->mpc_srcbus == bus &&
intsrc->mpc_srcbusirq == ((device << 2) | (pin - 1)))
break;
intsrc++;
}
return (i == entry_num) ? false : true;
}
/* TODO: move this to driver model */
__weak int mp_determine_pci_dstirq(int bus, int dev, int func, int pirq)
{
/* PIRQ[A-H] are connected to I/O APIC INTPIN#16-23 */
return pirq + 16;
}
static int mptable_add_intsrc(struct mp_config_table *mc,
int bus_isa, int apicid)
{
struct mpc_config_intsrc *intsrc_base;
int intsrc_entries = 0;
const void *blob = gd->fdt_blob;
struct udevice *dev;
int len, count;
const u32 *cell;
int i, ret;
ret = uclass_first_device_err(UCLASS_IRQ, &dev);
if (ret && ret != -ENODEV) {
debug("%s: Cannot find irq router node\n", __func__);
return ret;
}
/* Get I/O interrupt information from device tree */
cell = fdt_getprop(blob, dev_of_offset(dev), "intel,pirq-routing",
&len);
if (!cell)
return -ENOENT;
if ((len % sizeof(struct pirq_routing)) == 0)
count = len / sizeof(struct pirq_routing);
else
return -EINVAL;
intsrc_base = (struct mpc_config_intsrc *)mp_next_mpc_entry(mc);
for (i = 0; i < count; i++) {
struct pirq_routing pr;
int bus, dev, func;
int dstirq;
pr.bdf = fdt_addr_to_cpu(cell[0]);
pr.pin = fdt_addr_to_cpu(cell[1]);
pr.pirq = fdt_addr_to_cpu(cell[2]);
bus = PCI_BUS(pr.bdf);
dev = PCI_DEV(pr.bdf);
func = PCI_FUNC(pr.bdf);
if (check_dup_entry(intsrc_base, intsrc_entries,
bus, dev, pr.pin)) {
debug("found entry for bus %d device %d INT%c, skipping\n",
bus, dev, 'A' + pr.pin - 1);
cell += sizeof(struct pirq_routing) / sizeof(u32);
continue;
}
dstirq = mp_determine_pci_dstirq(bus, dev, func, pr.pirq);
/*
* For PIRQ which is connected to I/O APIC interrupt pin#0-15,
* mark it as occupied so that we can skip it later.
*/
if (dstirq < 16)
isa_irq_occupied[dstirq] = true;
mp_write_pci_intsrc(mc, MP_INT, bus, dev, pr.pin,
apicid, dstirq);
intsrc_entries++;
cell += sizeof(struct pirq_routing) / sizeof(u32);
}
/* Legacy Interrupts */
debug("Writing ISA IRQs\n");
mptable_add_isa_interrupts(mc, bus_isa, apicid, 0);
return 0;
}
static void mptable_add_lintsrc(struct mp_config_table *mc, int bus_isa)
{
mp_write_lintsrc(mc, MP_EXTINT,
MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
bus_isa, 0, MP_APIC_ALL, 0);
mp_write_lintsrc(mc, MP_NMI,
MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
bus_isa, 0, MP_APIC_ALL, 1);
}
ulong write_mp_table(ulong addr)
{
struct mp_config_table *mc;
int ioapic_id, ioapic_ver;
int bus_isa = 0xff;
int ret;
ulong end;
/* 16 byte align the table address */
addr = ALIGN(addr, 16);
/* Write floating table */
mc = mp_write_floating_table((struct mp_floating_table *)addr);
/* Write configuration table header */
mp_config_table_init(mc);
/* Write processor entry */
mp_write_processor(mc);
/* Write bus entry */
mp_write_bus(mc, bus_isa, BUSTYPE_ISA);
/* Write I/O APIC entry */
ioapic_id = io_apic_read(IO_APIC_ID) >> 24;
ioapic_ver = io_apic_read(IO_APIC_VER) & 0xff;
mp_write_ioapic(mc, ioapic_id, ioapic_ver, IO_APIC_ADDR);
/* Write I/O interrupt assignment entry */
ret = mptable_add_intsrc(mc, bus_isa, ioapic_id);
if (ret)
debug("Failed to write I/O interrupt assignment table\n");
/* Write local interrupt assignment entry */
mptable_add_lintsrc(mc, bus_isa);
/* Finalize the MP table */
end = mptable_finalize(mc);
return end;
}
@@ -0,0 +1,285 @@
// SPDX-License-Identifier: GPL-2.0
/*
* From coreboot src/southbridge/intel/bd82x6x/mrccache.c
*
* Copyright (C) 2014 Google Inc.
* Copyright (C) 2015 Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <net.h>
#include <spi.h>
#include <spi_flash.h>
#include <asm/mrccache.h>
DECLARE_GLOBAL_DATA_PTR;
static uint mrc_block_size(uint data_size)
{
uint mrc_size = sizeof(struct mrc_data_container) + data_size;
return ALIGN(mrc_size, MRC_DATA_ALIGN);
}
static struct mrc_data_container *next_mrc_block(
struct mrc_data_container *cache)
{
/* MRC data blocks are aligned within the region */
u8 *region_ptr = (u8 *)cache;
region_ptr += mrc_block_size(cache->data_size);
return (struct mrc_data_container *)region_ptr;
}
static int is_mrc_cache(struct mrc_data_container *cache)
{
return cache && (cache->signature == MRC_DATA_SIGNATURE);
}
struct mrc_data_container *mrccache_find_current(struct mrc_region *entry)
{
struct mrc_data_container *cache, *next;
ulong base_addr, end_addr;
uint id;
base_addr = entry->base + entry->offset;
end_addr = base_addr + entry->length;
cache = NULL;
/* Search for the last filled entry in the region */
for (id = 0, next = (struct mrc_data_container *)base_addr;
is_mrc_cache(next);
id++) {
cache = next;
next = next_mrc_block(next);
if ((ulong)next >= end_addr)
break;
}
if (id-- == 0) {
debug("%s: No valid MRC cache found.\n", __func__);
return NULL;
}
/* Verify checksum */
if (cache->checksum != compute_ip_checksum(cache->data,
cache->data_size)) {
printf("%s: MRC cache checksum mismatch\n", __func__);
return NULL;
}
debug("%s: picked entry %u from cache block\n", __func__, id);
return cache;
}
/**
* find_next_mrc_cache() - get next cache entry
*
* @entry: MRC cache flash area
* @cache: Entry to start from
*
* @return next cache entry if found, NULL if we got to the end
*/
static struct mrc_data_container *find_next_mrc_cache(struct mrc_region *entry,
struct mrc_data_container *cache)
{
ulong base_addr, end_addr;
base_addr = entry->base + entry->offset;
end_addr = base_addr + entry->length;
cache = next_mrc_block(cache);
if ((ulong)cache >= end_addr) {
/* Crossed the boundary */
cache = NULL;
debug("%s: no available entries found\n", __func__);
} else {
debug("%s: picked next entry from cache block at %p\n",
__func__, cache);
}
return cache;
}
int mrccache_update(struct udevice *sf, struct mrc_region *entry,
struct mrc_data_container *cur)
{
struct mrc_data_container *cache;
ulong offset;
ulong base_addr;
int ret;
if (!is_mrc_cache(cur)) {
debug("%s: Cache data not valid\n", __func__);
return -EINVAL;
}
/* Find the last used block */
base_addr = entry->base + entry->offset;
debug("Updating MRC cache data\n");
cache = mrccache_find_current(entry);
if (cache && (cache->data_size == cur->data_size) &&
(!memcmp(cache, cur, cache->data_size + sizeof(*cur)))) {
debug("MRC data in flash is up to date. No update\n");
return -EEXIST;
}
/* Move to the next block, which will be the first unused block */
if (cache)
cache = find_next_mrc_cache(entry, cache);
/*
* If we have got to the end, erase the entire mrc-cache area and start
* again at block 0.
*/
if (!cache) {
debug("Erasing the MRC cache region of %x bytes at %x\n",
entry->length, entry->offset);
ret = spi_flash_erase_dm(sf, entry->offset, entry->length);
if (ret) {
debug("Failed to erase flash region\n");
return ret;
}
cache = (struct mrc_data_container *)base_addr;
}
/* Write the data out */
offset = (ulong)cache - base_addr + entry->offset;
debug("Write MRC cache update to flash at %lx\n", offset);
ret = spi_flash_write_dm(sf, offset, cur->data_size + sizeof(*cur),
cur);
if (ret) {
debug("Failed to write to SPI flash\n");
return ret;
}
return 0;
}
static void mrccache_setup(void *data)
{
struct mrc_data_container *cache = data;
u16 checksum;
cache->signature = MRC_DATA_SIGNATURE;
cache->data_size = gd->arch.mrc_output_len;
checksum = compute_ip_checksum(gd->arch.mrc_output, cache->data_size);
debug("Saving %d bytes for MRC output data, checksum %04x\n",
cache->data_size, checksum);
cache->checksum = checksum;
cache->reserved = 0;
memcpy(cache->data, gd->arch.mrc_output, cache->data_size);
/* gd->arch.mrc_output now points to the container */
gd->arch.mrc_output = (char *)cache;
}
int mrccache_reserve(void)
{
if (!gd->arch.mrc_output_len)
return 0;
/* adjust stack pointer to store pure cache data plus the header */
gd->start_addr_sp -= (gd->arch.mrc_output_len + MRC_DATA_HEADER_SIZE);
mrccache_setup((void *)gd->start_addr_sp);
gd->start_addr_sp &= ~0xf;
return 0;
}
int mrccache_get_region(struct udevice **devp, struct mrc_region *entry)
{
const void *blob = gd->fdt_blob;
int node, mrc_node;
u32 reg[2];
int ret;
/* Find the flash chip within the SPI controller node */
node = fdtdec_next_compatible(blob, 0, COMPAT_GENERIC_SPI_FLASH);
if (node < 0) {
debug("%s: Cannot find SPI flash\n", __func__);
return -ENOENT;
}
if (fdtdec_get_int_array(blob, node, "memory-map", reg, 2)) {
debug("%s: Cannot find memory map\n", __func__);
return -EINVAL;
}
entry->base = reg[0];
/* Find the place where we put the MRC cache */
mrc_node = fdt_subnode_offset(blob, node, "rw-mrc-cache");
if (mrc_node < 0) {
debug("%s: Cannot find node\n", __func__);
return -EPERM;
}
if (fdtdec_get_int_array(blob, mrc_node, "reg", reg, 2)) {
debug("%s: Cannot find address\n", __func__);
return -EINVAL;
}
entry->offset = reg[0];
entry->length = reg[1];
if (devp) {
ret = uclass_get_device_by_of_offset(UCLASS_SPI_FLASH, node,
devp);
debug("ret = %d\n", ret);
if (ret)
return ret;
}
return 0;
}
int mrccache_save(void)
{
struct mrc_data_container *data;
struct mrc_region entry;
struct udevice *sf;
int ret;
if (!gd->arch.mrc_output_len)
return 0;
debug("Saving %d bytes of MRC output data to SPI flash\n",
gd->arch.mrc_output_len);
ret = mrccache_get_region(&sf, &entry);
if (ret)
goto err_entry;
data = (struct mrc_data_container *)gd->arch.mrc_output;
ret = mrccache_update(sf, &entry, data);
if (!ret) {
debug("Saved MRC data with checksum %04x\n", data->checksum);
} else if (ret == -EEXIST) {
debug("MRC data is the same as last time, skipping save\n");
ret = 0;
}
err_entry:
if (ret)
debug("%s: Failed: %d\n", __func__, ret);
return ret;
}
int mrccache_spl_save(void)
{
void *data;
int size;
size = gd->arch.mrc_output_len + MRC_DATA_HEADER_SIZE;
data = malloc(size);
if (!data)
return log_msg_ret("Allocate MRC cache block", -ENOMEM);
mrccache_setup(data);
gd->arch.mrc_output = data;
return mrccache_save();
}
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#include <common.h>
#include <dm.h>
#include <dm/root.h>
UCLASS_DRIVER(northbridge) = {
.id = UCLASS_NORTHBRIDGE,
.name = "northbridge",
};
@@ -0,0 +1,203 @@
/*
* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*/
#include <common.h>
#include <physmem.h>
#include <asm/cpu.h>
#include <linux/compiler.h>
DECLARE_GLOBAL_DATA_PTR;
/* Large pages are 2MB. */
#define LARGE_PAGE_SIZE ((1 << 20) * 2)
/*
* Paging data structures.
*/
struct pdpe {
uint64_t p:1;
uint64_t mbz_0:2;
uint64_t pwt:1;
uint64_t pcd:1;
uint64_t mbz_1:4;
uint64_t avl:3;
uint64_t base:40;
uint64_t mbz_2:12;
};
typedef struct pdpe pdpt_t[512];
struct pde {
uint64_t p:1; /* present */
uint64_t rw:1; /* read/write */
uint64_t us:1; /* user/supervisor */
uint64_t pwt:1; /* page-level writethrough */
uint64_t pcd:1; /* page-level cache disable */
uint64_t a:1; /* accessed */
uint64_t d:1; /* dirty */
uint64_t ps:1; /* page size */
uint64_t g:1; /* global page */
uint64_t avl:3; /* available to software */
uint64_t pat:1; /* page-attribute table */
uint64_t mbz_0:8; /* must be zero */
uint64_t base:31; /* base address */
};
typedef struct pde pdt_t[512];
static pdpt_t pdpt __aligned(4096);
static pdt_t pdts[4] __aligned(4096);
/*
* Map a virtual address to a physical address and optionally invalidate any
* old mapping.
*
* @param virt The virtual address to use.
* @param phys The physical address to use.
* @param invlpg Whether to use invlpg to clear any old mappings.
*/
static void x86_phys_map_page(uintptr_t virt, phys_addr_t phys, int invlpg)
{
/* Extract the two bit PDPT index and the 9 bit PDT index. */
uintptr_t pdpt_idx = (virt >> 30) & 0x3;
uintptr_t pdt_idx = (virt >> 21) & 0x1ff;
/* Set up a handy pointer to the appropriate PDE. */
struct pde *pde = &(pdts[pdpt_idx][pdt_idx]);
memset(pde, 0, sizeof(struct pde));
pde->p = 1;
pde->rw = 1;
pde->us = 1;
pde->ps = 1;
pde->base = phys >> 21;
if (invlpg) {
/* Flush any stale mapping out of the TLBs. */
__asm__ __volatile__(
"invlpg %0\n\t"
:
: "m" (*(uint8_t *)virt)
);
}
}
/* Identity map the lower 4GB and turn on paging with PAE. */
static void x86_phys_enter_paging(void)
{
phys_addr_t page_addr;
unsigned i;
/* Zero out the page tables. */
memset(pdpt, 0, sizeof(pdpt));
memset(pdts, 0, sizeof(pdts));
/* Set up the PDPT. */
for (i = 0; i < ARRAY_SIZE(pdts); i++) {
pdpt[i].p = 1;
pdpt[i].base = ((uintptr_t)&pdts[i]) >> 12;
}
/* Identity map everything up to 4GB. */
for (page_addr = 0; page_addr < (1ULL << 32);
page_addr += LARGE_PAGE_SIZE) {
/* There's no reason to invalidate the TLB with paging off. */
x86_phys_map_page(page_addr, page_addr, 0);
}
cpu_enable_paging_pae((ulong)pdpt);
}
/* Disable paging and PAE mode. */
static void x86_phys_exit_paging(void)
{
cpu_disable_paging_pae();
}
/*
* Set physical memory to a particular value when the whole region fits on one
* page.
*
* @param map_addr The address that starts the physical page.
* @param offset How far into that page to start setting a value.
* @param c The value to set memory to.
* @param size The size in bytes of the area to set.
*/
static void x86_phys_memset_page(phys_addr_t map_addr, uintptr_t offset, int c,
unsigned size)
{
/*
* U-Boot should be far away from the beginning of memory, so that's a
* good place to map our window on top of.
*/
const uintptr_t window = LARGE_PAGE_SIZE;
/* Make sure the window is below U-Boot. */
assert(window + LARGE_PAGE_SIZE <
gd->relocaddr - CONFIG_SYS_MALLOC_LEN - CONFIG_SYS_STACK_SIZE);
/* Map the page into the window and then memset the appropriate part. */
x86_phys_map_page(window, map_addr, 1);
memset((void *)(window + offset), c, size);
}
/*
* A physical memory anologue to memset with matching parameters and return
* value.
*/
phys_addr_t arch_phys_memset(phys_addr_t start, int c, phys_size_t size)
{
const phys_addr_t max_addr = (phys_addr_t)~(uintptr_t)0;
const phys_addr_t orig_start = start;
if (!size)
return orig_start;
/* Handle memory below 4GB. */
if (start <= max_addr) {
phys_size_t low_size = min(max_addr + 1 - start, size);
void *start_ptr = (void *)(uintptr_t)start;
assert(((phys_addr_t)(uintptr_t)start) == start);
memset(start_ptr, c, low_size);
start += low_size;
size -= low_size;
}
/* Use paging and PAE to handle memory above 4GB up to 64GB. */
if (size) {
phys_addr_t map_addr = start & ~(LARGE_PAGE_SIZE - 1);
phys_addr_t offset = start - map_addr;
x86_phys_enter_paging();
/* Handle the first partial page. */
if (offset) {
phys_addr_t end =
min(map_addr + LARGE_PAGE_SIZE, start + size);
phys_size_t cur_size = end - start;
x86_phys_memset_page(map_addr, offset, c, cur_size);
size -= cur_size;
map_addr += LARGE_PAGE_SIZE;
}
/* Handle the complete pages. */
while (size > LARGE_PAGE_SIZE) {
x86_phys_memset_page(map_addr, 0, c, LARGE_PAGE_SIZE);
size -= LARGE_PAGE_SIZE;
map_addr += LARGE_PAGE_SIZE;
}
/* Handle the last partial page. */
if (size)
x86_phys_memset_page(map_addr, 0, c, size);
x86_phys_exit_paging();
}
return orig_start;
}
@@ -0,0 +1,215 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Google, Inc
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <pch.h>
#include <pci.h>
#include <asm/cpu.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/pci.h>
#include <dm/pinctrl.h>
DECLARE_GLOBAL_DATA_PTR;
#define GPIO_USESEL_OFFSET(x) (x)
#define GPIO_IOSEL_OFFSET(x) (x + 4)
#define GPIO_LVL_OFFSET(x) ((x) ? (x) + 8 : 0xc)
#define GPI_INV 0x2c
#define IOPAD_MODE_MASK 0x7
#define IOPAD_PULL_ASSIGN_SHIFT 7
#define IOPAD_PULL_ASSIGN_MASK (0x3 << IOPAD_PULL_ASSIGN_SHIFT)
#define IOPAD_PULL_STRENGTH_SHIFT 9
#define IOPAD_PULL_STRENGTH_MASK (0x3 << IOPAD_PULL_STRENGTH_SHIFT)
static int ich6_pinctrl_set_value(uint16_t base, unsigned offset, int value)
{
if (value)
setio_32(base, 1UL << offset);
else
clrio_32(base, 1UL << offset);
return 0;
}
static int ich6_pinctrl_set_function(uint16_t base, unsigned offset, int func)
{
if (func)
setio_32(base, 1UL << offset);
else
clrio_32(base, 1UL << offset);
return 0;
}
static int ich6_pinctrl_set_direction(uint16_t base, unsigned offset, int dir)
{
if (!dir)
setio_32(base, 1UL << offset);
else
clrio_32(base, 1UL << offset);
return 0;
}
static int ich6_pinctrl_cfg_pin(s32 gpiobase, s32 iobase, int pin_node)
{
bool is_gpio, invert;
u32 gpio_offset[2];
int pad_offset;
int dir, val;
int ret;
/*
* GPIO node is not mandatory, so we only do the pinmuxing if the
* node exists.
*/
ret = fdtdec_get_int_array(gd->fdt_blob, pin_node, "gpio-offset",
gpio_offset, 2);
if (!ret) {
/* Do we want to force the GPIO mode? */
is_gpio = fdtdec_get_bool(gd->fdt_blob, pin_node, "mode-gpio");
if (is_gpio)
ich6_pinctrl_set_function(GPIO_USESEL_OFFSET(gpiobase) +
gpio_offset[0], gpio_offset[1],
1);
dir = fdtdec_get_int(gd->fdt_blob, pin_node, "direction", -1);
if (dir != -1)
ich6_pinctrl_set_direction(GPIO_IOSEL_OFFSET(gpiobase) +
gpio_offset[0], gpio_offset[1],
dir);
val = fdtdec_get_int(gd->fdt_blob, pin_node, "output-value",
-1);
if (val != -1)
ich6_pinctrl_set_value(GPIO_LVL_OFFSET(gpiobase) +
gpio_offset[0], gpio_offset[1],
val);
invert = fdtdec_get_bool(gd->fdt_blob, pin_node, "invert");
if (invert)
setio_32(gpiobase + GPI_INV, 1 << gpio_offset[1]);
debug("gpio %#x bit %d, is_gpio %d, dir %d, val %d, invert %d\n",
gpio_offset[0], gpio_offset[1], is_gpio, dir, val,
invert);
}
/* if iobase is present, let's configure the pad */
if (iobase != -1) {
ulong iobase_addr;
/*
* The offset for the same pin for the IOBASE and GPIOBASE are
* different, so instead of maintaining a lookup table,
* the device tree should provide directly the correct
* value for both mapping.
*/
pad_offset = fdtdec_get_int(gd->fdt_blob, pin_node,
"pad-offset", -1);
if (pad_offset == -1)
return 0;
/* compute the absolute pad address */
iobase_addr = iobase + pad_offset;
/*
* Do we need to set a specific function mode?
* If someone put also 'mode-gpio', this option will
* be just ignored by the controller
*/
val = fdtdec_get_int(gd->fdt_blob, pin_node, "mode-func", -1);
if (val != -1)
clrsetbits_le32(iobase_addr, IOPAD_MODE_MASK, val);
/* Configure the pull-up/down if needed */
val = fdtdec_get_int(gd->fdt_blob, pin_node, "pull-assign", -1);
if (val != -1)
clrsetbits_le32(iobase_addr,
IOPAD_PULL_ASSIGN_MASK,
val << IOPAD_PULL_ASSIGN_SHIFT);
val = fdtdec_get_int(gd->fdt_blob, pin_node, "pull-strength",
-1);
if (val != -1)
clrsetbits_le32(iobase_addr,
IOPAD_PULL_STRENGTH_MASK,
val << IOPAD_PULL_STRENGTH_SHIFT);
debug("%s: pad cfg [0x%x]: %08x\n", __func__, pad_offset,
readl(iobase_addr));
}
return 0;
}
static int ich6_pinctrl_probe(struct udevice *dev)
{
struct udevice *pch;
int pin_node;
int ret;
u32 gpiobase;
u32 iobase = -1;
debug("%s: start\n", __func__);
ret = uclass_first_device(UCLASS_PCH, &pch);
if (ret)
return ret;
if (!pch)
return -ENODEV;
/*
* Get the memory/io base address to configure every pins.
* IOBASE is used to configure the mode/pads
* GPIOBASE is used to configure the direction and default value
*/
ret = pch_get_gpio_base(pch, &gpiobase);
if (ret) {
debug("%s: invalid GPIOBASE address (%08x)\n", __func__,
gpiobase);
return -EINVAL;
}
/*
* Get the IOBASE, this is not mandatory as this is not
* supported by all the CPU
*/
ret = pch_get_io_base(pch, &iobase);
if (ret && ret != -ENOSYS) {
debug("%s: invalid IOBASE address (%08x)\n", __func__, iobase);
return -EINVAL;
}
for (pin_node = fdt_first_subnode(gd->fdt_blob, dev_of_offset(dev));
pin_node > 0;
pin_node = fdt_next_subnode(gd->fdt_blob, pin_node)) {
/* Configure the pin */
ret = ich6_pinctrl_cfg_pin(gpiobase, iobase, pin_node);
if (ret != 0) {
debug("%s: invalid configuration for the pin %d\n",
__func__, pin_node);
return ret;
}
}
debug("%s: done\n", __func__);
return 0;
}
static const struct udevice_id ich6_pinctrl_match[] = {
{ .compatible = "intel,x86-pinctrl", .data = X86_SYSCON_PINCONF },
{ /* sentinel */ }
};
U_BOOT_DRIVER(ich6_pinctrl) = {
.name = "ich6_pinctrl",
.id = UCLASS_SYSCON,
.of_match = ich6_pinctrl_match,
.probe = ich6_pinctrl_probe,
};
@@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*
* Part of this file is ported from coreboot src/arch/x86/boot/pirq_routing.c
*/
#include <common.h>
#include <pci.h>
#include <asm/pci.h>
#include <asm/pirq_routing.h>
static u8 pirq_get_next_free_irq(struct udevice *dev, u8 *pirq, u16 bitmap,
bool irq_already_routed[])
{
int i, link;
u8 irq = 0;
/* IRQ sharing starts from IRQ#3 */
for (i = 3; i < 16; i++) {
/* Can we assign this IRQ? */
if (!((bitmap >> i) & 1))
continue;
/* We can, now let's assume we can use this IRQ */
irq = i;
/* Have we already routed it? */
if (irq_already_routed[irq])
continue;
for (link = 0; link < CONFIG_MAX_PIRQ_LINKS; link++) {
if (pirq_check_irq_routed(dev, link, irq)) {
irq_already_routed[irq] = true;
break;
}
}
/* If it's not yet routed, use it */
if (!irq_already_routed[irq]) {
irq_already_routed[irq] = true;
break;
}
/* But if it was already routed, try the next one */
}
/* Now we get our IRQ */
return irq;
}
void pirq_route_irqs(struct udevice *dev, struct irq_info *irq, int num)
{
unsigned char irq_slot[MAX_INTX_ENTRIES];
unsigned char pirq[CONFIG_MAX_PIRQ_LINKS];
bool irq_already_routed[16];
int i, intx;
memset(pirq, 0, CONFIG_MAX_PIRQ_LINKS);
memset(irq_already_routed, '\0', sizeof(irq_already_routed));
/* Set PCI IRQs */
for (i = 0; i < num; i++) {
debug("PIRQ Entry %d Dev: %d.%x.%d\n", i,
irq->bus, irq->devfn >> 3, irq->devfn & 7);
for (intx = 0; intx < MAX_INTX_ENTRIES; intx++) {
int link = irq->irq[intx].link;
int bitmap = irq->irq[intx].bitmap;
int irq = 0;
debug("INT%c link: %x bitmap: %x ",
'A' + intx, link, bitmap);
if (!bitmap || !link) {
debug("not routed\n");
irq_slot[intx] = irq;
continue;
}
/* translate link value to link number */
link = pirq_translate_link(dev, link);
/* yet not routed */
if (!pirq[link]) {
irq = pirq_get_next_free_irq(dev, pirq, bitmap,
irq_already_routed);
pirq[link] = irq;
} else {
irq = pirq[link];
}
debug("IRQ: %d\n", irq);
irq_slot[intx] = irq;
/* Assign IRQ in the interrupt router */
pirq_assign_irq(dev, link, irq);
}
/* Bus, device, slots IRQs for {A,B,C,D} */
pci_assign_irqs(irq->bus, irq->devfn >> 3, irq_slot);
irq++;
}
for (i = 0; i < CONFIG_MAX_PIRQ_LINKS; i++)
debug("PIRQ%c: %d\n", 'A' + i, pirq[i]);
}
u32 copy_pirq_routing_table(u32 addr, struct irq_routing_table *rt)
{
struct irq_routing_table *rom_rt;
/* Align the table to be 16 byte aligned */
addr = ALIGN(addr, 16);
debug("Copying Interrupt Routing Table to 0x%x\n", addr);
memcpy((void *)(uintptr_t)addr, rt, rt->size);
/*
* We do the sanity check here against the copied table after memcpy,
* as something might go wrong after the memcpy, which is normally
* due to the F segment decode is not turned on to systeam RAM.
*/
rom_rt = (struct irq_routing_table *)(uintptr_t)addr;
if (rom_rt->signature != PIRQ_SIGNATURE ||
rom_rt->version != PIRQ_VERSION || rom_rt->size % 16) {
printf("Interrupt Routing Table not valid\n");
return addr;
}
return addr + rt->size;
}
@@ -0,0 +1,116 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2017 Intel Corporation
*/
#include <common.h>
#include <dm.h>
#include <regmap.h>
#include <syscon.h>
#include <asm/cpu.h>
#include <asm/pmu.h>
#include <linux/errno.h>
#include <linux/io.h>
/* Registers */
struct pmu_regs {
u32 sts;
u32 cmd;
u32 ics;
u32 reserved;
u32 wkc[4];
u32 wks[4];
u32 ssc[4];
u32 sss[4];
};
/* Bits in PMU_REGS_STS */
#define PMU_REGS_STS_BUSY (1 << 8)
struct pmu_mid {
struct pmu_regs *regs;
};
static int pmu_read_status(struct pmu_regs *regs)
{
int retry = 500000;
u32 val;
do {
val = readl(&regs->sts);
if (!(val & PMU_REGS_STS_BUSY))
return 0;
udelay(1);
} while (--retry);
printf("WARNING: PMU still busy\n");
return -EBUSY;
}
static int pmu_power_lss(struct pmu_regs *regs, unsigned int lss, bool on)
{
unsigned int offset = (lss * 2) / 32;
unsigned int shift = (lss * 2) % 32;
u32 ssc;
int ret;
/* Check PMU status */
ret = pmu_read_status(regs);
if (ret)
return ret;
/* Read PMU values */
ssc = readl(&regs->sss[offset]);
/* Modify PMU values */
if (on)
ssc &= ~(0x3 << shift); /* D0 */
else
ssc |= 0x3 << shift; /* D3hot */
/* Write modified PMU values */
writel(ssc, &regs->ssc[offset]);
/* Update modified PMU values */
writel(0x00002201, &regs->cmd);
/* Check PMU status */
return pmu_read_status(regs);
}
int pmu_turn_power(unsigned int lss, bool on)
{
struct pmu_mid *pmu;
struct udevice *dev;
int ret;
ret = syscon_get_by_driver_data(X86_SYSCON_PMU, &dev);
if (ret)
return ret;
pmu = dev_get_priv(dev);
return pmu_power_lss(pmu->regs, lss, on);
}
static int pmu_mid_probe(struct udevice *dev)
{
struct pmu_mid *pmu = dev_get_priv(dev);
pmu->regs = syscon_get_first_range(X86_SYSCON_PMU);
return 0;
}
static const struct udevice_id pmu_mid_match[] = {
{ .compatible = "intel,pmu-mid", .data = X86_SYSCON_PMU },
{ /* sentinel */ }
};
U_BOOT_DRIVER(intel_mid_pmu) = {
.name = "pmu_mid",
.id = UCLASS_SYSCON,
.of_match = pmu_mid_match,
.probe = pmu_mid_probe,
.priv_auto_alloc_size = sizeof(struct pmu_mid),
};
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2014 Google, Inc
*
* From Coreboot src/lib/ramtest.c
*/
#include <common.h>
#include <asm/io.h>
#include <asm/post.h>
static void write_phys(unsigned long addr, u32 value)
{
#if CONFIG_SSE2
asm volatile(
"movnti %1, (%0)"
: /* outputs */
: "r" (addr), "r" (value) /* inputs */
: /* clobbers */
);
#else
writel(value, addr);
#endif
}
static u32 read_phys(unsigned long addr)
{
return readl(addr);
}
static void phys_memory_barrier(void)
{
#if CONFIG_SSE2
/* Needed for movnti */
asm volatile(
"sfence"
:
:
: "memory"
);
#else
asm volatile(""
:
:
: "memory");
#endif
}
void quick_ram_check(void)
{
int fail = 0;
u32 backup;
backup = read_phys(CONFIG_RAMBASE);
write_phys(CONFIG_RAMBASE, 0x55555555);
phys_memory_barrier();
if (read_phys(CONFIG_RAMBASE) != 0x55555555)
fail = 1;
write_phys(CONFIG_RAMBASE, 0xaaaaaaaa);
phys_memory_barrier();
if (read_phys(CONFIG_RAMBASE) != 0xaaaaaaaa)
fail = 1;
write_phys(CONFIG_RAMBASE, 0x00000000);
phys_memory_barrier();
if (read_phys(CONFIG_RAMBASE) != 0x00000000)
fail = 1;
write_phys(CONFIG_RAMBASE, 0xffffffff);
phys_memory_barrier();
if (read_phys(CONFIG_RAMBASE) != 0xffffffff)
fail = 1;
write_phys(CONFIG_RAMBASE, backup);
if (fail) {
post_code(POST_RAM_FAILURE);
panic("RAM INIT FAILURE!\n");
}
phys_memory_barrier();
}
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
* reloc_ia32.c - position independent x86 ELF shared object relocator
* Copyright (C) 1999 Hewlett-Packard Co.
* Contributed by David Mosberger <davidm@hpl.hp.com>.
*
* All rights reserved.
*/
#include <common.h>
#include <efi.h>
#include <elf.h>
efi_status_t EFIAPI _relocate(long ldbase, Elf32_Dyn *dyn)
{
long relsz = 0, relent = 0;
Elf32_Rel *rel = 0;
unsigned long *addr;
int i;
for (i = 0; dyn[i].d_tag != DT_NULL; ++i) {
switch (dyn[i].d_tag) {
case DT_REL:
rel = (Elf32_Rel *)((unsigned long)dyn[i].d_un.d_ptr +
ldbase);
break;
case DT_RELSZ:
relsz = dyn[i].d_un.d_val;
break;
case DT_RELENT:
relent = dyn[i].d_un.d_val;
break;
case DT_RELA:
break;
default:
break;
}
}
if (!rel && relent == 0)
return EFI_SUCCESS;
if (!rel || relent == 0)
return EFI_LOAD_ERROR;
while (relsz > 0) {
/* apply the relocs */
switch (ELF32_R_TYPE(rel->r_info)) {
case R_386_NONE:
break;
case R_386_RELATIVE:
addr = (unsigned long *)(ldbase + rel->r_offset);
*addr += ldbase;
break;
default:
break;
}
rel = (Elf32_Rel *)((char *)rel + relent);
relsz -= relent;
}
return EFI_SUCCESS;
}
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
* reloc_x86_64.c - position independent x86_64 ELF shared object relocator
* Copyright (C) 1999 Hewlett-Packard Co.
* Contributed by David Mosberger <davidm@hpl.hp.com>.
* Copyright (C) 2005 Intel Co.
* Contributed by Fenghua Yu <fenghua.yu@intel.com>.
*
* All rights reserved.
*/
#include <common.h>
#include <efi.h>
#include <elf.h>
efi_status_t EFIAPI _relocate(long ldbase, Elf64_Dyn *dyn)
{
long relsz = 0, relent = 0;
Elf64_Rel *rel = 0;
unsigned long *addr;
int i;
for (i = 0; dyn[i].d_tag != DT_NULL; ++i) {
switch (dyn[i].d_tag) {
case DT_RELA:
rel = (Elf64_Rel *)
((unsigned long)dyn[i].d_un.d_ptr + ldbase);
break;
case DT_RELASZ:
relsz = dyn[i].d_un.d_val;
break;
case DT_RELAENT:
relent = dyn[i].d_un.d_val;
break;
default:
break;
}
}
if (!rel && relent == 0)
return EFI_SUCCESS;
if (!rel || relent == 0)
return EFI_LOAD_ERROR;
while (relsz > 0) {
/* apply the relocs */
switch (ELF64_R_TYPE(rel->r_info)) {
case R_X86_64_NONE:
break;
case R_X86_64_RELATIVE:
addr = (unsigned long *)(ldbase + rel->r_offset);
*addr += ldbase;
break;
default:
break;
}
rel = (Elf64_Rel *)((char *)rel + relent);
relsz -= relent;
}
return EFI_SUCCESS;
}
@@ -0,0 +1,173 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2008-2011
* Graeme Russ, <graeme.russ@gmail.com>
*
* (C) Copyright 2002
* Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
*
* (C) Copyright 2002
* Wolfgang Denk, DENX Software Engineering, <wd@denx.de>
*
* (C) Copyright 2002
* Sysgo Real-Time Solutions, GmbH <www.elinos.com>
* Marius Groeger <mgroeger@sysgo.de>
*/
#include <common.h>
#include <relocate.h>
#include <asm/u-boot-x86.h>
#include <asm/sections.h>
#include <elf.h>
DECLARE_GLOBAL_DATA_PTR;
int copy_uboot_to_ram(void)
{
size_t len = (uintptr_t)&__data_end - (uintptr_t)&__text_start;
if (gd->flags & GD_FLG_SKIP_RELOC)
return 0;
memcpy((void *)gd->relocaddr, (void *)&__text_start, len);
return 0;
}
int clear_bss(void)
{
ulong dst_addr = (ulong)&__bss_start + gd->reloc_off;
size_t len = (uintptr_t)&__bss_end - (uintptr_t)&__bss_start;
if (gd->flags & GD_FLG_SKIP_RELOC)
return 0;
memset((void *)dst_addr, 0x00, len);
return 0;
}
#if CONFIG_IS_ENABLED(X86_64)
static void do_elf_reloc_fixups64(unsigned int text_base, uintptr_t size,
Elf64_Rela *re_src, Elf64_Rela *re_end)
{
Elf64_Addr *offset_ptr_rom, *last_offset = NULL;
Elf64_Addr *offset_ptr_ram;
do {
unsigned long long type = ELF64_R_TYPE(re_src->r_info);
if (type != R_X86_64_RELATIVE) {
printf("%s: unsupported relocation type 0x%llx "
"at %p, ", __func__, type, re_src);
printf("offset = 0x%llx\n", re_src->r_offset);
continue;
}
/* Get the location from the relocation entry */
offset_ptr_rom = (Elf64_Addr *)(uintptr_t)re_src->r_offset;
/* Check that the location of the relocation is in .text */
if (offset_ptr_rom >= (Elf64_Addr *)(uintptr_t)text_base &&
offset_ptr_rom > last_offset) {
/* Switch to the in-RAM version */
offset_ptr_ram = (Elf64_Addr *)((ulong)offset_ptr_rom +
gd->reloc_off);
/* Check that the target points into .text */
if (*offset_ptr_ram >= text_base &&
*offset_ptr_ram <= text_base + size) {
*offset_ptr_ram = gd->reloc_off +
re_src->r_addend;
} else {
debug(" %p: %lx: rom reloc %lx, ram %p, value %lx, limit %lX\n",
re_src, (ulong)re_src->r_info,
(ulong)re_src->r_offset, offset_ptr_ram,
(ulong)*offset_ptr_ram, text_base + size);
}
} else {
debug(" %p: %lx: rom reloc %lx, last %p\n", re_src,
(ulong)re_src->r_info, (ulong)re_src->r_offset,
last_offset);
}
last_offset = offset_ptr_rom;
} while (++re_src < re_end);
}
#else
static void do_elf_reloc_fixups32(unsigned int text_base, uintptr_t size,
Elf32_Rel *re_src, Elf32_Rel *re_end)
{
Elf32_Addr *offset_ptr_rom, *last_offset = NULL;
Elf32_Addr *offset_ptr_ram;
do {
unsigned int type = ELF32_R_TYPE(re_src->r_info);
if (type != R_386_RELATIVE) {
printf("%s: unsupported relocation type 0x%x "
"at %p, ", __func__, type, re_src);
printf("offset = 0x%x\n", re_src->r_offset);
continue;
}
/* Get the location from the relocation entry */
offset_ptr_rom = (Elf32_Addr *)(uintptr_t)re_src->r_offset;
/* Check that the location of the relocation is in .text */
if (offset_ptr_rom >= (Elf32_Addr *)(uintptr_t)text_base &&
offset_ptr_rom > last_offset) {
/* Switch to the in-RAM version */
offset_ptr_ram = (Elf32_Addr *)((ulong)offset_ptr_rom +
gd->reloc_off);
/* Check that the target points into .text */
if (*offset_ptr_ram >= text_base &&
*offset_ptr_ram <= text_base + size) {
*offset_ptr_ram += gd->reloc_off;
} else {
debug(" %p: rom reloc %x, ram %p, value %x, limit %lX\n",
re_src, re_src->r_offset, offset_ptr_ram,
*offset_ptr_ram, text_base + size);
}
} else {
debug(" %p: rom reloc %x, last %p\n", re_src,
re_src->r_offset, last_offset);
}
last_offset = offset_ptr_rom;
} while (++re_src < re_end);
}
#endif
/*
* This function has more error checking than you might expect. Please see
* this commit message for more information:
* 62f7970a x86: Add error checking to x86 relocation code
*/
int do_elf_reloc_fixups(void)
{
void *re_src = (void *)(&__rel_dyn_start);
void *re_end = (void *)(&__rel_dyn_end);
uint text_base;
/* The size of the region of u-boot that runs out of RAM. */
uintptr_t size = (uintptr_t)&__bss_end - (uintptr_t)&__text_start;
if (gd->flags & GD_FLG_SKIP_RELOC)
return 0;
if (re_src == re_end)
panic("No relocation data");
#ifdef CONFIG_SYS_TEXT_BASE
text_base = CONFIG_SYS_TEXT_BASE;
#else
panic("No CONFIG_SYS_TEXT_BASE");
#endif
#if CONFIG_IS_ENABLED(X86_64)
do_elf_reloc_fixups64(text_base, size, re_src, re_end);
#else
do_elf_reloc_fixups32(text_base, size, re_src, re_end);
#endif
return 0;
}
@@ -0,0 +1,229 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2017 Intel Corporation
*
* Intel Mobile Internet Devices (MID) based on Intel Atom SoCs have few
* microcontrollers inside to do some auxiliary tasks. One of such
* microcontroller is System Controller Unit (SCU) which, in particular,
* is servicing watchdog and controlling system reset function.
*
* This driver enables IPC channel to SCU.
*/
#include <common.h>
#include <dm.h>
#include <regmap.h>
#include <syscon.h>
#include <asm/cpu.h>
#include <asm/scu.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/kernel.h>
/* SCU register map */
struct ipc_regs {
u32 cmd;
u32 status;
u32 sptr;
u32 dptr;
u32 reserved[28];
u32 wbuf[4];
u32 rbuf[4];
};
struct scu {
struct ipc_regs *regs;
};
/**
* scu_ipc_send_command() - send command to SCU
* @regs: register map of SCU
* @cmd: command
*
* Command Register (Write Only):
* A write to this register results in an interrupt to the SCU core processor
* Format:
* |rfu2(8) | size(8) | command id(4) | rfu1(3) | ioc(1) | command(8)|
*/
static void scu_ipc_send_command(struct ipc_regs *regs, u32 cmd)
{
writel(cmd, &regs->cmd);
}
/**
* scu_ipc_check_status() - check status of last command
* @regs: register map of SCU
*
* Status Register (Read Only):
* Driver will read this register to get the ready/busy status of the IPC
* block and error status of the IPC command that was just processed by SCU
* Format:
* |rfu3(8)|error code(8)|initiator id(8)|cmd id(4)|rfu1(2)|error(1)|busy(1)|
*/
static int scu_ipc_check_status(struct ipc_regs *regs)
{
int loop_count = 100000;
int status;
do {
status = readl(&regs->status);
if (!(status & BIT(0)))
break;
udelay(1);
} while (--loop_count);
if (!loop_count)
return -ETIMEDOUT;
if (status & BIT(1)) {
printf("%s() status=0x%08x\n", __func__, status);
return -EIO;
}
return 0;
}
static int scu_ipc_cmd(struct ipc_regs *regs, u32 cmd, u32 sub,
u32 *in, int inlen, u32 *out, int outlen)
{
int i, err;
for (i = 0; i < inlen; i++)
writel(*in++, &regs->wbuf[i]);
scu_ipc_send_command(regs, (inlen << 16) | (sub << 12) | cmd);
err = scu_ipc_check_status(regs);
if (!err) {
for (i = 0; i < outlen; i++)
*out++ = readl(&regs->rbuf[i]);
}
return err;
}
/**
* scu_ipc_raw_command() - IPC command with data and pointers
* @cmd: IPC command code
* @sub: IPC command sub type
* @in: input data of this IPC command
* @inlen: input data length in dwords
* @out: output data of this IPC command
* @outlen: output data length in dwords
* @dptr: data writing to SPTR register
* @sptr: data writing to DPTR register
*
* Send an IPC command to SCU with input/output data and source/dest pointers.
*
* Return: an IPC error code or 0 on success.
*/
int scu_ipc_raw_command(u32 cmd, u32 sub, u32 *in, int inlen, u32 *out,
int outlen, u32 dptr, u32 sptr)
{
int inbuflen = DIV_ROUND_UP(inlen, 4);
struct udevice *dev;
struct scu *scu;
int ret;
ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
if (ret)
return ret;
scu = dev_get_priv(dev);
/* Up to 16 bytes */
if (inbuflen > 4)
return -EINVAL;
writel(dptr, &scu->regs->dptr);
writel(sptr, &scu->regs->sptr);
/*
* SRAM controller doesn't support 8-bit writes, it only
* supports 32-bit writes, so we have to copy input data into
* the temporary buffer, and SCU FW will use the inlen to
* determine the actual input data length in the temporary
* buffer.
*/
u32 inbuf[4] = {0};
memcpy(inbuf, in, inlen);
return scu_ipc_cmd(scu->regs, cmd, sub, inbuf, inlen, out, outlen);
}
/**
* scu_ipc_simple_command() - send a simple command
* @cmd: command
* @sub: sub type
*
* Issue a simple command to the SCU. Do not use this interface if
* you must then access data as any data values may be overwritten
* by another SCU access by the time this function returns.
*
* This function may sleep. Locking for SCU accesses is handled for
* the caller.
*/
int scu_ipc_simple_command(u32 cmd, u32 sub)
{
struct scu *scu;
struct udevice *dev;
int ret;
ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
if (ret)
return ret;
scu = dev_get_priv(dev);
scu_ipc_send_command(scu->regs, sub << 12 | cmd);
return scu_ipc_check_status(scu->regs);
}
/**
* scu_ipc_command - command with data
* @cmd: command
* @sub: sub type
* @in: input data
* @inlen: input length in dwords
* @out: output data
* @outlen: output length in dwords
*
* Issue a command to the SCU which involves data transfers.
*/
int scu_ipc_command(u32 cmd, u32 sub, u32 *in, int inlen, u32 *out, int outlen)
{
struct scu *scu;
struct udevice *dev;
int ret;
ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
if (ret)
return ret;
scu = dev_get_priv(dev);
return scu_ipc_cmd(scu->regs, cmd, sub, in, inlen, out, outlen);
}
static int scu_ipc_probe(struct udevice *dev)
{
struct scu *scu = dev_get_priv(dev);
scu->regs = syscon_get_first_range(X86_SYSCON_SCU);
return 0;
}
static const struct udevice_id scu_ipc_match[] = {
{ .compatible = "intel,scu-ipc", .data = X86_SYSCON_SCU },
{ /* sentinel */ }
};
U_BOOT_DRIVER(scu_ipc) = {
.name = "scu_ipc",
.id = UCLASS_SYSCON,
.of_match = scu_ipc_match,
.probe = scu_ipc_probe,
.priv_auto_alloc_size = sizeof(struct scu),
};
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2013 Albert ARIBAUD <albert.u.boot@aribaud.net>
*/
char __efi_runtime_start[0] __attribute__((section(".__efi_runtime_start")));
char __efi_runtime_stop[0] __attribute__((section(".__efi_runtime_stop")));
char __efi_runtime_rel_start[0]
__attribute__((section(".__efi_runtime_rel_start")));
char __efi_runtime_rel_stop[0]
__attribute__((section(".__efi_runtime_rel_stop")));
@@ -0,0 +1,153 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
/*
* Intel Simple Firmware Interface (SFI)
*
* Yet another way to pass information to the Linux kernel.
*
* See https://simplefirmware.org/ for details
*/
#include <common.h>
#include <cpu.h>
#include <dm.h>
#include <asm/cpu.h>
#include <asm/ioapic.h>
#include <asm/sfi.h>
#include <asm/tables.h>
#include <dm/uclass-internal.h>
struct table_info {
u32 base;
int ptr;
u32 entry_start;
u64 table[SFI_TABLE_MAX_ENTRIES];
int count;
};
static void *get_entry_start(struct table_info *tab)
{
if (tab->count == SFI_TABLE_MAX_ENTRIES)
return NULL;
tab->entry_start = tab->base + tab->ptr;
tab->table[tab->count] = tab->entry_start;
tab->entry_start += sizeof(struct sfi_table_header);
return (void *)(uintptr_t)tab->entry_start;
}
static void finish_table(struct table_info *tab, const char *sig, void *entry)
{
struct sfi_table_header *hdr;
hdr = (struct sfi_table_header *)(uintptr_t)(tab->base + tab->ptr);
strcpy(hdr->sig, sig);
hdr->len = sizeof(*hdr) + ((ulong)entry - tab->entry_start);
hdr->rev = 1;
strncpy(hdr->oem_id, "U-Boot", SFI_OEM_ID_SIZE);
strncpy(hdr->oem_table_id, "Table v1", SFI_OEM_TABLE_ID_SIZE);
hdr->csum = 0;
hdr->csum = table_compute_checksum(hdr, hdr->len);
tab->ptr += hdr->len;
tab->ptr = ALIGN(tab->ptr, 16);
tab->count++;
}
static int sfi_write_system_header(struct table_info *tab)
{
u64 *entry = get_entry_start(tab);
int i;
if (!entry)
return -ENOSPC;
for (i = 0; i < tab->count; i++)
*entry++ = tab->table[i];
finish_table(tab, SFI_SIG_SYST, entry);
return 0;
}
static int sfi_write_cpus(struct table_info *tab)
{
struct sfi_cpu_table_entry *entry = get_entry_start(tab);
struct udevice *dev;
int count = 0;
if (!entry)
return -ENOSPC;
for (uclass_find_first_device(UCLASS_CPU, &dev);
dev;
uclass_find_next_device(&dev)) {
struct cpu_platdata *plat = dev_get_parent_platdata(dev);
if (!device_active(dev))
continue;
entry->apic_id = plat->cpu_id;
entry++;
count++;
}
/* Omit the table if there is only one CPU */
if (count > 1)
finish_table(tab, SFI_SIG_CPUS, entry);
return 0;
}
static int sfi_write_apic(struct table_info *tab)
{
struct sfi_apic_table_entry *entry = get_entry_start(tab);
if (!entry)
return -ENOSPC;
entry->phys_addr = IO_APIC_ADDR;
entry++;
finish_table(tab, SFI_SIG_APIC, entry);
return 0;
}
static int sfi_write_xsdt(struct table_info *tab)
{
struct sfi_xsdt_header *entry = get_entry_start(tab);
if (!entry)
return -ENOSPC;
entry->oem_revision = 1;
entry->creator_id = 1;
entry->creator_revision = 1;
entry++;
finish_table(tab, SFI_SIG_XSDT, entry);
return 0;
}
ulong write_sfi_table(ulong base)
{
struct table_info table;
table.base = base;
table.ptr = 0;
table.count = 0;
sfi_write_cpus(&table);
sfi_write_apic(&table);
/*
* The SFI specification marks the XSDT table as option, but Linux 4.0
* crashes on start-up when it is not provided.
*/
sfi_write_xsdt(&table);
/* Finally, write out the system header which points to the others */
sfi_write_system_header(&table);
return base + table.ptr;
}
@@ -0,0 +1,237 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016 Google, Inc
*/
#include <common.h>
#include <cpu_func.h>
#include <debug_uart.h>
#include <dm.h>
#include <irq_func.h>
#include <malloc.h>
#include <spl.h>
#include <syscon.h>
#include <asm/cpu.h>
#include <asm/cpu_common.h>
#include <asm/mrccache.h>
#include <asm/mtrr.h>
#include <asm/pci.h>
#include <asm/processor.h>
#include <asm/spl.h>
#include <asm-generic/sections.h>
DECLARE_GLOBAL_DATA_PTR;
__weak int arch_cpu_init_dm(void)
{
return 0;
}
#ifdef CONFIG_TPL
static int set_max_freq(void)
{
if (cpu_get_burst_mode_state() == BURST_MODE_UNAVAILABLE) {
/*
* Burst Mode has been factory-configured as disabled and is not
* available in this physical processor package
*/
debug("Burst Mode is factory-disabled\n");
return -ENOENT;
}
/* Enable burst mode */
cpu_set_burst_mode(true);
/* Enable speed step */
cpu_set_eist(true);
/* Set P-State ratio */
cpu_set_p_state_to_turbo_ratio();
return 0;
}
#endif
static int x86_spl_init(void)
{
#ifndef CONFIG_TPL
/*
* TODO(sjg@chromium.org): We use this area of RAM for the stack
* and global_data in SPL. Once U-Boot starts up and releocates it
* is not needed. We could make this a CONFIG option or perhaps
* place it immediately below CONFIG_SYS_TEXT_BASE.
*/
char *ptr = (char *)0x110000;
#else
struct udevice *punit;
#endif
int ret;
debug("%s starting\n", __func__);
if (IS_ENABLED(TPL))
ret = x86_cpu_reinit_f();
else
ret = x86_cpu_init_f();
ret = spl_init();
if (ret) {
debug("%s: spl_init() failed\n", __func__);
return ret;
}
ret = arch_cpu_init();
if (ret) {
debug("%s: arch_cpu_init() failed\n", __func__);
return ret;
}
#ifndef CONFIG_TPL
ret = arch_cpu_init_dm();
if (ret) {
debug("%s: arch_cpu_init_dm() failed\n", __func__);
return ret;
}
#endif
preloader_console_init();
#ifndef CONFIG_TPL
ret = print_cpuinfo();
if (ret) {
debug("%s: print_cpuinfo() failed\n", __func__);
return ret;
}
#endif
ret = dram_init();
if (ret) {
debug("%s: dram_init() failed\n", __func__);
return ret;
}
if (IS_ENABLED(CONFIG_ENABLE_MRC_CACHE)) {
ret = mrccache_spl_save();
if (ret)
debug("%s: Failed to write to mrccache (err=%d)\n",
__func__, ret);
}
#ifndef CONFIG_TPL
memset(&__bss_start, 0, (ulong)&__bss_end - (ulong)&__bss_start);
/* TODO(sjg@chromium.org): Consider calling cpu_init_r() here */
ret = interrupt_init();
if (ret) {
debug("%s: interrupt_init() failed\n", __func__);
return ret;
}
/*
* The stack grows down from ptr. Put the global data at ptr. This
* will only be used for SPL. Once SPL loads U-Boot proper it will
* set up its own stack.
*/
gd->new_gd = (struct global_data *)ptr;
memcpy(gd->new_gd, gd, sizeof(*gd));
arch_setup_gd(gd->new_gd);
gd->start_addr_sp = (ulong)ptr;
/* Cache the SPI flash. Otherwise copying the code to RAM takes ages */
ret = mtrr_add_request(MTRR_TYPE_WRBACK,
(1ULL << 32) - CONFIG_XIP_ROM_SIZE,
CONFIG_XIP_ROM_SIZE);
if (ret) {
debug("%s: SPI cache setup failed (err=%d)\n", __func__, ret);
return ret;
}
mtrr_commit(true);
#else
ret = syscon_get_by_driver_data(X86_SYSCON_PUNIT, &punit);
if (ret)
debug("Could not find PUNIT (err=%d)\n", ret);
ret = set_max_freq();
if (ret)
debug("Failed to set CPU frequency (err=%d)\n", ret);
#endif
return 0;
}
void board_init_f(ulong flags)
{
int ret;
ret = x86_spl_init();
if (ret) {
debug("Error %d\n", ret);
panic("x86_spl_init fail");
}
#ifdef CONFIG_TPL
gd->bd = malloc(sizeof(*gd->bd));
if (!gd->bd) {
printf("Out of memory for bd_info size %x\n", sizeof(*gd->bd));
hang();
}
board_init_r(gd, 0);
#else
/* Uninit CAR and jump to board_init_f_r() */
board_init_f_r_trampoline(gd->start_addr_sp);
#endif
}
void board_init_f_r(void)
{
init_cache_f_r();
gd->flags &= ~GD_FLG_SERIAL_READY;
debug("cache status %d\n", dcache_status());
board_init_r(gd, 0);
}
u32 spl_boot_device(void)
{
return BOOT_DEVICE_SPI_MMAP;
}
int spl_start_uboot(void)
{
return 0;
}
void spl_board_announce_boot_device(void)
{
printf("SPI flash");
}
static int spl_board_load_image(struct spl_image_info *spl_image,
struct spl_boot_device *bootdev)
{
spl_image->size = CONFIG_SYS_MONITOR_LEN;
spl_image->entry_point = CONFIG_SYS_TEXT_BASE;
spl_image->load_addr = CONFIG_SYS_TEXT_BASE;
spl_image->os = IH_OS_U_BOOT;
spl_image->name = "U-Boot";
debug("Loading to %lx\n", spl_image->load_addr);
return 0;
}
SPL_LOAD_IMAGE_METHOD("SPI", 5, BOOT_DEVICE_SPI_MMAP, spl_board_load_image);
int spl_spi_load_image(void)
{
return -EPERM;
}
#ifdef CONFIG_X86_RUN_64BIT
void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image)
{
int ret;
printf("Jumping to 64-bit U-Boot: Note many features are missing\n");
ret = cpu_jump_to_64bit_uboot(spl_image->entry_point);
debug("ret=%d\n", ret);
hang();
}
#endif
void spl_board_init(void)
{
#ifndef CONFIG_TPL
preloader_console_init();
#endif
}
@@ -0,0 +1,292 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 1991,1992,1993,1997,1998,2003, 2005 Free Software Foundation, Inc.
* This file is part of the GNU C Library.
* Copyright (c) 2011 The Chromium OS Authors.
*/
/* From glibc-2.14, sysdeps/i386/memset.c */
#include <linux/types.h>
#include <linux/compiler.h>
#include <asm/string.h>
typedef uint32_t op_t;
void *memset(void *dstpp, int c, size_t len)
{
int d0;
unsigned long int dstp = (unsigned long int) dstpp;
/* This explicit register allocation improves code very much indeed. */
register op_t x asm("ax");
x = (unsigned char) c;
/* Clear the direction flag, so filling will move forward. */
asm volatile("cld");
/* This threshold value is optimal. */
if (len >= 12) {
/* Fill X with four copies of the char we want to fill with. */
x |= (x << 8);
x |= (x << 16);
/* Adjust LEN for the bytes handled in the first loop. */
len -= (-dstp) % sizeof(op_t);
/*
* There are at least some bytes to set. No need to test for
* LEN == 0 in this alignment loop.
*/
/* Fill bytes until DSTP is aligned on a longword boundary. */
asm volatile(
"rep\n"
"stosb" /* %0, %2, %3 */ :
"=D" (dstp), "=c" (d0) :
"0" (dstp), "1" ((-dstp) % sizeof(op_t)), "a" (x) :
"memory");
/* Fill longwords. */
asm volatile(
"rep\n"
"stosl" /* %0, %2, %3 */ :
"=D" (dstp), "=c" (d0) :
"0" (dstp), "1" (len / sizeof(op_t)), "a" (x) :
"memory");
len %= sizeof(op_t);
}
/* Write the last few bytes. */
asm volatile(
"rep\n"
"stosb" /* %0, %2, %3 */ :
"=D" (dstp), "=c" (d0) :
"0" (dstp), "1" (len), "a" (x) :
"memory");
return dstpp;
}
#define OP_T_THRES 8
#define OPSIZ (sizeof(op_t))
#define BYTE_COPY_FWD(dst_bp, src_bp, nbytes) \
do { \
int __d0; \
asm volatile( \
/* Clear the direction flag, so copying goes forward. */ \
"cld\n" \
/* Copy bytes. */ \
"rep\n" \
"movsb" : \
"=D" (dst_bp), "=S" (src_bp), "=c" (__d0) : \
"0" (dst_bp), "1" (src_bp), "2" (nbytes) : \
"memory"); \
} while (0)
#define WORD_COPY_FWD(dst_bp, src_bp, nbytes_left, nbytes) \
do { \
int __d0; \
asm volatile( \
/* Clear the direction flag, so copying goes forward. */ \
"cld\n" \
/* Copy longwords. */ \
"rep\n" \
"movsl" : \
"=D" (dst_bp), "=S" (src_bp), "=c" (__d0) : \
"0" (dst_bp), "1" (src_bp), "2" ((nbytes) / 4) : \
"memory"); \
(nbytes_left) = (nbytes) % 4; \
} while (0)
void *memcpy(void *dstpp, const void *srcpp, size_t len)
{
unsigned long int dstp = (long int)dstpp;
unsigned long int srcp = (long int)srcpp;
/* Copy from the beginning to the end. */
/* If there not too few bytes to copy, use word copy. */
if (len >= OP_T_THRES) {
/* Copy just a few bytes to make DSTP aligned. */
len -= (-dstp) % OPSIZ;
BYTE_COPY_FWD(dstp, srcp, (-dstp) % OPSIZ);
/* Copy from SRCP to DSTP taking advantage of the known
* alignment of DSTP. Number of bytes remaining is put
* in the third argument, i.e. in LEN. This number may
* vary from machine to machine.
*/
WORD_COPY_FWD(dstp, srcp, len, len);
/* Fall out and copy the tail. */
}
/* There are just a few bytes to copy. Use byte memory operations. */
BYTE_COPY_FWD(dstp, srcp, len);
return dstpp;
}
void *memmove(void *dest, const void *src, size_t n)
{
int d0, d1, d2, d3, d4, d5;
char *ret = dest;
__asm__ __volatile__(
/* Handle more 16 bytes in loop */
"cmp $0x10, %0\n\t"
"jb 1f\n\t"
/* Decide forward/backward copy mode */
"cmp %2, %1\n\t"
"jb 2f\n\t"
/*
* movs instruction have many startup latency
* so we handle small size by general register.
*/
"cmp $680, %0\n\t"
"jb 3f\n\t"
/* movs instruction is only good for aligned case */
"mov %1, %3\n\t"
"xor %2, %3\n\t"
"and $0xff, %3\n\t"
"jz 4f\n\t"
"3:\n\t"
"sub $0x10, %0\n\t"
/* We gobble 16 bytes forward in each loop */
"3:\n\t"
"sub $0x10, %0\n\t"
"mov 0*4(%1), %3\n\t"
"mov 1*4(%1), %4\n\t"
"mov %3, 0*4(%2)\n\t"
"mov %4, 1*4(%2)\n\t"
"mov 2*4(%1), %3\n\t"
"mov 3*4(%1), %4\n\t"
"mov %3, 2*4(%2)\n\t"
"mov %4, 3*4(%2)\n\t"
"lea 0x10(%1), %1\n\t"
"lea 0x10(%2), %2\n\t"
"jae 3b\n\t"
"add $0x10, %0\n\t"
"jmp 1f\n\t"
/* Handle data forward by movs */
".p2align 4\n\t"
"4:\n\t"
"mov -4(%1, %0), %3\n\t"
"lea -4(%2, %0), %4\n\t"
"shr $2, %0\n\t"
"rep movsl\n\t"
"mov %3, (%4)\n\t"
"jmp 11f\n\t"
/* Handle data backward by movs */
".p2align 4\n\t"
"6:\n\t"
"mov (%1), %3\n\t"
"mov %2, %4\n\t"
"lea -4(%1, %0), %1\n\t"
"lea -4(%2, %0), %2\n\t"
"shr $2, %0\n\t"
"std\n\t"
"rep movsl\n\t"
"mov %3,(%4)\n\t"
"cld\n\t"
"jmp 11f\n\t"
/* Start to prepare for backward copy */
".p2align 4\n\t"
"2:\n\t"
"cmp $680, %0\n\t"
"jb 5f\n\t"
"mov %1, %3\n\t"
"xor %2, %3\n\t"
"and $0xff, %3\n\t"
"jz 6b\n\t"
/* Calculate copy position to tail */
"5:\n\t"
"add %0, %1\n\t"
"add %0, %2\n\t"
"sub $0x10, %0\n\t"
/* We gobble 16 bytes backward in each loop */
"7:\n\t"
"sub $0x10, %0\n\t"
"mov -1*4(%1), %3\n\t"
"mov -2*4(%1), %4\n\t"
"mov %3, -1*4(%2)\n\t"
"mov %4, -2*4(%2)\n\t"
"mov -3*4(%1), %3\n\t"
"mov -4*4(%1), %4\n\t"
"mov %3, -3*4(%2)\n\t"
"mov %4, -4*4(%2)\n\t"
"lea -0x10(%1), %1\n\t"
"lea -0x10(%2), %2\n\t"
"jae 7b\n\t"
/* Calculate copy position to head */
"add $0x10, %0\n\t"
"sub %0, %1\n\t"
"sub %0, %2\n\t"
/* Move data from 8 bytes to 15 bytes */
".p2align 4\n\t"
"1:\n\t"
"cmp $8, %0\n\t"
"jb 8f\n\t"
"mov 0*4(%1), %3\n\t"
"mov 1*4(%1), %4\n\t"
"mov -2*4(%1, %0), %5\n\t"
"mov -1*4(%1, %0), %1\n\t"
"mov %3, 0*4(%2)\n\t"
"mov %4, 1*4(%2)\n\t"
"mov %5, -2*4(%2, %0)\n\t"
"mov %1, -1*4(%2, %0)\n\t"
"jmp 11f\n\t"
/* Move data from 4 bytes to 7 bytes */
".p2align 4\n\t"
"8:\n\t"
"cmp $4, %0\n\t"
"jb 9f\n\t"
"mov 0*4(%1), %3\n\t"
"mov -1*4(%1, %0), %4\n\t"
"mov %3, 0*4(%2)\n\t"
"mov %4, -1*4(%2, %0)\n\t"
"jmp 11f\n\t"
/* Move data from 2 bytes to 3 bytes */
".p2align 4\n\t"
"9:\n\t"
"cmp $2, %0\n\t"
"jb 10f\n\t"
"movw 0*2(%1), %%dx\n\t"
"movw -1*2(%1, %0), %%bx\n\t"
"movw %%dx, 0*2(%2)\n\t"
"movw %%bx, -1*2(%2, %0)\n\t"
"jmp 11f\n\t"
/* Move data for 1 byte */
".p2align 4\n\t"
"10:\n\t"
"cmp $1, %0\n\t"
"jb 11f\n\t"
"movb (%1), %%cl\n\t"
"movb %%cl, (%2)\n\t"
".p2align 4\n\t"
"11:"
: "=&c" (d0), "=&S" (d1), "=&D" (d2),
"=r" (d3), "=r" (d4), "=r"(d5)
: "0" (n),
"1" (src),
"2" (dest)
: "memory");
return ret;
}
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <smbios.h>
#include <asm/sfi.h>
#include <asm/mpspec.h>
#include <asm/tables.h>
#include <asm/acpi_table.h>
#include <asm/coreboot_tables.h>
/**
* Function prototype to write a specific configuration table
*
* @addr: start address to write the table
* @return: end address of the table
*/
typedef ulong (*table_write)(ulong addr);
static table_write table_write_funcs[] = {
#ifdef CONFIG_GENERATE_PIRQ_TABLE
write_pirq_routing_table,
#endif
#ifdef CONFIG_GENERATE_SFI_TABLE
write_sfi_table,
#endif
#ifdef CONFIG_GENERATE_MP_TABLE
write_mp_table,
#endif
#ifdef CONFIG_GENERATE_ACPI_TABLE
write_acpi_tables,
#endif
#ifdef CONFIG_GENERATE_SMBIOS_TABLE
write_smbios_table,
#endif
};
void table_fill_string(char *dest, const char *src, size_t n, char pad)
{
int start, len;
int i;
strncpy(dest, src, n);
/* Fill the remaining bytes with pad */
len = strlen(src);
start = len < n ? len : n;
for (i = start; i < n; i++)
dest[i] = pad;
}
void write_tables(void)
{
u32 rom_table_start = ROM_TABLE_ADDR;
u32 rom_table_end;
#ifdef CONFIG_SEABIOS
u32 high_table, table_size;
struct memory_area cfg_tables[ARRAY_SIZE(table_write_funcs) + 1];
#endif
int i;
for (i = 0; i < ARRAY_SIZE(table_write_funcs); i++) {
rom_table_end = table_write_funcs[i](rom_table_start);
rom_table_end = ALIGN(rom_table_end, ROM_TABLE_ALIGN);
#ifdef CONFIG_SEABIOS
table_size = rom_table_end - rom_table_start;
high_table = (u32)high_table_malloc(table_size);
if (high_table) {
table_write_funcs[i](high_table);
cfg_tables[i].start = high_table;
cfg_tables[i].size = table_size;
} else {
printf("%d: no memory for configuration tables\n", i);
}
#endif
rom_table_start = rom_table_end;
}
#ifdef CONFIG_SEABIOS
/* make sure the last item is zero */
cfg_tables[i].size = 0;
write_coreboot_table(CB_TABLE_ADDR, cfg_tables);
#endif
}
@@ -0,0 +1,142 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2018 Google, Inc
*/
#include <common.h>
#include <debug_uart.h>
#include <dm.h>
#include <spl.h>
#include <asm/cpu.h>
#include <asm/mtrr.h>
#include <asm/processor.h>
#include <asm-generic/sections.h>
DECLARE_GLOBAL_DATA_PTR;
__weak int arch_cpu_init_dm(void)
{
return 0;
}
static int x86_tpl_init(void)
{
int ret;
debug("%s starting\n", __func__);
ret = x86_cpu_init_tpl();
if (ret) {
debug("%s: x86_cpu_init_tpl() failed\n", __func__);
return ret;
}
ret = spl_init();
if (ret) {
debug("%s: spl_init() failed\n", __func__);
return ret;
}
ret = arch_cpu_init();
if (ret) {
debug("%s: arch_cpu_init() failed\n", __func__);
return ret;
}
ret = arch_cpu_init_dm();
if (ret) {
debug("%s: arch_cpu_init_dm() failed\n", __func__);
return ret;
}
preloader_console_init();
return 0;
}
void board_init_f(ulong flags)
{
int ret;
ret = x86_tpl_init();
if (ret) {
debug("Error %d\n", ret);
panic("x86_tpl_init fail");
}
/* Uninit CAR and jump to board_init_f_r() */
board_init_r(gd, 0);
}
void board_init_f_r(void)
{
/* Not used since we never call board_init_f_r_trampoline() */
while (1);
}
u32 spl_boot_device(void)
{
return IS_ENABLED(CONFIG_CHROMEOS) ? BOOT_DEVICE_CROS_VBOOT :
BOOT_DEVICE_SPI_MMAP;
}
int spl_start_uboot(void)
{
return 0;
}
void spl_board_announce_boot_device(void)
{
printf("SPI flash");
}
static int spl_board_load_image(struct spl_image_info *spl_image,
struct spl_boot_device *bootdev)
{
spl_image->size = CONFIG_SYS_MONITOR_LEN; /* We don't know SPL size */
spl_image->entry_point = CONFIG_SPL_TEXT_BASE;
spl_image->load_addr = CONFIG_SPL_TEXT_BASE;
spl_image->os = IH_OS_U_BOOT;
spl_image->name = "U-Boot";
debug("Loading to %lx\n", spl_image->load_addr);
return 0;
}
SPL_LOAD_IMAGE_METHOD("SPI", 5, BOOT_DEVICE_SPI_MMAP, spl_board_load_image);
int spl_spi_load_image(void)
{
return -EPERM;
}
void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image)
{
debug("Jumping to U-Boot SPL at %lx\n", (ulong)spl_image->entry_point);
jump_to_spl(spl_image->entry_point);
hang();
}
void spl_board_init(void)
{
preloader_console_init();
}
#if !CONFIG_IS_ENABLED(PCI)
/*
* This is a fake PCI bus for TPL when it doesn't have proper PCI. It is enough
* to bind the devices on the PCI bus, some of which have early-regs properties
* providing fixed BARs. Individual drivers program these BARs themselves so
* that they can access the devices. The BARs are allocated statically in the
* device tree.
*
* Once SPL is running it enables PCI properly, but does not auto-assign BARs
* for devices, so the TPL BARs continue to be used. Once U-Boot starts it does
* the auto allocation (after relocation).
*/
static const struct udevice_id tpl_fake_pci_ids[] = {
{ .compatible = "pci-x86" },
{ }
};
U_BOOT_DRIVER(pci_x86) = {
.name = "pci_x86",
.id = UCLASS_SIMPLE_BUS,
.of_match = tpl_fake_pci_ids,
};
#endif
@@ -0,0 +1,376 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2011 The Chromium OS Authors.
* (C) Copyright 2002
* Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
*/
/*
* Linux x86 zImage and bzImage loading
*
* based on the procdure described in
* linux/Documentation/i386/boot.txt
*/
#include <common.h>
#include <env.h>
#include <irq_func.h>
#include <malloc.h>
#include <asm/acpi_table.h>
#include <asm/io.h>
#include <asm/ptrace.h>
#include <asm/zimage.h>
#include <asm/byteorder.h>
#include <asm/bootm.h>
#include <asm/bootparam.h>
#ifdef CONFIG_SYS_COREBOOT
#include <asm/arch/timestamp.h>
#endif
#include <linux/compiler.h>
#include <linux/libfdt.h>
/*
* Memory lay-out:
*
* relative to setup_base (which is 0x90000 currently)
*
* 0x0000-0x7FFF Real mode kernel
* 0x8000-0x8FFF Stack and heap
* 0x9000-0x90FF Kernel command line
*/
#define DEFAULT_SETUP_BASE 0x90000
#define COMMAND_LINE_OFFSET 0x9000
#define HEAP_END_OFFSET 0x8e00
#define COMMAND_LINE_SIZE 2048
static void build_command_line(char *command_line, int auto_boot)
{
char *env_command_line;
command_line[0] = '\0';
env_command_line = env_get("bootargs");
/* set console= argument if we use a serial console */
if (!strstr(env_command_line, "console=")) {
if (!strcmp(env_get("stdout"), "serial")) {
/* We seem to use serial console */
sprintf(command_line, "console=ttyS0,%s ",
env_get("baudrate"));
}
}
if (auto_boot)
strcat(command_line, "auto ");
if (env_command_line)
strcat(command_line, env_command_line);
printf("Kernel command line: \"%s\"\n", command_line);
}
static int kernel_magic_ok(struct setup_header *hdr)
{
if (KERNEL_MAGIC != hdr->boot_flag) {
printf("Error: Invalid Boot Flag "
"(found 0x%04x, expected 0x%04x)\n",
hdr->boot_flag, KERNEL_MAGIC);
return 0;
} else {
printf("Valid Boot Flag\n");
return 1;
}
}
static int get_boot_protocol(struct setup_header *hdr)
{
if (hdr->header == KERNEL_V2_MAGIC) {
printf("Magic signature found\n");
return hdr->version;
} else {
/* Very old kernel */
printf("Magic signature not found\n");
return 0x0100;
}
}
static int setup_device_tree(struct setup_header *hdr, const void *fdt_blob)
{
int bootproto = get_boot_protocol(hdr);
struct setup_data *sd;
int size;
if (bootproto < 0x0209)
return -ENOTSUPP;
if (!fdt_blob)
return 0;
size = fdt_totalsize(fdt_blob);
if (size < 0)
return -EINVAL;
size += sizeof(struct setup_data);
sd = (struct setup_data *)malloc(size);
if (!sd) {
printf("Not enough memory for DTB setup data\n");
return -ENOMEM;
}
sd->next = hdr->setup_data;
sd->type = SETUP_DTB;
sd->len = fdt_totalsize(fdt_blob);
memcpy(sd->data, fdt_blob, sd->len);
hdr->setup_data = (unsigned long)sd;
return 0;
}
struct boot_params *load_zimage(char *image, unsigned long kernel_size,
ulong *load_addressp)
{
struct boot_params *setup_base;
int setup_size;
int bootproto;
int big_image;
struct boot_params *params = (struct boot_params *)image;
struct setup_header *hdr = &params->hdr;
/* base address for real-mode segment */
setup_base = (struct boot_params *)DEFAULT_SETUP_BASE;
if (!kernel_magic_ok(hdr))
return 0;
/* determine size of setup */
if (0 == hdr->setup_sects) {
printf("Setup Sectors = 0 (defaulting to 4)\n");
setup_size = 5 * 512;
} else {
setup_size = (hdr->setup_sects + 1) * 512;
}
printf("Setup Size = 0x%8.8lx\n", (ulong)setup_size);
if (setup_size > SETUP_MAX_SIZE)
printf("Error: Setup is too large (%d bytes)\n", setup_size);
/* determine boot protocol version */
bootproto = get_boot_protocol(hdr);
printf("Using boot protocol version %x.%02x\n",
(bootproto & 0xff00) >> 8, bootproto & 0xff);
if (bootproto >= 0x0200) {
if (hdr->setup_sects >= 15) {
printf("Linux kernel version %s\n",
(char *)params +
hdr->kernel_version + 0x200);
} else {
printf("Setup Sectors < 15 - "
"Cannot print kernel version.\n");
}
}
/* Determine image type */
big_image = (bootproto >= 0x0200) &&
(hdr->loadflags & BIG_KERNEL_FLAG);
/* Determine load address */
if (big_image)
*load_addressp = BZIMAGE_LOAD_ADDR;
else
*load_addressp = ZIMAGE_LOAD_ADDR;
printf("Building boot_params at 0x%8.8lx\n", (ulong)setup_base);
memset(setup_base, 0, sizeof(*setup_base));
setup_base->hdr = params->hdr;
if (bootproto >= 0x0204)
kernel_size = hdr->syssize * 16;
else
kernel_size -= setup_size;
if (bootproto == 0x0100) {
/*
* A very old kernel MUST have its real-mode code
* loaded at 0x90000
*/
if ((ulong)setup_base != 0x90000) {
/* Copy the real-mode kernel */
memmove((void *)0x90000, setup_base, setup_size);
/* Copy the command line */
memmove((void *)0x99000,
(u8 *)setup_base + COMMAND_LINE_OFFSET,
COMMAND_LINE_SIZE);
/* Relocated */
setup_base = (struct boot_params *)0x90000;
}
/* It is recommended to clear memory up to the 32K mark */
memset((u8 *)0x90000 + setup_size, 0,
SETUP_MAX_SIZE - setup_size);
}
if (big_image) {
if (kernel_size > BZIMAGE_MAX_SIZE) {
printf("Error: bzImage kernel too big! "
"(size: %ld, max: %d)\n",
kernel_size, BZIMAGE_MAX_SIZE);
return 0;
}
} else if ((kernel_size) > ZIMAGE_MAX_SIZE) {
printf("Error: zImage kernel too big! (size: %ld, max: %d)\n",
kernel_size, ZIMAGE_MAX_SIZE);
return 0;
}
printf("Loading %s at address %lx (%ld bytes)\n",
big_image ? "bzImage" : "zImage", *load_addressp, kernel_size);
memmove((void *)*load_addressp, image + setup_size, kernel_size);
return setup_base;
}
int setup_zimage(struct boot_params *setup_base, char *cmd_line, int auto_boot,
unsigned long initrd_addr, unsigned long initrd_size)
{
struct setup_header *hdr = &setup_base->hdr;
int bootproto = get_boot_protocol(hdr);
setup_base->e820_entries = install_e820_map(
ARRAY_SIZE(setup_base->e820_map), setup_base->e820_map);
if (bootproto == 0x0100) {
setup_base->screen_info.cl_magic = COMMAND_LINE_MAGIC;
setup_base->screen_info.cl_offset = COMMAND_LINE_OFFSET;
}
if (bootproto >= 0x0200) {
hdr->type_of_loader = 8;
if (initrd_addr) {
printf("Initial RAM disk at linear address "
"0x%08lx, size %ld bytes\n",
initrd_addr, initrd_size);
hdr->ramdisk_image = initrd_addr;
hdr->ramdisk_size = initrd_size;
}
}
if (bootproto >= 0x0201) {
hdr->heap_end_ptr = HEAP_END_OFFSET;
hdr->loadflags |= HEAP_FLAG;
}
if (cmd_line) {
if (bootproto >= 0x0202) {
hdr->cmd_line_ptr = (uintptr_t)cmd_line;
} else if (bootproto >= 0x0200) {
setup_base->screen_info.cl_magic = COMMAND_LINE_MAGIC;
setup_base->screen_info.cl_offset =
(uintptr_t)cmd_line - (uintptr_t)setup_base;
hdr->setup_move_size = 0x9100;
}
/* build command line at COMMAND_LINE_OFFSET */
build_command_line(cmd_line, auto_boot);
}
#ifdef CONFIG_INTEL_MID
if (bootproto >= 0x0207)
hdr->hardware_subarch = X86_SUBARCH_INTEL_MID;
#endif
#ifdef CONFIG_GENERATE_ACPI_TABLE
setup_base->acpi_rsdp_addr = acpi_get_rsdp_addr();
#endif
setup_device_tree(hdr, (const void *)env_get_hex("fdtaddr", 0));
setup_video(&setup_base->screen_info);
#ifdef CONFIG_EFI_STUB
setup_efi_info(&setup_base->efi_info);
#endif
return 0;
}
void setup_pcat_compatibility(void)
__attribute__((weak, alias("__setup_pcat_compatibility")));
void __setup_pcat_compatibility(void)
{
}
int do_zboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
{
struct boot_params *base_ptr;
void *bzImage_addr = NULL;
ulong load_address;
char *s;
ulong bzImage_size = 0;
ulong initrd_addr = 0;
ulong initrd_size = 0;
disable_interrupts();
/* Setup board for maximum PC/AT Compatibility */
setup_pcat_compatibility();
if (argc >= 2) {
/* argv[1] holds the address of the bzImage */
s = argv[1];
} else {
s = env_get("fileaddr");
}
if (s)
bzImage_addr = (void *)simple_strtoul(s, NULL, 16);
if (argc >= 3) {
/* argv[2] holds the size of the bzImage */
bzImage_size = simple_strtoul(argv[2], NULL, 16);
}
if (argc >= 4)
initrd_addr = simple_strtoul(argv[3], NULL, 16);
if (argc >= 5)
initrd_size = simple_strtoul(argv[4], NULL, 16);
/* Lets look for */
base_ptr = load_zimage(bzImage_addr, bzImage_size, &load_address);
if (!base_ptr) {
puts("## Kernel loading failed ...\n");
return -1;
}
if (setup_zimage(base_ptr, (char *)base_ptr + COMMAND_LINE_OFFSET,
0, initrd_addr, initrd_size)) {
puts("Setting up boot parameters failed ...\n");
return -1;
}
/* we assume that the kernel is in place */
return boot_linux_kernel((ulong)base_ptr, load_address, false);
}
U_BOOT_CMD(
zboot, 5, 0, do_zboot,
"Boot bzImage",
"[addr] [size] [initrd addr] [initrd size]\n"
" addr - The optional starting address of the bzimage.\n"
" If not set it defaults to the environment\n"
" variable \"fileaddr\".\n"
" size - The optional size of the bzimage. Defaults to\n"
" zero.\n"
" initrd addr - The address of the initrd image to use, if any.\n"
" initrd size - The size of the initrd image to use, if any.\n"
);