From b124ba61130114503ef9e4e89c2f3d1a57911443 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sun, 17 Nov 2024 08:46:39 -0800 Subject: [PATCH 1/3] [topgen] Add function to render AC range checks Signed-off-by: Robert Schilling --- util/topgen.py | 19 +++++++++++++++++++ util/topgen/validate.py | 1 + 2 files changed, 20 insertions(+) diff --git a/util/topgen.py b/util/topgen.py index 5f58cc764d06a..660e8e01abe60 100755 --- a/util/topgen.py +++ b/util/topgen.py @@ -467,6 +467,22 @@ def generate_flash(topcfg: Dict[str, object], out_path: Path) -> None: ipgen_render("flash_ctrl", topname, params, out_path) +# generate ac_range_check with ipgen +def generate_ac_range_check(topcfg: Dict[str, object], out_path: Path) -> None: + # Not all tops have an ac range check instance + if 'ac_range_check' not in topcfg: + return + + log.info('Generating ac_range_check with ipgen') + topname = topcfg['name'] + + params = { + "num_ranges": topcfg['ac_range_check']['num_ranges'] + } + + ipgen_render("ac_range_check", topname, params, out_path) + + def generate_top_only(top_only_dict: Dict[str, bool], out_path: Path, top_name: str, alt_hjson_path: str) -> None: log.info("Generating top only modules") @@ -792,6 +808,9 @@ def _process_top( # Generate rstmgr generate_rstmgr(completecfg, out_path) + # Generate ac_range_check + generate_ac_range_check(completecfg, out_path) + # Generate top only modules # These modules are not ipgen, but are not in hw/ip generate_top_only(top_only_dict, cfg_path, top_name, args.hjson_path) diff --git a/util/topgen/validate.py b/util/topgen/validate.py index 5679428595951..d24cb4de09601 100644 --- a/util/topgen/validate.py +++ b/util/topgen/validate.py @@ -53,6 +53,7 @@ } top_optional = { + 'ac_range_check': ['g', 'Optional AC range configuration'], 'alert_async': ['l', 'async alerts (generated)'], 'alert': ['lnw', 'alerts (generated)'], 'alert_module': [ From 7019a589abb14f58053a4d017b142380fda13af1 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sun, 17 Nov 2024 08:52:56 -0800 Subject: [PATCH 2/3] [hw,ac_range_check,rtl] Initial register description and top-level Signed-off-by: Robert Schilling --- hw/ip_templates/ac_range_check/README.md | 1 + .../ac_range_check/ac_range_check.core.tpl | 70 ++++ .../data/ac_range_check.hjson.tpl | 323 ++++++++++++++++++ .../data/ac_range_check.tpldesc.hjson | 25 ++ .../ac_range_check/doc/registers.md.tpl | 4 + .../ac_range_check/doc/theory_of_operation.md | 1 + .../ac_range_check/lint/ac_range_check.vlt | 5 + .../ac_range_check/lint/ac_range_check.waiver | 5 + .../ac_range_check/rtl/ac_range_check.sv.tpl | 90 +++++ util/reggen/ip_block.py | 3 +- 10 files changed, 526 insertions(+), 1 deletion(-) create mode 100644 hw/ip_templates/ac_range_check/README.md create mode 100644 hw/ip_templates/ac_range_check/ac_range_check.core.tpl create mode 100644 hw/ip_templates/ac_range_check/data/ac_range_check.hjson.tpl create mode 100644 hw/ip_templates/ac_range_check/data/ac_range_check.tpldesc.hjson create mode 100644 hw/ip_templates/ac_range_check/doc/registers.md.tpl create mode 100644 hw/ip_templates/ac_range_check/doc/theory_of_operation.md create mode 100644 hw/ip_templates/ac_range_check/lint/ac_range_check.vlt create mode 100644 hw/ip_templates/ac_range_check/lint/ac_range_check.waiver create mode 100644 hw/ip_templates/ac_range_check/rtl/ac_range_check.sv.tpl diff --git a/hw/ip_templates/ac_range_check/README.md b/hw/ip_templates/ac_range_check/README.md new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/hw/ip_templates/ac_range_check/README.md @@ -0,0 +1 @@ + diff --git a/hw/ip_templates/ac_range_check/ac_range_check.core.tpl b/hw/ip_templates/ac_range_check/ac_range_check.core.tpl new file mode 100644 index 0000000000000..d4f7d3bab0d3a --- /dev/null +++ b/hw/ip_templates/ac_range_check/ac_range_check.core.tpl @@ -0,0 +1,70 @@ +CAPI=2: +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: ${instance_vlnv(f"lowrisc:ip:{module_instance_name}:0.1")} +description: "AC Range Check RTL" +virtual: + - lowrisc:ip_interfaces:ac_range_check + +filesets: + files_rtl: + depend: + - lowrisc:ip:tlul + - lowrisc:prim:mubi + - lowrisc:prim:all + files: + - rtl/${module_instance_name}_reg_pkg.sv + - rtl/${module_instance_name}_reg_top.sv + - rtl/${module_instance_name}.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + files: + - lint/ac_range_check.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + files: + - lint/ac_range_check.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + +parameters: + SYNTHESIS: + datatype: bool + paramtype: vlogdefine + + +targets: + default: &default_target + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + toplevel: ${module_instance_name} + + lint: + <<: *default_target + default_tool: verilator + parameters: + - SYNTHESIS=true + tools: + verilator: + mode: lint-only + verilator_options: + - "-Wall" diff --git a/hw/ip_templates/ac_range_check/data/ac_range_check.hjson.tpl b/hw/ip_templates/ac_range_check/data/ac_range_check.hjson.tpl new file mode 100644 index 0000000000000..831cb10e07b77 --- /dev/null +++ b/hw/ip_templates/ac_range_check/data/ac_range_check.hjson.tpl @@ -0,0 +1,323 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +# AC Range Check register template +{ + name: "ac_range_check" + human_name: "Access Control Range Check" + one_line_desc: "Access Control Range Check." + one_paragraph_desc: ''' + ''' + cip_id: "41", + design_spec: "../doc" + dv_doc: "../doc/dv" + version: "1.0.0" + + clocking: [{clock: "clk_i", reset: "rst_ni", primary: true}] + bus_interfaces: [ + { protocol: "tlul", direction: "device", hier_path: "u_ac_range_check_reg" } + ] + param_list: [ + { name: "NumRanges", + desc: "Number of range registers", + type: "int", + default: "${num_ranges}", + }, + ], + inter_signal_list: [ + { name: "range_check_overwrite" + type: "uni", + act: "req", + package: "prim_mubi_pkg", + struct: "mubi4", + width: "1" + desc: "Overwrites all ranges and let all requests pass through." + }, + { struct: "tl_h2d" + package: "tlul_pkg" + type: "uni" + name: "ctn_tl_h2d" + act: "rcv" + desc: "TL-UL input port (request part), synchronous" + } + { struct: "tl_d2h" + package: "tlul_pkg" + type: "uni" + name: "ctn_tl_d2h" + act: "req" + desc: "TL-UL input port (response part), synchronous" + } + { struct: "tl_h2d" + package: "tlul_pkg" + type: "uni" + name: "ctn_filtered_tl_h2d" + act: "req" + desc: "Filtered TL-UL output port (request part), synchronous" + } + { struct: "tl_d2h" + package: "tlul_pkg" + type: "uni" + name: "ctn_filtered_tl_d2h" + act: "rcv" + desc: "Filtered TL-UL output port (response part), synchronous" + } + ] + interrupt_list: [ + { name: "deny_cnt_reached" + desc: "Deny counter has reached threshold." + } + ] + alert_list: [ + { name: "recov_ctrl_update_err", + desc: "This recoverable alert is triggered upon detecting an update error in the shadowed Control Register." + } + { name: "fatal_fault" + desc: "This fatal alert is triggered when a fatal TL-UL bus integrity fault is detected or the internal counter has an error." + } + ] + countermeasures: [ + { name: "BUS.INTEGRITY", + desc: "End-to-end bus integrity scheme." + } + ] + regwidth: "32" + registers: [ + { name: "LOG_CONFIG" + desc: "" + swaccess: "rw" + hwaccess: "hro" + fields: [ + { bits: "9:2" + name: "deny_cnt_threshold" + resval: 0x0 + desc: "An interrupt is raised (if enabled) when deny_cnt reaches the configured deny_cnt_threshold." + } + { bits: "1" + name: "log_clear" + resval: 0x0 + desc: '''Clears all log information for the first denied access including: + - LOG_STATUS + - LOG_ADDRESS. + ''' + } + { bits: "0" + name: "log_enable" + resval: 0x0 + desc: "When set, blocked requests are logged by the deny counter." + } + ] + } + { name: "LOG_STATUS" + desc: ''' + The LOG_STATUS register stores the number of denied accesses and gives more detailed diagnostics to the first denied request. + All fields of LOG_STATUS (other than deny_cnt) are only valid if deny_cnt > 0. + ''' + swaccess: "ro" + hwaccess: "hwo" + fields: [ + { bits: "22:18" + name: "deny_range_index" + resval: 0x0 + desc: "Index of the range that caused the denied access." + } + { bits: "17:14" + name: "denied_source_role" + resval: 0x0 + desc: "Source RACL role that was denied access." + } + { bits: "13" + name: "denied_racl_write" + resval: 0x0 + desc: "Indicates whether a write access was denied by RACL." + } + { bits: "12" + name: "denied_racl_read" + resval: 0x0 + desc: "Indicates whether a read access was denied by RACL." + } + { bits: "11" + name: "denied_no_match" + resval: 0x0 + desc: "Indicates whether the access was denied because no range matched." + } + { bits: "10" + name: "denied_execute_access" + resval: 0x0 + desc: "Indicates whether execution access was denied." + } + { bits: "9" + name: "denied_write_access" + resval: 0x0 + desc: "Indicates whether a write access was denied." + } + { bits: "8" + name: "denied_read_access" + resval: 0x0 + desc: "Indicates whether a read access was denied." + } + { bits: "7:0" + name: "deny_cnt" + resval: 0x0 + desc: ''' + Software mirror of the internal deny counter. + Gets incremented for every blocked request. + ''' + } + ] + } + { name: "LOG_ADDRESS" + desc: ''' + First denied request address (if logging is enabled) gets written into that register. + ''' + swaccess: "ro" + hwaccess: "hwo" + fields: [ + { bits: "31:0" + name: "log_address" + resval: 0x0 + desc: "First denied request address." + } + ] + } + { multireg: { + name: "RANGE_REGWEN" + desc: ''' + This register exists per range and provides a regwen signal for the RANGE_BASE_x, RANGE_LIMIT_x, RANGE_PERM_x, and RANGE_RACL_POLICY_SHADOWED_x register. + When cleared to Mubi4::False, the corresponding range configuration registers are locked and cannot be changed until the next reset. + ''' + count: "NumRanges" + cname: "range_regwen" + swaccess: "rw0c" + hwaccess: "none" + compact: false + fields: [ + { bits: "3:0" + resval: true + mubi: true + name: "regwen" + desc: "Clearing this register, locks the confgiguration registers of that range until the next reset." + } + ] + } + } + { multireg: { + name: "RANGE_BASE" + desc: ''' + Base address for the range check. + The range base register exists per range and holds the base address for the range check. + The minimum granularity of the range is 4 bytes. + Therefore, the lowest 2 bits of the 32-bit base and limit registers are tied to zero. + ''' + count: "NumRanges" + cname: "range_base" + swaccess: "rw" + hwaccess: "hro" + regwen: "RANGE_REGWEN" + regwen_multi: true + fields: [ + { name: "base" + desc: "Base address" + bits: "31:2" + resval: 0x0 + } + ] + } + } + { multireg: { + name: "RANGE_LIMIT" + desc: ''' + The (exclusive) limit address register used for the address matching. + ''' + count: "NumRanges" + cname: "BASE" + swaccess: "rw" + hwaccess: "hro" + regwen: "RANGE_REGWEN" + regwen_multi: true + fields: [ + { name: "limit" + desc: "Exclusive limit address." + bits: "31:2" + resval: 0x0 + } + ] + } + } + { multireg: { + name: "RANGE_PERM" + desc: ''' + Permission configuration of the range. + The permission register exists per range and determines the access permissions of the particular range. + If it is not enabled, the range is not considered during the range check. + ''' + count: "NumRanges" + cname: "BASE" + swaccess: "rw" + hwaccess: "hro" + regwen: "RANGE_REGWEN" + regwen_multi: true + fields: [ + { name: "log_denied_access" + desc: "When set to Mubi4::True, a denied access based on in this range is being logged." + bits: "19:16" + mubi: true + resval: true + } + { name: "execute_access" + desc: "When set to Mubi4::True, code execution from this range is allowed." + bits: "15:12" + mubi: true + resval: false + } + { name: "write_access" + desc: "When set to Mubi4::True, write access to that range is allowed." + bits: "11:8" + mubi: true + resval: false + } + { name: "read_access" + desc: "When set to Mubi4::True, read access from that range is allowed." + bits: "7:4" + mubi: true + resval: false + } + { name: "enable" + desc: "When set to Mubi4::True, the range is considered in the range check." + bits: "3:0" + mubi: true + resval: false + } + ] + } + } + { multireg: { + name: "RANGE_RACL_POLICY_SHADOWED" + desc: ''' + The RACL policy register exists and allows the system to further restrict the access to specific source roles. + The default value for both the read and write permission bitmap is set to a value to allow the access from all roles. + This register is protected against fault attacks by using a shadow register implementation. + ''' + count: "NumRanges" + cname: "RACL" + swaccess: "rw" + hwaccess: "hro" + regwen: "RANGE_REGWEN" + regwen_multi: true + shadowed: "true", + fields: [ + { name: "write_perm" + desc: "Write permission policy bitmap." + bits: "31:16" + resval: 0x0 + } + { name: "read_perm" + desc: "Read permission policy bitmap." + bits: "15:0" + resval: 0x0 + } + ] + } + } + ] +} diff --git a/hw/ip_templates/ac_range_check/data/ac_range_check.tpldesc.hjson b/hw/ip_templates/ac_range_check/data/ac_range_check.tpldesc.hjson new file mode 100644 index 0000000000000..fb65ec872a80b --- /dev/null +++ b/hw/ip_templates/ac_range_check/data/ac_range_check.tpldesc.hjson @@ -0,0 +1,25 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + template_param_list: [ + { + name: "topname" + desc: "Name of top-level design, e.g., 'darjeeling' or 'earlgrey'" + type: "string" + default: "" + } + { + name: "module_instance_name" + desc: "instance name in case there are multiple AC Range Check instances" + type: "string" + default: "ac_range_check" + } + { + name: "num_ranges", + desc: "Number of range registers", + type: "int", + default: "32", + } + ] +} diff --git a/hw/ip_templates/ac_range_check/doc/registers.md.tpl b/hw/ip_templates/ac_range_check/doc/registers.md.tpl new file mode 100644 index 0000000000000..eb01fc83e55dc --- /dev/null +++ b/hw/ip_templates/ac_range_check/doc/registers.md.tpl @@ -0,0 +1,4 @@ +# Registers + + + diff --git a/hw/ip_templates/ac_range_check/doc/theory_of_operation.md b/hw/ip_templates/ac_range_check/doc/theory_of_operation.md new file mode 100644 index 0000000000000..b1e31a5e53eac --- /dev/null +++ b/hw/ip_templates/ac_range_check/doc/theory_of_operation.md @@ -0,0 +1 @@ +# Theory of Operation diff --git a/hw/ip_templates/ac_range_check/lint/ac_range_check.vlt b/hw/ip_templates/ac_range_check/lint/ac_range_check.vlt new file mode 100644 index 0000000000000..f5984ff79f639 --- /dev/null +++ b/hw/ip_templates/ac_range_check/lint/ac_range_check.vlt @@ -0,0 +1,5 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// waiver file for ac_range_check diff --git a/hw/ip_templates/ac_range_check/lint/ac_range_check.waiver b/hw/ip_templates/ac_range_check/lint/ac_range_check.waiver new file mode 100644 index 0000000000000..d1b68019ea839 --- /dev/null +++ b/hw/ip_templates/ac_range_check/lint/ac_range_check.waiver @@ -0,0 +1,5 @@ +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# waiver file for ac_range_check diff --git a/hw/ip_templates/ac_range_check/rtl/ac_range_check.sv.tpl b/hw/ip_templates/ac_range_check/rtl/ac_range_check.sv.tpl new file mode 100644 index 0000000000000..5b8fe3f172f8a --- /dev/null +++ b/hw/ip_templates/ac_range_check/rtl/ac_range_check.sv.tpl @@ -0,0 +1,90 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module ${module_instance_name} + import tlul_pkg::*; + import ${module_instance_name}_reg_pkg::*; +#( +) ( + input logic clk_i, + input logic rst_ni, + input logic rst_shadowed_ni, + // Alerts + input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i, + output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o, + // Access range check interrupts + output logic intr_deny_cnt_reached_o, + // Inter module signals + input prim_mubi_pkg::mubi4_t range_check_overwrite_i, + // Incoming TLUL interface + input tlul_pkg::tl_h2d_t ctn_tl_h2d_i, + output tlul_pkg::tl_d2h_t ctn_tl_d2h_o, + // Filtered outgoing TLUL interface to the target if request is not squashed + output tlul_pkg::tl_h2d_t ctn_filtered_tl_h2d_o, + input tlul_pkg::tl_d2h_t ctn_filtered_tl_d2h_i +); + ${module_instance_name}_reg2hw_t reg2hw; + ${module_instance_name}_hw2reg_t hw2reg; + + ////////////////////////////////////////////////////////////////////////////// + // Register Interface + ////////////////////////////////////////////////////////////////////////////// + logic reg_intg_error, shadowed_storage_err, shadowed_update_err; + // SEC_CM: BUS.INTEGRITY + ${module_instance_name}_reg_top u_ac_range_check_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .rst_shadowed_ni ( rst_shadowed_ni ), + .tl_i ( tl_i ), + .tl_o ( tl_o ), + .reg2hw ( reg2hw ), + .hw2reg ( hw2reg ), + .shadowed_storage_err_o ( shadowed_storage_err ), + .shadowed_update_err_o ( shadowed_update_err ), + .intg_err_o ( reg_intg_error ) + ); + + ////////////////////////////////////////////////////////////////////////////// + // Alerts + ////////////////////////////////////////////////////////////////////////////// + logic [NumAlerts-1:0] alert_test, alert; + + assign alert[0] = shadowed_update_err; + assign alert[1] = reg_intg_error | shadowed_storage_err; + + assign alert_test = { + reg2hw.alert_test.fatal_fault.q & + reg2hw.alert_test.fatal_fault.qe, + reg2hw.alert_test.recov_ctrl_update_err.q & + reg2hw.alert_test.recov_ctrl_update_err.qe + }; + + for (genvar i = 0; i < NumAlerts; i++) begin : gen_alert_tx + prim_alert_sender #( + .AsyncOn(AlertAsyncOn[i]), + .IsFatal(i) + ) u_prim_alert_sender ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .alert_test_i ( alert_test[i] ), + .alert_req_i ( alerts[i] ), + .alert_ack_o ( ), + .alert_state_o ( ), + .alert_rx_i ( alert_rx_i[i] ), + .alert_tx_o ( alert_tx_o[i] ) + ); + end + + ////////////////////////////////////////////////////////////////////////////// + // Assertions + ////////////////////////////////////////////////////////////////////////////// + + // All outputs should be known value after reset + `ASSERT_KNOWN(AlertsKnown_A, alert_tx_o) + + // Alert assertions for reg_we onehot check + `ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ALERT(RegWeOnehotCheck_A, u_ac_range_check_reg, + alert_tx_o[0]) + +endmodule diff --git a/util/reggen/ip_block.py b/util/reggen/ip_block.py index 4b55035bc0a03..425bcfe1ac03e 100644 --- a/util/reggen/ip_block.py +++ b/util/reggen/ip_block.py @@ -61,7 +61,8 @@ 37: 'mbx', 38: 'soc_proxy', 39: 'keymgr_dpe', - 40: 'ascon' + 40: 'ascon', + 41: 'ac_range_check' } REQUIRED_ALIAS_FIELDS = { From 467970568f1bd0fe181c9dd36ca5e9589a81e4ab Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Tue, 19 Nov 2024 15:32:44 +0100 Subject: [PATCH 3/3] [hw,ac_ranges,doc] Port over the RFC documentation Signed-off-by: Robert Schilling --- hw/ip_templates/ac_range_check/README.md | 124 ++++++++++++++++++ .../ac_range_check/doc/ac_range_check.svg | 1 + .../ac_range_check/doc/theory_of_operation.md | 36 +++++ 3 files changed, 161 insertions(+) create mode 100644 hw/ip_templates/ac_range_check/doc/ac_range_check.svg diff --git a/hw/ip_templates/ac_range_check/README.md b/hw/ip_templates/ac_range_check/README.md index 8b137891791fe..287720f7ca1c3 100644 --- a/hw/ip_templates/ac_range_check/README.md +++ b/hw/ip_templates/ac_range_check/README.md @@ -1 +1,125 @@ +# Access Control Range Check (AC Range Check) +The System on Chip (SoC) must implement differentiated security access controls for shared memory regions located outside of the Root of Trust (RoT). +To facilitate access to these memory regions, a dedicated TL-UL port is provided, connecting to the Control Network (CTN) fabric. +This document outlines the requirements for a framework, referred to as Access Control Range Check (AC Range Check). +In the proposed architecture, the AC Range Check mechanism intercepts the outgoing TL-UL bus, ensuring that only valid and authorized addresses are accessed. +Adding the AC Range Check on the TL-UL bus will likely introduce a flop stage to meet the timings, which will cause a cycle of delay for bus initiators to receive their response. + +![145](./doc/ac_range_check.svg) + +## Comparison to IOPMP + +AC Range checks on the memory bus shares similarities with an I/O Peripheral Memory Protection (IOPMP) in that both mechanisms control access to memory regions, ensuring that only authorized transactions pass through. +Like an IOPMP, AC ranges define specific address spaces and enforce permissions, preventing unauthorized access. +However, AC range checks offer a simpler implementation and verification process. + +## Parameterizable Range Registers + +The AC range check IP shall support a configurable number of range registers, allowing a flexible and granular security configuration. +The number of range registers shall be parameterizable at design time for every instance of the IP to accommodate different use cases and system requirements, providing scalability based on system size and complexity. +A typical configuration would be between 16 and 32 ranges, but the design should not be limited to those bounds. + +## Range Configuration + +Each range register shall be configured by the following mechanism: + +* **Base/Limit Register** (Top-of-Range Matching): + * The range is defined using a base address and an upper limit address, where the base represents the starting address and the limit represents the end of the range. + +The limit register should be sufficiently large to cover the whole 32-bit address space. +Each range register shall also be associated with a set of permissions that determine the allowed access types within that range. +The permission options shall include: + +* **READ:** Allows read access within the range. +* **WRITE:** Allows write access within the range. +* **EXECUTE:** Allows to execute code from this range. + +If all, READ, WRITE, and EXECUTE are cleared any access to that range shall be denied. + +## Request Matching and Permissions + +Incoming access requests (e.g., memory reads or writes) shall be compared against all enabled range registers. +Each request shall be evaluated for a match based on its address and the configured ranges. +The matching process includes: + +* **Address Match:** The request's address is checked against the ranges defined by the enabled range registers (top-of-range matching). +* **Permission Check:** For each matching range, the permissions associated with that range (READ, WRITE) are checked against the type of access requested. +* **RACL Check:** For each matching range, the RACL policy associated with that range (read_perm, write_perm) is checked against the source role of the access requested. +RACL is specified in Integrated OpenTitan: Register Access Control (RACL). + +## Access Policy + +The following policy governs the handling of access requests based on the outcome of the range and permission matching: + +* **Allowed Access:** If an incoming request matches an enabled range and the corresponding permission allows the requested operation (READ or WRITE), the request is granted, and the transaction proceeds normally. +* **Denied Access:** If the request does not match any range or matches a range but the configured permission denied the requested operation, the TL-UL response shall return an error, i.e., setting `d_error = 1` when acknowledging the request. +Further, the request is denied as follows: + * **Write Requests** Denied write requests shall be dropped, meaning no write operation is performed. + * **Read/Execute Requests:** Denied read/execute requests shall return a zero value as a response. + +Range register 0 may be used as a configurable default policy to match the whole address space. +Subsequent range configuration may overwrite this policy to allow a different behavior. +See Priority of Matching for the priority based address matching. + + +## Priority of Matching + +If a request matches multiple ranges with conflicting permissions, the following priority shall be enforced: + +* If multiple ranges allow the operation, the first matching range (based on register configuration order) shall determine the access rights. + +## Error Logging and Interrupt Mechanism + +To ensure proper tracking and response to unauthorized access attempts, the system shall implement an error logging mechanism that monitors denied access requests across all configured ranges. + +### Global Denied Access Counter + +* A **global counter** shall be maintained to track the total number of denied access requests across all enabled range registers in the system. +* The counter increments by one each time a request is denied, regardless of whether the denial results from a READ or WRITE operation. +* The counter saturates once it reaches the programmed threshold. + +### Programmable Denied Access Threshold + +* The system shall support a programmable threshold that specifies the maximum number of denied access requests before triggering a further action. +* Once the number of denied accesses reaches or exceeds this threshold, the system shall raise an interrupt to the RoT. +* Acknowledging the interrupt automatically clears the denied access counter. + +To facilitate detailed diagnostics, the system shall implement a logging mechanism for the first denied access request. +This log captures key information to help identify the cause and context of the denial. + +### First Denied Access Log + +Upon the **first denied request**, the system shall record the following information: + +* **Range Index:** Identifies the specific range responsible for denying the request. If the access is denied because no range matches, it is logged in a dedicated status bit. +* **Access Type:** Indicates whether the request was a READ, WRITE, or EXECUTE operation. +* **RACL role:** Indicates the RACL role of th request and an indicator if a RACL read or write check caused the denial. +* **Access Address:** Logs the exact address that the request attempted to access. + +### Single Event Logging + +* This logging mechanism is triggered only for the first denied access after a reset or after the log has been cleared, ensuring that only the initial violation is captured for diagnostics. +* Subsequent denied requests will not overwrite the log, preserving the information of the first event until manually cleared by the RoT. +The log is cleared automatically when the interrupt is acknowledged or manually by writing a configuration bit. + +## Enable/Disable Control and Lock Mechanism + +Each range register shall include both an enable/disable control bit and a lock mechanism. +The following controls apply: + +* **Enable/Disable** Control: Each range register can be enabled or disabled individually. +Disabled range registers shall not participate in the matching process and shall have no effect on access control decisions. +* **Lock Mechanism:** Once a range register is configured, a lock can be asserted to prevent further modification of the range's configuration. +When the lock is active, the base, mask/limit, and permission settings of the range register are immutable and cannot be altered until the system is reset. + +## Bypass Mode + +The system shall support a bypass mode controlled via a bypass_wire signal. +The bypass_signal shall be driven by a 4-bit encoded multi-bit encoded signal in the ROT-controlled register space. +A single bypass_signal shall manage access override for all source AC range registers within the system. + +When the bypass_wire is asserted, the range register logic shall allow access for all transactions, overriding any existing configuration settings. +This includes ignoring permission restrictions such as READ, WRITE, or EXECUTE and RACL checks, ensuring all transactions are permitted while bypass mode is active. + +The bypass mode may be used during early silicon bring-up or debugging phases, where reducing the security due to disabled range filters is acceptable. diff --git a/hw/ip_templates/ac_range_check/doc/ac_range_check.svg b/hw/ip_templates/ac_range_check/doc/ac_range_check.svg new file mode 100644 index 0000000000000..ccf62f5aa4414 --- /dev/null +++ b/hw/ip_templates/ac_range_check/doc/ac_range_check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/hw/ip_templates/ac_range_check/doc/theory_of_operation.md b/hw/ip_templates/ac_range_check/doc/theory_of_operation.md index b1e31a5e53eac..08fd714a587ba 100644 --- a/hw/ip_templates/ac_range_check/doc/theory_of_operation.md +++ b/hw/ip_templates/ac_range_check/doc/theory_of_operation.md @@ -1 +1,37 @@ # Theory of Operation + +The following pseudo-code illustrates the range check logic, incorporating range priorities and access control rules. +The default system behavior is to deny access unless explicitly allowed by the range configuration. +The incoming address is compared against each enabled range register, and access control decisions are made based on matching and permissions. +The priority order (a lower range slot has a higher priority) of the ranges ensures that higher-priority ranges override lower-priority ones when a conflict occurs (i.e., if more than one range matches the incoming request). + +``` +def range_check_access(address, access_type): + access_granted = False # Default: access is denied + for i = 0 to (num_ranges - 1): # Iterate through ranges, starting from the highest priority + if range[i].enabled: # Only process enabled ranges + # Address matching based on base/limit + if (range[i].base >= address) and (address < range[i].limit): + range_match = True + else: + range_match = False + + # If address matches within this range, check permissions + if range_match: + if access_type == EXECUTE and range[i].execute and access_role in range[i].read_perm: + access_granted == True + else if access_type == READ and range[i].read and access_role in range[i].read_perm: + access_granted = True + else if access_type == WRITE and range[i].write and access_role in range[i].write_perm: + access_granted = True + else: + access_granted = False # No matching permissions + # Stop after the first match (highest-priority range matched) + break + + # Return the final access decision + if access_granted: + return ACCESS_GRANTED + else: + return ACCESS_DENIED +```