diff options
Diffstat (limited to 'roms/skiboot/hw/fsp/fsp-dump.c')
-rw-r--r-- | roms/skiboot/hw/fsp/fsp-dump.c | 916 |
1 files changed, 916 insertions, 0 deletions
diff --git a/roms/skiboot/hw/fsp/fsp-dump.c b/roms/skiboot/hw/fsp/fsp-dump.c new file mode 100644 index 000000000..96cb45e6f --- /dev/null +++ b/roms/skiboot/hw/fsp/fsp-dump.c @@ -0,0 +1,916 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +/* + * Dump support: + * We get dump notification from different sources: + * - During system initialization via HDAT + * - During FSP reset/reload (FipS dump) + * - Dump available notification MBOX command (0xCE, 0x78, 0x00) + * + * To avoid complications, we keep list of dumps in a list and fetch + * them serially. + * + * Dump retrieve process: + * - Once we get notification from FSP we enqueue the dump ID and notify + * Linux via OPAL event notification. + * - Linux reads dump info and allocates required memory to fetch the dump + * and makes dump read call. + * - Sapphire fetches dump data from FSP. + * - Linux writes dump to disk and sends acknowledgement. + * - Sapphire acknowledges FSP. + * + * Copyright 2013-2015 IBM Corp. + */ + +#include <fsp.h> +#include <psi.h> +#include <lock.h> +#include <device.h> +#include <skiboot.h> +#include <errorlog.h> +#include <opal-api.h> + +/* + * Max outstanding dumps to retrieve + * + * Note: + * Dumps are serialized. We don't get notification for second + * dump of given type until we acknowledge first one. But we + * may get notification for different dump type. And our dump + * retrieval code is serialized. Hence we use list to keep + * track of outstanding dumps to be retrieved. + */ +#define MAX_DUMP_RECORD 0x04 + +/* Max retry */ +#define FIPS_DUMP_MAX_RETRY 0x03 + +/* Dump type */ +#define DUMP_TYPE_FSP 0x01 +#define DUMP_TYPE_SYS 0x02 +#define DUMP_TYPE_SMA 0x03 + +/* Dump fetch size */ +#define DUMP_FETCH_SIZE_FSP 0x500000 +#define DUMP_FETCH_SIZE_SYS 0x400000 +#define DUMP_FETCH_SIZE_RES 0x200000 + +/* Params for Fips dump */ +#define FSP_DUMP_TOOL_TYPE "SYS " +#define FSP_DUMP_CLIENT_ID "SAPPHIRE_CLIENT" + +enum dump_state { + DUMP_STATE_ABSENT, /* No FSP dump */ + DUMP_STATE_NONE, /* No dump to retrieve */ + DUMP_STATE_NOTIFY, /* Notified Linux */ + DUMP_STATE_FETCHING, /* Dump retrieval is in progress */ + DUMP_STATE_FETCH, /* Dump retrieve complete */ + DUMP_STATE_PARTIAL, /* Partial read */ + DUMP_STATE_ABORTING, /* Aborting due to kexec */ +}; + +/* Pending dump list */ +struct dump_record { + uint8_t type; + uint32_t id; + uint32_t size; + struct list_node link; +}; + +/* List definations */ +static LIST_HEAD(dump_pending); +static LIST_HEAD(dump_free); + +/* Dump retrieve state */ +static enum dump_state dump_state = DUMP_STATE_NONE; + +/* Dump buffer SG list */ +static struct opal_sg_list *dump_data; +static struct dump_record *dump_entry; +static int64_t dump_offset; +static size_t fetch_remain; + +/* FipS dump retry count */ +static int retry_cnt; + +/* Protect list and dump retrieve state */ +static struct lock dump_lock = LOCK_UNLOCKED; + +/* Forward declaration */ +static int64_t fsp_opal_dump_init(uint8_t dump_type); +static int64_t fsp_dump_read(void); + +DEFINE_LOG_ENTRY(OPAL_RC_DUMP_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_DUMP, + OPAL_PLATFORM_FIRMWARE, + OPAL_PREDICTIVE_ERR_FAULT_RECTIFY_REBOOT, + OPAL_NA); + +DEFINE_LOG_ENTRY(OPAL_RC_DUMP_LIST, OPAL_PLATFORM_ERR_EVT, OPAL_DUMP, + OPAL_PLATFORM_FIRMWARE, + OPAL_INFO, + OPAL_NA); + +DEFINE_LOG_ENTRY(OPAL_RC_DUMP_ACK, OPAL_PLATFORM_ERR_EVT, OPAL_DUMP, + OPAL_PLATFORM_FIRMWARE, OPAL_INFO, + OPAL_NA); + +/* + * Helper functions + */ +static inline void update_dump_state(enum dump_state state) +{ + dump_state = state; +} + +static int64_t check_dump_state(void) +{ + switch (dump_state) { + case DUMP_STATE_ABSENT: + return OPAL_HARDWARE; + case DUMP_STATE_NONE: + case DUMP_STATE_NOTIFY: + /* During dump fetch, notify is wrong state */ + return OPAL_WRONG_STATE; + case DUMP_STATE_FETCHING: + case DUMP_STATE_ABORTING: + return OPAL_BUSY_EVENT; + case DUMP_STATE_FETCH: + return OPAL_SUCCESS; + case DUMP_STATE_PARTIAL: + return OPAL_PARTIAL; + } + return OPAL_SUCCESS; +} + +static inline void dump_tce_map(uint32_t tce_offset, + void *buffer, uint32_t size) +{ + uint32_t tlen = ALIGN_UP(size, TCE_PSIZE); + fsp_tce_map(PSI_DMA_DUMP_DATA + tce_offset, buffer, tlen); +} + +static inline void dump_tce_unmap(uint32_t size) +{ + fsp_tce_unmap(PSI_DMA_DUMP_DATA, size); +} + +/* + * Returns Data set ID for the given dump type + */ +static inline uint16_t get_dump_data_set_id(uint8_t type) +{ + switch (type) { + case DUMP_TYPE_FSP: + return FSP_DATASET_SP_DUMP; + case DUMP_TYPE_SYS: + return FSP_DATASET_HW_DUMP; + default: + break; + } + return OPAL_INTERNAL_ERROR; +} + +/* + * Returns max data we can fetch from FSP fetch data call + */ +static inline int64_t get_dump_fetch_max_size(uint8_t type) +{ + switch (type) { + case DUMP_TYPE_FSP: + return DUMP_FETCH_SIZE_FSP; + case DUMP_TYPE_SYS: + return DUMP_FETCH_SIZE_SYS; + default: + break; + } + return OPAL_INTERNAL_ERROR; +} + +/* + * Get dump record from pending list + */ +static inline struct dump_record *get_dump_rec_from_list(uint32_t id) +{ + struct dump_record *record; + + list_for_each(&dump_pending, record, link) { + if (record->id == id) + return record; + } + return NULL; +} + +/* + * New dump available notification to Linux + */ +static void update_opal_dump_notify(void) +{ + /* + * Wait until current dump retrieval to complete + * before notifying again. + */ + if (dump_state != DUMP_STATE_NONE) + return; + + /* More dump's to retrieve */ + if (!list_empty(&dump_pending)) { + update_dump_state(DUMP_STATE_NOTIFY); + opal_update_pending_evt(OPAL_EVENT_DUMP_AVAIL, + OPAL_EVENT_DUMP_AVAIL); + } +} + +static int64_t remove_dump_id_from_list(uint32_t dump_id) +{ + struct dump_record *record, *nxt_record; + int rc = OPAL_SUCCESS; + bool found = false; + + /* Remove record from pending list */ + list_for_each_safe(&dump_pending, record, nxt_record, link) { + if (record->id != dump_id) + continue; + + found = true; + list_del(&record->link); + list_add(&dump_free, &record->link); + break; + } + + /* + * Continue update_opal_dump_notify even if it fails + * to remove ID. So that we can resend notification + * for the same dump ID to Linux. + */ + if (!found) { /* List corrupted? */ + log_simple_error(&e_info(OPAL_RC_DUMP_LIST), + "DUMP: ID 0x%x not found in list!\n", + dump_id); + rc = OPAL_PARAMETER; + } + + /* Update state */ + update_dump_state(DUMP_STATE_NONE); + /* Notify next available dump to retrieve */ + update_opal_dump_notify(); + + return rc; +} + +static int64_t add_dump_id_to_list(uint8_t dump_type, + uint32_t dump_id, uint32_t dump_size) +{ + struct dump_record *record; + int rc = OPAL_SUCCESS; + + lock(&dump_lock); + + rc = check_dump_state(); + if (rc == OPAL_HARDWARE) + goto out; + + /* List is full ? */ + if (list_empty(&dump_free)) { + printf("DUMP: Dump ID 0x%x is not queued.\n", dump_id); + rc = OPAL_RESOURCE; + goto out; + } + + /* Already queued? */ + record = get_dump_rec_from_list(dump_id); + if (record) { + rc = OPAL_SUCCESS; + goto out; + } + + /* Add to list */ + record = list_pop(&dump_free, struct dump_record, link); + record->type = dump_type; + record->id = dump_id; + record->size = dump_size; + list_add_tail(&dump_pending, &record->link); + + /* OPAL notification */ + update_opal_dump_notify(); + rc = OPAL_SUCCESS; + +out: + unlock(&dump_lock); + return rc; +} + +static void dump_init_complete(struct fsp_msg *msg) +{ + uint8_t status = (msg->resp->word1 >> 8) & 0xff; + + printf("DUMP: FipS dump init status = 0x%x\n", status); + fsp_freemsg(msg); + + switch (status) { + case FSP_STATUS_SUCCESS: + printf("DUMP: Initiated FipS dump.\n"); + break; + case FSP_STATUS_BUSY: /* Retry, if FSP is busy */ + if (retry_cnt++ < FIPS_DUMP_MAX_RETRY) + if (fsp_opal_dump_init(DUMP_TYPE_FSP) == OPAL_SUCCESS) + return; + break; + default: + break; + } + /* Reset max retry count */ + retry_cnt = 0; +} + +/* + * Initiate new FipS dump + */ +static int64_t fsp_opal_dump_init(uint8_t dump_type) +{ + struct fsp_msg *msg; + int rc = OPAL_SUCCESS; + uint32_t *tool_type = (void *)FSP_DUMP_TOOL_TYPE; + uint32_t *client_id = (void *)FSP_DUMP_CLIENT_ID; + + /* Only FipS dump generate request is supported */ + if (dump_type != DUMP_TYPE_FSP) + return OPAL_PARAMETER; + + msg = fsp_mkmsg(FSP_CMD_FSP_DUMP_INIT, 6, *tool_type, + sizeof(FSP_DUMP_CLIENT_ID), *client_id, + *(client_id + 1), *(client_id + 2), *(client_id + 3)); + + if (!msg) { + log_simple_error(&e_info(OPAL_RC_DUMP_INIT), + "DUMP: Message allocation failed.\n"); + rc = OPAL_INTERNAL_ERROR; + } else if (fsp_queue_msg(msg, dump_init_complete)) { + log_simple_error(&e_info(OPAL_RC_DUMP_INIT), + "DUMP: Failed to queue FipS dump init request.\n"); + fsp_freemsg(msg); + rc = OPAL_INTERNAL_ERROR; + } + + return rc; +} + +/* + * OPAL interface to send dump information to Linux. + */ +static int64_t fsp_opal_dump_info2(__be32 *dump_id, __be32 *dump_size, + __be32 *dump_type) +{ + struct dump_record *record; + int rc = OPAL_SUCCESS; + + lock(&dump_lock); + + /* Clear notification */ + opal_update_pending_evt(OPAL_EVENT_DUMP_AVAIL, 0); + + record = list_top(&dump_pending, struct dump_record, link); + if (!record) { /* List corrupted? */ + update_dump_state(DUMP_STATE_NONE); + rc = OPAL_INTERNAL_ERROR; + goto out; + } + *dump_id = cpu_to_be32(record->id); + *dump_size = cpu_to_be32(record->size); + *dump_type = cpu_to_be32(record->type); + +out: + unlock(&dump_lock); + return rc; +} + +static int64_t fsp_opal_dump_info(__be32 *dump_id, __be32 *dump_size) +{ + __be32 dump_type; + return fsp_opal_dump_info2(dump_id, dump_size, &dump_type); +} + +static int64_t validate_dump_sglist(struct opal_sg_list *list, + int64_t *size) +{ + struct opal_sg_list *sg; + struct opal_sg_entry *prev_entry, *entry; + int length, num_entries, i; + + prev_entry = NULL; + *size = 0; + for (sg = list; sg; sg = (struct opal_sg_list*)be64_to_cpu(sg->next)) { + length = be64_to_cpu(sg->length) - 16; + num_entries = length / sizeof(struct opal_sg_entry); + if (num_entries <= 0) + return OPAL_PARAMETER; + + for (i = 0; i < num_entries; i++) { + entry = &sg->entry[i]; + *size += be64_to_cpu(entry->length); + + /* All entries must be aligned */ + if (((uint64_t)be64_to_cpu(entry->data)) & 0xfff) + return OPAL_PARAMETER; + + /* All non-terminal entries size must be aligned */ + if (prev_entry && (be64_to_cpu(prev_entry->length) & 0xfff)) + return OPAL_PARAMETER; + + prev_entry = entry; + } + } + return OPAL_SUCCESS; +} + +/* + * Map dump buffer to TCE buffer + */ +static int64_t map_dump_buffer(void) +{ + struct opal_sg_list *sg; + struct opal_sg_entry *entry; + int64_t fetch_max; + int length, num_entries, i; + int buf_off, fetch_off, tce_off, sg_off; + bool last = false; + + /* FSP fetch max size */ + fetch_max = get_dump_fetch_max_size(dump_entry->type); + if (fetch_max > (dump_entry->size - dump_offset)) + fetch_remain = dump_entry->size - dump_offset; + else + fetch_remain = fetch_max; + + /* offsets */ + fetch_off = fetch_remain; + tce_off = sg_off = 0; + + for (sg = dump_data; sg; sg = (struct opal_sg_list*)be64_to_cpu(sg->next)) { + num_entries = (be64_to_cpu(sg->length) - 16) / + sizeof(struct opal_sg_entry); + if (num_entries <= 0) + return OPAL_PARAMETER; + + for (i = 0; i < num_entries; i++) { + entry = &sg->entry[i]; + + /* Continue until we get offset */ + if ((sg_off + be64_to_cpu(entry->length)) < dump_offset) { + sg_off += be64_to_cpu(entry->length); + continue; + } + + /* + * SG list entry size can be more than 4k. + * Map only required pages, instead of + * mapping entire entry. + */ + if (!tce_off) { + buf_off = (dump_offset - sg_off) & ~0xfff; + length = be64_to_cpu(entry->length) - buf_off; + } else { + buf_off = 0; + length = be64_to_cpu(entry->length); + } + + /* Adjust length for last mapping */ + if (fetch_off <= length) { + length = fetch_off; + last = true; + } + + /* Adjust offset */ + sg_off += be64_to_cpu(entry->length); + fetch_off -= length; + + /* TCE mapping */ + dump_tce_map(tce_off, (void*)(be64_to_cpu(entry->data) + buf_off), length); + tce_off += length; + + /* TCE mapping complete */ + if (last) + return OPAL_SUCCESS; + } + } /* outer loop */ + return OPAL_PARAMETER; +} + +static void dump_read_complete(struct fsp_msg *msg) +{ + void *buffer; + size_t length, offset; + int rc; + uint32_t dump_id; + uint16_t id; + uint8_t flags, status; + bool compl = false; + + status = (msg->resp->word1 >> 8) & 0xff; + flags = (fsp_msg_get_data_word(msg, 0) >> 16) & 0xff; + id = fsp_msg_get_data_word(msg, 0) & 0xffff; + dump_id = fsp_msg_get_data_word(msg, 1); + offset = fsp_msg_get_data_word(msg->resp, 1); + length = fsp_msg_get_data_word(msg->resp, 2); + + fsp_freemsg(msg); + + lock(&dump_lock); + + if (dump_state == DUMP_STATE_ABORTING) { + printf("DUMP: Fetch dump aborted, ID = 0x%x\n", dump_id); + dump_tce_unmap(PSI_DMA_DUMP_DATA_SIZE); + update_dump_state(DUMP_STATE_NONE); + goto bail; + } + + switch (status) { + case FSP_STATUS_SUCCESS: /* Fetch next dump block */ + if (dump_offset < dump_entry->size) { + dump_tce_unmap(PSI_DMA_DUMP_DATA_SIZE); + rc = fsp_dump_read(); + if (rc == OPAL_SUCCESS) + goto bail; + } else { /* Dump read complete */ + compl = true; + } + break; + case FSP_STATUS_MORE_DATA: /* More data to read */ + offset += length; + buffer = (void *)PSI_DMA_DUMP_DATA + offset; + fetch_remain -= length; + + rc = fsp_fetch_data_queue(flags, id, dump_id, offset, buffer, + &fetch_remain, dump_read_complete); + if (rc == OPAL_SUCCESS) + goto bail; + break; + default: + break; + } + + dump_tce_unmap(PSI_DMA_DUMP_DATA_SIZE); + + /* Update state */ + if (compl) { + printf("DUMP: Fetch dump success. ID = 0x%x\n", dump_id); + update_dump_state(DUMP_STATE_FETCH); + } else { + printf("DUMP: Fetch dump partial. ID = 0x%x\n", dump_id); + update_dump_state(DUMP_STATE_PARTIAL); + } + bail: + unlock(&dump_lock); +} + +/* + * Fetch dump data from FSP + */ +static int64_t fsp_dump_read(void) +{ + int64_t rc; + uint16_t data_set; + uint8_t flags = 0x00; + + /* Get data set ID */ + data_set = get_dump_data_set_id(dump_entry->type); + + /* Map TCE buffer */ + rc = map_dump_buffer(); + if (rc != OPAL_SUCCESS) { + printf("DUMP: TCE mapping failed\n"); + return rc; + } + + printf("DUMP: Fetch Dump. ID = %02x, sub ID = %08x, len = %ld\n", + data_set, dump_entry->id, fetch_remain); + + /* Fetch data */ + rc = fsp_fetch_data_queue(flags, data_set, dump_entry->id, + dump_offset, (void *)PSI_DMA_DUMP_DATA, + &fetch_remain, dump_read_complete); + + /* Adjust dump fetch offset */ + dump_offset += fetch_remain; + + return rc; +} + +static int64_t fsp_opal_dump_read(uint32_t dump_id, + struct opal_sg_list *list) +{ + struct dump_record *record; + int64_t rc, size; + + lock(&dump_lock); + + /* Check state */ + if (dump_state != DUMP_STATE_NOTIFY) { + rc = check_dump_state(); + goto out; + } + + /* Validate dump ID */ + record = get_dump_rec_from_list(dump_id); + if (!record) { /* List corrupted? */ + rc = OPAL_INTERNAL_ERROR; + goto out; + } + + /* Validate dump buffer and size */ + rc = validate_dump_sglist(list, &size); + if (rc != OPAL_SUCCESS) { + printf("DUMP: SG list validation failed\n"); + goto out; + } + + if (size < record->size) { /* Insuffient buffer */ + printf("DUMP: Insufficient buffer\n"); + rc = OPAL_PARAMETER; + goto out; + } + + /* Update state */ + update_dump_state(DUMP_STATE_FETCHING); + + /* Fetch dump data */ + dump_entry = record; + dump_data = list; + dump_offset = 0; + rc = fsp_dump_read(); + if (rc != OPAL_SUCCESS) + goto out; + + /* Check status after initiating fetch data */ + rc = check_dump_state(); + +out: + unlock(&dump_lock); + return rc; +} + +static void dump_ack_complete(struct fsp_msg *msg) +{ + uint8_t status = (msg->resp->word1 >> 8) & 0xff; + + if (status) + log_simple_error(&e_info(OPAL_RC_DUMP_ACK), + "DUMP: ACK failed for ID: 0x%x\n", + fsp_msg_get_data_word(msg, 0)); + else + printf("DUMP: ACKed dump ID: 0x%x\n", fsp_msg_get_data_word(msg, 0)); + + fsp_freemsg(msg); +} + +/* + * Acknowledge dump + */ +static int64_t fsp_opal_dump_ack(uint32_t dump_id) +{ + struct dump_record *record; + struct fsp_msg *msg; + int rc; + uint32_t cmd; + uint8_t dump_type = 0; + + /* Get dump type */ + lock(&dump_lock); + record = get_dump_rec_from_list(dump_id); + if (record) + dump_type = record->type; + + /* + * Next available dump in pending list will be of different + * type. Hence we don't need to wait for ack complete. + * + * Note: + * This allows us to proceed even if we fail to ACK. + * In the worst case we may get notification for the + * same dump again, which is probably better than + * looping forever. + */ + rc = remove_dump_id_from_list(dump_id); + if (rc != OPAL_SUCCESS) /* Invalid dump id */ + goto out; + + /* Adjust mod value */ + cmd = FSP_CMD_ACK_DUMP | (dump_type & 0xff); + msg = fsp_mkmsg(cmd, 1, dump_id); + if (!msg) { + log_simple_error(&e_info(OPAL_RC_DUMP_ACK), + "DUMP: Message allocation failed.!\n"); + rc = OPAL_INTERNAL_ERROR; + } else if (fsp_queue_msg(msg, dump_ack_complete)) { + log_simple_error(&e_info(OPAL_RC_DUMP_ACK), + "DUMP: Failed to queue dump ack message.\n"); + fsp_freemsg(msg); + rc = OPAL_INTERNAL_ERROR; + } +out: + unlock(&dump_lock); + return rc; +} + +/* Resend dump available notification */ +static int64_t fsp_opal_dump_resend_notification(void) +{ + lock(&dump_lock); + + if (dump_state != DUMP_STATE_ABSENT) + update_dump_state(DUMP_STATE_NONE); + + update_opal_dump_notify(); + + unlock(&dump_lock); + + return OPAL_SUCCESS; +} + +/* + * Handle FSP R/R event. + */ +static bool fsp_dump_retrieve_rr(uint32_t cmd_sub_mod, + struct fsp_msg *msg __unused) +{ + switch (cmd_sub_mod) { + case FSP_RESET_START: + lock(&dump_lock); + /* Reset dump state */ + if (dump_state == DUMP_STATE_FETCHING) + update_dump_state(DUMP_STATE_ABORTING); + unlock(&dump_lock); + return true; + case FSP_RELOAD_COMPLETE: + lock(&dump_lock); + + /* Reset TCE mapping */ + dump_tce_unmap(PSI_DMA_DUMP_DATA_SIZE); + + /* Reset dump state */ + update_dump_state(DUMP_STATE_NONE); + + /* + * For now keeping R/R handler simple. In the worst case + * we may endup resending dump available notification for + * same dump ID twice to Linux. + */ + update_opal_dump_notify(); + unlock(&dump_lock); + return true; + } + return false; +} + +/* + * Handle host kexec'ing scenarios + */ +static bool opal_kexec_dump_notify(void *data __unused) +{ + bool ready = true; + + lock(&dump_lock); + + /* Dump retrieve is in progress? */ + if (dump_state == DUMP_STATE_FETCHING) + dump_state = DUMP_STATE_ABORTING; + + /* Not yet safe to kexec */ + if (dump_state == DUMP_STATE_ABORTING) + ready = false; + + unlock(&dump_lock); + + return ready; +} + +/* + * FipS dump notification + */ +void fsp_fips_dump_notify(uint32_t dump_id, uint32_t dump_size) +{ + printf("DUMP: FipS dump available. ID = 0x%x [size: %d bytes]\n", + dump_id, dump_size); + add_dump_id_to_list(DUMP_TYPE_FSP, dump_id, dump_size); +} + +/* + * System/Platform dump notification + */ +static bool fsp_sys_dump_notify(uint32_t cmd_sub_mod, struct fsp_msg *msg) +{ + /* + * Though spec says mod 00 is deprecated we still + * seems to get mod 00 notification (at least on + * P7 machine). + */ + if (cmd_sub_mod != FSP_RSP_SYS_DUMP && + cmd_sub_mod != FSP_RSP_SYS_DUMP_OLD) + return false; + + printf("DUMP: Platform dump available. ID = 0x%x [size: %d bytes]\n", + fsp_msg_get_data_word(msg, 0), fsp_msg_get_data_word(msg, 1)); + + add_dump_id_to_list(DUMP_TYPE_SYS, + fsp_msg_get_data_word(msg, 0), + fsp_msg_get_data_word(msg, 1)); + return true; +} + +/* + * If platform dump available during IPL time, then we + * get notification via HDAT. Check for DT for the dump + * presence. + */ +static void check_ipl_sys_dump(void) +{ + struct dt_node *dump_node, *opal_node; + uint32_t dump_id, dump_size; + + if (proc_gen >= proc_gen_p9) { + 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) { + if (dt_find_property(dump_node, "mpipl-boot")) + return; + } + } + + dump_node = dt_find_by_path(dt_root, "ipl-params/platform-dump"); + if (!dump_node) + return; + + if (!dt_find_property(dump_node, "dump-id")) + return; + + dump_id = dt_prop_get_u32(dump_node, "dump-id"); + dump_size = (uint32_t)dt_prop_get_u64(dump_node, "total-size"); + + printf("DUMP: Platform dump present during IPL.\n"); + printf(" ID = 0x%x [size: %d bytes]\n", dump_id, dump_size); + + add_dump_id_to_list(DUMP_TYPE_SYS, dump_id, dump_size); +} + +/* + * Allocate and initialize dump list + */ +static int init_dump_free_list(void) +{ + struct dump_record *entry; + int i; + + entry = zalloc(sizeof(struct dump_record) * MAX_DUMP_RECORD); + if (!entry) { + log_simple_error(&e_info(OPAL_RC_DUMP_INIT), + "DUMP: Out of memory\n"); + return -ENOMEM; + } + + for (i = 0; i < MAX_DUMP_RECORD; i++) { + list_add_tail(&dump_free, &entry->link); + entry++; + } + return 0; +} + +static struct fsp_client fsp_sys_dump_client = { + .message = fsp_sys_dump_notify, +}; + +static struct fsp_client fsp_dump_client_rr = { + .message = fsp_dump_retrieve_rr, +}; + +void fsp_dump_init(void) +{ + if (!fsp_present()) { + update_dump_state(DUMP_STATE_ABSENT); + return; + } + + /* Initialize list */ + if (init_dump_free_list() != 0) { + update_dump_state(DUMP_STATE_ABSENT); + return; + } + + /* Register for Class CE */ + fsp_register_client(&fsp_sys_dump_client, FSP_MCLASS_SERVICE); + /* Register for Class AA (FSP R/R) */ + fsp_register_client(&fsp_dump_client_rr, FSP_MCLASS_RR_EVENT); + + /* Register for sync on host reboot call */ + opal_add_host_sync_notifier(opal_kexec_dump_notify, NULL); + + /* OPAL interface */ + opal_register(OPAL_DUMP_INIT, fsp_opal_dump_init, 1); + opal_register(OPAL_DUMP_INFO, fsp_opal_dump_info, 2); + opal_register(OPAL_DUMP_INFO2, fsp_opal_dump_info2, 3); + opal_register(OPAL_DUMP_READ, fsp_opal_dump_read, 2); + opal_register(OPAL_DUMP_ACK, fsp_opal_dump_ack, 1); + opal_register(OPAL_DUMP_RESEND, fsp_opal_dump_resend_notification, 0); + + /* Check for platform dump presence during IPL time */ + check_ipl_sys_dump(); +} |