From e46c953c536a30e60fbc38c4870e3139bcac6911 Mon Sep 17 00:00:00 2001 From: Chaitanya Gambali Date: Mon, 10 Feb 2025 05:30:19 +0530 Subject: [PATCH 1/2] added final working script and files after multiple versions - last clean commit --- README.md | 42 +++++----------- pmp_check.py | 95 ++++++++++++++++++++++++++++++++++++ pmp_config.txt | 128 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 236 insertions(+), 29 deletions(-) create mode 100644 pmp_check.py create mode 100644 pmp_config.txt diff --git a/README.md b/README.md index 3b0e82f..3ce10d7 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,24 @@ -# Challenge +# PMP Checker -Create a program in a language of your choice that implements a Physical Memory Protection (PMP) check according to Chapter 3.7 of the RISC-V privleged manual (https://drive.google.com/file/d/17GeetSnT5wW3xNuAHI95-SI1gPGd5sJ_/view). -It must: +This is a simple Python script that simulates a RISC-V Physical Memory Protection (PMP) check. It reads a PMP configuration from a text file and then checks whether a given physical address would be allowed for a specified privilege mode and access type. -1. Accept four command line arguments: - 1. The path to a file containing a PMP configuration. - 2. A physical address in hexadecimal, with a leading '0x'. - 3. A privilege mode (one of 'M', 'S', or 'U'). - 4. An operation (one of 'R' (read), 'W' (write), or 'X' (execute/fetch). -2. For the given physical address and privilege mode, print whether or not the address would cause an access fault. +## Overview -The PMP configuration file will be a text file with 128 lines. The first 64 lines will contain the contents of pmpNcfg (for N = 0..63) as a hexadecimal integer. (NOTE: *not* pmpcfgN) -The last 64 lines will contain the contents of pmpaddrN (for N = 0..63). +- **PMP Configuration:** The config file contains 128 lines. The first 64 lines are configuration bytes - one per PMP entry that define permissions (read, write, execute), address matching mode (TOR, NA4, or NAPOT), and whether the entry is locked. The next 64 lines are the corresponding PMP address registers. +- **Access Check:** The script goes through each active PMP entry, computes the effective address range, and then determines if the given physical address falls into one of these regions. If it does, the appropriate permission is checked. +- **Privilege Modes:** Machine mode (`M`) typically bypasses PMP checks unless the entry is locked. Supervisor (`S`) and User (`U`) modes follow the PMP rules strictly. -Example invocation: +## Files -``` -program pmp_configuration.txt 0xdeadbeef M R -``` +- **pmp_check.py:** The main script that reads the configuration file, parses the command-line arguments, and prints whether the access is allowed or not. +- **pmp_config.txt:** A sample configuration file. In this sample, only the first PMP entry is active (using TOR mode with read enabled), while the other entries are disabled. -Example configuration file: -``` -0x0 -0x1 -(... 62 more lines) -0x8000000 -0x100000000 -(... 62 more lines) -``` +## Example Usage -# Submission + ```./pmp_check.py pmp_config.txt 0x3000 S R ``` -To submit your entry, create a Pull Request against this repository (https://github.com/dhower-qc/idl-programming-challenge.git) that: + Output : -- Contains your submission in a new directory under `submissions` named after your GitHub username. For example, `submissions/dhower-qc`. -- Contains instructions on how to build your program, if needed (i.e., if using a compiled language). + ``` Access Allowed ``` -You will need to create a fork of this repository. See https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork for more information. diff --git a/pmp_check.py b/pmp_check.py new file mode 100644 index 0000000..56e692a --- /dev/null +++ b/pmp_check.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +import sys + +PMP_ENTRIES = 64 + +def count_trailing_ones(n): + cnt = 0 + while n & 1: + cnt += 1 + n //= 2 + return cnt + +def load_pmp_config(fname): + with open(fname, 'r') as f: + lines = [line.strip() for line in f if line.strip()] + if len(lines) != 128: + sys.exit("Config file must have 128 lines") + configs = [] + for i in range(PMP_ENTRIES): + try: + val = int(lines[i], 16) + except: + sys.exit(f"Bad config on line {i+1}") + configs.append({ + 'read': bool(val & 0x01), + 'write': bool(val & 0x02), + 'exec': bool(val & 0x04), + 'mode': (val & 0x18) >> 3, + 'locked': bool(val & 0x80) + }) + addrs = [] + for i in range(PMP_ENTRIES, 128): + try: + addrs.append(int(lines[i], 16)) + except: + sys.exit(f"Bad addr on line {i+1}") + return configs, addrs + +def compute_range(idx, cfg, addrs): + m = cfg['mode'] + if m == 1: # TOR + lo = 0 if idx == 0 else addrs[idx-1] << 2 + hi = addrs[idx] << 2 + return lo, hi + elif m == 2: # NA4 + base = addrs[idx] << 2 + return base, base + 4 + elif m == 3: # NAPOT + raw = addrs[idx] + n = count_trailing_ones(raw) + if n < 1: n = 1 + size = 8 << (n - 1) + base = (raw & ~((1 << n) - 1)) << 2 + return base, base + size + else: + return None, None + +def check_access(configs, addrs, addr, priv, op): + any_enabled = False + for i in range(PMP_ENTRIES): + cfg = configs[i] + if cfg['mode'] == 0: + continue + any_enabled = True + lo, hi = compute_range(i, cfg, addrs) + if lo <= addr < hi: + if priv == 'M' and not cfg['locked']: + return True + if op == 'R' and cfg['read']: return True + if op == 'W' and cfg['write']: return True + if op == 'X' and cfg['exec']: return True + return False + return True if priv == 'M' else not any_enabled + +def main(): + if len(sys.argv) != 5: + sys.exit("Usage: pmp_check.py ") + config_file, addr_str, priv, op = sys.argv[1:5] + priv, op = priv.upper(), op.upper() + if not addr_str.startswith("0x"): + sys.exit("Address must start with 0x") + try: + addr = int(addr_str, 16) + except: + sys.exit("Invalid address") + if priv not in ('M', 'S', 'U'): + sys.exit("Invalid privilege") + if op not in ('R', 'W', 'X'): + sys.exit("Invalid op") + configs, addrs = load_pmp_config(config_file) + allowed = check_access(configs, addrs, addr, priv, op) + print("Access allowed" if allowed else "Access fault") + +if __name__ == '__main__': + main() diff --git a/pmp_config.txt b/pmp_config.txt new file mode 100644 index 0000000..9a76624 --- /dev/null +++ b/pmp_config.txt @@ -0,0 +1,128 @@ +0x09 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x00 +0x1000 +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 +0x0 +0x0 +0x0 +0x0 +0x0 \ No newline at end of file From 394f43b48a71d1264bb223773c512aced842948b Mon Sep 17 00:00:00 2001 From: Chaitanya Gambali Date: Mon, 10 Feb 2025 05:33:05 +0530 Subject: [PATCH 2/2] fixed structure --- README.md | 42 +++++++++++++------ submissions/gs-chaitanya/README_for_project | 24 +++++++++++ .../gs-chaitanya/pmp_check.py | 0 .../gs-chaitanya/pmp_config.txt | 0 4 files changed, 53 insertions(+), 13 deletions(-) create mode 100644 submissions/gs-chaitanya/README_for_project rename pmp_check.py => submissions/gs-chaitanya/pmp_check.py (100%) rename pmp_config.txt => submissions/gs-chaitanya/pmp_config.txt (100%) diff --git a/README.md b/README.md index 3ce10d7..3b0e82f 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,40 @@ -# PMP Checker +# Challenge -This is a simple Python script that simulates a RISC-V Physical Memory Protection (PMP) check. It reads a PMP configuration from a text file and then checks whether a given physical address would be allowed for a specified privilege mode and access type. +Create a program in a language of your choice that implements a Physical Memory Protection (PMP) check according to Chapter 3.7 of the RISC-V privleged manual (https://drive.google.com/file/d/17GeetSnT5wW3xNuAHI95-SI1gPGd5sJ_/view). +It must: -## Overview +1. Accept four command line arguments: + 1. The path to a file containing a PMP configuration. + 2. A physical address in hexadecimal, with a leading '0x'. + 3. A privilege mode (one of 'M', 'S', or 'U'). + 4. An operation (one of 'R' (read), 'W' (write), or 'X' (execute/fetch). +2. For the given physical address and privilege mode, print whether or not the address would cause an access fault. -- **PMP Configuration:** The config file contains 128 lines. The first 64 lines are configuration bytes - one per PMP entry that define permissions (read, write, execute), address matching mode (TOR, NA4, or NAPOT), and whether the entry is locked. The next 64 lines are the corresponding PMP address registers. -- **Access Check:** The script goes through each active PMP entry, computes the effective address range, and then determines if the given physical address falls into one of these regions. If it does, the appropriate permission is checked. -- **Privilege Modes:** Machine mode (`M`) typically bypasses PMP checks unless the entry is locked. Supervisor (`S`) and User (`U`) modes follow the PMP rules strictly. +The PMP configuration file will be a text file with 128 lines. The first 64 lines will contain the contents of pmpNcfg (for N = 0..63) as a hexadecimal integer. (NOTE: *not* pmpcfgN) +The last 64 lines will contain the contents of pmpaddrN (for N = 0..63). -## Files +Example invocation: -- **pmp_check.py:** The main script that reads the configuration file, parses the command-line arguments, and prints whether the access is allowed or not. -- **pmp_config.txt:** A sample configuration file. In this sample, only the first PMP entry is active (using TOR mode with read enabled), while the other entries are disabled. +``` +program pmp_configuration.txt 0xdeadbeef M R +``` +Example configuration file: -## Example Usage +``` +0x0 +0x1 +(... 62 more lines) +0x8000000 +0x100000000 +(... 62 more lines) +``` - ```./pmp_check.py pmp_config.txt 0x3000 S R ``` +# Submission - Output : +To submit your entry, create a Pull Request against this repository (https://github.com/dhower-qc/idl-programming-challenge.git) that: - ``` Access Allowed ``` +- Contains your submission in a new directory under `submissions` named after your GitHub username. For example, `submissions/dhower-qc`. +- Contains instructions on how to build your program, if needed (i.e., if using a compiled language). +You will need to create a fork of this repository. See https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork for more information. diff --git a/submissions/gs-chaitanya/README_for_project b/submissions/gs-chaitanya/README_for_project new file mode 100644 index 0000000..3ce10d7 --- /dev/null +++ b/submissions/gs-chaitanya/README_for_project @@ -0,0 +1,24 @@ +# PMP Checker + +This is a simple Python script that simulates a RISC-V Physical Memory Protection (PMP) check. It reads a PMP configuration from a text file and then checks whether a given physical address would be allowed for a specified privilege mode and access type. + +## Overview + +- **PMP Configuration:** The config file contains 128 lines. The first 64 lines are configuration bytes - one per PMP entry that define permissions (read, write, execute), address matching mode (TOR, NA4, or NAPOT), and whether the entry is locked. The next 64 lines are the corresponding PMP address registers. +- **Access Check:** The script goes through each active PMP entry, computes the effective address range, and then determines if the given physical address falls into one of these regions. If it does, the appropriate permission is checked. +- **Privilege Modes:** Machine mode (`M`) typically bypasses PMP checks unless the entry is locked. Supervisor (`S`) and User (`U`) modes follow the PMP rules strictly. + +## Files + +- **pmp_check.py:** The main script that reads the configuration file, parses the command-line arguments, and prints whether the access is allowed or not. +- **pmp_config.txt:** A sample configuration file. In this sample, only the first PMP entry is active (using TOR mode with read enabled), while the other entries are disabled. + + +## Example Usage + + ```./pmp_check.py pmp_config.txt 0x3000 S R ``` + + Output : + + ``` Access Allowed ``` + diff --git a/pmp_check.py b/submissions/gs-chaitanya/pmp_check.py similarity index 100% rename from pmp_check.py rename to submissions/gs-chaitanya/pmp_check.py diff --git a/pmp_config.txt b/submissions/gs-chaitanya/pmp_config.txt similarity index 100% rename from pmp_config.txt rename to submissions/gs-chaitanya/pmp_config.txt