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
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) :
Call the attack function
Execute some slow code (store -> load value forwarding) to delay the commit / flush of the pipeline
Having a misspredicted branch to avoid commiting the faulty code
Attacking the supervisor memory with a load
Producing a cache load at an address which depend on the readed supervisor memory (mem[(data >> bitId) << 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 :
Before sbt “runMain naxriscv.Gen”, edit sideChannels to true (https://github.com/SpinalHDL/NaxRiscv/blob/95b1cc8e5c69d3a2fe12af8f7478a2492971a0d1/src/main/scala/naxriscv/Gen.scala#L435)
Instead of sbt “runMain naxriscv.Gen”, run sbt “runMain naxriscv.Gen64”
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