diff options
Diffstat (limited to 'roms/u-boot/arch/x86/lib')
66 files changed, 10402 insertions, 0 deletions
diff --git a/roms/u-boot/arch/x86/lib/Makefile b/roms/u-boot/arch/x86/lib/Makefile new file mode 100644 index 000000000..65d9b3bd6 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/Makefile @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2002-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. + +ifndef CONFIG_X86_64 +ifndef CONFIG_TPL_BUILD +obj-y += bios.o +obj-y += bios_asm.o +obj-y += bios_interrupts.o +endif +obj-y += string.o +endif +ifndef CONFIG_SPL_BUILD +obj-$(CONFIG_CMD_BOOTM) += bootm.o +endif +obj-y += cmd_boot.o +obj-$(CONFIG_$(SPL_)COREBOOT_SYSINFO) += coreboot/ +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_$(SPL_TPL_)ACPIGEN) += acpi_nhlt.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-y += acpigen.o +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 +ifndef CONFIG_TPL_BUILD +obj-$(CONFIG_ENABLE_MRC_CACHE) += mrccache.o +obj-$(CONFIG_HAVE_FSP) += fsp/ +obj-$(CONFIG_FSP_VERSION1) += fsp1/ +obj-$(CONFIG_FSP_VERSION2) += fsp2/ +endif + +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 diff --git a/roms/u-boot/arch/x86/lib/acpi.c b/roms/u-boot/arch/x86/lib/acpi.c new file mode 100644 index 000000000..155fffabf --- /dev/null +++ b/roms/u-boot/arch/x86/lib/acpi.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <log.h> +#include <acpi/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); +} diff --git a/roms/u-boot/arch/x86/lib/acpi_nhlt.c b/roms/u-boot/arch/x86/lib/acpi_nhlt.c new file mode 100644 index 000000000..6c8cd83e1 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/acpi_nhlt.c @@ -0,0 +1,491 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020 Google LLC + * + * Modified from coreboot nhlt.c + */ + +#define LOG_CATEGORY LOGC_ACPI + +#include <common.h> +#include <binman.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <tables_csum.h> +#include <acpi/acpi_table.h> +#include <asm/acpi_nhlt.h> +#include <asm/unaligned.h> +#include <dm/acpi.h> + +#define NHLT_RID 1 +#define NHLT_SSID 1 +#define WAVEFORMAT_TAG 0xfffe +#define DEFAULT_VIRTUAL_BUS_ID 0 + +static const struct sub_format pcm_subformat = { + .data1 = 0x00000001, + .data2 = 0x0000, + .data3 = 0x0010, + .data4 = { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }, +}; + +struct nhlt *nhlt_init(void) +{ + struct nhlt *nhlt; + + nhlt = malloc(sizeof(*nhlt)); + + if (!nhlt) + return NULL; + + memset(nhlt, 0, sizeof(*nhlt)); + nhlt->subsystem_id = NHLT_SSID; + + return nhlt; +} + +struct nhlt_endpoint *nhlt_add_endpoint(struct nhlt *nhlt, int link_type, + int device_type, int dir, + u16 vid, u16 did) +{ + struct nhlt_endpoint *endp; + + if (link_type < NHLT_LINK_HDA || link_type >= NHLT_MAX_LINK_TYPES) + return NULL; + + if (nhlt->num_endpoints >= MAX_ENDPOINTS) + return NULL; + + endp = &nhlt->endpoints[nhlt->num_endpoints]; + + endp->link_type = link_type; + endp->instance_id = nhlt->current_instance_id[link_type]; + endp->vendor_id = vid; + endp->device_id = did; + endp->revision_id = NHLT_RID; + endp->subsystem_id = nhlt->subsystem_id; + endp->device_type = device_type; + endp->direction = dir; + endp->virtual_bus_id = DEFAULT_VIRTUAL_BUS_ID; + endp->num_formats = 0; + + nhlt->num_endpoints++; + + return endp; +} + +static int append_specific_config(struct nhlt_specific_config *spec_cfg, + const void *config, size_t config_sz) +{ + size_t new_sz; + void *new_cfg; + + new_sz = spec_cfg->size + config_sz; + new_cfg = malloc(new_sz); + if (!new_cfg) + return -ENOMEM; + + /* Append new config */ + memcpy(new_cfg, spec_cfg->capabilities, spec_cfg->size); + memcpy(new_cfg + spec_cfg->size, config, config_sz); + + free(spec_cfg->capabilities); + + /* Update with new config data */ + spec_cfg->size = new_sz; + spec_cfg->capabilities = new_cfg; + + return 0; +} + +int nhlt_endpoint_append_config(struct nhlt_endpoint *endp, const void *config, + size_t config_sz) +{ + return append_specific_config(&endp->config, config, config_sz); +} + +struct nhlt_format *nhlt_add_format(struct nhlt_endpoint *endp, + int num_channels, int sample_freq_khz, + int container_bits_per_sample, + int valid_bits_per_sample, + uint32_t speaker_mask) +{ + struct nhlt_format *fmt; + struct nhlt_waveform *wave; + + if (endp->num_formats >= MAX_FORMATS) + return NULL; + + fmt = &endp->formats[endp->num_formats]; + wave = &fmt->waveform; + + wave->tag = WAVEFORMAT_TAG; + wave->num_channels = num_channels; + wave->samples_per_second = sample_freq_khz * 1000; + wave->bits_per_sample = container_bits_per_sample; + wave->extra_size = sizeof(wave->valid_bits_per_sample); + wave->extra_size += sizeof(wave->channel_mask); + wave->extra_size += sizeof(wave->sub_format); + wave->valid_bits_per_sample = valid_bits_per_sample; + wave->channel_mask = speaker_mask; + memcpy(&wave->sub_format, &pcm_subformat, sizeof(wave->sub_format)); + + /* Calculate the dervied fields */ + wave->block_align = wave->num_channels * wave->bits_per_sample / 8; + wave->bytes_per_second = wave->block_align * wave->samples_per_second; + + endp->num_formats++; + + return fmt; +} + +int nhlt_format_append_config(struct nhlt_format *fmt, const void *config, + size_t config_sz) +{ + return append_specific_config(&fmt->config, config, config_sz); +} + +int nhlt_endpoint_add_formats(struct nhlt_endpoint *endp, + const struct nhlt_format_config *formats, + size_t num_formats) +{ + ofnode node; + size_t i; + + node = binman_section_find_node("private-files"); + + for (i = 0; i < num_formats; i++) { + const struct nhlt_format_config *cfg = &formats[i]; + struct nhlt_format *fmt; + void *data; + int size; + int ret; + + fmt = nhlt_add_format(endp, cfg->num_channels, + cfg->sample_freq_khz, + cfg->container_bits_per_sample, + cfg->valid_bits_per_sample, + cfg->speaker_mask); + if (!fmt) + return -ENOSPC; + + if (!cfg->settings_file) + continue; + + ret = binman_entry_map(node, cfg->settings_file, &data, &size); + if (ret) { + log_warning("Failed to find settings file %s\n", + cfg->settings_file); + return log_msg_ret("settings", ret); + } + + ret = nhlt_format_append_config(fmt, data, size); + if (ret) + return log_msg_ret("append", ret); + } + + return 0; +} + +void nhlt_next_instance(struct nhlt *nhlt, int link_type) +{ + if (link_type < NHLT_LINK_HDA || link_type >= NHLT_MAX_LINK_TYPES) + return; + + nhlt->current_instance_id[link_type]++; +} + +static size_t calc_specific_config_size(struct nhlt_specific_config *cfg) +{ + return sizeof(cfg->size) + cfg->size; +} + +static size_t calc_format_size(struct nhlt_format *fmt) +{ + size_t sz = 0; + + /* Wave format first */ + sz += sizeof(fmt->waveform.tag); + sz += sizeof(fmt->waveform.num_channels); + sz += sizeof(fmt->waveform.samples_per_second); + sz += sizeof(fmt->waveform.bytes_per_second); + sz += sizeof(fmt->waveform.block_align); + sz += sizeof(fmt->waveform.bits_per_sample); + sz += sizeof(fmt->waveform.extra_size); + sz += sizeof(fmt->waveform.valid_bits_per_sample); + sz += sizeof(fmt->waveform.channel_mask); + sz += sizeof(fmt->waveform.sub_format); + + sz += calc_specific_config_size(&fmt->config); + + return sz; +} + +static size_t calc_endpoint_size(struct nhlt_endpoint *endp) +{ + int i; + size_t sz = 0; + + sz += sizeof(endp->length) + sizeof(endp->link_type); + sz += sizeof(endp->instance_id) + sizeof(endp->vendor_id); + sz += sizeof(endp->device_id) + sizeof(endp->revision_id); + sz += sizeof(endp->subsystem_id) + sizeof(endp->device_type); + sz += sizeof(endp->direction) + sizeof(endp->virtual_bus_id); + sz += calc_specific_config_size(&endp->config); + sz += sizeof(endp->num_formats); + + for (i = 0; i < endp->num_formats; i++) + sz += calc_format_size(&endp->formats[i]); + + /* Adjust endpoint length to reflect current configuration */ + endp->length = sz; + + return sz; +} + +static size_t calc_endpoints_size(struct nhlt *nhlt) +{ + size_t sz = 0; + int i; + + for (i = 0; i < nhlt->num_endpoints; i++) + sz += calc_endpoint_size(&nhlt->endpoints[i]); + + return sz; +} + +static size_t calc_size(struct nhlt *nhlt) +{ + return sizeof(nhlt->num_endpoints) + calc_endpoints_size(nhlt); +} + +size_t nhlt_current_size(struct nhlt *nhlt) +{ + return calc_size(nhlt) + sizeof(struct acpi_table_header); +} + +static void nhlt_free_resources(struct nhlt *nhlt) +{ + int i, j; + + /* Free all specific configs */ + for (i = 0; i < nhlt->num_endpoints; i++) { + struct nhlt_endpoint *endp = &nhlt->endpoints[i]; + + free(endp->config.capabilities); + for (j = 0; j < endp->num_formats; j++) { + struct nhlt_format *fmt = &endp->formats[j]; + + free(fmt->config.capabilities); + } + } + + /* Free nhlt object proper */ + free(nhlt); +} + +struct cursor { + u8 *start; + u8 *buf; +}; + +static void ser8(struct cursor *cur, uint val) +{ + *cur->buf = val; + cur->buf += sizeof(u8); +} + +static void ser16(struct cursor *cur, uint val) +{ + put_unaligned_le16(val, cur->buf); + cur->buf += sizeof(u16); +} + +static void ser32(struct cursor *cur, uint val) +{ + put_unaligned_le32(val, cur->buf); + cur->buf += sizeof(u32); +} + +static void serblob(struct cursor *cur, void *from, size_t sz) +{ + memcpy(cur->buf, from, sz); + cur->buf += sz; +} + +static void serialise_specific_config(struct nhlt_specific_config *cfg, + struct cursor *cur) +{ + log_debug("%zx\n", cur->buf - cur->start); + ser32(cur, cfg->size); + serblob(cur, cfg->capabilities, cfg->size); +} + +static void serialise_waveform(struct nhlt_waveform *wave, struct cursor *cur) +{ + log_debug("%zx\n", cur->buf - cur->start); + ser16(cur, wave->tag); + ser16(cur, wave->num_channels); + ser32(cur, wave->samples_per_second); + ser32(cur, wave->bytes_per_second); + ser16(cur, wave->block_align); + ser16(cur, wave->bits_per_sample); + ser16(cur, wave->extra_size); + ser16(cur, wave->valid_bits_per_sample); + ser32(cur, wave->channel_mask); + ser32(cur, wave->sub_format.data1); + ser16(cur, wave->sub_format.data2); + ser16(cur, wave->sub_format.data3); + serblob(cur, wave->sub_format.data4, sizeof(wave->sub_format.data4)); +} + +static void serialise_format(struct nhlt_format *fmt, struct cursor *cur) +{ + log_debug("%zx\n", cur->buf - cur->start); + serialise_waveform(&fmt->waveform, cur); + serialise_specific_config(&fmt->config, cur); +} + +static void serialise_endpoint(struct nhlt_endpoint *endp, struct cursor *cur) +{ + int i; + + log_debug("%zx\n", cur->buf - cur->start); + ser32(cur, endp->length); + ser8(cur, endp->link_type); + ser8(cur, endp->instance_id); + ser16(cur, endp->vendor_id); + ser16(cur, endp->device_id); + ser16(cur, endp->revision_id); + ser32(cur, endp->subsystem_id); + ser8(cur, endp->device_type); + ser8(cur, endp->direction); + ser8(cur, endp->virtual_bus_id); + serialise_specific_config(&endp->config, cur); + ser8(cur, endp->num_formats); + + for (i = 0; i < endp->num_formats; i++) + serialise_format(&endp->formats[i], cur); +} + +static void nhlt_serialise_endpoints(struct nhlt *nhlt, struct cursor *cur) +{ + int i; + + ser8(cur, nhlt->num_endpoints); + + for (i = 0; i < nhlt->num_endpoints; i++) + serialise_endpoint(&nhlt->endpoints[i], cur); +} + +int nhlt_serialise_oem_overrides(struct acpi_ctx *ctx, struct nhlt *nhlt, + const char *oem_id, const char *oem_table_id, + uint32_t oem_revision) +{ + struct cursor cur; + struct acpi_table_header *header; + size_t sz; + size_t oem_id_len; + size_t oem_table_id_len; + int ret; + + log_debug("ACPI: * NHLT\n"); + sz = nhlt_current_size(nhlt); + + /* Create header */ + header = (void *)ctx->current; + memset(header, '\0', sizeof(struct acpi_table_header)); + memcpy(header->signature, "NHLT", 4); + header->length = sz; + header->revision = acpi_get_table_revision(ACPITAB_NHLT); + + if (oem_id) { + oem_id_len = min((int)strlen(oem_id), 6); + memcpy(header->oem_id, oem_id, oem_id_len); + } + if (oem_table_id) { + oem_table_id_len = min((int)strlen(oem_table_id), 8); + memcpy(header->oem_table_id, oem_table_id, oem_table_id_len); + } + header->oem_revision = oem_revision; + memcpy(header->aslc_id, ASLC_ID, 4); + + cur.buf = (void *)(header + 1); + cur.start = (void *)header; + nhlt_serialise_endpoints(nhlt, &cur); + + header->checksum = table_compute_checksum(header, sz); + nhlt_free_resources(nhlt); + assert(cur.buf - cur.start == sz); + + ret = acpi_add_table(ctx, ctx->current); + if (ret) + return log_msg_ret("add", ret); + acpi_inc_align(ctx, sz); + + return 0; +} + +static int _nhlt_add_single_endpoint(struct nhlt *nhlt, int virtual_bus_id, + const struct nhlt_endp_descriptor *epd) +{ + struct nhlt_endpoint *endp; + int ret; + + endp = nhlt_add_endpoint(nhlt, epd->link, epd->device, epd->direction, + epd->vid, epd->did); + if (!endp) + return -EINVAL; + + endp->virtual_bus_id = virtual_bus_id; + + ret = nhlt_endpoint_append_config(endp, epd->cfg, epd->cfg_size); + if (ret) + return ret; + + ret = nhlt_endpoint_add_formats(endp, epd->formats, epd->num_formats); + if (ret) + return log_msg_ret("formats", ret); + + return 0; +} + +static int _nhlt_add_endpoints(struct nhlt *nhlt, int virtual_bus_id, + const struct nhlt_endp_descriptor *epds, + size_t num_epds) +{ + size_t i; + int ret; + + for (i = 0; i < num_epds; i++) { + ret = _nhlt_add_single_endpoint(nhlt, virtual_bus_id, &epds[i]); + if (ret) + return log_ret(ret); + } + + return 0; +} + +int nhlt_add_endpoints(struct nhlt *nhlt, + const struct nhlt_endp_descriptor *epds, size_t num_epds) +{ + int ret; + + ret = _nhlt_add_endpoints(nhlt, DEFAULT_VIRTUAL_BUS_ID, epds, num_epds); + + return ret; +} + +int nhlt_add_ssp_endpoints(struct nhlt *nhlt, int virtual_bus_id, + const struct nhlt_endp_descriptor *epds, + size_t num_epds) +{ + int ret; + + ret = _nhlt_add_endpoints(nhlt, virtual_bus_id, epds, num_epds); + if (!ret) + nhlt_next_instance(nhlt, NHLT_LINK_SSP); + + return ret; +} diff --git a/roms/u-boot/arch/x86/lib/acpi_s3.c b/roms/u-boot/arch/x86/lib/acpi_s3.c new file mode 100644 index 000000000..2c70acbe7 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/acpi_s3.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <acpi/acpi_s3.h> +#include <acpi/acpi_table.h> +#include <asm/acpi.h> +#include <asm/global_data.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; +} diff --git a/roms/u-boot/arch/x86/lib/acpi_table.c b/roms/u-boot/arch/x86/lib/acpi_table.c new file mode 100644 index 000000000..5ec31301d --- /dev/null +++ b/roms/u-boot/arch/x86/lib/acpi_table.c @@ -0,0 +1,900 @@ +// 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> + */ + +#define LOG_CATEGORY LOGC_ACPI + +#include <common.h> +#include <bloblist.h> +#include <cpu.h> +#include <dm.h> +#include <log.h> +#include <dm/uclass-internal.h> +#include <mapmem.h> +#include <serial.h> +#include <version.h> +#include <acpi/acpigen.h> +#include <acpi/acpi_device.h> +#include <acpi/acpi_table.h> +#include <asm/acpi/global_nvs.h> +#include <asm/ioapic.h> +#include <asm/global_data.h> +#include <asm/lapic.h> +#include <asm/mpspec.h> +#include <asm/tables.h> +#include <asm/arch/global_nvs.h> +#include <dm/acpi.h> +#include <linux/err.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_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; + int cpu_num = 0; + + for (uclass_find_first_device(UCLASS_CPU, &dev); + dev; + uclass_find_next_device(&dev)) { + struct cpu_plat *plat = dev_get_parent_plat(dev); + int length; + + length = acpi_create_madt_lapic( + (struct acpi_madt_lapic *)current, cpu_num++, + 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 = ACPI_MADT_REV_ACPI_3_0; + + 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); +} + +/** + * acpi_create_tcpa() - Create a TCPA table + * + * @tcpa: Pointer to place to put table + * + * Trusted Computing Platform Alliance Capabilities Table + * TCPA PC Specific Implementation SpecificationTCPA is defined in the PCI + * Firmware Specification 3.0 + */ +static int acpi_create_tcpa(struct acpi_tcpa *tcpa) +{ + struct acpi_table_header *header = &tcpa->header; + u32 current = (u32)tcpa + sizeof(struct acpi_tcpa); + int size = 0x10000; /* Use this as the default size */ + void *log; + int ret; + + if (!CONFIG_IS_ENABLED(BLOBLIST)) + return -ENXIO; + memset(tcpa, '\0', sizeof(struct acpi_tcpa)); + + /* Fill out header fields */ + acpi_fill_header(header, "TCPA"); + header->length = sizeof(struct acpi_tcpa); + header->revision = 1; + + ret = bloblist_ensure_size_ret(BLOBLISTT_TCPA_LOG, &size, &log); + if (ret) + return log_msg_ret("blob", ret); + + tcpa->platform_class = 0; + tcpa->laml = size; + tcpa->lasa = (ulong)log; + + /* (Re)calculate length and checksum */ + header->length = current - (u32)tcpa; + header->checksum = table_compute_checksum((void *)tcpa, header->length); + + return 0; +} + +static int get_tpm2_log(void **ptrp, int *sizep) +{ + const int tpm2_default_log_len = 0x10000; + int size; + int ret; + + *sizep = 0; + size = tpm2_default_log_len; + ret = bloblist_ensure_size_ret(BLOBLISTT_TPM2_TCG_LOG, &size, ptrp); + if (ret) + return log_msg_ret("blob", ret); + *sizep = size; + + return 0; +} + +static int acpi_create_tpm2(struct acpi_tpm2 *tpm2) +{ + struct acpi_table_header *header = &tpm2->header; + int tpm2_log_len; + void *lasa; + int ret; + + memset((void *)tpm2, 0, sizeof(struct acpi_tpm2)); + + /* + * Some payloads like SeaBIOS depend on log area to use TPM2. + * Get the memory size and address of TPM2 log area or initialize it. + */ + ret = get_tpm2_log(&lasa, &tpm2_log_len); + if (ret) + return ret; + + /* Fill out header fields. */ + acpi_fill_header(header, "TPM2"); + memcpy(header->aslc_id, ASLC_ID, 4); + + header->length = sizeof(struct acpi_tpm2); + header->revision = acpi_get_table_revision(ACPITAB_TPM2); + + /* Hard to detect for coreboot. Just set it to 0 */ + tpm2->platform_class = 0; + + /* Must be set to 0 for FIFO-interface support */ + tpm2->control_area = 0; + tpm2->start_method = 6; + memset(tpm2->msp, 0, sizeof(tpm2->msp)); + + /* Fill the log area size and start address fields. */ + tpm2->laml = tpm2_log_len; + tpm2->lasa = (uintptr_t)lasa; + + /* Calculate checksum. */ + header->checksum = table_compute_checksum((void *)tpm2, header->length); + + return 0; +} + +__weak u32 acpi_fill_csrt(u32 current) +{ + return 0; +} + +static int acpi_create_csrt(struct acpi_csrt *csrt) +{ + struct acpi_table_header *header = &(csrt->header); + u32 current = (u32)csrt + sizeof(struct acpi_csrt); + uint ptr; + + 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; + + ptr = acpi_fill_csrt(current); + if (!ptr) + return -ENOENT; + current = ptr; + + /* (Re)calculate length and checksum */ + header->length = current - (u32)csrt; + header->checksum = table_compute_checksum((void *)csrt, header->length); + + return 0; +} + +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; + + memset((void *)spcr, 0, sizeof(struct acpi_spcr)); + + /* 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; + + /* + * SPCR has no clue if the UART base clock speed is different + * to the default one. However, the SPCR 1.04 defines baud rate + * 0 as a preconfigured state of UART and OS is supposed not + * to touch the configuration of the serial device. + */ + if (serial_info.clock != SERIAL_DEFAULT_CLOCK) + spcr->baud_rate = 0; + + /* Fix checksum */ + header->checksum = table_compute_checksum((void *)spcr, header->length); +} + +static int acpi_create_ssdt(struct acpi_ctx *ctx, + struct acpi_table_header *ssdt, + const char *oem_table_id) +{ + memset((void *)ssdt, '\0', sizeof(struct acpi_table_header)); + + acpi_fill_header(ssdt, "SSDT"); + ssdt->revision = acpi_get_table_revision(ACPITAB_SSDT); + ssdt->aslc_revision = 1; + ssdt->length = sizeof(struct acpi_table_header); + + acpi_inc(ctx, sizeof(struct acpi_table_header)); + + acpi_fill_ssdt(ctx); + + /* (Re)calculate length and checksum */ + ssdt->length = ctx->current - (void *)ssdt; + ssdt->checksum = table_compute_checksum((void *)ssdt, ssdt->length); + log_debug("SSDT at %p, length %x\n", ssdt, ssdt->length); + + /* Drop the table if it is empty */ + if (ssdt->length == sizeof(struct acpi_table_header)) { + ctx->current = ssdt; + return -ENOENT; + } + acpi_align(ctx); + + return 0; +} + +/* + * QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c + */ +ulong write_acpi_tables(ulong start_addr) +{ + const int thl = sizeof(struct acpi_table_header); + struct acpi_ctx *ctx; + struct acpi_facs *facs; + struct acpi_table_header *dsdt; + struct acpi_fadt *fadt; + struct acpi_table_header *ssdt; + struct acpi_mcfg *mcfg; + struct acpi_tcpa *tcpa; + struct acpi_madt *madt; + struct acpi_csrt *csrt; + struct acpi_spcr *spcr; + void *start; + int aml_len; + ulong addr; + int ret; + int i; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) + return log_msg_ret("mem", -ENOMEM); + gd->acpi_ctx = ctx; + + start = map_sysmem(start_addr, 0); + + debug("ACPI: Writing ACPI tables at %lx\n", start_addr); + + acpi_reset_items(); + acpi_setup_base_tables(ctx, start); + + debug("ACPI: * FACS\n"); + facs = ctx->current; + acpi_inc_align(ctx, sizeof(struct acpi_facs)); + + acpi_create_facs(facs); + + debug("ACPI: * DSDT\n"); + dsdt = ctx->current; + + /* Put the table header first */ + memcpy(dsdt, &AmlCode, thl); + acpi_inc(ctx, thl); + log_debug("DSDT starts at %p, hdr ends at %p\n", dsdt, ctx->current); + + /* If the table is not empty, allow devices to inject things */ + aml_len = dsdt->length - thl; + if (aml_len) { + void *base = ctx->current; + + acpi_inject_dsdt(ctx); + log_debug("Added %x bytes from inject_dsdt, now at %p\n", + ctx->current - base, ctx->current); + log_debug("Copy AML code size %x to %p\n", aml_len, + ctx->current); + memcpy(ctx->current, AmlCode + thl, aml_len); + acpi_inc(ctx, aml_len); + } + + dsdt->length = ctx->current - (void *)dsdt; + acpi_align(ctx); + log_debug("Updated DSDT length to %x, total %x\n", dsdt->length, + ctx->current - (void *)dsdt); + + if (!IS_ENABLED(CONFIG_ACPI_GNVS_EXTERNAL)) { + /* 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) { + *gnvs = map_to_sysmem(ctx->current); + debug("Fix up global NVS in DSDT to %#08x\n", + *gnvs); + break; + } + } + + /* + * Fill in platform-specific global NVS variables. If this fails + * we cannot return the error but this should only happen while + * debugging. + */ + addr = acpi_create_gnvs(ctx->current); + if (IS_ERR_VALUE(addr)) + printf("Error: Gailed to create GNVS\n"); + acpi_inc_align(ctx, sizeof(struct acpi_global_nvs)); + } + + /* + * Recalculate the length and update the DSDT checksum since we patched + * the GNVS address. Set the checksum to zero since it is part of the + * region being checksummed. + */ + dsdt->checksum = 0; + dsdt->checksum = table_compute_checksum((void *)dsdt, dsdt->length); + + /* + * Fill in platform-specific global NVS variables. If this fails we + * cannot return the error but this should only happen while debugging. + */ + addr = acpi_create_gnvs(ctx->current); + if (IS_ERR_VALUE(addr)) + printf("Error: Failed to create GNVS\n"); + + acpi_inc_align(ctx, sizeof(struct acpi_global_nvs)); + + debug("ACPI: * FADT\n"); + fadt = ctx->current; + acpi_inc_align(ctx, sizeof(struct acpi_fadt)); + acpi_create_fadt(fadt, facs, dsdt); + acpi_add_table(ctx, fadt); + + debug("ACPI: * SSDT\n"); + ssdt = (struct acpi_table_header *)ctx->current; + if (!acpi_create_ssdt(ctx, ssdt, OEM_TABLE_ID)) + acpi_add_table(ctx, ssdt); + + debug("ACPI: * MCFG\n"); + mcfg = ctx->current; + acpi_create_mcfg(mcfg); + acpi_inc_align(ctx, mcfg->header.length); + acpi_add_table(ctx, mcfg); + + if (IS_ENABLED(CONFIG_TPM_V2)) { + struct acpi_tpm2 *tpm2; + + debug("ACPI: * TPM2\n"); + tpm2 = (struct acpi_tpm2 *)ctx->current; + ret = acpi_create_tpm2(tpm2); + if (!ret) { + acpi_inc_align(ctx, tpm2->header.length); + acpi_add_table(ctx, tpm2); + } else { + log_warning("TPM2 table creation failed\n"); + } + } + + debug("ACPI: * MADT\n"); + madt = ctx->current; + acpi_create_madt(madt); + acpi_inc_align(ctx, madt->header.length); + acpi_add_table(ctx, madt); + + if (IS_ENABLED(CONFIG_TPM_V1)) { + debug("ACPI: * TCPA\n"); + tcpa = (struct acpi_tcpa *)ctx->current; + ret = acpi_create_tcpa(tcpa); + if (ret) { + log_warning("Failed to create TCPA table (err=%d)\n", + ret); + } else { + acpi_inc_align(ctx, tcpa->header.length); + acpi_add_table(ctx, tcpa); + } + } + + debug("ACPI: * CSRT\n"); + csrt = ctx->current; + if (!acpi_create_csrt(csrt)) { + acpi_inc_align(ctx, csrt->header.length); + acpi_add_table(ctx, csrt); + } + + debug("ACPI: * SPCR\n"); + spcr = ctx->current; + acpi_create_spcr(spcr); + acpi_inc_align(ctx, spcr->header.length); + acpi_add_table(ctx, spcr); + + acpi_write_dev_tables(ctx); + + addr = map_to_sysmem(ctx->current); + debug("current = %lx\n", addr); + + acpi_rsdp_addr = (unsigned long)ctx->rsdp; + debug("ACPI: done\n"); + + return addr; +} + +ulong acpi_get_rsdp_addr(void) +{ + return acpi_rsdp_addr; +} + +/** + * acpi_write_hpet() - Write out a HPET table + * + * Write out the table for High-Precision Event Timers + * + * @hpet: Place to put HPET table + */ +static int acpi_create_hpet(struct acpi_hpet *hpet) +{ + struct acpi_table_header *header = &hpet->header; + struct acpi_gen_regaddr *addr = &hpet->addr; + + /* + * See IA-PC HPET (High Precision Event Timers) Specification v1.0a + * https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/software-developers-hpet-spec-1-0a.pdf + */ + memset((void *)hpet, '\0', sizeof(struct acpi_hpet)); + + /* Fill out header fields. */ + acpi_fill_header(header, "HPET"); + + header->aslc_revision = ASL_REVISION; + header->length = sizeof(struct acpi_hpet); + header->revision = acpi_get_table_revision(ACPITAB_HPET); + + /* Fill out HPET address */ + addr->space_id = 0; /* Memory */ + addr->bit_width = 64; + addr->bit_offset = 0; + addr->addrl = CONFIG_HPET_ADDRESS & 0xffffffff; + addr->addrh = ((unsigned long long)CONFIG_HPET_ADDRESS) >> 32; + + hpet->id = *(u32 *)CONFIG_HPET_ADDRESS; + hpet->number = 0; + hpet->min_tick = 0; /* HPET_MIN_TICKS */ + + header->checksum = table_compute_checksum(hpet, + sizeof(struct acpi_hpet)); + + return 0; +} + +int acpi_write_hpet(struct acpi_ctx *ctx) +{ + struct acpi_hpet *hpet; + int ret; + + log_debug("ACPI: * HPET\n"); + + hpet = ctx->current; + acpi_inc_align(ctx, sizeof(struct acpi_hpet)); + acpi_create_hpet(hpet); + ret = acpi_add_table(ctx, hpet); + if (ret) + return log_msg_ret("add", ret); + + return 0; +} + +int acpi_write_dbg2_pci_uart(struct acpi_ctx *ctx, struct udevice *dev, + uint access_size) +{ + struct acpi_dbg2_header *dbg2 = ctx->current; + char path[ACPI_PATH_MAX]; + struct acpi_gen_regaddr address; + phys_addr_t addr; + int ret; + + if (!device_active(dev)) { + log_info("Device not enabled\n"); + return -EACCES; + } + /* + * PCI devices don't remember their resource allocation information in + * U-Boot at present. We assume that MMIO is used for the UART and that + * the address space is 32 bytes: ns16550 uses 8 registers of up to + * 32-bits each. This is only for debugging so it is not a big deal. + */ + addr = dm_pci_read_bar32(dev, 0); + log_debug("UART addr %lx\n", (ulong)addr); + + memset(&address, '\0', sizeof(address)); + address.space_id = ACPI_ADDRESS_SPACE_MEMORY; + address.addrl = (uint32_t)addr; + address.addrh = (uint32_t)((addr >> 32) & 0xffffffff); + address.access_size = access_size; + + ret = acpi_device_path(dev, path, sizeof(path)); + if (ret) + return log_msg_ret("path", ret); + acpi_create_dbg2(dbg2, ACPI_DBG2_SERIAL_PORT, + ACPI_DBG2_16550_COMPATIBLE, &address, 0x1000, path); + + acpi_inc_align(ctx, dbg2->header.length); + acpi_add_table(ctx, dbg2); + + return 0; +} + +void acpi_fadt_common(struct acpi_fadt *fadt, struct acpi_facs *facs, + void *dsdt) +{ + struct acpi_table_header *header = &fadt->header; + + memset((void *)fadt, '\0', sizeof(struct acpi_fadt)); + + acpi_fill_header(header, "FACP"); + header->length = sizeof(struct acpi_fadt); + header->revision = 4; + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, OEM_TABLE_ID, 8); + memcpy(header->aslc_id, ASLC_ID, 4); + header->aslc_revision = 1; + + fadt->firmware_ctrl = (unsigned long)facs; + fadt->dsdt = (unsigned long)dsdt; + + fadt->x_firmware_ctl_l = (unsigned long)facs; + fadt->x_firmware_ctl_h = 0; + fadt->x_dsdt_l = (unsigned long)dsdt; + fadt->x_dsdt_h = 0; + + fadt->preferred_pm_profile = ACPI_PM_MOBILE; + + /* Use ACPI 3.0 revision */ + fadt->header.revision = 4; +} + +void acpi_create_dmar_drhd(struct acpi_ctx *ctx, uint flags, uint segment, + u64 bar) +{ + struct dmar_entry *drhd = ctx->current; + + memset(drhd, '\0', sizeof(*drhd)); + drhd->type = DMAR_DRHD; + drhd->length = sizeof(*drhd); /* will be fixed up later */ + drhd->flags = flags; + drhd->segment = segment; + drhd->bar = bar; + acpi_inc(ctx, drhd->length); +} + +void acpi_create_dmar_rmrr(struct acpi_ctx *ctx, uint segment, u64 bar, + u64 limit) +{ + struct dmar_rmrr_entry *rmrr = ctx->current; + + memset(rmrr, '\0', sizeof(*rmrr)); + rmrr->type = DMAR_RMRR; + rmrr->length = sizeof(*rmrr); /* will be fixed up later */ + rmrr->segment = segment; + rmrr->bar = bar; + rmrr->limit = limit; + acpi_inc(ctx, rmrr->length); +} + +void acpi_dmar_drhd_fixup(struct acpi_ctx *ctx, void *base) +{ + struct dmar_entry *drhd = base; + + drhd->length = ctx->current - base; +} + +void acpi_dmar_rmrr_fixup(struct acpi_ctx *ctx, void *base) +{ + struct dmar_rmrr_entry *rmrr = base; + + rmrr->length = ctx->current - base; +} + +static int acpi_create_dmar_ds(struct acpi_ctx *ctx, enum dev_scope_type type, + uint enumeration_id, pci_dev_t bdf) +{ + /* we don't support longer paths yet */ + const size_t dev_scope_length = sizeof(struct dev_scope) + 2; + struct dev_scope *ds = ctx->current; + + memset(ds, '\0', dev_scope_length); + ds->type = type; + ds->length = dev_scope_length; + ds->enumeration = enumeration_id; + ds->start_bus = PCI_BUS(bdf); + ds->path[0].dev = PCI_DEV(bdf); + ds->path[0].fn = PCI_FUNC(bdf); + + return ds->length; +} + +int acpi_create_dmar_ds_pci_br(struct acpi_ctx *ctx, pci_dev_t bdf) +{ + return acpi_create_dmar_ds(ctx, SCOPE_PCI_SUB, 0, bdf); +} + +int acpi_create_dmar_ds_pci(struct acpi_ctx *ctx, pci_dev_t bdf) +{ + return acpi_create_dmar_ds(ctx, SCOPE_PCI_ENDPOINT, 0, bdf); +} + +int acpi_create_dmar_ds_ioapic(struct acpi_ctx *ctx, uint enumeration_id, + pci_dev_t bdf) +{ + return acpi_create_dmar_ds(ctx, SCOPE_IOAPIC, enumeration_id, bdf); +} + +int acpi_create_dmar_ds_msi_hpet(struct acpi_ctx *ctx, uint enumeration_id, + pci_dev_t bdf) +{ + return acpi_create_dmar_ds(ctx, SCOPE_MSI_HPET, enumeration_id, bdf); +} diff --git a/roms/u-boot/arch/x86/lib/acpigen.c b/roms/u-boot/arch/x86/lib/acpigen.c new file mode 100644 index 000000000..ea2ec2a90 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/acpigen.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Google LLC + */ + +#include <common.h> +#include <acpi/acpigen.h> +#include <acpi/acpi_table.h> +#include <asm/acpigen.h> + +void acpigen_write_empty_pct(struct acpi_ctx *ctx) +{ + /* + * Name (_PCT, Package (0x02) + * { + * ResourceTemplate () + * { + * Register (FFixedHW, + * 0x00, // Bit Width + * 0x00, // Bit Offset + * 0x0000000000000000, // Address + * ,) + * }, + * + * ResourceTemplate () + * { + * Register (FFixedHW, + * 0x00, // Bit Width + * 0x00, // Bit Offset + * 0x0000000000000000, // Address + * ,) + * } + * }) + */ + static char stream[] = { + /* 00000030 "0._PCT.," */ + 0x08, 0x5f, 0x50, 0x43, 0x54, 0x12, 0x2c, + /* 00000038 "........" */ + 0x02, 0x11, 0x14, 0x0a, 0x11, 0x82, 0x0c, 0x00, + /* 00000040 "........" */ + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000048 "....y..." */ + 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x11, 0x14, + /* 00000050 "........" */ + 0x0a, 0x11, 0x82, 0x0c, 0x00, 0x7f, 0x00, 0x00, + /* 00000058 "........" */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x79, 0x00 + }; + acpigen_emit_stream(ctx, stream, ARRAY_SIZE(stream)); +} + +void acpigen_write_empty_ptc(struct acpi_ctx *ctx) +{ + /* + * Name (_PTC, Package (0x02) + * { + * ResourceTemplate () + * { + * Register (FFixedHW, + * 0x00, // Bit Width + * 0x00, // Bit Offset + * 0x0000000000000000, // Address + * ,) + * }, + * + * ResourceTemplate () + * { + * Register (FFixedHW, + * 0x00, // Bit Width + * 0x00, // Bit Offset + * 0x0000000000000000, // Address + * ,) + * } + * }) + */ + struct acpi_gen_regaddr addr = { + .space_id = ACPI_ADDRESS_SPACE_FIXED, + .bit_width = 0, + .bit_offset = 0, + .access_size = 0, + .addrl = 0, + .addrh = 0, + }; + + acpigen_write_name(ctx, "_PTC"); + acpigen_write_package(ctx, 2); + + /* ControlRegister */ + acpigen_write_register_resource(ctx, &addr); + + /* StatusRegister */ + acpigen_write_register_resource(ctx, &addr); + + acpigen_pop_len(ctx); +} diff --git a/roms/u-boot/arch/x86/lib/asm-offsets.c b/roms/u-boot/arch/x86/lib/asm-offsets.c new file mode 100644 index 000000000..8df67db65 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/asm-offsets.c @@ -0,0 +1,26 @@ +// 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 <asm/global_data.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; +} diff --git a/roms/u-boot/arch/x86/lib/bios.c b/roms/u-boot/arch/x86/lib/bios.c new file mode 100644 index 000000000..fbdc3b04e --- /dev/null +++ b/roms/u-boot/arch/x86/lib/bios.c @@ -0,0 +1,357 @@ +// 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 <compiler.h> +#include <bios_emul.h> +#include <irq_func.h> +#include <log.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 = ®_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; +} diff --git a/roms/u-boot/arch/x86/lib/bios.h b/roms/u-boot/arch/x86/lib/bios.h new file mode 100644 index 000000000..2e51b9fbf --- /dev/null +++ b/roms/u-boot/arch/x86/lib/bios.h @@ -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 diff --git a/roms/u-boot/arch/x86/lib/bios_asm.S b/roms/u-boot/arch/x86/lib/bios_asm.S new file mode 100644 index 000000000..62dc5652d --- /dev/null +++ b/roms/u-boot/arch/x86/lib/bios_asm.S @@ -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 diff --git a/roms/u-boot/arch/x86/lib/bios_interrupts.c b/roms/u-boot/arch/x86/lib/bios_interrupts.c new file mode 100644 index 000000000..d6b4da7e2 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/bios_interrupts.c @@ -0,0 +1,219 @@ +// 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 <log.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; +} diff --git a/roms/u-boot/arch/x86/lib/bootm.c b/roms/u-boot/arch/x86/lib/bootm.c new file mode 100644 index 000000000..733dd7125 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/bootm.c @@ -0,0 +1,225 @@ +// 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 <bootstage.h> +#include <command.h> +#include <hang.h> +#include <log.h> +#include <asm/global_data.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_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) +{ + struct bd_info *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, 0); + + 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); +} diff --git a/roms/u-boot/arch/x86/lib/cmd_boot.c b/roms/u-boot/arch/x86/lib/cmd_boot.c new file mode 100644 index 000000000..4facbe5f3 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/cmd_boot.c @@ -0,0 +1,50 @@ +// 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/global_data.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; +} diff --git a/roms/u-boot/arch/x86/lib/coreboot/Makefile b/roms/u-boot/arch/x86/lib/coreboot/Makefile new file mode 100644 index 000000000..cb0ae1d01 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/coreboot/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2021 Google LLC +# + +obj-y += cb_sysinfo.o +obj-y += cb_support.o diff --git a/roms/u-boot/arch/x86/lib/coreboot/cb_support.c b/roms/u-boot/arch/x86/lib/coreboot/cb_support.c new file mode 100644 index 000000000..ebb45cdfb --- /dev/null +++ b/roms/u-boot/arch/x86/lib/coreboot/cb_support.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Support for booting from coreboot + * + * Copyright 2021 Google LLC + */ + +#include <common.h> +#include <asm/cb_sysinfo.h> +#include <asm/e820.h> + +unsigned int cb_install_e820_map(unsigned int max_entries, + struct e820_entry *entries) +{ + unsigned int num_entries; + int i; + + num_entries = min((unsigned int)lib_sysinfo.n_memranges, max_entries); + if (num_entries < lib_sysinfo.n_memranges) { + printf("Warning: Limiting e820 map to %d entries\n", + num_entries); + } + for (i = 0; i < num_entries; i++) { + struct memrange *memrange = &lib_sysinfo.memrange[i]; + + entries[i].addr = memrange->base; + entries[i].size = memrange->size; + + /* + * coreboot has some extensions (type 6 & 16) to the E820 types. + * When we detect this, mark it as E820_RESERVED. + */ + if (memrange->type == CB_MEM_VENDOR_RSVD || + memrange->type == CB_MEM_TABLE) + entries[i].type = E820_RESERVED; + else + entries[i].type = memrange->type; + } + + return num_entries; +} diff --git a/roms/u-boot/arch/x86/lib/coreboot/cb_sysinfo.c b/roms/u-boot/arch/x86/lib/coreboot/cb_sysinfo.c new file mode 100644 index 000000000..748fa4ee5 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/coreboot/cb_sysinfo.c @@ -0,0 +1,468 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * This file is part of the libpayload project. + * + * Copyright (C) 2008 Advanced Micro Devices, Inc. + * Copyright (C) 2009 coresystems GmbH + */ + +#include <common.h> +#include <asm/cb_sysinfo.h> +#include <init.h> +#include <mapmem.h> +#include <net.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * This needs to be in the .data section so that it's copied over during + * relocation. By default it's put in the .bss section which is simply filled + * with zeroes when transitioning from "ROM", which is really RAM, to other + * RAM. + */ +struct sysinfo_t lib_sysinfo __section(".data"); + +/* + * Some of this is x86 specific, and the rest of it is generic. Right now, + * since we only support x86, we'll avoid trying to make lots of infrastructure + * we don't need. If in the future, we want to use coreboot on some other + * architecture, then take out the generic parsing code and move it elsewhere. + */ + +/* === Parsing code === */ +/* This is the generic parsing code */ + +static void cb_parse_memory(unsigned char *ptr, struct sysinfo_t *info) +{ + struct cb_memory *mem = (struct cb_memory *)ptr; + int count = MEM_RANGE_COUNT(mem); + int i; + + if (count > SYSINFO_MAX_MEM_RANGES) + count = SYSINFO_MAX_MEM_RANGES; + + info->n_memranges = 0; + + for (i = 0; i < count; i++) { + struct cb_memory_range *range = + (struct cb_memory_range *)MEM_RANGE_PTR(mem, i); + + info->memrange[info->n_memranges].base = + UNPACK_CB64(range->start); + + info->memrange[info->n_memranges].size = + UNPACK_CB64(range->size); + + info->memrange[info->n_memranges].type = range->type; + + info->n_memranges++; + } +} + +static void cb_parse_serial(unsigned char *ptr, struct sysinfo_t *info) +{ + struct cb_serial *ser = (struct cb_serial *)ptr; + + info->serial = ser; +} + +static void cb_parse_vboot_handoff(unsigned char *ptr, struct sysinfo_t *info) +{ + struct lb_range *vbho = (struct lb_range *)ptr; + + info->vboot_handoff = (void *)(uintptr_t)vbho->range_start; + info->vboot_handoff_size = vbho->range_size; +} + +static void cb_parse_vbnv(unsigned char *ptr, struct sysinfo_t *info) +{ + struct lb_range *vbnv = (struct lb_range *)ptr; + + info->vbnv_start = vbnv->range_start; + info->vbnv_size = vbnv->range_size; +} + +static void cb_parse_cbmem_entry(unsigned char *ptr, struct sysinfo_t *info) +{ + struct cb_cbmem_entry *entry = (struct cb_cbmem_entry *)ptr; + + if (entry->id != CBMEM_ID_SMBIOS) + return; + + info->smbios_start = entry->address; + info->smbios_size = entry->entry_size; +} + +static void cb_parse_gpios(unsigned char *ptr, struct sysinfo_t *info) +{ + int i; + struct cb_gpios *gpios = (struct cb_gpios *)ptr; + + info->num_gpios = (gpios->count < SYSINFO_MAX_GPIOS) ? + (gpios->count) : SYSINFO_MAX_GPIOS; + + for (i = 0; i < info->num_gpios; i++) + info->gpios[i] = gpios->gpios[i]; +} + +static void cb_parse_vdat(unsigned char *ptr, struct sysinfo_t *info) +{ + struct lb_range *vdat = (struct lb_range *)ptr; + + info->vdat_addr = map_sysmem(vdat->range_start, vdat->range_size); + info->vdat_size = vdat->range_size; +} + +static void cb_parse_mac_addresses(unsigned char *ptr, + struct sysinfo_t *info) +{ + struct cb_macs *macs = (struct cb_macs *)ptr; + int i; + + info->num_macs = (macs->count < ARRAY_SIZE(info->macs)) ? + macs->count : ARRAY_SIZE(info->macs); + + for (i = 0; i < info->num_macs; i++) + info->macs[i] = macs->mac_addrs[i]; +} + +static void cb_parse_tstamp(void *ptr, struct sysinfo_t *info) +{ + struct cb_cbmem_tab *const cbmem = ptr; + + info->tstamp_table = map_sysmem(cbmem->cbmem_tab, 0); +} + +static void cb_parse_cbmem_cons(void *ptr, struct sysinfo_t *info) +{ + struct cb_cbmem_tab *const cbmem = ptr; + + info->cbmem_cons = map_sysmem(cbmem->cbmem_tab, 0); +} + +static void cb_parse_acpi_gnvs(unsigned char *ptr, struct sysinfo_t *info) +{ + struct cb_cbmem_tab *const cbmem = (struct cb_cbmem_tab *)ptr; + + info->acpi_gnvs = map_sysmem(cbmem->cbmem_tab, 0); +} + +static void cb_parse_board_id(unsigned char *ptr, struct sysinfo_t *info) +{ + struct cb_board_id *const cbbid = (struct cb_board_id *)ptr; + + info->board_id = cbbid->board_id; +} + +static void cb_parse_ram_code(unsigned char *ptr, struct sysinfo_t *info) +{ + struct cb_ram_code *const ram_code = (struct cb_ram_code *)ptr; + + info->ram_code = ram_code->ram_code; +} + +static void cb_parse_optiontable(void *ptr, struct sysinfo_t *info) +{ + /* ptr points to a coreboot table entry and is already virtual */ + info->option_table = ptr; +} + +static void cb_parse_checksum(void *ptr, struct sysinfo_t *info) +{ + struct cb_cmos_checksum *cmos_cksum = ptr; + + info->cmos_range_start = cmos_cksum->range_start; + info->cmos_range_end = cmos_cksum->range_end; + info->cmos_checksum_location = cmos_cksum->location; +} + +static void cb_parse_framebuffer(void *ptr, struct sysinfo_t *info) +{ + /* ptr points to a coreboot table entry and is already virtual */ + info->framebuffer = ptr; +} + +static void cb_parse_string(unsigned char *ptr, char **info) +{ + *info = (char *)((struct cb_string *)ptr)->string; +} + +static void cb_parse_wifi_calibration(void *ptr, struct sysinfo_t *info) +{ + struct cb_cbmem_tab *const cbmem = (struct cb_cbmem_tab *)ptr; + + info->wifi_calibration = map_sysmem(cbmem->cbmem_tab, 0); +} + +static void cb_parse_ramoops(void *ptr, struct sysinfo_t *info) +{ + struct lb_range *ramoops = (struct lb_range *)ptr; + + info->ramoops_buffer = ramoops->range_start; + info->ramoops_buffer_size = ramoops->range_size; +} + +static void cb_parse_mtc(void *ptr, struct sysinfo_t *info) +{ + struct lb_range *mtc = (struct lb_range *)ptr; + + info->mtc_start = mtc->range_start; + info->mtc_size = mtc->range_size; +} + +static void cb_parse_spi_flash(void *ptr, struct sysinfo_t *info) +{ + struct cb_spi_flash *flash = (struct cb_spi_flash *)ptr; + + info->spi_flash.size = flash->flash_size; + info->spi_flash.sector_size = flash->sector_size; + info->spi_flash.erase_cmd = flash->erase_cmd; +} + +static void cb_parse_boot_media_params(unsigned char *ptr, + struct sysinfo_t *info) +{ + struct cb_boot_media_params *const bmp = + (struct cb_boot_media_params *)ptr; + + info->fmap_offset = bmp->fmap_offset; + info->cbfs_offset = bmp->cbfs_offset; + info->cbfs_size = bmp->cbfs_size; + info->boot_media_size = bmp->boot_media_size; +} + +static void cb_parse_vpd(void *ptr, struct sysinfo_t *info) +{ + struct cb_cbmem_tab *const cbmem = (struct cb_cbmem_tab *)ptr; + + info->chromeos_vpd = map_sysmem(cbmem->cbmem_tab, 0); +} + +static void cb_parse_tsc_info(void *ptr, struct sysinfo_t *info) +{ + const struct cb_tsc_info *tsc_info = ptr; + + if (tsc_info->freq_khz == 0) + return; + + /* Honor the TSC frequency passed to the payload */ + info->cpu_khz = tsc_info->freq_khz; +} + +static void cb_parse_x86_rom_var_mtrr(void *ptr, struct sysinfo_t *info) +{ + struct cb_x86_rom_mtrr *rom_mtrr = ptr; + + info->x86_rom_var_mtrr_index = rom_mtrr->index; +} + +static void cb_parse_mrc_cache(void *ptr, struct sysinfo_t *info) +{ + struct cb_cbmem_tab *const cbmem = (struct cb_cbmem_tab *)ptr; + + info->mrc_cache = map_sysmem(cbmem->cbmem_tab, 0); +} + +__weak void cb_parse_unhandled(u32 tag, unsigned char *ptr) +{ +} + +static int cb_parse_header(void *addr, int len, struct sysinfo_t *info) +{ + unsigned char *ptr = addr; + struct cb_header *header; + int i; + + header = (struct cb_header *)ptr; + if (!header->table_bytes) + return 0; + + /* Make sure the checksums match */ + if (!ip_checksum_ok(header, sizeof(*header))) + return -1; + + if (compute_ip_checksum(ptr + sizeof(*header), header->table_bytes) != + header->table_checksum) + return -1; + + info->header = header; + + /* + * Board straps represented by numerical values are small numbers. + * Preset them to an invalid value in case the firmware does not + * supply the info. + */ + info->board_id = ~0; + info->ram_code = ~0; + + /* Now, walk the tables */ + ptr += header->header_bytes; + + /* Inintialize some fields to sentinel values */ + info->vbnv_start = info->vbnv_size = (uint32_t)(-1); + + for (i = 0; i < header->table_entries; i++) { + struct cb_record *rec = (struct cb_record *)ptr; + + /* We only care about a few tags here (maybe more later) */ + switch (rec->tag) { + case CB_TAG_FORWARD: + return cb_parse_header( + (void *)(unsigned long) + ((struct cb_forward *)rec)->forward, + len, info); + continue; + case CB_TAG_MEMORY: + cb_parse_memory(ptr, info); + break; + case CB_TAG_SERIAL: + cb_parse_serial(ptr, info); + break; + case CB_TAG_VERSION: + cb_parse_string(ptr, &info->cb_version); + break; + case CB_TAG_EXTRA_VERSION: + cb_parse_string(ptr, &info->extra_version); + break; + case CB_TAG_BUILD: + cb_parse_string(ptr, &info->build); + break; + case CB_TAG_COMPILE_TIME: + cb_parse_string(ptr, &info->compile_time); + break; + case CB_TAG_COMPILE_BY: + cb_parse_string(ptr, &info->compile_by); + break; + case CB_TAG_COMPILE_HOST: + cb_parse_string(ptr, &info->compile_host); + break; + case CB_TAG_COMPILE_DOMAIN: + cb_parse_string(ptr, &info->compile_domain); + break; + case CB_TAG_COMPILER: + cb_parse_string(ptr, &info->compiler); + break; + case CB_TAG_LINKER: + cb_parse_string(ptr, &info->linker); + break; + case CB_TAG_ASSEMBLER: + cb_parse_string(ptr, &info->assembler); + break; + case CB_TAG_CMOS_OPTION_TABLE: + cb_parse_optiontable(ptr, info); + break; + case CB_TAG_OPTION_CHECKSUM: + cb_parse_checksum(ptr, info); + break; + /* + * FIXME we should warn on serial if coreboot set up a + * framebuffer buf the payload does not know about it. + */ + case CB_TAG_FRAMEBUFFER: + cb_parse_framebuffer(ptr, info); + break; + case CB_TAG_MAINBOARD: + info->mainboard = (struct cb_mainboard *)ptr; + break; + case CB_TAG_GPIO: + cb_parse_gpios(ptr, info); + break; + case CB_TAG_VDAT: + cb_parse_vdat(ptr, info); + break; + case CB_TAG_VBNV: + cb_parse_vbnv(ptr, info); + break; + case CB_TAG_VBOOT_HANDOFF: + cb_parse_vboot_handoff(ptr, info); + break; + case CB_TAG_MAC_ADDRS: + cb_parse_mac_addresses(ptr, info); + break; + case CB_TAG_SERIALNO: + cb_parse_string(ptr, &info->serialno); + break; + case CB_TAG_TIMESTAMPS: + cb_parse_tstamp(ptr, info); + break; + case CB_TAG_CBMEM_CONSOLE: + cb_parse_cbmem_cons(ptr, info); + break; + case CB_TAG_ACPI_GNVS: + cb_parse_acpi_gnvs(ptr, info); + break; + case CB_TAG_CBMEM_ENTRY: + cb_parse_cbmem_entry(ptr, info); + break; + case CB_TAG_BOARD_ID: + cb_parse_board_id(ptr, info); + break; + case CB_TAG_RAM_CODE: + cb_parse_ram_code(ptr, info); + break; + case CB_TAG_WIFI_CALIBRATION: + cb_parse_wifi_calibration(ptr, info); + break; + case CB_TAG_RAM_OOPS: + cb_parse_ramoops(ptr, info); + break; + case CB_TAG_SPI_FLASH: + cb_parse_spi_flash(ptr, info); + break; + case CB_TAG_MTC: + cb_parse_mtc(ptr, info); + break; + case CB_TAG_BOOT_MEDIA_PARAMS: + cb_parse_boot_media_params(ptr, info); + break; + case CB_TAG_TSC_INFO: + cb_parse_tsc_info(ptr, info); + break; + case CB_TAG_VPD: + cb_parse_vpd(ptr, info); + break; + case CB_TAG_X86_ROM_MTRR: + cb_parse_x86_rom_var_mtrr(rec, info); + break; + case CB_TAG_MRC_CACHE: + cb_parse_mrc_cache(rec, info); + break; + default: + cb_parse_unhandled(rec->tag, ptr); + break; + } + + ptr += rec->size; + } + + return 1; +} + +/* == Architecture specific == */ +/* This is the x86 specific stuff */ + +int get_coreboot_info(struct sysinfo_t *info) +{ + long addr; + int ret; + + addr = locate_coreboot_table(); + if (addr < 0) + return addr; + ret = cb_parse_header((void *)addr, 0x1000, info); + if (!ret) + return -ENOENT; + gd->arch.coreboot_table = addr; + gd->flags |= GD_FLG_SKIP_LL_INIT; + + return 0; +} + +const struct sysinfo_t *cb_get_sysinfo(void) +{ + if (!ll_boot_init()) + return &lib_sysinfo; + + return NULL; +} diff --git a/roms/u-boot/arch/x86/lib/coreboot_table.c b/roms/u-boot/arch/x86/lib/coreboot_table.c new file mode 100644 index 000000000..6eab0452f --- /dev/null +++ b/roms/u-boot/arch/x86/lib/coreboot_table.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <vbe.h> +#include <acpi/acpi_s3.h> +#include <asm/coreboot_tables.h> +#include <asm/e820.h> +#include <asm/global_data.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 */ + if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) && + gd->arch.prev_sleep_state != ACPI_S3) { + 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); +} diff --git a/roms/u-boot/arch/x86/lib/crt0_ia32_efi.S b/roms/u-boot/arch/x86/lib/crt0_ia32_efi.S new file mode 100644 index 000000000..c0a4853ea --- /dev/null +++ b/roms/u-boot/arch/x86/lib/crt0_ia32_efi.S @@ -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 */ diff --git a/roms/u-boot/arch/x86/lib/crt0_x86_64_efi.S b/roms/u-boot/arch/x86/lib/crt0_x86_64_efi.S new file mode 100644 index 000000000..47ed5af97 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/crt0_x86_64_efi.S @@ -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 */ diff --git a/roms/u-boot/arch/x86/lib/div64.c b/roms/u-boot/arch/x86/lib/div64.c new file mode 100644 index 000000000..2bea205f6 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/div64.c @@ -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; +} diff --git a/roms/u-boot/arch/x86/lib/e820.c b/roms/u-boot/arch/x86/lib/e820.c new file mode 100644 index 000000000..12fcff123 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/e820.c @@ -0,0 +1,85 @@ +// 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> +#include <asm/global_data.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, 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 { + efi_add_memory_map(start, e820[i].size, type); + } + } +} +#endif /* CONFIG_IS_ENABLED(EFI_LOADER) */ diff --git a/roms/u-boot/arch/x86/lib/early_cmos.c b/roms/u-boot/arch/x86/lib/early_cmos.c new file mode 100644 index 000000000..f7b3bb2a8 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/early_cmos.c @@ -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; +} diff --git a/roms/u-boot/arch/x86/lib/elf_ia32_efi.lds b/roms/u-boot/arch/x86/lib/elf_ia32_efi.lds new file mode 100644 index 000000000..aad61e7f8 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/elf_ia32_efi.lds @@ -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) } +} diff --git a/roms/u-boot/arch/x86/lib/elf_x86_64_efi.lds b/roms/u-boot/arch/x86/lib/elf_x86_64_efi.lds new file mode 100644 index 000000000..b436429b3 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/elf_x86_64_efi.lds @@ -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) } +} diff --git a/roms/u-boot/arch/x86/lib/fsp/Makefile b/roms/u-boot/arch/x86/lib/fsp/Makefile new file mode 100644 index 000000000..da6c0a886 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/fsp/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2019 Google LLC + +obj-y += fsp_common.o +obj-y += fsp_dram.o +ifndef CONFIG_SPL_BUILD +obj-$(CONFIG_VIDEO_FSP) += fsp_graphics.o +endif +obj-y += fsp_support.o diff --git a/roms/u-boot/arch/x86/lib/fsp/fsp_common.c b/roms/u-boot/arch/x86/lib/fsp/fsp_common.c new file mode 100644 index 000000000..6365b0a50 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/fsp/fsp_common.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <cpu_func.h> +#include <dm.h> +#include <errno.h> +#include <init.h> +#include <log.h> +#include <rtc.h> +#include <acpi/acpi_s3.h> +#include <asm/cmos_layout.h> +#include <asm/early_cmos.h> +#include <asm/global_data.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_init(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 board_final_cleanup(void) +{ + u32 status; + + /* TODO(sjg@chromium.org): This causes Linux to crash */ + return; + + /* call into FspNotify */ + debug("Calling into FSP (notify phase INIT_PHASE_END_FIRMWARE): "); + status = fsp_notify(NULL, INIT_PHASE_END_FIRMWARE); + if (status) + debug("fail, error code %x\n", status); + else + debug("OK\n"); +} + +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; +} diff --git a/roms/u-boot/arch/x86/lib/fsp/fsp_dram.c b/roms/u-boot/arch/x86/lib/fsp/fsp_dram.c new file mode 100644 index 000000000..8ad9aeeda --- /dev/null +++ b/roms/u-boot/arch/x86/lib/fsp/fsp_dram.c @@ -0,0 +1,174 @@ +// 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 <log.h> +#include <asm/fsp/fsp_support.h> +#include <asm/e820.h> +#include <asm/global_data.h> +#include <asm/mrccache.h> +#include <asm/mtrr.h> +#include <asm/post.h> +#include <dm/ofnode.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) +{ + efi_guid_t fsp = FSP_HOB_RESOURCE_OWNER_FSP_GUID; + const struct hob_header *hdr; + struct hob_res_desc *res_desc; + phys_addr_t mtrr_top; + phys_addr_t low_end; + uint bank; + + if (!ll_boot_init()) { + gd->bd->bi_dram[0].start = 0; + gd->bd->bi_dram[0].size = gd->ram_size; + + mtrr_add_request(MTRR_TYPE_WRBACK, 0, gd->ram_size); + return 0; + } + + low_end = 0; /* top of low memory usable by U-Boot */ + mtrr_top = 0; /* top of low memory (even if reserved) */ + for (bank = 1, hdr = gd->arch.hob_list; + bank < CONFIG_NR_DRAM_BANKS && !end_of_hob(hdr); + hdr = get_next_hob(hdr)) { + if (hdr->type != HOB_TYPE_RES_DESC) + continue; + res_desc = (struct hob_res_desc *)hdr; + if (!guidcmp(&res_desc->owner, &fsp)) + low_end = res_desc->phys_start; + if (res_desc->type != RES_SYS_MEM && + res_desc->type != RES_MEM_RESERVED) + continue; + if (res_desc->phys_start < (1ULL << 32)) { + mtrr_top = max(mtrr_top, + res_desc->phys_start + res_desc->len); + } else { + gd->bd->bi_dram[bank].start = res_desc->phys_start; + gd->bd->bi_dram[bank].size = res_desc->len; + mtrr_add_request(MTRR_TYPE_WRBACK, res_desc->phys_start, + res_desc->len); + log_debug("ram %llx %llx\n", + gd->bd->bi_dram[bank].start, + gd->bd->bi_dram[bank].size); + } + } + + /* Add the memory below 4GB */ + gd->bd->bi_dram[0].start = 0; + gd->bd->bi_dram[0].size = low_end; + + /* + * Set up an MTRR to the top of low, reserved memory. This is necessary + * for graphics to run at full speed in U-Boot. + */ + mtrr_add_request(MTRR_TYPE_WRBACK, 0, mtrr_top); + + 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; + const fdt64_t *prop; + int size; + + 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++; + + if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) { + ulong stack_size; + + stack_size = CONFIG_IS_ENABLED(HAVE_ACPI_RESUME, + (CONFIG_STACK_SIZE_RESUME), (0)); + /* + * 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 - stack_size; + entries[num_entries].size = gd->ram_top - gd->start_addr_sp + + stack_size; + entries[num_entries].type = E820_RESERVED; + num_entries++; + } + + prop = ofnode_read_chosen_prop("e820-entries", &size); + if (prop) { + int count = size / (sizeof(u64) * 3); + int i; + + if (num_entries + count >= max_entries) + return -ENOSPC; + for (i = 0; i < count; i++, num_entries++, prop += 3) { + entries[num_entries].addr = fdt64_to_cpu(prop[0]); + entries[num_entries].size = fdt64_to_cpu(prop[1]); + entries[num_entries].type = fdt64_to_cpu(prop[2]); + } + } + + 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 = gd->bd->bi_dram[0].size; + ho->arch.hob_list = gd->arch.hob_list; + + return 0; +} +#endif diff --git a/roms/u-boot/arch/x86/lib/fsp/fsp_graphics.c b/roms/u-boot/arch/x86/lib/fsp/fsp_graphics.c new file mode 100644 index 000000000..02fd05c9f --- /dev/null +++ b/roms/u-boot/arch/x86/lib/fsp/fsp_graphics.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com> + */ + +#define LOG_CATEGORY UCLASS_VIDEO + +#include <common.h> +#include <dm.h> +#include <init.h> +#include <log.h> +#include <vbe.h> +#include <video.h> +#include <acpi/acpi_table.h> +#include <asm/fsp/fsp_support.h> +#include <asm/global_data.h> +#include <asm/intel_opregion.h> +#include <asm/mtrr.h> +#include <dm/acpi.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_plat *plat = dev_get_uclass_plat(dev); + struct video_priv *uc_priv = dev_get_uclass_priv(dev); + struct vesa_mode_info *vesa = &mode_info.vesa; + int ret; + + if (!ll_boot_init()) + return -ENODEV; + + 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); + gd->fb_base = vesa->phys_base_ptr; + + ret = vbe_setup_video_priv(vesa, uc_priv, plat); + if (ret) + goto err; + + mtrr_add_request(MTRR_TYPE_WRCOMB, vesa->phys_base_ptr, 256 << 20); + mtrr_commit(true); + + printf("%dx%dx%d @ %x\n", uc_priv->xsize, uc_priv->ysize, + vesa->bits_per_pixel, vesa->phys_base_ptr); + + return 0; + +err: + printf("No video mode configured in FSP!\n"); + return ret; +} + +static int fsp_video_bind(struct udevice *dev) +{ + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + + /* Set the maximum supported resolution */ + plat->size = 2560 * 1600 * 4; + + return 0; +} + +#ifdef CONFIG_INTEL_GMA_ACPI +static int fsp_video_acpi_write_tables(const struct udevice *dev, + struct acpi_ctx *ctx) +{ + struct igd_opregion *opregion; + int ret; + + log_debug("ACPI: * IGD OpRegion\n"); + opregion = (struct igd_opregion *)ctx->current; + + ret = intel_gma_init_igd_opregion((struct udevice *)dev, opregion); + if (ret) + return ret; + + acpi_inc_align(ctx, sizeof(struct igd_opregion)); + + return 0; +} +#endif + +struct acpi_ops fsp_video_acpi_ops = { +#ifdef CONFIG_INTEL_GMA_ACPI + .write_tables = fsp_video_acpi_write_tables, +#endif +}; + +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, + .bind = fsp_video_bind, + .probe = fsp_video_probe, + .flags = DM_FLAG_PRE_RELOC, + ACPI_OPS_PTR(&fsp_video_acpi_ops) +}; + +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); diff --git a/roms/u-boot/arch/x86/lib/fsp/fsp_support.c b/roms/u-boot/arch/x86/lib/fsp/fsp_support.c new file mode 100644 index 000000000..fd4d98ef6 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/fsp/fsp_support.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: Intel +/* + * Copyright (C) 2013, Intel Corporation + * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <log.h> +#include <asm/fsp/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); +} diff --git a/roms/u-boot/arch/x86/lib/fsp1/Makefile b/roms/u-boot/arch/x86/lib/fsp1/Makefile new file mode 100644 index 000000000..1cf5e5419 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/fsp1/Makefile @@ -0,0 +1,8 @@ +# 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-y += fsp_support.o diff --git a/roms/u-boot/arch/x86/lib/fsp1/fsp_car.S b/roms/u-boot/arch/x86/lib/fsp1/fsp_car.S new file mode 100644 index 000000000..a64a65343 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/fsp1/fsp_car.S @@ -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 */ diff --git a/roms/u-boot/arch/x86/lib/fsp1/fsp_common.c b/roms/u-boot/arch/x86/lib/fsp1/fsp_common.c new file mode 100644 index 000000000..209261718 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/fsp1/fsp_common.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <init.h> +#include <log.h> +#include <malloc.h> +#include <rtc.h> +#include <acpi/acpi_s3.h> +#include <asm/cmos_layout.h> +#include <asm/early_cmos.h> +#include <asm/global_data.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; + +static void *fsp_prepare_mrc_cache(void) +{ + struct mrc_data_container *cache; + struct mrc_region entry; + int ret; + + ret = mrccache_get_region(MRC_TYPE_NORMAL, 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; +} + +int arch_fsp_init(void) +{ + void *nvs; + int stack = CONFIG_FSP_TEMP_RAM_ADDR; + int boot_mode = BOOT_FULL_CONFIG; + int prev_sleep_state; + + if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) { + prev_sleep_state = chipset_prev_sleep_state(); + gd->arch.prev_sleep_state = prev_sleep_state; + } + + if (!gd->arch.hob_list) { + if (IS_ENABLED(CONFIG_ENABLE_MRC_CACHE)) + nvs = fsp_prepare_mrc_cache(); + else + nvs = NULL; + + if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) && + 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; + } + + /* + * 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; +} diff --git a/roms/u-boot/arch/x86/lib/fsp1/fsp_dram.c b/roms/u-boot/arch/x86/lib/fsp1/fsp_dram.c new file mode 100644 index 000000000..cfd9b9f48 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/fsp1/fsp_dram.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <init.h> +#include <asm/fsp/fsp_support.h> +#include <asm/global_data.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)) { + struct mrc_output *mrc = &gd->arch.mrc[MRC_TYPE_NORMAL]; + + mrc->buf = fsp_get_nvs_data(gd->arch.hob_list, &mrc->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); +} diff --git a/roms/u-boot/arch/x86/lib/fsp1/fsp_support.c b/roms/u-boot/arch/x86/lib/fsp1/fsp_support.c new file mode 100644 index 000000000..d84c632f1 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/fsp1/fsp_support.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: Intel +/* + * Copyright (C) 2013, Intel Corporation + * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <log.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(¶ms, 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 = ¶ms; + + 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(¶ms); +} + +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 = ¶ms; + + /* + * 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; +} diff --git a/roms/u-boot/arch/x86/lib/fsp2/Makefile b/roms/u-boot/arch/x86/lib/fsp2/Makefile new file mode 100644 index 000000000..ddbe2d0db --- /dev/null +++ b/roms/u-boot/arch/x86/lib/fsp2/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2019 Google LLC + +obj-y += fsp_common.o +obj-y += fsp_dram.o +obj-y += fsp_init.o +obj-y += fsp_meminit.o +obj-y += fsp_silicon_init.o +obj-y += fsp_support.o diff --git a/roms/u-boot/arch/x86/lib/fsp2/fsp_common.c b/roms/u-boot/arch/x86/lib/fsp2/fsp_common.c new file mode 100644 index 000000000..f69456e43 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/fsp2/fsp_common.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <init.h> + +int arch_fsp_init(void) +{ + return 0; +} diff --git a/roms/u-boot/arch/x86/lib/fsp2/fsp_dram.c b/roms/u-boot/arch/x86/lib/fsp2/fsp_dram.c new file mode 100644 index 000000000..42d3892b7 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/fsp2/fsp_dram.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#define LOG_CATEGORY LOGC_ARCH + +#include <common.h> +#include <handoff.h> +#include <init.h> +#include <log.h> +#include <spl.h> +#include <acpi/acpi_s3.h> +#include <asm/arch/cpu.h> +#include <asm/fsp/fsp_support.h> +#include <asm/fsp2/fsp_api.h> +#include <asm/fsp2/fsp_internal.h> +#include <asm/global_data.h> +#include <linux/sizes.h> + +int dram_init(void) +{ + int ret; + + if (!ll_boot_init()) { + /* Use a small and safe amount of 1GB */ + gd->ram_size = SZ_1G; + + return 0; + } + if (spl_phase() == PHASE_SPL) { + bool s3wake = false; + + s3wake = IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) && + gd->arch.prev_sleep_state == ACPI_S3; + + ret = fsp_memory_init(s3wake, + IS_ENABLED(CONFIG_APL_BOOT_FROM_FAST_SPI_FLASH)); + if (ret) { + log_debug("Memory init failed (err=%x)\n", ret); + return ret; + } + + /* The FSP has already set up DRAM, so grab the info we need */ + ret = fsp_scan_for_ram_size(); + if (ret) + return ret; + +#ifdef CONFIG_ENABLE_MRC_CACHE + gd->arch.mrc[MRC_TYPE_NORMAL].buf = + fsp_get_nvs_data(gd->arch.hob_list, + &gd->arch.mrc[MRC_TYPE_NORMAL].len); + gd->arch.mrc[MRC_TYPE_VAR].buf = + fsp_get_var_nvs_data(gd->arch.hob_list, + &gd->arch.mrc[MRC_TYPE_VAR].len); + log_debug("normal %x, var %x\n", + gd->arch.mrc[MRC_TYPE_NORMAL].len, + gd->arch.mrc[MRC_TYPE_VAR].len); +#endif + } else { +#if CONFIG_IS_ENABLED(HANDOFF) + struct spl_handoff *ho = gd->spl_handoff; + + if (!ho) { + log_debug("No SPL handoff found\n"); + return -ESTRPIPE; + } + gd->ram_size = ho->ram_size; + handoff_load_dram_banks(ho); +#endif + ret = arch_fsps_preinit(); + if (ret) + return log_msg_ret("fsp_s_preinit", ret); + } + + return 0; +} + +ulong board_get_usable_ram_top(ulong total_size) +{ + if (!ll_boot_init()) + return gd->ram_size; + +#if CONFIG_IS_ENABLED(HANDOFF) + struct spl_handoff *ho = gd->spl_handoff; + + log_debug("usable_ram_top = %lx\n", ho->arch.usable_ram_top); + + return ho->arch.usable_ram_top; +#endif + + return gd->ram_top; +} diff --git a/roms/u-boot/arch/x86/lib/fsp2/fsp_init.c b/roms/u-boot/arch/x86/lib/fsp2/fsp_init.c new file mode 100644 index 000000000..5afdce1e0 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/fsp2/fsp_init.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Google LLC + */ + +#include <common.h> +#include <binman.h> +#include <binman_sym.h> +#include <bootstage.h> +#include <cbfs.h> +#include <dm.h> +#include <init.h> +#include <log.h> +#include <spi.h> +#include <spl.h> +#include <spi_flash.h> +#include <asm/intel_pinctrl.h> +#include <dm/uclass-internal.h> +#include <asm/fsp2/fsp_internal.h> + +int arch_cpu_init_dm(void) +{ + struct udevice *dev; + ofnode node; + int ret; + + /* Make sure pads are set up early in U-Boot */ + if (!ll_boot_init() || spl_phase() != PHASE_BOARD_F) + return 0; + + /* Probe all pinctrl devices to set up the pads */ + ret = uclass_first_device_err(UCLASS_PINCTRL, &dev); + if (ret) + return log_msg_ret("no fsp pinctrl", ret); + node = ofnode_path("fsp"); + if (!ofnode_valid(node)) + return log_msg_ret("no fsp params", -EINVAL); + ret = pinctrl_config_pads_for_node(dev, node); + if (ret) + return log_msg_ret("pad config", ret); + + return ret; +} + +#if !defined(CONFIG_TPL_BUILD) +binman_sym_declare(ulong, intel_fsp_m, image_pos); +binman_sym_declare(ulong, intel_fsp_m, size); + +/** + * get_cbfs_fsp() - Obtain the FSP by looking up in CBFS + * + * This looks up an FSP in a CBFS. It is used mostly for testing, when booting + * U-Boot from a hybrid image containing coreboot as the first-stage bootloader. + * + * The typical use for this feature is when building a Chrome OS image which + * includes coreboot in it. By adding U-Boot into the 'COREBOOT' CBFS as well, + * it is possible to make coreboot chain-load U-Boot. Thus the initial stages of + * the SoC init can be done by coreboot and the later stages by U-Boot. This is + * a convenient way to start the porting work. The jump to U-Boot can then be + * moved progressively earlier and earlier, until U-Boot takes over all the init + * and you have a native port. + * + * This function looks up a CBFS at a known location and reads the FSP-M from it + * so that U-Boot can init the memory. + * + * This function is not used in the normal boot but is kept here for future + * development. + * + * @type; Type to look up (only FSP_M supported at present) + * @map_base: Base memory address for mapped SPI + * @entry: Returns an entry containing the position of the FSP image + */ +static int get_cbfs_fsp(enum fsp_type_t type, ulong map_base, + struct binman_entry *entry) +{ + /* + * Use a hard-coded position of CBFS in the ROM for now. It would be + * possible to read the position using the FMAP in the ROM, but since + * this code is only used for development, it doesn't seem worth it. + * Use the 'cbfstool <image> layout' command to get these values, e.g.: + * 'COREBOOT' (CBFS, size 1814528, offset 2117632). + */ + ulong cbfs_base = 0x205000; + struct cbfs_priv *cbfs; + int ret; + + ret = cbfs_init_mem(map_base + cbfs_base, CBFS_SIZE_UNKNOWN, true, + &cbfs); + if (ret) + return ret; + if (!ret) { + const struct cbfs_cachenode *node; + + node = cbfs_find_file(cbfs, "fspm.bin"); + if (!node) + return log_msg_ret("fspm node", -ENOENT); + + entry->image_pos = (ulong)node->data; + entry->size = node->data_length; + } + + return 0; +} + +int fsp_locate_fsp(enum fsp_type_t type, struct binman_entry *entry, + bool use_spi_flash, struct udevice **devp, + struct fsp_header **hdrp, ulong *rom_offsetp) +{ + ulong mask = CONFIG_ROM_SIZE - 1; + struct udevice *dev; + ulong rom_offset = 0; + uint map_size; + ulong map_base; + uint offset; + int ret; + + /* + * Find the devices but don't probe them, since we don't want to + * auto-config PCI before silicon init runs + */ + ret = uclass_find_first_device(UCLASS_NORTHBRIDGE, &dev); + if (ret) + return log_msg_ret("Cannot get northbridge", ret); + if (!use_spi_flash) { + struct udevice *sf; + + /* Just use the SPI driver to get the memory map */ + ret = uclass_find_first_device(UCLASS_SPI_FLASH, &sf); + if (ret) + return log_msg_ret("Cannot get SPI flash", ret); + ret = dm_spi_get_mmap(sf, &map_base, &map_size, &offset); + if (ret) + return log_msg_ret("Could not get flash mmap", ret); + } + + if (spl_phase() >= PHASE_BOARD_F) { + if (type != FSP_S) + return -EPROTONOSUPPORT; + ret = binman_entry_find("intel-fsp-s", entry); + if (ret) + return log_msg_ret("binman entry", ret); + if (!use_spi_flash) + rom_offset = (map_base & mask) - CONFIG_ROM_SIZE; + } else { + ret = -ENOENT; + if (false) + /* + * Support using a hybrid image build by coreboot. See + * the function comments for details + */ + ret = get_cbfs_fsp(type, map_base, entry); + if (ret) { + ulong mask = CONFIG_ROM_SIZE - 1; + + if (type != FSP_M) + return -EPROTONOSUPPORT; + entry->image_pos = binman_sym(ulong, intel_fsp_m, + image_pos); + entry->size = binman_sym(ulong, intel_fsp_m, size); + if (entry->image_pos != BINMAN_SYM_MISSING) { + ret = 0; + if (use_spi_flash) + entry->image_pos &= mask; + else + entry->image_pos += (map_base & mask); + } else { + ret = -ENOENT; + } + } + } + if (ret) + return log_msg_ret("Cannot find FSP", ret); + entry->image_pos += rom_offset; + + /* + * Account for the time taken to read memory-mapped SPI flash since in + * this case we don't use the SPI driver and BOOTSTAGE_ID_ACCUM_SPI. + */ + if (!use_spi_flash) + bootstage_start(BOOTSTAGE_ID_ACCUM_MMAP_SPI, "mmap_spi"); + ret = fsp_get_header(entry->image_pos, entry->size, use_spi_flash, + hdrp); + if (!use_spi_flash) + bootstage_accum(BOOTSTAGE_ID_ACCUM_MMAP_SPI); + if (ret) + return log_msg_ret("fsp_get_header", ret); + *devp = dev; + if (rom_offsetp) + *rom_offsetp = rom_offset; + + return 0; +} +#endif diff --git a/roms/u-boot/arch/x86/lib/fsp2/fsp_meminit.c b/roms/u-boot/arch/x86/lib/fsp2/fsp_meminit.c new file mode 100644 index 000000000..022e2cb64 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/fsp2/fsp_meminit.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: Intel +/* + * Copyright (C) 2015-2016 Intel Corp. + * (Written by Andrey Petrov <andrey.petrov@intel.com> for Intel Corp.) + * (Written by Alexandru Gagniuc <alexandrux.gagniuc@intel.com> for Intel Corp.) + * Mostly taken from coreboot fsp2_0/memory_init.c + */ + +#include <common.h> +#include <binman.h> +#include <bootstage.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/mrccache.h> +#include <asm/fsp/fsp_infoheader.h> +#include <asm/fsp2/fsp_api.h> +#include <asm/fsp2/fsp_internal.h> +#include <asm/arch/fsp/fsp_configs.h> +#include <asm/arch/fsp/fsp_m_upd.h> + +static int prepare_mrc_cache_type(enum mrc_type_t type, + struct mrc_data_container **cachep) +{ + struct mrc_data_container *cache; + struct mrc_region entry; + int ret; + + ret = mrccache_get_region(type, NULL, &entry); + if (ret) + return ret; + cache = mrccache_find_current(&entry); + if (!cache) + return -ENOENT; + + log_debug("MRC at %x, size %x\n", (uint)cache->data, cache->data_size); + *cachep = cache; + + return 0; +} + +int prepare_mrc_cache(struct fspm_upd *upd) +{ + struct mrc_data_container *cache; + int ret; + + ret = prepare_mrc_cache_type(MRC_TYPE_NORMAL, &cache); + if (ret) + return log_msg_ret("Cannot get normal cache", ret); + upd->arch.nvs_buffer_ptr = cache->data; + + ret = prepare_mrc_cache_type(MRC_TYPE_VAR, &cache); + if (ret) + return log_msg_ret("Cannot get var cache", ret); + upd->config.variable_nvs_buffer_ptr = cache->data; + + return 0; +} + +int fsp_memory_init(bool s3wake, bool use_spi_flash) +{ + struct fspm_upd upd, *fsp_upd; + fsp_memory_init_func func; + struct binman_entry entry; + struct fsp_header *hdr; + struct hob_header *hob; + struct udevice *dev; + int delay; + int ret; + + log_debug("Locating FSP\n"); + ret = fsp_locate_fsp(FSP_M, &entry, use_spi_flash, &dev, &hdr, NULL); + if (ret) + return log_msg_ret("locate FSP", ret); + debug("Found FSP_M at %x, size %x\n", hdr->img_base, hdr->img_size); + + /* Copy over the default config */ + fsp_upd = (struct fspm_upd *)(hdr->img_base + hdr->cfg_region_off); + if (fsp_upd->header.signature != FSPM_UPD_SIGNATURE) + return log_msg_ret("Bad UPD signature", -EPERM); + memcpy(&upd, fsp_upd, sizeof(upd)); + + delay = dev_read_u32_default(dev, "fspm,training-delay", 0); + ret = fspm_update_config(dev, &upd); + if (ret) { + if (ret != -ENOENT) + return log_msg_ret("Could not setup config", ret); + } else { + delay = 0; + } + + if (delay) + printf("SDRAM training (%d seconds)...", delay); + else + log_debug("SDRAM init..."); + bootstage_start(BOOTSTAGE_ID_ACCUM_FSP_M, "fsp-m"); + func = (fsp_memory_init_func)(hdr->img_base + hdr->fsp_mem_init); + ret = func(&upd, &hob); + bootstage_accum(BOOTSTAGE_ID_ACCUM_FSP_M); + cpu_reinit_fpu(); + if (delay) + printf("done\n"); + else + log_debug("done\n"); + if (ret) + return log_msg_ret("SDRAM init fail\n", ret); + + gd->arch.hob_list = hob; + + ret = fspm_done(dev); + if (ret) + return log_msg_ret("fsm_done\n", ret); + + return 0; +} diff --git a/roms/u-boot/arch/x86/lib/fsp2/fsp_silicon_init.c b/roms/u-boot/arch/x86/lib/fsp2/fsp_silicon_init.c new file mode 100644 index 000000000..a96d2b183 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/fsp2/fsp_silicon_init.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Intel +/* + * Copyright (C) 2015-2016 Intel Corp. + * (Written by Andrey Petrov <andrey.petrov@intel.com> for Intel Corp.) + * + * Mostly taken from coreboot fsp2_0/silicon_init.c + */ + +#define LOG_CATEGORY UCLASS_NORTHBRIDGE + +#include <common.h> +#include <binman.h> +#include <bootstage.h> +#include <dm.h> +#include <log.h> +#include <asm/arch/fsp/fsp_configs.h> +#include <asm/arch/fsp/fsp_s_upd.h> +#include <asm/fsp/fsp_infoheader.h> +#include <asm/fsp2/fsp_internal.h> +#include <asm/global_data.h> + +int fsp_silicon_init(bool s3wake, bool use_spi_flash) +{ + struct fsps_upd upd, *fsp_upd; + fsp_silicon_init_func func; + struct fsp_header *hdr; + struct binman_entry entry; + struct udevice *dev; + ulong rom_offset = 0; + u32 init_addr; + int ret; + + log_debug("Locating FSP\n"); + ret = fsp_locate_fsp(FSP_S, &entry, use_spi_flash, &dev, &hdr, + &rom_offset); + if (ret) + return log_msg_ret("locate FSP", ret); + binman_set_rom_offset(rom_offset); + gd->arch.fsp_s_hdr = hdr; + + /* Copy over the default config */ + fsp_upd = (struct fsps_upd *)(hdr->img_base + hdr->cfg_region_off); + if (fsp_upd->header.signature != FSPS_UPD_SIGNATURE) + return log_msg_ret("Bad UPD signature", -EPERM); + memcpy(&upd, fsp_upd, sizeof(upd)); + + ret = fsps_update_config(dev, rom_offset, &upd); + if (ret) + return log_msg_ret("Could not setup config", ret); + log_debug("Silicon init @ %x...", init_addr); + bootstage_start(BOOTSTAGE_ID_ACCUM_FSP_S, "fsp-s"); + func = (fsp_silicon_init_func)(hdr->img_base + hdr->fsp_silicon_init); + ret = func(&upd); + bootstage_accum(BOOTSTAGE_ID_ACCUM_FSP_S); + if (ret) + return log_msg_ret("Silicon init fail\n", ret); + log_debug("done\n"); + + return 0; +} diff --git a/roms/u-boot/arch/x86/lib/fsp2/fsp_support.c b/roms/u-boot/arch/x86/lib/fsp2/fsp_support.c new file mode 100644 index 000000000..b2c765824 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/fsp2/fsp_support.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: Intel +/* + * Copyright 2019 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <init.h> +#include <log.h> +#include <spi_flash.h> +#include <asm/fsp/fsp_support.h> +#include <asm/fsp2/fsp_internal.h> +#include <asm/global_data.h> + +/* The amount of the FSP header to probe to obtain what we need */ +#define PROBE_BUF_SIZE 0x180 + +int fsp_get_header(ulong offset, ulong size, bool use_spi_flash, + struct fsp_header **fspp) +{ + static efi_guid_t guid = FSP_HEADER_GUID; + struct fv_ext_header *exhdr; + struct fsp_header *fsp; + struct ffs_file_header *file_hdr; + struct fv_header *fv; + struct raw_section *raw; + void *ptr, *base; + u8 buf[PROBE_BUF_SIZE]; + struct udevice *dev; + int ret; + + /* + * There are quite a very steps to work through all the headers in this + * file and the structs have similar names. Turn on debugging if needed + * to understand what is going wrong. + * + * You are in a maze of twisty little headers all alike. + */ + log_debug("offset=%x buf=%x, use_spi_flash=%d\n", (uint)offset, + (uint)buf, use_spi_flash); + if (use_spi_flash) { + ret = uclass_first_device_err(UCLASS_SPI_FLASH, &dev); + if (ret) + return log_msg_ret("Cannot find flash device", ret); + ret = spi_flash_read_dm(dev, offset, PROBE_BUF_SIZE, buf); + if (ret) + return log_msg_ret("Cannot read flash", ret); + } else { + memcpy(buf, (void *)offset, PROBE_BUF_SIZE); + } + + /* Initalise the FSP base */ + ptr = buf; + fv = ptr; + + /* Check the FV signature, _FVH */ + log_debug("offset=%x sign=%x\n", (uint)offset, (uint)fv->sign); + if (fv->sign != EFI_FVH_SIGNATURE) + return log_msg_ret("Base FV signature", -EINVAL); + + /* Go to the end of the FV header and align the address */ + log_debug("fv->ext_hdr_off = %x\n", fv->ext_hdr_off); + ptr += fv->ext_hdr_off; + exhdr = ptr; + ptr += ALIGN(exhdr->ext_hdr_size, 8); + log_debug("ptr=%x\n", ptr - (void *)buf); + + /* Check the FFS GUID */ + file_hdr = ptr; + if (memcmp(&file_hdr->name, &guid, sizeof(guid))) + return log_msg_ret("Base FFS GUID", -ENXIO); + /* Add the FFS header size to find the raw section header */ + ptr = file_hdr + 1; + + raw = ptr; + log_debug("raw->type = %x\n", raw->type); + if (raw->type != EFI_SECTION_RAW) + return log_msg_ret("Section type not RAW", -ENOEXEC); + + /* Add the raw section header size to find the FSP header */ + ptr = raw + 1; + fsp = ptr; + + /* Check the FSPH header */ + log_debug("fsp %x, fsp-buf=%x, si=%x\n", (uint)fsp, ptr - (void *)buf, + (void *)&fsp->fsp_silicon_init - (void *)buf); + if (fsp->sign != EFI_FSPH_SIGNATURE) + return log_msg_ret("Base FSPH signature", -EACCES); + + base = (void *)fsp->img_base; + log_debug("image base %x\n", (uint)base); + if (fsp->fsp_mem_init) + log_debug("mem_init offset %x\n", (uint)fsp->fsp_mem_init); + else if (fsp->fsp_silicon_init) + log_debug("silicon_init offset %x\n", + (uint)fsp->fsp_silicon_init); + if (use_spi_flash) { + ret = spi_flash_read_dm(dev, offset, size, base); + if (ret) + return log_msg_ret("Could not read FPS-M", ret); + } else { + memcpy(base, (void *)offset, size); + } + ptr = base + (ptr - (void *)buf); + *fspp = ptr; + + return 0; +} + +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 (!ll_boot_init()) + return 0; + + if (!fsp_hdr) + fsp_hdr = gd->arch.fsp_s_hdr; + + if (!fsp_hdr) + return log_msg_ret("no FSP", -ENOENT); + + notify = (fsp_notify_f)(fsp_hdr->img_base + fsp_hdr->fsp_notify); + params.phase = phase; + params_ptr = ¶ms; + + /* + * 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; +} diff --git a/roms/u-boot/arch/x86/lib/hob.c b/roms/u-boot/arch/x86/lib/hob.c new file mode 100644 index 000000000..f2c47240e --- /dev/null +++ b/roms/u-boot/arch/x86/lib/hob.c @@ -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); +} diff --git a/roms/u-boot/arch/x86/lib/i8254.c b/roms/u-boot/arch/x86/lib/i8254.c new file mode 100644 index 000000000..0f9753891 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/i8254.c @@ -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); +} diff --git a/roms/u-boot/arch/x86/lib/i8259.c b/roms/u-boot/arch/x86/lib/i8259.c new file mode 100644 index 000000000..a0e3c0925 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/i8259.c @@ -0,0 +1,129 @@ +// 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 <log.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); +} diff --git a/roms/u-boot/arch/x86/lib/init_helpers.c b/roms/u-boot/arch/x86/lib/init_helpers.c new file mode 100644 index 000000000..67401b9ba --- /dev/null +++ b/roms/u-boot/arch/x86/lib/init_helpers.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2011 + * Graeme Russ, <graeme.russ@gmail.com> + */ + +#include <common.h> +#include <init.h> +#include <asm/global_data.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; + + if (!ll_boot_init()) + return 0; + + 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(); +} diff --git a/roms/u-boot/arch/x86/lib/interrupts.c b/roms/u-boot/arch/x86/lib/interrupts.c new file mode 100644 index 000000000..ff52959ed --- /dev/null +++ b/roms/u-boot/arch/x86/lib/interrupts.c @@ -0,0 +1,158 @@ +// 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 <command.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(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ +#if !CONFIG_IS_ENABLED(X86_64) + struct idt_ptr ptr; + int irq; + + interrupt_read_idt(&ptr); + printf("IDT at %lx, size %x\n", ptr.address, ptr.size); + 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 diff --git a/roms/u-boot/arch/x86/lib/lpc-uclass.c b/roms/u-boot/arch/x86/lib/lpc-uclass.c new file mode 100644 index 000000000..1302a6e34 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/lpc-uclass.c @@ -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 +}; diff --git a/roms/u-boot/arch/x86/lib/mpspec.c b/roms/u-boot/arch/x86/lib/mpspec.c new file mode 100644 index 000000000..8e97d9ff3 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/mpspec.c @@ -0,0 +1,410 @@ +// 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 <log.h> +#include <asm/cpu.h> +#include <asm/global_data.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_plat *plat = dev_get_parent_plat(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; +} diff --git a/roms/u-boot/arch/x86/lib/mrccache.c b/roms/u-boot/arch/x86/lib/mrccache.c new file mode 100644 index 000000000..b52691568 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/mrccache.c @@ -0,0 +1,357 @@ +// 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 <log.h> +#include <malloc.h> +#include <net.h> +#include <spi.h> +#include <spi_flash.h> +#include <asm/global_data.h> +#include <asm/mrccache.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.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 + * + * This moves to the next cache entry in the region, making sure it has enough + * space to hold data of size @data_size. + * + * @entry: MRC cache flash area + * @cache: Entry to start from + * @data_size: Required data size of the new entry. Note that we assume that + * all cache entries are the same size + * + * @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 *prev, int data_size) +{ + struct mrc_data_container *cache; + ulong base_addr, end_addr; + + base_addr = entry->base + entry->offset; + end_addr = base_addr + entry->length; + + /* + * We assume that all cache entries are the same size, but let's use + * data_size here for clarity. + */ + cache = next_mrc_block(prev); + if ((ulong)cache + mrc_block_size(data_size) > 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; +} + +/** + * mrccache_update() - update the MRC cache with a new record + * + * This writes a new record to the end of the MRC cache region. If the new + * record is the same as the latest record then the write is skipped + * + * @sf: SPI flash to write to + * @entry: Position and size of MRC cache in SPI flash + * @cur: Record to write + * @return 0 if updated, -EEXIST if the record is the same as the latest + * record, -EINVAL if the record is not valid, other error if SPI write failed + */ +static 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, cur->data_size); + + /* + * 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 log_msg_ret("Cannot update mrccache", ret); + } + + return 0; +} + +static void mrccache_setup(struct mrc_output *mrc, void *data) +{ + struct mrc_data_container *cache = data; + u16 checksum; + + cache->signature = MRC_DATA_SIGNATURE; + cache->data_size = mrc->len; + checksum = compute_ip_checksum(mrc->buf, 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, mrc->buf, cache->data_size); + + mrc->cache = cache; +} + +int mrccache_reserve(void) +{ + int i; + + for (i = 0; i < MRC_TYPE_COUNT; i++) { + struct mrc_output *mrc = &gd->arch.mrc[i]; + + if (!mrc->len) + continue; + + /* adjust stack pointer to store pure cache data plus header */ + gd->start_addr_sp -= (mrc->len + MRC_DATA_HEADER_SIZE); + mrccache_setup(mrc, (void *)gd->start_addr_sp); + + gd->start_addr_sp &= ~0xf; + } + + return 0; +} + +int mrccache_get_region(enum mrc_type_t type, struct udevice **devp, + struct mrc_region *entry) +{ + struct udevice *dev; + ofnode mrc_node; + ulong map_base; + uint map_size; + uint offset; + ofnode node; + u32 reg[2]; + int ret; + + /* + * Find the flash chip within the SPI controller node. Avoid probing + * the device here since it may put it into a strange state where the + * memory map cannot be read. + */ + ret = uclass_find_first_device(UCLASS_SPI_FLASH, &dev); + if (ret || !dev) { + /* + * Fall back to searching the device tree since driver model + * may not be ready yet (e.g. with FSPv1) + */ + node = ofnode_by_compatible(ofnode_null(), "jedec,spi-nor"); + if (!ofnode_valid(node)) + return log_msg_ret("Cannot find SPI flash\n", -ENOENT); + ret = -ENODEV; + } else { + ret = dm_spi_get_mmap(dev, &map_base, &map_size, &offset); + if (!ret) + entry->base = map_base; + node = dev_ofnode(dev); + } + + /* + * At this point we have entry->base if ret == 0. If not, then we have + * the node and can look for memory-map + */ + if (ret) { + ret = ofnode_read_u32_array(node, "memory-map", reg, 2); + if (ret) + return log_msg_ret("Cannot find memory map\n", ret); + entry->base = reg[0]; + } + + /* Find the place where we put the MRC cache */ + mrc_node = ofnode_find_subnode(node, type == MRC_TYPE_NORMAL ? + "rw-mrc-cache" : "rw-var-mrc-cache"); + if (!ofnode_valid(mrc_node)) + return log_msg_ret("Cannot find node", -EPERM); + + ret = ofnode_read_u32_array(mrc_node, "reg", reg, 2); + if (ret) + return log_msg_ret("Cannot find address", ret); + entry->offset = reg[0]; + entry->length = reg[1]; + + if (devp) + *devp = dev; + debug("MRC cache type %d in '%s', offset %x, len %x, base %x\n", + type, dev ? dev->name : ofnode_get_name(node), entry->offset, + entry->length, entry->base); + + return 0; +} + +static int mrccache_save_type(enum mrc_type_t type) +{ + struct mrc_data_container *cache; + struct mrc_output *mrc; + struct mrc_region entry; + struct udevice *sf; + int ret; + + mrc = &gd->arch.mrc[type]; + if (!mrc->len) + return 0; + log_debug("Saving %#x bytes of MRC output data type %d to SPI flash\n", + mrc->len, type); + ret = mrccache_get_region(type, &sf, &entry); + if (ret) + return log_msg_ret("Cannot get region", ret); + ret = device_probe(sf); + if (ret) + return log_msg_ret("Cannot probe device", ret); + cache = mrc->cache; + + ret = mrccache_update(sf, &entry, cache); + if (!ret) + debug("Saved MRC data with checksum %04x\n", cache->checksum); + else if (ret == -EEXIST) + debug("MRC data is the same as last time, skipping save\n"); + + return 0; +} + +int mrccache_save(void) +{ + int i; + + for (i = 0; i < MRC_TYPE_COUNT; i++) { + int ret; + + ret = mrccache_save_type(i); + if (ret) + return ret; + } + + return 0; +} + +int mrccache_spl_save(void) +{ + int i; + + for (i = 0; i < MRC_TYPE_COUNT; i++) { + struct mrc_output *mrc = &gd->arch.mrc[i]; + void *data; + int size; + + size = mrc->len + MRC_DATA_HEADER_SIZE; + data = malloc(size); + if (!data) + return log_msg_ret("Allocate MRC cache block", -ENOMEM); + mrccache_setup(mrc, data); + } + + return mrccache_save(); +} diff --git a/roms/u-boot/arch/x86/lib/northbridge-uclass.c b/roms/u-boot/arch/x86/lib/northbridge-uclass.c new file mode 100644 index 000000000..383888724 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/northbridge-uclass.c @@ -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", +}; diff --git a/roms/u-boot/arch/x86/lib/physmem.c b/roms/u-boot/arch/x86/lib/physmem.c new file mode 100644 index 000000000..c11101b44 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/physmem.c @@ -0,0 +1,205 @@ +/* + * 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 <log.h> +#include <physmem.h> +#include <asm/cpu.h> +#include <asm/global_data.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; +} diff --git a/roms/u-boot/arch/x86/lib/pinctrl_ich6.c b/roms/u-boot/arch/x86/lib/pinctrl_ich6.c new file mode 100644 index 000000000..fd5e311b2 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/pinctrl_ich6.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <log.h> +#include <pch.h> +#include <pci.h> +#include <asm/cpu.h> +#include <asm/global_data.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, +}; diff --git a/roms/u-boot/arch/x86/lib/pirq_routing.c b/roms/u-boot/arch/x86/lib/pirq_routing.c new file mode 100644 index 000000000..caeaec928 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/pirq_routing.c @@ -0,0 +1,145 @@ +// 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 <log.h> +#include <pci.h> +#include <asm/global_data.h> +#include <asm/pci.h> +#include <asm/pirq_routing.h> + +DECLARE_GLOBAL_DATA_PTR; + +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; +} + +ulong write_pirq_routing_table(ulong addr) +{ + if (!gd->arch.pirq_routing_table) + return addr; + + return copy_pirq_routing_table(addr, gd->arch.pirq_routing_table); +} diff --git a/roms/u-boot/arch/x86/lib/pmu.c b/roms/u-boot/arch/x86/lib/pmu.c new file mode 100644 index 000000000..083aec8d8 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/pmu.c @@ -0,0 +1,117 @@ +// 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/delay.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(®s->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(®s->sss[offset]); + + /* Modify PMU values */ + if (on) + ssc &= ~(0x3 << shift); /* D0 */ + else + ssc |= 0x3 << shift; /* D3hot */ + + /* Write modified PMU values */ + writel(ssc, ®s->ssc[offset]); + + /* Update modified PMU values */ + writel(0x00002201, ®s->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 = sizeof(struct pmu_mid), +}; diff --git a/roms/u-boot/arch/x86/lib/ramtest.c b/roms/u-boot/arch/x86/lib/ramtest.c new file mode 100644 index 000000000..033853963 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/ramtest.c @@ -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(); +} diff --git a/roms/u-boot/arch/x86/lib/reloc_ia32_efi.c b/roms/u-boot/arch/x86/lib/reloc_ia32_efi.c new file mode 100644 index 000000000..d56cd50bd --- /dev/null +++ b/roms/u-boot/arch/x86/lib/reloc_ia32_efi.c @@ -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; +} diff --git a/roms/u-boot/arch/x86/lib/reloc_x86_64_efi.c b/roms/u-boot/arch/x86/lib/reloc_x86_64_efi.c new file mode 100644 index 000000000..2694de711 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/reloc_x86_64_efi.c @@ -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; +} diff --git a/roms/u-boot/arch/x86/lib/relocate.c b/roms/u-boot/arch/x86/lib/relocate.c new file mode 100644 index 000000000..6fe515164 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/relocate.c @@ -0,0 +1,175 @@ +// 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 <log.h> +#include <relocate.h> +#include <asm/global_data.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; +} diff --git a/roms/u-boot/arch/x86/lib/scu.c b/roms/u-boot/arch/x86/lib/scu.c new file mode 100644 index 000000000..90ef239bc --- /dev/null +++ b/roms/u-boot/arch/x86/lib/scu.c @@ -0,0 +1,231 @@ +// 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/bitops.h> +#include <linux/delay.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, ®s->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(®s->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++, ®s->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(®s->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 = sizeof(struct scu), +}; diff --git a/roms/u-boot/arch/x86/lib/sections.c b/roms/u-boot/arch/x86/lib/sections.c new file mode 100644 index 000000000..375029ead --- /dev/null +++ b/roms/u-boot/arch/x86/lib/sections.c @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2013 Albert ARIBAUD <albert.u.boot@aribaud.net> + */ +#include <linux/compiler.h> + +char __efi_runtime_start[0] __section(".__efi_runtime_start"); +char __efi_runtime_stop[0] __section(".__efi_runtime_stop"); +char __efi_runtime_rel_start[0] + __section(".__efi_runtime_rel_start"); +char __efi_runtime_rel_stop[0] + __section(".__efi_runtime_rel_stop"); diff --git a/roms/u-boot/arch/x86/lib/sfi.c b/roms/u-boot/arch/x86/lib/sfi.c new file mode 100644 index 000000000..85e963b63 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/sfi.c @@ -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_plat *plat = dev_get_parent_plat(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; +} diff --git a/roms/u-boot/arch/x86/lib/spl.c b/roms/u-boot/arch/x86/lib/spl.c new file mode 100644 index 000000000..b18c1cd60 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/spl.c @@ -0,0 +1,257 @@ +// 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 <hang.h> +#include <image.h> +#include <init.h> +#include <irq_func.h> +#include <log.h> +#include <malloc.h> +#include <spl.h> +#include <syscon.h> +#include <asm/cpu.h> +#include <asm/cpu_common.h> +#include <asm/global_data.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. + */ + __maybe_unused 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(); +#if !defined(CONFIG_TPL) && !CONFIG_IS_ENABLED(CPU) + 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_SYS_COREBOOT + memset(&__bss_start, 0, (ulong)&__bss_end - (ulong)&__bss_start); +# ifndef CONFIG_TPL + + /* 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 +#endif + + return 0; +} + +void board_init_f(ulong flags) +{ + int ret; + + ret = x86_spl_init(); + if (ret) { + printf("x86_spl_init: error %d\n", ret); + hang(); + } +#if IS_ENABLED(CONFIG_TPL) || IS_ENABLED(CONFIG_SYS_COREBOOT) + 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"; + + if (!IS_ENABLED(CONFIG_SYS_COREBOOT)) { + /* + * Copy U-Boot from ROM + * TODO(sjg@chromium.org): Figure out a way to get the text base + * correctly here, and in the device-tree binman definition. + * + * Also consider using FIT so we get the correct image length + * and parameters. + */ + memcpy((char *)spl_image->load_addr, (char *)0xfff00000, + 0x100000); + } + + 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 +} diff --git a/roms/u-boot/arch/x86/lib/string.c b/roms/u-boot/arch/x86/lib/string.c new file mode 100644 index 000000000..c6263cd2f --- /dev/null +++ b/roms/u-boot/arch/x86/lib/string.c @@ -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; +} diff --git a/roms/u-boot/arch/x86/lib/tables.c b/roms/u-boot/arch/x86/lib/tables.c new file mode 100644 index 000000000..1779bb3e1 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/tables.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <bloblist.h> +#include <log.h> +#include <malloc.h> +#include <smbios.h> +#include <acpi/acpi_table.h> +#include <asm/global_data.h> +#include <asm/sfi.h> +#include <asm/mpspec.h> +#include <asm/tables.h> +#include <asm/coreboot_tables.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * 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); + +/** + * struct table_info - Information about each table to write + * + * @name: Name of table (for debugging) + * @write: Function to call to write this table + * @tag: Bloblist tag if using CONFIG_BLOBLIST_TABLES + * @size: Maximum table size + * @align: Table alignment in bytes + */ +struct table_info { + const char *name; + table_write write; + enum bloblist_tag_t tag; + int size; + int align; +}; + +static struct table_info table_list[] = { +#ifdef CONFIG_GENERATE_PIRQ_TABLE + { "pirq", write_pirq_routing_table }, +#endif +#ifdef CONFIG_GENERATE_SFI_TABLE + { "sfi", write_sfi_table, }, +#endif +#ifdef CONFIG_GENERATE_MP_TABLE + { "mp", write_mp_table, }, +#endif +#ifdef CONFIG_GENERATE_ACPI_TABLE + { "acpi", write_acpi_tables, BLOBLISTT_ACPI_TABLES, 0x10000, 0x1000}, +#endif +#ifdef CONFIG_GENERATE_SMBIOS_TABLE + { "smbios", write_smbios_table, BLOBLISTT_SMBIOS_TABLES, 0x1000, 0x100}, +#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; +} + +int write_tables(void) +{ + u32 rom_table_start; + u32 rom_table_end; + u32 high_table, table_size; + struct memory_area cfg_tables[ARRAY_SIZE(table_list) + 1]; + int i; + + rom_table_start = ROM_TABLE_ADDR; + + debug("Writing tables to %x:\n", rom_table_start); + for (i = 0; i < ARRAY_SIZE(table_list); i++) { + const struct table_info *table = &table_list[i]; + int size = table->size ? : CONFIG_ROM_TABLE_SIZE; + + if (IS_ENABLED(CONFIG_BLOBLIST_TABLES) && table->tag) { + rom_table_start = (ulong)bloblist_add(table->tag, size, + table->align); + if (!rom_table_start) + return log_msg_ret("bloblist", -ENOBUFS); + } + rom_table_end = table->write(rom_table_start); + rom_table_end = ALIGN(rom_table_end, ROM_TABLE_ALIGN); + + if (IS_ENABLED(CONFIG_SEABIOS)) { + table_size = rom_table_end - rom_table_start; + high_table = (u32)(ulong)high_table_malloc(table_size); + if (high_table) { + table->write(high_table); + + cfg_tables[i].start = high_table; + cfg_tables[i].size = table_size; + } else { + printf("%d: no memory for configuration tables\n", + i); + return -ENOSPC; + } + } + + debug("- wrote '%s' to %x, end %x\n", table->name, + rom_table_start, rom_table_end); + if (rom_table_end - rom_table_start > size) { + log_err("Out of space for configuration tables: need %x, have %x\n", + rom_table_end - rom_table_start, size); + return log_msg_ret("bloblist", -ENOSPC); + } + rom_table_start = rom_table_end; + } + + if (IS_ENABLED(CONFIG_SEABIOS)) { + /* make sure the last item is zero */ + cfg_tables[i].size = 0; + write_coreboot_table(CB_TABLE_ADDR, cfg_tables); + } + + if (IS_ENABLED(CONFIG_BLOBLIST_TABLES)) { + void *ptr = (void *)CONFIG_ROM_TABLE_ADDR; + + /* Write an RSDP pointing to the tables */ + if (IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE)) { + struct acpi_ctx *ctx = gd_acpi_ctx(); + + acpi_write_rsdp(ptr, ctx->rsdt, ctx->xsdt); + ptr += ALIGN(sizeof(struct acpi_rsdp), 16); + } + if (IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE)) { + void *smbios; + + smbios = bloblist_find(BLOBLISTT_SMBIOS_TABLES, 0); + if (!smbios) + return log_msg_ret("smbios", -ENOENT); + memcpy(ptr, smbios, sizeof(struct smbios_entry)); + } + } + + debug("- done writing tables\n"); + + return 0; +} diff --git a/roms/u-boot/arch/x86/lib/tpl.c b/roms/u-boot/arch/x86/lib/tpl.c new file mode 100644 index 000000000..b3e5f9c91 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/tpl.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 Google, Inc + */ + +#include <common.h> +#include <debug_uart.h> +#include <dm.h> +#include <hang.h> +#include <image.h> +#include <init.h> +#include <log.h> +#include <spl.h> +#include <asm/cpu.h> +#include <asm/global_data.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_VBOOT) ? 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 %s at %lx\n", spl_phase_name(spl_next_phase()), + (ulong)spl_image->entry_point); +#ifdef DEBUG + print_buffer(spl_image->entry_point, (void *)spl_image->entry_point, 1, + 0x20, 0); +#endif + 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). + */ +#if !CONFIG_IS_ENABLED(OF_PLATDATA) +static const struct udevice_id tpl_fake_pci_ids[] = { + { .compatible = "pci-x86" }, + { } +}; +#endif + +U_BOOT_DRIVER(pci_x86) = { + .name = "pci_x86", + .id = UCLASS_SIMPLE_BUS, + .of_match = of_match_ptr(tpl_fake_pci_ids), + DM_PHASE(tpl) +}; +#endif diff --git a/roms/u-boot/arch/x86/lib/zimage.c b/roms/u-boot/arch/x86/lib/zimage.c new file mode 100644 index 000000000..90fc8a466 --- /dev/null +++ b/roms/u-boot/arch/x86/lib/zimage.c @@ -0,0 +1,792 @@ +// 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 + */ + +#define LOG_CATEGORY LOGC_BOOT + +#include <common.h> +#include <bootm.h> +#include <command.h> +#include <env.h> +#include <init.h> +#include <irq_func.h> +#include <log.h> +#include <malloc.h> +#include <acpi/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> +#include <asm/global_data.h> +#ifdef CONFIG_SYS_COREBOOT +#include <asm/arch/timestamp.h> +#endif +#include <linux/compiler.h> +#include <linux/ctype.h> +#include <linux/libfdt.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * 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 + +/** + * struct zboot_state - Current state of the boot + * + * @bzimage_addr: Address of the bzImage to boot + * @bzimage_size: Size of the bzImage, or 0 to detect this + * @initrd_addr: Address of the initial ramdisk, or 0 if none + * @initrd_size: Size of the initial ramdisk, or 0 if none + * @load_address: Address where the bzImage is moved before booting, either + * BZIMAGE_LOAD_ADDR or ZIMAGE_LOAD_ADDR + * @base_ptr: Pointer to the boot parameters, typically at address + * DEFAULT_SETUP_BASE + * @cmdline: Environment variable containing the 'override' command line, or + * NULL to use the one in the setup block + */ +struct zboot_state { + ulong bzimage_addr; + ulong bzimage_size; + ulong initrd_addr; + ulong initrd_size; + ulong load_address; + struct boot_params *base_ptr; + char *cmdline; +} state; + +enum { + ZBOOT_STATE_START = BIT(0), + ZBOOT_STATE_LOAD = BIT(1), + ZBOOT_STATE_SETUP = BIT(2), + ZBOOT_STATE_INFO = BIT(3), + ZBOOT_STATE_GO = BIT(4), + + /* This one doesn't execute automatically, so stop the count before 5 */ + ZBOOT_STATE_DUMP = BIT(5), + ZBOOT_STATE_COUNT = 5, +}; + +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); +#ifdef DEBUG + printf("Kernel command line:"); + puts(command_line); + printf("\n"); +#endif +} + +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, bool verbose) +{ + if (hdr->header == KERNEL_V2_MAGIC) { + if (verbose) + printf("Magic signature found\n"); + return hdr->version; + } else { + /* Very old kernel */ + if (verbose) + 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, false); + 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; +} + +static const char *get_kernel_version(struct boot_params *params, + void *kernel_base) +{ + struct setup_header *hdr = ¶ms->hdr; + int bootproto; + const char *s, *end; + + bootproto = get_boot_protocol(hdr, false); + if (bootproto < 0x0200 || hdr->setup_sects < 15) + return NULL; + + /* sanity-check the kernel version in case it is missing */ + for (s = kernel_base + hdr->kernel_version + 0x200, end = s + 0x100; *s; + s++) { + if (!isprint(*s)) + return NULL; + } + + return kernel_base + hdr->kernel_version + 0x200; +} + +struct boot_params *load_zimage(char *image, unsigned long kernel_size, + ulong *load_addressp) +{ + struct boot_params *setup_base; + const char *version; + int setup_size; + int bootproto; + int big_image; + + struct boot_params *params = (struct boot_params *)image; + struct setup_header *hdr = ¶ms->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) { + log_warning("Setup Sectors = 0 (defaulting to 4)\n"); + setup_size = 5 * 512; + } else { + setup_size = (hdr->setup_sects + 1) * 512; + } + + log_debug("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, true); + + log_debug("Using boot protocol version %x.%02x\n", + (bootproto & 0xff00) >> 8, bootproto & 0xff); + + version = get_kernel_version(params, image); + if (version) + printf("Linux kernel version %s\n", version); + 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, + ulong initrd_addr, ulong initrd_size, ulong cmdline_force) +{ + struct setup_header *hdr = &setup_base->hdr; + int bootproto = get_boot_protocol(hdr, false); + + log_debug("Setup E820 entries\n"); + if (ll_boot_init()) { + setup_base->e820_entries = install_e820_map( + ARRAY_SIZE(setup_base->e820_map), setup_base->e820_map); + } else if (IS_ENABLED(CONFIG_COREBOOT_SYSINFO)) { + setup_base->e820_entries = cb_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 = 0x80; /* U-Boot version 0 */ + 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) { + int max_size = 0xff; + int ret; + + log_debug("Setup cmdline\n"); + if (bootproto >= 0x0206) + max_size = hdr->cmdline_size; + 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 */ + if (cmdline_force) + strcpy(cmd_line, (char *)cmdline_force); + else + build_command_line(cmd_line, auto_boot); + ret = bootm_process_cmdline(cmd_line, max_size, BOOTM_CL_ALL); + if (ret) { + printf("Cmdline setup failed (max_size=%x, bootproto=%x, err=%d)\n", + max_size, bootproto, ret); + return ret; + } + printf("Kernel command line: \""); + puts(cmd_line); + printf("\"\n"); + } + + if (IS_ENABLED(CONFIG_INTEL_MID) && bootproto >= 0x0207) + hdr->hardware_subarch = X86_SUBARCH_INTEL_MID; + + if (IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE)) + setup_base->acpi_rsdp_addr = acpi_get_rsdp_addr(); + + log_debug("Setup devicetree\n"); + setup_device_tree(hdr, (const void *)env_get_hex("fdtaddr", 0)); + setup_video(&setup_base->screen_info); + + if (IS_ENABLED(CONFIG_EFI_STUB)) + setup_efi_info(&setup_base->efi_info); + + return 0; +} + +static int do_zboot_start(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *s; + + memset(&state, '\0', sizeof(state)); + if (argc >= 2) { + /* argv[1] holds the address of the bzImage */ + s = argv[1]; + } else { + s = env_get("fileaddr"); + } + + if (s) + state.bzimage_addr = simple_strtoul(s, NULL, 16); + + if (argc >= 3) { + /* argv[2] holds the size of the bzImage */ + state.bzimage_size = simple_strtoul(argv[2], NULL, 16); + } + + if (argc >= 4) + state.initrd_addr = simple_strtoul(argv[3], NULL, 16); + if (argc >= 5) + state.initrd_size = simple_strtoul(argv[4], NULL, 16); + if (argc >= 6) { + /* + * When the base_ptr is passed in, we assume that the image is + * already loaded at the address given by argv[1] and therefore + * the original bzImage is somewhere else, or not accessible. + * In any case, we don't need access to the bzImage since all + * the processing is assumed to be done. + * + * So set the base_ptr to the given address, use this arg as the + * load address and set bzimage_addr to 0 so we know that it + * cannot be proceesed (or processed again). + */ + state.base_ptr = (void *)simple_strtoul(argv[5], NULL, 16); + state.load_address = state.bzimage_addr; + state.bzimage_addr = 0; + } + if (argc >= 7) + state.cmdline = env_get(argv[6]); + + return 0; +} + +static int do_zboot_load(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct boot_params *base_ptr; + + if (state.base_ptr) { + struct boot_params *from = (struct boot_params *)state.base_ptr; + + base_ptr = (struct boot_params *)DEFAULT_SETUP_BASE; + log_debug("Building boot_params at 0x%8.8lx\n", + (ulong)base_ptr); + memset(base_ptr, '\0', sizeof(*base_ptr)); + base_ptr->hdr = from->hdr; + } else { + base_ptr = load_zimage((void *)state.bzimage_addr, state.bzimage_size, + &state.load_address); + if (!base_ptr) { + puts("## Kernel loading failed ...\n"); + return CMD_RET_FAILURE; + } + } + state.base_ptr = base_ptr; + if (env_set_hex("zbootbase", (ulong)base_ptr) || + env_set_hex("zbootaddr", state.load_address)) + return CMD_RET_FAILURE; + + return 0; +} + +static int do_zboot_setup(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct boot_params *base_ptr = state.base_ptr; + int ret; + + if (!base_ptr) { + printf("base is not set: use 'zboot load' first\n"); + return CMD_RET_FAILURE; + } + ret = setup_zimage(base_ptr, (char *)base_ptr + COMMAND_LINE_OFFSET, + 0, state.initrd_addr, state.initrd_size, + (ulong)state.cmdline); + if (ret) { + puts("Setting up boot parameters failed ...\n"); + return CMD_RET_FAILURE; + } + + return 0; +} + +static int do_zboot_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + printf("Kernel loaded at %08lx, setup_base=%p\n", + state.load_address, state.base_ptr); + + return 0; +} + +static int do_zboot_go(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int ret; + + disable_interrupts(); + + /* we assume that the kernel is in place */ + ret = boot_linux_kernel((ulong)state.base_ptr, state.load_address, + false); + printf("Kernel returned! (err=%d)\n", ret); + + return CMD_RET_FAILURE; +} + +static void print_num(const char *name, ulong value) +{ + printf("%-20s: %lx\n", name, value); +} + +static void print_num64(const char *name, u64 value) +{ + printf("%-20s: %llx\n", name, value); +} + +static const char *const e820_type_name[E820_COUNT] = { + [E820_RAM] = "RAM", + [E820_RESERVED] = "Reserved", + [E820_ACPI] = "ACPI", + [E820_NVS] = "ACPI NVS", + [E820_UNUSABLE] = "Unusable", +}; + +static const char *const bootloader_id[] = { + "LILO", + "Loadlin", + "bootsect-loader", + "Syslinux", + "Etherboot/gPXE/iPXE", + "ELILO", + "undefined", + "GRUB", + "U-Boot", + "Xen", + "Gujin", + "Qemu", + "Arcturus Networks uCbootloader", + "kexec-tools", + "Extended", + "Special", + "Reserved", + "Minimal Linux Bootloader", + "OVMF UEFI virtualization stack", +}; + +struct flag_info { + uint bit; + const char *name; +}; + +static struct flag_info load_flags[] = { + { LOADED_HIGH, "loaded-high" }, + { QUIET_FLAG, "quiet" }, + { KEEP_SEGMENTS, "keep-segments" }, + { CAN_USE_HEAP, "can-use-heap" }, +}; + +static struct flag_info xload_flags[] = { + { XLF_KERNEL_64, "64-bit-entry" }, + { XLF_CAN_BE_LOADED_ABOVE_4G, "can-load-above-4gb" }, + { XLF_EFI_HANDOVER_32, "32-efi-handoff" }, + { XLF_EFI_HANDOVER_64, "64-efi-handoff" }, + { XLF_EFI_KEXEC, "kexec-efi-runtime" }, +}; + +static void print_flags(struct flag_info *flags, int count, uint value) +{ + int i; + + printf("%-20s:", ""); + for (i = 0; i < count; i++) { + uint mask = flags[i].bit; + + if (value & mask) + printf(" %s", flags[i].name); + } + printf("\n"); +} + +static void show_loader(struct setup_header *hdr) +{ + bool version_valid = false; + int type, version; + const char *name; + + type = hdr->type_of_loader >> 4; + version = hdr->type_of_loader & 0xf; + if (type == 0xe) + type = 0x10 + hdr->ext_loader_type; + version |= hdr->ext_loader_ver << 4; + if (!hdr->type_of_loader) { + name = "pre-2.00 bootloader"; + } else if (hdr->type_of_loader == 0xff) { + name = "unknown"; + } else if (type < ARRAY_SIZE(bootloader_id)) { + name = bootloader_id[type]; + version_valid = true; + } else { + name = "undefined"; + } + printf("%20s %s", "", name); + if (version_valid) + printf(", version %x", version); + printf("\n"); +} + +void zimage_dump(struct boot_params *base_ptr) +{ + struct setup_header *hdr; + const char *version; + int i; + + printf("Setup located at %p:\n\n", base_ptr); + print_num64("ACPI RSDP addr", base_ptr->acpi_rsdp_addr); + + printf("E820: %d entries\n", base_ptr->e820_entries); + if (base_ptr->e820_entries) { + printf("%18s %16s %s\n", "Addr", "Size", "Type"); + for (i = 0; i < base_ptr->e820_entries; i++) { + struct e820_entry *entry = &base_ptr->e820_map[i]; + + printf("%12llx %10llx %s\n", entry->addr, entry->size, + entry->type < E820_COUNT ? + e820_type_name[entry->type] : + simple_itoa(entry->type)); + } + } + + hdr = &base_ptr->hdr; + print_num("Setup sectors", hdr->setup_sects); + print_num("Root flags", hdr->root_flags); + print_num("Sys size", hdr->syssize); + print_num("RAM size", hdr->ram_size); + print_num("Video mode", hdr->vid_mode); + print_num("Root dev", hdr->root_dev); + print_num("Boot flag", hdr->boot_flag); + print_num("Jump", hdr->jump); + print_num("Header", hdr->header); + if (hdr->header == KERNEL_V2_MAGIC) + printf("%-20s %s\n", "", "Kernel V2"); + else + printf("%-20s %s\n", "", "Ancient kernel, using version 100"); + print_num("Version", hdr->version); + print_num("Real mode switch", hdr->realmode_swtch); + print_num("Start sys", hdr->start_sys); + print_num("Kernel version", hdr->kernel_version); + version = get_kernel_version(base_ptr, (void *)state.bzimage_addr); + if (version) + printf(" @%p: %s\n", version, version); + print_num("Type of loader", hdr->type_of_loader); + show_loader(hdr); + print_num("Load flags", hdr->loadflags); + print_flags(load_flags, ARRAY_SIZE(load_flags), hdr->loadflags); + print_num("Setup move size", hdr->setup_move_size); + print_num("Code32 start", hdr->code32_start); + print_num("Ramdisk image", hdr->ramdisk_image); + print_num("Ramdisk size", hdr->ramdisk_size); + print_num("Bootsect kludge", hdr->bootsect_kludge); + print_num("Heap end ptr", hdr->heap_end_ptr); + print_num("Ext loader ver", hdr->ext_loader_ver); + print_num("Ext loader type", hdr->ext_loader_type); + print_num("Command line ptr", hdr->cmd_line_ptr); + if (hdr->cmd_line_ptr) { + printf(" "); + /* Use puts() to avoid limits from CONFIG_SYS_PBSIZE */ + puts((char *)(ulong)hdr->cmd_line_ptr); + printf("\n"); + } + print_num("Initrd addr max", hdr->initrd_addr_max); + print_num("Kernel alignment", hdr->kernel_alignment); + print_num("Relocatable kernel", hdr->relocatable_kernel); + print_num("Min alignment", hdr->min_alignment); + if (hdr->min_alignment) + printf("%-20s: %x\n", "", 1 << hdr->min_alignment); + print_num("Xload flags", hdr->xloadflags); + print_flags(xload_flags, ARRAY_SIZE(xload_flags), hdr->xloadflags); + print_num("Cmdline size", hdr->cmdline_size); + print_num("Hardware subarch", hdr->hardware_subarch); + print_num64("HW subarch data", hdr->hardware_subarch_data); + print_num("Payload offset", hdr->payload_offset); + print_num("Payload length", hdr->payload_length); + print_num64("Setup data", hdr->setup_data); + print_num64("Pref address", hdr->pref_address); + print_num("Init size", hdr->init_size); + print_num("Handover offset", hdr->handover_offset); + if (get_boot_protocol(hdr, false) >= 0x215) + print_num("Kernel info offset", hdr->kernel_info_offset); +} + +static int do_zboot_dump(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct boot_params *base_ptr = state.base_ptr; + + if (argc > 1) + base_ptr = (void *)simple_strtoul(argv[1], NULL, 16); + if (!base_ptr) { + printf("No zboot setup_base\n"); + return CMD_RET_FAILURE; + } + zimage_dump(base_ptr); + + return 0; +} + +/* Note: This defines the complete_zboot() function */ +U_BOOT_SUBCMDS(zboot, + U_BOOT_CMD_MKENT(start, 8, 1, do_zboot_start, "", ""), + U_BOOT_CMD_MKENT(load, 1, 1, do_zboot_load, "", ""), + U_BOOT_CMD_MKENT(setup, 1, 1, do_zboot_setup, "", ""), + U_BOOT_CMD_MKENT(info, 1, 1, do_zboot_info, "", ""), + U_BOOT_CMD_MKENT(go, 1, 1, do_zboot_go, "", ""), + U_BOOT_CMD_MKENT(dump, 2, 1, do_zboot_dump, "", ""), +) + +int do_zboot_states(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[], int state_mask) +{ + int i; + + for (i = 0; i < ZBOOT_STATE_COUNT; i++) { + struct cmd_tbl *cmd = &zboot_subcmds[i]; + int mask = 1 << i; + int ret; + + if (mask & state_mask) { + ret = cmd->cmd(cmd, flag, argc, argv); + if (ret) + return ret; + } + } + + return 0; +} + +int do_zboot_parent(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[], int *repeatable) +{ + /* determine if we have a sub command */ + if (argc > 1) { + char *endp; + + simple_strtoul(argv[1], &endp, 16); + /* + * endp pointing to nul means that argv[1] was just a valid + * number, so pass it along to the normal processing + */ + if (*endp) + return do_zboot(cmdtp, flag, argc, argv, repeatable); + } + + do_zboot_states(cmdtp, flag, argc, argv, ZBOOT_STATE_START | + ZBOOT_STATE_LOAD | ZBOOT_STATE_SETUP | + ZBOOT_STATE_INFO | ZBOOT_STATE_GO); + + return CMD_RET_FAILURE; +} + +U_BOOT_CMDREP_COMPLETE( + zboot, 8, do_zboot_parent, "Boot bzImage", + "[addr] [size] [initrd addr] [initrd size] [setup] [cmdline]\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" + " setup - The address of the kernel setup region, if this\n" + " is not at addr\n" + " cmdline - Environment variable containing the kernel\n" + " command line, to override U-Boot's normal\n" + " cmdline generation\n" + "\n" + "Sub-commands to do part of the zboot sequence:\n" + "\tstart [addr [arg ...]] - specify arguments\n" + "\tload - load OS image\n" + "\tsetup - set up table\n" + "\tinfo - show summary info\n" + "\tgo - start OS\n" + "\tdump [addr] - dump info (optional address of boot params)", + complete_zboot +); |