| /* |
| * This file is part of the coreboot project. |
| * |
| * Copyright (C) 2014 Google Inc. |
| * Copyright (C) 2015-2017 Intel Corporation. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; version 2 of the License. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <arch/ebda.h> |
| #include <device/mmio.h> |
| #include <cbmem.h> |
| #include <chip.h> |
| #include <console/console.h> |
| #include <device/device.h> |
| #include <device/pci.h> |
| #include <fsp/memmap.h> |
| #include <intelblocks/ebda.h> |
| #include <intelblocks/systemagent.h> |
| #include <soc/msr.h> |
| #include <soc/pci_devs.h> |
| #include <soc/systemagent.h> |
| #include <stdlib.h> |
| |
| size_t mmap_region_granularity(void) |
| { |
| if (CONFIG(HAVE_SMI_HANDLER)) |
| /* Align to TSEG size when SMM is in use */ |
| if (CONFIG_SMM_TSEG_SIZE != 0) |
| return CONFIG_SMM_TSEG_SIZE; |
| |
| /* Make it 8MiB by default. */ |
| return 8*MiB; |
| } |
| |
| void smm_region(void **start, size_t *size) |
| { |
| *start = (void *)sa_get_tseg_base(); |
| *size = sa_get_tseg_size(); |
| } |
| |
| /* |
| * Subregions within SMM |
| * +-------------------------+ BGSM |
| * | IED | IED_REGION_SIZE |
| * +-------------------------+ |
| * | External Stage Cache | SMM_RESERVED_SIZE |
| * +-------------------------+ |
| * | code and data | |
| * | (TSEG) | |
| * +-------------------------+ TSEG |
| */ |
| int smm_subregion(int sub, void **start, size_t *size) |
| { |
| uintptr_t sub_base; |
| size_t sub_size; |
| void *smm_base; |
| const size_t ied_size = CONFIG_IED_REGION_SIZE; |
| const size_t cache_size = CONFIG_SMM_RESERVED_SIZE; |
| |
| smm_region(&smm_base, &sub_size); |
| sub_base = (uintptr_t)smm_base; |
| |
| switch (sub) { |
| case SMM_SUBREGION_HANDLER: |
| /* Handler starts at the base of TSEG. */ |
| sub_size -= ied_size; |
| sub_size -= cache_size; |
| break; |
| case SMM_SUBREGION_CACHE: |
| /* External cache is in the middle of TSEG. */ |
| sub_base += sub_size - (ied_size + cache_size); |
| sub_size = cache_size; |
| break; |
| case SMM_SUBREGION_CHIPSET: |
| /* IED is at the top. */ |
| sub_base += sub_size - ied_size; |
| sub_size = ied_size; |
| break; |
| default: |
| return -1; |
| } |
| |
| *start = (void *)sub_base; |
| *size = sub_size; |
| |
| return 0; |
| } |
| |
| static bool is_ptt_enable(void) |
| { |
| if ((read32((void *)PTT_TXT_BASE_ADDRESS) & PTT_PRESENT) == |
| PTT_PRESENT) |
| return true; |
| |
| return false; |
| } |
| |
| /* Calculate PTT size */ |
| static size_t get_ptt_size(void) |
| { |
| /* Allocate 4KB for PTT if enabled */ |
| return is_ptt_enable() ? 4*KiB : 0; |
| } |
| |
| /* Calculate Trace Hub size */ |
| static size_t get_tracehub_size(uintptr_t dram_base, |
| const struct soc_intel_skylake_config *config) |
| { |
| uintptr_t tracehub_base = dram_base; |
| size_t tracehub_size = 0; |
| |
| if (!config->ProbelessTrace) |
| return 0; |
| |
| /* GDXC MOT */ |
| tracehub_base -= GDXC_MOT_MEMORY_SIZE; |
| /* Round down to natural boundary according to PSMI size */ |
| tracehub_base = ALIGN_DOWN(tracehub_base, PSMI_BUFFER_AREA_SIZE); |
| /* GDXC IOT */ |
| tracehub_base -= GDXC_IOT_MEMORY_SIZE; |
| /* PSMI buffer area */ |
| tracehub_base -= PSMI_BUFFER_AREA_SIZE; |
| |
| /* Tracehub Area Size */ |
| tracehub_size = dram_base - tracehub_base; |
| |
| return tracehub_size; |
| } |
| |
| /* Calculate PRMRR size based on user input PRMRR size and alignment */ |
| static size_t get_prmrr_size(uintptr_t dram_base, |
| const struct soc_intel_skylake_config *config) |
| { |
| uintptr_t prmrr_base = dram_base; |
| size_t prmrr_size; |
| |
| if (CONFIG(PLATFORM_USES_FSP1_1)) |
| prmrr_size = 1*MiB; |
| else |
| prmrr_size = config->PrmrrSize; |
| |
| if (!prmrr_size) |
| return 0; |
| |
| /* |
| * PRMRR Sizes that are > 1MB and < 32MB are |
| * not supported and will fail out. |
| */ |
| if ((prmrr_size > 1*MiB) && (prmrr_size < 32*MiB)) |
| die("PRMRR Sizes that are > 1MB and < 32MB are not" |
| "supported!\n"); |
| |
| prmrr_base -= prmrr_size; |
| if (prmrr_size >= 32*MiB) |
| prmrr_base = ALIGN_DOWN(prmrr_base, 128*MiB); |
| |
| /* PRMRR Area Size */ |
| prmrr_size = dram_base - prmrr_base; |
| |
| return prmrr_size; |
| } |
| |
| /* Calculate Intel Traditional Memory size based on GSM, DSM, TSEG and DPR. */ |
| static size_t calculate_traditional_mem_size(uintptr_t dram_base, |
| const struct device *dev) |
| { |
| uintptr_t traditional_mem_base = dram_base; |
| size_t traditional_mem_size; |
| |
| if (dev->enabled) { |
| /* Read BDSM from Host Bridge */ |
| traditional_mem_base -= sa_get_dsm_size(); |
| |
| /* Read BGSM from Host Bridge */ |
| traditional_mem_base -= sa_get_gsm_size(); |
| } |
| /* Get TSEG size */ |
| traditional_mem_base -= sa_get_tseg_size(); |
| |
| /* Get DPR size */ |
| if (CONFIG(SA_ENABLE_DPR)) |
| traditional_mem_base -= sa_get_dpr_size(); |
| |
| /* Traditional Area Size */ |
| traditional_mem_size = dram_base - traditional_mem_base; |
| |
| return traditional_mem_size; |
| } |
| |
| /* |
| * Calculate Intel Reserved Memory size based on |
| * PRMRR size, Trace Hub config and PTT selection. |
| */ |
| static size_t calculate_reserved_mem_size(uintptr_t dram_base, |
| const struct device *dev) |
| { |
| uintptr_t reserve_mem_base = dram_base; |
| size_t reserve_mem_size; |
| const struct soc_intel_skylake_config *config; |
| |
| config = dev->chip_info; |
| |
| /* Get PRMRR size */ |
| reserve_mem_base -= get_prmrr_size(reserve_mem_base, config); |
| |
| /* Get Tracehub size */ |
| reserve_mem_base -= get_tracehub_size(reserve_mem_base, config); |
| |
| /* Get PTT size */ |
| reserve_mem_base -= get_ptt_size(); |
| |
| /* Traditional Area Size */ |
| reserve_mem_size = dram_base - reserve_mem_base; |
| |
| return reserve_mem_size; |
| } |
| |
| /* |
| * Host Memory Map: |
| * |
| * +--------------------------+ TOUUD |
| * | | |
| * +--------------------------+ 4GiB |
| * | PCI Address Space | |
| * +--------------------------+ TOLUD (also maps into MC address space) |
| * | iGD | |
| * +--------------------------+ BDSM |
| * | GTT | |
| * +--------------------------+ BGSM |
| * | TSEG | |
| * +--------------------------+ TSEGMB |
| * | DMA Protected Region | |
| * +--------------------------+ DPR |
| * | PRM (C6DRAM/SGX) | |
| * +--------------------------+ PRMRR |
| * | Trace Memory | |
| * +--------------------------+ Probless Trace |
| * | PTT | |
| * +--------------------------+ top_of_ram |
| * | Reserved - FSP/CBMEM | |
| * +--------------------------+ TOLUM |
| * | Usage DRAM | |
| * +--------------------------+ 0 |
| * |
| * Some of the base registers above can be equal making the size of those |
| * regions 0. The reason is because the memory controller internally subtracts |
| * the base registers from each other to determine sizes of the regions. In |
| * other words, the memory map is in a fixed order no matter what. |
| */ |
| static uintptr_t calculate_dram_base(size_t *reserved_mem_size) |
| { |
| uintptr_t dram_base; |
| const struct device *dev; |
| |
| dev = dev_find_slot(0, PCI_DEVFN(SA_DEV_SLOT_IGD, 0)); |
| if (!dev) |
| die("ERROR - IGD device not found!"); |
| |
| /* Read TOLUD from Host Bridge offset */ |
| dram_base = sa_get_tolud_base(); |
| |
| /* Get Intel Traditional Memory Range Size */ |
| dram_base -= calculate_traditional_mem_size(dram_base, dev); |
| |
| /* Get Intel Reserved Memory Range Size */ |
| *reserved_mem_size = calculate_reserved_mem_size(dram_base, dev); |
| |
| dram_base -= *reserved_mem_size; |
| |
| return dram_base; |
| } |
| |
| /* |
| * SoC implementation |
| * |
| * SoC call to summarize all Intel Reserve MMIO size and report to SA |
| */ |
| size_t soc_reserved_mmio_size(void) |
| { |
| struct ebda_config cfg; |
| |
| retrieve_ebda_object(&cfg); |
| |
| /* Get Intel Reserved Memory Range Size */ |
| return cfg.reserved_mem_size; |
| } |
| |
| /* Fill up memory layout information */ |
| void fill_soc_memmap_ebda(struct ebda_config *cfg) |
| { |
| size_t chipset_mem_size; |
| |
| cfg->tolum_base = calculate_dram_base(&chipset_mem_size); |
| cfg->reserved_mem_size = chipset_mem_size; |
| } |
| |
| void cbmem_top_init(void) |
| { |
| /* Fill up EBDA area */ |
| fill_ebda_area(); |
| } |
| |
| /* |
| * +-------------------------+ Top of RAM (aligned) |
| * | System Management Mode | |
| * | code and data | Length: CONFIG_TSEG_SIZE |
| * | (TSEG) | |
| * +-------------------------+ SMM base (aligned) |
| * | | |
| * | Chipset Reserved Memory | |
| * | | |
| * +-------------------------+ top_of_ram (aligned) |
| * | | |
| * | CBMEM Root | |
| * | | |
| * +-------------------------+ |
| * | | |
| * | FSP Reserved Memory | |
| * | | |
| * +-------------------------+ |
| * | | |
| * | Various CBMEM Entries | |
| * | | |
| * +-------------------------+ top_of_stack (8 byte aligned) |
| * | | |
| * | stack (CBMEM Entry) | |
| * | | |
| * +-------------------------+ |
| */ |
| void *cbmem_top(void) |
| { |
| struct ebda_config ebda_cfg; |
| |
| /* |
| * Check if Tseg has been initialized, we will use this as a flag |
| * to check if the MRC is done, and only then continue to read the |
| * PRMMR_BASE MSR. The system hangs if PRMRR_BASE MSR is read before |
| * PRMRR_MASK MSR lock bit is set. |
| */ |
| if (sa_get_tseg_base() == 0) |
| return NULL; |
| |
| retrieve_ebda_object(&ebda_cfg); |
| |
| return (void *)(uintptr_t)ebda_cfg.tolum_base; |
| } |