492 lines
12 KiB
C
492 lines
12 KiB
C
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
|
// Copyright 2022 IBM Corp.
|
|
|
|
#define pr_fmt(fmt) "PLDM: " fmt
|
|
|
|
#include <opal.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
#include <timebase.h>
|
|
#include <libpldm/file_io.h>
|
|
#include "pldm.h"
|
|
|
|
/* list of lid files available */
|
|
static void *file_attr_table;
|
|
static size_t file_attr_length;
|
|
|
|
static bool file_io_ready;
|
|
|
|
static void file_io_init_complete(bool success)
|
|
{
|
|
/* Read not successful, error out and free the buffer */
|
|
if (!success) {
|
|
file_io_ready = false;
|
|
|
|
if (file_attr_table != NULL) {
|
|
free(file_attr_table);
|
|
file_attr_length = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Mark ready */
|
|
file_io_ready = true;
|
|
}
|
|
|
|
#define CHKSUM_PADDING 8
|
|
|
|
/*
|
|
* Retrieve the file handle and file length from the file attribute
|
|
* table.
|
|
*/
|
|
static int find_file_handle_by_lid_id(const char *lid_id,
|
|
uint32_t *file_handle,
|
|
uint32_t *file_length)
|
|
{
|
|
const struct pldm_file_attr_table_entry *file_entry;
|
|
char *startptr, *endptr;
|
|
uint16_t file_name_length;
|
|
|
|
if ((file_attr_table == NULL) || (file_attr_length == 0))
|
|
return OPAL_HARDWARE;
|
|
|
|
startptr = (char *)file_attr_table;
|
|
endptr = startptr + file_attr_length - CHKSUM_PADDING;
|
|
*file_handle = 0;
|
|
*file_length = 0;
|
|
|
|
while (startptr < endptr) {
|
|
/* file entry:
|
|
* 4 Bytes: file handle
|
|
* 2 Bytes: file name length
|
|
* <file name length> Bytes: file name
|
|
* 4 Bytes: file length
|
|
*/
|
|
file_entry = (struct pldm_file_attr_table_entry *)startptr;
|
|
|
|
*file_handle = le32_to_cpu(file_entry->file_handle);
|
|
startptr += sizeof(uint32_t);
|
|
|
|
file_name_length = le16_to_cpu(file_entry->file_name_length);
|
|
startptr += sizeof(file_name_length);
|
|
|
|
if (!strncmp(startptr, lid_id, strlen(lid_id))) {
|
|
startptr += file_name_length;
|
|
*file_length = le32_to_cpu(*(uint32_t *)startptr);
|
|
break;
|
|
}
|
|
startptr += file_name_length;
|
|
startptr += sizeof(uint32_t);
|
|
startptr += sizeof(bitfield32_t);
|
|
}
|
|
|
|
if (*file_length == 0) {
|
|
prlog(PR_ERR, "%s - lid_id: %s, no file handle found\n",
|
|
__func__, lid_id);
|
|
*file_handle = 0xff;
|
|
*file_length = 0;
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
prlog(PR_DEBUG, "%s - lid_id: %s, file_handle: %d, file_length: %d\n",
|
|
__func__, lid_id, *file_handle, *file_length);
|
|
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Retrieve the file handle and file length based on lid id.
|
|
*/
|
|
int pldm_find_file_handle_by_lid_id(const char *lid_id,
|
|
uint32_t *file_handle,
|
|
uint32_t *file_length)
|
|
{
|
|
if (!file_io_ready)
|
|
return OPAL_HARDWARE;
|
|
|
|
return find_file_handle_by_lid_id(lid_id,
|
|
file_handle,
|
|
file_length);
|
|
}
|
|
|
|
/* maximum currently transfer size for PLDM */
|
|
#define KILOBYTE 1024ul
|
|
#define MAX_TRANSFER_SIZE_BYTES (127 * KILOBYTE)
|
|
|
|
/*
|
|
* Send/receive a PLDM ReadFile request message.
|
|
*/
|
|
static int read_file_req(uint32_t file_handle, uint32_t file_length,
|
|
uint32_t pos, void *buf, uint64_t len)
|
|
{
|
|
size_t data_size = PLDM_MSG_SIZE(struct pldm_read_file_req);
|
|
size_t response_len, payload_len, file_data_offset;
|
|
uint8_t completion_code;
|
|
struct pldm_tx_data *tx;
|
|
uint32_t resp_length;
|
|
uint64_t total_read;
|
|
void *response_msg;
|
|
int num_transfers;
|
|
void *curr_buf;
|
|
int rc, i;
|
|
|
|
struct pldm_read_file_req file_req = {
|
|
.file_handle = file_handle,
|
|
.offset = pos,
|
|
.length = len
|
|
};
|
|
|
|
if (!file_length)
|
|
return OPAL_PARAMETER;
|
|
|
|
if ((!len) || ((len + pos) > file_length))
|
|
return OPAL_PARAMETER;
|
|
|
|
num_transfers = 1;
|
|
curr_buf = buf;
|
|
total_read = 0;
|
|
|
|
if (file_req.length > MAX_TRANSFER_SIZE_BYTES) {
|
|
num_transfers = (file_req.length + MAX_TRANSFER_SIZE_BYTES - 1) /
|
|
MAX_TRANSFER_SIZE_BYTES;
|
|
file_req.length = MAX_TRANSFER_SIZE_BYTES;
|
|
}
|
|
|
|
prlog(PR_TRACE, "%s - file_handle: %d, offset: 0x%x, len: 0x%llx num_transfers: %d\n",
|
|
__func__, file_handle, file_req.offset,
|
|
len, num_transfers);
|
|
|
|
/* init request */
|
|
tx = zalloc(sizeof(struct pldm_tx_data) + data_size);
|
|
if (!tx)
|
|
return OPAL_NO_MEM;
|
|
tx->data_size = data_size;
|
|
|
|
for (i = 0; i < num_transfers; i++) {
|
|
file_req.offset = pos + (i * MAX_TRANSFER_SIZE_BYTES);
|
|
|
|
/* Encode the file request */
|
|
rc = encode_read_file_req(
|
|
DEFAULT_INSTANCE_ID,
|
|
file_req.file_handle,
|
|
file_req.offset,
|
|
file_req.length,
|
|
(struct pldm_msg *)tx->data);
|
|
if (rc != PLDM_SUCCESS) {
|
|
prlog(PR_ERR, "Encode ReadFileReq Error, rc: %d\n",
|
|
rc);
|
|
free(tx);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
/* 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: ReadFileReq, rc: %d\n", rc);
|
|
free(tx);
|
|
return rc;
|
|
}
|
|
|
|
/* Decode the message */
|
|
payload_len = response_len - sizeof(struct pldm_msg_hdr);
|
|
rc = decode_read_file_resp(
|
|
response_msg,
|
|
payload_len,
|
|
&completion_code,
|
|
&resp_length,
|
|
&file_data_offset);
|
|
if (rc != PLDM_SUCCESS || completion_code != PLDM_SUCCESS) {
|
|
prlog(PR_ERR, "Decode ReadFileResp Error, rc: %d, cc: %d\n",
|
|
rc, completion_code);
|
|
free(tx);
|
|
free(response_msg);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
if (resp_length == 0) {
|
|
free(response_msg);
|
|
break;
|
|
}
|
|
|
|
memcpy(curr_buf,
|
|
((struct pldm_msg *)response_msg)->payload + file_data_offset,
|
|
resp_length);
|
|
|
|
total_read += resp_length;
|
|
curr_buf += resp_length;
|
|
free(response_msg);
|
|
|
|
prlog(PR_TRACE, "%s - file_handle: %d, resp_length: 0x%x, total_read: 0x%llx\n",
|
|
__func__, file_handle, resp_length, total_read);
|
|
|
|
if (total_read >= len)
|
|
break;
|
|
else if (resp_length != file_req.length) {
|
|
/* end of file */
|
|
break;
|
|
} else if (MAX_TRANSFER_SIZE_BYTES > (len - total_read))
|
|
file_req.length = len - total_read;
|
|
}
|
|
|
|
free(tx);
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
int pldm_file_io_read_file(uint32_t file_handle, uint32_t file_length,
|
|
uint32_t pos, void *buf, uint64_t len)
|
|
{
|
|
if (!file_io_ready)
|
|
return OPAL_HARDWARE;
|
|
|
|
return read_file_req(file_handle, file_length, pos, buf, len);
|
|
}
|
|
|
|
/*
|
|
* Send/receive a PLDM WriteFile request message.
|
|
*/
|
|
static int write_file_req(uint32_t file_handle, uint32_t pos,
|
|
const void *buf, uint64_t len)
|
|
{
|
|
void *response_msg, *current_ptr, *payload_data;
|
|
uint32_t total_write, resp_length, request_length;
|
|
size_t response_len, payload_len;
|
|
uint8_t completion_code;
|
|
struct pldm_tx_data *tx;
|
|
int num_transfers;
|
|
int rc, i;
|
|
|
|
struct pldm_write_file_req file_req = {
|
|
.file_handle = file_handle,
|
|
.offset = pos,
|
|
.length = len,
|
|
};
|
|
|
|
if (!len)
|
|
return OPAL_PARAMETER;
|
|
|
|
if ((pos) && (pos > len))
|
|
return OPAL_PARAMETER;
|
|
|
|
/* init request */
|
|
request_length = sizeof(struct pldm_msg_hdr) +
|
|
sizeof(struct pldm_write_file_req) +
|
|
len;
|
|
|
|
tx = zalloc(sizeof(struct pldm_tx_data) + request_length);
|
|
if (!tx)
|
|
return OPAL_NO_MEM;
|
|
tx->data_size = request_length - 1;
|
|
|
|
payload_data = ((struct pldm_msg *)tx->data)->payload
|
|
+ offsetof(struct pldm_write_file_req, file_data);
|
|
|
|
memcpy(payload_data, buf, len);
|
|
current_ptr = payload_data;
|
|
num_transfers = 1;
|
|
total_write = 0;
|
|
|
|
if (len > MAX_TRANSFER_SIZE_BYTES) {
|
|
num_transfers = (len + MAX_TRANSFER_SIZE_BYTES - 1) /
|
|
MAX_TRANSFER_SIZE_BYTES;
|
|
file_req.length = MAX_TRANSFER_SIZE_BYTES;
|
|
}
|
|
|
|
prlog(PR_TRACE, "%s - file_handle: %d, offset: 0x%x, len: 0x%x, num_transfers: %d\n",
|
|
__func__, file_handle, file_req.offset,
|
|
file_req.length, num_transfers);
|
|
|
|
for (i = 0; i < num_transfers; i++) {
|
|
file_req.offset = pos + (i * MAX_TRANSFER_SIZE_BYTES);
|
|
|
|
/* Encode the file request */
|
|
rc = encode_write_file_req(
|
|
DEFAULT_INSTANCE_ID,
|
|
file_req.file_handle,
|
|
file_req.offset,
|
|
file_req.length,
|
|
(struct pldm_msg *)tx->data);
|
|
if (rc != PLDM_SUCCESS) {
|
|
prlog(PR_ERR, "Encode WriteFileReq Error, rc: %d\n", rc);
|
|
free(tx);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
/* 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: WriteFileReq, rc: %d\n", rc);
|
|
free(tx);
|
|
return rc;
|
|
}
|
|
|
|
/* Decode the message */
|
|
payload_len = response_len - sizeof(struct pldm_msg_hdr);
|
|
rc = decode_write_file_resp(
|
|
response_msg,
|
|
payload_len,
|
|
&completion_code,
|
|
&resp_length);
|
|
if (rc != PLDM_SUCCESS || completion_code != PLDM_SUCCESS) {
|
|
prlog(PR_ERR, "Decode WriteFileResp Error, rc: %d, cc: %d\n",
|
|
rc, completion_code);
|
|
free(tx);
|
|
free(response_msg);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
if (resp_length == 0) {
|
|
free(response_msg);
|
|
break;
|
|
}
|
|
|
|
total_write += resp_length;
|
|
current_ptr += resp_length;
|
|
free(response_msg);
|
|
|
|
if (total_write == len)
|
|
break;
|
|
else if (resp_length != file_req.length) {
|
|
/* end of file */
|
|
break;
|
|
} else if (MAX_TRANSFER_SIZE_BYTES > (len - total_write))
|
|
file_req.length = len - total_write;
|
|
}
|
|
|
|
free(tx);
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
int pldm_file_io_write_file(uint32_t file_handle, uint32_t pos,
|
|
const void *buf, uint64_t len)
|
|
{
|
|
if (!file_io_ready)
|
|
return OPAL_HARDWARE;
|
|
|
|
return write_file_req(file_handle, pos, buf, len);
|
|
}
|
|
|
|
/*
|
|
* Send/receive a PLDM GetFileTable request message.
|
|
* The file table contains the list of files available and
|
|
* their attributes.
|
|
*
|
|
* Ex:
|
|
* {
|
|
* "FileHandle": "11",
|
|
* "FileNameLength": 12,
|
|
* "FileName": "81e0066b.lid",
|
|
* "FileSize": 589824,
|
|
* "FileTraits": 6
|
|
* },
|
|
*/
|
|
static int get_file_table_req(void)
|
|
{
|
|
size_t data_size = PLDM_MSG_SIZE(struct pldm_get_file_table_req);
|
|
size_t response_len, payload_len;
|
|
uint8_t file_table_data_start_offset;
|
|
uint8_t transfer_flag, completion_code;
|
|
struct pldm_tx_data *tx = NULL;
|
|
uint32_t next_transfer_handle;
|
|
void *response_msg;
|
|
int rc = OPAL_SUCCESS;
|
|
|
|
struct pldm_get_file_table_req file_table_req = {
|
|
.transfer_handle = 0, /* (0 if operation op is FIRSTPART) */
|
|
.operation_flag = PLDM_GET_FIRSTPART,
|
|
.table_type = PLDM_FILE_ATTRIBUTE_TABLE,
|
|
};
|
|
|
|
prlog(PR_DEBUG, "%s - GetFileReq\n", __func__);
|
|
|
|
/* Encode the file table request */
|
|
tx = zalloc(sizeof(struct pldm_tx_data) + data_size);
|
|
if (!tx)
|
|
return OPAL_NO_MEM;
|
|
tx->data_size = data_size;
|
|
|
|
rc = encode_get_file_table_req(
|
|
DEFAULT_INSTANCE_ID,
|
|
file_table_req.transfer_handle,
|
|
file_table_req.operation_flag,
|
|
file_table_req.table_type,
|
|
(struct pldm_msg *)tx->data);
|
|
if (rc != PLDM_SUCCESS) {
|
|
prlog(PR_ERR, "Encode GetFileReq Error, rc: %d\n", rc);
|
|
free(tx);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
/* 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: GetFileReq, rc: %d\n", rc);
|
|
free(tx);
|
|
return rc;
|
|
}
|
|
|
|
/* Decode the message */
|
|
payload_len = response_len - sizeof(struct pldm_msg_hdr);
|
|
rc = decode_get_file_table_resp(
|
|
response_msg,
|
|
payload_len,
|
|
&completion_code,
|
|
&next_transfer_handle,
|
|
&transfer_flag,
|
|
&file_table_data_start_offset,
|
|
&file_attr_length);
|
|
if (rc != PLDM_SUCCESS || completion_code != PLDM_SUCCESS) {
|
|
prlog(PR_ERR, "Decode GetFileResp Error, rc: %d, cc: %d\n",
|
|
rc, completion_code);
|
|
rc = OPAL_PARAMETER;
|
|
goto out;
|
|
}
|
|
|
|
/* we do not support multipart transfer */
|
|
if ((next_transfer_handle != PLDM_GET_NEXTPART) ||
|
|
(transfer_flag != PLDM_START_AND_END)) {
|
|
prlog(PR_ERR, "Transfert GetFileResp not complete, "
|
|
"transfer_hndl: %d, transfer_flag: %d\n",
|
|
next_transfer_handle,
|
|
transfer_flag);
|
|
}
|
|
|
|
file_attr_table = zalloc(file_attr_length);
|
|
if (!file_attr_table) {
|
|
rc = OPAL_NO_MEM;
|
|
goto out;
|
|
}
|
|
|
|
memcpy(file_attr_table,
|
|
((struct pldm_msg *)response_msg)->payload +
|
|
file_table_data_start_offset,
|
|
file_attr_length);
|
|
|
|
out:
|
|
free(tx);
|
|
free(response_msg);
|
|
return rc;
|
|
}
|
|
|
|
int pldm_file_io_init(void)
|
|
{
|
|
int rc;
|
|
|
|
/* PLDM GetFileTable request */
|
|
rc = get_file_table_req();
|
|
if (rc)
|
|
goto err;
|
|
|
|
file_io_init_complete(true);
|
|
prlog(PR_DEBUG, "%s - done\n", __func__);
|
|
|
|
return OPAL_SUCCESS;
|
|
|
|
err:
|
|
file_io_init_complete(false);
|
|
return rc;
|
|
}
|