diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..0433142 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,4 @@ + + +[target.aarch64-unknown-linux-gnu] +linker = "aarch64-linux-gnu-gcc" diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 397a378..88f6588 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -23,7 +23,7 @@ # -name: register_tool +name: Register_Tool on: push: @@ -33,7 +33,7 @@ env: CARGO_TERM_COLOR: always jobs: - build: + Build_and_Test: runs-on: ubuntu-latest container: rust:latest @@ -47,7 +47,7 @@ jobs: - name: Build run: cargo build --verbose - name: Run tests - run: cargo test && cd aep_rust_common && cargo test + run: cargo test && cd tools/ucompleter/aep_rust_common && cargo test - name: rustup aarch64 run: rustup target add aarch64-unknown-linux-gnu - name: gcc-arm64 @@ -61,11 +61,11 @@ jobs: - uses: actions/upload-artifact@v4 with: name: register_tool-arm64 - path: "target/aarch64-unknown-linux-gnu/debian/register_tool*.deb" + path: "target/aarch64-unknown-linux-gnu/debian/register-tool*.deb" overwrite: true - uses: actions/upload-artifact@v4 with: name: register_tool-x86 - path: target/debian/register_tool*.deb + path: target/debian/register-tool*.deb overwrite: true diff --git a/.gitmodules b/.gitmodules index 33a743f..f86ab7a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "tools/ucompleter"] path = tools/ucompleter - url = git@github.com:AndrewOfC/ucompleter + url = git@github.com:AndrewOfC/ucompleter.git diff --git a/Cargo.lock b/Cargo.lock index 3b7893e..fac271e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -184,6 +184,7 @@ version = "0.1.0" dependencies = [ "aep_rust_common", "clap", + "lazy_static", "libc", "memmap", "regex", diff --git a/Cargo.toml b/Cargo.toml index 0881d2a..25c1ecb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,3 +37,14 @@ yaml-rust = "0.4.5" libc = "0.2.174" regex = "1.11.1" aep_rust_common = { path = "tools/ucompleter/aep_rust_common" } +lazy_static = "1.5.0" + + +[package.metadata.deb] +copyright = "© copyright Andrew Ellis Page" +extended-description-file= "README.md" +section = "Utilities" +assets = [ "$auto", [ "examples/raspberrypi4b_source.yaml", "/usr/share/doc/register-tool/examples/", "0644" ], + ["examples/raspberrypi4b.yaml", "/usr/share/doc/register-tool/examples/", "0644" ], + ["docs/*", "/usr/share/doc/register-tool/docs/", "0644"]] + \ No newline at end of file diff --git a/README.md b/README.md index 77c74f1..81354ba 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,31 @@ + +[![Build, Test and Package](https://github.com/AndrewOfC/register_tool/actions/workflows/build_and_test.yml/badge.svg?branch=master)](https://github.com/AndrewOfC/register_tool/actions/workflows/build_and_test.yml) +![](https://www.rust-lang.org/logos/rust-logo-blk.svg) + # Overview This is a tool for examining and manipulating memory mapped registers in embedded devices via symbolic -names instead of raw addresses. +names instead of raw addresses. + The program reads a register configuration file in yaml format which contains register definitions. +# Demonstration + +[YouTube Video](https://youtu.be/6FWzfbQRQ3M) + +![sparkfun](./docs/sparkfun.jpg) + +Here on a [SparkFun PiWedge](https://www.sparkfun.com/sparkfun-pi-wedge.html) we've connected GPIO[27] pin of the RaspberyPi 4b's BCM2711 to an LED through a 330Ω resister. +On the command line we set the pins .function register to 1(output), then set the .set register to +1 to turn on the LED, then set the .clear register to 1 to turn it off. + +``` +register_tool GPIO.pins[27].function=1 # configures for output +register_tool GPIO.pins[27].set=1 # sets pin 27 hi +register_tool GPIO.pins[27].clear=1 # sets pin 27 lo +``` + + # Building ## aarch64-unknown-linux-gnu @@ -16,11 +38,25 @@ sudo apt-get install -y gcc-aarch64-linux-gnu # once git clone https://https://github.com/AndrewOfC/register_tool.git --recursive cd register_tool -cargo build - cargo build --target aarch64-unknown-linux-gnu ``` +The executable can be found in "target/aarch64-uknonwn-linux-gnu/debug/register_tool" + +# Installation + +From .deb package + +## RasberryPi4b + +### With .deb package +If installing from the .deb package, copy or symlink the file [/usr/share/doc/register-tool/examples/raspberrypi4b.yaml](file:///usr/share/doc/register-tool/examples/raspberrypi4b.yaml) to +$HOME/.config/register_tool/register_tool.yaml + +### With local build + +copy or symlink the file [examples/raspberrypi4.yaml](file://examples/raspberrypi4.yaml) + # Usage ```bash @@ -170,21 +206,11 @@ on the bash command line. It is not required, but it is recommended. # Example Files -| File | Contents | -|-----------------------------------------------------------------|------------------------------------------| +| File | Contents | +|----------------------------------------------------------|------------------------------------------| | [raspberrypi4b_source.yaml](examples/raspberrypi4b_source.yaml) | Register definitions for RaspberryPi 4b This file was constructed with data from: [bcm2711-peripherals.pdf](https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf)| -| [raspberrypi4b.yaml](examples/raspberrypi4b.yaml) | The raspberrypi4b_source.yaml file remapped for a 'per pin' perspective. | - -# Example +| [raspberrypi4b.yaml](examples/raspberrypi4b.yaml) | The raspberrypi4b_source.yaml file remapped for a 'per pin' perspective.| | -To toggle GPIO pin 27 on a RaspberryPi 4b using a [SparkFun PiWedge](https://www.sparkfun.com/sparkfun-pi-wedge.html) - -``` -register_tool GPIO.pins[27].function=1 # configures for output -register_tool GPIO.pins[27].set=1 # sets pin 27 hi -register_tool GPIO.pins[27].clear=1 # sets pin 27 lo -``` -![sparkfun](./docs/sparkfun.jpg) # Layout | Dir | Contents | diff --git a/docs/sparkfun.jpg b/docs/sparkfun.jpg index 83ef7cf..e2376d5 100644 Binary files a/docs/sparkfun.jpg and b/docs/sparkfun.jpg differ diff --git a/src/main.rs b/src/main.rs index 1904838..e9e8ab1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -96,7 +96,15 @@ fn main() { process::exit(3); } ; - let mut register_tool = RegisterTool::new(descender); + let mut register_tool = match RegisterTool::new(descender) { + Ok(rt) => rt, + Err(errs) => { + for e in errs { + eprintln!("{}", e); + } + process::exit(4); + } + } ; if *options.get_one::("dump").unwrap_or(&false) { @@ -109,10 +117,16 @@ fn main() { } } + /* + * gather up all the registers to read or set. + * If there are erroneous registers report them all and exit + */ match register_tool.gather_regs(®isters) { Ok(_) => {} Err(e) => { - println!("{}", e); + for e in e { + eprintln!("{}", e); + } process::exit(1); } } ; @@ -120,7 +134,13 @@ fn main() { if *options.get_one::("test").unwrap_or(&false) { register_tool.set_test_area() ; } else { - register_tool.set_base_address() ; + match register_tool.set_base_address() { + Ok(_) => {} + Err(e) => { + eprintln!("{}", e); + process::exit(1); + } + } } let results = register_tool.apply_registers(|v| { diff --git a/src/register_op.rs b/src/register_op.rs index d3ce216..9301be9 100644 --- a/src/register_op.rs +++ b/src/register_op.rs @@ -80,6 +80,19 @@ pub fn parse_bits(bitsstr: &str) -> Result<(u32, u32), String> { } impl RegisterOp { + + /// a noop register + pub fn noop() -> RegisterOp { + RegisterOp { + offset: 0, + set_mask: 0, + read_mask: 0, + shift: 0, + value: None, + shadow_offset: None, + access_type: RegisterAccess::Unspecified, + } + } pub fn new(descender: &dyn Descender, value: Option, path:&str) -> Result { let offset_r = descender.get_int_field_or_parent(path, "offset"); diff --git a/src/register_tool.rs b/src/register_tool.rs index 8755382..1a73bfa 100644 --- a/src/register_tool.rs +++ b/src/register_tool.rs @@ -31,6 +31,9 @@ pub struct RegisterTool { regs: Vec, addr: *mut u8, test_mode: bool, + device: String, + base: u64, + length: u64, } impl RegisterTool { @@ -69,7 +72,7 @@ impl RegisterTool { match self.descender.set_root(&*old_root) { Ok(_) => {}, Err(e) => { - println!("Error resetting root: {}", e); + eprintln!("Error resetting root: {}", e); fail = true; } } @@ -84,27 +87,73 @@ impl RegisterTool { } impl RegisterTool { - pub fn new(descender: Box>) -> Self { - Self {descender, regs: Vec::new(), addr: std::ptr::null_mut(), test_mode: false } + pub fn new(descender: Box>) -> Result> { + let mut errs: Vec = Vec::new(); + + /* + * Validate the integrity of the file + */ + let device = match descender.get_string_field_or_parent("","device") { + Ok(d) => d, + Err(e) => { + errs.push(format!("device not found: {}", e)); + "".to_string() + } + } ; + + let base = match descender.get_int_field_or_parent("","base") { + Ok(b) => b, + Err(e) => { + errs.push(format!("base not found: {}", e)); + 0 + } + } as u64 ; + + let length = match descender.get_int_field_or_parent("","length") { + Ok(l) => l, + Err(e) => { + errs.push(format!("length not found: {}", e)); + 0 + } + } as u64 ; + + /* + * return all collected errors + */ + if errs.len() > 0 { + return Err(errs); + } + let register_tool = Self {descender, regs: Vec::new(), addr: std::ptr::null_mut(), test_mode: false, device, base, length } ; + + Ok(register_tool) } - pub fn set_base_address(&mut self) { - let device = self.descender.get_string_field_or_parent("","device").expect("device not found") ; - let base = self.descender.get_int_field_or_parent("","base").expect("base not found") ; - let length = self.descender.get_int_field_or_parent("","length").expect("length not found") ; - self.addr = mmap_memory(device.as_str(), base as u64, length as u64).expect("mmap failed") ; + pub fn set_base_address(&mut self) -> Result<(), String> { + self.addr = match mmap_memory(self.device.as_str(), self.base, self.length) { + Ok(a) => a, + Err(e) => { + return Err(format!("Error mapping memory: {}", e)); + } + } ; + Ok(()) } pub fn set_test_area(&mut self) { - let length = self.descender.get_int_field_or_parent("", "length").expect("length not found"); - let mut memory = Vec::with_capacity(length as usize); - memory.resize(length as usize, 0u8); + let mut memory = Vec::with_capacity(self.length as usize); + memory.resize(self.length as usize, 0u8); self.addr = memory.as_mut_ptr(); std::mem::forget(memory); self.test_mode = true; } - pub fn gather_regs(&mut self, regsspecs: &Vec<&str>) -> Result<(), String> { + + /// + /// gather the registers to set or read. + /// + /// return Ok or the collected errors + /// + pub fn gather_regs(&mut self, regsspecs: &Vec<&str>) -> Result<(), Vec> { + let mut errs: Vec = Vec::new(); let old_root = match &self.descender.get_string_field_or_parent("completion-metadata", "root") { Ok(r) => self.descender.set_root(r).unwrap(), @@ -114,31 +163,43 @@ impl RegisterTool { for spec in regsspecs { let parts = spec.split("=").collect::>(); if parts.len() > 2 { - return Err(format!("Bad argument {}", spec)); + errs.push(format!("Bad argument {}", spec)); + continue ; } let is_set = parts.len() == 2 ; let value = if is_set { match parts[1].parse::() { Ok(v) => Some(v), - Err(_) => return Err(format!("Bad argument {}", spec)), + Err(_) => { + errs.push(format!("Bad argument {}", spec)) ; + None + } } } else { None }; let r = match RegisterOp::new(&*self.descender, value, parts[0]) { Ok(r) => r, - Err(e) => return Err(e), + Err(e) => { errs.push(e) ; + RegisterOp::noop() + } } ; self.regs.push(r) } + match self.descender.set_root(&*old_root) { Ok(_) => {}, Err(e) => { println!("Error resetting root: {}", e); - return Err(format!("Error resetting root: {}", e)); + errs.push(format!("Error resetting old root: {}", e)); } + } ; + + if errs.len() > 0 { + Err(errs) + } else { + Ok(()) } - Ok(()) } pub fn apply_registers(&self, f: F) -> Result>, String> @@ -159,8 +220,11 @@ impl RegisterTool { } Ok(results) } - } + +/// +/// Free any memory we might have allocated +/// impl Drop for RegisterTool { fn drop(&mut self) { if self.test_mode { diff --git a/src/unittests.rs b/src/unittests.rs index 342f8ba..1515dfc 100644 --- a/src/unittests.rs +++ b/src/unittests.rs @@ -20,16 +20,17 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -// +// + #[cfg(test)] pub mod rtool_tests { use std::io::Write; - use std::process; - use aep_rust_common::descender::Descender; + use aep_rust_common::descender::{Descender as OtherDescender, Descender}; use aep_rust_common::yaml_descender::YamlDescender; use crate::register_op::parse_bits; use crate::register_tool::RegisterTool; + #[test] fn test_bitmask() { let (mask, lo) = parse_bits("4:2").unwrap(); @@ -46,15 +47,15 @@ pub mod rtool_tests { } #[test] fn test_gather_and_apply_regsters() { - let regspecs = vec!["GPIO.words.function2=0", "GPIO.pins@27.function=1", "GPIO.words.function2", "GPIO.pins@27.function"] ; + let regspecs = vec!["GPIO.words.function2=0", "GPIO.pins[27].function=1", "GPIO.words.function2", "GPIO.pins[27].function"] ; let working_dir = env!("CARGO_MANIFEST_DIR"); let config_file = format!("{}/register_tool.yaml", working_dir); - let descender = Box::new(YamlDescender::new_from_file(&*config_file, false).unwrap()) as Box> ; - let mut register_tool = RegisterTool::new(descender); + let descender = Box::new(YamlDescender::new_from_file(&*config_file, true).unwrap()) as Box> ; + let mut register_tool = RegisterTool::new(descender).unwrap() ; - register_tool.gather_regs(®specs) ; + register_tool.gather_regs(®specs).expect("TODO: panic message"); register_tool.set_test_area() ; let replies = register_tool.apply_registers(|v| { @@ -68,5 +69,22 @@ pub mod rtool_tests { assert_eq!(replies[3].clone().unwrap(), 0x00000001); } - + #[test] + fn test_bad_config() { + let bogus_base = r"{}" ; + let descender = YamlDescender::new(bogus_base, true).unwrap() ; + let register_tool = RegisterTool::new(Box::new(descender)) ; + match register_tool { + Err(errs) => assert_eq!(errs.len(), 3), + Ok(_) => panic!("Should have failed to create a register tool") + } ; + } + + #[test] + fn test_correct_basic() { + let correct = r"{device: /dev/mem, base: 0x40000000, length: 0x1000}" ; + let descender = YamlDescender::new(correct, true).unwrap() ; + let register_tool = RegisterTool::new(Box::new(descender)) ; + assert!(register_tool.is_ok()) ; + } } \ No newline at end of file diff --git a/tools/ucompleter b/tools/ucompleter index deebbfd..d33b922 160000 --- a/tools/ucompleter +++ b/tools/ucompleter @@ -1 +1 @@ -Subproject commit deebbfd356a7aeb3a5fbf29bf65103b0c9b35b53 +Subproject commit d33b9227f907fe1630138d703c8e14ee7053ea0e