diff options
Diffstat (limited to 'arch/x86/lib/fsp1')
-rw-r--r-- | arch/x86/lib/fsp1/Makefile | 9 | ||||
-rw-r--r-- | arch/x86/lib/fsp1/fsp_car.S | 111 | ||||
-rw-r--r-- | arch/x86/lib/fsp1/fsp_common.c | 77 | ||||
-rw-r--r-- | arch/x86/lib/fsp1/fsp_dram.c | 36 | ||||
-rw-r--r-- | arch/x86/lib/fsp1/fsp_graphics.c | 127 | ||||
-rw-r--r-- | arch/x86/lib/fsp1/fsp_support.c | 199 |
6 files changed, 559 insertions, 0 deletions
diff --git a/arch/x86/lib/fsp1/Makefile b/arch/x86/lib/fsp1/Makefile new file mode 100644 index 0000000000..870de71bd7 --- /dev/null +++ b/arch/x86/lib/fsp1/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2015 Google, Inc + +obj-y += fsp_car.o +obj-y += fsp_common.o +obj-y += fsp_dram.o +obj-$(CONFIG_VIDEO_FSP) += fsp_graphics.o +obj-y += fsp_support.o diff --git a/arch/x86/lib/fsp1/fsp_car.S b/arch/x86/lib/fsp1/fsp_car.S new file mode 100644 index 0000000000..a64a653435 --- /dev/null +++ b/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/arch/x86/lib/fsp1/fsp_common.c b/arch/x86/lib/fsp1/fsp_common.c new file mode 100644 index 0000000000..e8066d8de3 --- /dev/null +++ b/arch/x86/lib/fsp1/fsp_common.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <acpi_s3.h> +#include <dm.h> +#include <errno.h> +#include <rtc.h> +#include <asm/cmos_layout.h> +#include <asm/early_cmos.h> +#include <asm/io.h> +#include <asm/mrccache.h> +#include <asm/post.h> +#include <asm/processor.h> +#include <asm/fsp1/fsp_support.h> + +DECLARE_GLOBAL_DATA_PTR; + +int arch_fsp_init(void) +{ + void *nvs; + int stack = CONFIG_FSP_TEMP_RAM_ADDR; + int boot_mode = BOOT_FULL_CONFIG; +#ifdef CONFIG_HAVE_ACPI_RESUME + int prev_sleep_state = chipset_prev_sleep_state(); + gd->arch.prev_sleep_state = prev_sleep_state; +#endif + + if (!gd->arch.hob_list) { + if (IS_ENABLED(CONFIG_ENABLE_MRC_CACHE)) + nvs = fsp_prepare_mrc_cache(); + else + nvs = NULL; + +#ifdef CONFIG_HAVE_ACPI_RESUME + if (prev_sleep_state == ACPI_S3) { + if (nvs == NULL) { + /* If waking from S3 and no cache then */ + debug("No MRC cache found in S3 resume path\n"); + post_code(POST_RESUME_FAILURE); + /* Clear Sleep Type */ + chipset_clear_sleep_state(); + /* Reboot */ + debug("Rebooting..\n"); + outb(SYS_RST | RST_CPU, IO_PORT_RESET); + /* Should not reach here.. */ + panic("Reboot System"); + } + + /* + * DM is not available yet at this point, hence call + * CMOS access library which does not depend on DM. + */ + stack = cmos_read32(CMOS_FSP_STACK_ADDR); + boot_mode = BOOT_ON_S3_RESUME; + } +#endif + /* + * The first time we enter here, call fsp_init(). + * Note the execution does not return to this function, + * instead it jumps to fsp_continue(). + */ + fsp_init(stack, boot_mode, nvs); + } else { + /* + * The second time we enter here, adjust the size of malloc() + * pool before relocation. Given gd->malloc_base was adjusted + * after the call to board_init_f_init_reserve() in arch/x86/ + * cpu/start.S, we should fix up gd->malloc_limit here. + */ + gd->malloc_limit += CONFIG_FSP_SYS_MALLOC_F_LEN; + } + + return 0; +} diff --git a/arch/x86/lib/fsp1/fsp_dram.c b/arch/x86/lib/fsp1/fsp_dram.c new file mode 100644 index 0000000000..6a3349b42a --- /dev/null +++ b/arch/x86/lib/fsp1/fsp_dram.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <asm/fsp/fsp_support.h> + +int dram_init(void) +{ + int ret; + + /* The FSP has already set up DRAM, so grab the info we need */ + ret = fsp_scan_for_ram_size(); + if (ret) + return ret; + + if (IS_ENABLED(CONFIG_ENABLE_MRC_CACHE)) + gd->arch.mrc_output = fsp_get_nvs_data(gd->arch.hob_list, + &gd->arch.mrc_output_len); + + return 0; +} + +/* + * This function looks for the highest region of memory lower than 4GB which + * has enough space for U-Boot where U-Boot is aligned on a page boundary. + * It overrides the default implementation found elsewhere which simply + * picks the end of ram, wherever that may be. The location of the stack, + * the relocation address, and how far U-Boot is moved by relocation are + * set in the global data structure. + */ +ulong board_get_usable_ram_top(ulong total_size) +{ + return fsp_get_usable_lowmem_top(gd->arch.hob_list); +} diff --git a/arch/x86/lib/fsp1/fsp_graphics.c b/arch/x86/lib/fsp1/fsp_graphics.c new file mode 100644 index 0000000000..52e71334f9 --- /dev/null +++ b/arch/x86/lib/fsp1/fsp_graphics.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <dm.h> +#include <vbe.h> +#include <video.h> +#include <asm/fsp1/fsp_support.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct pixel { + u8 pos; + u8 size; +}; + +static const struct fsp_framebuffer { + struct pixel red; + struct pixel green; + struct pixel blue; + struct pixel rsvd; +} fsp_framebuffer_format_map[] = { + [pixel_rgbx_8bpc] = { {0, 8}, {8, 8}, {16, 8}, {24, 8} }, + [pixel_bgrx_8bpc] = { {16, 8}, {8, 8}, {0, 8}, {24, 8} }, +}; + +static int save_vesa_mode(struct vesa_mode_info *vesa) +{ + const struct hob_graphics_info *ginfo; + const struct fsp_framebuffer *fbinfo; + + ginfo = fsp_get_graphics_info(gd->arch.hob_list, NULL); + + /* + * If there is no graphics info structure, bail out and keep + * running on the serial console. + * + * Note: on some platforms (eg: Braswell), the FSP will not produce + * the graphics info HOB unless you plug some cables to the display + * interface (eg: HDMI) on the board. + */ + if (!ginfo) { + debug("FSP graphics hand-off block not found\n"); + return -ENXIO; + } + + vesa->x_resolution = ginfo->width; + vesa->y_resolution = ginfo->height; + vesa->bits_per_pixel = 32; + vesa->bytes_per_scanline = ginfo->pixels_per_scanline * 4; + vesa->phys_base_ptr = ginfo->fb_base; + + if (ginfo->pixel_format >= pixel_bitmask) { + debug("FSP set unknown framebuffer format: %d\n", + ginfo->pixel_format); + return -EINVAL; + } + fbinfo = &fsp_framebuffer_format_map[ginfo->pixel_format]; + vesa->red_mask_size = fbinfo->red.size; + vesa->red_mask_pos = fbinfo->red.pos; + vesa->green_mask_size = fbinfo->green.size; + vesa->green_mask_pos = fbinfo->green.pos; + vesa->blue_mask_size = fbinfo->blue.size; + vesa->blue_mask_pos = fbinfo->blue.pos; + vesa->reserved_mask_size = fbinfo->rsvd.size; + vesa->reserved_mask_pos = fbinfo->rsvd.pos; + + return 0; +} + +static int fsp_video_probe(struct udevice *dev) +{ + struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); + struct video_priv *uc_priv = dev_get_uclass_priv(dev); + struct vesa_mode_info *vesa = &mode_info.vesa; + int ret; + + printf("Video: "); + + /* Initialize vesa_mode_info structure */ + ret = save_vesa_mode(vesa); + if (ret) + goto err; + + /* + * The framebuffer base address in the FSP graphics info HOB reflects + * the value assigned by the FSP. After PCI enumeration the framebuffer + * base address may be relocated. Let's get the updated one from device. + * + * For IGD, it seems to be always on BAR2. + */ + vesa->phys_base_ptr = dm_pci_read_bar32(dev, 2); + + ret = vbe_setup_video_priv(vesa, uc_priv, plat); + if (ret) + goto err; + + printf("%dx%dx%d\n", uc_priv->xsize, uc_priv->ysize, + vesa->bits_per_pixel); + + return 0; + +err: + printf("No video mode configured in FSP!\n"); + return ret; +} + +static const struct udevice_id fsp_video_ids[] = { + { .compatible = "fsp-fb" }, + { } +}; + +U_BOOT_DRIVER(fsp_video) = { + .name = "fsp_video", + .id = UCLASS_VIDEO, + .of_match = fsp_video_ids, + .probe = fsp_video_probe, +}; + +static struct pci_device_id fsp_video_supported[] = { + { PCI_DEVICE_CLASS(PCI_CLASS_DISPLAY_VGA << 8, 0xffff00) }, + { }, +}; + +U_BOOT_PCI_DEVICE(fsp_video, fsp_video_supported); diff --git a/arch/x86/lib/fsp1/fsp_support.c b/arch/x86/lib/fsp1/fsp_support.c new file mode 100644 index 0000000000..c7a2c73846 --- /dev/null +++ b/arch/x86/lib/fsp1/fsp_support.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: Intel +/* + * Copyright (C) 2013, Intel Corporation + * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <asm/fsp1/fsp_support.h> +#include <asm/post.h> + +struct fsp_header *__attribute__((optimize("O0"))) fsp_find_header(void) +{ + /* + * This function may be called before the a stack is established, + * so special care must be taken. First, it cannot declare any local + * variable using stack. Only register variable can be used here. + * Secondly, some compiler version will add prolog or epilog code + * for the C function. If so the function call may not work before + * stack is ready. + * + * GCC 4.8.1 has been verified to be working for the following codes. + */ + volatile register u8 *fsp asm("eax"); + + /* Initalize the FSP base */ + fsp = (u8 *)CONFIG_FSP_ADDR; + + /* Check the FV signature, _FVH */ + if (((struct fv_header *)fsp)->sign == EFI_FVH_SIGNATURE) { + /* Go to the end of the FV header and align the address */ + fsp += ((struct fv_header *)fsp)->ext_hdr_off; + fsp += ((struct fv_ext_header *)fsp)->ext_hdr_size; + fsp = (u8 *)(((u32)fsp + 7) & 0xFFFFFFF8); + } else { + fsp = 0; + } + + /* Check the FFS GUID */ + if (fsp && + ((struct ffs_file_header *)fsp)->name.b[0] == FSP_GUID_BYTE0 && + ((struct ffs_file_header *)fsp)->name.b[1] == FSP_GUID_BYTE1 && + ((struct ffs_file_header *)fsp)->name.b[2] == FSP_GUID_BYTE2 && + ((struct ffs_file_header *)fsp)->name.b[3] == FSP_GUID_BYTE3 && + ((struct ffs_file_header *)fsp)->name.b[4] == FSP_GUID_BYTE4 && + ((struct ffs_file_header *)fsp)->name.b[5] == FSP_GUID_BYTE5 && + ((struct ffs_file_header *)fsp)->name.b[6] == FSP_GUID_BYTE6 && + ((struct ffs_file_header *)fsp)->name.b[7] == FSP_GUID_BYTE7 && + ((struct ffs_file_header *)fsp)->name.b[8] == FSP_GUID_BYTE8 && + ((struct ffs_file_header *)fsp)->name.b[9] == FSP_GUID_BYTE9 && + ((struct ffs_file_header *)fsp)->name.b[10] == FSP_GUID_BYTE10 && + ((struct ffs_file_header *)fsp)->name.b[11] == FSP_GUID_BYTE11 && + ((struct ffs_file_header *)fsp)->name.b[12] == FSP_GUID_BYTE12 && + ((struct ffs_file_header *)fsp)->name.b[13] == FSP_GUID_BYTE13 && + ((struct ffs_file_header *)fsp)->name.b[14] == FSP_GUID_BYTE14 && + ((struct ffs_file_header *)fsp)->name.b[15] == FSP_GUID_BYTE15) { + /* Add the FFS header size to find the raw section header */ + fsp += sizeof(struct ffs_file_header); + } else { + fsp = 0; + } + + if (fsp && + ((struct raw_section *)fsp)->type == EFI_SECTION_RAW) { + /* Add the raw section header size to find the FSP header */ + fsp += sizeof(struct raw_section); + } else { + fsp = 0; + } + + return (struct fsp_header *)fsp; +} + +void fsp_continue(u32 status, void *hob_list) +{ + post_code(POST_MRC); + + assert(status == 0); + + /* The boot loader main function entry */ + fsp_init_done(hob_list); +} + +void fsp_init(u32 stack_top, u32 boot_mode, void *nvs_buf) +{ + struct fsp_config_data config_data; + fsp_init_f init; + struct fsp_init_params params; + struct fspinit_rtbuf rt_buf; + struct fsp_header *fsp_hdr; + struct fsp_init_params *params_ptr; +#ifdef CONFIG_FSP_USE_UPD + struct vpd_region *fsp_vpd; + struct upd_region *fsp_upd; +#endif + + fsp_hdr = fsp_find_header(); + if (fsp_hdr == NULL) { + /* No valid FSP info header was found */ + panic("Invalid FSP header"); + } + + config_data.common.fsp_hdr = fsp_hdr; + config_data.common.stack_top = stack_top; + config_data.common.boot_mode = boot_mode; + +#ifdef CONFIG_FSP_USE_UPD + /* Get VPD region start */ + fsp_vpd = (struct vpd_region *)(fsp_hdr->img_base + + fsp_hdr->cfg_region_off); + + /* Verify the VPD data region is valid */ + assert(fsp_vpd->sign == VPD_IMAGE_ID); + + fsp_upd = &config_data.fsp_upd; + + /* Copy default data from Flash */ + memcpy(fsp_upd, (void *)(fsp_hdr->img_base + fsp_vpd->upd_offset), + sizeof(struct upd_region)); + + /* Verify the UPD data region is valid */ + assert(fsp_upd->terminator == UPD_TERMINATOR); +#endif + + memset(&rt_buf, 0, sizeof(struct fspinit_rtbuf)); + + /* Override any configuration if required */ + fsp_update_configs(&config_data, &rt_buf); + + memset(¶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; +} |