diff --git a/submissions/Abhinavagarwa/README.md b/submissions/Abhinavagarwa/README.md new file mode 100644 index 0000000..6394236 --- /dev/null +++ b/submissions/Abhinavagarwa/README.md @@ -0,0 +1,68 @@ +

RISC-V PMP Checker - Abhinavagarwa

+ +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 +``` + +Where: + +- ``: 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). +- ``: The target address for verification, provided in hexadecimal format (e.g., 0xdeadbeef). +- ``: The privilege level of the access attempt. Acceptable values: `M` (Machine), `S` (Supervisor), or `U` (User). +- ``: 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. + diff --git a/submissions/Abhinavagarwa/pmp_checker.py b/submissions/Abhinavagarwa/pmp_checker.py new file mode 100644 index 0000000..e0d46e5 --- /dev/null +++ b/submissions/Abhinavagarwa/pmp_checker.py @@ -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() diff --git a/submissions/Abhinavagarwa/pmp_configuration.txt b/submissions/Abhinavagarwa/pmp_configuration.txt new file mode 100644 index 0000000..fbba5f9 --- /dev/null +++ b/submissions/Abhinavagarwa/pmp_configuration.txt @@ -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 \ No newline at end of file