aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/core/opal-dump.c
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/core/opal-dump.c
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/core/opal-dump.c')
-rw-r--r--roms/skiboot/core/opal-dump.c582
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;
+}