diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/core/opal-dump.c | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/core/opal-dump.c')
-rw-r--r-- | roms/skiboot/core/opal-dump.c | 582 |
1 files changed, 582 insertions, 0 deletions
diff --git a/roms/skiboot/core/opal-dump.c b/roms/skiboot/core/opal-dump.c new file mode 100644 index 000000000..4f54a3ef1 --- /dev/null +++ b/roms/skiboot/core/opal-dump.c @@ -0,0 +1,582 @@ +/* Copyright 2019 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define pr_fmt(fmt) "DUMP: " fmt + +#include <chip.h> +#include <cpu.h> +#include <device.h> +#include <mem-map.h> +#include <mem_region.h> +#include <mem_region-malloc.h> +#include <opal.h> +#include <opal-dump.h> +#include <opal-internal.h> +#include <sbe-p9.h> +#include <skiboot.h> + +#include <ccan/endian/endian.h> + +#include "hdata/spira.h" + +/* XXX Ideally we should use HDAT provided data (proc_dump_area->thread_size). + * But we are not getting this data durig boot. Hence lets reserve fixed + * memory for architected registers data collection. + */ +#define ARCH_REGS_DATA_SIZE_PER_CHIP (512 * 1024) + +/* Actual address of MDST and MDDT table */ +#define MDST_TABLE_BASE (SKIBOOT_BASE + MDST_TABLE_OFF) +#define MDDT_TABLE_BASE (SKIBOOT_BASE + MDDT_TABLE_OFF) +#define PROC_DUMP_AREA_BASE (SKIBOOT_BASE + PROC_DUMP_AREA_OFF) + +static struct spira_ntuple *ntuple_mdst; +static struct spira_ntuple *ntuple_mddt; +static struct spira_ntuple *ntuple_mdrt; + +static struct mpipl_metadata *mpipl_metadata; + +/* Dump metadata area */ +static struct opal_mpipl_fadump *opal_mpipl_data; +static struct opal_mpipl_fadump *opal_mpipl_cpu_data; + +/* + * Number of tags passed by OPAL to kernel after MPIPL boot. + * Currently it supports below tags: + * - CPU register data area + * - OPAL metadata area address + * - Kernel passed tag during MPIPL registration + * - Post MPIPL boot memory size + */ +#define MAX_OPAL_MPIPL_TAGS 0x04 +static u64 opal_mpipl_tags[MAX_OPAL_MPIPL_TAGS]; +static int opal_mpipl_max_tags = MAX_OPAL_MPIPL_TAGS; + +static u64 opal_dump_addr, opal_dump_size; + +static bool mpipl_enabled; + +static int opal_mpipl_add_entry(u8 region, u64 src, u64 dest, u64 size) +{ + int i; + int mdst_cnt = be16_to_cpu(ntuple_mdst->act_cnt); + int mddt_cnt = be16_to_cpu(ntuple_mddt->act_cnt); + struct mdst_table *mdst; + struct mddt_table *mddt; + + if (mdst_cnt >= MDST_TABLE_SIZE / sizeof(struct mdst_table)) { + prlog(PR_DEBUG, "MDST table is full\n"); + return OPAL_RESOURCE; + } + + if (mddt_cnt >= MDDT_TABLE_SIZE / sizeof(struct mddt_table)) { + prlog(PR_DEBUG, "MDDT table is full\n"); + return OPAL_RESOURCE; + } + + /* Use relocated memory address */ + mdst = (void *)(MDST_TABLE_BASE); + mddt = (void *)(MDDT_TABLE_BASE); + + /* Check for duplicate entry */ + for (i = 0; i < mdst_cnt; i++) { + if (be64_to_cpu(mdst->addr) == (src | HRMOR_BIT)) { + prlog(PR_DEBUG, + "Duplicate source address : 0x%llx", src); + return OPAL_PARAMETER; + } + mdst++; + } + for (i = 0; i < mddt_cnt; i++) { + if (be64_to_cpu(mddt->addr) == (dest | HRMOR_BIT)) { + prlog(PR_DEBUG, + "Duplicate destination address : 0x%llx", dest); + return OPAL_PARAMETER; + } + mddt++; + } + + /* Add OPAL source address to MDST entry */ + mdst->addr = cpu_to_be64(src | HRMOR_BIT); + mdst->data_region = region; + mdst->size = cpu_to_be32(size); + ntuple_mdst->act_cnt = cpu_to_be16(mdst_cnt + 1); + + /* Add OPAL destination address to MDDT entry */ + mddt->addr = cpu_to_be64(dest | HRMOR_BIT); + mddt->data_region = region; + mddt->size = cpu_to_be32(size); + ntuple_mddt->act_cnt = cpu_to_be16(mddt_cnt + 1); + + prlog(PR_TRACE, "Added new entry. src : 0x%llx, dest : 0x%llx," + " size : 0x%llx\n", src, dest, size); + return OPAL_SUCCESS; +} + +/* Remove entry from source (MDST) table */ +static int opal_mpipl_remove_entry_mdst(bool remove_all, u8 region, u64 src) +{ + bool found = false; + int i, j; + int mdst_cnt = be16_to_cpu(ntuple_mdst->act_cnt); + struct mdst_table *tmp_mdst; + struct mdst_table *mdst = (void *)(MDST_TABLE_BASE); + + for (i = 0; i < mdst_cnt;) { + if (mdst->data_region != region) { + mdst++; + i++; + continue; + } + + if (remove_all != true && + be64_to_cpu(mdst->addr) != (src | HRMOR_BIT)) { + mdst++; + i++; + continue; + } + + tmp_mdst = mdst; + memset(tmp_mdst, 0, sizeof(struct mdst_table)); + + for (j = i; j < mdst_cnt - 1; j++) { + memcpy((void *)tmp_mdst, + (void *)(tmp_mdst + 1), sizeof(struct mdst_table)); + tmp_mdst++; + memset(tmp_mdst, 0, sizeof(struct mdst_table)); + } + + mdst_cnt--; + + if (remove_all == false) { + found = true; + break; + } + } /* end - for loop */ + + ntuple_mdst->act_cnt = cpu_to_be16((u16)mdst_cnt); + + if (remove_all == false && found == false) { + prlog(PR_DEBUG, + "Source address [0x%llx] not found in MDST table\n", src); + return OPAL_PARAMETER; + } + + return OPAL_SUCCESS; +} + +/* Remove entry from destination (MDDT) table */ +static int opal_mpipl_remove_entry_mddt(bool remove_all, u8 region, u64 dest) +{ + bool found = false; + int i, j; + int mddt_cnt = be16_to_cpu(ntuple_mddt->act_cnt); + struct mddt_table *tmp_mddt; + struct mddt_table *mddt = (void *)(MDDT_TABLE_BASE); + + for (i = 0; i < mddt_cnt;) { + if (mddt->data_region != region) { + mddt++; + i++; + continue; + } + + if (remove_all != true && + be64_to_cpu(mddt->addr) != (dest | HRMOR_BIT)) { + mddt++; + i++; + continue; + } + + tmp_mddt = mddt; + memset(tmp_mddt, 0, sizeof(struct mddt_table)); + + for (j = i; j < mddt_cnt - 1; j++) { + memcpy((void *)tmp_mddt, + (void *)(tmp_mddt + 1), sizeof(struct mddt_table)); + tmp_mddt++; + memset(tmp_mddt, 0, sizeof(struct mddt_table)); + } + + mddt_cnt--; + + if (remove_all == false) { + found = true; + break; + } + } /* end - for loop */ + + ntuple_mddt->act_cnt = cpu_to_be16((u16)mddt_cnt); + + if (remove_all == false && found == false) { + prlog(PR_DEBUG, + "Dest address [0x%llx] not found in MDDT table\n", dest); + return OPAL_PARAMETER; + } + + return OPAL_SUCCESS; +} + +/* Register for OPAL dump. */ +static void opal_mpipl_register(void) +{ + u64 arch_regs_dest, arch_regs_size; + struct proc_dump_area *proc_dump = (void *)(PROC_DUMP_AREA_BASE); + + /* Add OPAL reservation detail to MDST/MDDT table */ + opal_mpipl_add_entry(DUMP_REGION_OPAL_MEMORY, + SKIBOOT_BASE, opal_dump_addr, opal_dump_size); + + /* Thread size check */ + if (proc_dump->thread_size != 0) { + prlog(PR_INFO, "Thread register entry size is available, " + "but not supported.\n"); + } + + /* Reserve memory used to capture architected register state */ + arch_regs_dest = opal_dump_addr + opal_dump_size; + arch_regs_size = nr_chips() * ARCH_REGS_DATA_SIZE_PER_CHIP; + proc_dump->alloc_addr = cpu_to_be64(arch_regs_dest | HRMOR_BIT); + proc_dump->alloc_size = cpu_to_be32(arch_regs_size); + prlog(PR_NOTICE, "Architected register dest addr : 0x%llx, " + "size : 0x%llx\n", arch_regs_dest, arch_regs_size); +} + +static int payload_mpipl_register(u64 src, u64 dest, u64 size) +{ + if (!opal_addr_valid((void *)src)) { + prlog(PR_DEBUG, "Invalid source address [0x%llx]\n", src); + return OPAL_PARAMETER; + } + + if (!opal_addr_valid((void *)dest)) { + prlog(PR_DEBUG, "Invalid dest address [0x%llx]\n", dest); + return OPAL_PARAMETER; + } + + if (size <= 0) { + prlog(PR_DEBUG, "Invalid size [0x%llx]\n", size); + return OPAL_PARAMETER; + } + + return opal_mpipl_add_entry(DUMP_REGION_KERNEL, src, dest, size); +} + +static int payload_mpipl_unregister(u64 src, u64 dest) +{ + int rc; + + /* Remove src from MDST table */ + rc = opal_mpipl_remove_entry_mdst(false, DUMP_REGION_KERNEL, src); + if (rc) + return rc; + + /* Remove dest from MDDT table */ + rc = opal_mpipl_remove_entry_mddt(false, DUMP_REGION_KERNEL, dest); + return rc; +} + +static int payload_mpipl_unregister_all(void) +{ + opal_mpipl_remove_entry_mdst(true, DUMP_REGION_KERNEL, 0); + opal_mpipl_remove_entry_mddt(true, DUMP_REGION_KERNEL, 0); + + return OPAL_SUCCESS; +} + +static int64_t opal_mpipl_update(enum opal_mpipl_ops ops, + u64 src, u64 dest, u64 size) +{ + int rc; + + switch (ops) { + case OPAL_MPIPL_ADD_RANGE: + rc = payload_mpipl_register(src, dest, size); + if (!rc) + prlog(PR_NOTICE, "Payload registered for MPIPL\n"); + break; + case OPAL_MPIPL_REMOVE_RANGE: + rc = payload_mpipl_unregister(src, dest); + if (!rc) { + prlog(PR_NOTICE, "Payload removed entry from MPIPL." + "[src : 0x%llx, dest : 0x%llx]\n", src, dest); + } + break; + case OPAL_MPIPL_REMOVE_ALL: + rc = payload_mpipl_unregister_all(); + if (!rc) + prlog(PR_NOTICE, "Payload unregistered for MPIPL\n"); + break; + case OPAL_MPIPL_FREE_PRESERVED_MEMORY: + /* Clear tags */ + memset(&opal_mpipl_tags, 0, (sizeof(u64) * MAX_OPAL_MPIPL_TAGS)); + opal_mpipl_max_tags = 0; + /* Release memory */ + free(opal_mpipl_data); + opal_mpipl_data = NULL; + free(opal_mpipl_cpu_data); + opal_mpipl_cpu_data = NULL; + /* Clear MDRT table */ + memset((void *)MDRT_TABLE_BASE, 0, MDRT_TABLE_SIZE); + /* Set MDRT count to max allocated count */ + ntuple_mdrt->act_cnt = cpu_to_be16(MDRT_TABLE_SIZE / sizeof(struct mdrt_table)); + rc = OPAL_SUCCESS; + prlog(PR_NOTICE, "Payload Invalidated MPIPL\n"); + break; + default: + prlog(PR_DEBUG, "Unsupported MPIPL update operation : 0x%x\n", ops); + rc = OPAL_PARAMETER; + break; + } + + return rc; +} + +static int64_t opal_mpipl_register_tag(enum opal_mpipl_tags tag, + uint64_t tag_val) +{ + int rc = OPAL_SUCCESS; + + switch (tag) { + case OPAL_MPIPL_TAG_BOOT_MEM: + if (tag_val <= 0 || tag_val > top_of_ram) { + prlog(PR_DEBUG, "Payload sent invalid boot mem size" + " : 0x%llx\n", tag_val); + rc = OPAL_PARAMETER; + } else { + mpipl_metadata->boot_mem_size = tag_val; + prlog(PR_NOTICE, "Boot mem size : 0x%llx\n", tag_val); + } + break; + case OPAL_MPIPL_TAG_KERNEL: + mpipl_metadata->kernel_tag = tag_val; + prlog(PR_NOTICE, "Payload sent metadata tag : 0x%llx\n", tag_val); + break; + default: + prlog(PR_DEBUG, "Payload sent unsupported tag : 0x%x\n", tag); + rc = OPAL_PARAMETER; + break; + } + return rc; +} + +static uint64_t opal_mpipl_query_tag(enum opal_mpipl_tags tag, __be64 *tag_val) +{ + if (!opal_addr_valid(tag_val)) { + prlog(PR_DEBUG, "Invalid tag address\n"); + return OPAL_PARAMETER; + } + + if (tag >= opal_mpipl_max_tags) + return OPAL_PARAMETER; + + *tag_val = cpu_to_be64(opal_mpipl_tags[tag]); + return OPAL_SUCCESS; +} + +static inline void post_mpipl_get_preserved_tags(void) +{ + if (mpipl_metadata->kernel_tag) + opal_mpipl_tags[OPAL_MPIPL_TAG_KERNEL] = mpipl_metadata->kernel_tag; + if (mpipl_metadata->boot_mem_size) + opal_mpipl_tags[OPAL_MPIPL_TAG_BOOT_MEM] = mpipl_metadata->boot_mem_size; +} + +static void post_mpipl_arch_regs_data(void) +{ + struct proc_dump_area *proc_dump = (void *)(PROC_DUMP_AREA_BASE); + + if (proc_dump->dest_addr == 0) { + prlog(PR_DEBUG, "Invalid CPU registers destination address\n"); + return; + } + + if (proc_dump->act_size == 0) { + prlog(PR_DEBUG, "Invalid CPU registers destination size\n"); + return; + } + + opal_mpipl_cpu_data = zalloc(sizeof(struct opal_mpipl_fadump) + + sizeof(struct opal_mpipl_region)); + if (!opal_mpipl_cpu_data) { + prlog(PR_ERR, "Failed to allocate memory\n"); + return; + } + + /* Fill CPU register details */ + opal_mpipl_cpu_data->version = OPAL_MPIPL_VERSION; + opal_mpipl_cpu_data->cpu_data_version = cpu_to_be32((u32)proc_dump->version); + opal_mpipl_cpu_data->cpu_data_size = proc_dump->thread_size; + opal_mpipl_cpu_data->region_cnt = cpu_to_be32(1); + + opal_mpipl_cpu_data->region[0].src = proc_dump->dest_addr & ~(cpu_to_be64(HRMOR_BIT)); + opal_mpipl_cpu_data->region[0].dest = proc_dump->dest_addr & ~(cpu_to_be64(HRMOR_BIT)); + opal_mpipl_cpu_data->region[0].size = cpu_to_be64(be32_to_cpu(proc_dump->act_size)); + + /* Update tag */ + opal_mpipl_tags[OPAL_MPIPL_TAG_CPU] = (u64)opal_mpipl_cpu_data; +} + +static void post_mpipl_get_opal_data(void) +{ + struct mdrt_table *mdrt = (void *)(MDRT_TABLE_BASE); + int i, j = 0, count = 0; + int mdrt_cnt = be16_to_cpu(ntuple_mdrt->act_cnt); + struct opal_mpipl_region *region; + + /* Count OPAL dump regions */ + for (i = 0; i < mdrt_cnt; i++) { + if (mdrt->data_region == DUMP_REGION_OPAL_MEMORY) + count++; + mdrt++; + } + + if (count == 0) { + prlog(PR_INFO, "OPAL dump is not available\n"); + return; + } + + opal_mpipl_data = zalloc(sizeof(struct opal_mpipl_fadump) + + count * sizeof(struct opal_mpipl_region)); + if (!opal_mpipl_data) { + prlog(PR_ERR, "Failed to allocate memory\n"); + return; + } + + /* Fill OPAL dump details */ + opal_mpipl_data->version = OPAL_MPIPL_VERSION; + opal_mpipl_data->crashing_pir = cpu_to_be32(mpipl_metadata->crashing_pir); + opal_mpipl_data->region_cnt = cpu_to_be32(count); + region = opal_mpipl_data->region; + + mdrt = (void *)(MDRT_TABLE_BASE); + for (i = 0; i < mdrt_cnt; i++) { + if (mdrt->data_region != DUMP_REGION_OPAL_MEMORY) { + mdrt++; + continue; + } + + region[j].src = mdrt->src_addr & ~(cpu_to_be64(HRMOR_BIT)); + region[j].dest = mdrt->dest_addr & ~(cpu_to_be64(HRMOR_BIT)); + region[j].size = cpu_to_be64(be32_to_cpu(mdrt->size)); + + prlog(PR_NOTICE, "OPAL reserved region %d - src : 0x%llx, " + "dest : 0x%llx, size : 0x%llx\n", j, + be64_to_cpu(region[j].src), be64_to_cpu(region[j].dest), + be64_to_cpu(region[j].size)); + + mdrt++; + j++; + if (j == count) + break; + } + + opal_mpipl_tags[OPAL_MPIPL_TAG_OPAL] = (u64)opal_mpipl_data; +} + +void opal_mpipl_save_crashing_pir(void) +{ + if (!is_mpipl_enabled()) + return; + + mpipl_metadata->crashing_pir = this_cpu()->pir; + prlog(PR_NOTICE, "Crashing PIR = 0x%x\n", this_cpu()->pir); +} + +void opal_mpipl_reserve_mem(void) +{ + struct dt_node *opal_node, *dump_node; + u64 arch_regs_dest, arch_regs_size; + + opal_node = dt_find_by_path(dt_root, "ibm,opal"); + if (!opal_node) + return; + + dump_node = dt_find_by_path(opal_node, "dump"); + if (!dump_node) + return; + + /* Calculcate and Reserve OPAL dump destination memory */ + opal_dump_size = SKIBOOT_SIZE + (cpu_max_pir + 1) * STACK_SIZE; + opal_dump_addr = SKIBOOT_BASE + opal_dump_size; + mem_reserve_fw("ibm,firmware-dump", + opal_dump_addr, opal_dump_size); + + /* Reserve memory to capture CPU register data */ + arch_regs_dest = opal_dump_addr + opal_dump_size; + arch_regs_size = nr_chips() * ARCH_REGS_DATA_SIZE_PER_CHIP; + mem_reserve_fw("ibm,firmware-arch-registers", + arch_regs_dest, arch_regs_size); +} + +bool is_mpipl_enabled(void) +{ + return mpipl_enabled; +} + +void opal_mpipl_init(void) +{ + void *mdst_base = (void *)MDST_TABLE_BASE; + void *mddt_base = (void *)MDDT_TABLE_BASE; + struct dt_node *dump_node; + + dump_node = dt_find_by_path(opal_node, "dump"); + if (!dump_node) + return; + + /* Get MDST and MDDT ntuple from SPIRAH */ + ntuple_mdst = &(spirah.ntuples.mdump_src); + ntuple_mddt = &(spirah.ntuples.mdump_dst); + ntuple_mdrt = &(spirah.ntuples.mdump_res); + + /* Get metadata area pointer */ + mpipl_metadata = (void *)(DUMP_METADATA_AREA_BASE); + + if (dt_find_property(dump_node, "mpipl-boot")) { + disable_fast_reboot("MPIPL Boot"); + + post_mpipl_get_preserved_tags(); + post_mpipl_get_opal_data(); + post_mpipl_arch_regs_data(); + } + + /* Clear OPAL metadata area */ + if (sizeof(struct mpipl_metadata) > DUMP_METADATA_AREA_SIZE) { + prlog(PR_ERR, "INSUFFICIENT OPAL METADATA AREA\n"); + prlog(PR_ERR, "INCREASE OPAL MEDTADATA AREA SIZE\n"); + assert(false); + } + memset(mpipl_metadata, 0, sizeof(struct mpipl_metadata)); + + /* Clear MDST and MDDT table */ + memset(mdst_base, 0, MDST_TABLE_SIZE); + ntuple_mdst->act_cnt = 0; + memset(mddt_base, 0, MDDT_TABLE_SIZE); + ntuple_mddt->act_cnt = 0; + + opal_mpipl_register(); + + /* Send OPAL relocated base address to SBE */ + p9_sbe_send_relocated_base(SKIBOOT_BASE); + + /* OPAL API for MPIPL update */ + opal_register(OPAL_MPIPL_UPDATE, opal_mpipl_update, 4); + opal_register(OPAL_MPIPL_REGISTER_TAG, opal_mpipl_register_tag, 2); + opal_register(OPAL_MPIPL_QUERY_TAG, opal_mpipl_query_tag, 2); + + /* Enable MPIPL */ + mpipl_enabled = true; +} |