| /* |
| * This file is part of the coreboot project. |
| * |
| * Copyright (C) 2018 Intel Corp. |
| * |
| * 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 <cbmem.h> |
| #include <chip.h> |
| #include <console/console.h> |
| #include <device/device.h> |
| #include <device/pci.h> |
| #include <fsp/util.h> |
| #include <intelblocks/ebda.h> |
| #include <intelblocks/systemagent.h> |
| #include <soc/pci_devs.h> |
| #include <soc/smm.h> |
| #include <soc/systemagent.h> |
| #include <stdlib.h> |
| |
| 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; |
| } |
| |
| /* Calculate ME Stolen size */ |
| static size_t get_imr_size(void) |
| { |
| size_t imr_size; |
| |
| /* ME stolen memory */ |
| imr_size = MCHBAR32(IMRLIMIT) - MCHBAR32(IMRBASE); |
| |
| return imr_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_icelake_config *config) |
| { |
| uintptr_t prmrr_base = dram_base; |
| size_t prmrr_size; |
| |
| prmrr_size = config->PrmrrSize; |
| |
| /* Allocate PRMRR memory for C6DRAM */ |
| if (!prmrr_size) { |
| if (config->enable_c6dram) |
| prmrr_size = 1*MiB; |
| else |
| 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); |
| else |
| prmrr_base = ALIGN_DOWN(prmrr_base, 16*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 (IS_ENABLED(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, Me stolen memory 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_icelake_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_imr_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 |
| * | ME Stolen Memory | |
| * +--------------------------+ ME Stolen |
| * | 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; |
| } |