Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Darjeeling updates #55

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 208 additions & 0 deletions docs/opentitan/jtagmbx.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
# Darjeeling JTAG Mailbox

The JTAG mailbox host side (responder) is connected to the private OT address space, while the
system side (requester) is connected to a Debug Tile Link bus.

## Communicating with the JTAG Mailbox through a JTAG connection

In QEMU, a bridge between the Debug Module Interface (DMI) and the JTAG Mailbox is implemented
as Debug Module bridge.

```
+----------------+
| Host (OpenOCD) |
+----------------+
|
| TCP connection ("bitbang mode")
|
+-----|-----------------------------------------------------------------------------------+
| v |
| +-------------+ +-----+ +----------+ +---------+ +------+ |
| | JTAG server |---->| DMI |---->| ot_dm_tl |====D====|S> MBX <H|====P====| Hart | |
| +-------------+ +-----+ +----------+ +---------+ +------+ |
| QEMU|
+-----------------------------------------------------------------------------------------+
```

where:
`P` is the private OT bus
`D` is the debug bus

QEMU should be started with an option such as `-jtag tcp::3335` so that the JTAG server is
instantiated and listen for incoming connection on TCP port 3335.

It is possible from OpenOCD running a host to connect to the embedded JTAG server using the
`remote_bitbang` protocol, using a configuration script such as

```tcl
adapter driver remote_bitbang
remote_bitbang host localhost
remote_bitbang port 3335

transport select jtag

set _CHIPNAME riscv
# part 1, ver 0, lowRISC; actual TAP id depends on the QEMU machine
set _CPUTAPID 0x00011cdf

jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id $_CPUTAPID

# Hart debug base also depends on the QEMU machine
set DM_BASE 0x00

target create $_CHIPNAME riscv -chain-position $_TARGETNAME rtos hwthread -dbgbase $DM_BASE
```

From here, it is possible reach the JTAG dmi through the DMI device:

```tcl
# Mailbox system register addresses
set MBX_SYS_BASE 0x200

set SYS_INTR_MSG_ADDR $($MBX_SYS_BASE + 0x0)
set SYS_INTR_MSG_DATA $($MBX_SYS_BASE + 0x1)
set SYS_CONTROL $($MBX_SYS_BASE + 0x2)
set SYS_STATUS $($MBX_SYS_BASE + 0x3)
set SYS_WRITE_DATA $($MBX_SYS_BASE + 0x4)
set SYS_READ_DATA $($MBX_SYS_BASE + 0x5)

# Mailbox system register bits
set SYS_CONTROL_ABORT 0x1
set SYS_CONTROL_SYS_INT_EN 0x2
set SYS_CONTROL_GO 0x80000000

set SYS_STATUS_BUSY 0x1
set SYS_STATUS_INT 0x2
set SYS_STATUS_ERROR 0x4
set SYS_STATUS_READY 0x80000000

# read the STATUS register
dmi_read $SYS_STATUS

# write the GO bit of the control register
dmi_write $SYS_CONTROL $SYS_CONTROL_GO
```

Unfortunalely the current version of OpenOCD (v0.12+) does not support a JTAG-DMI communication if
no RISC-V DM module is connected to the DMI device, _i.e._

```
+----------------+
| Host (OpenOCD) |
+----------------+
|
| TCP connection ("bitbang mode")
|
+-----|-----------------------------------------------------------------------------------+
| v |
| +-------------+ +-----+ +----------+ +---------+ +------+ |
| | JTAG server |---->| DMI |-+-->| ot_dm_tl |====D====|S> MBX <H|===++=P==| Hart | |
| +-------------+ +-----+ | +----------+ +---------+ || +------+ |
| | || |
| | +----+ +-------- + || |
| -->| DM |---->| PulpDM |============== |
| +----+ +---------+ |
| | ROM/RAM | |
| +---------+ |
| |
| QEMU|
+-----------------------------------------------------------------------------------------+
```

If no RISC-V compatible DM is detected at `$DM_BASE`, OpenOCD enters an infinite loop seeking a
valid DM module.

DM / PulpDM should be part of an upcoming delivery. Meanwhile it is required to tweak OpenOCD so
that it accepts staying connected to the DMI even if it cannot locate a valud DM:

