[rom_ctrl] Instantiate scrambling primitives for rom_ctrl Also update SW build scripts so that we install the scrambled binaries to build-bin as well and update all the documentation examples to show how to use the new filename. Note that we don't update top_englishbreakfast to match: that doesn't have a rom_ctrl, so shouldn't take a scrambled ROM image. Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
diff --git a/hw/ip/otbn/README.md b/hw/ip/otbn/README.md index 948cc57..75c91af 100644 --- a/hw/ip/otbn/README.md +++ b/hw/ip/otbn/README.md
@@ -105,7 +105,7 @@ ```sh build/lowrisc_systems_chip_earlgrey_verilator_0.1/sim-verilator/Vchip_earlgrey_verilator \ - --meminit=rom,build-bin/sw/device/boot_rom/boot_rom_sim_verilator.elf \ + --meminit=rom,build-bin/sw/device/boot_rom/boot_rom_sim_verilator.scr.40.vmem \ --meminit=flash,build-bin/sw/device/tests/dif_otbn_smoketest_sim_verilator.elf \ --meminit=otp,build-bin/sw/device/otp_img/otp_img_sim_verilator.vmem \ +UARTDPI_LOG_uart0=- \
diff --git a/hw/ip/rom_ctrl/data/rom_ctrl.hjson b/hw/ip/rom_ctrl/data/rom_ctrl.hjson index 6adcdb3..128ef77 100644 --- a/hw/ip/rom_ctrl/data/rom_ctrl.hjson +++ b/hw/ip/rom_ctrl/data/rom_ctrl.hjson
@@ -18,6 +18,20 @@ local: "false", expose: "true" } + + { name: "RndCnstScrNonce", + type: "bit [63:0]", + desc: "Fixed nonce used for address / data scrambling" + randcount: "64", + randtype: "data" + } + + { name: "RndCnstScrKey", + type: "bit [127:0]", + desc: "Randomised constant used as a scrambling key for ROM data" + randcount: "128", + randtype: "data" + } ] alert_list: [ { name: "fatal"
diff --git a/hw/ip/rom_ctrl/rom_ctrl.core b/hw/ip/rom_ctrl/rom_ctrl.core index 359e3df..cc8a49c 100644 --- a/hw/ip/rom_ctrl/rom_ctrl.core +++ b/hw/ip/rom_ctrl/rom_ctrl.core
@@ -10,6 +10,7 @@ depend: - lowrisc:prim:alert - lowrisc:prim:assert + - lowrisc:prim:cipher - lowrisc:prim:rom_adv - lowrisc:prim:subreg - lowrisc:prim:util @@ -25,6 +26,7 @@ - rtl/rom_ctrl_counter.sv - rtl/rom_ctrl_fsm.sv - rtl/rom_ctrl_mux.sv + - rtl/rom_ctrl_scrambled_rom.sv file_type: systemVerilogSource files_verilator_waiver:
diff --git a/hw/ip/rom_ctrl/rtl/rom_ctrl.sv b/hw/ip/rom_ctrl/rtl/rom_ctrl.sv index 600d51c..d3ad41d 100644 --- a/hw/ip/rom_ctrl/rtl/rom_ctrl.sv +++ b/hw/ip/rom_ctrl/rtl/rom_ctrl.sv
@@ -10,6 +10,8 @@ #( parameter BootRomInitFile = "", parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}, + parameter bit [63:0] RndCnstScrNonce = '0, + parameter bit [127:0] RndCnstScrKey = '0, parameter bit SkipCheck = 1'b1 ) ( input clk_i, @@ -49,7 +51,8 @@ logic [RomIndexWidth-1:0] rom_index; logic rom_req; - logic [39:0] rom_rdata; + logic [39:0] rom_scr_rdata; + logic [39:0] rom_clr_rdata; logic rom_rvalid; logic [RomIndexWidth-1:0] bus_rom_index; @@ -142,37 +145,41 @@ rom_ctrl_mux #( .AW (RomIndexWidth) ) u_mux ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .sel_i (rom_select), - .bus_addr_i (bus_rom_index), - .bus_req_i (bus_rom_req), - .bus_gnt_o (bus_rom_gnt), - .bus_rdata_o (bus_rom_rdata), - .bus_rvalid_o (bus_rom_rvalid), - .chk_addr_i (checker_rom_index), - .chk_rdata_o (checker_rom_rdata), - .rom_addr_o (rom_index), - .rom_req_o (rom_req), - .rom_rdata_i (rom_rdata), - .rom_rvalid_i (rom_rvalid), - .alert_o (mux_alert) + .clk_i (clk_i), + .rst_ni (rst_ni), + .sel_i (rom_select), + .bus_addr_i (bus_rom_index), + .bus_req_i (bus_rom_req), + .bus_gnt_o (bus_rom_gnt), + .bus_rdata_o (bus_rom_rdata), + .bus_rvalid_o (bus_rom_rvalid), + .chk_addr_i (checker_rom_index), + .chk_rdata_o (checker_rom_rdata), + .rom_addr_o (rom_index), + .rom_req_o (rom_req), + .rom_scr_rdata_i (rom_scr_rdata), + .rom_clr_rdata_i (rom_clr_rdata), + .rom_rvalid_i (rom_rvalid), + .alert_o (mux_alert) ); // The ROM itself ============================================================ - prim_rom_adv #( + rom_ctrl_scrambled_rom #( + .MemInitFile (BootRomInitFile), .Width (40), .Depth (RomSizeWords), - .MemInitFile (BootRomInitFile) + .ScrNonce (RndCnstScrNonce), + .ScrKey (RndCnstScrKey) ) u_rom ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .req_i (rom_req), - .addr_i (rom_index), - .rdata_o (rom_rdata), - .rvalid_o (rom_rvalid), - .cfg_i (rom_cfg_i) + .clk_i (clk_i), + .rst_ni (rst_ni), + .req_i (rom_req), + .addr_i (rom_index), + .rvalid_o (rom_rvalid), + .scr_rdata_o (rom_scr_rdata), + .clr_rdata_o (rom_clr_rdata), + .cfg_i (rom_cfg_i) ); // TODO: The ROM has been expanded to 40 bits wide to allow us to add 9 ECC check bits. At the
diff --git a/hw/ip/rom_ctrl/rtl/rom_ctrl_mux.sv b/hw/ip/rom_ctrl/rtl/rom_ctrl_mux.sv index 9aec4bb..a45360d 100644 --- a/hw/ip/rom_ctrl/rtl/rom_ctrl_mux.sv +++ b/hw/ip/rom_ctrl/rtl/rom_ctrl_mux.sv
@@ -29,7 +29,8 @@ // Interface for ROM output logic [AW-1:0] rom_addr_o, output logic rom_req_o, - input logic [39:0] rom_rdata_i, + input logic [39:0] rom_scr_rdata_i, + input logic [39:0] rom_clr_rdata_i, input logic rom_rvalid_i, // Alert output @@ -52,11 +53,11 @@ // The bus can have access every cycle, once the select signal has gone to zero assign bus_gnt_o = ~sel_q; - assign bus_rdata_o = rom_rdata_i; + assign bus_rdata_o = rom_clr_rdata_i; // A high rom_rvalid_i is a response to a bus request if sel_i was zero on the previous cycle. assign bus_rvalid_o = ~sel_q & rom_rvalid_i; - assign chk_rdata_o = rom_rdata_i; + assign chk_rdata_o = rom_scr_rdata_i; assign rom_addr_o = sel_i ? chk_addr_i : bus_addr_i; assign rom_req_o = sel_i ? 1'b1 : bus_req_i;
diff --git a/hw/ip/rom_ctrl/rtl/rom_ctrl_scrambled_rom.sv b/hw/ip/rom_ctrl/rtl/rom_ctrl_scrambled_rom.sv new file mode 100644 index 0000000..af96881 --- /dev/null +++ b/hw/ip/rom_ctrl/rtl/rom_ctrl_scrambled_rom.sv
@@ -0,0 +1,142 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`include "prim_assert.sv" + +// +// A scrambled ROM. This is scrambled with a fixed key, passed in as a parameter (this parameter +// will be a compile-time random constant). +// +// This code follows the structure of prim_ram_1p_scr.sv (although it's much simplified because the +// key is fixed and we don't support writes). For more information about what is going on, see that +// file. Using the parameter names in prim_ram_1p_scr, we have NumPrinceRoundsHalf = 2 (so +// approximately 5 effective rounds), NumDiffRounds = 2 and NumAddrScrRounds = 2 (enabling address +// scrambling with 2 rounds). +// + +module rom_ctrl_scrambled_rom + import prim_rom_pkg::rom_cfg_t; +#( + // The initial contents of the ROM. This is used for synthesis. For simulation, this is not used; + // instead, the simulator loads the contents of ROM over DPI. + // + // In either case, the input file should be scrambled. That is, it should contain the bits that + // will appear in the physical ROM. + parameter MemInitFile = "", + + // The width of ROM words in bits + parameter int Width = 40, + + // The number of words in the ROM + parameter int Depth = 16, + + // The nonce for data and address scrambling + parameter bit [63:0] ScrNonce = '0, + + // The (fixed) key for the PRINCE cipher + parameter bit [127:0] ScrKey = '0, + + localparam int Aw = $clog2(Depth) +) ( + input logic clk_i, + input logic rst_ni, + + input logic req_i, + input logic [Aw-1:0] addr_i, + output logic rvalid_o, + output logic [Width-1:0] scr_rdata_o, + output logic [Width-1:0] clr_rdata_o, + + input rom_cfg_t cfg_i +); + + localparam bit [63-Aw:0] DataScrNonce = ScrNonce[Aw +: (64 - Aw)]; + localparam bit [Aw-1:0] AddrScrNonce = ScrNonce[Aw-1:0]; + + // Parameter Checks ========================================================== + + // The depth needs to be a power of 2 to use address scrambling + `ASSERT_INIT(DepthPow2Check_A, (Depth & (Depth - 1)) == 0) + // We only support a width up to 64 + `ASSERT_INIT(MaxWidthCheck_A, Width <= 64) + + // Address scrambling ======================================================== + + logic [Aw-1:0] addr_scr; + prim_subst_perm #( + .DataWidth (Aw), + .NumRounds (2), + .Decrypt (0) + ) u_sp_addr ( + .data_i (addr_i), + .key_i (AddrScrNonce), + .data_o (addr_scr) + ); + + // Keystream generation ====================================================== + + logic [63:0] keystream; + + prim_prince #( + .DataWidth (64), + .KeyWidth (128), + .NumRoundsHalf (2), + .HalfwayDataReg (1'b1), + .HalfwayKeyReg (1'b1) + ) u_prince ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .valid_i (req_i), + .data_i ({DataScrNonce, addr_i}), + .key_i (ScrKey), + .dec_i (1'b0), + .data_o (keystream), + .valid_o () + ); + + if (Width < 64) begin : gen_unread_keystream + // Ignore top bits of keystream: we just use the bottom Width bits. + logic unused_top_keystream; + assign unused_top_keystream = &{1'b0, keystream[63:Width]}; + end + + // The physical ROM ========================================================== + + logic [Width-1:0] rdata_scr; + + prim_rom_adv #( + .Width (Width), + .Depth (Depth), + .MemInitFile (MemInitFile) + ) u_rom ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .req_i (req_i), + .addr_i (addr_scr), + .rvalid_o (rvalid_o), + .rdata_o (rdata_scr), + .cfg_i (cfg_i) + ); + + assign scr_rdata_o = rdata_scr; + + // Data scrambling =========================================================== + + logic [Width-1:0] rdata_xor; + + prim_subst_perm #( + .DataWidth (Width), + .NumRounds (2), + .Decrypt (1) + ) u_sp_data ( + .data_i (rdata_scr), + .key_i ('0), + .data_o (rdata_xor) + ); + + // XOR rdata with keystream ================================================== + + assign clr_rdata_o = rdata_xor ^ keystream[Width-1:0]; + +endmodule
diff --git a/hw/ip/rom_ctrl/util/mem.py b/hw/ip/rom_ctrl/util/mem.py index 2f55169..8ee9be7 100644 --- a/hw/ip/rom_ctrl/util/mem.py +++ b/hw/ip/rom_ctrl/util/mem.py
@@ -195,19 +195,23 @@ if seg_type != 'PT_LOAD' or segment['p_memsz'] == 0: continue - seg_lma = segment['p_paddr'] - seg_end = seg_lma + segment['p_memsz'] + # seg_lma is the (relative) address of the first byte to be loaded. + # seg_top is the address of the last byte to be loaded. A one-byte + # segment will have seg_lma == seg_top. + seg_lma = segment['p_paddr'] - base_addr + seg_top = seg_lma + segment['p_memsz'] - 1 + + assert seg_lma <= seg_top # We re-map the addresses relative to base_addr: check that no # segment starts before it. - if seg_lma < base_addr: + if seg_lma < 0: raise ValueError('ELF file contains a segment starting at ' '{:#x}, so cannot be loaded relative to base ' 'address {:#x}.' - .format(seg_lma, base_addr)) + .format(base_addr + seg_lma, base_addr)) - segments.append((seg_lma - base_addr, - seg_end - base_addr, segment.data())) + segments.append((seg_lma, seg_top, segment.data())) # Sort the segments by base address segments.sort(key=lambda t: t[0]) @@ -215,21 +219,24 @@ # Make sure that they don't overlap prev_lma = 0 next_addr = 0 - for lma, end, data in segments: + for lma, top, data in segments: if lma < next_addr: raise ValueError('ELF file contains overlapping segments with ' 'address ranges {:#x}..{:#x} and ' '{:#x}..{:#x}.' - .format(prev_lma, next_addr - 1, lma, end)) + .format(base_addr + prev_lma, + base_addr + next_addr - 1, + base_addr + lma, + base_addr + top)) prev_lma = lma - next_addr = end + 1 + next_addr = top + 1 # Merge any adjacent segments, bridging any sub-word gaps. This doesn't # do any other right padding: we'll do that on the final pass that # converts to 32-bit words. merged_segments = [] # type: List[Tuple[int, int, bytes]] next_word = 0 - for lma, end, data in segments: + for lma, top, data in segments: # Round the LMA down to the previous word boundary. The non-overlap # check above should ensure that this is never actually less than # next_word. @@ -239,22 +246,25 @@ # If there isn't an aligned whole word between the two segments, # bridge the gap if merged_segments and next_word == lma_word: - last_lma_word, last_end, last_data = merged_segments[-1] - if last_end < lma: - # The largest gap here is be something like last_end = 1; - # lma = 7, which has size 2*4 - 1 - 1 = 6. - assert lma - last_end <= 6 - last_data += bytes(lma - last_end) - merged_segments[-1] = (last_lma_word, end, last_data + data) + last_lma_word, last_top, last_data = merged_segments[-1] + if last_top < lma: + # The largest gap possible here happens with addresses like + # last_top = 0x100; lma = 0x107, which just bridges two + # 4-byte words (0x100..0x103 and 0x104..0x107) with one + # byte used from each, leaving 6 bytes to fill. + assert lma - (last_top + 1) <= 6 + last_data += bytes(lma - (last_top + 1)) + merged_segments[-1] = (last_lma_word, top, last_data + data) else: # Pad on the left if necessary to ensure that lma is 32-bit # aligned. if lma % 4: - merged_segments.append((lma_word, end, bytes(lma % 4) + data)) + merged_segments.append((lma_word, top, bytes(lma % 4) + data)) else: - merged_segments.append((lma_word, end, data)) + merged_segments.append((lma_word, top, data)) - next_word = 1 + (end // 4) + # The index of the first word that starts strictly above top. + next_word = 1 + (top // 4) # Assemble the bytes in each segment into little-endian 32-bit words. # Zero-extend any partial word at the end of a segment. Because of the
diff --git a/hw/top_earlgrey/chip_earlgrey_nexysvideo.core b/hw/top_earlgrey/chip_earlgrey_nexysvideo.core index 2836fab..e46352b 100644 --- a/hw/top_earlgrey/chip_earlgrey_nexysvideo.core +++ b/hw/top_earlgrey/chip_earlgrey_nexysvideo.core
@@ -35,14 +35,14 @@ parameters: # XXX: This parameter needs to be absolute, or relative to the *.runs/synth_1 # directory. It's best to pass it as absolute path when invoking fusesoc, e.g. - # --BootRomInitFile=$PWD/build-bin/sw/device/boot_rom/boot_rom_fpga_nexysvideo.32.vmem + # --BootRomInitFile=$PWD/build-bin/sw/device/boot_rom/boot_rom_fpga_nexysvideo.scr.40.vmem # XXX: The VMEM file should be added to the sources of the Vivado project to # make the Vivado dependency tracking work. However this requires changes to # fusesoc first. BootRomInitFile: datatype: str - description: Boot ROM initialization file in 32 bit vmem hex format - default: "../../../../../build-bin/sw/device/boot_rom/boot_rom_fpga_nexysvideo.32.vmem" + description: Scrambled boot ROM initialization file in 40 bit vmem hex format + default: "../../../../../build-bin/sw/device/boot_rom/boot_rom_fpga_nexysvideo.scr.40.vmem" paramtype: vlogparam OtpCtrlMemInitFile: datatype: str
diff --git a/hw/top_earlgrey/chip_earlgrey_verilator.cc b/hw/top_earlgrey/chip_earlgrey_verilator.cc index 7b5856a..348817e 100644 --- a/hw/top_earlgrey/chip_earlgrey_verilator.cc +++ b/hw/top_earlgrey/chip_earlgrey_verilator.cc
@@ -21,9 +21,9 @@ "u_prim_ram_1p_adv.u_mem." "gen_generic.u_impl_generic"); - MemArea rom( - top_scope + ".u_rom_ctrl.u_rom.u_prim_rom.gen_generic.u_impl_generic", - 0x4000 / 4, 4); + MemArea rom(top_scope + (".u_rom_ctrl.u_rom.u_rom." + "u_prim_rom.gen_generic.u_impl_generic"), + 0x4000 / 4, 4); MemArea ram(top_scope + ".u_ram1p_ram_main." + ram1p_adv_scope, 0x20000 / 4, 4); MemArea flash(top_scope +
diff --git a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson index af28623..0336d02 100644 --- a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson +++ b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
@@ -5040,6 +5040,26 @@ expose: "true" name_top: RomCtrlBootRomInitFile } + { + name: RndCnstScrNonce + desc: Fixed nonce used for address / data scrambling + type: bit [63:0] + randcount: 64 + randtype: data + name_top: RndCnstRomCtrlScrNonce + default: 0xfc00de9d9734c3fe + randwidth: 64 + } + { + name: RndCnstScrKey + desc: Randomised constant used as a scrambling key for ROM data + type: bit [127:0] + randcount: 128 + randtype: data + name_top: RndCnstRomCtrlScrKey + default: 0x23c074e020fd502869582e71443c8be0 + randwidth: 128 + } ] inter_signal_list: [
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_sw_base_vseq.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_base_vseq.sv index 25ba066..3c9630d 100644 --- a/hw/top_earlgrey/dv/env/seq_lib/chip_sw_base_vseq.sv +++ b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_base_vseq.sv
@@ -37,7 +37,7 @@ cfg.flash_bank1_bkdr_vif.set_mem(); // Backdoor load memories with sw images. - cfg.rom_bkdr_vif.load_mem_from_file({cfg.sw_images[SwTypeRom], ".32.vmem"}); + cfg.rom_bkdr_vif.load_mem_from_file({cfg.sw_images[SwTypeRom], ".scr.40.vmem"}); // TODO: the location of the main execution image should be randomized for either bank in future if (cfg.use_spi_load_bootstrap) begin
diff --git a/hw/top_earlgrey/dv/tb/chip_hier_macros.svh b/hw/top_earlgrey/dv/tb/chip_hier_macros.svh index d71592f..cefaa1f 100644 --- a/hw/top_earlgrey/dv/tb/chip_hier_macros.svh +++ b/hw/top_earlgrey/dv/tb/chip_hier_macros.svh
@@ -11,7 +11,7 @@ `define CPU_HIER `CHIP_HIER.u_rv_core_ibex `define RAM_MAIN_HIER `CHIP_HIER.u_ram1p_ram_main.u_prim_ram_1p_adv.u_mem `define RAM_RET_HIER `CHIP_HIER.u_ram1p_ram_ret_aon.u_prim_ram_1p_adv.u_mem -`define ROM_HIER `CHIP_HIER.u_rom_ctrl.u_rom.u_prim_rom +`define ROM_HIER `CHIP_HIER.u_rom_ctrl.u_rom.u_rom.u_prim_rom `define FLASH_HIER `CHIP_HIER.u_flash_eflash.u_flash `define RSTMGR_HIER `CHIP_HIER.u_rstmgr_aon `define CLKMGR_HIER `CHIP_HIER.u_clkmgr_aon
diff --git a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv index 7e7698f..7aac243 100644 --- a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv +++ b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
@@ -2274,7 +2274,9 @@ rom_ctrl #( .AlertAsyncOn(alert_handler_reg_pkg::AsyncOn[30:30]), - .BootRomInitFile(RomCtrlBootRomInitFile) + .BootRomInitFile(RomCtrlBootRomInitFile), + .RndCnstScrNonce(RndCnstRomCtrlScrNonce), + .RndCnstScrKey(RndCnstRomCtrlScrKey) ) u_rom_ctrl ( // [30]: fatal .alert_tx_o ( alert_tx[30:30] ),
diff --git a/hw/top_earlgrey/rtl/autogen/top_earlgrey_rnd_cnst_pkg.sv b/hw/top_earlgrey/rtl/autogen/top_earlgrey_rnd_cnst_pkg.sv index 12dbfc5..01fb60b 100644 --- a/hw/top_earlgrey/rtl/autogen/top_earlgrey_rnd_cnst_pkg.sv +++ b/hw/top_earlgrey/rtl/autogen/top_earlgrey_rnd_cnst_pkg.sv
@@ -228,4 +228,17 @@ 256'hA46ED80E5942BC02513FBDFD5A98A66805BC17DDED6CCD3271A3E37A08C92847 }; + //////////////////////////////////////////// + // rom_ctrl + //////////////////////////////////////////// + // Fixed nonce used for address / data scrambling + parameter bit [63:0] RndCnstRomCtrlScrNonce = { + 64'hFC00DE9D9734C3FE + }; + + // Randomised constant used as a scrambling key for ROM data + parameter bit [127:0] RndCnstRomCtrlScrKey = { + 128'h23C074E020FD502869582E71443C8BE0 + }; + endpackage : top_earlgrey_rnd_cnst_pkg
diff --git a/hw/top_earlgrey/util/opentitan_earlgrey_usbdev_pin_config_sim.sh b/hw/top_earlgrey/util/opentitan_earlgrey_usbdev_pin_config_sim.sh index b5a3232..da2c9c4 100755 --- a/hw/top_earlgrey/util/opentitan_earlgrey_usbdev_pin_config_sim.sh +++ b/hw/top_earlgrey/util/opentitan_earlgrey_usbdev_pin_config_sim.sh
@@ -7,7 +7,7 @@ VERILATOR=build/lowrisc_systems_chip_earlgrey_verilator_0.1/sim-verilator/Vchip_earlgrey_verilator # Code to load -ROMCODE=build-bin/sw/device/boot_rom/boot_rom_sim_verilator.elf +ROMCODE=build-bin/sw/device/boot_rom/boot_rom_sim_verilator.scr.40.vmem FLASH=build-bin/sw/device/examples/hello_usbdev/hello_usbdev_sim_verilator.elf OTP=build-bin/sw/device/otp_img/otp_img_sim_verilator.vmem