Jtag / OpenOCD / GDB

NaxRiscv implements the RISCV External Debug Support v. 0.13.2 specification via JTAG. This enables upstream openocd support, which in itself allows to use GDB to debug software running on a target.

The JTAG layer supports 2 modes:

  • Native : Where a full JTAG TAP is implemented

  • Tunneled : Where an external JTAG TAP should provide a single JTAG instruction port to take control (ex BSCANE2 from xilinx)

The implementation of the debug spec is done as follows :

  • Abstract access register to x0-x31 (implemented by running a hidden abstract program buffer)

  • Abstract program buffer to do everything else (memory/CSR access)

  • The HART can read abstract command arguments via a CSR read, and provide a return value via a CSR write

  • Triggers can only be used as hardware breakpoints

  • PrivilegedPlugin implement things related to the HART, while the EmbeddedJtagPlugin integrate the different JTAG alternatives and DM in the core.

  • The tunneled jtag mode use the BSCAN_TUNNEL_DATA_REGISTER mode, not the default one.

The reason why BSCAN_TUNNEL_DATA_REGISTER was picked instead of the BSCAN_TUNNEL_NESTED_TAP is to properly support JTAG chains with multiple taps. During a JTAG scan with openocd, the only reference you can have is relative to the end of a DR-SHIFT, you can’t use the beginning of the DR-SHIFT as a reference, because openocd may add a few cycles there to propagate the data through the chain to the selected tap, which make BSCAN_TUNNEL_NESTED_TAP unpractical as the bits which identify the nature of its DR-SHIFT are sent first.


Side channel attack

You can find an example of side channel attack here (need NaxRiscv hardware protection to be disabled): https://github.com/SpinalHDL/NaxSoftware/tree/main/baremetal/side_channel/src https://github.com/SpinalHDL/NaxSoftware/blob/main/baremetal/side_channel/src/side_channel.S

It allows in user mode to read the data from the supervisor. Bypassing page fault.

It is based on a few things

  • Speculative execution

  • Load page fault still writting in the register file the readed value

  • Load dependencies being waked up by the load cache hit predictor

  • Using that rogue value to produce a cache line refill

  • Mesuring which cache line is loaded to figure out the secret value

The image bellow show the execution of the attack (in grayish color is the speculative execution which wasn’t commited) :

  1. Call the attack function

  2. Execute some slow code (store -> load value forwarding) to delay the commit / flush of the pipeline

  3. Having a misspredicted branch to avoid commiting the faulty code

  4. Attacking the supervisor memory with a load

  5. Producing a cache load at an address which depend on the readed supervisor memory (mem[(data >> bitId) << 6]

  6. Back on track, the cpu rolled back all the speculative execution, but the step 5 got a specific cache line loaded

Then after that execution, mesuring which cache line is loaded will tell use the value of the secret bit from supervisor

How to reproduce

Follow https://github.com/SpinalHDL/NaxRiscv/tree/main/src/test/cpp/naxriscv#how-to-setup-things but with a few modification :

Once that whole setup is done, you can run the simulation via :

obj_dir/VNaxRiscv --name play --load-elf ../../../../ext/NaxSoftware/baremetal/side_channel/build/rv64ima/side_channel.elf --start-symbol _start --pass-symbol pass --fail-symbol fail

You can add some arguments to generate log files in the output folder (will slow down things) via :

–trace –trace-ref –trace –trace-ref –spike-debug