```diff
diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c
index 2ab28deac..0e84418d9 100644
--- a/src/target/riscv/riscv-013.c
+++ b/src/target/riscv/riscv-013.c
@@ -1882,6 +1882,8 @@ static int examine(struct target *target)
return ERROR_FAIL;
}

+ target->state = TARGET_UNAVAILABLE;
+ return ERROR_OK;
/* Reset the Debug Module. */
dm013_info_t *dm = get_dm(target);
if (!dm)
diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c
index 5bae01d5f..786f2520a 100644
--- a/src/target/riscv/riscv.c
+++ b/src/target/riscv/riscv.c
@@ -3167,6 +3167,8 @@ int riscv_openocd_poll(struct target *target)
{
LOG_TARGET_DEBUG_IO(target, "Polling all harts.");

+ return ERROR_OK;
+
struct list_head *targets;

LIST_HEAD(single_target_list);
```

## Communicating with the JTAG Mailbox through a DevProxy connection

DevProxy is a communication tool that enables to control QEMU devices from a remote host, over a
TCP connection.

It can access QEMU `SysBusDevice` through their `MemoryRegion` API, intercepts or generate IRQs on
those devices, intercepts accesses to plain RAM region and read or modify their content. See the
[devproxy.md](devproxy.md) document for information about the DevProxy communication protocol and
supported features.

The `devproxy.py` Python module implements the DevProxy protocol and can be used on a host to
remotely control selected devices in the QEMU machine. It includes support for the Mailbox devices
for both the Host and System interfaces.

```
+--------------------+
| Host (Python, ...) |
+--------------------+
|
| TCP connection
|
+-----|-----------------------------------------+
| v |
| +-----------------+ |
| | DevProxy server |------ |
| +-----------------+ | |
| | | |
| CTN Private |
| | | |
| | +---------+ | |
| +--->|S> MBX <H|<---+ |
| | +---------+ | +------+ |
| | +---<| Hart | |
| | +---------+ | +------+ |
| +--->|S> MBX <H|<---+ |
| | +---------+ | +---------+ |
| | +--->| MBX RAM | |
| | +---------+ | +---------+ |
| +--->|S> MBX <H|<---+ |
| | +---------+ | +-----+ |
| | +--->| RAM | |
| | | +-----+ |
| ... | |
| | +---------+ | |
| +--->| IPI | | |
| | +---------+ | |
| ... ... |
| QEMU|
+-----------------------------------------------+
```

