In MCD, all accesses to register or memory are issued over transaction lists. This commit implements three types of transactions:
* register access * logical memory access (with MMU) * physical memory access (no MMU) Signed-off-by: Mario Fleischmann <mario.fleischm...@lauterbach.com> --- mcd/libmcd_qapi.c | 128 +++++++++++++++++++++++++++++++++ mcd/libmcd_qapi.h | 14 ++++ mcd/mcdserver.c | 176 ++++++++++++++++++++++++++++++++++++++++++++- mcd/mcdstub_qapi.c | 26 +++++++ qapi/mcd.json | 83 +++++++++++++++++++++ 5 files changed, 425 insertions(+), 2 deletions(-) diff --git a/mcd/libmcd_qapi.c b/mcd/libmcd_qapi.c index 44fc9bd6b4..8088a9eed2 100644 --- a/mcd/libmcd_qapi.c +++ b/mcd/libmcd_qapi.c @@ -176,6 +176,18 @@ MCDAddr *marshal_mcd_addr(const mcd_addr_st *addr) return marshal; } +mcd_addr_st unmarshal_mcd_addr(MCDAddr *addr) +{ + mcd_addr_st unmarshal = { + .address = addr->address, + .mem_space_id = addr->mem_space_id, + .addr_space_id = addr->addr_space_id, + .addr_space_type = addr->addr_space_type, + }; + + return unmarshal; +} + MCDRegisterInfo *marshal_mcd_register_info(const mcd_register_info_st *reg_info) { MCDRegisterInfo *marshal = g_malloc0(sizeof(*marshal)); @@ -211,3 +223,119 @@ MCDCoreState *marshal_mcd_core_state(const mcd_core_state_st *state) return marshal; } + +MCDTx *marshal_mcd_tx(const mcd_tx_st *tx) +{ + MCDTx *marshal = g_malloc0(sizeof(*marshal)); + + *marshal = (MCDTx) { + .addr = marshal_mcd_addr(&tx->addr), + .access_type = tx->access_type, + .options = tx->options, + .access_width = tx->access_width, + .core_mode = tx->core_mode, + .num_bytes = tx->num_bytes, + .num_bytes_ok = tx->num_bytes_ok, + }; + + if (tx->num_bytes == 0) { + return marshal; + } + + g_assert(tx->data); + + uint8List *head = marshal->data; + for (int i = tx->num_bytes - 1; i >= 0; i--) { + QAPI_LIST_PREPEND(head, tx->data[i]); + } + marshal->data = head; + return marshal; +} + +mcd_tx_st unmarshal_mcd_tx(MCDTx *tx) +{ + mcd_tx_st unmarshal = { + .addr = unmarshal_mcd_addr(tx->addr), + .access_type = tx->access_type, + .options = tx->options, + .access_width = tx->access_width, + .core_mode = tx->core_mode, + .data = NULL, + .num_bytes = tx->num_bytes, + .num_bytes_ok = tx->num_bytes_ok, + }; + + if (tx->num_bytes == 0) { + return unmarshal; + } + + unmarshal.data = g_malloc(tx->num_bytes); + uint8List *current_data = tx->data; + for (uint32_t i = 0; i < tx->num_bytes; i++) { + g_assert(current_data); + unmarshal.data[i] = current_data->value; + current_data = current_data->next; + } + + return unmarshal; +} + +void free_mcd_tx(mcd_tx_st *tx) +{ + g_free(tx->data); +} + +MCDTxlist *marshal_mcd_txlist(const mcd_txlist_st *txlist) +{ + MCDTxlist *marshal = g_malloc0(sizeof(*marshal)); + + *marshal = (MCDTxlist) { + .num_tx = txlist->num_tx, + .num_tx_ok = txlist->num_tx_ok, + }; + + if (txlist->num_tx == 0) { + return marshal; + } + + g_assert(txlist->tx); + + MCDTxList **tailp = &(marshal->tx); + for (uint32_t i = 0; i < txlist->num_tx; i++) { + MCDTx *tx = marshal_mcd_tx(txlist->tx + i); + QAPI_LIST_APPEND(tailp, tx); + } + + return marshal; +} + +mcd_txlist_st unmarshal_mcd_txlist(MCDTxlist *txlist) +{ + mcd_txlist_st unmarshal = { + .tx = g_malloc(txlist->num_tx * sizeof(mcd_tx_st)), + .num_tx = txlist->num_tx, + .num_tx_ok = txlist->num_tx_ok, + }; + + MCDTxList *current_tx = txlist->tx; + for (uint32_t i = 0; i < txlist->num_tx; i++) { + g_assert(current_tx); + unmarshal.tx[i] = unmarshal_mcd_tx(current_tx->value); + current_tx = current_tx->next; + } + + return unmarshal; +} + +void free_mcd_txlist(mcd_txlist_st *txlist) +{ + if (!txlist->tx) { + return; + } + + for (uint32_t i = 0; i < txlist->num_tx; i++) { + free_mcd_tx(txlist->tx + i); + } + + g_free(txlist->tx); +} diff --git a/mcd/libmcd_qapi.h b/mcd/libmcd_qapi.h index 7d68d60f02..27bb945db5 100644 --- a/mcd/libmcd_qapi.h +++ b/mcd/libmcd_qapi.h @@ -37,8 +37,22 @@ MCDRegisterInfo *marshal_mcd_register_info( MCDCoreState *marshal_mcd_core_state(const mcd_core_state_st *state); +MCDTx *marshal_mcd_tx(const mcd_tx_st *tx); + +MCDTxlist *marshal_mcd_txlist(const mcd_txlist_st *txlist); + mcd_api_version_st unmarshal_mcd_api_version(MCDAPIVersion *api_version); mcd_core_con_info_st unmarshal_mcd_core_con_info(MCDCoreConInfo *con_info); +mcd_addr_st unmarshal_mcd_addr(MCDAddr *addr); + +mcd_tx_st unmarshal_mcd_tx(MCDTx *tx); + +mcd_txlist_st unmarshal_mcd_txlist(MCDTxlist *txlist); + +void free_mcd_tx(mcd_tx_st *tx); + +void free_mcd_txlist(mcd_txlist_st *txlist); + #endif /* LIBMCD_QAPI_H */ diff --git a/mcd/mcdserver.c b/mcd/mcdserver.c index 116fbfaa30..837c0276e7 100644 --- a/mcd/mcdserver.c +++ b/mcd/mcdserver.c @@ -1081,11 +1081,183 @@ mcd_return_et mcd_qry_trig_set_state_f(const mcd_core_st *core, return g_server_state.last_error->return_status; } +static mcd_return_et execute_memory_tx(mcdcore_state *core_state, mcd_tx_st *tx, + mcd_mem_type_et type) +{ + MemTxResult result; + + /* each address space has one physical and one virtual memory */ + int address_space_id = (tx->addr.mem_space_id - 1) / 2; + AddressSpace *as = cpu_get_address_space(core_state->cpu, address_space_id); + + hwaddr addr = tx->addr.address; + hwaddr len = tx->num_bytes; + void *buf = tx->data; + bool is_write; + + if (tx->access_type == MCD_TX_AT_R) { + is_write = false; + } else if (tx->access_type == MCD_TX_AT_W) { + is_write = true; + } else { + core_state->custom_error = (mcd_error_info_st) { + .return_status = MCD_RET_ACT_HANDLE_ERROR, + .error_code = MCD_ERR_TXLIST_TX, + .error_events = MCD_ERR_EVT_NONE, + .error_str = "tx access type not supported", + }; + core_state->last_error = &core_state->custom_error; + return core_state->last_error->return_status; + } + + if (type & MCD_MEM_SPACE_IS_PHYSICAL) { + MemTxAttrs attrs = { + .secure = !!(type & MCD_MEM_SPACE_IS_SECURE), + .space = address_space_id, + }; + result = address_space_rw(as, addr, attrs, buf, len, is_write); + } else if (type & MCD_MEM_SPACE_IS_LOGICAL) { + int ret = cpu_memory_rw_debug(core_state->cpu, addr, buf, len, + is_write); + result = (ret == 0) ? MEMTX_OK : MEMTX_ERROR; + } else { + core_state->custom_error = (mcd_error_info_st) { + .return_status = MCD_RET_ACT_HANDLE_ERROR, + .error_code = MCD_ERR_TXLIST_TX, + .error_events = MCD_ERR_EVT_NONE, + .error_str = "unknown mem space type", + }; + core_state->last_error = &core_state->custom_error; + return core_state->last_error->return_status; + } + + if (result != MEMTX_OK) { + core_state->custom_error = (mcd_error_info_st) { + .return_status = MCD_RET_ACT_HANDLE_ERROR, + .error_code = is_write ? MCD_ERR_TXLIST_WRITE : MCD_ERR_TXLIST_READ, + .error_events = MCD_ERR_EVT_NONE, + .error_str = "", + }; + snprintf(core_state->custom_error.error_str, MCD_INFO_STR_LEN, + "Memory tx failed with error code %d", result); + core_state->last_error = &core_state->custom_error; + return core_state->last_error->return_status; + } + + tx->num_bytes_ok = tx->num_bytes; + core_state->last_error = &MCD_ERROR_NONE; + return core_state->last_error->return_status; +} + +static mcd_return_et execute_register_tx(mcdcore_state *core_state, + mcd_tx_st *tx) +{ + if (tx->access_type == MCD_TX_AT_R) { + GByteArray *mem_buf = g_byte_array_new(); + int read_bytes = gdb_read_register(core_state->cpu, mem_buf, + tx->addr.address); + if (read_bytes > tx->num_bytes) { + g_byte_array_free(mem_buf, true); + core_state->custom_error = (mcd_error_info_st) { + .return_status = MCD_RET_ACT_HANDLE_ERROR, + .error_code = MCD_ERR_TXLIST_READ, + .error_events = MCD_ERR_EVT_NONE, + .error_str = "too many bytes read", + }; + core_state->last_error = &core_state->custom_error; + return core_state->last_error->return_status; + } + memcpy(tx->data, mem_buf->data, read_bytes); + g_byte_array_free(mem_buf, true); + tx->num_bytes_ok = read_bytes; + } else if (tx->access_type == MCD_TX_AT_W) { + int written_bytes = gdb_write_register(core_state->cpu, tx->data, + tx->addr.address); + if (written_bytes > tx->num_bytes) { + core_state->custom_error = (mcd_error_info_st) { + .return_status = MCD_RET_ACT_HANDLE_ERROR, + .error_code = MCD_ERR_TXLIST_READ, + .error_events = MCD_ERR_EVT_NONE, + .error_str = "too many bytes written", + }; + core_state->last_error = &core_state->custom_error; + return core_state->last_error->return_status; + } + tx->num_bytes_ok = written_bytes; + } else { + core_state->custom_error = (mcd_error_info_st) { + .return_status = MCD_RET_ACT_HANDLE_ERROR, + .error_code = MCD_ERR_TXLIST_TX, + .error_events = MCD_ERR_EVT_NONE, + .error_str = "tx access type not supported", + }; + core_state->last_error = &core_state->custom_error; + return core_state->last_error->return_status; + } + + core_state->last_error = &MCD_ERROR_NONE; + return core_state->last_error->return_status; +} + +static mcd_return_et execute_tx(mcdcore_state *core_state, mcd_tx_st *tx) +{ + mcd_memspace_st *ms; + + uint32_t ms_id = tx->addr.mem_space_id; + if (ms_id == 0 || ms_id > core_state->memory_spaces->len) { + core_state->custom_error = (mcd_error_info_st) { + .return_status = MCD_RET_ACT_HANDLE_ERROR, + .error_code = MCD_ERR_PARAM, + .error_events = MCD_ERR_EVT_NONE, + .error_str = "unknown memory space ID", + }; + } + + ms = &g_array_index(core_state->memory_spaces, mcd_memspace_st, ms_id - 1); + if (ms->mem_type & MCD_MEM_SPACE_IS_PHYSICAL || + ms->mem_type & MCD_MEM_SPACE_IS_LOGICAL) { + return execute_memory_tx(core_state, tx, ms->mem_type); + } else if (ms->mem_type & MCD_MEM_SPACE_IS_REGISTERS) { + return execute_register_tx(core_state, tx); + } else { + core_state->custom_error = (mcd_error_info_st) { + .return_status = MCD_RET_ACT_HANDLE_ERROR, + .error_code = MCD_ERR_TXLIST_TX, + .error_events = MCD_ERR_EVT_NONE, + .error_str = "unknown memory space type", + }; + core_state->last_error = &core_state->custom_error; + return core_state->last_error->return_status; + } +} + mcd_return_et mcd_execute_txlist_f(const mcd_core_st *core, mcd_txlist_st *txlist) { - g_server_state.last_error = &MCD_ERROR_NOT_IMPLEMENTED; - return g_server_state.last_error->return_status; + mcdcore_state *core_state; + + if (!core || !txlist) { + g_server_state.last_error = &MCD_ERROR_INVALID_NULL_PARAM; + return g_server_state.last_error->return_status; + } + + core_state = find_core(core->core_con_info); + if (!core_state || core_state->open_core != core) { + g_server_state.last_error = &MCD_ERROR_UNKNOWN_CORE; + return g_server_state.last_error->return_status; + } + + for (uint32_t i = 0; i < txlist->num_tx; i++) { + mcd_tx_st *tx = txlist->tx + i; + if (execute_tx(core_state, tx) != MCD_RET_ACT_NONE) { + return core_state->last_error->return_status; + } else { + txlist->num_tx_ok++; + } + } + + core_state->last_error = &MCD_ERROR_NONE; + return core_state->last_error->return_status; } mcd_return_et mcd_run_f(const mcd_core_st *core, bool global) diff --git a/mcd/mcdstub_qapi.c b/mcd/mcdstub_qapi.c index e1bde14b47..724b7ff7ea 100644 --- a/mcd/mcdstub_qapi.c +++ b/mcd/mcdstub_qapi.c @@ -506,6 +506,32 @@ MCDQryRegMapResult *qmp_mcd_qry_reg_map(uint32_t core_uid, return result; } +MCDExecuteTxlistResult *qmp_mcd_execute_txlist(uint32_t core_uid, + MCDTxlist *txlist, + Error **errp) +{ + MCDExecuteTxlistResult *result = g_malloc0(sizeof(*result)); + mcd_core_st *core = NULL; + + result->return_status = retrieve_open_core(core_uid, &core); + if (result->return_status != MCD_RET_ACT_NONE) { + g_stub_state.on_error_ask_server = false; + return result; + } + + mcd_txlist_st txlist_unmarshalled = unmarshal_mcd_txlist(txlist); + + result->return_status = mcd_execute_txlist_f(core, &txlist_unmarshalled); + + if (result->return_status == MCD_RET_ACT_NONE) { + result->txlist = marshal_mcd_txlist(&txlist_unmarshalled); + } + + free_mcd_txlist(&txlist_unmarshalled); + g_stub_state.on_error_ask_server = true; + return result; +} + MCDRunResult *qmp_mcd_run(uint32_t core_uid, bool global, Error **errp) { MCDRunResult *result = g_malloc0(sizeof(*result)); diff --git a/qapi/mcd.json b/qapi/mcd.json index 3560658451..fabb3eea89 100644 --- a/qapi/mcd.json +++ b/qapi/mcd.json @@ -306,6 +306,54 @@ 'reg-type' : 'uint32', 'hw-thread-id' : 'uint32' } } +## +# @MCDTx: +# +# Structure type containing information about a single transaction. +# +# @addr: The address of the first memory cell/register. +# @access-type: Type of access: Read/Write/Read+Write/Write+Verify. +# @options: Access options: burst, side-effects, alternate path, cache, +# etc. +# @access-width: Access size in bytes (or 0 if access size does not matter). +# @core-mode: The core mode in which the access should be performed (or 0 +# for most permissive mode). +# @data: Byte array of size @num-bytes storing the access data. +# @num-bytes: Size of the memory/register access. The buffer @data needs to +# be of this size. +# @num-bytes-ok: Number of successfully received/sent bytes. +# +# Since: 9.1 +## +{ 'struct': 'MCDTx', + 'data': { + 'addr' : 'MCDAddr', + 'access-type' : 'uint32', + 'options' : 'uint32', + 'access-width': 'uint8', + 'core-mode' : 'uint8', + 'data' : ['uint8'], + 'num-bytes' : 'uint32', + 'num-bytes-ok': 'uint32' } } + + +## +# @MCDTxlist: +# +# Structure type containing a transaction list. +# +# @tx: Array of size @num-tx storing the transactions. +# @num-tx: Number of transactions. +# @num-tx-ok: Number of transactions which succeeded without any errors. +# +# Since: 9.1 +## +{ 'struct': 'MCDTxlist', + 'data': { + 'tx' : ['MCDTx'], + 'num-tx' : 'uint32', + 'num-tx-ok': 'uint32' } } + ## # @MCDCoreState: @@ -1403,6 +1451,41 @@ ## +## +# @MCDExecuteTxlistResult: +# +# Return value of @mcd-execute-txlist. +# +# @return-status: Return code. +# @txlist: Transaction list after execution. +# +# Since: 9.1 +## +{ 'struct': 'MCDExecuteTxlistResult', + 'data': { + 'return-status': 'uint32', + '*txlist' : 'MCDTxlist' }} + + +## +# @mcd-execute-txlist: +# +# Function executing a transaction list on the target. +# +# @core-uid: Unique identifier of the open core as returned by @mcd-open-core. +# @txlist: Transaction list for execution. +# +# Returns: @MCDExecuteTxlistResult +# +# Since: 9.1 +## +{ 'command': 'mcd-execute-txlist', + 'data': { + 'core-uid': 'uint32', + 'txlist' : 'MCDTxlist' }, + 'returns': 'MCDExecuteTxlistResult' } + + ## # @MCDRunResult: # -- 2.34.1