diff --git a/dv/riscv_compliance/ibex_riscv_compliance.core b/dv/riscv_compliance/ibex_riscv_compliance.core index 8ce6e479..92374f6d 100644 --- a/dv/riscv_compliance/ibex_riscv_compliance.core +++ b/dv/riscv_compliance/ibex_riscv_compliance.core @@ -78,6 +78,12 @@ parameters: paramtype: vlogparam description: "Number of PMP regions" + PointerAuthentication: + datatype: int + default: 0 + paramtype: vlogparam + description: "Enable pointer authentication (EXPERIMENTAL) [0/1]" + targets: sim: default_tool: verilator @@ -94,6 +100,7 @@ targets: - PMPEnable - PMPGranularity - PMPNumRegions + - PointerAuthentication toplevel: ibex_riscv_compliance tools: verilator: diff --git a/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv b/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv index 8cb860cc..e9f573ba 100644 --- a/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv +++ b/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv @@ -24,6 +24,7 @@ module ibex_riscv_compliance ( parameter MultiplierImplementation = "fast"; parameter bit BranchTargetALU = 1'b0; parameter bit WritebackStage = 1'b0; + parameter bit PointerAuthentication = 1'b0; logic clk_sys, rst_sys_n; @@ -120,7 +121,8 @@ module ibex_riscv_compliance ( .BranchTargetALU (BranchTargetALU ), .WritebackStage (WritebackStage ), .DmHaltAddr (32'h00000000 ), - .DmExceptionAddr (32'h00000000 ) + .DmExceptionAddr (32'h00000000 ), + .PointerAuthentication (PointerAuthentication ) ) u_core ( .clk_i (clk_sys ), .rst_ni (rst_sys_n ), diff --git a/examples/simple_system/ibex_simple_system.core b/examples/simple_system/ibex_simple_system.core index 4e8a93ed..a096c29d 100644 --- a/examples/simple_system/ibex_simple_system.core +++ b/examples/simple_system/ibex_simple_system.core @@ -88,6 +88,12 @@ parameters: paramtype: vlogparam description: "Number of PMP regions" + PointerAuthentication: + datatype: int + default: 0 + paramtype: vlogparam + description: "Enable pointer authentication (EXPERIMENTAL) [0/1]" + targets: default: &default_target filesets: @@ -106,7 +112,7 @@ targets: - PMPGranularity - PMPNumRegions - SRAMInitFile - + - PointerAuthentication lint: <<: *default_target default_tool: verilator diff --git a/examples/simple_system/rtl/ibex_simple_system.sv b/examples/simple_system/rtl/ibex_simple_system.sv index f4c630e9..887ca4cd 100644 --- a/examples/simple_system/rtl/ibex_simple_system.sv +++ b/examples/simple_system/rtl/ibex_simple_system.sv @@ -34,6 +34,7 @@ module ibex_simple_system ( parameter bit WritebackStage = 1'b0; parameter MultiplierImplementation = "fast"; parameter SRAMInitFile = ""; + parameter bit PointerAuthentication = 1'b0; logic clk_sys = 1'b0, rst_sys_n; @@ -159,7 +160,8 @@ module ibex_simple_system ( .WritebackStage ( WritebackStage ), .MultiplierImplementation ( MultiplierImplementation ), .DmHaltAddr ( 32'h00100000 ), - .DmExceptionAddr ( 32'h00100000 ) + .DmExceptionAddr ( 32'h00100000 ), + .PointerAuthentication ( PointerAuthentication ) ) u_core ( .clk_i (clk_sys), .rst_ni (rst_sys_n), diff --git a/ibex_core.core b/ibex_core.core index dce96f4b..01b6cb5b 100644 --- a/ibex_core.core +++ b/ibex_core.core @@ -37,6 +37,8 @@ filesets: # - rtl/ibex_register_file_fpga.sv # FPGA - rtl/ibex_register_file_ff.sv # generic FF-based - rtl/ibex_core.sv + - rtl/ibex_pointer_authentication.sv + - rtl/ibex_cipher.sv file_type: systemVerilogSource files_lint_verilator: @@ -130,6 +132,12 @@ parameters: paramtype: vlogparam description: "Number of PMP regions" + PointerAuthentication: + datatype: int + default: 0 + paramtype: vlogparam + description: "Enable pointer authentication (EXPERIMENTAL) [0/1]" + targets: default: &default_target filesets: diff --git a/rtl/ibex_cipher.sv b/rtl/ibex_cipher.sv new file mode 100644 index 00000000..716ad955 --- /dev/null +++ b/rtl/ibex_cipher.sv @@ -0,0 +1,87 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// This is just a placeholder for the actual cipher core that will be connected later. +module ibex_cipher ( + input logic clk_i, + input logic rst_ni, + + input logic [127:0] key_i, + + input logic [63:0] in_data_i, + input logic in_valid_i, + output logic in_ready_o, + + output logic [31:0] out_data_o, + output logic out_valid_o, + input logic out_ready_i +); + + logic [127:0] key_q, key_d; + logic [63:0] data_q, data_d; + + typedef enum logic [2:0] { + C_INPUT, C_CALC1, C_CALC2, C_CALC3, C_OUTPUT + } c_fsm_e; + c_fsm_e c_fsm_q, c_fsm_d; + + always_comb begin + key_d = key_q; + data_d = data_q; + in_ready_o = '0; + out_data_o = '0; + out_valid_o = '0; + c_fsm_d = c_fsm_q; + + unique case (c_fsm_q) + + C_INPUT: begin + in_ready_o = 1'b1; + if (in_valid_i) begin + data_d = in_data_i; + key_d = key_i; + c_fsm_d = C_CALC1; + end + end + + C_CALC1: begin + data_d = key_q[63:0] ^ data_q; // placeholder calculation + c_fsm_d = C_CALC2; + end + C_CALC2: begin + data_d = key_q[127:64] ^ data_q; // placeholder calculation + c_fsm_d = C_CALC3; + end + C_CALC3: begin + data_d = key_q[63:0] ^ data_q; // placeholder calculation + c_fsm_d = C_OUTPUT; + end + + C_OUTPUT: begin + if (out_ready_i) begin + out_data_o = data_q[31:0] ^ data_q[63:32]; + out_valid_o = 1'b1; + c_fsm_d = C_INPUT; + end + end + + default: begin + c_fsm_d = C_INPUT; + end + endcase + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + c_fsm_q <= C_INPUT; + key_q <= '0; + data_q <= '0; + end else begin + c_fsm_q <= c_fsm_d; + key_q <= key_d; + data_q <= data_d; + end + end + +endmodule diff --git a/rtl/ibex_core.sv b/rtl/ibex_core.sv index 9c6d9a04..4aab38c3 100644 --- a/rtl/ibex_core.sv +++ b/rtl/ibex_core.sv @@ -33,7 +33,8 @@ module ibex_core #( parameter bit DbgTriggerEn = 1'b0, parameter bit SecureIbex = 1'b0, parameter int unsigned DmHaltAddr = 32'h1A110800, - parameter int unsigned DmExceptionAddr = 32'h1A110808 + parameter int unsigned DmExceptionAddr = 32'h1A110808, + parameter bit PointerAuthentication = 1'b0 ) ( // Clock and Reset input logic clk_i, @@ -344,6 +345,16 @@ module ibex_core #( logic [31:0] rvfi_mem_addr_q; `endif + // Pointer Authentication + logic pa_pac_en; + logic pa_aut_en; + logic [31:0] pa_data0; + logic [31:0] pa_data1; + logic pa_ready_id; + logic [31:0] pa_result; + logic pa_valid; + logic [127:0] csr_pa_key; + ////////////////////// // Clock management // ////////////////////// @@ -467,13 +478,14 @@ module ibex_core #( ////////////// ibex_id_stage #( - .RV32E ( RV32E ), - .RV32M ( RV32M ), - .RV32B ( RV32B ), - .BranchTargetALU ( BranchTargetALU ), - .DataIndTiming ( DataIndTiming ), - .SpecBranch ( SpecBranch ), - .WritebackStage ( WritebackStage ) + .RV32E ( RV32E ), + .RV32M ( RV32M ), + .RV32B ( RV32B ), + .BranchTargetALU ( BranchTargetALU ), + .DataIndTiming ( DataIndTiming ), + .SpecBranch ( SpecBranch ), + .WritebackStage ( WritebackStage ), + .PointerAuthentication ( PointerAuthentication ) ) id_stage_i ( .clk_i ( clk ), .rst_ni ( rst_ni ), @@ -616,7 +628,16 @@ module ibex_core #( .perf_mul_wait_o ( perf_mul_wait ), .perf_div_wait_o ( perf_div_wait ), .instr_id_done_o ( instr_id_done ), - .instr_id_done_compressed_o ( instr_id_done_compressed ) + .instr_id_done_compressed_o ( instr_id_done_compressed ), + + // Pointer Authentication + .pac_en_dec_o ( pa_pac_en ), + .aut_en_dec_o ( pa_aut_en ), + .pa_data0_o ( pa_data0 ), + .pa_data1_o ( pa_data1 ), + .pa_ready_id_o ( pa_ready_id ), + .pa_result_i ( pa_result ), + .pa_valid_i ( pa_valid ) ); // for RVFI only @@ -906,17 +927,18 @@ module ibex_core #( assign csr_addr = csr_num_e'(csr_access ? alu_operand_b_ex[11:0] : 12'b0); ibex_cs_registers #( - .DbgTriggerEn ( DbgTriggerEn ), - .DataIndTiming ( DataIndTiming ), - .DummyInstructions ( DummyInstructions ), - .ICache ( ICache ), - .MHPMCounterNum ( MHPMCounterNum ), - .MHPMCounterWidth ( MHPMCounterWidth ), - .PMPEnable ( PMPEnable ), - .PMPGranularity ( PMPGranularity ), - .PMPNumRegions ( PMPNumRegions ), - .RV32E ( RV32E ), - .RV32M ( RV32M ) + .DbgTriggerEn ( DbgTriggerEn ), + .DataIndTiming ( DataIndTiming ), + .DummyInstructions ( DummyInstructions ), + .ICache ( ICache ), + .MHPMCounterNum ( MHPMCounterNum ), + .MHPMCounterWidth ( MHPMCounterWidth ), + .PMPEnable ( PMPEnable ), + .PMPGranularity ( PMPGranularity ), + .PMPNumRegions ( PMPNumRegions ), + .RV32E ( RV32E ), + .RV32M ( RV32M ), + .PointerAuthentication ( PointerAuthentication ) ) cs_registers_i ( .clk_i ( clk ), .rst_ni ( rst_ni ), @@ -956,6 +978,9 @@ module ibex_core #( .csr_pmp_cfg_o ( csr_pmp_cfg ), .csr_pmp_addr_o ( csr_pmp_addr ), + // Pointer Authenticaiton + .csr_pa_key_o ( csr_pa_key ), + // debug .csr_depc_o ( csr_depc ), .debug_mode_i ( debug_mode ), @@ -1385,4 +1410,36 @@ module ibex_core #( `endif + if (PointerAuthentication) begin : g_pa + ibex_pointer_authentication pointer_authentication_i ( + .clk_i ( clk ), + .rst_ni ( rst_ni ), + .csr_pa_key_i ( csr_pa_key ), + .pac_en_i ( pa_pac_en ), + .aut_en_i ( pa_aut_en ), + .pa_data0_i ( pa_data0 ), + .pa_data1_i ( pa_data1 ), + .pa_ready_id_i ( pa_ready_id ), + .pa_result_o ( pa_result ), + .pa_valid_o ( pa_valid ) + ); + end else begin : g_no_pa + logic unused_pa_pac_en; + logic unused_pa_aut_en; + logic [31:0] unused_pa_data0; + logic [31:0] unused_pa_data1; + logic unused_pa_ready_id; + logic [127:0] unused_csr_pa_key; + assign unused_pa_pac_en = pa_pac_en; + assign unused_pa_aut_en = pa_aut_en; + assign unused_pa_data0 = pa_data0; + assign unused_pa_data1 = pa_data1; + assign unused_pa_ready_id = pa_ready_id; + assign unused_csr_pa_key = csr_pa_key; + + // Output tieoff + assign pa_result = '0; + assign pa_valid = '0; + end + endmodule diff --git a/rtl/ibex_core_tracing.sv b/rtl/ibex_core_tracing.sv index 4fbcde08..f03d2dd0 100644 --- a/rtl/ibex_core_tracing.sv +++ b/rtl/ibex_core_tracing.sv @@ -27,7 +27,8 @@ module ibex_core_tracing #( parameter bit DbgTriggerEn = 1'b0, parameter bit SecureIbex = 1'b0, parameter int unsigned DmHaltAddr = 32'h1A110800, - parameter int unsigned DmExceptionAddr = 32'h1A110808 + parameter int unsigned DmExceptionAddr = 32'h1A110808, + parameter bit PointerAuthentication = 1'b0 ) ( // Clock and Reset input logic clk_i, @@ -123,7 +124,8 @@ module ibex_core_tracing #( .WritebackStage ( WritebackStage ), .SecureIbex ( SecureIbex ), .DmHaltAddr ( DmHaltAddr ), - .DmExceptionAddr ( DmExceptionAddr ) + .DmExceptionAddr ( DmExceptionAddr ), + .PointerAuthentication ( PointerAuthentication ) ) u_ibex_core ( .clk_i, .rst_ni, diff --git a/rtl/ibex_cs_registers.sv b/rtl/ibex_cs_registers.sv index 6e5eaf4d..9a94dfd0 100644 --- a/rtl/ibex_cs_registers.sv +++ b/rtl/ibex_cs_registers.sv @@ -13,17 +13,18 @@ `include "prim_assert.sv" module ibex_cs_registers #( - parameter bit DbgTriggerEn = 0, - parameter bit DataIndTiming = 1'b0, - parameter bit DummyInstructions = 1'b0, - parameter bit ICache = 1'b0, - parameter int unsigned MHPMCounterNum = 10, - parameter int unsigned MHPMCounterWidth = 40, - parameter bit PMPEnable = 0, - parameter int unsigned PMPGranularity = 0, - parameter int unsigned PMPNumRegions = 4, - parameter bit RV32E = 0, - parameter bit RV32M = 0 + parameter bit DbgTriggerEn = 0, + parameter bit DataIndTiming = 1'b0, + parameter bit DummyInstructions = 1'b0, + parameter bit ICache = 1'b0, + parameter int unsigned MHPMCounterNum = 10, + parameter int unsigned MHPMCounterWidth = 40, + parameter bit PMPEnable = 0, + parameter int unsigned PMPGranularity = 0, + parameter int unsigned PMPNumRegions = 4, + parameter bit RV32E = 0, + parameter bit RV32M = 0, + parameter bit PointerAuthentication = 0 ) ( // Clock and Reset input logic clk_i, @@ -66,6 +67,9 @@ module ibex_cs_registers #( output ibex_pkg::pmp_cfg_t csr_pmp_cfg_o [PMPNumRegions], output logic [33:0] csr_pmp_addr_o [PMPNumRegions], + // Pointer Authentication + output logic [127:0] csr_pa_key_o, + // debug input logic debug_mode_i, input ibex_pkg::dbg_cause_e debug_cause_i, @@ -199,6 +203,9 @@ module ibex_cs_registers #( logic [31:0] pmp_addr_rdata [PMP_MAX_REGIONS]; logic [PMP_CFG_W-1:0] pmp_cfg_rdata [PMP_MAX_REGIONS]; + // Key for Pointer Authentication + logic [127:0] pa_key; + // Hardware performance monitor signals logic [31:0] mcountinhibit; // Only have mcountinhibit flops for counters that actually exist @@ -431,6 +438,11 @@ module ibex_cs_registers #( csr_rdata_int = '0; end + // Custom CSR for Pointer Authentication (cannot be read) + CSR_PAKEY0, CSR_PAKEY1, CSR_PAKEY2, CSR_PAKEY3: begin + csr_rdata_int = '0; + end + default: begin illegal_csr = 1'b1; end @@ -855,6 +867,36 @@ module ibex_cs_registers #( end end + //////////////////////////// + // Pointer Authentication // + //////////////////////////// + + if (PointerAuthentication) begin : g_pa_key + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + pa_key <= '0; + end else begin + if (csr_we_int && (csr_addr == CSR_PAKEY0)) begin + pa_key[31:0] <= csr_wdata_int; + end + if (csr_we_int && (csr_addr == CSR_PAKEY1)) begin + pa_key[63:32] <= csr_wdata_int; + end + if (csr_we_int && (csr_addr == CSR_PAKEY2)) begin + pa_key[95:64] <= csr_wdata_int; + end + if (csr_we_int && (csr_addr == CSR_PAKEY3)) begin + pa_key[127:96] <= csr_wdata_int; + end + end + end + end else begin : g_no_pa_key + // Generate tieoff when PointerAuthentication is not configured + assign pa_key = '0; + end + + assign csr_pa_key_o = pa_key; + ////////////////////////// // Performance monitor // ////////////////////////// diff --git a/rtl/ibex_decoder.sv b/rtl/ibex_decoder.sv index 3b2807eb..794949b1 100644 --- a/rtl/ibex_decoder.sv +++ b/rtl/ibex_decoder.sv @@ -14,10 +14,11 @@ `include "prim_assert.sv" module ibex_decoder #( - parameter bit RV32E = 0, - parameter bit RV32M = 1, - parameter bit BranchTargetALU = 0, - parameter ibex_pkg::rv32b_e RV32B = ibex_pkg::RV32BNone + parameter bit RV32E = 0, + parameter bit RV32M = 1, + parameter bit BranchTargetALU = 0, + parameter ibex_pkg::rv32b_e RV32B = ibex_pkg::RV32BNone, + parameter bit PointerAuthentication = 0 ) ( input logic clk_i, input logic rst_ni, @@ -94,7 +95,11 @@ module ibex_decoder #( // jump/branches output logic jump_in_dec_o, // jump is being calculated in ALU - output logic branch_in_dec_o + output logic branch_in_dec_o, + + // Pointer Authentication + output logic pac_en_dec_o, + output logic aut_en_dec_o ); import ibex_pkg::*; @@ -115,6 +120,9 @@ module ibex_decoder #( logic use_rs3_d; logic use_rs3_q; + logic pa_use_rs3; + logic pa_write_to_rs1; + csr_op_e csr_op; opcode_e opcode; @@ -154,11 +162,11 @@ module ibex_decoder #( assign instr_rs2 = instr[24:20]; assign instr_rs3 = instr[31:27]; assign rf_raddr_a_o = (use_rs3_q & ~instr_first_cycle_i) ? instr_rs3 : instr_rs1; // rs3 / rs1 - assign rf_raddr_b_o = instr_rs2; // rs2 + assign rf_raddr_b_o = pa_use_rs3 ? instr_rs3 : instr_rs2; // rs3 / rs2 // destination register assign instr_rd = instr[11:7]; - assign rf_waddr_o = instr_rd; // rd + assign rf_waddr_o = pa_write_to_rs1 ? instr_rs1 : instr_rd; // rs1 / rd //////////////////// // Register check // @@ -219,6 +227,11 @@ module ibex_decoder #( ecall_insn_o = 1'b0; wfi_insn_o = 1'b0; + pac_en_dec_o = 1'b0; + aut_en_dec_o = 1'b0; + pa_use_rs3 = 1'b0; + pa_write_to_rs1 = 1'b0; + opcode = opcode_e'(instr[6:0]); unique case (opcode) @@ -612,6 +625,45 @@ module ibex_decoder #( end end + + //////////////////////////// + // Pointer Authentication // + //////////////////////////// + + OPCODE_PA: begin // Custom Operations for Pointer Authentication + unique case (instr[14:12]) + 3'b000: begin // PAC + pac_en_dec_o = (PointerAuthentication) ? 1'b1 : 1'b0; + rf_wdata_sel_o = RF_WD_PA; + rf_we = 1'b1; + if (instr_first_cycle_i) begin + // First write magic number and LSBs of pointer to rs1 + pa_write_to_rs1 = 1'b1; + end else begin + // Then write MSBs of pointer and pac to rd + pa_write_to_rs1 = 1'b0; + end + illegal_insn = (PointerAuthentication) ? 1'b0 : 1'b1; + end + + 3'b001: begin // AUT + aut_en_dec_o = (PointerAuthentication) ? 1'b1 : 1'b0; + rf_wdata_sel_o = RF_WD_PA; + rf_we = 1'b1; + if (instr_first_cycle_i) begin + // First read MSBs of pointer and pac from rs2 + pa_use_rs3 = 1'b0; + end else begin + // Then read LSBs of pointer from rs1 and read context from rs3 + pa_use_rs3 = 1'b1; + end + illegal_insn = (PointerAuthentication) ? 1'b0 : 1'b1; + end + + default: + illegal_insn = 1'b1; + endcase + end default: begin illegal_insn = 1'b1; end diff --git a/rtl/ibex_id_stage.sv b/rtl/ibex_id_stage.sv index 809d8927..35defa4f 100644 --- a/rtl/ibex_id_stage.sv +++ b/rtl/ibex_id_stage.sv @@ -17,13 +17,14 @@ `include "prim_assert.sv" module ibex_id_stage #( - parameter bit RV32E = 0, - parameter bit RV32M = 1, - parameter ibex_pkg::rv32b_e RV32B = ibex_pkg::RV32BNone, - parameter bit DataIndTiming = 1'b0, - parameter bit BranchTargetALU = 0, - parameter bit SpecBranch = 0, - parameter bit WritebackStage = 0 + parameter bit RV32E = 0, + parameter bit RV32M = 1, + parameter ibex_pkg::rv32b_e RV32B = ibex_pkg::RV32BNone, + parameter bit DataIndTiming = 1'b0, + parameter bit BranchTargetALU = 0, + parameter bit SpecBranch = 0, + parameter bit WritebackStage = 0, + parameter bit PointerAuthentication = 0 ) ( input logic clk_i, input logic rst_ni, @@ -177,7 +178,16 @@ module ibex_id_stage #( output logic perf_mul_wait_o, output logic perf_div_wait_o, output logic instr_id_done_o, - output logic instr_id_done_compressed_o + output logic instr_id_done_compressed_o, + + // Pointer Authentication + output logic pac_en_dec_o, + output logic aut_en_dec_o, + output logic [31:0] pa_data0_o, + output logic [31:0] pa_data1_o, + output logic pa_ready_id_o, + input logic [31:0] pa_result_i, + input logic pa_valid_i ); import ibex_pkg::*; @@ -273,6 +283,10 @@ module ibex_id_stage #( logic [31:0] alu_operand_a; logic [31:0] alu_operand_b; + // Pointer Authentication + logic pa_en_dec; + logic stall_pa; + ///////////// // LSU Mux // ///////////// @@ -401,6 +415,7 @@ module ibex_id_stage #( unique case (rf_wdata_sel) RF_WD_EX: rf_wdata_id_o = result_ex_i; RF_WD_CSR: rf_wdata_id_o = csr_rdata_i; + RF_WD_PA: rf_wdata_id_o = pa_result_i; default: rf_wdata_id_o = result_ex_i; endcase; end @@ -410,10 +425,11 @@ module ibex_id_stage #( ///////////// ibex_decoder #( - .RV32E ( RV32E ), - .RV32M ( RV32M ), - .RV32B ( RV32B ), - .BranchTargetALU ( BranchTargetALU ) + .RV32E ( RV32E ), + .RV32M ( RV32M ), + .RV32B ( RV32B ), + .BranchTargetALU ( BranchTargetALU ), + .PointerAuthentication (PointerAuthentication ) ) decoder_i ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), @@ -484,7 +500,11 @@ module ibex_id_stage #( // jump/branches .jump_in_dec_o ( jump_in_dec ), - .branch_in_dec_o ( branch_in_dec ) + .branch_in_dec_o ( branch_in_dec ), + + // Pointer Authentication + .pac_en_dec_o ( pac_en_dec_o ), + .aut_en_dec_o ( aut_en_dec_o ) ); ///////////////////////////////// @@ -638,6 +658,8 @@ module ibex_id_stage #( assign multdiv_operand_a_ex_o = rf_rdata_a_fwd; assign multdiv_operand_b_ex_o = rf_rdata_b_fwd; + assign pa_en_dec = pac_en_dec_o | aut_en_dec_o; + //////////////////////// // Branch set control // //////////////////////// @@ -723,6 +745,7 @@ module ibex_id_stage #( stall_jump = 1'b0; stall_branch = 1'b0; stall_alu = 1'b0; + stall_pa = 1'b0; branch_set_d = 1'b0; branch_spec = 1'b0; jump_set = 1'b0; @@ -776,6 +799,22 @@ module ibex_id_stage #( id_fsm_d = MULTI_CYCLE; rf_we_raw = 1'b0; end + pa_en_dec: begin + if (pac_en_dec_o) begin // PAC + if (ready_wb_i) begin + id_fsm_d = MULTI_CYCLE; + rf_we_raw = 1'b1; + end else begin + // Wait until the writeback stage is ready + id_fsm_d = FIRST_CYCLE; + rf_we_raw = 1'b0; + end + end else begin // AUT + id_fsm_d = MULTI_CYCLE; + rf_we_raw = 1'b0; + end + stall_pa = 1'b1; + end default: begin id_fsm_d = FIRST_CYCLE; end @@ -787,12 +826,17 @@ module ibex_id_stage #( rf_we_raw = rf_we_dec & ex_valid_i; end + if (pa_en_dec) begin + rf_we_raw = rf_we_dec & pa_valid_i; + end + if (multicycle_done & ready_wb_i) begin id_fsm_d = FIRST_CYCLE; end else begin stall_multdiv = multdiv_en_dec; stall_branch = branch_in_dec; stall_jump = jump_in_dec; + stall_pa = pa_en_dec; end end @@ -805,12 +849,13 @@ module ibex_id_stage #( // Note for the two-stage configuration ready_wb_i is always set assign multdiv_ready_id_o = ready_wb_i; + assign pa_ready_id_o = ready_wb_i; `ASSERT(StallIDIfMulticycle, (id_fsm_q == FIRST_CYCLE) & (id_fsm_d == MULTI_CYCLE) |-> stall_id) // Stall ID/EX stage for reason that relates to instruction in ID/EX assign stall_id = stall_ld_hz | stall_mem | stall_multdiv | stall_jump | stall_branch | - stall_alu; + stall_alu | stall_pa; assign instr_done = ~stall_id & ~flush_id & instr_executing; @@ -833,7 +878,8 @@ module ibex_id_stage #( logic instr_kill; - assign multicycle_done = lsu_req_dec ? ~stall_mem : ex_valid_i; + assign multicycle_done = lsu_req_dec ? ~stall_mem : + pa_en_dec ? pa_valid_i : ex_valid_i; // Is a memory access ongoing that isn't finishing this cycle assign outstanding_memory_access = (outstanding_load_wb_i | outstanding_store_wb_i) & @@ -910,7 +956,7 @@ module ibex_id_stage #( lsu_we ? WB_INSTR_STORE : WB_INSTR_LOAD; - assign en_wb_o = instr_done; + assign en_wb_o = instr_done | (pac_en_dec_o & instr_first_cycle); assign instr_id_done_o = en_wb_o & ready_wb_i; @@ -921,7 +967,8 @@ module ibex_id_stage #( (outstanding_memory_access | stall_ld_hz); end else begin : gen_no_stall_mem - assign multicycle_done = lsu_req_dec ? lsu_resp_valid_i : ex_valid_i; + assign multicycle_done = lsu_req_dec ? lsu_resp_valid_i : + pa_en_dec ? pa_valid_i : ex_valid_i; assign data_req_allowed = instr_first_cycle; @@ -981,6 +1028,9 @@ module ibex_id_stage #( assign instr_id_done_compressed_o = instr_id_done_o & instr_is_compressed_i; + assign pa_data0_o = rf_rdata_a_fwd; + assign pa_data1_o = rf_rdata_b_fwd; + //////////////// // Assertions // //////////////// @@ -1004,7 +1054,8 @@ module ibex_id_stage #( IMM_B_INCR_PC}) `ASSERT(IbexRegfileWdataSelValid, instr_valid_i |-> rf_wdata_sel inside { RF_WD_EX, - RF_WD_CSR}) + RF_WD_CSR, + RF_WD_PA}) `ASSERT_KNOWN(IbexWbStateKnown, id_fsm_q) // Branch decision must be valid when jumping. diff --git a/rtl/ibex_pkg.sv b/rtl/ibex_pkg.sv index bb086ecb..86fd5728 100644 --- a/rtl/ibex_pkg.sv +++ b/rtl/ibex_pkg.sv @@ -24,6 +24,7 @@ typedef enum integer { typedef enum logic [6:0] { OPCODE_LOAD = 7'h03, + OPCODE_PA = 7'h0b, OPCODE_MISC_MEM = 7'h0f, OPCODE_OP_IMM = 7'h13, OPCODE_AUIPC = 7'h17, @@ -224,9 +225,10 @@ typedef enum logic [2:0] { } imm_b_sel_e; // Regfile write data selection -typedef enum logic { +typedef enum logic [1:0] { RF_WD_EX, - RF_WD_CSR + RF_WD_CSR, + RF_WD_PA } rf_wd_sel_e; ////////////// @@ -466,7 +468,11 @@ typedef enum logic[11:0] { CSR_MHPMCOUNTER30H = 12'hB9E, CSR_MHPMCOUNTER31H = 12'hB9F, CSR_CPUCTRL = 12'h7C0, - CSR_SECURESEED = 12'h7C1 + CSR_SECURESEED = 12'h7C1, + CSR_PAKEY0 = 12'h7C2, + CSR_PAKEY1 = 12'h7C3, + CSR_PAKEY2 = 12'h7C4, + CSR_PAKEY3 = 12'h7C5 } csr_num_e; // CSR pmp-related offsets diff --git a/rtl/ibex_pointer_authentication.sv b/rtl/ibex_pointer_authentication.sv new file mode 100644 index 00000000..d57ad3e2 --- /dev/null +++ b/rtl/ibex_pointer_authentication.sv @@ -0,0 +1,193 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module ibex_pointer_authentication #( + // This parameter is used in PAC instruction to rewrite MSBs of pointer to an invalid value + parameter logic [3:0] MSBsInvalidPointer = 4'b1010 +) ( + input logic clk_i, + input logic rst_ni, + + input logic [127:0] csr_pa_key_i, + + input logic pac_en_i, + input logic aut_en_i, + input logic [31:0] pa_data0_i, + input logic [31:0] pa_data1_i, + + input logic pa_ready_id_i, + + output logic [31:0] pa_result_o, + output logic pa_valid_o +); + + logic [3:0] msbs_pointer_q, msbs_pointer_d; + + // We could theoretically avoid to buffer the pac + // by changing the decoder to provide the following outputs: + // Cycle 1: ptr MSBs (just store ptr MSBs, ignore the pac coming from same register) + // Cycle 2: ptr LSBs + context (now we can start the cipher) + // Cycle 3 onwards: pac + // But this is not currently done as it requires a lot of changes. + logic [27:0] pac_q, pac_d; + + logic [63:0] cipher_in_data; + logic cipher_in_ready; + logic cipher_in_valid; + logic [31:0] cipher_out_data; + logic cipher_out_ready; + logic cipher_out_valid; + + ibex_cipher ibex_cipher_i ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .key_i ( csr_pa_key_i ), + .in_data_i ( cipher_in_data ), + .in_valid_i ( cipher_in_valid ), + .in_ready_o ( cipher_in_ready ), + .out_data_o ( cipher_out_data ), + .out_valid_o ( cipher_out_valid ), + .out_ready_i ( cipher_out_ready ) + ); + + typedef enum logic [1:0] { + PA_IDLE, PA_START_AUT, PA_WAIT + } pa_fsm_e; + pa_fsm_e pa_fsm_q, pa_fsm_d; + + typedef enum logic [1:0] { + PAC_START, PAC_DONE, AUT_SUCCESS, AUT_FAILURE + } pa_result_sel_e; + pa_result_sel_e pa_result_sel; + + typedef enum logic [1:0] { + CIPHER_IDLE, CIPHER_PAC_START, CIPHER_AUT_START + } cipher_in_data_sel_e; + cipher_in_data_sel_e cipher_in_data_sel; + + always_comb begin : pa_result_mux + unique case (pa_result_sel) + // MSBs of invalid pointer, LSBs of pointer + PAC_START: pa_result_o = {MSBsInvalidPointer, pa_data0_i[27:0]}; + // MSBs of pointer, Pointer Authentication Code + PAC_DONE: pa_result_o = {msbs_pointer_q, cipher_out_data[27:0]}; + // MSBs of pointer, LSB of pointer + AUT_SUCCESS: pa_result_o = {msbs_pointer_q, pa_data0_i[27:0]}; + // We should make sure that the invalid bits get filled in the pointer + // no matter the content of msbs_pointer_q + // MSBs of invalid pointer, LSBs of pointer + AUT_FAILURE: pa_result_o = {MSBsInvalidPointer, pa_data0_i[27:0]}; + // We usually take the first value from above, + // but AUT_FAILURE seems to be more sensible in this scenario + default: pa_result_o = {MSBsInvalidPointer, pa_data0_i[27:0]}; + endcase + end + + always_comb begin : cipher_in_data_mux + unique case (cipher_in_data_sel) + CIPHER_IDLE: cipher_in_data = '0; + // context, MSBs of pointer, LSBs of pointer + CIPHER_PAC_START: cipher_in_data = {pa_data1_i, msbs_pointer_d, pa_data0_i[27:0]}; + CIPHER_AUT_START: cipher_in_data = {pa_data1_i, msbs_pointer_q, pa_data0_i[27:0]}; + endcase + end + + // FSM + always_comb begin + pa_result_sel = AUT_FAILURE; + pa_valid_o = '0; + + msbs_pointer_d = msbs_pointer_q; + pac_d = pac_q; + + cipher_in_data_sel = CIPHER_IDLE; + cipher_in_valid = '0; + cipher_out_ready = '0; + + pa_fsm_d = pa_fsm_q; + + unique case (pa_fsm_q) + + PA_IDLE: begin + unique case (1'b1) + pac_en_i: begin + pa_result_sel = PAC_START; + msbs_pointer_d = pa_data0_i[31:28]; + cipher_in_data_sel = CIPHER_PAC_START; + cipher_in_valid = 1'b1; + if (cipher_in_ready) begin + pa_fsm_d = PA_WAIT; + end + end + aut_en_i: begin + msbs_pointer_d = pa_data1_i[31:28]; + pac_d = pa_data1_i[27:0]; + pa_fsm_d = PA_START_AUT; + end + default: begin + pa_fsm_d = PA_IDLE; + end + endcase + end + + PA_START_AUT: begin + cipher_in_data_sel = CIPHER_AUT_START; + cipher_in_valid = 1'b1; + if (cipher_in_ready) begin + pa_fsm_d = PA_WAIT; + end + end + + PA_WAIT: begin + unique case (1'b1) + pac_en_i: begin + if (pa_ready_id_i) begin + cipher_out_ready = 1'b1; + end + if (cipher_out_ready && cipher_out_valid) begin + pa_result_sel = PAC_DONE; + pa_valid_o = 1'b1; + pa_fsm_d = PA_IDLE; + end + end + aut_en_i: begin + if (pa_ready_id_i) begin + cipher_out_ready = 1'b1; + end + if (cipher_out_ready && cipher_out_valid) begin + if (pac_q == cipher_out_data[27:0]) begin // Authentication success + pa_result_sel = AUT_SUCCESS; + end else begin // Authentication failure + pa_result_sel = AUT_FAILURE; + end + pa_valid_o = 1'b1; + pa_fsm_d = PA_IDLE; + end + end + default: begin + pa_fsm_d = PA_IDLE; + end + endcase + end + + default: begin + pa_fsm_d = PA_IDLE; + end + endcase + end + + // registers for FSM + always_ff @(posedge clk_i or negedge rst_ni) begin + if(~rst_ni) begin + pa_fsm_q <= PA_IDLE; + msbs_pointer_q <= '0; + pac_q <= '0; + end else begin + pa_fsm_q <= pa_fsm_d; + msbs_pointer_q <= msbs_pointer_d; + pac_q <= pac_d; + end + end + +endmodule