QEMU should be started with a option pair to enable communication with the DevProxy server:
* `-chardev socket,id=devproxy,host=localhost,port=8003,server=on,wait=off
* `-global ot-dev_proxy.chardev=devproxy`

Subsequently, a Python script importing the `devproxy.py` module can be used to communicate with
the JTAG mailbox.

Note: `devproxy.py` needs to be found within the Python path, using for example
```sh
exprot PYTHONPATH=${QEMU_SOURCE_PATH}/scripts/opentitan
```
6 changes: 5 additions & 1 deletion docs/opentitan/pyot.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

````text
usage: pyot.py [-h] [-q QEMU] [-Q OPTS] [-m MACHINE] [-p DEVICE] [-L LOG_FILE]
[-M LOG] [-t TRACE] [-i N] [-s] [-T SECS] [-U] [-c JSON]
[-M LOG] [-t TRACE] [-i N] [-s] [-T SECS] [-U] [-D] [-c JSON]
[-w CSV] [-K] [-r ELF] [-O RAW] [-o VMEM] [-f RAW] [-x file]
[-b file] [-R] [-k SECONDS] [-F TEST] [-v] [-d]

Expand Down Expand Up @@ -35,6 +35,8 @@ Virtual machine:
timeout factor
-U, --muxserial enable multiple virtual UARTs to be muxed into same
host output channel
-D DELAY, --start-delay DELAY
QEMU start up delay before initial comm

Files:
-c JSON, --config JSON
Expand Down Expand Up @@ -118,6 +120,8 @@ This tool may be used in two ways, which can be combined:
it can be greater to increase timeouts or lower than 1 to decrease timeouts.
* `-U` / `--muxserial` enable muxing QEMU VCP. This option is required when several virtual UARTs
are routed to the same host output channel.
* `-D` / `--start-delay` VM start up delay. Grace period to wait for the VM to start up before
attempting to communicate with its char devices.

### File options:

Expand Down
4 changes: 2 additions & 2 deletions hw/opentitan/ot_aon_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,8 @@ static uint64_t ot_aon_timer_read(void *opaque, hwaddr addr, unsigned size)
case R_ALERT_TEST:
case R_INTR_TEST:
qemu_log_mask(LOG_GUEST_ERROR,
"W/O register 0x%02" HWADDR_PRIx " (%s)\n", addr,
REG_NAME(reg));
"%s: W/O register 0x%02" HWADDR_PRIx " (%s)\n", __func__,
addr, REG_NAME(reg));
val32 = 0;
break;
default:
Expand Down
4 changes: 2 additions & 2 deletions hw/opentitan/ot_clkmgr.c
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,8 @@ static uint64_t ot_clkmgr_read(void *opaque, hwaddr addr, unsigned size)
break;
case R_ALERT_TEST:
qemu_log_mask(LOG_GUEST_ERROR,
"W/O register 0x%02" HWADDR_PRIx " (%s)\n", addr,
REG_NAME(reg));
"%s: W/O register 0x%02" HWADDR_PRIx " (%s)\n", __func__,
addr, REG_NAME(reg));
val32 = 0;
break;
default:
Expand Down
16 changes: 7 additions & 9 deletions hw/opentitan/ot_csrng.c
Original file line number Diff line number Diff line change
Expand Up @@ -427,17 +427,16 @@ int ot_csrng_push_command(OtCSRNGState *s, unsigned app_id,
/*
* Very special hacky case:
*
* Uninstanciation is requested before instanciation has been
* Uninstantiation is requested before instantiation has been
* completed (instantiation command in still in the command queue).
* Flush the command queue to discard the instantiation command,
* which stays uncompleted on the client side, and resume (the
* uninstanciation request should complete this new command).
* uninstantiation request should complete this new command).
*
* It might be useful to implement a "cancel outstanding request"
* API so that the EDN can tell CSRNG its last command needs to be
* cancelled before completion - and remove this workaround.
*/
OtCSRNGState *s = inst->parent;
QSIMPLEQ_REMOVE(&s->cmd_requests, inst, OtCSRNGInstance,
cmd_request);

Expand Down Expand Up @@ -921,7 +920,7 @@ static void ot_csrng_handle_enable(OtCSRNGState *s)

/* reset all instances */
for (unsigned ix = 0u; ix < OT_CSRNG_HW_APP_MAX + 1u; ix++) {
OtCSRNGInstance *inst = &s->instances[ix];
inst = &s->instances[ix];
ot_csrng_drng_uninstantiate(inst);
ot_fifo32_reset(&inst->cmd_fifo);
if (ix == SW_INSTANCE_ID) {
Expand Down Expand Up @@ -1411,7 +1410,6 @@ static void ot_csrng_command_scheduler(void *opaque)

if (!ot_csrng_instance_is_command_ready(inst)) {
xtrace_ot_csrng_error("cannot execute an incomplete command");
uint32_t command = ot_fifo32_peek(&inst->cmd_fifo);
uint32_t length = FIELD_EX32(command, OT_CSNRG_CMD, CLEN) + 1u;
qemu_log_mask(LOG_GUEST_ERROR, "empty: %u, length %u, exp: %u\n",
ot_fifo32_is_empty(&inst->cmd_fifo),
Expand Down Expand Up @@ -1589,8 +1587,8 @@ static uint64_t ot_csrng_regs_read(void *opaque, hwaddr addr, unsigned size)
case R_ALERT_TEST:
case R_CMD_REQ:
qemu_log_mask(LOG_GUEST_ERROR,
"W/O register 0x%02" HWADDR_PRIx " (%s)\n", addr,
REG_NAME(reg));
"%s: W/O register 0x%02" HWADDR_PRIx " (%s)\n", __func__,
addr, REG_NAME(reg));
val32 = 0;
break;
default:
Expand Down Expand Up @@ -1737,8 +1735,8 @@ static void ot_csrng_regs_write(void *opaque, hwaddr addr, uint64_t val64,
case R_ERR_CODE:
case R_MAIN_SM_STATE:
qemu_log_mask(LOG_GUEST_ERROR,
"R/O register 0x%02" HWADDR_PRIx " (%s)\n", addr,
REG_NAME(reg));
"%s: R/O register 0x%02" HWADDR_PRIx " (%s)\n", __func__,
addr, REG_NAME(reg));
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
Expand Down
1 change: 1 addition & 0 deletions hw/opentitan/ot_dev_proxy.c
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ static void ot_dev_proxy_enumerate_devices(OtDevProxyState *s)
const OtDevProxyItem *item = &s->items[ix];
const OtDevProxyCaps *caps = &item->caps;
struct entry *entry = &entries[count];
memset(entry, 0, sizeof(*entry));
if (object_dynamic_cast(item->obj, TYPE_OT_MBX)) {
memcpy(&entry->desc[0u], item->prefix, 4u);
const char *oid =
Expand Down
Loading
Loading