Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions submissions/Abhinavagarwa/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<h1>RISC-V PMP Checker - Abhinavagarwa<h1>

Author: Abhinavagarwa

Overview:

This script is designed to verify access permissions for physical addresses in RISC-V processors based on Physical Memory Protection (PMP) rules. It follows the guidelines outlined in Chapter 3.7 of the RISC-V Privileged Architecture specification. The program accepts a PMP configuration file, a physical address, a privilege mode, and an access type as input arguments and determines whether the access is permitted or results in a fault.


Execution Instructions:

This script does not require compilation and can be run directly using Python.

To execute the program, use the following command:

```bash
python pmp_checker.py <pmp_config_file> <physical_address> <privilege_mode> <operation>
```

Where:

- `<pmp_config_file>`: Path to the PMP configuration file. The file consists of 128 lines:
- The first 64 lines represent the hexadecimal values of pmpNcfg registers (N = 0 to 63).
- The last 64 lines represent the hexadecimal values of pmpaddrN registers (N = 0 to 63).
- `<physical_address>`: The target address for verification, provided in hexadecimal format (e.g., 0xdeadbeef).
- `<privilege_mode>`: The privilege level of the access attempt. Acceptable values: `M` (Machine), `S` (Supervisor), or `U` (User).
- `<operation>`: The type of memory access: `R` (Read), `W` (Write), or `X` (Execute).

Example Usage:
```bash
python pmp_checker.py pmp_configuration.txt 0x80000000 M R
```

---

Dependencies:

This script is implemented using standard Python libraries and does not require any additional dependencies.

---

Implementation Details:

1. Reading Configuration: The script loads the PMP settings from the configuration file, storing `pmpNcfg` and `pmpaddrN` values in separate lists.
2. Parsing Inputs: It processes command-line arguments, converting the physical address to an integer and validating privilege modes and access types.
3. PMP Region Matching: The script iterates through PMP entries to determine if the given address falls within a defined PMP region. It evaluates:
- `TOR (Top of Range)`: Defines a memory region between a previous address and the current PMP address.
- `NA4 (Naturally Aligned 4-byte)`: Protects a fixed 4-byte region.
- `NAPOT (Naturally Aligned Power of Two)`: Covers larger aligned memory blocks.
4. Access Permission Check: If the address falls within a matching PMP region, the script checks the access permissions based on `R/W/X` bits in `pmpNcfg`.
5. Output Decision: If the access is allowed, the script prints `Access allowed`; otherwise, it prints `Access fault`.

Testing Scenarios:

The script has been tested under multiple conditions, including:

- Access requests inside and outside PMP regions.
- Various combinations of `R`, `W`, and `X` permissions.
- Addresses located at PMP region boundaries.
- Handling of `NAPOT` regions with different configurations.
- Error handling for invalid inputs (e.g., incorrect file paths, malformed addresses, and unsupported modes).

This ensures robust functionality across different access conditions.

Conclusion:

The RISC-V PMP Checker provides a straightforward method to validate memory access permissions on RISC-V processors. It efficiently interprets PMP configurations and verifies access control based on defined security rules. This tool is particularly useful for developers working on RISC-V-based embedded systems, operating systems, and security-sensitive applications.

94 changes: 94 additions & 0 deletions submissions/Abhinavagarwa/pmp_checker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import sys

def main():
# Parse command-line arguments
if len(sys.argv) != 5:
print("Usage: python3 pmp_check.py pmp_configuration.txt 0xaddress M/S/U R/W/X")
sys.exit(1)

config_file = sys.argv[1]
address_str = sys.argv[2]
mode = sys.argv[3].upper()
operation = sys.argv[4].upper()

# Validate physical address
if not address_str.startswith("0x"):
print("Invalid address format. Must start with '0x'.")
sys.exit(1)
try:
address = int(address_str, 16)
except ValueError:
print("Invalid address format.")
sys.exit(1)

# Validate privilege mode and operation
if mode not in ['M', 'S', 'U']:
print("Invalid privilege mode. Must be one of 'M', 'S', or 'U'.")
sys.exit(1)
if operation not in ['R', 'W', 'X']:
print("Invalid operation. Must be one of 'R', 'W', or 'X'.")
sys.exit(1)

# Map privilege modes to levels
priv_level = {'M': 3, 'S': 1, 'U': 0}[mode]

try:
with open(config_file, 'r') as f:
lines = f.read().splitlines()
if len(lines) != 128:
print("Configuration file must have exactly 128 lines.")
sys.exit(1)
pmpcfg = [int(line, 16) for line in lines[:64]]
pmpaddr = [int(line, 16) for line in lines[64:]]
except Exception as e:
print(f"Error reading configuration file: {e}")
sys.exit(1)

# Define PMP region class
class PMPRegion:
def __init__(self, cfg, addr):
self.cfg = cfg
self.addr = addr
self.a = (cfg >> 3) & 0x3 # Address matching mode
self.r = (cfg >> 0) & 0x1 # Read permission
self.w = (cfg >> 1) & 0x1 # Write permission
self.x = (cfg >> 2) & 0x1 # Execute permission

def matches(self, addr, prev_addr):
if self.a == 0: # OFF (disabled region)
return False
elif self.a == 1: # TOR (Top of Range)
return prev_addr <= addr < self.addr
elif self.a == 2: # NA4 (Naturally Aligned 4-byte region)
return addr >= self.addr and addr < self.addr + 4
elif self.a == 3: # NAPOT (Naturally Aligned Power-of-Two region)
size = 1 << ((self.addr & 0xFFFFFFFC) + 2) # Calculate size from address bits
return (addr & ~(size - 1)) == (self.addr & ~(size - 1))
return False

def permits(self, op):
if op == 'R':
return self.r
if op == 'W':
return self.w
if op == 'X':
return self.x
return False

# Iterate through PMP entries to check access permissions
previous_addr = 0
for i in range(64):
region = PMPRegion(pmpcfg[i], pmpaddr[i])
if region.matches(address, previous_addr):
if region.permits(operation):
print("Access allowed")
return
else:
print("Access fault")
return
previous_addr = pmpaddr[i]

print("Access fault")

if __name__ == "__main__":
main()
128 changes: 128 additions & 0 deletions submissions/Abhinavagarwa/pmp_configuration.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x1F
0x0F
0x80000000
0x90000000
0xA0000000
0xB0000000
0xC0000000
0xD0000000
0xE0000000
0xF0000000
0x100000000
0x110000000
0x120000000
0x130000000
0x140000000
0x150000000
0x160000000
0x170000000
0x180000000
0x190000000
0x1A0000000
0x1B0000000
0x1C0000000
0x1D0000000
0x1E0000000
0x1F0000000
0x200000000
0x210000000
0x220000000
0x230000000
0x240000000
0x250000000
0x260000000
0x270000000
0x280000000
0x290000000
0x2A0000000
0x2B0000000
0x2C0000000
0x2D0000000
0x2E0000000
0x2F0000000
0x300000000
0x310000000
0x320000000
0x330000000
0x340000000
0x350000000
0x360000000
0x370000000
0x380000000
0x390000000
0x3A0000000
0x3B0000000
0x3C0000000
0x3D0000000
0x3E0000000
0x3F0000000
0x400000000
0x4A0000000
0x4B0000000
0x4C0000000
0x4D0000000
0x4E0000000
0x4F0000000
0x500000000