| // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later |
| // Copyright 2022 IBM Corp. |
| |
| #define pr_fmt(fmt) "PLDM: " fmt |
| |
| #include <cpu.h> |
| #include <opal.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <timebase.h> |
| #include <inttypes.h> |
| #include <libpldm/entity.h> |
| #include <libpldm/pdr.h> |
| #include <libpldm/state_set.h> |
| #include <libpldm/platform.h> |
| #include "pldm.h" |
| |
| #define NO_MORE_PDR_HANDLES 0 |
| |
| static pldm_pdr *pdrs_repo; |
| static bool pdr_ready; |
| |
| struct pldm_pdrs { |
| struct pldm_tx_data *tx; |
| uint32_t record_hndl; |
| bool done; |
| int rc; |
| }; |
| |
| struct pldm_pdrs *pdrs; |
| /* assign specific sensor/effecter IDs */ |
| #define PLDM_SENSOR_SE_ID_RANGE_START 0x6666 |
| static int sensor_effecter_id = PLDM_SENSOR_SE_ID_RANGE_START; |
| |
| static void pdr_init_complete(bool success) |
| { |
| /* Read not successful, error out and free the buffer */ |
| if (!success) { |
| pdr_ready = false; |
| |
| if (pdrs_repo) |
| pldm_pdr_destroy(pdrs_repo); |
| return; |
| } |
| |
| /* Mark ready */ |
| pdr_ready = true; |
| } |
| |
| /* |
| * Find PDR record by record handle. |
| */ |
| int pldm_platform_pdr_find_record(uint32_t record_handle, |
| uint8_t **pdr_data, |
| uint32_t *pdr_data_size, |
| uint32_t *next_record_handle) |
| { |
| const pldm_pdr_record *pdr_record; |
| |
| if (!pdr_ready) |
| return OPAL_HARDWARE; |
| |
| pdr_record = pldm_pdr_find_record(pdrs_repo, |
| record_handle, |
| pdr_data, |
| pdr_data_size, |
| next_record_handle); |
| |
| if (!pdr_record) |
| return OPAL_PARAMETER; |
| |
| return OPAL_SUCCESS; |
| } |
| |
| /* |
| * Search the matching record and return the sensor id. |
| * PDR type = PLDM_STATE_SENSOR_PDR |
| */ |
| static int find_sensor_id_by_state_set_Id(uint16_t entity_type, |
| uint16_t state_set_id, |
| uint16_t *sensor_id, |
| uint16_t terminus_handle) |
| { |
| struct state_sensor_possible_states *possible_states; |
| struct pldm_state_sensor_pdr *state_sensor_pdr; |
| const pldm_pdr_record *record = NULL; |
| uint8_t *outData = NULL; |
| uint32_t size; |
| |
| do { |
| /* Find (first) PDR record by PLDM_STATE_SENSOR_PDR type |
| * if record not NULL, then search will begin from this |
| * record's next record |
| */ |
| record = pldm_pdr_find_record_by_type( |
| pdrs_repo, /* PDR repo handle */ |
| PLDM_STATE_SENSOR_PDR, |
| record, /* PDR record handle */ |
| &outData, &size); |
| |
| if (record) { |
| state_sensor_pdr = (struct pldm_state_sensor_pdr *) outData; |
| |
| *sensor_id = le16_to_cpu(state_sensor_pdr->sensor_id); |
| |
| possible_states = (struct state_sensor_possible_states *) |
| state_sensor_pdr->possible_states; |
| |
| if ((le16_to_cpu(state_sensor_pdr->entity_type) == entity_type) && |
| (le16_to_cpu(state_sensor_pdr->terminus_handle) == terminus_handle) && |
| (le16_to_cpu(possible_states->state_set_id) == state_set_id)) |
| return OPAL_SUCCESS; |
| } |
| |
| } while (record); |
| |
| return OPAL_PARAMETER; |
| } |
| |
| /* |
| * Search the matching record and return the effecter id. |
| * PDR type = PLDM_STATE_EFFECTER_PDR |
| */ |
| static int find_effecter_id_by_state_set_Id(uint16_t entity_type, |
| uint16_t state_set_id, |
| uint16_t *effecter_id, |
| uint16_t terminus_handle) |
| { |
| struct state_effecter_possible_states *possible_states; |
| struct pldm_state_effecter_pdr *state_effecter_pdr; |
| const pldm_pdr_record *record = NULL; |
| uint8_t *outData = NULL; |
| uint32_t size; |
| |
| do { |
| /* Find (first) PDR record by PLDM_STATE_EFFECTER_PDR type |
| * if record not NULL, then search will begin from this |
| * record's next record |
| */ |
| record = pldm_pdr_find_record_by_type( |
| pdrs_repo, /* PDR repo handle */ |
| PLDM_STATE_EFFECTER_PDR, |
| record, /* PDR record handle */ |
| &outData, &size); |
| |
| if (record) { |
| state_effecter_pdr = (struct pldm_state_effecter_pdr *) outData; |
| |
| *effecter_id = le16_to_cpu(state_effecter_pdr->effecter_id); |
| |
| possible_states = (struct state_effecter_possible_states *) |
| state_effecter_pdr->possible_states; |
| |
| if ((le16_to_cpu(state_effecter_pdr->entity_type) == entity_type) && |
| (le16_to_cpu(state_effecter_pdr->terminus_handle) == terminus_handle) && |
| (le16_to_cpu(possible_states->state_set_id) == state_set_id)) |
| return OPAL_SUCCESS; |
| } |
| |
| } while (record); |
| |
| return OPAL_PARAMETER; |
| } |
| |
| struct set_effecter_state_response { |
| uint8_t completion_code; |
| }; |
| |
| /* |
| * Create and send a PLDM request message for SetStateEffecterStates. |
| */ |
| static int set_state_effecter_states_req(uint16_t effecter_id, |
| set_effecter_state_field *field, |
| bool no_timeout) |
| { |
| size_t data_size = PLDM_MSG_SIZE(struct pldm_set_state_effecter_states_req); |
| struct set_effecter_state_response response; |
| size_t response_len, payload_len; |
| struct pldm_tx_data *tx = NULL; |
| void *response_msg; |
| int rc; |
| |
| struct pldm_set_state_effecter_states_req states_req = { |
| .effecter_id = effecter_id, |
| .comp_effecter_count = 1 |
| }; |
| |
| /* Encode the state effecter states request */ |
| tx = zalloc(sizeof(struct pldm_tx_data) + data_size); |
| if (!tx) |
| return OPAL_NO_MEM; |
| tx->data_size = data_size; |
| |
| rc = encode_set_state_effecter_states_req( |
| DEFAULT_INSTANCE_ID, |
| states_req.effecter_id, |
| states_req.comp_effecter_count, |
| field, |
| (struct pldm_msg *)tx->data); |
| if (rc != PLDM_SUCCESS) { |
| prlog(PR_ERR, "Encode SetStateEffecter Error, rc: %d\n", |
| rc); |
| free(tx); |
| return OPAL_PARAMETER; |
| } |
| |
| /* Send and get the response message bytes. |
| * It may happen that for some commands, the responder does not |
| * have time to respond. |
| */ |
| if (no_timeout) { |
| rc = pldm_mctp_message_tx(tx); |
| if (rc) |
| prlog(PR_ERR, "Failed to send SetStateEffecter request, rc = %d\n", rc); |
| free(tx); |
| return rc; |
| } |
| |
| /* Send and get the response message bytes */ |
| rc = pldm_requester_queue_and_wait(tx, &response_msg, &response_len); |
| if (rc) { |
| prlog(PR_ERR, "Communication Error, req: SetStateEffecter, rc: %d\n", rc); |
| free(tx); |
| return rc; |
| } |
| |
| /* Decode the message */ |
| payload_len = response_len - sizeof(struct pldm_msg_hdr); |
| |
| rc = decode_set_state_effecter_states_resp( |
| response_msg, |
| payload_len, |
| &response.completion_code); |
| if (rc != PLDM_SUCCESS || response.completion_code != PLDM_SUCCESS) { |
| prlog(PR_ERR, "Decode SetStateEffecter Error, rc: %d, cc: %d\n", |
| rc, response.completion_code); |
| free(tx); |
| free(response_msg); |
| return OPAL_PARAMETER; |
| } |
| |
| free(tx); |
| free(response_msg); |
| return OPAL_SUCCESS; |
| } |
| |
| /* |
| * entity_type: System chassis (main enclosure) |
| * state_set: System Power State (260) |
| * states: Off-Soft Graceful(9) |
| */ |
| int pldm_platform_power_off(void) |
| { |
| set_effecter_state_field field; |
| uint16_t effecter_id; |
| int rc; |
| |
| if (!pdr_ready) |
| return OPAL_HARDWARE; |
| |
| rc = find_effecter_id_by_state_set_Id( |
| PLDM_ENTITY_SYSTEM_CHASSIS, |
| PLDM_STATE_SET_SYSTEM_POWER_STATE, |
| &effecter_id, BMC_TID); |
| if (rc) { |
| prlog(PR_ERR, "%s - effecter id not found\n", __func__); |
| return rc; |
| } |
| |
| field.set_request = PLDM_REQUEST_SET; |
| field.effecter_state = PLDM_STATE_SET_SYS_POWER_STATE_OFF_SOFT_GRACEFUL; |
| |
| prlog(PR_INFO, "sending system chassis Off-Soft Graceful request (effecter_id: %d)\n", |
| effecter_id); |
| |
| return set_state_effecter_states_req(effecter_id, &field, true); |
| } |
| |
| /* |
| * entity_type: System Firmware |
| * state_set: Software Termination Status(129) |
| * states: Graceful Restart Requested(6) |
| */ |
| int pldm_platform_restart(void) |
| { |
| set_effecter_state_field field; |
| uint16_t effecter_id; |
| int rc; |
| |
| if (!pdr_ready) |
| return OPAL_HARDWARE; |
| |
| rc = find_effecter_id_by_state_set_Id( |
| PLDM_ENTITY_SYS_FIRMWARE, |
| PLDM_STATE_SET_SW_TERMINATION_STATUS, |
| &effecter_id, BMC_TID); |
| if (rc) { |
| prlog(PR_ERR, "%s - effecter id not found\n", __func__); |
| return rc; |
| } |
| |
| field.set_request = PLDM_REQUEST_SET; |
| field.effecter_state = PLDM_SW_TERM_GRACEFUL_RESTART_REQUESTED; |
| |
| prlog(PR_INFO, "sending system firmware Graceful Restart request (effecter_id: %d)\n", |
| effecter_id); |
| |
| return set_state_effecter_states_req(effecter_id, &field, true); |
| } |
| |
| static int send_sensor_state_changed_event(uint16_t state_set_id, |
| uint16_t sensor_id, |
| uint8_t sensor_offset, |
| uint8_t sensor_state, |
| bool no_timeout) |
| { |
| size_t event_data_size = 0, actual_event_data_size; |
| size_t response_len, payload_len, data_size; |
| uint8_t *event_data = NULL; |
| struct pldm_tx_data *tx; |
| void *response_msg; |
| int rc, i; |
| |
| struct pldm_platform_event_message_req event_message_req = { |
| .format_version = PLDM_PLATFORM_EVENT_MESSAGE_FORMAT_VERSION, |
| .tid = HOST_TID, |
| .event_class = PLDM_SENSOR_EVENT, |
| }; |
| |
| struct pldm_platform_event_message_resp response; |
| |
| prlog(PR_DEBUG, "%s - state_set_id: %d, sensor_id: %d, sensor_state: %d\n", |
| __func__, state_set_id, sensor_id, sensor_state); |
| |
| /* |
| * The first time around this loop, event_data is nullptr which |
| * instructs the encoder to not actually do the encoding, but |
| * rather fill out actual_change_records_size with the correct |
| * size, stop and return PLDM_SUCCESS. Then we allocate the |
| * proper amount of memory and call the encoder again, which |
| * will cause it to actually encode the message. |
| */ |
| for (i = 0; i < 2; i++) { |
| rc = encode_sensor_event_data( |
| (struct pldm_sensor_event_data *)event_data, |
| event_data_size, |
| sensor_id, |
| PLDM_STATE_SENSOR_STATE, |
| sensor_offset, |
| sensor_state, |
| sensor_state, |
| &actual_event_data_size); |
| if (rc) { |
| prlog(PR_ERR, "encode PldmSensorChgEventData Error, rc: %d\n", rc); |
| return OPAL_PARAMETER; |
| } |
| |
| if (event_data == NULL) { |
| event_data_size = actual_event_data_size; |
| event_data = zalloc(event_data_size); |
| if (!event_data) { |
| prlog(PR_ERR, "failed to allocate event data (size: 0x%lx)\n", event_data_size); |
| return OPAL_NO_MEM; |
| } |
| } |
| } |
| |
| /* Send the event request */ |
| payload_len = PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES + event_data_size; |
| |
| data_size = sizeof(struct pldm_msg_hdr) + |
| sizeof(struct pldm_platform_event_message_req) + |
| event_data_size; |
| tx = zalloc(sizeof(struct pldm_tx_data) + data_size); |
| if (!tx) |
| return OPAL_NO_MEM; |
| tx->data_size = data_size - 1; |
| |
| /* Encode the platform event message request */ |
| rc = encode_platform_event_message_req( |
| DEFAULT_INSTANCE_ID, |
| event_message_req.format_version, |
| event_message_req.tid, |
| event_message_req.event_class, |
| (const uint8_t *)event_data, |
| event_data_size, |
| (struct pldm_msg *)tx->data, |
| payload_len); |
| if (rc != PLDM_SUCCESS) { |
| prlog(PR_ERR, "Encode PlatformEventMessage Error, rc: %d\n", rc); |
| free(event_data); |
| free(tx); |
| return OPAL_PARAMETER; |
| } |
| free(event_data); |
| |
| /* Send and get the response message bytes. |
| * It may happen that for some commands, the responder does not |
| * have time to respond. |
| */ |
| if (no_timeout) { |
| rc = pldm_mctp_message_tx(tx); |
| if (rc) |
| prlog(PR_ERR, "Failed to send PlatformEventMessage request, rc = %d\n", rc); |
| free(tx); |
| return rc; |
| } |
| |
| /* Send and get the response message bytes */ |
| rc = pldm_requester_queue_and_wait(tx, |
| &response_msg, &response_len); |
| if (rc) { |
| prlog(PR_ERR, "Communication Error, req: PlatformEventMessage, rc: %d\n", rc); |
| free(tx); |
| return rc; |
| } |
| free(tx); |
| |
| /* Decode the message */ |
| payload_len = response_len - sizeof(struct pldm_msg_hdr); |
| rc = decode_platform_event_message_resp( |
| response_msg, |
| payload_len, |
| &response.completion_code, |
| &response.platform_event_status); |
| if (rc != PLDM_SUCCESS || response.completion_code != PLDM_SUCCESS) { |
| prlog(PR_ERR, "Decode PlatformEventMessage Error, rc: %d, cc: %d, pes: %d\n", |
| rc, response.completion_code, |
| response.platform_event_status); |
| free(response_msg); |
| return OPAL_PARAMETER; |
| } |
| |
| free(response_msg); |
| |
| return OPAL_SUCCESS; |
| } |
| |
| #define BOOT_STATE_SENSOR_INDEX 0 |
| |
| int pldm_platform_send_progress_state_change( |
| enum pldm_state_set_boot_progress_state_values state) |
| { |
| struct state_sensor_possible_states *possible_states; |
| struct pldm_state_sensor_pdr *sensor_pdr = NULL; |
| const pldm_pdr_record *record = NULL; |
| uint16_t terminus_handle; |
| uint8_t *outData = NULL; |
| uint16_t sensor_id = 0; |
| uint32_t size; |
| |
| if (!pdr_ready) |
| return OPAL_HARDWARE; |
| |
| prlog(PR_INFO, "Setting boot progress, state: %d\n", state); |
| |
| do { |
| /* Find (first) PDR record by PLDM_STATE_SENSOR_PDR type |
| * if record not NULL, then search will begin from this |
| * record's next record |
| */ |
| record = pldm_pdr_find_record_by_type( |
| pdrs_repo, /* PDR repo handle */ |
| PLDM_STATE_SENSOR_PDR, |
| record, /* PDR record handle */ |
| &outData, &size); |
| |
| if (record) { |
| sensor_pdr = (struct pldm_state_sensor_pdr *) outData; |
| terminus_handle = le16_to_cpu(sensor_pdr->terminus_handle); |
| |
| if ((le16_to_cpu(sensor_pdr->entity_type) == PLDM_ENTITY_SYS_BOARD) && |
| (terminus_handle == HOST_TID)) { |
| possible_states = (struct state_sensor_possible_states *) |
| sensor_pdr->possible_states; |
| |
| if (le16_to_cpu(possible_states->state_set_id) == |
| PLDM_STATE_SET_BOOT_PROGRESS){ |
| sensor_id = le16_to_cpu(sensor_pdr->sensor_id); |
| break; |
| } |
| } |
| } |
| |
| } while (record); |
| |
| if (sensor_id == 0) |
| return OPAL_PARAMETER; |
| |
| return send_sensor_state_changed_event( |
| PLDM_STATE_SET_BOOT_PROGRESS, |
| sensor_id, |
| BOOT_STATE_SENSOR_INDEX, |
| state, |
| false); |
| } |
| |
| #define SW_TERM_GRACEFUL_SHUTDOWN_INDEX 0 |
| |
| /* |
| * entity_type: System Firmware |
| * state_set: Software Termination Status(129) |
| * states: Graceful Shutdown Requested(7) |
| */ |
| int pldm_platform_initiate_shutdown(void) |
| { |
| uint16_t sensor_id; |
| int rc; |
| |
| if (!pdr_ready) |
| return OPAL_HARDWARE; |
| |
| rc = find_sensor_id_by_state_set_Id( |
| PLDM_ENTITY_SYSTEM_CHASSIS, |
| PLDM_STATE_SET_SW_TERMINATION_STATUS, |
| &sensor_id, HOST_TID); |
| if (rc) { |
| prlog(PR_ERR, "%s - sensor id not found\n", __func__); |
| return rc; |
| } |
| |
| prlog(PR_INFO, "sending system firmware Graceful Shutdown request (sensor_id: %d)\n", |
| sensor_id); |
| |
| return send_sensor_state_changed_event( |
| PLDM_STATE_SET_SW_TERMINATION_STATUS, |
| sensor_id, |
| SW_TERM_GRACEFUL_SHUTDOWN_INDEX, |
| PLDM_SW_TERM_GRACEFUL_SHUTDOWN, |
| true); |
| } |
| |
| static int add_state_sensor_pdr(pldm_pdr *repo, |
| uint32_t *record_handle, |
| uint16_t entity_type, |
| uint16_t state_set_id, |
| uint32_t states) |
| { |
| struct state_sensor_possible_states *possible_states; |
| struct pldm_state_sensor_pdr *pdr; |
| uint8_t DEFAULT_CONTAINER_ID = 0; |
| size_t state_size, pdr_size, actual_pdr_size = 0; |
| uint8_t *state_storage; |
| uint32_t swapped; |
| int rc; |
| |
| /* fill in possible states structure */ |
| state_size = sizeof(struct state_sensor_possible_states) |
| + sizeof(states) |
| - sizeof(bitfield8_t); |
| state_storage = zalloc(state_size); |
| if (!state_storage) { |
| prlog(PR_ERR, "failed to allocate storage data (size: 0x%lx)\n", state_size); |
| return OPAL_NO_MEM; |
| } |
| |
| possible_states = (struct state_sensor_possible_states *) state_storage; |
| possible_states->state_set_id = state_set_id; |
| possible_states->possible_states_size = sizeof(states); |
| |
| /* need to swap the byte order for little endian order */ |
| swapped = htole32(states); |
| memcpy(possible_states->states, &swapped, sizeof(swapped)); |
| |
| pdr_size = sizeof(struct pldm_state_sensor_pdr) + state_size; |
| pdr = zalloc(pdr_size); |
| if (!pdr) { |
| prlog(PR_ERR, "failed to allocate sensor pdr (size: 0x%lx)\n", pdr_size); |
| free(state_storage); |
| return OPAL_NO_MEM; |
| } |
| |
| /* header */ |
| pdr->hdr.record_handle = 0; /* ask libpldm to fill this out */ |
| pdr->hdr.version = 0; /* will be filled out by the encoder */ |
| pdr->hdr.type = 0; /* will be filled out by the encoder */ |
| pdr->hdr.record_change_num = 0; |
| pdr->hdr.length = 0; /* will be filled out by the encoder */ |
| |
| /* body */ |
| pdr->terminus_handle = HOST_TID; |
| pdr->sensor_id = sensor_effecter_id++; |
| pdr->entity_type = entity_type; |
| pdr->entity_instance = 1; |
| pdr->container_id = DEFAULT_CONTAINER_ID; |
| pdr->sensor_init = PLDM_NO_INIT; |
| pdr->sensor_auxiliary_names_pdr = false; |
| pdr->composite_sensor_count = 1; |
| |
| rc = encode_state_sensor_pdr(pdr, pdr_size, |
| possible_states, |
| state_size, |
| &actual_pdr_size); |
| |
| if (rc != PLDM_SUCCESS) { |
| prlog(PR_ERR, "%s - Failed to encode state sensor PDR, rc: %d\n", |
| __func__, rc); |
| free(state_storage); |
| free(pdr); |
| return rc; |
| } |
| |
| *record_handle = pldm_pdr_add(repo, |
| (const uint8_t *) pdr, |
| actual_pdr_size, |
| 0, false, HOST_TID); |
| |
| free(state_storage); |
| free(pdr); |
| |
| return OPAL_SUCCESS; |
| } |
| |
| /* |
| * Add boot progress record in the repository. |
| */ |
| static uint32_t add_sensor_sw_term_pdr(pldm_pdr *repo, |
| uint32_t *record_handle) |
| { |
| int rc; |
| |
| rc = add_state_sensor_pdr( |
| repo, |
| record_handle, |
| PLDM_ENTITY_SYSTEM_CHASSIS, |
| PLDM_STATE_SET_SW_TERMINATION_STATUS, |
| enum_bit(PLDM_SW_TERM_NORMAL) | |
| enum_bit(PLDM_SW_TERM_GRACEFUL_SHUTDOWN_REQUESTED) | |
| enum_bit(PLDM_SW_TERM_GRACEFUL_SHUTDOWN)); |
| if (rc) { |
| prlog(PR_ERR, "%s - Failed to add states sensor PDR, rc: %d\n", |
| __func__, rc); |
| return rc; |
| } |
| |
| prlog(PR_DEBUG, "Add state sensor pdr (record handle: %d)\n", |
| *record_handle); |
| |
| return OPAL_SUCCESS; |
| } |
| |
| /* |
| * Add boot progress record in the repository. |
| */ |
| static uint32_t add_boot_progress_pdr(pldm_pdr *repo, |
| uint32_t *record_handle) |
| { |
| int rc; |
| |
| rc = add_state_sensor_pdr( |
| repo, |
| record_handle, |
| PLDM_ENTITY_SYS_BOARD, |
| PLDM_STATE_SET_BOOT_PROGRESS, |
| enum_bit(PLDM_STATE_SET_BOOT_PROG_STATE_COMPLETED) | |
| enum_bit(PLDM_STATE_SET_BOOT_PROG_STATE_PCI_RESORUCE_CONFIG) | |
| enum_bit(PLDM_STATE_SET_BOOT_PROG_STATE_STARTING_OP_SYS)); |
| if (rc) { |
| prlog(PR_ERR, "%s - Failed to add boot progress PDR, rc: %d\n", |
| __func__, rc); |
| return rc; |
| } |
| |
| prlog(PR_DEBUG, "Add boot progress pdr (record handle: %d)\n", |
| *record_handle); |
| |
| return OPAL_SUCCESS; |
| } |
| |
| static int add_state_effecter_pdr(pldm_pdr *repo, |
| uint32_t *record_handle, |
| uint16_t entity_type, |
| uint16_t state_set_id, |
| uint32_t states) |
| { |
| struct state_effecter_possible_states *possible_states; |
| struct pldm_state_effecter_pdr *pdr; |
| uint8_t DEFAULT_CONTAINER_ID = 0; |
| size_t state_size, pdr_size, actual_pdr_size = 0; |
| uint8_t *state_storage; |
| uint32_t swapped; |
| int rc; |
| |
| /* fill in possible states structure */ |
| state_size = sizeof(struct state_effecter_possible_states) |
| + sizeof(states) |
| - sizeof(bitfield8_t); |
| state_storage = zalloc(state_size); |
| if (!state_storage) { |
| prlog(PR_ERR, "failed to allocate storage data (size: 0x%lx)\n", state_size); |
| return OPAL_NO_MEM; |
| } |
| |
| possible_states = (struct state_effecter_possible_states *) state_storage; |
| possible_states->state_set_id = state_set_id; |
| possible_states->possible_states_size = sizeof(states); |
| |
| /* need to swap the byte order for little endian order */ |
| swapped = htole32(states); |
| memcpy(possible_states->states, &swapped, sizeof(swapped)); |
| |
| pdr_size = sizeof(struct pldm_state_effecter_pdr) + state_size; |
| pdr = zalloc(pdr_size); |
| if (!pdr) { |
| prlog(PR_ERR, "failed to allocate sensor pdr (size: 0x%lx)\n", pdr_size); |
| free(state_storage); |
| return OPAL_NO_MEM; |
| } |
| |
| /* header */ |
| pdr->hdr.record_handle = 0; /* ask libpldm to fill this out */ |
| pdr->hdr.version = 0; /* will be filled out by the encoder */ |
| pdr->hdr.type = 0; /* will be filled out by the encoder */ |
| pdr->hdr.record_change_num = 0; |
| pdr->hdr.length = 0; /* will be filled out by the encoder */ |
| |
| /* body */ |
| pdr->terminus_handle = HOST_TID; |
| pdr->effecter_id = sensor_effecter_id++; |
| pdr->entity_type = entity_type; |
| pdr->entity_instance = 1; |
| pdr->container_id = DEFAULT_CONTAINER_ID; |
| pdr->effecter_semantic_id = 0; /* PLDM defines no semantic IDs yet */ |
| pdr->effecter_init = PLDM_NO_INIT; |
| pdr->has_description_pdr = false; |
| pdr->composite_effecter_count = 1; |
| |
| rc = encode_state_effecter_pdr(pdr, pdr_size, possible_states, |
| state_size, &actual_pdr_size); |
| if (rc != PLDM_SUCCESS) { |
| prlog(PR_ERR, "%s - Failed to encode state effecter PDR, rc: %d\n", |
| __func__, rc); |
| free(state_storage); |
| free(pdr); |
| return rc; |
| } |
| |
| *record_handle = pldm_pdr_add(repo, |
| (const uint8_t *) pdr, |
| actual_pdr_size, |
| 0, false, HOST_TID); |
| |
| free(state_storage); |
| free(pdr); |
| |
| return OPAL_SUCCESS; |
| } |
| |
| /* |
| * Add state software termination record in the repository. |
| */ |
| static uint32_t add_state_sw_term_pdr(pldm_pdr *repo, |
| uint32_t *record_handle) |
| { |
| int rc; |
| |
| rc = add_state_effecter_pdr( |
| repo, |
| record_handle, |
| PLDM_ENTITY_SYSTEM_CHASSIS, |
| PLDM_STATE_SET_SW_TERMINATION_STATUS, |
| enum_bit(PLDM_SW_TERM_GRACEFUL_SHUTDOWN_REQUESTED)); |
| if (rc) { |
| prlog(PR_ERR, "%s - Failed to add boot progress PDR, rc: %d\n", |
| __func__, rc); |
| return rc; |
| } |
| |
| prlog(PR_DEBUG, "Add state software termination pdr (record handle: %d)\n", |
| *record_handle); |
| |
| return OPAL_SUCCESS; |
| } |
| |
| /* |
| * Add terminus locator record in the repository. |
| */ |
| static int add_terminus_locator_pdr(pldm_pdr *repo, |
| uint32_t *record_handle) |
| { |
| struct pldm_terminus_locator_type_mctp_eid *locator_value; |
| struct pldm_terminus_locator_pdr pdr; |
| uint8_t DEFAULT_CONTAINER_ID = 0; |
| uint32_t size; |
| |
| pdr.hdr.record_handle = 0; /* record_handle will be generated for us */ |
| pdr.hdr.version = 1; |
| pdr.hdr.type = PLDM_TERMINUS_LOCATOR_PDR; |
| pdr.hdr.record_change_num = 0; |
| pdr.hdr.length = htole16(sizeof(struct pldm_terminus_locator_pdr) - |
| sizeof(struct pldm_pdr_hdr)); |
| pdr.terminus_handle = htole16(HOST_TID); |
| pdr.validity = PLDM_TL_PDR_VALID; |
| pdr.tid = HOST_TID; |
| pdr.container_id = DEFAULT_CONTAINER_ID; |
| pdr.terminus_locator_type = PLDM_TERMINUS_LOCATOR_TYPE_MCTP_EID; |
| pdr.terminus_locator_value_size = sizeof(struct pldm_terminus_locator_type_mctp_eid); |
| locator_value = (struct pldm_terminus_locator_type_mctp_eid *)pdr.terminus_locator_value; |
| locator_value->eid = HOST_EID; |
| |
| size = sizeof(struct pldm_terminus_locator_pdr) + |
| sizeof(struct pldm_terminus_locator_type_mctp_eid); |
| |
| *record_handle = pldm_pdr_add(repo, |
| (const uint8_t *)(&pdr), size, |
| 0, false, HOST_TID); |
| |
| prlog(PR_DEBUG, "Add terminus locator pdr (record handle: %d)\n", |
| *record_handle); |
| |
| return OPAL_SUCCESS; |
| } |
| |
| static int send_repository_changed_event(uint32_t num_changed_pdrs, |
| uint32_t *record_handle) |
| { |
| size_t actual_change_records_size = 0; |
| uint8_t number_of_change_entries[1]; |
| size_t max_change_records_size = 0; |
| size_t response_len, payload_len; |
| uint8_t event_data_operation[1]; |
| uint32_t *change_entries[1]; |
| uint8_t *event_data = NULL; |
| struct pldm_tx_data *tx; |
| void *response_msg; |
| size_t data_size; |
| int rc, i; |
| |
| struct pldm_platform_event_message_req event_message_req = { |
| .format_version = PLDM_PLATFORM_EVENT_MESSAGE_FORMAT_VERSION, |
| .tid = HOST_TID, |
| .event_class = PLDM_PDR_REPOSITORY_CHG_EVENT, |
| }; |
| |
| struct pldm_platform_event_message_resp response; |
| |
| prlog(PR_DEBUG, "%s - num_changed_pdrs: %d\n", __func__, num_changed_pdrs); |
| |
| if (num_changed_pdrs == 0) |
| return OPAL_PARAMETER; |
| |
| /* encode the platform change event data */ |
| event_data_operation[0] = PLDM_RECORDS_ADDED; |
| number_of_change_entries[0] = num_changed_pdrs; |
| change_entries[0] = record_handle; |
| |
| /* |
| * The first time around this loop, event_data is nullptr which |
| * instructs the encoder to not actually do the encoding, but |
| * rather fill out actual_change_records_size with the correct |
| * size, stop and return PLDM_SUCCESS. Then we allocate the |
| * proper amount of memory and call the encoder again, which |
| * will cause it to actually encode the message. |
| */ |
| for (i = 0; i < 2; i++) { |
| rc = encode_pldm_pdr_repository_chg_event_data( |
| FORMAT_IS_PDR_HANDLES, |
| 1, /* only one change record (RECORDS_ADDED) */ |
| event_data_operation, |
| number_of_change_entries, |
| (const uint32_t * const*)change_entries, |
| (struct pldm_pdr_repository_chg_event_data *)event_data, |
| &actual_change_records_size, |
| max_change_records_size); |
| if (rc) { |
| prlog(PR_ERR, "Encode PldmPdrRepositoryChgEventData Error, rc: %d\n", rc); |
| return OPAL_PARAMETER; |
| } |
| |
| if (event_data == NULL) { |
| max_change_records_size = actual_change_records_size; |
| event_data = zalloc(max_change_records_size); |
| if (!event_data) { |
| prlog(PR_ERR, "failed to allocate event data (size: 0x%lx)\n", max_change_records_size); |
| return OPAL_NO_MEM; |
| } |
| } |
| } |
| |
| /* Send the event request */ |
| payload_len = PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES + max_change_records_size; |
| |
| data_size = sizeof(struct pldm_msg_hdr) + |
| sizeof(struct pldm_platform_event_message_req) + |
| max_change_records_size; |
| tx = zalloc(sizeof(struct pldm_tx_data) + data_size); |
| if (!tx) |
| return OPAL_NO_MEM; |
| tx->data_size = data_size - 1; |
| |
| /* Encode the platform event message request */ |
| rc = encode_platform_event_message_req( |
| DEFAULT_INSTANCE_ID, |
| event_message_req.format_version, |
| event_message_req.tid, |
| event_message_req.event_class, |
| (const uint8_t *)event_data, |
| max_change_records_size, |
| (struct pldm_msg *)tx->data, |
| payload_len); |
| if (rc != PLDM_SUCCESS) { |
| prlog(PR_ERR, "Encode PlatformEventMessage Error, rc: %d\n", rc); |
| free(event_data); |
| free(tx); |
| return OPAL_PARAMETER; |
| } |
| free(event_data); |
| |
| /* Send and get the response message bytes */ |
| rc = pldm_requester_queue_and_wait(tx, &response_msg, &response_len); |
| if (rc) { |
| prlog(PR_ERR, "Communication Error, req: PlatformEventMessage, rc: %d\n", rc); |
| free(tx); |
| return rc; |
| } |
| free(tx); |
| |
| /* Decode the message */ |
| payload_len = response_len - sizeof(struct pldm_msg_hdr); |
| rc = decode_platform_event_message_resp( |
| response_msg, |
| payload_len, |
| &response.completion_code, |
| &response.platform_event_status); |
| if (rc != PLDM_SUCCESS || response.completion_code != PLDM_SUCCESS) { |
| prlog(PR_ERR, "Decode PlatformEventMessage Error, rc: %d, cc: %d, pes: %d\n", |
| rc, response.completion_code, |
| response.platform_event_status); |
| free(response_msg); |
| return OPAL_PARAMETER; |
| } |
| |
| free(response_msg); |
| |
| return OPAL_SUCCESS; |
| } |
| |
| static int add_hosted_pdrs(pldm_pdr *repo) |
| { |
| static uint32_t records_handle[2]; |
| uint8_t hosted_pdrs = 0; |
| uint32_t record_handle; |
| int rc = OPAL_SUCCESS; |
| |
| rc = add_state_sw_term_pdr(repo, &record_handle); |
| if (!rc) { |
| records_handle[hosted_pdrs] = record_handle; |
| hosted_pdrs++; |
| } |
| |
| rc = add_sensor_sw_term_pdr(repo, &record_handle); |
| if (!rc) { |
| records_handle[hosted_pdrs] = record_handle; |
| hosted_pdrs++; |
| } |
| |
| rc = add_boot_progress_pdr(repo, &record_handle); |
| if (!rc) { |
| records_handle[hosted_pdrs] = record_handle; |
| hosted_pdrs++; |
| } |
| |
| rc = add_terminus_locator_pdr(repo, &record_handle); |
| if (!rc) { |
| records_handle[hosted_pdrs] = record_handle; |
| hosted_pdrs++; |
| } |
| |
| /* tell BMC that these PDRs have changed */ |
| rc = send_repository_changed_event(hosted_pdrs, records_handle); |
| if (rc) |
| prlog(PR_ERR, "%s - Failed to update hosted PDRs\n", __func__); |
| |
| return rc; |
| } |
| |
| struct get_pdr_response { |
| uint8_t completion_code; |
| uint32_t next_record_hndl; |
| uint32_t next_data_transfer_hndl; |
| uint8_t transfer_flag; |
| uint16_t resp_cnt; |
| uint8_t *record_data; |
| size_t record_data_length; |
| uint8_t transfer_crc; |
| }; |
| |
| static int encode_and_queue_get_pdr_req(struct pldm_pdrs *pdrs); |
| |
| static void get_pdr_req_complete(struct pldm_rx_data *rx, |
| void *data) |
| { |
| struct pldm_pdrs *pdrs = (struct pldm_pdrs *)data; |
| uint32_t record_hndl = pdrs->record_hndl; |
| struct get_pdr_response response; |
| size_t payload_len; |
| int rc, i; |
| |
| prlog(PR_DEBUG, "%s - record_hndl: %d\n", __func__, record_hndl); |
| |
| if (rx == NULL) { |
| pdrs->rc = OPAL_PARAMETER; |
| pdrs->done = true; |
| } |
| |
| /* Decode the message twice; the first time, the payload buffer |
| * will be null so that the decoder will simply tell us how big |
| * the buffer should be. Then we create a suitable payload |
| * buffer and call the decoder again, this time with the real |
| * buffer so that it can fill it with data from the message. |
| * |
| * transfer_crc is not used in case of PLDM_START_AND_END. |
| */ |
| payload_len = rx->msg_len - sizeof(struct pldm_msg_hdr); |
| response.record_data_length = 0; |
| response.record_data = NULL; |
| |
| for (i = 0; i < 2; i++) { |
| rc = decode_get_pdr_resp( |
| rx->msg, payload_len, |
| &response.completion_code, |
| &response.next_record_hndl, |
| &response.next_data_transfer_hndl, |
| &response.transfer_flag, |
| &response.resp_cnt, |
| response.record_data, |
| response.record_data_length, |
| &response.transfer_crc); |
| |
| if (rc != PLDM_SUCCESS || response.completion_code != PLDM_SUCCESS) { |
| /* Message decoding failed */ |
| prlog(PR_ERR, "Decode GetPDRResp Error (rc: %d, cc: %d)\n", |
| rc, response.completion_code); |
| |
| /* BMC is not ready, try again. This behavior can be |
| * encountered when the BMC reboots and the host is |
| * still operational. |
| * The host receives a GET VERSION request indicating |
| * that we must rehcrage the pdrs. |
| */ |
| if (response.completion_code == PLDM_ERROR_NOT_READY) { |
| time_wait_ms(500); |
| encode_and_queue_get_pdr_req(pdrs); |
| return; |
| } |
| |
| pdrs->rc = OPAL_PARAMETER; |
| pdrs->done = true; |
| return; |
| } |
| |
| if (response.record_data == NULL) { |
| response.record_data_length = response.resp_cnt; |
| response.record_data = zalloc(response.resp_cnt); |
| if (!response.record_data) { |
| prlog(PR_ERR, "failed to allocate record data (size: 0x%lx)\n", response.record_data_length); |
| pdrs->rc = OPAL_NO_MEM; |
| pdrs->done = true; |
| return; |
| } |
| } |
| } |
| |
| /* we do not support multipart transfer */ |
| if (response.transfer_flag != PLDM_START_AND_END) |
| prlog(PR_ERR, "Transfert GetPDRResp not complete, transfer_flag: %d\n", |
| response.transfer_flag); |
| |
| prlog(PR_DEBUG, "%s - record_hndl: %d, next_record_hndl: %d, resp_cnt: %d\n", |
| __func__, record_hndl, |
| response.next_record_hndl, |
| response.resp_cnt); |
| |
| /* Add a PDR record to a PDR repository. |
| * Use HOST_TID as terminus handle |
| */ |
| pldm_pdr_add(pdrs_repo, |
| response.record_data, |
| response.resp_cnt, |
| record_hndl, |
| false, |
| HOST_TID); |
| |
| free(response.record_data); |
| |
| if (response.next_record_hndl != NO_MORE_PDR_HANDLES) { |
| pdrs->record_hndl = response.next_record_hndl; |
| encode_and_queue_get_pdr_req(pdrs); |
| } else { |
| /* We have to indicate the end of the initialization when we |
| * reload the pdrs in background |
| */ |
| pdr_init_complete(true); |
| pdrs->done = true; |
| pdrs->rc = OPAL_SUCCESS; |
| prlog(PR_DEBUG, "%s - done\n", __func__); |
| } |
| } |
| |
| /* |
| * Send/receive a PLDM GetPDR stateEffecter request message |
| * Get platform descriptor records. |
| * |
| * pldmtool platform GetPDR -t stateEffecter |
| * ... |
| * { |
| * "nextRecordHandle": 138, |
| * "responseCount": 30, |
| * "recordHandle": 137, |
| * "PDRHeaderVersion": 1, |
| * "PDRType": "State Effecter PDR", |
| * "recordChangeNumber": 0, |
| * "dataLength": 20, |
| * "PLDMTerminusHandle": 1, |
| * "effecterID": 43, |
| * "entityType": "[Physical] System chassis (main enclosure)", |
| * ... |
| * "Off-Soft Graceful(9)" |
| * } |
| * ... |
| */ |
| static int encode_and_queue_get_pdr_req(struct pldm_pdrs *pdrs) |
| { |
| uint32_t record_hndl = pdrs->record_hndl; |
| int rc; |
| |
| struct pldm_get_pdr_req pdr_req = { |
| .record_handle = record_hndl, /* record change number (0 for first request) */ |
| .data_transfer_handle = 0, /* (0 if transfer op is FIRSTPART) */ |
| .transfer_op_flag = PLDM_GET_FIRSTPART, /* transfer op flag */ |
| .request_count = SHRT_MAX, /* Don't limit the size of the PDR */ |
| .record_change_number = 0 /* record change number (0 for first request) */ |
| }; |
| |
| prlog(PR_DEBUG, "%s - record_hndl: %d\n", __func__, record_hndl); |
| |
| /* Encode the get_PDR request */ |
| rc = encode_get_pdr_req(DEFAULT_INSTANCE_ID, |
| pdr_req.record_handle, |
| pdr_req.data_transfer_handle, |
| pdr_req.transfer_op_flag, |
| pdr_req.request_count, |
| pdr_req.record_change_number, |
| (struct pldm_msg *)pdrs->tx->data, |
| PLDM_GET_PDR_REQ_BYTES); |
| if (rc != PLDM_SUCCESS) { |
| prlog(PR_ERR, "Encode GetPDRReq Error, rc: %d\n", rc); |
| pdrs->done = true; |
| pdrs->rc = OPAL_PARAMETER; |
| return OPAL_PARAMETER; |
| } |
| |
| /* Queue the first getpdr request */ |
| rc = pldm_requester_queue(pdrs->tx, get_pdr_req_complete, pdrs); |
| if (rc) { |
| prlog(PR_ERR, "Communication Error, req: GetPDRReq, rc: %d\n", rc); |
| pdrs->done = true; |
| pdrs->rc = OPAL_PARAMETER; |
| } |
| |
| return rc; |
| } |
| |
| static int pldm_platform_load_pdrs(void) |
| { |
| /* destroy current repo and mark repo not ready */ |
| pdr_init_complete(false); |
| |
| /* make a new PDR repository */ |
| pdrs_repo = pldm_pdr_init(); |
| |
| /* collect all PDrs into a PDR Repository */ |
| pdrs->record_hndl = 0; |
| pdrs->done = false; |
| return encode_and_queue_get_pdr_req(pdrs); |
| } |
| |
| int pldm_platform_reload_pdrs(void) |
| { |
| return pldm_platform_load_pdrs(); |
| } |
| |
| static int pdrs_init(void) |
| { |
| int rc; |
| |
| rc = pldm_platform_load_pdrs(); |
| if (rc) |
| return rc; |
| |
| /* wait for the end of pdrs received */ |
| for (;;) { |
| if (pdrs->done) |
| break; |
| |
| time_wait_ms(5); |
| } |
| return pdrs->rc; |
| } |
| |
| int pldm_platform_init(void) |
| { |
| size_t data_size = PLDM_MSG_SIZE(struct pldm_get_pdr_req); |
| int rc; |
| |
| pdrs = zalloc(sizeof(struct pldm_pdrs)); |
| if (!pdrs) { |
| prlog(PR_ERR, "failed to allocate pdrs\n"); |
| return OPAL_NO_MEM; |
| } |
| |
| pdrs->tx = zalloc(sizeof(struct pldm_tx_data) + data_size); |
| if (!pdrs->tx) |
| return OPAL_NO_MEM; |
| pdrs->tx->data_size = data_size; |
| |
| /* retrieve all PDRs */ |
| rc = pdrs_init(); |
| if (rc) |
| goto err; |
| |
| /* add hosted pdrs */ |
| rc = add_hosted_pdrs(pdrs_repo); |
| if (rc) |
| goto err; |
| |
| pdr_init_complete(true); |
| prlog(PR_DEBUG, "%s - done\n", __func__); |
| |
| return OPAL_SUCCESS; |
| |
| err: |
| prlog(PR_ERR, "%s - failed to initialize pdrs, rc: %d\n", __func__, rc); |
| pdr_init_complete(false); |
| free(pdrs->tx); |
| free(pdrs); |
| return rc; |
| } |
| |
| void pldm_platform_exit(void) |
| { |
| if (pdr_ready) |
| pldm_pdr_destroy(pdrs_repo); |
| |
| if (pdrs) { |
| free(pdrs->tx); |
| free(pdrs); |
| } |
| } |