diff --git a/.buildinfo b/.buildinfo new file mode 100644 index 000000000..24782f3e9 --- /dev/null +++ b/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: a33658b24e23c01ddcc34064eabd005b +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/_images/annotation.png b/_images/annotation.png new file mode 100644 index 000000000..bdac12295 Binary files /dev/null and b/_images/annotation.png differ diff --git a/_images/annotation_zoom.png b/_images/annotation_zoom.png new file mode 100644 index 000000000..22b5f0b7c Binary files /dev/null and b/_images/annotation_zoom.png differ diff --git a/_images/architecture.png b/_images/architecture.png new file mode 100644 index 000000000..ddc5e44be Binary files /dev/null and b/_images/architecture.png differ diff --git a/_images/arty-a7.jpg b/_images/arty-a7.jpg new file mode 100644 index 000000000..5a7c6bb03 Binary files /dev/null and b/_images/arty-a7.jpg differ diff --git a/_images/datacenter-rdimm-ddr5-tester.png b/_images/datacenter-rdimm-ddr5-tester.png new file mode 100644 index 000000000..250493b29 Binary files /dev/null and b/_images/datacenter-rdimm-ddr5-tester.png differ diff --git a/_images/ddr4_datacenter_dram_tester.jpg b/_images/ddr4_datacenter_dram_tester.jpg new file mode 100644 index 000000000..7eea08c4c Binary files /dev/null and b/_images/ddr4_datacenter_dram_tester.jpg differ diff --git a/_images/dqs_vict_vs_aggr.png b/_images/dqs_vict_vs_aggr.png new file mode 100644 index 000000000..3cccb4f3f Binary files /dev/null and b/_images/dqs_vict_vs_aggr.png differ diff --git a/_images/f4pga_visualizer_aggr_vs_vict.png b/_images/f4pga_visualizer_aggr_vs_vict.png new file mode 100644 index 000000000..744561c01 Binary files /dev/null and b/_images/f4pga_visualizer_aggr_vs_vict.png differ diff --git a/_images/lpddr4-test-board.jpg b/_images/lpddr4-test-board.jpg new file mode 100644 index 000000000..71eaf6279 Binary files /dev/null and b/_images/lpddr4-test-board.jpg differ diff --git a/_images/zcu104_loaded.jpg b/_images/zcu104_loaded.jpg new file mode 100644 index 000000000..147a9e6b9 Binary files /dev/null and b/_images/zcu104_loaded.jpg differ diff --git a/_images/zcu104_loading.jpg b/_images/zcu104_loading.jpg new file mode 100644 index 000000000..752a902d0 Binary files /dev/null and b/_images/zcu104_loading.jpg differ diff --git a/_sources/arty.md.txt b/_sources/arty.md.txt new file mode 100644 index 000000000..b5c72c7ab --- /dev/null +++ b/_sources/arty.md.txt @@ -0,0 +1,44 @@ +# Arty-A7 board + +The [Arty-A7 board](https://reference.digilentinc.com/reference/programmable-logic/arty-a7/start) allows testing its on-board DDR3 module. +The board is designed around the Artix-7™ Field Programmable Gate Array (FPGA) from Xilinx. + +```{image} images/arty-a7.jpg +``` + +The following instructions explain how to set up the board. + +## Board configuration + +Connect the board USB and Ethernet cables to your computer and configure the network. The board's IP address will be `192.168.100.50` (so you could e.g. use `192.168.100.2/24`). The `IP_ADDRESS` environment variable can be used to modify the board's address. +Next, generate the FPGA bitstream: + +```sh +export TARGET=arty +make build +``` + +```{note} +This will by default target Arty A7 with the XC7A35TICSG324-1L FPGA. To build for XC7A100TCSG324-1, +use `make build TARGET_ARGS="--variant a7-100"` +``` + +The results will be located in: `build/arty/gateware/digilent_arty.bit`. To upload it, use: + +```sh +export TARGET=arty +make upload +``` + +```{note} +By typing `make` (without `build`) LiteX will generate build files without invoking Vivado. +``` + +To save bitstream in flash memory, use: + +```sh +export TARGET=arty +make flash +``` + +Bitstream will be loaded from flash memory upon device power-on or after a PROG button press. diff --git a/_sources/ddr4_datacenter_dram_tester.md.txt b/_sources/ddr4_datacenter_dram_tester.md.txt new file mode 100644 index 000000000..3d5712a98 --- /dev/null +++ b/_sources/ddr4_datacenter_dram_tester.md.txt @@ -0,0 +1,46 @@ +# Data Center DRAM Tester + +```{image} images/ddr4_datacenter_dram_tester.jpg +``` + +The data center DRAM tester is an open source hardware test platform that enables testing and experimenting with various DDR4 RDIMMs (Registered Dual In-Line Memory Module). + +The hardware is open and can be found on GitHub: + + +The following instructions explain how to set up the board. + +## Board configuration + +First connect the board USB and Ethernet cables to your computer, plug the board to the socket and turn it on using power switch. Then configure the network. The board's IP address will be `192.168.100.50` (so you could e.g. use `192.168.100.2/24`). The `IP_ADDRESS` environment variable can be used to modify the board's address. +Next, generate the FPGA bitstream: + +```sh +export TARGET=ddr4_datacenter_test_board +make build +``` + +```{note} +By typing `make` (without `build`) LiteX will generate build files without invoking Vivado. +``` + +The results will be located in: `build/ddr4_datacenter_test_board/gateware/antmicro_datacenter_ddr4_test_board.bit`. To upload it, use: + +```sh +export TARGET=ddr4_datacenter_test_board +make upload +``` + +To save bitstream in flash memory, use: + +```sh +export TARGET=ddr4_datacenter_test_board +make flash +``` + +```{warning} +There is a JTAG/SPI jumper named `MODE2` on the right side of the board. +Unless it's set to the SPI setting, the FPGA will load the bitstream received via JTAG. +``` + +Bitstream will be loaded from flash memory upon device power-on or after a PROG button press. diff --git a/_sources/ddr5_test_board.md.txt b/_sources/ddr5_test_board.md.txt new file mode 100644 index 000000000..642a61604 --- /dev/null +++ b/_sources/ddr5_test_board.md.txt @@ -0,0 +1,55 @@ +# DDR5 Test Board + +```{image} images/lpddr4-test-board.jpg +``` + +The DDR5 test board is an open source hardware test platform that enables testing and experimenting with +various x4/x8 DDR5 modules embedded on DDR5 testbed. + +The hardware is open and can be found on GitHub: + +- Main board +- Testbed + +The following instructions explain how to set up the board. + +## Board configuration + +First connect the board USB and Ethernet cables to your computer, plug the board to the socket and turn it on using power switch. Then configure the network. The board's IP address will be `192.168.100.50` (so you could e.g. use `192.168.100.2/24`). The `IP_ADDRESS` environment variable can be used to modify the board's address. +Next, generate the FPGA bitstream: + +```sh +export TARGET=ddr5_test_board +make build TARGET_ARGS="--l2-size 256 --build --iodelay-clk-freq 400e6 --bios-lto --rw-bios --no-sdram-hw-test" +``` + +```{note} +--l2-size 256 sets L2 cache size to 256 bytes + +--no-sdram-hw-test disables hw accelerated memory test +``` + +```{note} +By typing `make` (without `build`) LiteX will generate build files without invoking Vivado. +``` + +The results will be located in: `build/ddr5_test_board/gateware/antmicro_ddr5_test_board.bit`. To upload it, use: + +```sh +export TARGET=ddr5_test_board +make upload +``` + +To save bitstream in flash memory, use: + +```sh +export TARGET=ddr5_test_board +make flash +``` + +```{warning} +There is a JTAG/SPI jumper named `MODE2` on the right side of the board. +Unless it's set to the SPI setting, the FPGA will load the bitstream received via JTAG. +``` + +Bitstream will be loaded from flash memory upon device power-on or after a PROG button press. diff --git a/_sources/ddr5_tester.md.txt b/_sources/ddr5_tester.md.txt new file mode 100644 index 000000000..404bf9fa1 --- /dev/null +++ b/_sources/ddr5_tester.md.txt @@ -0,0 +1,52 @@ +# DDR5 Tester + +```{image} images/datacenter-rdimm-ddr5-tester.png +``` + +The DDR5 tester is an open source hardware test platform that enables testing and experimenting with various DDR5 RDIMMs (Registered Dual In-Line Memory Module). + +The hardware is open and can be found on GitHub: + + +The following instructions explain how to set up the board. + +## Board configuration + +First connect the board USB and Ethernet cables to your computer, plug the board to the socket and turn it on using power switch. Then configure the network. The board's IP address will be `192.168.100.50` (so you could e.g. use `192.168.100.2/24`). The `IP_ADDRESS` environment variable can be used to modify the board's address. +Next, generate the FPGA bitstream: + +```sh +export TARGET=ddr5_tester +make build TARGET_ARGS="--l2-size 256 --build --iodelay-clk-freq 400e6 --bios-lto --rw-bios --module MTC10F1084S1RC --no-sdram-hw-test" +``` + +```{note} +--l2-size 256 sets L2 cache size to 256 bytes + +--no-sdram-hw-test disables hw accelerated memory test +``` + +```{note} +By typing `make` (without `build`) LiteX will generate build files without invoking Vivado. +``` + +The results will be located in: `build/ddr5_tester/gateware/antmicro_ddr5_tester.bit`. To upload it, use: + +```sh +export TARGET=ddr5_tester +make upload +``` + +To save bitstream in flash memory, use: + +```sh +export TARGET=ddr5_tester +make flash +``` + +```{warning} +There is a JTAG/SPI jumper named `MODE` on the right side of the board. +Unless it's set to the SPI setting, the FPGA will load the bitstream received via JTAG. +``` + +Bitstream will be loaded from flash memory upon device power-on or after a PROG button press. diff --git a/_sources/dram_modules.md.txt b/_sources/dram_modules.md.txt new file mode 100644 index 000000000..85cc1fb1e --- /dev/null +++ b/_sources/dram_modules.md.txt @@ -0,0 +1,57 @@ +# DRAM modules + +When building one of the targets in [rowhammer_tester/targets](https://github.com/antmicro/rowhammer-tester/tree/master/rowhammer_tester/targets), a custom DRAM module can be specified using the `--module` argument. To find the default modules for each target, check the output of `--help`. + +```{note} +Specifying different DRAM module makes most sense on boards that allow to easily replace the DRAM module, +such as on ZCU104. On other boards it would be necessary to desolder the DRAM chip and solder a new one. +``` + +(adding-new-modules)= + +## Adding new modules + +[LiteDRAM](https://github.com/enjoy-digital/litedram) controller provides out-of-the-box support for many DRAM modules. +Supported modules can be found in [litedram/modules.py](https://github.com/enjoy-digital/litedram/blob/master/litedram/modules.py). +If a module is not listed there, you can add a new definition. + +To make development more convenient, modules can be added in the rowhammer-tester repository directly in file [rowhammer_tester/targets/modules.py](https://github.com/antmicro/rowhammer-tester/blob/master/rowhammer_tester/targets/modules.py). These definitions will be used before definitions in LiteDRAM. + +```{note} +After ensuring that the module works correctly, a Pull Request to LiteDRAM should be created to add support for the module. +``` + +To add a new module definition, use the existing ones as a reference. New module class should derive from `SDRAMModule` (or the helper classes, e.g. `DDR4Module`). Timing/geometry values for a module have to be obtained from the relevant DRAM module's datasheet. The timings in classes deriving from `SDRAMModule` are specified in nanoseconds. The timing value can also be specified as a 2-element tuple `(ck, ns)`, in which case `ck` is the number of clock cycles and `ns` is the number of nanoseconds (and can be `None`). The highest of the resulting timing values will be used. + +## SPD EEPROM + +On boards that use DIMM/SO-DIMM modules (e.g. ZCU104) it is possible to read the contents of the DRAM modules's [SPD EEPROM memory](https://en.wikipedia.org/wiki/Serial_presence_detect). +SPD contains several essential module parameters that the memory controller needs in order to use the DRAM module. +SPD EEPROM can be read over I2C bus. + +### Reading SPD EEPROM + +To read the SPD memory use the script `rowhammer_tester/scripts/spd_eeprom.py`. +First prepare the environment as described in {ref}`controlling-the-board`. +Then use the following command to read the contents of SPD EEPROM and save it to a file, for example: + +```sh +python rowhammer_tester/scripts/spd_eeprom.py read MTA4ATF51264HZ-3G2J1.bin +``` + +The contents of the file can then be used to get DRAM module parameters. +Use the following command to examine the parameters: + +```sh +python rowhammer_tester/scripts/spd_eeprom.py show MTA4ATF51264HZ-3G2J1.bin 125e6 +``` + +Note that system clock frequency must be passed as an argument to determine timing values in controller clock cycles. + +### Using SPD data + +The memory controller is able to set the timings read from an SPD EEPROM during system boot. +The only requirement here is that the SoC is built with I2C controller, and I2C pins are routed to the (R)DIMM module. +There is no additional action required from system user. +The timings will be set automatically. + diff --git a/_sources/general.md.txt b/_sources/general.md.txt new file mode 100644 index 000000000..f81b5d81b --- /dev/null +++ b/_sources/general.md.txt @@ -0,0 +1,129 @@ +# General + +The aim of this project is to provide a platform for testing [DRAM vulnerability to rowhammer attacks](https://users.ece.cmu.edu/~yoonguk/papers/kim-isca14.pdf). + +(architecture)= +## Architecture + +The setup consists of FPGA gateware and application side software. +The following diagram illustrates the general system architecture. + +```{image} ./images/architecture.png +:alt: Architecture diagram +:target: ./images/architecture.png +``` + +The DRAM is connected to [LiteDRAM](https://github.com/enjoy-digital/litedram), which provides swappable PHYs and a DRAM controller implementation. + +In the default bulk transfer mode the LiteDRAM controller is connected to PHY and ensures correct DRAM traffic. +Bulk transfers can be controlled using dedicated Control & Status Registers (CSRs) and use LiteDRAM DMA to ensure fast operation. + +The Payload Executor allows executing a user-provided sequence of commands. +It temporarily disconnects the DRAM controller from PHY, executes the instructions stored in the SRAM memory, +translating them into DFI commands and finally reconnects the DRAM controller. + +The application side consists of a set of Python scripts communicating with the FPGA using the LiteX EtherBone bridge. + +## Installing dependencies + +Make sure you have Python 3 installed with the `venv` module, and the dependencies required to build +[verilator](https://github.com/verilator/verilator), [openFPGALoader](https://github.com/trabucayre/openFPGALoader) +and [OpenOCD](https://github.com/openocd-org/openocd). +To install the dependencies on Ubuntu 18.04 LTS, run: + +```sh +apt install git build-essential autoconf cmake flex bison libftdi-dev libjson-c-dev libevent-dev libtinfo-dev uml-utilities python3 python3-venv python3-wheel protobuf-compiler libcairo2 libftdi1-2 libftdi1-dev libhidapi-hidraw0 libhidapi-dev libudev-dev pkg-config tree zlib1g-dev zip unzip help2man curl ethtool +``` + +````{note} +On some Debian-based systems there's a problem with a broken dependency: + + ``` + libc6-dev : Breaks: libgcc-9-dev (< 9.3.0-5~) but 9.2.1-19 is to be installed + ``` + +`gcc-9-base` package installation solves the problem. +```` + +On Ubuntu 22.04 LTS the following dependencies may also be required: + +```sh +apt install libtool libusb-1.0-0-dev pkg-config +``` + +### Rowhammer tester + +Now clone the `rowhammer-tester` repository and install the rest of the required dependencies: + +```sh +git clone --recursive https://github.com/antmicro/rowhammer-tester.git +cd rowhammer-tester +make deps +``` + +The last command will download and build all the dependencies (including a RISC-V GCC toolchain) +and will set up a [Python virtual environment](https://docs.python.org/3/library/venv.html) under +the `./venv` directory with all the required packages installed. + +The virtual environment allows you to use Python without installing the packages system-wide. +To enter the environment, you have to run `source venv/bin/activate` in each new shell. +You can also use the provided `make env` target, which will start a new Bash shell with the virtualenv already sourced. +You can install packages inside the virtual environment by entering the environment and then using `pip`. + +To build the bitstream you will also need to have Vivado (version 2020.2 or newer) installed and the `vivado` command available in your `PATH`. +To configure Vivado in the current shell, you need to `source /PATH/TO/Vivado/VERSION/settings64.sh`. +This can be put in your `.bashrc` or other shell init script. + +To make the process automatic without hard-coding these things in shell init script, +tools like [direnv](https://github.com/direnv/direnv) can be used. A sample `.envrc` file would then look like this: + +```sh +source venv/bin/activate +source /PATH/TO/Vivado/VERSION/settings64.sh +``` + +All other commands assume that you run Python from the virtual environment with `vivado` in your `PATH`. + +## Packaging the bitstream + +If you want to save the bitstream and use it later or share it with someone, there is an utility target `make pack`. +It packs files necessary to load the bitstream and run rowhammer scripts on it. +Those files are: + - `build/$TARGET/gateware/$TOP.bit` + - `build/$TARGET/csr.csv` + - `build/$TARGET/defs.csv` + - `build/$TARGET/sdram_init.py` + - `build/$TARGET/litedram_settings.json` + +After running `make pack`, you should have a zip file named like `$TARGET-$BRANCH-$COMMIT.zip`. + +Next time you want to use a bitstream packaged in such way, all you need to do is to run +`unzip your-bitstream-file.zip` and you are all set. + +## Local documentation build + +The gateware part of the documentation is auto-generated from source files. +Other files are static and are located in the `doc/` directory. +To build the documentation, enter: + +```sh +source venv/bin/activate +pip install -r requirements.txt +python -m sphinx -b html doc build/documentation/html +``` + +The documentation will be located in `build/documentation/index.html`. + +```{note} +For easier development one can use [sphinx-autobuild](https://pypi.org/project/sphinx-autobuild) +using command `sphinx-autobuild -b html doc build/documentation/html --re-ignore 'doc/build/.*'`. +The documentation can be then viewed in a browser at `http://127.0.0.1:8000`. +``` + +## Tests + +To run project tests use: + +```sh +make test +``` diff --git a/_sources/index.md.txt b/_sources/index.md.txt new file mode 100644 index 000000000..fe1755189 --- /dev/null +++ b/_sources/index.md.txt @@ -0,0 +1,34 @@ +# Welcome to Rowhammer tester! + +```{toctree} +:maxdepth: 2 + +general.md +usage.md +visualization.md +playbook.md +dram_modules.md +``` +```{toctree} +:caption: Hardware +:maxdepth: 2 + +arty.md +zcu104.md +lpddr4_tb.md +ddr4_datacenter_dram_tester.md +ddr5_tester.md +ddr5_test_board.md +``` + +```{toctree} +:caption: Gateware documentation +:maxdepth: 2 + +build/arty/documentation/index.rst +build/zcu104/documentation/index.rst +build/ddr4_datacenter_test_board/documentation/index.rst +build/lpddr4_test_board/documentation/index.rst +build/ddr5_test_board/documentation/index.rst +build/ddr5_tester/documentation/index.rst +``` diff --git a/_sources/lpddr4_tb.md.txt b/_sources/lpddr4_tb.md.txt new file mode 100644 index 000000000..6d585bf2a --- /dev/null +++ b/_sources/lpddr4_tb.md.txt @@ -0,0 +1,52 @@ +# LPDDR4 Test Board + +```{image} images/lpddr4-test-board.jpg +``` + +LPDDR4 Test Board is a platform developed by Antmicro for testing LPDDR4 memory. +It uses Xilinx Kintex-7 FPGA (XC7K70T-FBG484) and by default includes a custom SO-DIMM module with Micron's MT53E256M16D1 LPDDR4 DRAM. + +The hardware is open and can be found on GitHub: + +- Test board: +- Testbed: + +## Board configuration + +First insert the LPDDR4 DRAM module into the socket and make sure that jumpers are set in correct positions: + +- VDDQ (J10) should be set in position 1V1 +- MODE2 should be set in position FLASH + +Then connect the board USB and Ethernet cables to your computer and configure the network. The board's IP address will be `192.168.100.50` (so you could e.g. use `192.168.100.2/24`). The `IP_ADDRESS` environment variable can be used to modify the board's address. +Next, generate the FPGA bitstream: + +```sh +export TARGET=lpddr4_test_board +make build +``` + +The results will be located in: `build/lpddr4_test_board/gateware/antmicro_lpddr4_test_board.bit`. To upload it, use: + +```sh +export TARGET=lpddr4_test_board +make upload +``` + +```{note} +By typing `make` (without `build`) LiteX will generate build files without invoking Vivado. +``` + +To save bitstream in flash memory, use: + +```sh +export TARGET=lpddr4_test_board +make flash +``` + +```{warning} +There is a JTAG/FLASH jumper named `MODE2` on the right side of the board. +Unless it's set to the FLASH setting, the FPGA will load the bitstream received via JTAG. +``` + +Bitstream will be loaded from flash memory upon device power-on or after a PROG button press. diff --git a/_sources/playbook.md.txt b/_sources/playbook.md.txt new file mode 100644 index 000000000..6e1848604 --- /dev/null +++ b/_sources/playbook.md.txt @@ -0,0 +1,300 @@ +# Playbook + +The [Playbook directory](https://github.com/antmicro/rowhammer-tester/tree/master/rowhammer_tester/scripts/playbook) contains a group of Python classes and scripts designed to simplify the process of writing various rowhammer-related tests. These tests can be executed against a hardware platform. + +## Payload + +Tests are generated as `payload` data. After generation, this data is transferred to a memory area in the device reserved for this purpose called `payload memory`. The payload contains an instruction list that can be interpreted by the `payload executor` module in hardware. The payload executor translates these instructions into DRAM commands. The payload executor connects directly to the DRAM PHY, bypassing the DRAM controller, as explained in {ref}`architecture`. + +### Changing payload memory size + +Payload memory size can be changed. Of course it can't exceed the memory available on the hardware platform used. +Currently, the payload memory size is defined in [common.py](https://github.com/antmicro/rowhammer-tester/blob/master/rowhammer_tester/targets/common.py), as an argument to LiteX: + +```python +add_argument("--payload-size", default="1024", help="Payload memory size in bytes") +``` + +The examples shown in this chapter don't require any changes. When writing your own {ref}`configurations`, you may need to change the default value. + +## Row mapping + +In the case of DRAM modules, the physical layout of the memory rows in hardware can be different from the logical numbers assigned to them. The nature of rowhammer attack is that only the physically adjacent rows are affected by the `aggressor row`. To deal with the problem of disparity between physical location and logical enumeration, we various mapping strategies can be implemented: + +- `TrivialRowMapping` - logical address is the same as physical one +- `TypeARowMapping` - more complex mapping method, reverse-engineered as a part of [this research](https://download.vusec.net/papers/hammertime_raid18.pdf) +- `TypeBRowMapping` - logical address is the physical one multiplied by 2, taken from [this paper](https://arxiv.org/pdf/2005.13121.pdf) + +## Row Generator class + +Row Generator class is responsible for creating a list of row numbers used in a rowhammer attack. +Currently, only one instance of this class is available. + +### EvenRowGenerator + +Generates a list of even numbered rows. Uses the row mapping specified by the {ref}`payload generator class` used. Two configuration parameters are needed for EvenRowGenerator: + +- `nr_rows` - number of rows to be generated +- `max_row` - maximal number to be used. The chosen numbers will be *modulo max_row* + +### HalfDoubleRowGenerator + +Generates a list of rows for a [Half-Double](https://github.com/google/hammer-kit/blob/main/20210525_half_double.pdf) attack. The list will repeat rows to create weight difference between attacks at different distances. Used by `HalfDoubleAnalysisPayloadGenerator`. + +- `nr_rows` - number of rows in the attack pattern. +- `distance_one` - does the attack have a distance one component? +- `double_sided` - is the attack double-sided? +- `distance_two` - does the attack have a distance two component? +- `attack_rows_start` - the index of the first attack row. +- `max_attack_row_idx` - the position of the last attack row relative to `attack_rows_start`. +- `decoy_rows_start` - the index of the first decoy row. There are 3 decoy rows that are used as placebos in various situations: to hammer away from the victim but to keep the number of hammers and timing constant. + +## Payload generator class + +The purpose of the payload generator is to prepare a payload and process the test outcome. It is a class that can be reused in different tests {ref}`configurations`. +Payload generators are located in [payload_generators directory](https://github.com/antmicro/rowhammer-tester/tree/master/rowhammer_tester/scripts/playbook/payload_generators) + +### Available payload generators + +Row mapping and row generator settings are combined into a payload generator class. +Currently, only two payload generators are available. + +#### RowListPayloadGenerator + +RowListPayloadGenerator is a simple payload generator that can use a RowGenerator class to generate rows, and then generate a payload that hammers that list of rows +(`hammering` is a term used to describe multiple read operations on the same row). +It can also issue refresh commands to the DRAM module. +Here are the configs that can be used in *payload_generator_config* for this payload generator: + +- `row_mapping` - this is the {ref}`row mapping` used +- `row_generator` - this is the {ref}`row generator class` used to generate the rows +- `row_generator_config` - parameters for the row generator +- `verbose` - should verbose output be generated (true or false) +- `fill_local` - when enabled, permits shrinking the filled memory area to just the aggressors and the victim +- `read_count` - number of hammers (reads) per row +- `refresh` - should refresh be enabled (true or false) + +Example {ref}`configurations` for this test were provided as `configs/example_row_list_*.cfg` files. +Some of them require a significant amount of memory declared as `payload memory`. +To execute a minimalistic example from within rowhammer-tester repo, enter: + +```console +source venv/bin/activate +export TARGET=arty # change accordingly +cd rowhammer_tester/scripts/playbook/ +python playbook.py configs/example_row_list_minimal.cfg +``` + +Expected output: + +```console +Progress: [========================================] 65536 / 65536 +Row sequence: +[0, 2, 4, 6, 14, 12, 10, 8, 16, 18] +Generating payload: + tRAS = 5 + tRP = 3 + tREFI = 782 + tRFC = 32 + Repeatable unit: 930 + Repetitions: 93 + Payload size = 0.10KB / 1.00KB + Payload per-row toggle count = 0.010K x10 rows + Payload refreshes (if enabled) = 10 (disabled) + Expected execution time = 1903 cycles = 0.019 ms + +Transferring the payload ... + +Executing ... +Time taken: 0.738 ms + +Progress: [== ] 3338 / 65536 (Errors: 1287) +... +``` + +#### HammerTolerancePayloadGenerator + +HammerTolerancePayloadGenerator is a payload generator for measuring and characterizing rowhammer tolerance. +It can provide information about how many rows and bits are susceptible to the rowhammer attack. +It can also provide information about where the susceptible bits are located. + +A series of double-sided hammers against the available group of victim rows is performed. +The double-sided hammers increase in intensity based on `read_count_step` parameter. +Here are the parameters that can be specified in *payload_generator_config* for this payload generator: + +- `row_mapping` - this is the {ref}`row mapping` used +- `row_generator` - this is the {ref}`row generator class` used to generate the rows +- `row_generator_config` - parameters for the row generator +- `verbose` - should verbose output be generated (true or false) +- `fill_local` - when enabled, permits shrinking the filled memory area to just the aggressors and the victim +- `nr_rows` - number of rows to conduct the experiment over. This is the number of aggressor rows. + Victim rows will be 2 times fewer than this number. For example, to perform hammering for 32 victim rows, use 34 as the parameter value +- `read_count_step` - this is how much to increment the hammer count between multiple tests for the same row. + This is the number of hammers on single side (total number of hammers on both sides is 2x this value) +- `initial_read_count` - hammer count for the first test for a given row. Defaults to `read_count_step` if unspecified. +- `distance` - distance between aggressors and victim. Defaults to 1. +- `baseline` - when enabled, a retention effect baseline is collected by hammering distant rows for the same amount of time that the aggressors would be hammered. +- `first_dummy_row` - location of the first of two dummy rows used for baselining. +- `iters_per_row` - number of times the hammer count is incremented for each row + +The results are a series of histograms with appropriate labeling. + +Example {ref}`configurations` for this test were provided as `configs/example_hammer_*.cfg` files. +Some of them require a significant amount of memory declared as `payload memory`. +To execute a minimalistic example from within rowhammer-tester repo, enter: + +```console +source venv/bin/activate +export TARGET=arty # change accordingly +cd rowhammer_tester/scripts/playbook/ +python playbook.py configs/example_hammer_minimal.cfg +``` + +Expected output: + +```console +Progress: [========================================] 3072 / 3072 +Generating payload: + tRAS = 5 + tRP = 3 + tREFI = 782 + tRFC = 32 + Repeatable unit: 186 + Repetitions: 93 + Payload size = 0.04KB / 1.00KB + Payload per-row toggle count = 0.010K x2 rows + Payload refreshes (if enabled) = 10 (disabled) + Expected execution time = 1263 cycles = 0.013 ms + +Transferring the payload ... + +Executing ... +Time taken: 0.647 ms + +Progress: [============ ] 323 / 1024 (Errors: 320) +... +``` + +#### HalfDoubleAnalysisPayloadGenerator + +[Half-Double](https://github.com/google/hammer-kit/blob/main/20210525_half_double.pdf) is a Rowhammer phenomenon where accesses to both distance-one and distance-two neighbours of a victim row are used to generate bit flips. This payload generator allows us to characterize the Half-Double effect on a memory part. + +For each candidate victim row, the analysis starts out with the maximum number of hammers and minimum dilution level. Then, we proceed as follows: + +> 1. Dilution is increased until pure distance-one attacks stop working. +> 2. Verify that pure distance-two attack doesn't work. +> 3. Increase dilution level and record the number of bit flips in the victim until either the bit flips stop or maximum dilution level is reached. +> 4. Once maximum dilution level is reached or bit flips stop, reduce hammer count by step and reset dilution to initial level and retry step 3. Repeat until the lowest hammer count is reached. + +Note: the hammer count changes on a linear scale and dilution changes on an exponential scale. + +Results are presented as a table of values with columns representing hammer count and the rows representing dilution levels. See Tables 2 and 3 in the Half-Double white paper as examples. + +- `max_total_read_count` - maximum number of hammers issued to any given row during an iteration. +- `read_count_steps` - the amount to decrement the number of hammers for each iteration of the outer loop. +- `initial_dilution` - initial value for dilution. Dilution resets to this value at the beginning of the inner loop. +- `dilution_multiplier` - dilution is multiplied by this value for each iteration of the inner loop. +- `verbose` - generates more output. +- `row_mapping` - specifies the style in which the rows are mapped on the chip. +- `attack_rows_start` - starting row number for rows actually used to attack the victim. +- `max_attack_row_idx` - index measured from `attack_rows_start` for the last attack row. +- `decoy_rows_start` - the position of the first decoy row. There are three decoy rows. They are used as placebos during pure distance one portions of the experiment to make the number of hammers and their timing comparable. +- `max_dilution` - maximum value for dilution. +- `fill_local` - only reinitialize affected rows between experiments, as an optimization. + +## Configurations + +Test configuration files are represented as JSON files. An example: + +```python +{ + "inversion_divisor" : 2, + "inversion_mask" : "0b10", + "payload_generator" : "RowListPayloadGenerator", + "payload_generator_config" : { + "row_mapping" : "TypeARowMapping", + "row_generator" : "EvenRowGenerator", + "read_count" : 27, + "max_iteration" : 10, + "verbose" : true, + "refresh" : false, + "fill_local" : true, + "row_generator_config" : { + "nr_rows" : 10, + "max_row" : 64 + } + } +} +``` + +Different parameters are supported: + +- `payload_generator` - name of the {ref}`payload generator class` to use +- `row_pattern` - pattern that will be stored in rows +- inversion_divisor and inversion_mask - controls which rows get the inverted pattern + described in {ref}`inversion` +- `payload_generator_config` - these parameters are specific for {ref}`payload generator class` used + +### Inversion + +If needed, the data pattern used for some of the tested rows can be `bitwise-inverted`. + +Two parameters are used to specify which rows are to be inverted: + +- `inversion_divisor` +- `inversion_mask` + +An example. `inversion_divisor = 8`, `inversion_mask = 0b10010010` (bits 1, 4 and 7 are "on"). +We iterate through all row numbers 0,1,2,3,4,...,8,9,10,... +First, a modulo `inversion_divisor` operation is performed on a row number. In our case it's `mod 8`. +Next, we check if the bit in `inversion_mask` in the position corresponding to our row number (after modulo) is "on" or "off". +If it's "on", this whole row will be inverted. The results for our example are shown in a table below. + +```{list-table} Inversion example +:widths: 10 10 80 +:header-rows: 1 + +* - Row number + - Row number modulo divisor (8) + - Value +* - 0 + - 0 + - pattern +* - 1 + - 1 + - inverted pattern +* - 2 + - 2 + - pattern +* - 3 + - 3 + - pattern +* - 4 + - 4 + - inverted pattern +* - 5 + - 5 + - pattern +* - 6 + - 6 + - pattern +* - 7 + - 7 + - inverted pattern +* - 8 + - 0 + - pattern +* - 9 + - 1 + - inverted pattern +* - 10 + - 2 + - pattern +* - 11 + - 3 + - pattern +* - 12 + - 4 + - inverted pattern + +``` diff --git a/_sources/usage.md.txt b/_sources/usage.md.txt new file mode 100644 index 000000000..f0f4eac4b --- /dev/null +++ b/_sources/usage.md.txt @@ -0,0 +1,640 @@ +# User guide + +This tool can be run on real hardware (FPGAs) or in a simulation mode. +As the rowhammer attack exploits physical properties of cells in DRAM (draining charges), no bit flips can be observed in simulation mode. +However, the simulation mode is useful to test command sequences during the development. + +The Makefile can be configured using environmental variables to modify the network configuration used and to select the target. +Currently, 4 boards are supported, each targeting different memory type: +| Board | Memory type | TARGET | +|----------------------------|------------------|------------------------------| +| Arty A7 | DDR3 | `arty` | +| ZCU104 | DDR4 (SO-DIMM) | `zcu104` | +| DDR Datacenter DRAM Tester | DDR4 (RDIMM) | `ddr4_datacenter_test_board` | +| LPDDR4 Test Board | LPDDR4 (SO-DIMM) | `lpddr4_test_board` | +| DDR5 Tester | DDR5 (RDIMM) | `ddr5_tester` | +| DDR5 Test Board | DDR5 (SO-DIMM) | `ddr5_test_board` | + +```{note} +Although you choose a target board for the simulation, it doesn't require having a physical board. +Simulation is done entirely on your computer. +``` + +For board-specific instructions refer to [Arty A7](arty.md), [ZCU104](zcu104.md), [DDR4 Datacenter DRAM Tester](ddr4_datacenter_dram_tester.md), [LPDDR4 Test Board](lpddr4_tb.md), [DDR5 Tester](ddr5_tester.md) and [DDR5 Test Board](ddr5_test_board.md) chapters. +The rest of this chapter describes operations that are common for all supported boards. + + +## Network USB adapter setup + +In order to control the Rowhammer platform an Ethernet connection is necessary. +In case you want to use an USB Ethernet adapter for this purpose read the instructions below. + +1. Make sure you use a 1GbE USB network adapter +2. Figure out the MAC address for the USB network adapter: + * Run ``sudo lshw -class network -short`` to get the list of all network interfaces + * Check which of the devices uses the r8152 driver by running ``sudo ethtool -i `` + * Display the link information for the device running ``sudo ip link show `` and look for the mac address next to the ``link/ether`` field +3. Configure the USB network adapter to appear as network device ``fpga0`` using systemd + * Create ``/etc/systemd/network/10-fpga0.link`` with the following contents: + ```sh + [Match] + # Set this to the MAC address of the USB network adapter + MACAddress=XX:XX:XX:XX:XX + + [Link] + Name=fpga0 + ``` +4. Configure the ``fpga0`` network device with a static IP address, always up (even when disconnected) and ignored by the network manager. + * Run the following command, assuming your system uses NetworkManager + ```sh + nmcli con add type ethernet con-name 'Rowhammer Tester' ifname fpga0 ipv4.method manual ipv4.addresses 192.168.100.100/24 + ``` + * Alternatively, if your system supports legacy ``interfaces`` configuration file: + 1. Make sure your ``/etc/network/interfaces`` file has the following line: + ```sh + source /etc/network/interfaces.d/* + ``` + 2. Create ``/etc/network/interfaces.d/fpga0`` with the following contents: + ```sh + auto fpga0 + allow-hotplug fpga0 + iface fpga0 inet static + address 192.168.100.100/24 + ``` + 3. Check that ``nmcli device`` says the state is ``connected (externally)`` otherwise run ``sudo systemctl restart NetworkManager`` + * Run ``ifup fpga0`` +5. Run ``sudo udevadm control --reload`` and then unplug the USB ethernet device and plug it back in +6. Check you have an ``fpga0`` interface and it has the correct IP address by running ``networkctl status`` + +```{note} +In case you see ``libusb_open() failed with LIBUSB_ERROR_ACCESS`` when trying to use the rowhammer tester scripts with the USB ethernet adapter then it means that you have a permissions issue and need to allow access to the FTDI USB to serial port chip. Check the group listed for the tty's when running ``ls -l /dev/ttyUSB*`` and add the current user to this group by running ``sudo adduser ``. +``` +(controlling-the-board)= +## Controlling the board + +Board control is the same for both simulation and hardware runs. +In order to communicate with the board via EtherBone, the `litex_server` needs to be started with the following command: + +```sh +export IP_ADDRESS=192.168.100.50 # optional, should match the one used during build +make srv +``` + +```{warning} +If you want to run the simulation and the rowhammer scripts on a physical board at the same time, +you have to change the ``IP_ADDRESS`` variable, otherwise the simulation can conflict with the communication with your board. +``` + +The build files (CSRs address list) must be up to date. It can be re-generated with `make` without arguments. + +Then, in another terminal, you can use the Python scripts provided. *Remember to enter the Python virtual environment before running the scripts!* Also, the `TARGET` variable should be set to load configuration for the given target. +For example, to use the `leds.py` script, run the following: + +```sh +source ./venv/bin/activate +export TARGET=arty # (or zcu104) required to load target configuration +cd rowhammer_tester/scripts/ +python leds.py # stop with Ctrl-C +``` + +## Hammering + +Rowhammer attacks can be run against a DRAM module. It can be then used for measuring cell retention. +For the complete list of scripts' modifiers, see `--help`. + +There are two versions of a rowhammer script: + +- `rowhammer.py` - this one uses regular memory access via EtherBone to fill/check the memory (slower) +- `hw_rowhammer.py` - BIST blocks will be used to fill/check the memory (much faster, but with some limitations regarding fill pattern) + +BIST blocks are faster and are the intended way of running Rowhammer tester. + +Hammering of a row is done by reading it. There are two ways to specify a number of reads: + +- `--read_count N` - one pass of `N` reads +- `--read_count_range K M N` - multiple passes of reads, as generated by `range(K, M, N)` + +Regardless of which one is used, the number of reads in one pass is divided equally between hammered rows. +If a user specifies `--read_count 1000`, then each row will be hammered 500 times. + +Normally hammering is being performed via DMA, but there is also an alternative way with `--payload-executor`. +It bypasses the DMA and directly talks with the PHY. +That allows the user to issue specific activation, refresh and precharge commands. + + +### Attack modes + +Different attack and row selection modes can be used, but only one of them can be specified at the same time. + +- `--hammer-only` + + Only hammers a pair of rows, without doing any error checks or reports. + + For example following command will hammer rows 4 and 6 1000 times total (so 500 times each): + + ```sh + (venv) $ python hw_rowhammer.py --hammer-only 4 6 --read_count 1000 + ``` + +- `--all-rows` + + Row pairs generated from `range(start-row, nrows - row-pair-distance, row-jump)` expression will be hammered. + + Generated pairs are of form `(i, i + row-pair-distance)`. + Default values for used arguments are: + + | argument | default | + | --------------------- | ------- | + | `--start-row` | 0 | + | `--row-jump` | 1 | + | `--row-pair-distance` | 2 | + + So you can run following command to hammer rows `(0, 2), (1, 3), (2, 4)`: + + ```sh + (venv) $ python hw_rowhammer.py --all-rows --nrows 5 + ``` + + And in case of: + + ```sh + (venv) $ python hw_rowhammer.py --all-rows --start-row 10 --nrows 16 --row-jump 2 --row-distance 3 + ``` + + hammered pairs would be: `(10, 13), (12, 15)`. + + In a special case, where `--row-pair-distance` is 0, you can check how hammering a single row affects other rows. + Normally activations and deactivations are achieved with row reads using the DMA, but in this case it is not possible. + Because the same row is being read all the time, no deactivation command would be sent by the DMA. + In this case, `--payload-executor` is required as it bypasses the DMA and sends deactivation commands on its own. + + ```sh + (venv) $ python hw_rowhammer.py --all-rows --nrows 5 --row-pair-distance 0 --payload-executor + ``` + +- `--row-pairs sequential` + + Hammers pairs of `(start-row, start-row + n)`, where `n` is from 0 to `nrows`. + + ```sh + (venv) $ python hw_rowhammer.py --row-pairs sequential --start-row 4 --nrows 10 + ``` + + Command above, would hammer following set of row pairs: + + ``` + (4, 4 + 0) + (4, 4 + 1) + ... + (4, 4 + 9) + (4, 4 + 10) + ``` + +- `--row-pairs const` + + Two rows specified with the `const-rows-pair` parameter will be hammered: + + ```sh + (venv) $ python hw_rowhammer.py --row-pairs const --const-rows-pair 4 6 + ``` + +- `--row-pairs random` + + `nrows` pairs of random rows will be hammered. Row numbers will be between `start-row` and `start-row + nrows`. + + ```sh + (venv) $ python hw_rowhammer.py --row-pairs random --start-row 4 --nrows 10 + ``` + +- `--no-attack-time