├── .cargo └── config ├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── build.rs ├── libllama ├── Cargo.toml ├── build.rs ├── src │ ├── clock.rs │ ├── cpu │ │ ├── arm.decoder │ │ ├── caches.rs │ │ ├── coproc │ │ │ ├── mod.rs │ │ │ └── sys_control.rs │ │ ├── cpu.rs │ │ ├── instructions_arm │ │ │ ├── branch.rs │ │ │ ├── coprocessor.rs │ │ │ ├── data_processing.rs │ │ │ ├── load_store.rs │ │ │ ├── load_store_multiple.rs │ │ │ ├── media.rs │ │ │ ├── misc.rs │ │ │ ├── mod.rs │ │ │ └── program_status.rs │ │ ├── instructions_thumb │ │ │ ├── branch.rs │ │ │ ├── data_processing.rs │ │ │ ├── load_store.rs │ │ │ ├── media.rs │ │ │ ├── misc.rs │ │ │ └── mod.rs │ │ ├── interpreter_arm.rs │ │ ├── interpreter_thumb.rs │ │ ├── irq.rs │ │ ├── mod.rs │ │ ├── regs.rs │ │ └── thumb.decoder │ ├── dbgcore.rs │ ├── fs.rs │ ├── gdbstub.rs │ ├── hwcore.rs │ ├── io │ │ ├── aes.rs │ │ ├── config.rs │ │ ├── ctrcard.rs │ │ ├── dmac.decoder │ │ ├── emmc │ │ │ ├── card.rs │ │ │ ├── cmds.rs │ │ │ ├── mod.rs │ │ │ └── mode_sd.rs │ │ ├── gpu.rs │ │ ├── hid.rs │ │ ├── i2c.rs │ │ ├── irq.rs │ │ ├── mod.rs │ │ ├── ndma.rs │ │ ├── otp.rs │ │ ├── priv11.rs │ │ ├── pxi.rs │ │ ├── regs.rs │ │ ├── rsa.rs │ │ ├── sha.rs │ │ ├── timer.rs │ │ └── xdma.rs │ ├── ldr │ │ ├── ctr9.rs │ │ ├── firm.rs │ │ └── mod.rs │ ├── lib.rs │ ├── mem.rs │ ├── msgs.rs │ └── utils │ │ ├── bcast.rs │ │ ├── bytes.rs │ │ ├── cache.rs │ │ ├── fifo.rs │ │ ├── mod.rs │ │ ├── num.rs │ │ └── strutils.rs └── tools │ └── decoder-gen │ ├── _parser.py │ ├── decoder-gen.py │ ├── emitter.py │ └── lexer.py └── llama-ui ├── commands.rs ├── main.rs ├── qml ├── CMakeLists.txt ├── CommandLine.qml ├── DbgConsole.qml ├── ScreenView.qml ├── SideButton.qml ├── icons │ ├── cfg.svg │ ├── close.svg │ ├── debug.svg │ ├── fullscreen.svg │ ├── play.svg │ └── reload.svg ├── interop.h ├── main.cpp ├── main.qml ├── qml.qrc ├── screens.cpp └── screens.hpp └── uilog.rs /.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | rustflags = [ 3 | "-C", "target-cpu=native", 4 | "-Clink-args=-Xlinker -rpath=$ORIGIN" 5 | ] 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/* 2 | libllama/target/* 3 | Cargo.lock 4 | .DS_Store 5 | .swp 6 | __pycache__ 7 | .vscode 8 | obj/* 9 | *.log 10 | *.tlog/* 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | rust: 4 | - stable 5 | - beta 6 | - nightly 7 | matrix: 8 | allow_failures: 9 | - rust: nightly 10 | 11 | cache: 12 | - cargo 13 | - directories: 14 | - capstone-3.0.4 15 | 16 | env: 17 | - QT_SELECT=5 18 | 19 | dist: trusty 20 | sudo: required 21 | 22 | install: 23 | - if [ ! -e capstone-3.0.4 ]; then 24 | wget https://github.com/aquynh/capstone/archive/3.0.4.tar.gz -O /tmp/capstone.tar.gz; 25 | tar -xvf /tmp/capstone.tar.gz; 26 | fi 27 | - pushd capstone-3.0.4 28 | - sudo ./make.sh install 29 | - popd 30 | - sudo apt-get install -y qtbase5-dev qtdeclarative5-dev qt5-qmake 31 | 32 | script: 33 | - cargo build --verbose 34 | - pushd libllama 35 | - cargo test --verbose 36 | - popd 37 | - cargo test --verbose 38 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llama" 3 | version = "0.1.0" 4 | authors = ["archshift "] 5 | build = "build.rs" 6 | 7 | [profile.release] 8 | lto = true 9 | debug = true 10 | 11 | [profile.dev] 12 | opt-level = 2 13 | overflow-checks = false 14 | 15 | [[bin]] 16 | name = "llama-ui" 17 | path = "llama-ui/main.rs" 18 | 19 | [dependencies] 20 | capstone = "0.5" 21 | lgl = { git = "https://github.com/archshift/lgl" } 22 | libc = "0.2" 23 | libllama = { path = "libllama" } 24 | log = "0.3" 25 | 26 | [build-dependencies] 27 | bindgen = "0.31" 28 | 29 | [features] 30 | trace_instructions = ["libllama/trace_instructions"] 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Gui Andrade 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## llama 2 | 3 | ### the Low Level ARM Machine Amulator (of course) 4 | 5 | --- 6 | 7 | ### What is llama? 8 | 9 | Llama is an experimental emulator for the Nintendo 3DS's ARM9 (with a very limited ARM11 implementation as well). 10 | 11 | Llama most certainly cannot run any 3DS games. Don't even try. 12 | 13 | ### What does it look like? 14 | 15 | See for yourself! 16 | 17 | ![Llama's GUI, running Hourglass9](https://i.imgur.com/dl5YOH1.png) 18 | 19 | > Source-level debugging in a GDB TUI? That's a lot of buzzwords! 20 | 21 | ### How do I use it? 22 | 23 | First, you have to build llama from source. See below. 24 | 25 | #### Loading applications 26 | 27 | Llama loads binaries from either a [FIRM](https://www.3dbrew.org/wiki/FIRM) file or a "ctr9" package. 28 | 29 | A ctr9 package is a directory named `[dirname].ctr9`, with the following structure: 30 | 31 | ``` 32 | foo.ctr9: 33 | |- desc.json 34 | |- ... 35 | ``` 36 | 37 | #### "desc.json" 38 | 39 | The `desc.json` file describes how llama will load your ARM9 binaries. `desc.json` files look like this: 40 | 41 | ``` 42 | { 43 | "entryPoint": "0x0801B01C", 44 | "entryPoint11": "0x1FFAD034" 45 | "binFiles": [ 46 | { "bin": "firm_0_1FF00000.bin", "vAddr": "0x1FF00000" }, 47 | { "bin": "firm_1_1FF80000.bin", "vAddr": "0x1FF80000" }, 48 | { "bin": "firm_2_08006800.bin", "vAddr": "0x08006800" } 49 | ], 50 | } 51 | ``` 52 | 53 | - `entryPoint`: Address at which llama will begin executing the ARM9 processor. 54 | - `entryPoint11`: Address at which llama will begin executing the ARM11 processor. 55 | - `binFiles`, `binFiles11`: Array of binaries found within the ctr9 package. 56 | - `bin`: The binary filename. 57 | - `vAddr`: Address where llama will copy the binary. 58 | 59 | #### Debugger 60 | 61 | Llama will not automatically begin running the ctr9 package upon opening. To run, press the play/pause button or use the `run` debugger command. 62 | 63 | Llama has a semi-useful built-in debugger controlled with textual commands. 64 | 65 | - `run`: Unpauses the loaded program. 66 | - `cpu `: Switches between actively debugged CPUs 67 | - `asm [address hex]`: Prints disassembly for the current instruction. 68 | - `brk
`: Adds a CPU breakpoint at the specified address. 69 | - `btn [button] [up/down]`: Toggles a button or prints full button state. 70 | - `irq `: Triggers an interrupt request of the specified type. 71 | - `keydmp`: Dump AES keys. 72 | - `mem [# bytes hex] [dumpfile.bin]`: Prints n bytes of memory from the specified address, optionally dumping to file. 73 | - `reg [register name]`: Prints specified register, or all registers if none specified. 74 | - `step`: Runs one CPU instruction. 75 | 76 | ### What can I use it with? 77 | 78 | My [crossbar9](https://github.com/archshift/crossbar9) repository can be used as a template for Rust programs that should run on both llama and the actual 3DS. 79 | 80 | ### How do I build it? 81 | 82 | Llama is written in Rust and C++, which means you need a compiler for both languages installed on your system. 83 | 84 | The GUI uses Qt5, which must be installed as well. Make sure you also have QtQuick/Qt-declarative. 85 | 86 | #### Miscellaneous dependencies: 87 | 88 | - Capstone disassembler 89 | 90 | #### Actually building 91 | 92 | Once all dependencies are installed, building should be as easy as running: 93 | 94 | ``` 95 | cargo build --release 96 | ``` 97 | 98 | ### License 99 | 100 | Llama is licensed under the BSD 3-clause license. 101 | 102 | Included icons were created by [material.io](material.io) and are licensed under Apache v2.0. 103 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | extern crate bindgen; 2 | 3 | use std::env; 4 | use std::io; 5 | use std::fs; 6 | use std::path; 7 | use std::process; 8 | 9 | fn exe_dir() -> path::PathBuf { 10 | let base_dir = env::current_dir().unwrap(); 11 | let host = env::var("HOST").unwrap(); 12 | let target = env::var("TARGET").unwrap(); 13 | let profile = env::var("PROFILE").unwrap(); 14 | let mut exe_dir = base_dir.join("target"); 15 | if host != target { 16 | exe_dir = exe_dir.join(target); 17 | } 18 | exe_dir = exe_dir.join(profile); 19 | exe_dir 20 | } 21 | 22 | fn lib_files(base_name: &str) -> Vec { 23 | if cfg!(target_os = "macos") { 24 | vec![ format!("lib{}.dylib", base_name) ] 25 | } 26 | else if cfg!(target_os = "linux") { 27 | vec![ format!("lib{}.so", base_name) ] 28 | } 29 | else if cfg!(target_os = "windows") { 30 | vec![ 31 | format!("{}.dll", base_name), 32 | format!("{}.lib", base_name) 33 | ] 34 | } 35 | else { 36 | unimplemented!() 37 | } 38 | } 39 | 40 | fn copy_lib_files(base_name: &str, dst_dir: &path::Path) -> io::Result<()> { 41 | let out_dir = path::PathBuf::from(env::var("OUT_DIR").unwrap()); 42 | let out_dir = if cfg!(target_os = "windows") { 43 | out_dir.clone().join(cmake_build_type()) 44 | } else { 45 | out_dir.clone() 46 | }; 47 | 48 | for lib_name in lib_files(base_name) { 49 | fs::copy(out_dir.join(&lib_name), dst_dir.join(lib_name))?; 50 | } 51 | 52 | Ok(()) 53 | } 54 | 55 | fn os_into(s: &path::Path) -> &str { 56 | s.as_os_str().to_str().unwrap() 57 | } 58 | 59 | fn make() -> &'static [&'static str] { 60 | if cfg!(target_os = "windows") { &["cmake", "--build", "."] } 61 | else { &["make"] } 62 | } 63 | 64 | fn cmake_generator() -> &'static str { 65 | if cfg!(target_os = "windows") { "-GVisual Studio 16 2019" } 66 | else { "-GUnix Makefiles" } 67 | } 68 | 69 | fn cmake_build_type() -> &'static str { 70 | match env::var("PROFILE").unwrap().as_ref() { 71 | "release" => "RelWithDebInfo", 72 | _ => "Debug", 73 | } 74 | } 75 | fn cmake_build_type_arg() -> String { 76 | "-DCMAKE_BUILD_TYPE=".to_owned() + cmake_build_type() 77 | } 78 | 79 | fn main() -> io::Result<()> { 80 | let base_dir = env::current_dir().unwrap(); 81 | let out_dir = path::PathBuf::from(env::var("OUT_DIR").unwrap()); 82 | let exe_dir = exe_dir(); 83 | 84 | let qml_dir = base_dir.join("llama-ui/qml"); 85 | 86 | let status = process::Command::new("cmake") 87 | .current_dir(&out_dir) 88 | .arg(&qml_dir) 89 | .arg(cmake_generator()) 90 | .arg(cmake_build_type_arg()) 91 | .spawn() 92 | .expect("failed to start cmake") 93 | .wait()?; 94 | 95 | assert!(status.success(), "failed to execute cmake"); 96 | 97 | let make = make(); 98 | let status = process::Command::new(make[0]) 99 | .current_dir(&out_dir) 100 | .args(&make[1..]) 101 | .spawn() 102 | .expect("failed to start make") 103 | .wait()?; 104 | 105 | assert!(status.success(), "failed to execute make"); 106 | 107 | copy_lib_files("llamagui", &exe_dir)?; 108 | 109 | println!("cargo:rustc-link-search=native={}", os_into(&exe_dir)); 110 | println!("cargo:rustc-link-lib=dylib={}", "llamagui"); 111 | 112 | for entry in fs::read_dir(qml_dir)? { 113 | println!("cargo:rerun-if-changed={}", os_into(&entry?.path())); 114 | } 115 | println!("cargo:rerun-if-changed=build.rs"); 116 | 117 | bindgen::Builder::default() 118 | .header("llama-ui/qml/interop.h") 119 | .generate() 120 | .expect("Unable to generate bindings") 121 | .write_to_file(out_dir.join("qml_interop.rs")) 122 | .expect("Couldn't write bindings!"); 123 | 124 | Ok(()) 125 | } 126 | -------------------------------------------------------------------------------- /libllama/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libllama" 3 | version = "0.1.0" 4 | authors = ["archshift "] 5 | 6 | [dependencies] 7 | bitutils = "2.0" 8 | derive-error = "0.0.4" 9 | indextree = "1.0" 10 | json = "0.11" 11 | log = "0.3" 12 | mio = "0.6" 13 | openssl = "0.10" 14 | parking_lot = "0.4" 15 | arraydeque = "0.4" 16 | 17 | [features] 18 | trace_instructions = [] 19 | -------------------------------------------------------------------------------- /libllama/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::process::Command; 3 | use std::fs::File; 4 | use std::path::Path; 5 | 6 | #[cfg(target_os = "windows")] 7 | const PYTHON: &'static str = "python"; 8 | 9 | #[cfg(not(target_os = "windows"))] 10 | const PYTHON: &'static str = "python3"; 11 | 12 | fn main() { 13 | let decoders = [ 14 | "src/cpu/arm.decoder", 15 | "src/cpu/thumb.decoder", 16 | "src/io/dmac.decoder" 17 | ]; 18 | 19 | for decoder in decoders.iter() { 20 | let out_dir = env::var("OUT_DIR").unwrap(); 21 | let filename = Path::new(decoder).file_name().unwrap(); 22 | let out = format!("{}/{}.rs", out_dir, filename.to_os_string().to_str().unwrap()); 23 | 24 | let decoder_stat = Command::new(PYTHON) 25 | .arg("tools/decoder-gen/decoder-gen.py") 26 | .arg(decoder) 27 | .stdout(File::create(&out).unwrap()) 28 | .output() 29 | .expect("failed to execute child"); 30 | 31 | if !decoder_stat.status.success() { 32 | use std::io::{stderr, Write}; 33 | 34 | eprintln!("ERROR: decoder generation failed on {}!", out); 35 | eprintln!("Script stderr:"); 36 | stderr().write_all(&decoder_stat.stderr).unwrap(); 37 | } 38 | 39 | println!("cargo:rerun-if-changed={}", decoder); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /libllama/src/clock.rs: -------------------------------------------------------------------------------- 1 | use cpu::irq::IrqSyncClient; 2 | use io::timer; 3 | 4 | #[derive(Clone)] 5 | pub struct SysClock { 6 | pub timer_states: timer::TimerStates, 7 | pub irq_tx: IrqSyncClient, 8 | } 9 | 10 | 11 | 12 | impl SysClock { 13 | pub fn increment(&mut self, by: u64) { 14 | timer::handle_clock_update(&self.timer_states, by, &mut self.irq_tx); 15 | } 16 | 17 | pub fn get(&self) -> u64 { 18 | self.timer_states.global_counter.get() 19 | } 20 | } 21 | 22 | pub fn make_channel(irq_tx: IrqSyncClient) -> SysClock { 23 | let timer_states = timer::TimerStates::new(); 24 | SysClock { 25 | timer_states: timer_states, 26 | irq_tx: irq_tx 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /libllama/src/cpu/arm.decoder: -------------------------------------------------------------------------------- 1 | decoder u32 ArmInstruction { 2 | category [ 0b1111:4; _:28 ] // Unconditional instructions 3 | { 4 | mod_blx = [ 0b1111101:7; h_bit:1; signed_imm_24:24 ] 5 | cps = [0b111100010000:12; imod:2; mmod:1; 0b00000000:8; a_bit:1; i_bit:1; f_bit:1; 0:1; mode:5] 6 | clrex = [0b111101010111:12; 0b111111110000:12; 0b00011111:8] 7 | rfe = [0b1111100:7; p_bit:1; u_bit:1; 0b0:1; w_bit:1; 0b1:1; rn:4; 0b0000101000000000:16] 8 | srs = [0b1111100:7; p_bit:1; u_bit:1; 0b1:1; w_bit:1; 0b0110100000101000:16; mode:5] 9 | undef = [ _:32 ] 10 | } 11 | 12 | category [ _:4; 0b000:3; _:20; 0b0:1; _:4 ] // Data processing immediate shift 13 | or [ _:4; 0b000:3; _:17; 0b0:1; _:2; 0b1:1; _:4 ] // Data processing register shift 14 | or [ _:4; 0b001:3; _:25 ] // Data processing immediate 15 | { 16 | and = [ cond:4; 0b00:2; i_bit:1; 0b0000:4; s_bit:1; rn:4; rd:4; shifter_operand:12 ] 17 | eor = [ cond:4; 0b00:2; i_bit:1; 0b0001:4; s_bit:1; rn:4; rd:4; shifter_operand:12 ] 18 | sub = [ cond:4; 0b00:2; i_bit:1; 0b0010:4; s_bit:1; rn:4; rd:4; shifter_operand:12 ] 19 | rsb = [ cond:4; 0b00:2; i_bit:1; 0b0011:4; s_bit:1; rn:4; rd:4; shifter_operand:12 ] 20 | add = [ cond:4; 0b00:2; i_bit:1; 0b0100:4; s_bit:1; rn:4; rd:4; shifter_operand:12 ] 21 | adc = [ cond:4; 0b00:2; i_bit:1; 0b0101:4; s_bit:1; rn:4; rd:4; shifter_operand:12 ] 22 | sbc = [ cond:4; 0b00:2; i_bit:1; 0b0110:4; s_bit:1; rn:4; rd:4; shifter_operand:12 ] 23 | rsc = [ cond:4; 0b00:2; i_bit:1; 0b0111:4; s_bit:1; rn:4; rd:4; shifter_operand:12 ] 24 | tst = [ cond:4; 0b00:2; i_bit:1; 0b1000:4; 0b1:1; rn:4; 0b0000:4; shifter_operand:12 ] 25 | teq = [ cond:4; 0b00:2; i_bit:1; 0b1001:4; 0b1:1; rn:4; 0b0000:4; shifter_operand:12 ] 26 | cmp = [ cond:4; 0b00:2; i_bit:1; 0b1010:4; 0b1:1; rn:4; 0b0000:4; shifter_operand:12 ] 27 | cmn = [ cond:4; 0b00:2; i_bit:1; 0b1011:4; 0b1:1; rn:4; 0b0000:4; shifter_operand:12 ] 28 | orr = [ cond:4; 0b00:2; i_bit:1; 0b1100:4; s_bit:1; rn:4; rd:4; shifter_operand:12 ] 29 | mov = [ cond:4; 0b00:2; i_bit:1; 0b1101:4; s_bit:1; 0b0000:4; rd:4; shifter_operand:12 ] 30 | bic = [ cond:4; 0b00:2; i_bit:1; 0b1110:4; s_bit:1; rn:4; rd:4; shifter_operand:12 ] 31 | mvn = [ cond:4; 0b00:2; i_bit:1; 0b1111:4; s_bit:1; 0b0000:4; rd:4; shifter_operand:12 ] 32 | } 33 | 34 | category [ _:4; 0b00010:5; _:2; 0b0:1; _:15; 0b0:1; _:4 ] // Misc instructions 1 35 | or [ _:4; 0b00010:5; _:2; 0b0:1; _:12; 0b0:1; _:2; 0b1:1; _:4 ] // Misc instructions 2 36 | { 37 | blx_2 = [ cond:4; 0b000100101111111111110011:24; rm:4 ] 38 | bx = [ cond:4; 0b000100101111111111110001:24; rm:4 ] 39 | clz = [ cond:4; 0b000101101111:12; rd:4; 0b1111:4; 0b0001:4; rm:4 ] 40 | mrs = [ cond:4; 0b00010:5; r_bit:1; 0b00:2; 0b1111:4; rd:4; 0b000000000000:12 ] 41 | msr_2 = [ cond:4; 0b00010:5; r_bit:1; 0b10:2; field_mask:4; 0b111100000000:12; rm:4 ] 42 | bkpt = [ 0b111000010010:12; immed_hi:11; 0b0111:4; immed_lo:4 ] 43 | } 44 | 45 | category [ _:4; 0b000:3; _:17; 0b1:1; _:2; 0b1:1; _:4 ] // Multiplies extra loads/stores 46 | { 47 | ldrd = [ cond:4; 0b000:3; p_bit:1; u_bit:1; i_bit:1; w_bit:1; 0b0:1; rn:4; rd:4; addr_mode_hi:4; 0b1101:4; addr_mode_lo:4 ] 48 | ldrex = [ cond:4; 0b00011001:8; rn:4; rd:4; 0b111110010000:12 ] 49 | ldrh = [ cond:4; 0b000:3; p_bit:1; u_bit:1; i_bit:1; w_bit:1; 0b1:1; rn:4; rd:4; addr_mode_hi:4; 0b1011:4; addr_mode_lo:4 ] 50 | ldrsb = [ cond:4; 0b000:3; p_bit:1; u_bit:1; i_bit:1; w_bit:1; 0b1:1; rn:4; rd:4; addr_mode_hi:4; 0b1101:4; addr_mode_lo:4 ] 51 | ldrsh = [ cond:4; 0b000:3; p_bit:1; u_bit:1; i_bit:1; w_bit:1; 0b1:1; rn:4; rd:4; addr_mode_hi:4; 0b1111:4; addr_mode_lo:4 ] 52 | mla = [ cond:4; 0b0000001:7; s_bit:1; rd:4; rn:4; rs:4; 0b1001:4; rm:4 ] 53 | mul = [ cond:4; 0b0000000:7; s_bit:1; rd:4; 0b0000:4; rs:4; 0b1001:4; rm:4 ] 54 | smlal = [ cond:4; 0b0000111:7; s_bit:1; rd_hi:4; rd_lo:4; rs:4; 0b1001:4; rm:4 ] 55 | smull = [ cond:4; 0b0000110:7; s_bit:1; rd_hi:4; rd_lo:4; rs:4; 0b1001:4; rm:4 ] 56 | strd = [ cond:4; 0b000:3; p_bit:1; u_bit:1; i_bit:1; w_bit:1; 0b0:1; rn:4; rd:4; addr_mode_hi:4; 0b1111:4; addr_mode_lo:4 ] 57 | strh = [ cond:4; 0b000:3; p_bit:1; u_bit:1; i_bit:1; w_bit:1; 0b0:1; rn:4; rd:4; addr_mode_hi:4; 0b1011:4; addr_mode_lo:4 ] 58 | swp = [ cond:4; 0b00010000:8; rn:4; rd:4; 0b0000:4; 0b1001:4; rm:4 ] 59 | swpb = [ cond:4; 0b00010100:8; rn:4; rd:4; 0b0000:4; 0b1001:4; rm:4 ] 60 | umlal = [ cond:4; 0b0000101:7; s_bit:1; rd_hi:4; rd_lo:4; rs:4; 0b1001:4; rm:4 ] 61 | umull = [ cond:4; 0b0000100:7; s_bit:1; rd_hi:4; rd_lo:4; rs:4; 0b1001:4; rm:4 ] 62 | } 63 | 64 | category [ _:4; 0b011:3; _:20; 0b1:1; _:4 ] // Media instructions 65 | { 66 | rev = [ cond:4; 0b011010111111:12; rd:4; 0b11110011:8; rn:4 ] 67 | uxtb = [ cond:4; 0b011011101111:12; rd:4; rot:2; 0b000111:6; rm:4 ] 68 | uxth = [ cond:4; 0b011011111111:12; rd:4; rot:2; 0b000111:6; rm:4 ] 69 | } 70 | 71 | category [ _:4; 0b010:3; _:25 ] // Load/store immediate offset 72 | or [ _:4; 0b011:3; _:20; 0b0:1; _:4 ] // Load/store register offset 73 | { 74 | ldr = [ cond:4; 0b01:2; i_bit:1; p_bit:1; u_bit:1; 0b0:1; w_bit:1; 0b1:1; rn:4; rd:4; addr_mode:12 ] 75 | ldrb = [ cond:4; 0b01:2; i_bit:1; p_bit:1; u_bit:1; 0b1:1; w_bit:1; 0b1:1; rn:4; rd:4; addr_mode:12 ] 76 | str = [ cond:4; 0b01:2; i_bit:1; p_bit:1; u_bit:1; 0b0:1; w_bit:1; 0b0:1; rn:4; rd:4; addr_mode:12 ] 77 | strb = [ cond:4; 0b01:2; i_bit:1; p_bit:1; u_bit:1; 0b1:1; w_bit:1; 0b0:1; rn:4; rd:4; addr_mode:12 ] 78 | } 79 | 80 | category [ _:4; 0b100:3; _:25 ] // Load/store multiple 81 | { 82 | ldm_1 = [ cond:4; 0b100:3; p_bit:1; u_bit:1; 0b0:1; w_bit:1; 0b1:1; rn:4; register_list:16 ] 83 | ldm_2 = [ cond:4; 0b100:3; p_bit:1; u_bit:1; 0b1:1; w_bit:1; 0b1:1; rn:4; 0b0:1; register_list:15 ] 84 | ldm_3 = [ cond:4; 0b100:3; p_bit:1; u_bit:1; 0b1:1; w_bit:1; 0b1:1; rn:4; 0b1:1; register_list:15 ] 85 | stm_1 = [ cond:4; 0b100:3; p_bit:1; u_bit:1; 0b0:1; w_bit:1; 0b0:1; rn:4; register_list:16 ] 86 | stm_2 = [ cond:4; 0b100:3; p_bit:1; u_bit:1; 0b100:3; rn:4; register_list:16 ] 87 | } 88 | 89 | category [ _:32 ] // Other 90 | { 91 | bbl = [ cond:4; 0b101:3; link_bit:1; signed_imm_24:24 ] 92 | mcr = [ cond:4; 0b1110:4; opcode_1:3; 0b0:1; crn:4; rd:4; cp_num:4; opcode_2:3; 0b1:1; crm:4 ] 93 | mrc = [ cond:4; 0b1110:4; opcode_1:3; 0b1:1; crn:4; rd:4; cp_num:4; opcode_2:3; 0b1:1; crm:4 ] 94 | msr_1 = [ cond:4; 0b00110:5; r_bit:1; 0b10:2; field_mask:4; 0b1111:4; shifter_operand:12 ] 95 | swi = [ cond:4; 0b1111:4; swi_index:24 ] 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /libllama/src/cpu/coproc/mod.rs: -------------------------------------------------------------------------------- 1 | mod sys_control; 2 | 3 | use cpu; 4 | pub use self::sys_control::*; 5 | 6 | pub type CpEffect = Box)>; 7 | 8 | pub trait Coprocessor { 9 | fn move_in(&mut self, cpreg1: usize, cpreg2: usize, op1: usize, op2: usize, val: u32) -> CpEffect; 10 | fn move_out(&mut self, cpreg1: usize, cpreg2: usize, op1: usize, op2: usize) -> u32; 11 | } 12 | -------------------------------------------------------------------------------- /libllama/src/cpu/cpu.rs: -------------------------------------------------------------------------------- 1 | use clock; 2 | use cpu; 3 | use cpu::InstrStatus; 4 | use cpu::caches; 5 | use cpu::coproc; 6 | use cpu::irq; 7 | use cpu::regs::{GpRegs, Psr}; 8 | use mem; 9 | 10 | use utils::cache::TinyCache; 11 | 12 | use std::collections::HashSet; 13 | 14 | use arraydeque::{ArrayDeque, Wrapping}; 15 | 16 | #[derive(Copy, Clone, Debug)] 17 | pub enum Mode { 18 | Usr = 0b10000, 19 | Fiq = 0b10001, 20 | Irq = 0b10010, 21 | Svc = 0b10011, 22 | Abt = 0b10111, 23 | Und = 0b11011, 24 | Sys = 0b11111 25 | } 26 | 27 | impl Mode { 28 | pub fn from_num(val: u32) -> Mode { 29 | match val { 30 | n if n == Mode::Usr as u32 => Mode::Usr, 31 | n if n == Mode::Fiq as u32 => Mode::Fiq, 32 | n if n == Mode::Irq as u32 => Mode::Irq, 33 | n if n == Mode::Svc as u32 => Mode::Svc, 34 | n if n == Mode::Abt as u32 => Mode::Abt, 35 | n if n == Mode::Und as u32 => Mode::Und, 36 | n if n == Mode::Sys as u32 => Mode::Sys, 37 | _ => unreachable!() 38 | } 39 | } 40 | } 41 | 42 | #[allow(non_camel_case_types)] #[derive(Debug)] pub struct v5; 43 | #[allow(non_camel_case_types)] #[derive(Debug)] pub struct v6; 44 | pub trait Version: 'static + ::std::fmt::Debug { 45 | #[inline(always)] 46 | fn is() -> bool { 47 | use std::any::TypeId; 48 | TypeId::of::() == TypeId::of::() 49 | } 50 | } 51 | impl Version for v5 {} 52 | impl Version for v6 {} 53 | 54 | pub struct Cpu { 55 | pub regs: GpRegs, 56 | pub cpsr: Psr::Bf, 57 | pub spsr_fiq: Psr::Bf, 58 | pub spsr_irq: Psr::Bf, 59 | pub spsr_svc: Psr::Bf, 60 | pub spsr_abt: Psr::Bf, 61 | pub spsr_und: Psr::Bf, 62 | 63 | coproc_syscnt: coproc::SysControl, 64 | pub mpu: caches::MemMgr, 65 | 66 | irq_line: irq::IrqLine, 67 | sys_clk: clock::SysClock, 68 | 69 | pub(crate) thumb_decode_cache: TinyCache, ()>, 70 | pub(crate) arm_decode_cache: TinyCache, ()>, 71 | 72 | pub last_instructions: ArrayDeque<[u32; 1024], Wrapping>, 73 | 74 | pub breakpoints: HashSet, // addr, is_triggered 75 | 76 | pub(crate) _version: V 77 | } 78 | 79 | #[derive(Copy, Clone)] 80 | pub enum BreakReason { 81 | LimitReached, 82 | Breakpoint, 83 | Trapped, 84 | WFI 85 | } 86 | 87 | const ASYNC_IRQ_CYCLE_MASK: u64 = 0xFFF; 88 | 89 | impl Cpu { 90 | pub fn new(version: V, memory: mem::MemController, irq_line: irq::IrqLine, clk: clock::SysClock) -> Cpu { 91 | Cpu { 92 | regs: GpRegs::new(Mode::Svc), 93 | cpsr: Psr::new(0), 94 | spsr_fiq: Psr::new(0), 95 | spsr_irq: Psr::new(0), 96 | spsr_svc: Psr::new(0), 97 | spsr_abt: Psr::new(0), 98 | spsr_und: Psr::new(0), 99 | 100 | coproc_syscnt: coproc::SysControl::new(), 101 | mpu: caches::MemMgr::new::(memory), 102 | 103 | irq_line: irq_line, 104 | sys_clk: clk, 105 | 106 | thumb_decode_cache: TinyCache::new( 107 | |_, k| cpu::thumb::decode(k as u16), |_, _, _| {} 108 | ), 109 | arm_decode_cache: TinyCache::new( 110 | |_, k| cpu::arm::decode(k), |_, _, _| {} 111 | ), 112 | 113 | last_instructions: ArrayDeque::new(), 114 | 115 | breakpoints: HashSet::new(), 116 | _version: version 117 | } 118 | } 119 | 120 | pub fn reset(&mut self, entry: u32) { 121 | self.regs.swap(Mode::Svc); 122 | self.cpsr.mode.set(Mode::Svc as u32); 123 | self.cpsr.thumb_bit.set(0b0); 124 | self.cpsr.disable_fiq_bit.set(0b1); 125 | self.cpsr.disable_irq_bit.set(0b1); 126 | 127 | self.regs[15] = entry + Self::pc_offset(0); 128 | } 129 | 130 | #[inline] 131 | fn instr_size(thumb_bit: u32) -> u32 { 132 | 4 >> thumb_bit 133 | } 134 | 135 | #[inline] 136 | fn pc_offset(thumb_bit: u32) -> u32 { 137 | Self::instr_size(thumb_bit) * 2 138 | } 139 | 140 | // For external interface 141 | pub fn get_pc_offset(&self) -> u32 { 142 | let thumb_bit = self.cpsr.thumb_bit.get(); 143 | Self::pc_offset(thumb_bit) 144 | } 145 | 146 | pub fn check_alignment(&self, thumb_bit: u32) { 147 | assert_eq!(self.regs[15] & (Self::instr_size(thumb_bit) - 1), 0); 148 | } 149 | 150 | pub fn get_coprocessor(&mut self, cp_index: usize) -> &mut dyn coproc::Coprocessor { 151 | match cp_index { 152 | 15 => &mut self.coproc_syscnt, 153 | _ => panic!("Tried to access unknown CP{}", cp_index), 154 | } 155 | } 156 | 157 | pub fn get_current_spsr(&mut self) -> &mut Psr::Bf { 158 | match Mode::from_num(self.cpsr.mode.get()) { 159 | Mode::Fiq => &mut self.spsr_fiq, 160 | Mode::Irq => &mut self.spsr_irq, 161 | Mode::Svc => &mut self.spsr_svc, 162 | Mode::Abt => &mut self.spsr_abt, 163 | Mode::Und => &mut self.spsr_und, 164 | _ => panic!("Attempted to access non-existent SPSR!"), 165 | } 166 | } 167 | 168 | pub fn spsr_make_current(&mut self) { 169 | self.cpsr = self.get_current_spsr().clone(); 170 | self.regs.swap(cpu::Mode::from_num(self.cpsr.mode.get())); 171 | } 172 | 173 | #[inline(always)] 174 | pub fn branch(&mut self, addr: u32) { 175 | let thumb_bit = self.cpsr.thumb_bit.get(); 176 | self.regs[15] = addr + Self::pc_offset(thumb_bit); 177 | self.check_alignment(thumb_bit); 178 | } 179 | 180 | pub fn run(&mut self, num_instrs: u32) -> BreakReason { 181 | let mut irq_known_pending = false; 182 | let mut thumb_bit = self.cpsr.thumb_bit.get(); 183 | self.check_alignment(thumb_bit); 184 | 185 | for _ in 0..num_instrs { 186 | let addr = self.regs[15] - Self::pc_offset(thumb_bit); 187 | 188 | self.sys_clk.increment(8); // Probably speeds up time but w/e 189 | 190 | irq_known_pending |= self.irq_line.is_high_sync(); 191 | if self.sys_clk.get() & ASYNC_IRQ_CYCLE_MASK != 0 { 192 | // Amortize the cost of checking for async IRQs 193 | irq_known_pending |= self.irq_line.is_high_async(); 194 | } 195 | 196 | if irq_known_pending && self.cpsr.disable_irq_bit.get() == 0 && self.irq_line.is_high() { 197 | trace!("Entering exception for ARM{:?}!", self._version); 198 | self.enter_exception(addr+4, Mode::Irq); 199 | thumb_bit = 0; 200 | irq_known_pending = false; 201 | continue 202 | } 203 | 204 | if self.find_toggle_breakpoint(addr) { 205 | return BreakReason::Breakpoint; 206 | } 207 | 208 | self.last_instructions.push_back(addr); 209 | 210 | let status = if thumb_bit == 0 { 211 | cpu::arm::interpret_next(self, addr) 212 | } else { 213 | cpu::thumb::interpret_next(self, addr) 214 | }; 215 | match status { 216 | InstrStatus::InBlock => self.regs[15] += Self::instr_size(thumb_bit), 217 | InstrStatus::Branched => thumb_bit = self.cpsr.thumb_bit.get(), 218 | } 219 | } 220 | 221 | BreakReason::LimitReached 222 | } 223 | 224 | pub fn enter_exception(&mut self, return_loc: u32, mode: Mode) { 225 | let r14_exc = return_loc; 226 | let spsr_exc = self.cpsr; 227 | 228 | self.regs.swap(mode); 229 | self.cpsr.mode.set(mode as u32); 230 | 231 | self.regs[14] = r14_exc; 232 | *self.get_current_spsr() = spsr_exc; 233 | self.cpsr.thumb_bit.set(0); 234 | self.cpsr.disable_irq_bit.set(1); 235 | 236 | let vector_addr = if V::is::() { 237 | // These vectors look like 0x080000XX because that's where the bootrom redirects them 238 | match mode { 239 | Mode::Irq => 0x08000000, 240 | Mode::Fiq => unimplemented!(), 241 | Mode::Svc => 0x08000010, 242 | Mode::Und => 0x08000018, 243 | Mode::Abt => 0x08000028, 244 | Mode::Sys | Mode::Usr => panic!("No exception associated with {:?}", mode) 245 | } 246 | } else { 247 | // These vectors look like 0x1FFFFFXX because that's where the bootrom redirects them 248 | match mode { 249 | Mode::Irq => 0x1FFFFFA0, 250 | Mode::Fiq => 0x1FFFFFA8, 251 | Mode::Svc => 0x1FFFFFB0, 252 | Mode::Und => 0x1FFFFFB8, 253 | Mode::Abt => 0x1FFFFFC8, 254 | Mode::Sys | Mode::Usr => panic!("No exception associated with {:?}", mode) 255 | } 256 | }; 257 | self.branch(vector_addr); 258 | } 259 | 260 | pub fn find_toggle_breakpoint(&mut self, addr: u32) -> bool { 261 | !self.breakpoints.is_empty() && self.breakpoints.remove(&addr) 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /libllama/src/cpu/instructions_arm/branch.rs: -------------------------------------------------------------------------------- 1 | use cpu::{self, Cpu, Version}; 2 | use cpu::interpreter_arm as arm; 3 | use bitutils::sign_extend32; 4 | 5 | fn instr_branch_exchange(cpu: &mut Cpu, data: arm::Bx::Bf, link: bool) -> cpu::InstrStatus { 6 | if !cpu::cond_passed(data.cond.get(), &cpu.cpsr) { 7 | return cpu::InstrStatus::InBlock; 8 | } 9 | 10 | let addr = cpu.regs[data.rm.get() as usize]; 11 | 12 | if link { 13 | cpu.regs[14] = cpu.regs[15] - 4; 14 | } 15 | 16 | cpu.cpsr.thumb_bit.set(bit!(addr, 0)); 17 | cpu.branch(addr & 0xFFFFFFFE); 18 | 19 | cpu::InstrStatus::Branched 20 | } 21 | 22 | pub fn bbl(cpu: &mut Cpu, data: arm::Bbl::Bf) -> cpu::InstrStatus { 23 | if !cpu::cond_passed(data.cond.get(), &cpu.cpsr) { 24 | return cpu::InstrStatus::InBlock; 25 | } 26 | 27 | let signed_imm_24 = data.signed_imm_24.get(); 28 | 29 | if data.link_bit.get() == 1 { 30 | cpu.regs[14] = cpu.regs[15] - 4; 31 | } 32 | 33 | let pc = cpu.regs[15]; 34 | cpu.branch(((pc as i32) + (sign_extend32(signed_imm_24, 24) << 2)) as u32); 35 | 36 | cpu::InstrStatus::Branched 37 | } 38 | 39 | pub fn blx(cpu: &mut Cpu, data: arm::Blx2::Bf) -> cpu::InstrStatus { 40 | instr_branch_exchange(cpu, arm::Bx::new(data.val), true) 41 | } 42 | 43 | pub fn bx(cpu: &mut Cpu, data: arm::Bx::Bf) -> cpu::InstrStatus { 44 | instr_branch_exchange(cpu, data, false) 45 | } 46 | 47 | pub fn mod_blx(cpu: &mut Cpu, data: arm::ModBlx::Bf) -> cpu::InstrStatus { 48 | let signed_imm_24 = data.signed_imm_24.get(); 49 | let h_bit = data.h_bit.get(); 50 | 51 | cpu.regs[14] = cpu.regs[15] - 4; 52 | cpu.cpsr.thumb_bit.set(1); 53 | 54 | let pc = cpu.regs[15]; 55 | cpu.branch((pc as i32 + (sign_extend32(signed_imm_24, 24) << 2)) as u32 + (h_bit << 1)); 56 | 57 | cpu::InstrStatus::Branched 58 | } 59 | -------------------------------------------------------------------------------- /libllama/src/cpu/instructions_arm/coprocessor.rs: -------------------------------------------------------------------------------- 1 | use cpu::{self, Cpu, Version}; 2 | use cpu::interpreter_arm as arm; 3 | 4 | pub fn mcr(cpu: &mut Cpu, data: arm::Mcr::Bf) -> cpu::InstrStatus { 5 | if !cpu::cond_passed(data.cond.get(), &cpu.cpsr) { 6 | return cpu::InstrStatus::InBlock; 7 | } 8 | 9 | let src_val = cpu.regs[data.rd.get() as usize]; 10 | let crn = data.crn.get() as usize; 11 | let crm = data.crm.get() as usize; 12 | let opcode_1 = data.opcode_1.get() as usize; 13 | let opcode_2 = data.opcode_2.get() as usize; 14 | 15 | let cp_effect = { 16 | let coproc = cpu.get_coprocessor(data.cp_num.get() as usize); 17 | coproc.move_in(crn, crm, opcode_1, opcode_2, src_val) 18 | }; 19 | cp_effect(cpu); 20 | 21 | cpu::InstrStatus::InBlock 22 | } 23 | 24 | pub fn mrc(cpu: &mut Cpu, data: arm::Mrc::Bf) -> cpu::InstrStatus { 25 | if !cpu::cond_passed(data.cond.get(), &cpu.cpsr) { 26 | return cpu::InstrStatus::InBlock; 27 | } 28 | 29 | let crn = data.crn.get() as usize; 30 | let crm = data.crm.get() as usize; 31 | let opcode_1 = data.opcode_1.get() as usize; 32 | let opcode_2 = data.opcode_2.get() as usize; 33 | let rd = data.rd.get(); 34 | 35 | let retval = { 36 | let coproc = cpu.get_coprocessor(data.cp_num.get() as usize); 37 | coproc.move_out(crn, crm, opcode_1, opcode_2) 38 | }; 39 | 40 | if rd == 15 { 41 | cpu.cpsr.n_bit.set(bit!(retval, 31)); 42 | cpu.cpsr.z_bit.set(bit!(retval, 30)); 43 | cpu.cpsr.c_bit.set(bit!(retval, 29)); 44 | cpu.cpsr.v_bit.set(bit!(retval, 28)); 45 | } else { 46 | cpu.regs[rd as usize] = retval; 47 | } 48 | 49 | cpu::InstrStatus::InBlock 50 | } 51 | -------------------------------------------------------------------------------- /libllama/src/cpu/instructions_arm/load_store_multiple.rs: -------------------------------------------------------------------------------- 1 | use cpu; 2 | use cpu::{Cpu, Version}; 3 | use cpu::caches::Ops; 4 | use cpu::interpreter_arm as arm; 5 | 6 | fn addressing_mode_inner(p_bit: bool, u_bit: bool, w_bit: bool, rn_val: u32, num_registers: u32) -> (u32, u32) { 7 | let (addr, wb) = match (p_bit, u_bit) { 8 | (false, true) => (rn_val, rn_val + num_registers * 4), // Increment after 9 | (true, true) => (rn_val + 4, rn_val + num_registers * 4), // Increment before 10 | (false, false) => (rn_val - num_registers * 4 + 4, rn_val - num_registers * 4), // Decrement after 11 | (true, false) => (rn_val - num_registers * 4, rn_val - num_registers * 4) // Decrement before 12 | }; 13 | 14 | if !w_bit { 15 | (addr, addr) 16 | } else { 17 | (addr, wb) 18 | } 19 | } 20 | 21 | fn decode_addressing_mode(instr_data: u32, cpu: &mut Cpu) -> (u32, u32) { 22 | let instr_data = arm::Ldm1::new(instr_data); 23 | 24 | let register_list = instr_data.register_list.get(); 25 | let num_registers = register_list.count_ones(); 26 | 27 | let p_bit = instr_data.p_bit.get() == 1; 28 | let u_bit = instr_data.u_bit.get() == 1; 29 | let w_bit = instr_data.w_bit.get() == 1; 30 | let rn_val = cpu.regs[instr_data.rn.get() as usize]; 31 | 32 | addressing_mode_inner(p_bit, u_bit, w_bit, rn_val, num_registers) 33 | } 34 | 35 | pub fn ldm_1(cpu: &mut Cpu, data: arm::Ldm1::Bf) -> cpu::InstrStatus { 36 | if !cpu::cond_passed(data.cond.get(), &cpu.cpsr) { 37 | return cpu::InstrStatus::InBlock; 38 | } 39 | 40 | let (mut addr, writeback) = decode_addressing_mode(data.val, cpu); 41 | let register_list = data.register_list.get(); 42 | 43 | // TODO: determine behavior based on CP15 r1 bit_U (22) 44 | assert!( V::is::() || addr % 4 == 0 ); 45 | 46 | if data.w_bit.get() == 1 { 47 | // TODO: needs more testing 48 | assert!(register_list & (1 << data.rn.get()) == 0); 49 | cpu.regs[data.rn.get() as usize] = writeback; 50 | } 51 | 52 | for i in 0..15 { 53 | if bit!(register_list, i) == 1 { 54 | cpu.regs[i] = cpu.mpu.dmem_read::(addr); 55 | addr += 4; 56 | } 57 | } 58 | 59 | if bit!(register_list, 15) == 1 { 60 | let val = cpu.mpu.dmem_read::(addr); 61 | cpu.cpsr.thumb_bit.set(bit!(val, 0)); 62 | cpu.branch(val & 0xFFFFFFFE); 63 | return cpu::InstrStatus::Branched; 64 | } else { 65 | return cpu::InstrStatus::InBlock; 66 | } 67 | } 68 | 69 | pub fn ldm_2(cpu: &mut Cpu, data: arm::Ldm2::Bf) -> cpu::InstrStatus { 70 | if !cpu::cond_passed(data.cond.get(), &cpu.cpsr) { 71 | return cpu::InstrStatus::InBlock; 72 | } 73 | 74 | let (mut addr, _) = decode_addressing_mode(data.val, cpu); 75 | let register_list = data.register_list.get(); 76 | 77 | // TODO: determine behavior based on CP15 r1 bit_U (22) 78 | assert!( V::is::() || addr % 4 == 0 ); 79 | 80 | let current_mode = cpu::Mode::from_num(cpu.cpsr.mode.get()); 81 | cpu.regs.swap(cpu::Mode::Usr); 82 | for i in 0..15 { 83 | if bit!(register_list, i) == 1 { 84 | cpu.regs[i] = cpu.mpu.dmem_read::(addr); 85 | addr += 4; 86 | } 87 | } 88 | cpu.regs.swap(current_mode); 89 | 90 | return cpu::InstrStatus::InBlock; 91 | } 92 | 93 | pub fn ldm_3(cpu: &mut Cpu, data: arm::Ldm3::Bf) -> cpu::InstrStatus { 94 | if !cpu::cond_passed(data.cond.get(), &cpu.cpsr) { 95 | return cpu::InstrStatus::InBlock; 96 | } 97 | 98 | let (mut addr, writeback) = decode_addressing_mode(data.val, cpu); 99 | let register_list = data.register_list.get(); 100 | 101 | // TODO: determine behavior based on CP15 r1 bit_U (22) 102 | assert!( V::is::() || addr % 4 == 0 ); 103 | 104 | if data.w_bit.get() == 1 { 105 | // TODO: needs more testing 106 | assert!(register_list & (1 << data.rn.get()) == 0); 107 | cpu.regs[data.rn.get() as usize] = writeback; 108 | } 109 | 110 | for i in 0..15 { 111 | if bit!(register_list, i) == 1 { 112 | cpu.regs[i] = cpu.mpu.dmem_read::(addr); 113 | addr += 4; 114 | } 115 | } 116 | 117 | cpu.spsr_make_current(); 118 | let dest = cpu.mpu.dmem_read::(addr); 119 | cpu.branch(dest & 0xFFFFFFFE); 120 | cpu::InstrStatus::Branched 121 | } 122 | 123 | pub fn rfe(cpu: &mut Cpu, data: arm::Rfe::Bf) -> cpu::InstrStatus { 124 | assert!( V::is::() ); 125 | 126 | let p_bit = data.p_bit.get() == 1; 127 | let u_bit = data.u_bit.get() == 1; 128 | let w_bit = data.w_bit.get() == 1; 129 | let rn_val = cpu.regs[data.rn.get() as usize]; 130 | let (addr, writeback) = addressing_mode_inner(p_bit, u_bit, w_bit, rn_val, 2); 131 | 132 | // TODO: determine behavior based on CP15 r1 bit_U (22) 133 | assert!( addr % 4 == 0 ); 134 | 135 | let new_pc = cpu.mpu.dmem_read::(addr); 136 | let new_cpsr = cpu.mpu.dmem_read::(addr+4); 137 | 138 | if data.w_bit.get() == 1 { 139 | cpu.regs[data.rn.get() as usize] = writeback; 140 | } 141 | 142 | cpu.cpsr.val = new_cpsr; 143 | cpu.branch(new_pc); 144 | cpu::InstrStatus::Branched 145 | } 146 | 147 | pub fn srs(cpu: &mut Cpu, data: arm::Srs::Bf) -> cpu::InstrStatus { 148 | assert!( V::is::() ); 149 | 150 | let src_lr = cpu.regs[14]; 151 | let src_spsr = cpu.get_current_spsr().val; 152 | 153 | let current_mode = cpu::Mode::from_num(cpu.cpsr.mode.get()); 154 | let dst_mode = cpu::Mode::from_num(data.mode.get()); 155 | cpu.regs.swap(dst_mode); 156 | 157 | let p_bit = data.p_bit.get() == 1; 158 | let u_bit = data.u_bit.get() == 1; 159 | let w_bit = data.w_bit.get() == 1; 160 | let rn_val = cpu.regs[13]; 161 | let (addr, writeback) = addressing_mode_inner(p_bit, u_bit, w_bit, rn_val, 2); 162 | 163 | // TODO: determine behavior based on CP15 r1 bit_U (22) 164 | assert!( addr % 4 == 0 ); 165 | 166 | cpu.mpu.dmem_write::(addr, src_lr); 167 | cpu.mpu.dmem_write::(addr+4, src_spsr); 168 | 169 | if data.w_bit.get() == 1 { 170 | cpu.regs[13] = writeback; 171 | } 172 | 173 | cpu.regs.swap(current_mode); 174 | cpu::InstrStatus::InBlock 175 | } 176 | 177 | pub fn stm_1(cpu: &mut Cpu, data: arm::Stm1::Bf) -> cpu::InstrStatus { 178 | if !cpu::cond_passed(data.cond.get(), &cpu.cpsr) { 179 | return cpu::InstrStatus::InBlock; 180 | } 181 | 182 | let (mut addr, writeback) = decode_addressing_mode(data.val, cpu); 183 | 184 | // TODO: determine behavior based on CP15 r1 bit_U (22) 185 | assert!( V::is::() || addr % 4 == 0 ); 186 | 187 | let register_list = data.register_list.get(); 188 | 189 | for i in 0..16 { 190 | if bit!(register_list, i) == 1 { 191 | cpu.mpu.dmem_write::(addr, cpu.regs[i]); 192 | addr += 4; 193 | } 194 | } 195 | 196 | if data.w_bit.get() == 1 { 197 | cpu.regs[data.rn.get() as usize] = writeback; 198 | } 199 | 200 | cpu::InstrStatus::InBlock 201 | } 202 | 203 | pub fn stm_2(cpu: &mut Cpu, data: arm::Stm2::Bf) -> cpu::InstrStatus { 204 | if !cpu::cond_passed(data.cond.get(), &cpu.cpsr) { 205 | return cpu::InstrStatus::InBlock; 206 | } 207 | 208 | let (mut addr, _) = decode_addressing_mode(data.val, cpu); 209 | 210 | // TODO: determine behavior based on CP15 r1 bit_U (22) 211 | assert!( V::is::() || addr % 4 == 0 ); 212 | 213 | let register_list = data.register_list.get(); 214 | 215 | let current_mode = cpu::Mode::from_num(cpu.cpsr.mode.get()); 216 | cpu.regs.swap(cpu::Mode::Usr); 217 | for i in 0..16 { 218 | if bit!(register_list, i) == 1 { 219 | cpu.mpu.dmem_write::(addr, cpu.regs[i]); 220 | addr += 4; 221 | } 222 | } 223 | cpu.regs.swap(current_mode); 224 | 225 | return cpu::InstrStatus::InBlock; 226 | } 227 | -------------------------------------------------------------------------------- /libllama/src/cpu/instructions_arm/media.rs: -------------------------------------------------------------------------------- 1 | use cpu::{self, Cpu, Version}; 2 | use cpu::interpreter_arm as arm; 3 | 4 | pub fn rev(cpu: &mut Cpu, data: arm::Rev::Bf) -> cpu::InstrStatus { 5 | assert!(V::is::()); 6 | if !cpu::cond_passed(data.cond.get(), &cpu.cpsr) { 7 | return cpu::InstrStatus::InBlock; 8 | } 9 | 10 | let rn = cpu.regs[data.rn.get() as usize]; 11 | let out = rn.swap_bytes(); 12 | cpu.regs[data.rd.get() as usize] = out; 13 | 14 | cpu::InstrStatus::InBlock 15 | } 16 | 17 | pub fn uxtb(cpu: &mut Cpu, data: arm::Uxtb::Bf) -> cpu::InstrStatus { 18 | assert!( V::is::() ); 19 | if !cpu::cond_passed(data.cond.get(), &cpu.cpsr) { 20 | return cpu::InstrStatus::InBlock; 21 | } 22 | 23 | let rm = cpu.regs[data.rm.get() as usize]; 24 | let rot = 8 * data.rot.get() as usize; 25 | 26 | let val = (rm >> rot) & 0xFF; 27 | cpu.regs[data.rd.get() as usize] = val; 28 | 29 | cpu::InstrStatus::InBlock 30 | } 31 | 32 | pub fn uxth(cpu: &mut Cpu, data: arm::Uxth::Bf) -> cpu::InstrStatus { 33 | assert!( V::is::() ); 34 | if !cpu::cond_passed(data.cond.get(), &cpu.cpsr) { 35 | return cpu::InstrStatus::InBlock; 36 | } 37 | 38 | let rm = cpu.regs[data.rm.get() as usize]; 39 | let rot = 8 * data.rot.get(); 40 | 41 | let val = rm.rotate_right(rot) & 0xFFFF; 42 | cpu.regs[data.rd.get() as usize] = val; 43 | 44 | cpu::InstrStatus::InBlock 45 | } 46 | -------------------------------------------------------------------------------- /libllama/src/cpu/instructions_arm/misc.rs: -------------------------------------------------------------------------------- 1 | use cpu::{self, Cpu, Version}; 2 | use cpu::interpreter_arm as arm; 3 | 4 | pub fn swi(cpu: &mut Cpu, data: arm::Swi::Bf) -> cpu::InstrStatus { 5 | assert!(V::is::()); 6 | if !cpu::cond_passed(data.cond.get(), &cpu.cpsr) { 7 | return cpu::InstrStatus::InBlock; 8 | } 9 | 10 | let next_instr = cpu.regs[15] - cpu.get_pc_offset() / 2; 11 | cpu.enter_exception(next_instr, cpu::Mode::Svc); 12 | cpu::InstrStatus::Branched 13 | } 14 | 15 | pub fn bkpt(_cpu: &mut Cpu, data: arm::Bkpt::Bf) -> cpu::InstrStatus { 16 | let brk_num = data.immed_lo.get() | (data.immed_hi.get() << 4); 17 | panic!("Hit breakpoint instruction! (#{})", brk_num); 18 | } 19 | -------------------------------------------------------------------------------- /libllama/src/cpu/instructions_arm/mod.rs: -------------------------------------------------------------------------------- 1 | mod branch; 2 | mod coprocessor; 3 | mod data_processing; 4 | mod load_store; 5 | mod load_store_multiple; 6 | mod media; 7 | mod misc; 8 | mod program_status; 9 | 10 | pub use self::branch::*; 11 | pub use self::coprocessor::*; 12 | pub use self::data_processing::*; 13 | pub use self::load_store::*; 14 | pub use self::load_store_multiple::*; 15 | pub use self::media::*; 16 | pub use self::misc::*; 17 | pub use self::program_status::*; 18 | -------------------------------------------------------------------------------- /libllama/src/cpu/instructions_arm/program_status.rs: -------------------------------------------------------------------------------- 1 | use cpu; 2 | use cpu::{Cpu, Version, v5, v6}; 3 | use cpu::interpreter_arm as arm; 4 | 5 | pub fn mrs(cpu: &mut Cpu, data: arm::Mrs::Bf) -> cpu::InstrStatus { 6 | if !cpu::cond_passed(data.cond.get(), &cpu.cpsr) { 7 | return cpu::InstrStatus::InBlock; 8 | } 9 | 10 | let rd = data.rd.get(); 11 | let r_bit = data.r_bit.get(); 12 | 13 | if r_bit == 1 { 14 | cpu.regs[rd as usize] = cpu.get_current_spsr().val; 15 | } else { 16 | cpu.regs[rd as usize] = cpu.cpsr.val; 17 | } 18 | 19 | cpu::InstrStatus::InBlock 20 | } 21 | 22 | pub fn instr_msr(cpu: &mut Cpu, data: arm::Msr1::Bf, immediate: bool) -> cpu::InstrStatus { 23 | if !cpu::cond_passed(data.cond.get(), &cpu.cpsr) { 24 | return cpu::InstrStatus::InBlock; 25 | } 26 | 27 | let field_mask = data.field_mask.get(); 28 | let shifter_operand = data.shifter_operand.get(); 29 | 30 | let val = if immediate { 31 | let immed_8 = bits!(shifter_operand, 0:7); 32 | let rotate_imm = bits!(shifter_operand, 8:11); 33 | immed_8.rotate_right(rotate_imm * 2) 34 | } else { 35 | cpu.regs[bits!(shifter_operand, 0:3) as usize] 36 | }; 37 | 38 | let unalloc_mask = if V::is::() { 0x07FFFF00u32 } else { 0x06F0FC00 }; 39 | let user_mask = if V::is::() { 0xF8000000u32 } else { 0xF80F0200 }; 40 | let priv_mask = if V::is::() { 0x0000000Fu32 } else { 0x000001DF }; 41 | let state_mask = if V::is::() { 0x00000020u32 } else { 0x01000020 }; 42 | 43 | if val & unalloc_mask != 0 { 44 | error!("Attempted to set reserved PSR bits through MSR instruction!"); 45 | } 46 | 47 | let mut byte_mask = 0u32; 48 | byte_mask |= if bit!(field_mask, 0) == 1 { 0x000000FF } else { 0 }; 49 | byte_mask |= if bit!(field_mask, 1) == 1 { 0x0000FF00 } else { 0 }; 50 | byte_mask |= if bit!(field_mask, 2) == 1 { 0x00FF0000 } else { 0 }; 51 | byte_mask |= if bit!(field_mask, 3) == 1 { 0xFF000000 } else { 0 }; 52 | 53 | if data.r_bit.get() == 0 { 54 | // CPSR 55 | // TODO: Check privileges 56 | let cleared_cpsr = cpu.cpsr.val & !byte_mask; 57 | cpu.cpsr.val = cleared_cpsr | (val & byte_mask); 58 | 59 | if bit!(field_mask, 0) == 1 { 60 | // CPU mode may have been changed 61 | cpu.regs.swap(cpu::Mode::from_num(cpu.cpsr.mode.get())); 62 | } 63 | } else { 64 | // SPSR 65 | let spsr = cpu.get_current_spsr(); 66 | byte_mask &= user_mask | priv_mask | state_mask; 67 | 68 | let cleared_spsr = spsr.val & !byte_mask; 69 | spsr.val = cleared_spsr | (val & byte_mask); 70 | } 71 | 72 | cpu::InstrStatus::InBlock 73 | } 74 | 75 | pub fn cps(cpu: &mut Cpu, data: arm::Cps::Bf) -> cpu::InstrStatus { 76 | assert!(V::is::()); 77 | 78 | if let cpu::Mode::Usr = cpu::Mode::from_num(cpu.cpsr.mode.get()) { 79 | return cpu::InstrStatus::InBlock 80 | } 81 | 82 | if data.imod.get() & 2 != 0 { 83 | let new = data.imod.get() & 1; 84 | if data.a_bit.get() != 0 { cpu.cpsr.disable_imp_abt.set(new) } 85 | if data.i_bit.get() != 0 { cpu.cpsr.disable_irq_bit.set(new) } 86 | if data.f_bit.get() != 0 { cpu.cpsr.disable_fiq_bit.set(new) } 87 | } 88 | if data.mmod.get() != 0 { 89 | cpu.cpsr.mode.set(data.mode.get()); 90 | cpu.regs.swap(cpu::Mode::from_num(data.mode.get())); 91 | } 92 | 93 | cpu::InstrStatus::InBlock 94 | } 95 | 96 | pub fn msr_1(cpu: &mut Cpu, data: arm::Msr1::Bf) -> cpu::InstrStatus { 97 | instr_msr(cpu, data, true) 98 | } 99 | 100 | pub fn msr_2(cpu: &mut Cpu, data: arm::Msr2::Bf) -> cpu::InstrStatus { 101 | instr_msr(cpu, arm::Msr1::new(data.val), false) 102 | } 103 | 104 | -------------------------------------------------------------------------------- /libllama/src/cpu/instructions_thumb/branch.rs: -------------------------------------------------------------------------------- 1 | use cpu::{self, Cpu, Version}; 2 | use cpu::interpreter_thumb as thumb; 3 | use bitutils::sign_extend32; 4 | 5 | pub fn b_1(cpu: &mut Cpu, data: thumb::B1::Bf) -> cpu::InstrStatus { 6 | let offset_8 = data.signed_imm_8.get(); 7 | let cond = data.cond.get(); 8 | 9 | if !cpu::cond_passed(cond as u32, &cpu.cpsr) { 10 | return cpu::InstrStatus::InBlock; 11 | } 12 | 13 | let addr = (cpu.regs[15] as i32 + (sign_extend32(offset_8 as u32, 8) << 1)) as u32; 14 | cpu.branch(addr); 15 | cpu::InstrStatus::Branched 16 | } 17 | 18 | pub fn branch(cpu: &mut Cpu, data: thumb::Branch::Bf) -> cpu::InstrStatus { 19 | let offset_11 = data.offset_11.get(); 20 | 21 | match data.h_bits.get() { 22 | 0b00 => { 23 | let addr = (cpu.regs[15] as i32 + (sign_extend32(offset_11 as u32, 11) << 1)) as u32; 24 | cpu.branch(addr); 25 | cpu::InstrStatus::Branched 26 | }, 27 | 0b01 => { 28 | let addr = (cpu.regs[14] + (offset_11 << 1) as u32) & 0xFFFFFFFC; 29 | cpu.regs[14] = (cpu.regs[15] - 2) as u32 | 1; 30 | cpu.cpsr.thumb_bit.set(0); 31 | cpu.branch(addr); 32 | cpu::InstrStatus::Branched 33 | }, 34 | 0b10 => { 35 | cpu.regs[14] = (cpu.regs[15] as i32 + (sign_extend32(offset_11 as u32, 11) << 12)) as u32; 36 | cpu::InstrStatus::InBlock 37 | }, 38 | 0b11 => { 39 | let addr = cpu.regs[14] + (offset_11 << 1) as u32; 40 | cpu.regs[14] = (cpu.regs[15] - 2) as u32 | 1; 41 | cpu.branch(addr); 42 | cpu::InstrStatus::Branched 43 | }, 44 | _ => unreachable!(), 45 | } 46 | } 47 | 48 | pub fn blx_2(cpu: &mut Cpu, data: thumb::Blx2::Bf) -> cpu::InstrStatus { 49 | let rm = data.rm.get() | (data.h2.get() << 3); 50 | let addr = cpu.regs[rm as usize]; 51 | 52 | cpu.regs[14] = (cpu.regs[15] - 2) as u32 | 1; 53 | cpu.cpsr.thumb_bit.set(bit!(addr, 0)); 54 | 55 | cpu.branch(addr & 0xFFFFFFFE); 56 | cpu::InstrStatus::Branched 57 | } 58 | 59 | pub fn bx(cpu: &mut Cpu, data: thumb::Bx::Bf) -> cpu::InstrStatus { 60 | let addr = cpu.regs[((data.h2.get() << 3) | data.rm.get()) as usize]; 61 | cpu.cpsr.thumb_bit.set(bit!(addr, 0)); 62 | cpu.branch(addr & 0xFFFFFFFE); 63 | cpu::InstrStatus::Branched 64 | } 65 | -------------------------------------------------------------------------------- /libllama/src/cpu/instructions_thumb/media.rs: -------------------------------------------------------------------------------- 1 | use cpu::{self, arm, thumb, Cpu, Version}; 2 | 3 | pub fn rev(cpu: &mut Cpu, data: thumb::Rev::Bf) -> cpu::InstrStatus { 4 | assert!(V::is::()); 5 | let arminst: u32 = 0b1110011010111111_0000_11110011_0000 6 | | ((data.rd.get() as u32) << 12) 7 | | ((data.rn.get() as u32) << 0); 8 | cpu::instructions_arm::rev(cpu, arm::Rev::new(arminst)) 9 | } 10 | 11 | pub fn uxtb(cpu: &mut Cpu, data: thumb::Uxtb::Bf) -> cpu::InstrStatus { 12 | assert!(V::is::()); 13 | let arminst: u32 = 0b1110011011101111_0000_00000111_0000 14 | | ((data.rd.get() as u32) << 12) 15 | | ((data.rm.get() as u32) << 0); 16 | cpu::instructions_arm::uxtb(cpu, arm::Uxtb::new(arminst)) 17 | } 18 | 19 | pub fn uxth(cpu: &mut Cpu, data: thumb::Uxth::Bf) -> cpu::InstrStatus { 20 | assert!(V::is::()); 21 | let arminst: u32 = 0b1110011011111111_0000_00000111_0000 22 | | ((data.rd.get() as u32) << 12) 23 | | ((data.rm.get() as u32) << 0); 24 | cpu::instructions_arm::uxth(cpu, arm::Uxth::new(arminst)) 25 | } 26 | -------------------------------------------------------------------------------- /libllama/src/cpu/instructions_thumb/misc.rs: -------------------------------------------------------------------------------- 1 | use cpu::{self, arm, thumb, Cpu, Version}; 2 | 3 | pub fn bkpt(cpu: &mut Cpu, data: thumb::Bkpt::Bf) -> cpu::InstrStatus { 4 | assert!(V::is::()); 5 | let immed_lo = data.immed_8.get() as u32 & 0b1111; 6 | let immed_hi = data.immed_8.get() as u32 >> 4; 7 | let arminst: u32 = 0b11100001001000000000_0000_0111_0000 8 | | (immed_hi << 8) 9 | | (immed_lo << 0); 10 | cpu::instructions_arm::bkpt(cpu, arm::Bkpt::new(arminst)) 11 | } 12 | 13 | pub fn swi(cpu: &mut Cpu, data: thumb::Swi::Bf) -> cpu::InstrStatus { 14 | assert!(V::is::()); 15 | let arminst: u32 = 0b111011110000000000000000_00000000 16 | | ((data.immed_8.get() as u32) << 0); 17 | cpu::instructions_arm::swi(cpu, arm::Swi::new(arminst)) 18 | } 19 | 20 | -------------------------------------------------------------------------------- /libllama/src/cpu/instructions_thumb/mod.rs: -------------------------------------------------------------------------------- 1 | mod branch; 2 | mod data_processing; 3 | mod load_store; 4 | mod media; 5 | mod misc; 6 | 7 | pub use self::branch::*; 8 | pub use self::data_processing::*; 9 | pub use self::load_store::*; 10 | pub use self::media::*; 11 | pub use self::misc::*; 12 | -------------------------------------------------------------------------------- /libllama/src/cpu/interpreter_arm.rs: -------------------------------------------------------------------------------- 1 | use cpu::{Cpu, Version}; 2 | use cpu::caches::Ops; 3 | use cpu::regs::Psr; 4 | use cpu::InstrStatus; 5 | 6 | pub fn cond_passed(cond_opcode: u32, cpsr: &Psr::Bf) -> bool { 7 | match cond_opcode { 8 | 0b0000 => return cpsr.z_bit.get() == 1, // EQ 9 | 0b0001 => return cpsr.z_bit.get() == 0, // NE 10 | 0b0010 => return cpsr.c_bit.get() == 1, // CS 11 | 0b0011 => return cpsr.c_bit.get() == 0, // CC 12 | 0b0100 => return cpsr.n_bit.get() == 1, // MI 13 | 0b0101 => return cpsr.n_bit.get() == 0, // PL 14 | 0b0110 => return cpsr.v_bit.get() == 1, // VS 15 | 0b0111 => return cpsr.v_bit.get() == 0, // VC 16 | 0b1000 => { // HI 17 | return (cpsr.c_bit.get() == 1) && (cpsr.z_bit.get() == 0) 18 | }, 19 | 0b1001 => { // LS 20 | return (cpsr.c_bit.get() == 0) || (cpsr.z_bit.get() == 1) 21 | }, 22 | 0b1010 => { // GE 23 | return cpsr.n_bit.get() == cpsr.v_bit.get() 24 | }, 25 | 0b1011 => { // LT 26 | return cpsr.n_bit.get() != cpsr.v_bit.get() 27 | }, 28 | 0b1100 => { // GT 29 | return (cpsr.z_bit.get() == 0) && 30 | (cpsr.n_bit.get() == cpsr.v_bit.get()) 31 | }, 32 | 0b1101 => { // LE 33 | return (cpsr.z_bit.get() == 1) || 34 | (cpsr.n_bit.get() != cpsr.v_bit.get()) 35 | }, 36 | 0b1110 => return true, // AL 37 | _ => panic!("Unhandled condition code {:#b}!", cond_opcode), 38 | } 39 | } 40 | 41 | pub type InstFn = fn(&mut Cpu, u32) -> InstrStatus; 42 | 43 | mod interpreter { 44 | use cpu; 45 | pub use cpu::instructions_arm::*; 46 | 47 | pub fn undef(cpu: &mut cpu::Cpu, instr: u32) -> cpu::InstrStatus { 48 | panic!("Unimplemented instruction! {:#X}: {:#08X}", cpu.regs[15] - cpu.get_pc_offset(), instr) 49 | } 50 | 51 | #[inline(always)] 52 | pub fn blx_2(cpu: &mut cpu::Cpu, instr: super::Blx2::Bf) -> cpu::InstrStatus { 53 | blx(cpu, instr) 54 | } 55 | } 56 | 57 | include!(concat!(env!("OUT_DIR"), "/arm.decoder.rs")); 58 | 59 | #[inline] 60 | pub fn interpret_next(cpu: &mut Cpu, addr: u32) -> InstrStatus { 61 | let instr = cpu.mpu.imem_read::(addr); 62 | let inst_fn = *cpu.arm_decode_cache.get_or(instr, &mut ()); 63 | 64 | // trace!("ARM{:?} @ {:08X}: {} ({:08X})", cpu._version, addr, ::cpu::arm::disasm(instr), instr); 65 | inst_fn(cpu, instr) 66 | } 67 | -------------------------------------------------------------------------------- /libllama/src/cpu/interpreter_thumb.rs: -------------------------------------------------------------------------------- 1 | use cpu::{Cpu, InstrStatus, Version}; 2 | use cpu::caches::Ops; 3 | 4 | pub type InstFn = fn(&mut Cpu, u16) -> InstrStatus; 5 | mod interpreter { 6 | use cpu; 7 | pub use cpu::instructions_thumb::*; 8 | pub fn undef(cpu: &mut cpu::Cpu, instr: u16) -> cpu::InstrStatus { 9 | panic!("Unimplemented instruction! {:#X}: {:?}", cpu.regs[15] - cpu.get_pc_offset(), instr) 10 | } 11 | } 12 | 13 | include!(concat!(env!("OUT_DIR"), "/thumb.decoder.rs")); 14 | 15 | #[inline] 16 | pub fn interpret_next(cpu: &mut Cpu, addr: u32) -> InstrStatus { 17 | let instr = cpu.mpu.imem_read::(addr); 18 | let inst_fn = *cpu.thumb_decode_cache.get_or(instr as u32, &mut ()); 19 | 20 | // trace!("THUMB{:?} @ {:08X}: {} ({:04X})", cpu._version, addr, ::cpu::thumb::disasm(instr), instr); 21 | inst_fn(cpu, instr) 22 | } 23 | -------------------------------------------------------------------------------- /libllama/src/cpu/irq.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::sync::Arc; 3 | use std::rc::Rc; 4 | use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; 5 | use std::cell::Cell; 6 | use std::sync::mpsc; 7 | 8 | pub trait IrqType: fmt::Debug + Copy + Clone { 9 | fn index(&self) -> u32; 10 | } 11 | 12 | #[derive(Debug, Copy, Clone)] 13 | pub enum IrqType9 { 14 | Dmac1_0 = 0, 15 | Dmac1_1 = 1, 16 | Dmac1_2 = 2, 17 | Dmac1_3 = 3, 18 | Dmac1_4 = 4, 19 | Dmac1_5 = 5, 20 | Dmac1_6 = 6, 21 | Dmac1_7 = 7, 22 | Timer0 = 8, 23 | Timer1 = 9, 24 | Timer2 = 10, 25 | Timer3 = 11, 26 | PxiSync = 12, 27 | PxiNotFull = 13, 28 | PxiNotEmpty = 14, 29 | Aes = 15, 30 | Sdio1 = 16, 31 | Sdio1Async = 17, 32 | Sdio3 = 18, 33 | Sdio3Async = 19, 34 | DebugRecv = 20, 35 | DebugSend = 21, 36 | RSA = 22, 37 | CtrCard1 = 23, 38 | CtrCard2 = 24, 39 | Cgc = 25, 40 | CgcDet = 26, 41 | DsCard = 27, 42 | Dmac2 = 28, 43 | Dmac2Abort = 29 44 | } 45 | 46 | impl IrqType for IrqType9 { 47 | fn index(&self) -> u32 { 48 | *self as u32 49 | } 50 | } 51 | 52 | #[derive(Debug, Copy, Clone)] 53 | pub enum IrqType11 { 54 | PxiSync = 80, 55 | } 56 | 57 | impl IrqType for IrqType11 { 58 | fn index(&self) -> u32 { 59 | *self as u32 60 | } 61 | } 62 | 63 | 64 | 65 | 66 | 67 | type RcMut = Rc>; 68 | 69 | type AsyncLine = Arc; 70 | type SyncLine = RcMut; 71 | type AsyncEnabled = Arc<[AtomicU32; 4]>; 72 | type SyncEnabled = RcMut; 73 | 74 | /// CPU end of the IRQ subsystem, just a simple triggered/not triggered API 75 | #[derive(Clone)] 76 | pub struct IrqLine { 77 | pub(crate) async: AsyncLine, 78 | pub(crate) sync: SyncLine, 79 | } 80 | 81 | impl IrqLine { 82 | pub fn is_high_sync(&self) -> bool { 83 | self.sync.get() 84 | } 85 | pub fn is_high_async(&self) -> bool { 86 | self.async.load(Ordering::Relaxed) 87 | } 88 | pub fn is_high(&self) -> bool { 89 | self.is_high_sync() || self.is_high_async() 90 | } 91 | } 92 | 93 | 94 | 95 | 96 | pub trait IrqClient: Clone { 97 | fn assert(&mut self, irq: IRQ); 98 | fn is_enabled(&self, u32) -> bool; 99 | } 100 | 101 | pub trait IrqServer { 102 | fn pop(&mut self) -> Option; 103 | } 104 | 105 | 106 | 107 | #[derive(Clone)] 108 | pub struct IrqSyncClient { 109 | irqs: RcMut, 110 | enabled: SyncEnabled, 111 | line: SyncLine, 112 | } 113 | 114 | impl IrqClient for IrqSyncClient { 115 | fn assert(&mut self, irq: IRQ) { 116 | let index = irq.index(); 117 | trace!("Trying to assert irq {}", index); 118 | if !self.is_enabled(index) { 119 | return 120 | } 121 | 122 | let set = self.irqs.get(); 123 | self.irqs.set(set | 1u128 << (index as u128)); 124 | self.line.set(true); 125 | } 126 | 127 | fn is_enabled(&self, index: u32) -> bool { 128 | self.enabled.get() & (1u128 << (index as u128)) != 0 129 | } 130 | } 131 | 132 | 133 | 134 | pub struct IrqSyncServer { 135 | irqs: RcMut 136 | } 137 | 138 | impl IrqServer for IrqSyncServer { 139 | fn pop(&mut self) -> Option { 140 | let set = self.irqs.get(); 141 | if set == 0 { 142 | return None 143 | } 144 | let out = set.trailing_zeros(); 145 | self.irqs.set(set & !(1 << out)); 146 | Some(out) 147 | } 148 | } 149 | 150 | 151 | #[derive(Clone)] 152 | pub struct IrqAsyncClient { 153 | irqs: mpsc::Sender, 154 | enabled: AsyncEnabled, 155 | line: AsyncLine, 156 | } 157 | 158 | impl IrqClient for IrqAsyncClient { 159 | fn assert(&mut self, irq: IRQ) { 160 | let index = irq.index(); 161 | trace!("Trying to assert irq {}", index); 162 | if !self.is_enabled(index) { 163 | return 164 | } 165 | self.irqs.send(index).unwrap(); 166 | self.line.store(true, Ordering::Relaxed); 167 | } 168 | 169 | fn is_enabled(&self, index: u32) -> bool { 170 | let which_word = (index / 32) as usize; 171 | let enabled = self.enabled[which_word].load(Ordering::Relaxed); 172 | enabled & (1 << (index % 32)) != 0 173 | } 174 | } 175 | 176 | pub struct IrqAsyncServer { 177 | irqs: mpsc::Receiver 178 | } 179 | 180 | impl IrqServer for IrqAsyncServer { 181 | fn pop(&mut self) -> Option { 182 | self.irqs.try_recv().ok() 183 | } 184 | } 185 | 186 | 187 | pub struct Aggregator { 188 | async_rx: IrqAsyncServer, 189 | async_enabled: AsyncEnabled, 190 | sync_rx: IrqSyncServer, 191 | sync_enabled: SyncEnabled, 192 | line: IrqLine, 193 | pending: u128, 194 | } 195 | 196 | impl fmt::Debug for Aggregator { 197 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 198 | write!(f, "Aggregator") 199 | } 200 | } 201 | 202 | impl Aggregator { 203 | pub(crate) fn set_enabled(&self, new: u128) { 204 | self.sync_enabled.set(new); 205 | self.async_enabled[0].store(new as u32, Ordering::Relaxed); 206 | self.async_enabled[1].store((new >> 32) as u32, Ordering::Relaxed); 207 | self.async_enabled[2].store((new >> 64) as u32, Ordering::Relaxed); 208 | self.async_enabled[3].store((new >> 96) as u32, Ordering::Relaxed); 209 | } 210 | 211 | pub(crate) fn drain_asserts(&mut self) -> u128 { 212 | let mut pending = self.pending; 213 | while let Some(x) = self.sync_rx.pop() { 214 | pending |= 1u128 << (x as u128); 215 | } 216 | while let Some(x) = self.async_rx.pop() { 217 | pending |= 1u128 << (x as u128); 218 | } 219 | self.pending = pending; 220 | pending 221 | } 222 | 223 | pub(crate) fn acknowledge(&mut self, which: u128) -> u128 { 224 | self.pending = self.drain_asserts() & !which; 225 | self.line.sync.set(self.pending != 0); 226 | self.line.async.store(self.pending != 0, Ordering::Relaxed); 227 | self.pending 228 | } 229 | } 230 | 231 | pub struct IrqSubsys { 232 | pub(crate) agg: Aggregator, 233 | pub(crate) sync_tx: IrqSyncClient, 234 | pub(crate) async_tx: IrqAsyncClient, 235 | pub(crate) line: IrqLine 236 | } 237 | 238 | impl IrqSubsys { 239 | pub(crate) fn create() -> Self { 240 | let line = IrqLine { 241 | sync: Rc::new(Cell::new(false)), 242 | async: Arc::new(AtomicBool::new(false)) 243 | }; 244 | let sync_enabled = Rc::new(Cell::new(0)); 245 | let async_enabled = Arc::new([AtomicU32::new(0), AtomicU32::new(0), AtomicU32::new(0), AtomicU32::new(0)]); 246 | let sync_irqs = Rc::new(Cell::new(0)); 247 | let (async_irqs_tx, async_irqs_rx) = mpsc::channel(); 248 | Self { 249 | agg: Aggregator { 250 | async_rx: IrqAsyncServer { 251 | irqs: async_irqs_rx, 252 | }, 253 | async_enabled: async_enabled.clone(), 254 | sync_rx: IrqSyncServer { 255 | irqs: sync_irqs.clone() 256 | }, 257 | sync_enabled: sync_enabled.clone(), 258 | line: line.clone(), 259 | pending: 0 260 | }, 261 | sync_tx: IrqSyncClient { 262 | line: line.sync.clone(), 263 | enabled: sync_enabled, 264 | irqs: sync_irqs, 265 | }, 266 | async_tx: IrqAsyncClient { 267 | line: line.async.clone(), 268 | enabled: async_enabled, 269 | irqs: async_irqs_tx, 270 | }, 271 | line: line 272 | } 273 | } 274 | } 275 | 276 | -------------------------------------------------------------------------------- /libllama/src/cpu/mod.rs: -------------------------------------------------------------------------------- 1 | mod cpu; 2 | 3 | pub mod caches; 4 | mod coproc; 5 | pub mod interpreter_arm; 6 | pub mod interpreter_thumb; 7 | 8 | pub use self::cpu::*; 9 | pub use self::interpreter_arm as arm; 10 | pub use self::interpreter_thumb as thumb; 11 | pub use self::arm::cond_passed; 12 | 13 | pub mod instructions_arm; 14 | pub mod instructions_thumb; 15 | pub mod irq; 16 | pub mod regs; 17 | 18 | pub enum InstrStatus { 19 | InBlock, // Advance PC by instruction width 20 | Branched, // Do not advance PC 21 | } 22 | -------------------------------------------------------------------------------- /libllama/src/cpu/regs.rs: -------------------------------------------------------------------------------- 1 | use cpu; 2 | 3 | use std::iter::Iterator; 4 | use std::ops; 5 | 6 | // Program status register 7 | bf!(Psr[u32] { 8 | mode: 0:4, 9 | thumb_bit: 5:5, 10 | disable_fiq_bit: 6:6, 11 | disable_irq_bit: 7:7, 12 | disable_imp_abt: 8:8, 13 | q_bit: 27:27, 14 | v_bit: 28:28, 15 | c_bit: 29:29, 16 | z_bit: 30:30, 17 | n_bit: 31:31 18 | }); 19 | 20 | #[derive(Debug)] 21 | pub struct GpRegs { 22 | active: [u32; 16], 23 | mode: cpu::Mode, 24 | banks: GpBanks 25 | } 26 | 27 | impl GpRegs { 28 | pub fn new(mode: cpu::Mode) -> GpRegs { 29 | GpRegs { 30 | active: [0; 16], 31 | mode: mode, 32 | banks: GpBanks { 33 | basic_bank: [0; 13], 34 | usr_bank: [0; 2], 35 | svc_bank: [0; 2], 36 | abt_bank: [0; 2], 37 | und_bank: [0; 2], 38 | irq_bank: [0; 2], 39 | fiq_bank: [0; 8] 40 | } 41 | } 42 | } 43 | 44 | pub fn swap(&mut self, mode: cpu::Mode) { 45 | trace!("Swapping register banks: mode {:?} to {:?}", self.mode, mode); 46 | { // Save active regs to bank 47 | let mut iter = self.banks.build_iter(self.mode); 48 | for i in 0..15 { 49 | *iter.next().unwrap() = self.active[i]; 50 | } 51 | } 52 | { // Load bank into active regs 53 | let mut iter = self.banks.build_iter(mode); 54 | for i in 0..15 { 55 | self.active[i] = *iter.next().unwrap(); 56 | } 57 | } 58 | self.mode = mode; 59 | } 60 | } 61 | 62 | impl ops::Index for GpRegs { 63 | type Output = u32; 64 | fn index(&self, i: usize) -> &Self::Output { 65 | &self.active[i] 66 | } 67 | } 68 | 69 | impl ops::IndexMut for GpRegs { 70 | fn index_mut<'a>(&'a mut self, i: usize) -> &mut Self::Output { 71 | &mut self.active[i] 72 | } 73 | } 74 | 75 | // Helper struct to contain all the mode-dependent register banks 76 | #[derive(Debug)] 77 | struct GpBanks { 78 | basic_bank: [u32; 13], 79 | usr_bank: [u32; 2], 80 | svc_bank: [u32; 2], 81 | abt_bank: [u32; 2], 82 | und_bank: [u32; 2], 83 | irq_bank: [u32; 2], 84 | fiq_bank: [u32; 8], 85 | } 86 | 87 | impl GpBanks { 88 | fn build_iter<'a>(&'a mut self, mode: cpu::Mode) -> impl Iterator + 'a { 89 | match mode { 90 | cpu::Mode::Sys | cpu::Mode::Usr 91 | => self.basic_bank.iter_mut().take(13).chain(self.usr_bank.iter_mut()), 92 | cpu::Mode::Svc => self.basic_bank.iter_mut().take(13).chain(self.svc_bank.iter_mut()), 93 | cpu::Mode::Abt => self.basic_bank.iter_mut().take(13).chain(self.abt_bank.iter_mut()), 94 | cpu::Mode::Und => self.basic_bank.iter_mut().take(13).chain(self.und_bank.iter_mut()), 95 | cpu::Mode::Irq => self.basic_bank.iter_mut().take(13).chain(self.irq_bank.iter_mut()), 96 | cpu::Mode::Fiq => self.basic_bank.iter_mut().take(7).chain(self.fiq_bank.iter_mut()) 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /libllama/src/cpu/thumb.decoder: -------------------------------------------------------------------------------- 1 | decoder u16 ThumbInstruction { 2 | category [ _:16 ] 3 | { 4 | adc = [ 0b0100000101:10; rm:3; rd:3 ] 5 | add_1 = [ 0b0001110:7; immed_3:3; rn:3; rd:3 ] 6 | add_2 = [ 0b00110:5; rd:3; immed_8:8 ] 7 | add_3 = [ 0b0001100:7; rm:3; rn:3; rd:3 ] 8 | add_4 = [ 0b01000100:8; h1:1; h2:1; rm:3; rd:3 ] 9 | add_5 = [ 0b10100:5; rd:3; immed_8:8 ] 10 | add_6 = [ 0b10101:5; rd:3; immed_8:8 ] 11 | add_7 = [ 0b101100000:9; immed_7:7 ] 12 | and = [ 0b0100000000:10; rm:3; rd:3 ] 13 | asr_1 = [ 0b00010:5; immed_5:5; rm:3; rd:3 ] 14 | asr_2 = [ 0b0100000100:10; rs:3; rd:3 ] 15 | b_1 = [ 0b1101:4; cond:4; signed_imm_8:8 ] 16 | bic = [ 0b0100001110:10; rm:3; rd:3 ] 17 | bkpt = [ 0b10111110:8; immed_8:8 ] 18 | branch = [ 0b111:3; h_bits:2; offset_11:11 ] 19 | blx_2 = [ 0b010001111:9; h2:1; rm:3; 0b000:3 ] 20 | bx = [ 0b010001110:9; h2:1; rm:3; 0b000:3 ] 21 | cmn = [ 0b0100001011:10; rm:3; rn:3 ] 22 | cmp_1 = [ 0b00101:5; rn:3; immed_8:8 ] 23 | cmp_2 = [ 0b0100001010:10; rm:3; rn:3 ] 24 | cmp_3 = [ 0b01000101:8; h1:1; h2:1; rm:3; rn:3 ] 25 | eor = [ 0b0100000001:10; rm:3; rd:3 ] 26 | ldmia = [ 0b11001:5; rn:3; register_list:8 ] 27 | ldr_1 = [ 0b01101:5; immed_5:5; rn:3; rd:3 ] 28 | ldr_2 = [ 0b0101100:7; rm:3; rn:3; rd:3 ] 29 | ldr_3 = [ 0b01001:5; rd:3; immed_8:8 ] 30 | ldr_4 = [ 0b10011:5; rd:3; immed_8:8 ] 31 | ldrb_1 = [ 0b01111:5; immed_5:5; rn:3; rd:3 ] 32 | ldrb_2 = [ 0b0101110:7; rm:3; rn:3; rd:3 ] 33 | ldrh_1 = [ 0b10001:5; immed_5:5; rn:3; rd:3 ] 34 | ldrh_2 = [ 0b0101101:7; rm:3; rn:3; rd:3 ] 35 | ldrsb = [ 0b0101011:7; rm:3; rn:3; rd:3 ] 36 | ldrsh = [ 0b0101111:7; rm:3; rn:3; rd:3 ] 37 | lsl_1 = [ 0b00000:5; immed_5:5; rm:3; rd:3 ] 38 | lsl_2 = [ 0b0100000010:10; rs:3; rd:3 ] 39 | lsr_1 = [ 0b00001:5; immed_5:5; rm:3; rd:3 ] 40 | lsr_2 = [ 0b0100000011:10; rs:3; rd:3 ] 41 | mov_1 = [ 0b00100:5; rd:3; immed_8:8 ] 42 | mov_2 = [ 0b0001110000:10; rn:3; rd:3 ] 43 | mov_3 = [ 0b01000110:8; h1:1; h2:1; rm:3; rd:3 ] 44 | mul = [ 0b0100001101:10; rm:3; rd:3 ] 45 | mvn = [ 0b0100001111:10; rm:3; rd:3 ] 46 | neg = [ 0b0100001001:10; rm:3; rd:3 ] 47 | orr = [ 0b0100001100:10; rm:3; rd:3 ] 48 | pop = [ 0b1011110:7; r_bit:1; register_list:8 ] 49 | push = [ 0b1011010:7; r_bit:1; register_list:8 ] 50 | rev = [ 0b1011101000:10; rn:3; rd:3 ] 51 | ror = [ 0b0100000111:10; rs:3; rd:3 ] 52 | sbc = [ 0b0100000110:10; rm:3; rd:3 ] 53 | stmia = [ 0b11000:5; rn:3; register_list:8 ] 54 | str_1 = [ 0b01100:5; immed_5:5; rn:3; rd:3 ] 55 | str_2 = [ 0b0101000:7; rm:3; rn:3; rd:3 ] 56 | str_3 = [ 0b10010:5; rd:3; immed_8:8 ] 57 | strb_1 = [ 0b01110:5; immed_5:5; rn:3; rd:3 ] 58 | strb_2 = [ 0b0101010:7; rm:3; rn:3; rd:3 ] 59 | strh_1 = [ 0b10000:5; immed_5:5; rn:3; rd:3 ] 60 | strh_2 = [ 0b0101001:7; rm:3; rn:3; rd:3 ] 61 | sub_1 = [ 0b0001111:7; immed_3:3; rn:3; rd:3 ] 62 | sub_2 = [ 0b00111:5; rd:3; immed_8:8 ] 63 | sub_3 = [ 0b0001101:7; rm:3; rn:3; rd:3 ] 64 | sub_4 = [ 0b101100001:9; immed_7:7 ] 65 | swi = [ 0b11011111:8; immed_8:8 ] 66 | tst = [ 0b0100001000:10; rm:3; rn:3 ] 67 | uxtb = [ 0b1011001011:10; rm:3; rd:3 ] 68 | uxth = [ 0b1011001010:10; rm:3; rd:3 ] 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /libllama/src/dbgcore.rs: -------------------------------------------------------------------------------- 1 | use std::sync; 2 | 3 | use cpu::{self, v5, v6}; 4 | pub use cpu::irq::{IrqType9, IrqClient}; 5 | use cpu::caches::Ops; 6 | use hwcore; 7 | use io; 8 | 9 | #[derive(Clone)] 10 | pub struct DbgCore { 11 | hw: sync::Arc> 12 | } 13 | 14 | impl DbgCore { 15 | pub fn bind(hw: hwcore::HwCore) -> DbgCore { 16 | DbgCore { 17 | hw: sync::Arc::new(sync::Mutex::new(hw)), 18 | } 19 | } 20 | 21 | pub fn ctx<'a>(&'a mut self, which: ActiveCpu) -> DbgContext<'a> { 22 | DbgContext { 23 | active_cpu: which, 24 | hwcore: self.hw.lock().unwrap() 25 | } 26 | } 27 | } 28 | 29 | pub struct DbgContext<'a> { 30 | active_cpu: ActiveCpu, 31 | hwcore: sync::MutexGuard<'a, hwcore::HwCore> 32 | } 33 | 34 | impl<'a> DbgContext<'a> { 35 | pub fn pause(&mut self) { 36 | self.hwcore.stop(); 37 | } 38 | 39 | pub fn resume(&mut self) { 40 | self.hwcore.start(); 41 | } 42 | 43 | pub fn running(&mut self) -> bool { 44 | self.hwcore.running() 45 | } 46 | 47 | pub fn hwcore(&self) -> &hwcore::HwCore { 48 | &*self.hwcore 49 | } 50 | 51 | pub fn hwcore_mut(&mut self) -> &mut hwcore::HwCore { 52 | &mut *self.hwcore 53 | } 54 | 55 | pub fn hw9<'b>(&'b mut self) -> DbgHw9Context<'b> { 56 | use std::sync::PoisonError; 57 | use hwcore::Hardware9; 58 | 59 | let print_regs = |p: PoisonError>| { 60 | let hw9 = p.into_inner(); 61 | let s = format!("Internal error!\nCPU register state:\n\ 62 | gpregs: {:#X?}\n\ 63 | cpsr: {:#X?}\n\ 64 | last 1024 instruction addresses:\n\ 65 | {:#X?}", hw9.arm9.regs, hw9.arm9.cpsr.val, hw9.arm9.last_instructions); 66 | panic!("{}", s); 67 | }; 68 | DbgHw9Context { 69 | // Will panic if still running 70 | hw: self.hwcore.hardware9.lock().unwrap_or_else(print_regs) 71 | } 72 | } 73 | 74 | pub fn hw11<'b>(&'b mut self) -> DbgHw11Context<'b> { 75 | use std::sync::PoisonError; 76 | use hwcore::Hardware11; 77 | 78 | let print_regs = |p: PoisonError>| { 79 | let hw11 = p.into_inner(); 80 | let s = format!("Internal error!\nCPU register state:\n\ 81 | gpregs: {:#X?}\n\ 82 | cpsr: {:#X?}\n\ 83 | last 1024 instruction addresses:\n\ 84 | {:#X?}", hw11.arm11.regs, hw11.arm11.cpsr.val, hw11.arm11.last_instructions); 85 | panic!("{}", s); 86 | }; 87 | DbgHw11Context { 88 | // Will panic if still running 89 | hw: self.hwcore.hardware11.lock().unwrap_or_else(print_regs) 90 | } 91 | } 92 | 93 | pub fn hw<'b>(&'b mut self) -> Box { 94 | match self.active_cpu { 95 | ActiveCpu::Arm9 => Box::new(self.hw9()), 96 | ActiveCpu::Arm11 => Box::new(self.hw11()) 97 | } 98 | } 99 | 100 | pub fn trigger_irq(&mut self, irq: IrqType9) { 101 | self.hwcore_mut().irq_tx.assert(irq); 102 | } 103 | } 104 | 105 | #[derive(Copy, Clone, Eq, PartialEq)] 106 | pub enum ActiveCpu { 107 | Arm9, Arm11 108 | } 109 | 110 | #[allow(non_camel_case_types)] 111 | pub enum CpuRef<'a> { 112 | v5(&'a cpu::Cpu), 113 | v6(&'a cpu::Cpu), 114 | } 115 | 116 | #[allow(non_camel_case_types)] 117 | pub enum CpuMut<'a> { 118 | v5(&'a mut cpu::Cpu), 119 | v6(&'a mut cpu::Cpu), 120 | } 121 | 122 | macro_rules! any_cpu { 123 | ($self:expr, mut $ident:ident; $code:block) => { 124 | match $self.cpu_mut() { 125 | CpuMut::v5($ident) => $code, 126 | CpuMut::v6($ident) => $code 127 | } 128 | }; 129 | ($self:expr, ref $ident:ident; $code:block) => { 130 | match $self.cpu_ref() { 131 | CpuRef::v5($ident) => $code, 132 | CpuRef::v6($ident) => $code 133 | } 134 | }; 135 | } 136 | 137 | pub trait HwCtx { 138 | fn cpu_ref(&self) -> CpuRef; 139 | fn cpu_mut(&mut self) -> CpuMut; 140 | 141 | fn read_mem(&mut self, address: u32, bytes: &mut [u8]) -> Result<(), String> { 142 | any_cpu!(self, mut cpu; { 143 | cpu.mpu.icache_invalidate(); 144 | cpu.mpu.dcache_invalidate(); 145 | cpu.mpu.main_mem_mut().debug_read_buf(address, bytes) 146 | }) 147 | } 148 | 149 | fn write_mem(&mut self, address: u32, bytes: &[u8]) { 150 | any_cpu!(self, mut cpu; { 151 | cpu.mpu.icache_invalidate(); 152 | cpu.mpu.dcache_invalidate(); 153 | cpu.mpu.main_mem_mut().write_buf(address, bytes); 154 | }) 155 | } 156 | 157 | fn read_reg(&self, reg: usize) -> u32 { 158 | any_cpu!(self, ref cpu; { 159 | cpu.regs[reg] 160 | }) 161 | } 162 | 163 | fn write_reg(&mut self, reg: usize, value: u32) { 164 | any_cpu!(self, mut cpu; { 165 | cpu.regs[reg] = value; 166 | }) 167 | } 168 | 169 | fn read_cpsr(&self) -> u32 { 170 | any_cpu!(self, ref cpu; { 171 | cpu.cpsr.val 172 | }) 173 | } 174 | 175 | fn write_cpsr(&mut self, value: u32) { 176 | any_cpu!(self, mut cpu; { 177 | cpu.cpsr.val = value; 178 | let mode_num = cpu.cpsr.mode.get(); 179 | cpu.regs.swap(cpu::Mode::from_num(mode_num)); 180 | }) 181 | } 182 | 183 | fn pause_addr(&self) -> u32 { 184 | any_cpu!(self, ref cpu; { 185 | cpu.regs[15] - cpu.get_pc_offset() 186 | }) 187 | } 188 | 189 | fn branch_to(&mut self, addr: u32) { 190 | any_cpu!(self, mut cpu; { 191 | cpu.branch(addr); 192 | }) 193 | } 194 | 195 | fn is_thumb(&self) -> bool { 196 | any_cpu!(self, ref cpu; { 197 | cpu.cpsr.thumb_bit.get() == 1 198 | }) 199 | } 200 | 201 | fn step(&mut self) { 202 | any_cpu!(self, mut cpu; { 203 | cpu.run(1); 204 | }) 205 | } 206 | 207 | fn set_breakpoint(&mut self, addr: u32) { 208 | any_cpu!(self, mut cpu; { 209 | cpu.breakpoints.insert(addr); 210 | }) 211 | } 212 | 213 | fn has_breakpoint(&mut self, addr: u32) -> bool { 214 | any_cpu!(self, ref cpu; { 215 | cpu.breakpoints.contains(&addr) 216 | }) 217 | } 218 | 219 | fn del_breakpoint(&mut self, addr: u32) { 220 | any_cpu!(self, mut cpu; { 221 | cpu.breakpoints.remove(&addr); 222 | }) 223 | } 224 | } 225 | 226 | pub struct DbgHw9Context<'a> { 227 | hw: sync::MutexGuard<'a, hwcore::Hardware9> 228 | } 229 | 230 | impl<'a> DbgHw9Context<'a> { 231 | pub fn io9_devices(&self) -> &io::IoRegsArm9 { 232 | self.hw.io9() 233 | } 234 | pub fn io_shared_devices(&self) -> &io::IoRegsShared { 235 | self.hw.io_shared() 236 | } 237 | } 238 | 239 | impl<'a> HwCtx for DbgHw9Context<'a> { 240 | fn cpu_ref(&self) -> CpuRef { 241 | CpuRef::v5(&self.hw.arm9) 242 | } 243 | fn cpu_mut(&mut self) -> CpuMut { 244 | CpuMut::v5(&mut self.hw.arm9) 245 | } 246 | } 247 | 248 | pub struct DbgHw11Context<'a> { 249 | hw: sync::MutexGuard<'a, hwcore::Hardware11> 250 | } 251 | 252 | impl<'a> DbgHw11Context<'a> { 253 | pub fn io11_devices(&self) -> &io::IoRegsArm11 { 254 | self.hw.io11() 255 | } 256 | pub fn io_shared_devices(&self) -> &io::IoRegsShared { 257 | self.hw.io_shared() 258 | } 259 | } 260 | 261 | impl<'a> HwCtx for DbgHw11Context<'a> { 262 | fn cpu_ref(&self) -> CpuRef { 263 | CpuRef::v6(&self.hw.arm11) 264 | } 265 | fn cpu_mut(&mut self) -> CpuMut { 266 | CpuMut::v6(&mut self.hw.arm11) 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /libllama/src/fs.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | 4 | #[derive(Debug, Copy, Clone)] 5 | pub enum LlamaFile { 6 | SdCardImg, 7 | NandImg, 8 | NandCid, 9 | AesKeyDb, 10 | Otp, 11 | Boot9, 12 | Boot11, 13 | } 14 | 15 | #[cfg(not(target_os = "windows"))] 16 | fn make_filepath(filename: &str) -> String { 17 | format!("{}/.config/llama/{}", env::var("HOME").unwrap(), filename) 18 | } 19 | 20 | #[cfg(target_os = "windows")] 21 | fn make_filepath(filename: &str) -> String { 22 | format!("{}/llama/{}", env::var("APPDATA").unwrap(), filename) 23 | } 24 | 25 | fn get_path(lf: LlamaFile) -> String { 26 | let filename = match lf { 27 | LlamaFile::SdCardImg => "sd.fat", 28 | LlamaFile::NandImg => "nand.bin", 29 | LlamaFile::NandCid => "nand-cid.bin", 30 | LlamaFile::AesKeyDb => "aeskeydb.bin", 31 | LlamaFile::Otp => "otp.bin", 32 | LlamaFile::Boot9 => "boot9.bin", 33 | LlamaFile::Boot11 => "boot11.bin", 34 | }; 35 | make_filepath(filename) 36 | } 37 | 38 | pub fn open_file(lf: LlamaFile) -> Result { 39 | let path = get_path(lf); 40 | let res = fs::OpenOptions::new().read(true).write(true).open(path.as_str()); 41 | match res { 42 | Ok(file) => Ok(file), 43 | Err(_) => Err(format!("Could not open file `{}`", path)) 44 | } 45 | } 46 | 47 | pub fn create_file(lf: LlamaFile, initializer: F) -> Result 48 | where F: FnOnce(&mut fs::File) { 49 | let path = get_path(lf); 50 | let res = fs::OpenOptions::new() 51 | .read(true).write(true) 52 | .create(true).truncate(true) 53 | .open(path.as_str()); 54 | let mut file = match res { 55 | Ok(file) => file, 56 | Err(x) => return Err(format!("Could not create file `{}`; {:?}", path, x)) 57 | }; 58 | initializer(&mut file); 59 | Ok(file) 60 | } 61 | -------------------------------------------------------------------------------- /libllama/src/io/config.rs: -------------------------------------------------------------------------------- 1 | iodevice!(ConfigDevice, { 2 | regs: { 3 | 0x000 => sysprot9: u8 { 4 | write_effect = |dev: &ConfigDevice| { 5 | if dev.sysprot9.get() != 0 { 6 | panic!("Protecting ARM9 bootrom!"); 7 | } 8 | }; 9 | } 10 | 0x001 => sysprot11: u8 { 11 | write_effect = |dev: &ConfigDevice| { 12 | if dev.sysprot11.get() != 0 { 13 | panic!("Protecting ARM11 bootrom!"); 14 | } 15 | }; 16 | } 17 | 0x002 => reset11: u8 { } 18 | 0x004 => debugctl: u16 { } 19 | 0x008 => unknown0: u8 { 20 | read_effect = |_| warn!("STUBBED: Read from unknown CONFIG+0x8 register!"); 21 | write_effect = |_| warn!("STUBBED: Write to unknown CONFIG+0x8 register!"); 22 | } 23 | 0x00C => cardctl: u16 { } 24 | 0x010 => cardstatus: u8 { } 25 | 0x012 => cardcycles0: u16 { } 26 | 0x014 => cardcycles1: u16 { } 27 | 0x020 => sdmmcctl: u16 { } 28 | 0x022 => unknown1: u16 { 29 | read_effect = |_| warn!("STUBBED: Read from unknown CONFIG+0x22 register!"); 30 | write_effect = |_| warn!("STUBBED: Write to unknown CONFIG+0x22 register!"); 31 | } 32 | 0x100 => unknown2: u16 { 33 | read_effect = |_| warn!("STUBBED: Read from unknown CONFIG+0x100 register!"); 34 | write_effect = |_| warn!("STUBBED: Write to unknown CONFIG+0x100 register!"); 35 | } 36 | 0x200 => extmem_cnt: u8 { } 37 | } 38 | }); 39 | 40 | iodevice!(ConfigExtDevice, { 41 | regs: { 42 | 0x000 => bootenv: u32 { } 43 | 0x010 => unitinfo: u8 { } 44 | 0x014 => twl_unitinfo: u8 { } 45 | } 46 | }); 47 | -------------------------------------------------------------------------------- /libllama/src/io/ctrcard.rs: -------------------------------------------------------------------------------- 1 | iodevice!(CtrcardDevice, { 2 | regs: { 3 | 0x000 => cnt: u32 { 4 | write_effect = |dev: &mut CtrcardDevice| { 5 | trace!("Wrote 0x{:08X} to CTRCARD CNT register!", dev.cnt.get()); 6 | }; 7 | } 8 | 0x004 => blk_cnt: u32 { } 9 | 0x008 => sec_cnt: u32 { } 10 | 0x010 => sec_seed: u32 { } 11 | 0x020 => cmd0: u32 { } 12 | 0x024 => cmd1: u32 { } 13 | 0x028 => cmd2: u32 { } 14 | 0x02C => cmd3: u32 { } 15 | 0x030 => fifo: u32 { } 16 | } 17 | }); -------------------------------------------------------------------------------- /libllama/src/io/dmac.decoder: -------------------------------------------------------------------------------- 1 | decoder u64 DmacInstruction { 2 | category [ _:64 ] 3 | { 4 | end = [ _:56; 0b00000000:8 ] 5 | kill = [ _:56; 0b00000001:8 ] 6 | nop = [ _:56; 0b00011000:8 ] 7 | rmb = [ _:56; 0b00010010:8 ] 8 | wmb = [ _:56; 0b00010011:8 ] 9 | st = [ _:56; 0b000010:6; bs:1; x:1 ] 10 | ld = [ _:56; 0b000001:6; bs:1; x:1 ] 11 | 12 | lp = [ _:48; iter:8; 0b001000:6; lc:1; 0b0:1 ] 13 | lpend = [ _:48; jump:8; 0b001:3; nf:1; 0b1:1; lc:1; bs:1; x:1 ] 14 | wfp = [ _:48; periph:5; 0b000001100:9; bs:1; p:1 ] 15 | ldp = [ _:48; periph:5; 0b000001001:9; bs:1; 0b1:1 ] 16 | flushp = [ _:48; periph:5; 0b00000110101:11 ] 17 | sev = [ _:48; event:5; 0b00000110100:11 ] 18 | 19 | go = [ _:16; addr:32; 0b00000:5; cn:3; 0b101000:6; ns:1; 0b0:1 ] 20 | mov = [ _:16; imm:32; 0b00000:5; rd:3; 0b10111100:8 ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /libllama/src/io/emmc/card.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::{self, Seek, Read, Write}; 3 | 4 | use io::emmc::TransferType; 5 | use utils::bytes; 6 | use utils::cache::TinyCache; 7 | use fs; 8 | 9 | #[derive(Clone, Copy)] 10 | pub enum CardType { 11 | Mmc, 12 | Sd, 13 | Sdmmc 14 | } 15 | 16 | #[derive(Clone, Copy)] 17 | pub enum CardState { 18 | _Idle = 0, 19 | Ready = 1, 20 | Ident = 2, 21 | Stby = 3, 22 | Tran = 4, 23 | _Data = 5, 24 | _Rcv = 6, 25 | _Prg = 7, 26 | _Dis = 8, 27 | _Btst = 9, 28 | _Slp = 10 29 | } 30 | 31 | bf!(CardStatusReg[u32] { 32 | app_cmd: 5:5, 33 | ready_for_data: 8:8, 34 | current_state: 9:12, 35 | erase_reset: 13:13, 36 | illegal_cmd: 22:22, 37 | cmd_crc_err: 23:23, 38 | erase_seq_err: 28:28, 39 | address_err: 30:30 40 | }); 41 | 42 | bf!(CardIdentReg[u128] {}); 43 | bf!(CardSpecificData[u128] {}); 44 | 45 | #[derive(Clone, Copy, Debug)] 46 | pub enum TransferLoc { 47 | Storage, 48 | RegScr, 49 | RegSsr 50 | } 51 | 52 | #[derive(Debug)] 53 | pub struct ActiveTransfer { 54 | loc: TransferLoc, 55 | pub ty: TransferType, 56 | pub blocks_left: u16, 57 | pub fifo_pos: u16, 58 | seek_pos: u64 59 | } 60 | 61 | const CACHE_LINE_SIZE: usize = 512; 62 | 63 | pub struct Card { 64 | pub ty: CardType, 65 | pub csr: CardStatusReg::Bf, 66 | pub cid: CardIdentReg::Bf, 67 | pub csd: CardSpecificData::Bf, 68 | pub rca: u16, 69 | 70 | storage: File, 71 | cache: TinyCache<[u8; CACHE_LINE_SIZE], File>, 72 | transfer: Option, 73 | } 74 | 75 | impl Card { 76 | pub fn new(ty: CardType, storage: File, cid: CardIdentReg::Bf) -> Card { 77 | let fill_cacheline = |f: &mut File, pos: u32| { 78 | let mut out = [0u8; CACHE_LINE_SIZE]; 79 | f.seek(io::SeekFrom::Start(CACHE_LINE_SIZE as u64 * pos as u64)).unwrap(); 80 | f.read(&mut out).unwrap(); 81 | out 82 | }; 83 | let wb_cacheline = |f: &mut File, pos: u32, data: &[u8; 512]| { 84 | f.seek(io::SeekFrom::Start(CACHE_LINE_SIZE as u64 * pos as u64)).unwrap(); 85 | f.write(data).unwrap(); 86 | }; 87 | 88 | Card { 89 | ty: ty, 90 | csr: CardStatusReg::new(0), 91 | cid: cid, 92 | csd: CardSpecificData::new(0), 93 | rca: 1, 94 | storage: storage, 95 | cache: TinyCache::new(fill_cacheline, wb_cacheline), 96 | transfer: None 97 | } 98 | } 99 | 100 | pub fn make_transfer(&mut self, loc: TransferLoc, ttype: TransferType, num_blocks: u16) { 101 | let transfer = ActiveTransfer { 102 | loc: loc, 103 | ty: ttype, 104 | blocks_left: num_blocks, 105 | fifo_pos: 0, 106 | seek_pos: 0 107 | }; 108 | trace!("Initializing SDMMC transfer ({}): {:?}", if ttype == TransferType::Read { "read" } else { "write" }, transfer); 109 | self.transfer = Some(transfer); 110 | } 111 | 112 | pub fn get_transfer_mut<'a>(&'a mut self) -> Option<&'a mut ActiveTransfer> { 113 | self.transfer.as_mut() 114 | } 115 | 116 | pub fn kill_transfer(&mut self) { 117 | self.transfer = None; 118 | } 119 | 120 | pub fn set_state(&mut self, state: CardState) { 121 | self.csr.current_state.set(state as u32); 122 | } 123 | 124 | pub fn reset(&mut self, _spi: bool) { 125 | // TODO: stubbed 126 | } 127 | } 128 | 129 | impl io::Read for Card { 130 | fn read(&mut self, buf: &mut [u8]) -> Result { 131 | let xfer = self.transfer.as_mut() 132 | .ok_or(io::Error::new(io::ErrorKind::NotConnected, "No active transfer found"))?; 133 | let to_advance = match xfer.loc { 134 | TransferLoc::Storage => { 135 | let pos = xfer.seek_pos as u32; 136 | let read = self.cache.get_or(pos / CACHE_LINE_SIZE as u32, &mut self.storage); 137 | 138 | let read_start = (pos as usize) % CACHE_LINE_SIZE; 139 | let read_end = (read_start + buf.len()).min(CACHE_LINE_SIZE); 140 | let read_amount = read_end - read_start; 141 | 142 | buf[..read_amount].copy_from_slice(&read[read_start..read_end]); 143 | 144 | Ok(read_amount) 145 | }, 146 | TransferLoc::RegScr => { 147 | warn!("STUBBED: Read from SD card SCR register"); 148 | for b in buf.iter_mut() { *b = 0; } 149 | Ok(buf.len().checked_sub(xfer.seek_pos as usize).unwrap()) 150 | } 151 | TransferLoc::RegSsr => { 152 | warn!("STUBBED: Read from SD card SSR register"); 153 | for b in buf.iter_mut() { *b = 0; } 154 | Ok(buf.len().checked_sub(xfer.seek_pos as usize).unwrap()) 155 | } 156 | }; 157 | if let Ok(to_advance) = to_advance { 158 | xfer.seek_pos += to_advance as u64; 159 | } 160 | to_advance 161 | } 162 | } 163 | 164 | impl io::Write for Card { 165 | fn write(&mut self, buf: &[u8]) -> Result { 166 | let xfer = self.transfer.as_mut() 167 | .ok_or(io::Error::new(io::ErrorKind::NotConnected, "No active transfer found"))?; 168 | let to_advance = match xfer.loc { 169 | TransferLoc::Storage => { 170 | let pos = xfer.seek_pos as u32; 171 | 172 | let write_start = (pos as usize) % CACHE_LINE_SIZE; 173 | let write_end = (write_start + buf.len()).min(CACHE_LINE_SIZE); 174 | let write_amount = write_end - write_start; 175 | 176 | let updater = |_: u32, line: &mut [u8; CACHE_LINE_SIZE]| { 177 | line[write_start..write_end].copy_from_slice(&buf[..write_amount]); 178 | }; 179 | 180 | self.cache.update_or(pos / CACHE_LINE_SIZE as u32, updater, &mut self.storage); 181 | Ok(write_amount) 182 | } 183 | TransferLoc::RegScr => { 184 | Err(io::Error::new(io::ErrorKind::PermissionDenied, "Cannot write to SCR register")) 185 | } 186 | TransferLoc::RegSsr => { 187 | Err(io::Error::new(io::ErrorKind::PermissionDenied, "Cannot write to SSR register")) 188 | } 189 | }; 190 | if let Ok(to_advance) = to_advance { 191 | xfer.seek_pos += to_advance as u64; 192 | } 193 | to_advance 194 | } 195 | 196 | fn flush(&mut self) -> Result<(), io::Error> { 197 | let xfer = self.transfer.as_mut() 198 | .ok_or(io::Error::new(io::ErrorKind::NotConnected, "No active transfer found"))?; 199 | match xfer.loc { 200 | TransferLoc::Storage => Ok(self.cache.invalidate(&mut self.storage)), 201 | TransferLoc::RegScr => Ok(()), 202 | TransferLoc::RegSsr => Ok(()), 203 | } 204 | } 205 | } 206 | 207 | impl io::Seek for Card { 208 | fn seek(&mut self, seek_from: io::SeekFrom) -> Result { 209 | let xfer = self.transfer.as_mut() 210 | .ok_or(io::Error::new(io::ErrorKind::NotConnected, "No active transfer found"))?; 211 | let new_pos = match seek_from { 212 | io::SeekFrom::Current(v) => ((xfer.seek_pos as i64) + v as i64) as u64, 213 | io::SeekFrom::End(_v) => unimplemented!(), 214 | io::SeekFrom::Start(v) => v as u64, 215 | }; 216 | xfer.seek_pos = new_pos; 217 | Ok(new_pos) 218 | } 219 | } 220 | 221 | impl Drop for Card { 222 | fn drop(&mut self) { 223 | self.cache.invalidate(&mut self.storage); 224 | } 225 | } 226 | 227 | pub fn nand_cid() -> CardIdentReg::Bf { 228 | let mut file = fs::open_file(fs::LlamaFile::NandCid).unwrap(); 229 | let mut bytes = [0u8; 16]; 230 | file.read_exact(&mut bytes).unwrap(); 231 | CardIdentReg::new(bytes::to_u128(&bytes)) 232 | } 233 | 234 | pub fn sd_cid() -> CardIdentReg::Bf { 235 | CardIdentReg::new(0) 236 | } 237 | -------------------------------------------------------------------------------- /libllama/src/io/emmc/cmds.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Seek, SeekFrom}; 2 | 3 | use io::emmc::{self, EmmcDevice, Status1, Status32, TransferType}; 4 | use io::emmc::card::{CardState, TransferLoc}; 5 | 6 | pub fn go_idle_state(dev: &mut EmmcDevice) { 7 | for card in dev._internal_state.cards.iter_mut() { 8 | card.reset(false); 9 | } 10 | warn!("STUBBED: SDMMC CMD0 GO_IDLE_STATE!"); 11 | } 12 | 13 | pub fn send_op_cond(dev: &mut EmmcDevice) -> u32 { 14 | let ocr = emmc::get_params_u32(dev); 15 | emmc::get_active_card(dev).set_state(CardState::Ready); 16 | warn!("STUBBED: SDMMC CMD1 SEND_OP_COND!"); 17 | return ocr | (1 << 31); 18 | } 19 | 20 | pub fn all_send_cid(dev: &mut EmmcDevice) -> u128 { 21 | let cid = emmc::get_active_card(dev).cid; 22 | emmc::get_active_card(dev).set_state(CardState::Ident); 23 | return cid.val; 24 | } 25 | 26 | pub fn set_relative_addr(dev: &mut EmmcDevice) { 27 | let reladdr = emmc::get_params_u16(dev)[1]; 28 | emmc::get_active_card(dev).rca = reladdr; 29 | emmc::get_active_card(dev).set_state(CardState::Stby); 30 | } 31 | 32 | pub fn get_relative_addr(dev: &mut EmmcDevice) -> u16 { 33 | let rca = emmc::get_active_card(dev).rca + 1; 34 | emmc::get_active_card(dev).rca = rca; 35 | emmc::get_active_card(dev).set_state(CardState::Stby); 36 | rca 37 | } 38 | 39 | pub fn switch(_dev: &mut EmmcDevice) { 40 | warn!("STUBBED: SDMMC CMD6 SWITCH!"); 41 | } 42 | 43 | pub fn select_deselect_card(dev: &mut EmmcDevice) { 44 | emmc::get_active_card(dev).set_state(CardState::Tran); 45 | warn!("STUBBED: SDMMC CMD7 SELECT_DESELECT_CARD!"); 46 | } 47 | 48 | pub fn send_if_cond(dev: &mut EmmcDevice) -> u32 { 49 | let out = emmc::get_params_u32(dev); 50 | warn!("STUBBED: SDMMC CMD8 SEND_IF_COND!"); 51 | out 52 | } 53 | 54 | pub fn send_csd(dev: &mut EmmcDevice) -> u128 { 55 | let csd = emmc::get_active_card(dev).csd; 56 | emmc::get_active_card(dev).set_state(CardState::Ident); 57 | return csd.val; 58 | } 59 | 60 | pub fn stop_transmission(dev: &mut EmmcDevice) { 61 | emmc::get_active_card(dev).kill_transfer(); 62 | emmc::clear_status(dev, Status1::RxReady); 63 | emmc::clear_status(dev, Status1::TxRq); 64 | emmc::clear_status(dev, Status32::RxReady); 65 | emmc::clear_status(dev, Status32::_TxRq); 66 | warn!("STUBBED: SDMMC CMD12 STOP_TRANSMISSION!"); 67 | } 68 | 69 | pub fn set_blocklen(_dev: &mut EmmcDevice) { 70 | warn!("STUBBED: SDMMC CMD16 SET_BLOCKLEN!"); 71 | } 72 | 73 | pub fn prepare_multi_transfer(dev: &mut EmmcDevice, ttype: TransferType) { 74 | let file_offset = emmc::get_params_u32(&*dev); 75 | 76 | let block_count = if emmc::use_32bit(dev) { 77 | match ttype { 78 | TransferType::Read => emmc::trigger_status(dev, Status32::RxReady), 79 | TransferType::Write => { 80 | // TODO: no trigger_status? 81 | dev._internal_state.dma_out.trigger(); 82 | } 83 | } 84 | dev.data32_blk_cnt.get() 85 | } else { 86 | match ttype { 87 | TransferType::Read => emmc::trigger_status(dev, Status1::RxReady), 88 | TransferType::Write => emmc::trigger_status(dev, Status1::TxRq) 89 | } 90 | dev.data16_blk_cnt.get() 91 | }; 92 | 93 | let card = &mut emmc::get_active_card(dev); 94 | card.make_transfer(TransferLoc::Storage, ttype, block_count); 95 | card.seek(SeekFrom::Start(file_offset as u64)).unwrap(); 96 | trace!("Seeking SDMMC pointer to offset 0x{:08X}!", file_offset); 97 | } 98 | 99 | pub fn app_cmd(dev: &mut EmmcDevice) { 100 | emmc::get_active_card(dev).csr.app_cmd.set(1); 101 | } 102 | 103 | pub fn set_bus_width(_dev: &mut EmmcDevice) { 104 | warn!("STUBBED: SDMMC ACMD6 SET_BUS_WIDTH!"); 105 | } 106 | 107 | pub fn get_ssr(dev: &mut EmmcDevice) { 108 | warn!("STUBBED: SDMMC ACMD13 GET_SSR!"); 109 | assert!(dev.data16_blk_len.get() == 64); 110 | emmc::trigger_status(dev, Status1::RxReady); 111 | emmc::get_active_card(dev).make_transfer(TransferLoc::RegSsr, TransferType::Read, 1); 112 | } 113 | 114 | pub fn app_send_op_cond(dev: &mut EmmcDevice) -> u32 { 115 | let voltages = emmc::get_params_u32(dev) & 0xFFF; 116 | emmc::get_active_card(dev).set_state(CardState::Ready); 117 | warn!("STUBBED: SDMMC ACMD41 SD_SEND_OP_COND!"); 118 | return voltages | (1 << 31); 119 | } 120 | 121 | pub fn set_clr_card_detect(_dev: &mut EmmcDevice) { 122 | warn!("STUBBED: SDMMC ACMD42 SET_CLR_CARD_DETECT!"); 123 | } 124 | 125 | pub fn get_scr(dev: &mut EmmcDevice) { 126 | warn!("STUBBED: SDMMC ACMD51 GET_SCR!"); 127 | assert!(dev.data16_blk_len.get() == 8); 128 | emmc::trigger_status(dev, Status1::RxReady); 129 | emmc::get_active_card(dev).make_transfer(TransferLoc::RegScr, TransferType::Read, 1); 130 | } 131 | -------------------------------------------------------------------------------- /libllama/src/io/emmc/mode_sd.rs: -------------------------------------------------------------------------------- 1 | use io::emmc::{self, EmmcDevice, TransferType}; 2 | use io::emmc::card::{CardType}; 3 | use io::emmc::cmds; 4 | use utils::bytes; 5 | 6 | enum CmdHandler { 7 | R1(fn(&mut EmmcDevice) -> ()), 8 | R2(fn(&mut EmmcDevice) -> u128), 9 | R3(fn(&mut EmmcDevice) -> u32), 10 | R6(fn(&mut EmmcDevice) -> u16), 11 | R7(fn(&mut EmmcDevice) -> u32) 12 | } 13 | 14 | static CMDS: [(usize, CmdHandler, CardType); 16] = [ 15 | (0, CmdHandler::R1(cmds::go_idle_state), CardType::Sdmmc), 16 | (1, CmdHandler::R3(cmds::send_op_cond), CardType::Mmc), 17 | (2, CmdHandler::R2(cmds::all_send_cid), CardType::Sdmmc), 18 | (3, CmdHandler::R6(cmds::get_relative_addr), CardType::Sd), 19 | (3, CmdHandler::R1(cmds::set_relative_addr), CardType::Mmc), 20 | (6, CmdHandler::R1(cmds::switch), CardType::Sdmmc), 21 | (7, CmdHandler::R1(cmds::select_deselect_card), CardType::Sdmmc), 22 | (8, CmdHandler::R7(cmds::send_if_cond), CardType::Sd), 23 | (9, CmdHandler::R2(cmds::send_csd), CardType::Sdmmc), 24 | (10, CmdHandler::R2(cmds::all_send_cid), CardType::Sdmmc), 25 | (12, CmdHandler::R1(cmds::stop_transmission), CardType::Sdmmc), 26 | (13, CmdHandler::R1(|_| {}), CardType::Sdmmc), 27 | (16, CmdHandler::R1(cmds::set_blocklen), CardType::Sdmmc), 28 | // (17, CmdHandler::R1(cmds::read_single_block)), 29 | (18, CmdHandler::R1(|dev: &mut EmmcDevice| cmds::prepare_multi_transfer(dev, TransferType::Read)), CardType::Sdmmc), 30 | // (23, CmdHandler::R1(cmds::set_block_count)), 31 | // (24, CmdHandler::R1(cmds::write_block)), 32 | (25, CmdHandler::R1(|dev: &mut EmmcDevice| cmds::prepare_multi_transfer(dev, TransferType::Write)), CardType::Sdmmc), 33 | (55, CmdHandler::R1(cmds::app_cmd), CardType::Sd), 34 | // (58, CmdHandler::R3(cmds::read_ocr)) 35 | ]; 36 | 37 | static ACMDS: [(usize, CmdHandler, CardType); 5] = [ 38 | (6, CmdHandler::R1(cmds::set_bus_width), CardType::Sd), 39 | (13, CmdHandler::R1(cmds::get_ssr), CardType::Sd), 40 | (41, CmdHandler::R3(cmds::app_send_op_cond), CardType::Sd), 41 | (42, CmdHandler::R1(cmds::set_clr_card_detect), CardType::Sd), 42 | (51, CmdHandler::R1(cmds::get_scr), CardType::Sd), 43 | ]; 44 | 45 | #[inline] 46 | fn handle_any_cmd(dev: &mut EmmcDevice, cmdlist: &[(usize, CmdHandler, CardType)], cmd_index: u16) { 47 | let mut found_wrong_type = false; 48 | emmc::get_active_card(dev).csr.illegal_cmd.set(0); 49 | 50 | for &(i, ref handler, ty) in cmdlist.iter() { 51 | if i != cmd_index as usize { continue } 52 | 53 | let card_ty = emmc::get_active_card(dev).ty; 54 | match (card_ty, ty) { 55 | (CardType::Sd, CardType::Mmc) | (CardType::Mmc, CardType::Sd) => { 56 | found_wrong_type = true; 57 | continue 58 | } 59 | (CardType::Sdmmc, _) => panic!("Found card with illegal joint type `CardType::Sdmmc`"), 60 | _ => {} 61 | } 62 | 63 | match handler { 64 | &CmdHandler::R1(f) => { 65 | f(dev); 66 | let csr = emmc::get_active_card(dev).csr; 67 | emmc::push_resp_u32(dev, csr.val); 68 | } 69 | &CmdHandler::R2(f) => { 70 | let data = f(dev); 71 | emmc::set_resp_u8(dev, &bytes::from_u128(data)); 72 | } 73 | &CmdHandler::R3(f) | &CmdHandler::R7(f) => { 74 | let data = f(dev); 75 | emmc::push_resp_u32(dev, data); 76 | } 77 | &CmdHandler::R6(f) => { 78 | let data = f(dev); 79 | let csr = emmc::get_active_card(dev).csr.val; 80 | let data32 = (data as u32) << 16 81 | | (((csr >> 22) & 0b11) << 14) 82 | | (((csr >> 19) & 0b1) << 13) 83 | | (csr & 0b1111111111111); 84 | emmc::push_resp_u32(dev, data32); 85 | } 86 | } 87 | return 88 | } 89 | 90 | if found_wrong_type { 91 | emmc::get_active_card(dev).csr.illegal_cmd.set(1); 92 | warn!("Tried to run illegal SDMMC (APP_?')CMD{}", cmd_index); 93 | emmc::trigger_status(dev, emmc::Status1::CmdTimeout); 94 | } else { 95 | panic!("UNIMPLEMENTED: SDMMC (APP_?')CMD{}", cmd_index) 96 | } 97 | } 98 | 99 | pub fn handle_cmd(dev: &mut EmmcDevice, cmd_index: u16) { 100 | handle_any_cmd(dev, &CMDS, cmd_index); 101 | } 102 | 103 | pub fn handle_acmd(dev: &mut EmmcDevice, cmd_index: u16) { 104 | handle_any_cmd(dev, &ACMDS, cmd_index); 105 | } 106 | -------------------------------------------------------------------------------- /libllama/src/io/hid.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Copy)] 2 | pub enum Button { 3 | A = 0, 4 | B = 1, 5 | Select = 2, 6 | Start = 3, 7 | Right = 4, 8 | Left = 5, 9 | Up = 6, 10 | Down = 7, 11 | R = 8, 12 | L = 9, 13 | X = 10, 14 | Y = 11 15 | } 16 | 17 | #[derive(Clone, Copy)] 18 | pub enum ButtonState { 19 | Pressed(Button), 20 | Released(Button) 21 | } 22 | 23 | pub fn update_pad(dev: &mut HidDevice, change: ButtonState) { 24 | let mut current_pad = dev.pad.get(); 25 | match change { 26 | ButtonState::Pressed(b) => current_pad &= !(1 << b as u32), 27 | ButtonState::Released(b) => current_pad |= 1 << b as u32 28 | } 29 | dev.pad.set_unchecked(current_pad); 30 | } 31 | 32 | pub fn pad(dev: &mut HidDevice) -> u16 { 33 | !dev.pad.get() 34 | } 35 | 36 | iodevice!(HidDevice, { 37 | regs: { 38 | 0x000 => pad: u16 { 39 | default = !0; 40 | write_bits = 0; 41 | } 42 | 0x002 => unk: u16 { 43 | read_effect = |_| trace!("STUBBED: Read from unknown HID+0x2 register!"); 44 | write_effect = |_| warn!("STUBBED: Write to unknown HID+0x2 register!"); 45 | } 46 | } 47 | }); 48 | -------------------------------------------------------------------------------- /libllama/src/io/i2c.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::fmt; 3 | 4 | 5 | fn mcu_power_write(dat: &mut u8) { 6 | if *dat & 0b111 != 0 { 7 | panic!("Powering off!"); 8 | } 9 | } 10 | 11 | 12 | 13 | 14 | struct PeriphMCU { 15 | regs: HashMap 16 | } 17 | 18 | impl PeriphMCU { 19 | fn new() -> Self { 20 | let mut regs: HashMap = HashMap::new(); 21 | 22 | regs.insert(0x00, McuReg::new(0x00, NOP, NOP)); // Stubbed: Version high 23 | regs.insert(0x01, McuReg::new(0x00, NOP, NOP)); // Stubbed: Version low 24 | regs.insert(0x09, McuReg::new(0x00, NOP, NOP)); // Stubbed: Volume slider 25 | regs.insert(0x0B, McuReg::new(0xFF, NOP, NOP)); // Stubbed: Battery charge 26 | regs.insert(0x0F, McuReg::new(0x00, NOP, NOP)); // Stubbed: Port statuses 27 | regs.insert(0x10, McuReg::new(0x00, NOP, NOP)); // Stubbed: IRQs b0 28 | regs.insert(0x11, McuReg::new(0x00, NOP, NOP)); // Stubbed: IRQs b1 29 | regs.insert(0x12, McuReg::new(0x00, NOP, NOP)); // Stubbed: IRQs b2 30 | regs.insert(0x13, McuReg::new(0x00, NOP, NOP)); // Stubbed: IRQs b3 31 | regs.insert(0x18, McuReg::new(0x00, NOP, NOP)); // Stubbed: IRQ mask b0 32 | regs.insert(0x19, McuReg::new(0x00, NOP, NOP)); // Stubbed: IRQ mask b1 33 | regs.insert(0x1A, McuReg::new(0x00, NOP, NOP)); // Stubbed: IRQ mask b2 34 | regs.insert(0x1B, McuReg::new(0x00, NOP, NOP)); // Stubbed: IRQ mask b3 35 | regs.insert(0x20, McuReg::new(0x00, NOP, mcu_power_write)); 36 | regs.insert(0x22, McuReg::new(0x00, NOP, NOP)); // Stubbed: LCD backlights 37 | regs.insert(0x30, McuReg::new(0x00, NOP, NOP)); // Stubbed: Realtime clock seconds 38 | regs.insert(0x31, McuReg::new(0x00, NOP, NOP)); // Stubbed: Realtime clock minutes 39 | regs.insert(0x32, McuReg::new(0x00, NOP, NOP)); // Stubbed: Realtime clock hours 40 | regs.insert(0x33, McuReg::new(0x00, NOP, NOP)); // Stubbed: Realtime clock week 41 | regs.insert(0x34, McuReg::new(0x01, NOP, NOP)); // Stubbed: Realtime clock days 42 | regs.insert(0x35, McuReg::new(0x01, NOP, NOP)); // Stubbed: Realtime clock months 43 | regs.insert(0x36, McuReg::new(0x20, NOP, NOP)); // Stubbed: Realtime clock years 44 | regs.insert(0x37, McuReg::new(0x00, NOP, NOP)); // Stubbed: Realtime clock leapyear counter 45 | 46 | Self { 47 | regs 48 | } 49 | } 50 | } 51 | 52 | impl Peripheral for PeriphMCU { 53 | fn read(&mut self, register: u8) -> Option { 54 | if let Some(reg) = self.regs.get_mut(®ister) { 55 | (reg.read_effect)(&mut reg.dat); 56 | return Some(reg.dat); 57 | } 58 | panic!("Unimplemented MCU read at register {:02X}", register); 59 | //None 60 | } 61 | fn write(&mut self, register: u8, dat: u8) -> bool { 62 | if let Some(reg) = self.regs.get_mut(®ister) { 63 | reg.dat = dat; 64 | (reg.write_effect)(&mut reg.dat); 65 | return true; 66 | } 67 | panic!("Unimplemented MCU write at register {:02X}", register); 68 | //false 69 | } 70 | } 71 | 72 | 73 | struct PeriphLCD; 74 | impl PeriphLCD { 75 | fn new() -> Self { Self } 76 | } 77 | 78 | impl Peripheral for PeriphLCD { 79 | fn read(&mut self, register: u8) -> Option { 80 | warn!("STUBBED: I2C LCD read at register {:02X}", register); 81 | Some(0) 82 | } 83 | fn write(&mut self, register: u8, dat: u8) -> bool { 84 | warn!("STUBBED: I2C LCD write {:02X} at register {:02X}", dat, register); 85 | true 86 | } 87 | } 88 | 89 | 90 | struct McuReg { 91 | dat: u8, 92 | read_effect: fn(&mut u8), 93 | write_effect: fn(&mut u8) 94 | } 95 | 96 | const NOP: fn(&mut u8) = |_: &mut u8| {}; 97 | 98 | impl McuReg { 99 | fn new(dat: u8, read_effect: fn(&mut u8), write_effect: fn(&mut u8)) -> Self { 100 | Self { 101 | dat, 102 | read_effect, 103 | write_effect 104 | } 105 | } 106 | } 107 | 108 | 109 | 110 | trait Peripheral { 111 | fn read(&mut self, register: u8) -> Option; 112 | fn write(&mut self, register: u8, dat: u8) -> bool; 113 | } 114 | 115 | pub struct I2cPeripherals { 116 | periphs: HashMap> 117 | } 118 | 119 | impl fmt::Debug for I2cPeripherals { 120 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 121 | write!(f, "I2cPeripherals {{ }}") 122 | } 123 | } 124 | 125 | impl I2cPeripherals { 126 | fn read(&mut self, device: u8, register: u8) -> Option { 127 | if let Some(p) = self.periphs.get_mut(&device) { 128 | p.read(register) 129 | } else { 130 | panic!("Unimplemented I2C read for peripheral at 0x{:02X}", device); 131 | } 132 | } 133 | 134 | fn write(&mut self, device: u8, register: u8, dat: u8) -> bool { 135 | if let Some(p) = self.periphs.get_mut(&device) { 136 | p.write(register, dat) 137 | } else { 138 | panic!("Unimplemented I2C write for peripheral at 0x{:02X}", device); 139 | } 140 | } 141 | } 142 | 143 | 144 | pub fn make_peripherals() -> I2cPeripherals { 145 | let mut periphs: HashMap> = HashMap::new(); 146 | 147 | periphs.insert(0x2c, Box::new(PeriphLCD::new())); 148 | periphs.insert(0x2e, Box::new(PeriphLCD::new())); 149 | periphs.insert(0x4a, Box::new(PeriphMCU::new())); 150 | 151 | I2cPeripherals { 152 | periphs 153 | } 154 | } 155 | 156 | 157 | 158 | 159 | bf!(RegCnt[u8] { 160 | stop: 0:0, 161 | start: 1:1, 162 | pause: 2:2, 163 | ack: 4:4, 164 | is_read: 5:5, 165 | enable_interrupt: 6:6, 166 | busy: 7:7 167 | }); 168 | 169 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 170 | enum I2cByteExpected { 171 | DeviceSelect, 172 | RegisterSelect, 173 | DataWrite, 174 | DataRead 175 | } 176 | 177 | impl Default for I2cByteExpected { 178 | fn default() -> Self { 179 | I2cByteExpected::DeviceSelect 180 | } 181 | } 182 | 183 | #[derive(Debug)] 184 | pub struct I2cDeviceState { 185 | device: u8, 186 | register: u8, 187 | next_input: I2cByteExpected, 188 | 189 | periphs: I2cPeripherals 190 | } 191 | 192 | impl I2cDeviceState { 193 | pub fn new(periphs: I2cPeripherals) -> Self { 194 | Self { 195 | device: 0, 196 | register: 0, 197 | next_input: I2cByteExpected::DeviceSelect, 198 | 199 | periphs 200 | } 201 | } 202 | } 203 | 204 | 205 | fn advance_state_machine(dev: &mut I2cDevice) -> bool { 206 | let byte = dev.data.get(); 207 | let state = &mut dev._internal_state; 208 | let next_input = match state.next_input { 209 | I2cByteExpected::DeviceSelect => { 210 | state.device = byte & 0xFE; 211 | 212 | trace!("Selected I2C device 0x{:02X}", byte); 213 | if byte & 1 == 0 { 214 | I2cByteExpected::RegisterSelect 215 | } else { 216 | I2cByteExpected::DataRead 217 | } 218 | } 219 | I2cByteExpected::RegisterSelect => { 220 | state.register = byte; 221 | trace!("Selected I2C register 0x{:02X}", byte); 222 | I2cByteExpected::DataWrite 223 | } 224 | I2cByteExpected::DataWrite => { 225 | let ok = state.periphs.write(state.device, state.register, byte); 226 | trace!("Wrote I2C data to dev 0x{:02X} reg 0x{:02X}: 0x{:02X}", state.device, state.register, byte); 227 | 228 | if !ok { 229 | return false; 230 | } 231 | state.register += 1; 232 | I2cByteExpected::DataWrite 233 | } 234 | I2cByteExpected::DataRead => { 235 | let byte = state.periphs.read(state.device, state.register); 236 | trace!("Read I2C data from dev 0x{:02X} reg 0x{:02X}", state.device, state.register); 237 | if let Some(byte) = byte { 238 | dev.data.set(byte); 239 | } else { 240 | return false; 241 | } 242 | state.register += 1; 243 | I2cByteExpected::DataRead 244 | } 245 | }; 246 | state.next_input = next_input; 247 | return true; 248 | } 249 | 250 | iodevice!(I2cDevice, { 251 | internal_state: I2cDeviceState; 252 | regs: { 253 | 0x000 => data: u8 { 254 | write_effect = |dev: &I2cDevice| { 255 | trace!("I2C write to DATA: 0x{:02X}", dev.data.get()); 256 | }; 257 | } 258 | 0x001 => cnt: u8 { 259 | read_effect = |_| {}; 260 | write_effect = |dev: &mut I2cDevice| { 261 | let mut cnt = RegCnt::new(dev.cnt.get()); 262 | trace!("I2C write to CNT: 0x{:02X}, {:?}", dev.cnt.get(), cnt); 263 | if cnt.start.get() == 1 { 264 | dev._internal_state.next_input = I2cByteExpected::DeviceSelect; 265 | } 266 | if cnt.busy.get() == 1 { 267 | if cnt.is_read.get() == 1 { 268 | assert!(dev._internal_state.next_input == I2cByteExpected::DataRead); 269 | } 270 | let res = advance_state_machine(dev); 271 | cnt.ack.set(res as u8); 272 | cnt.busy.set(0); 273 | } 274 | dev.cnt.set_unchecked(cnt.val); 275 | }; 276 | } 277 | 0x002 => cntex: u16 { 278 | write_effect = |_| warn!("STUBBED: Write to I2C CNTEX register"); 279 | } 280 | 0x004 => scl: u16 { 281 | write_effect = |_| warn!("STUBBED: Write to I2C SCL register"); 282 | } 283 | } 284 | }); 285 | -------------------------------------------------------------------------------- /libllama/src/io/irq.rs: -------------------------------------------------------------------------------- 1 | use cpu::irq::Aggregator; 2 | 3 | iodevice!(IrqDevice, { 4 | internal_state: Aggregator; 5 | regs: { 6 | 0x000 => enabled: u32 { 7 | write_bits = 0b00111111_11111111_11111111_11111111; 8 | write_effect = |dev: &mut IrqDevice| { 9 | let state = &mut dev._internal_state; 10 | state.set_enabled(dev.enabled.get() as u128); 11 | }; 12 | } 13 | 0x004 => pending: u32 { 14 | write_bits = 0b00111111_11111111_11111111_11111111; 15 | read_effect = |dev: &mut IrqDevice| { 16 | let state = &mut dev._internal_state; 17 | dev.pending.set_unchecked(state.drain_asserts() as u32); 18 | }; 19 | write_effect = |dev: &mut IrqDevice| { 20 | let state = &mut dev._internal_state; 21 | let new_pending = state.acknowledge(dev.pending.get() as u128); 22 | dev.pending.set_unchecked(new_pending as u32); 23 | }; 24 | } 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /libllama/src/io/ndma.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::collections::HashMap; 3 | use std::rc::Rc; 4 | use std::fmt; 5 | 6 | use hwcore::HardwareDma9; 7 | use io::{DmaBus, DmaBuses}; 8 | 9 | bf!(RegGlobalCnt[u32] { 10 | enabled: 0:0, 11 | cycle_select: 16:19, 12 | round_robin: 31:31 13 | }); 14 | 15 | bf!(RegChannelCnt[u32] { 16 | startup_mode_ext: 0:4, 17 | dst_addr_writeback_mode: 10:11, 18 | _dst_addr_reload: 12:12, 19 | src_addr_writeback_mode: 13:14, 20 | _src_addr_reload: 15:15, 21 | xfer_size: 16:19, 22 | startup_mode: 24:27, 23 | immed_mode: 28:28, 24 | repeat_mode: 29:29, 25 | _enable_irq: 30:30, 26 | enabled: 31:31 27 | }); 28 | 29 | #[derive(Clone, Debug)] 30 | pub struct NdmaChannelState { 31 | connections: Rc>, 32 | started: bool, 33 | src_addr: u32, 34 | dst_addr: u32, 35 | xfer_total: u32, 36 | } 37 | 38 | fn startup_mode(cnt: &RegChannelCnt::Bf) -> u32 { 39 | cnt.startup_mode.get() | (cnt.startup_mode_ext.get() << 4) 40 | } 41 | 42 | fn should_xfer(dev: &mut NdmaChannel) -> bool { 43 | let chan_cnt = RegChannelCnt::new(dev.chan_cnt.get()); 44 | if chan_cnt.enabled.get() == 0 { 45 | return false; 46 | } 47 | let startup_dev = startup_mode(&chan_cnt); 48 | let mut xns = dev._internal_state.connections.borrow_mut(); 49 | 50 | let mode = (chan_cnt.immed_mode.get(), chan_cnt.repeat_mode.get()); 51 | match mode { 52 | (0, 0) => { 53 | let bus = xns.buses.get_mut(&startup_dev) 54 | .expect(&format!("Could not find NDMA bus for device 0x{:X}", startup_dev)); 55 | bus.observe() 56 | } 57 | (1, 0) => { 58 | // Immediate mode 59 | true 60 | } 61 | (0, 1) => { 62 | unimplemented!() 63 | } 64 | _ => panic!("Attempted to use impossible channel mode immed+repeat") 65 | } 66 | } 67 | 68 | fn process_channel(dev: &mut NdmaChannel) { 69 | if !should_xfer(dev) { 70 | let chan_cnt = RegChannelCnt::alias_mut(dev.chan_cnt.ref_mut()); 71 | chan_cnt.enabled.set(0); 72 | return; 73 | } 74 | 75 | let xns = dev._internal_state.connections.borrow_mut(); 76 | let mut hw = xns.hw.borrow_mut(); 77 | 78 | let chan_cnt = RegChannelCnt::new(dev.chan_cnt.get()); 79 | 80 | let line_size = 1u32 << chan_cnt.xfer_size.get(); 81 | let src_addr = &mut dev._internal_state.src_addr; 82 | let dst_addr = &mut dev._internal_state.dst_addr; 83 | let total_words = dev.write_cnt.get() & 0xFFFFFF; 84 | 85 | let mut tmp_buf = vec![0u8; 4 * line_size as usize]; 86 | let word_fill = dev.fill_data.get().to_le_bytes(); 87 | 88 | assert!(*src_addr % 4 == 0); 89 | assert!(*dst_addr % 4 == 0); 90 | assert!(total_words % line_size == 0); 91 | 92 | let src_wb_mode = chan_cnt.src_addr_writeback_mode.get(); 93 | let dst_wb_mode = chan_cnt.dst_addr_writeback_mode.get(); 94 | 95 | for _burst in 0..(total_words / line_size) { 96 | 97 | info!("Processing NDMA 0x{:X}-word burst to address {:08X}!", line_size, dst_addr); 98 | 99 | if src_wb_mode == 3 { 100 | info!("NDMA using fill source {:08X}", dev.fill_data.get()); 101 | 102 | // Constant data fill 103 | for word in tmp_buf.chunks_exact_mut(4) { 104 | word.copy_from_slice(&word_fill); 105 | } 106 | } else { 107 | info!("NDMA using address source {:08X}", dev.fill_data.get()); 108 | 109 | hw.mem.read_buf(*src_addr, &mut tmp_buf[..]); 110 | 111 | *src_addr = match src_wb_mode { 112 | 0 => *src_addr + 4 * line_size, 113 | 1 => *src_addr - 4 * line_size, 114 | 2 => *src_addr, 115 | _ => unreachable!() 116 | }; 117 | } 118 | 119 | hw.mem.write_buf(*dst_addr, &mut tmp_buf[..]); 120 | 121 | *dst_addr = match dst_wb_mode { 122 | 0 => *dst_addr + 4 * line_size, 123 | 1 => *dst_addr - 4 * line_size, 124 | 2 => *dst_addr, 125 | _ => unreachable!() 126 | }; 127 | } 128 | 129 | dev._internal_state.xfer_total += total_words; 130 | 131 | let chan_cnt = RegChannelCnt::alias_mut(dev.chan_cnt.ref_mut()); 132 | let mode = (chan_cnt.immed_mode.get(), chan_cnt.repeat_mode.get()); 133 | match mode { 134 | (0, 0) => { 135 | // Suspend mode 136 | let xfer_total = dev._internal_state.xfer_total; 137 | let xfer_max = dev.xfer_max.get(); 138 | assert!(xfer_total <= xfer_max); 139 | if xfer_total == xfer_max { 140 | chan_cnt.enabled.set(0); 141 | } 142 | } 143 | (1, 0) => { 144 | // Immediate mode 145 | chan_cnt.enabled.set(0); 146 | } 147 | (0, 1) => { 148 | unimplemented!() 149 | } 150 | _ => panic!("Attempted to use impossible channel mode immed+repeat") 151 | } 152 | } 153 | 154 | 155 | fn reg_chan_cnt_write(dev: &mut NdmaChannel) { 156 | let chan_cnt = RegChannelCnt::alias_mut(dev.chan_cnt.ref_mut()); 157 | let state = &mut dev._internal_state; 158 | 159 | if !state.started && chan_cnt.enabled.get() == 1 { 160 | // Just starting transfer now, copy data down. 161 | state.src_addr = dev.src_addr.get(); 162 | state.dst_addr = dev.dst_addr.get(); 163 | state.xfer_total = 0; 164 | } 165 | state.started = chan_cnt.enabled.get() == 1; 166 | } 167 | 168 | 169 | iodevice!(NdmaChannel, { 170 | internal_state: NdmaChannelState; 171 | regs: { 172 | 0x004 => src_addr: u32 { } 173 | 0x008 => dst_addr: u32 { } 174 | 0x00C => xfer_max: u32 { 175 | write_bits = 0x0FFFFFFF; 176 | } 177 | 0x010 => write_cnt: u32 { 178 | write_bits = 0x00FFFFFF; 179 | } 180 | 0x014 => block_cnt: u32 { } 181 | 0x018 => fill_data: u32 { } 182 | 0x01C => chan_cnt: u32 { 183 | write_effect = reg_chan_cnt_write; 184 | } 185 | } 186 | }); 187 | 188 | #[derive(Clone)] 189 | pub struct NdmaConnections { 190 | hw: Rc>, 191 | buses: HashMap 192 | } 193 | 194 | impl fmt::Debug for NdmaConnections { 195 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 196 | write!(f, "NdmaConnections {{ }}") 197 | } 198 | } 199 | 200 | #[derive(Debug)] 201 | pub struct NdmaDeviceState { 202 | channels: [NdmaChannel; 8], 203 | } 204 | 205 | impl NdmaDeviceState { 206 | pub fn new(hw: Rc>, buses: DmaBuses) -> Self { 207 | let mut bus_map = HashMap::new(); 208 | bus_map.insert(0x8, buses.aes_in.clone()); 209 | bus_map.insert(0x9, buses.aes_out.clone()); 210 | bus_map.insert(0xA, buses.sha_in.clone()); 211 | bus_map.insert(0xB, buses.sha_out.clone()); 212 | 213 | let sdmmc_to_aes= buses.sdmmc_out.clone().union_with(buses.aes_in.clone()); 214 | let aes_to_sha = buses.aes_out.clone().union_with(buses.sha_in.clone()); 215 | bus_map.insert(0x8F, sdmmc_to_aes); 216 | bus_map.insert(0x10F, aes_to_sha); 217 | 218 | let chan_state = NdmaChannelState { 219 | connections: Rc::new(RefCell::new( 220 | NdmaConnections { 221 | hw, buses: bus_map 222 | } 223 | )), 224 | started: false, 225 | src_addr: 0, 226 | dst_addr: 0, 227 | xfer_total: 0 228 | }; 229 | 230 | Self { 231 | channels: [ 232 | NdmaChannel::new(chan_state.clone()), NdmaChannel::new(chan_state.clone()), 233 | NdmaChannel::new(chan_state.clone()), NdmaChannel::new(chan_state.clone()), 234 | NdmaChannel::new(chan_state.clone()), NdmaChannel::new(chan_state.clone()), 235 | NdmaChannel::new(chan_state.clone()), NdmaChannel::new(chan_state.clone()), 236 | ], 237 | } 238 | } 239 | } 240 | 241 | pub fn schedule(dev: &mut NdmaDevice) { 242 | for channel in dev._internal_state.channels.iter_mut() { 243 | process_channel(channel); 244 | } 245 | } 246 | 247 | iodevice!(NdmaDevice, { 248 | internal_state: NdmaDeviceState; 249 | regs: { 250 | 0x000 => global_cnt: u32 { } 251 | } 252 | ranges: { 253 | 0x004;0xE0 => { 254 | // Remap addresses for individual channel registers 255 | read_effect = |dev: &mut NdmaDevice, buf_pos: usize, dest: &mut [u8]| { 256 | let channel = buf_pos / 0x1C; 257 | let new_buf_pos = buf_pos % 0x1C + 4; // As if the pos was for channel 0 258 | dev._internal_state.channels[channel].read_reg(new_buf_pos, dest); 259 | }; 260 | write_effect = |dev: &mut NdmaDevice, buf_pos: usize, src: &[u8]| { 261 | let channel = buf_pos / 0x1C; 262 | let new_buf_pos = buf_pos % 0x1C + 4; // As if the pos was for channel 0 263 | dev._internal_state.channels[channel].write_reg(new_buf_pos, src); 264 | }; 265 | } 266 | } 267 | }); 268 | -------------------------------------------------------------------------------- /libllama/src/io/otp.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::io::Read; 3 | 4 | use fs; 5 | 6 | pub struct OtpDeviceState { 7 | otp: [u8; 0x100] 8 | } 9 | 10 | impl fmt::Debug for OtpDeviceState { 11 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 12 | write!(f, "OtpDeviceState {{ }}") 13 | } 14 | } 15 | 16 | impl Default for OtpDeviceState { 17 | fn default() -> OtpDeviceState { 18 | let mut file = fs::open_file(fs::LlamaFile::Otp).unwrap(); 19 | let mut otp = [0u8; 0x100]; 20 | file.read_exact(&mut otp[..]) 21 | .expect(&format!("Failed to read 256 bytes from OTP file!")); 22 | 23 | OtpDeviceState { 24 | otp: otp 25 | } 26 | } 27 | } 28 | 29 | fn reg_otp_write(dev: &mut OtpDevice, buf_pos: usize, source: &[u8]) { 30 | dev._internal_state.otp[buf_pos .. buf_pos + source.len()].copy_from_slice(source); 31 | } 32 | 33 | fn reg_otp_read(dev: &mut OtpDevice, buf_pos: usize, dest: &mut [u8]) { 34 | let src_slice = &dev._internal_state.otp[buf_pos .. buf_pos + dest.len()]; 35 | dest.clone_from_slice(src_slice); 36 | } 37 | 38 | iodevice!(OtpDevice, { 39 | internal_state: OtpDeviceState; 40 | regs: { 41 | 0x100 => twl_id0: u32 {} 42 | 0x104 => twl_id1: u32 {} 43 | } 44 | ranges: { 45 | 0x000;0x100 => { 46 | read_effect = reg_otp_read; 47 | write_effect = reg_otp_write; 48 | } 49 | } 50 | }); -------------------------------------------------------------------------------- /libllama/src/io/priv11.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | use std::cell::RefCell; 3 | 4 | use cpu::irq::Aggregator; 5 | 6 | iodevice!(Priv11Device, { 7 | internal_state: Rc>; 8 | regs: { 9 | 0x000 => scu_ctrl: u32 { 10 | default = 0x00001FFE; 11 | write_effect = |_| warn!("STUBBED: Write to ARM11 SCU ctrl register!"); 12 | } 13 | 0x00C => scu_invalidate_all: u32 {} 14 | 0x100 => interrupt_ctrl: u32 { 15 | write_effect = |_| warn!("STUBBED: Write to ARM11 Interrupt ctrl register!"); 16 | } 17 | 0x104 => interrupt_prio_mask: u32 { 18 | write_effect = |_| warn!("STUBBED: Write to ARM11 Interrupt priority mask register!"); 19 | } 20 | 0x108 => interrupt_preemptable: u32 { 21 | write_effect = |_| warn!("STUBBED: Write to ARM11 Interrupt binary point register!"); 22 | } 23 | 0x10C => interrupt_ack: u32 { 24 | default = 1023; 25 | write_bits = 0; 26 | read_effect = |dev: &mut Priv11Device| { 27 | let mut next_interrupt = dev._internal_state.borrow_mut() 28 | .drain_asserts().trailing_zeros(); 29 | if next_interrupt == 128 { 30 | next_interrupt = 1023; 31 | } 32 | dev.interrupt_ack.set_unchecked(next_interrupt); 33 | warn!("STUBBED: Read from ARM11 Acknowledge Interrupt register!") 34 | }; 35 | } 36 | 0x110 => interrupt_end: u32 { 37 | write_effect = |dev: &mut Priv11Device| { 38 | let which = dev.interrupt_end.get(); 39 | let which_mask = 1u128 << (which as usize); 40 | dev._internal_state.borrow_mut().acknowledge(which_mask); 41 | warn!("STUBBED: Write {} to ARM11 Interrupt end register!", which); 42 | }; 43 | } 44 | 0x118 => interrupt_highest_pending: u32 { 45 | default = 1023; 46 | write_bits = 0; 47 | read_effect = |dev: &mut Priv11Device| { 48 | let mut next_interrupt = dev._internal_state.borrow_mut() 49 | .drain_asserts().trailing_zeros(); 50 | if next_interrupt == 128 { 51 | next_interrupt = 1023; 52 | } 53 | dev.interrupt_highest_pending.set_unchecked(next_interrupt); 54 | warn!("STUBBED: Read {} from ARM11 Highest Pending Interrupt register!", next_interrupt) 55 | }; 56 | } 57 | 58 | 0x600 => timer_load: u32 { 59 | write_effect = |dev: &mut Priv11Device| { 60 | warn!("STUBBED: Write {:08X} to ARM11 Timer load register!", dev.timer_load.get()); 61 | }; 62 | } 63 | 0x604 => timer_counter: u32 { 64 | read_effect = |dev: &mut Priv11Device| { 65 | warn!("STUBBED: Read {:08X} from ARM11 Timer counter register!", dev.timer_counter.get()); 66 | }; 67 | write_effect = |dev: &mut Priv11Device| { 68 | warn!("STUBBED: Write {:08X} to ARM11 Timer counter register!", dev.timer_counter.get()); 69 | }; 70 | } 71 | 0x608 => timer_ctrl: u32 { 72 | write_effect = |dev: &mut Priv11Device| { 73 | warn!("STUBBED: Write {:08X} to ARM11 Timer control register!", dev.timer_ctrl.get()); 74 | }; 75 | } 76 | 0x60C => timer_interrupt_stat: u32 { 77 | write_effect = |dev: &mut Priv11Device| { 78 | warn!("STUBBED: Write {:08X} to ARM11 Timer interrupt status register!", dev.timer_interrupt_stat.get()); 79 | }; 80 | } 81 | 0x628 => wdg_ctrl: u32 { 82 | write_effect = |dev: &mut Priv11Device| { 83 | warn!("STUBBED: Write {:08X} to ARM11 Watchdog control register!", dev.wdg_ctrl.get()); 84 | }; 85 | } 86 | 0x62C => wdg_interrupt_stat: u32 { 87 | write_effect = |dev: &mut Priv11Device| { 88 | warn!("STUBBED: Write {:08X} to ARM11 Watchdog interrupt status register!", dev.wdg_interrupt_stat.get()); 89 | }; 90 | } 91 | 0x630 => wdg_reset_sent: u32 { 92 | write_effect = |dev: &mut Priv11Device| { 93 | warn!("STUBBED: Write {:08X} to ARM11 Watchdog reset sent register!", dev.wdg_reset_sent.get()); 94 | }; 95 | } 96 | 0x634 => wdg_disable: u32 { 97 | write_effect = |dev: &mut Priv11Device| { 98 | warn!("STUBBED: Write {:08X} to ARM11 Watchdog disable register!", dev.wdg_disable.get()); 99 | }; 100 | } 101 | } 102 | }); 103 | 104 | #[derive(Debug)] 105 | pub struct GidState { 106 | agg: Rc>, 107 | enabled: [u32; 4], 108 | } 109 | 110 | impl GidState { 111 | pub fn new(agg: Rc>) -> Self { 112 | Self { 113 | agg, 114 | enabled: [0;4], 115 | } 116 | } 117 | } 118 | 119 | fn update_enabled(dev: &mut GidDevice) { 120 | let enabled = { 121 | let enabled = &dev._internal_state.enabled; 122 | ((enabled[3] as u128) << 96) 123 | | ((enabled[2] as u128) << 64) 124 | | ((enabled[1] as u128) << 32) 125 | | (enabled[0] as u128) 126 | }; 127 | dev._internal_state.agg.borrow().set_enabled(enabled); 128 | } 129 | 130 | macro_rules! enable_setX { 131 | ($reg:ident, $i:expr) => (|dev: &mut GidDevice| { 132 | dev._internal_state.enabled[$i] |= dev.$reg.get(); 133 | dev.$reg.set_unchecked(0); 134 | update_enabled(dev); 135 | }) 136 | } 137 | 138 | macro_rules! enable_clrX { 139 | ($reg:ident, $i:expr) => (|dev: &mut GidDevice| { 140 | dev._internal_state.enabled[$i] &= !dev.$reg.get(); 141 | dev.$reg.set_unchecked(0); 142 | update_enabled(dev); 143 | }) 144 | } 145 | 146 | macro_rules! pending_clrX { 147 | ($reg:ident, $i:expr) => (|dev: &mut GidDevice| { 148 | let which = (dev.$reg.get() as u128) << ($i * 32); 149 | dev._internal_state.agg.borrow_mut().acknowledge(which); 150 | }) 151 | } 152 | 153 | macro_rules! pending_setX { 154 | ($reg:ident, $i:expr) => (|dev: &mut GidDevice| { 155 | let which = (dev.$reg.get() as u128) << ($i * 32); 156 | warn!("STUBBED: Force set ARM11 interrupts with val {:X}", which); 157 | }) 158 | } 159 | 160 | macro_rules! pending_getX { 161 | ($reg:ident, $i:expr) => (|dev: &mut GidDevice| { 162 | let asserted = dev._internal_state.agg 163 | .borrow_mut().drain_asserts(); 164 | dev.$reg.set_unchecked((asserted >> ($i * 32)) as u32); 165 | }) 166 | } 167 | 168 | 169 | iodevice!(GidDevice, { 170 | internal_state: GidState; 171 | regs: { 172 | 0x000 => dist_ctrl: u32 { 173 | write_effect = |_| warn!("STUBBED: Write to ARM11 Interrupt Distributor ctrl register!"); 174 | } 175 | 176 | 0x100 => enable_set0: u32 { 177 | write_effect = enable_setX!(enable_set0, 0); 178 | } 179 | 0x104 => enable_set1: u32 { 180 | write_effect = enable_setX!(enable_set1, 1); 181 | } 182 | 0x108 => enable_set2: u32 { 183 | write_effect = enable_setX!(enable_set2, 2); 184 | } 185 | 0x10C => enable_set3: u32 { 186 | write_effect = enable_setX!(enable_set3, 3); 187 | } 188 | 189 | 0x180 => enable_clr0: u32 { 190 | write_effect = enable_clrX!(enable_clr0, 0); 191 | } 192 | 0x184 => enable_clr1: u32 { 193 | write_effect = enable_clrX!(enable_clr1, 1); 194 | } 195 | 0x188 => enable_clr2: u32 { 196 | write_effect = enable_clrX!(enable_clr2, 2); 197 | } 198 | 0x18C => enable_clr3: u32 { 199 | write_effect = enable_clrX!(enable_clr3, 3); 200 | } 201 | 202 | 0x200 => pending_set0: u32 { 203 | read_effect = pending_getX!(pending_set0, 0); 204 | write_effect = pending_setX!(pending_set0, 0); 205 | } 206 | 0x204 => pending_set1: u32 { 207 | read_effect = pending_getX!(pending_set1, 1); 208 | write_effect = pending_setX!(pending_set1, 1); 209 | } 210 | 0x208 => pending_set2: u32 { 211 | read_effect = pending_getX!(pending_set2, 2); 212 | write_effect = pending_setX!(pending_set2, 2); 213 | } 214 | 0x20C => pending_set3: u32 { 215 | read_effect = pending_getX!(pending_set3, 3); 216 | write_effect = pending_setX!(pending_set3, 3); 217 | } 218 | 219 | 0x280 => pending_clr0: u32 { 220 | read_effect = pending_getX!(pending_clr0, 0); 221 | write_effect = pending_clrX!(pending_clr0, 0); 222 | } 223 | 0x284 => pending_clr1: u32 { 224 | read_effect = pending_getX!(pending_clr1, 1); 225 | write_effect = pending_clrX!(pending_clr1, 1); 226 | } 227 | 0x288 => pending_clr2: u32 { 228 | read_effect = pending_getX!(pending_clr2, 2); 229 | write_effect = pending_clrX!(pending_clr2, 2); 230 | } 231 | 0x28C => pending_clr3: u32 { 232 | read_effect = pending_getX!(pending_clr3, 3); 233 | write_effect = pending_clrX!(pending_clr3, 3); 234 | } 235 | } 236 | ranges: { 237 | 0x400;0x100 => { 238 | write_effect = |_, _buf_pos, _src| { 239 | warn!("STUBBED: Set ARM11 Interrupt Priority"); 240 | }; 241 | } 242 | 0x800;0x100 => { 243 | write_effect = |_, _buf_pos, _src| { 244 | warn!("STUBBED: Set ARM11 Interrupt Target"); 245 | }; 246 | } 247 | 0xC00;0x100 => { 248 | write_effect = |_, _buf_pos, _src| { 249 | warn!("STUBBED: Set ARM11 Interrupt Configuration"); 250 | }; 251 | } 252 | } 253 | }); 254 | -------------------------------------------------------------------------------- /libllama/src/io/pxi.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, atomic, mpsc}; 2 | use std::fmt; 3 | 4 | use cpu::irq::{self, IrqClient}; 5 | 6 | bf!(RegCnt[u16] { 7 | send_empty: 0:0, 8 | send_full: 1:1, 9 | flush_send: 3:3, 10 | recv_empty: 8:8, 11 | recv_full: 9:9, 12 | cannot_rw: 14:14 13 | }); 14 | 15 | bf!(RegSyncCtrl[u8] { 16 | trigger_irq11: 5:5, 17 | trigger_irq9: 6:6, 18 | irq_enabled: 7:7 19 | }); 20 | 21 | fn reg_sync_read(dev: &mut PxiDevice) { 22 | let byte = dev._internal_state.sync_rx.load(atomic::Ordering::SeqCst) as u8; 23 | trace!("Read {:X} from PXI_SYNC", byte); 24 | dev.sync_recv.set_unchecked(byte); 25 | } 26 | 27 | fn reg_sync_write(dev: &mut PxiDevice) { 28 | let byte = dev.sync_send.get(); 29 | trace!("Wrote {:X} to PXI_SYNC", byte); 30 | dev._internal_state.sync_tx.store(byte as usize, atomic::Ordering::SeqCst); 31 | } 32 | 33 | fn reg_cnt_read(dev: &mut PxiDevice) { 34 | let cnt = RegCnt::alias_mut(dev.cnt.ref_mut()); 35 | let tx_count = dev._internal_state.tx_count.load(atomic::Ordering::SeqCst); 36 | let rx_count = dev._internal_state.rx_count.load(atomic::Ordering::SeqCst); 37 | cnt.send_empty.set((tx_count == 0) as u16); 38 | cnt.send_full.set((tx_count == 4) as u16); 39 | cnt.recv_empty.set((rx_count == 0) as u16); 40 | cnt.recv_full.set((rx_count == 4) as u16); 41 | } 42 | 43 | fn reg_cnt_write(dev: &mut PxiDevice) { 44 | let cnt = RegCnt::alias_mut(dev.cnt.ref_mut()); 45 | if (cnt.flush_send.get() == 1) { 46 | warn!("STUBBED: cannot flush PXI tx channel!"); 47 | cnt.flush_send.set(0); 48 | } 49 | if (cnt.cannot_rw.get() == 1) { 50 | cnt.cannot_rw.set(0); 51 | } 52 | warn!("STUBBED: Write to PXI_CNT"); 53 | } 54 | 55 | #[derive(Eq, PartialEq, Copy, Clone, Debug)] 56 | enum PxiEnd { 57 | Arm9, 58 | Arm11 59 | } 60 | 61 | pub struct PxiShared { 62 | end: PxiEnd, 63 | 64 | tx_count: Arc, 65 | rx_count: Arc, 66 | tx: mpsc::SyncSender, 67 | rx: mpsc::Receiver, 68 | sync_tx: Arc, 69 | sync_rx: Arc, 70 | 71 | irq_enabled: Arc, 72 | other_irq_enabled: Arc, 73 | irq_client: irq::IrqAsyncClient, 74 | } 75 | 76 | impl PxiShared { 77 | pub fn make_channel(irq9: irq::IrqAsyncClient, irq11: irq::IrqAsyncClient) -> (PxiShared, PxiShared) { 78 | let count_1rx_2tx = Arc::new(atomic::AtomicUsize::new(0)); 79 | let count_2rx_1tx = Arc::new(atomic::AtomicUsize::new(0)); 80 | let sync_1rx_2tx = Arc::new(atomic::AtomicUsize::new(0)); 81 | let sync_2rx_1tx = Arc::new(atomic::AtomicUsize::new(0)); 82 | let (pxi1_tx, pxi2_rx) = mpsc::sync_channel(4); 83 | let (pxi2_tx, pxi1_rx) = mpsc::sync_channel(4); 84 | let irq_1enabled = Arc::new(atomic::AtomicBool::new(false)); 85 | let irq_2enabled = Arc::new(atomic::AtomicBool::new(false)); 86 | 87 | let pxi11 = PxiShared { 88 | end: PxiEnd::Arm11, 89 | 90 | tx_count: count_2rx_1tx.clone(), 91 | rx_count: count_1rx_2tx.clone(), 92 | tx: pxi1_tx, 93 | rx: pxi1_rx, 94 | sync_tx: sync_2rx_1tx.clone(), 95 | sync_rx: sync_1rx_2tx.clone(), 96 | 97 | irq_enabled: irq_1enabled.clone(), 98 | other_irq_enabled: irq_2enabled.clone(), 99 | irq_client: irq9, 100 | }; 101 | 102 | let pxi9 = PxiShared { 103 | end: PxiEnd::Arm9, 104 | 105 | tx_count: count_1rx_2tx, 106 | rx_count: count_2rx_1tx, 107 | tx: pxi2_tx, 108 | rx: pxi2_rx, 109 | sync_tx: sync_1rx_2tx, 110 | sync_rx: sync_2rx_1tx, 111 | 112 | irq_enabled: irq_2enabled, 113 | other_irq_enabled: irq_1enabled, 114 | irq_client: irq11, 115 | }; 116 | (pxi9, pxi11) 117 | } 118 | } 119 | 120 | impl fmt::Debug for PxiShared { 121 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 122 | f.debug_struct("PxiShared") 123 | .field("end", &self.end) 124 | .field("tx_count", &self.tx_count) 125 | .field("rx_count", &self.rx_count) 126 | .field("tx", &self.tx) 127 | .field("rx", &self.rx) 128 | .field("sync_tx", &self.sync_tx) 129 | .field("sync_rx", &self.sync_rx) 130 | .field("irq_enabled", &self.irq_enabled) 131 | .finish() 132 | } 133 | } 134 | 135 | fn reg_sync_ctrl_write(dev: &mut PxiDevice) { 136 | let cntl = RegSyncCtrl::alias_mut(dev.sync_ctrl.ref_mut()); 137 | let irq_enabled = cntl.irq_enabled.get() == 1; 138 | 139 | let state = &mut dev._internal_state; 140 | 141 | if cntl.trigger_irq9.get() == 1 { 142 | // Assume for now we're only targeting opposite PXI end 143 | assert!(state.end == PxiEnd::Arm11); 144 | if state.other_irq_enabled.load(atomic::Ordering::Relaxed) { 145 | info!("Triggering PXI sync on ARM9"); 146 | state.irq_client.assert(irq::IrqType9::PxiSync); 147 | } 148 | } 149 | if cntl.trigger_irq11.get() == 1 { 150 | // Assume for now we're only targeting opposite PXI end 151 | assert!(state.end == PxiEnd::Arm9); 152 | if state.other_irq_enabled.load(atomic::Ordering::Relaxed) { 153 | info!("Triggering PXI sync on ARM11"); 154 | state.irq_client.assert(irq::IrqType11::PxiSync); 155 | } 156 | } 157 | 158 | cntl.trigger_irq9.set(0); 159 | cntl.trigger_irq11.set(0); 160 | 161 | state.irq_enabled.store(irq_enabled, atomic::Ordering::Relaxed); 162 | } 163 | 164 | iodevice!(PxiDevice, { 165 | internal_state: PxiShared; 166 | regs: { 167 | 0x000 => sync_recv: u8 { 168 | // write_bits = 0xFFFFFF00; 169 | read_effect = reg_sync_read; 170 | } 171 | 0x001 => sync_send: u8 { 172 | write_effect = reg_sync_write; 173 | } 174 | 0x002 => sync_unk: u8 { 175 | write_effect = |_| trace!("STUBBED: Write to PXI unknown sync ctrl register!"); 176 | } 177 | 0x003 => sync_ctrl: u8 { 178 | write_effect = reg_sync_ctrl_write; 179 | } 180 | 0x004 => cnt: u16 { 181 | write_bits = 0b11000100_00001100; 182 | read_effect = reg_cnt_read; 183 | write_effect = reg_cnt_write; 184 | } 185 | 0x006 => cnt_ext: u16 { } 186 | 0x008 => send: u32 { 187 | write_effect = |dev: &mut PxiDevice| { 188 | let dat = dev.send.get(); 189 | match dev._internal_state.tx.try_send(dat) { 190 | Ok(_) => dev._internal_state.tx_count.fetch_add(1, atomic::Ordering::SeqCst), 191 | Err(mpsc::TrySendError::Full(_)) => panic!("Attempted to send PXI word while FIFO full"), 192 | Err(e) => panic!("{:?}", e), 193 | } 194 | }; 195 | } 196 | 0x00C => recv: u32 { 197 | read_effect = |dev: &mut PxiDevice| { 198 | let dat = match dev._internal_state.rx.try_recv() { 199 | Ok(dat) => { 200 | dev._internal_state.rx_count.fetch_sub(1, atomic::Ordering::SeqCst); 201 | dat 202 | } 203 | Err(mpsc::TryRecvError::Empty) => { 204 | debug!("Attempted to receive PXI word while FIFO empty"); 205 | return 206 | } 207 | Err(e) => panic!("{:?}", e), 208 | }; 209 | dev.recv.set_unchecked(dat); 210 | }; 211 | } 212 | } 213 | }); 214 | -------------------------------------------------------------------------------- /libllama/src/io/regs.rs: -------------------------------------------------------------------------------- 1 | use std::ops::BitAnd; 2 | use std::ops::BitAndAssign; 3 | use std::ops::BitOrAssign; 4 | use std::ops::Not; 5 | 6 | use utils::bytes; 7 | 8 | #[derive(Debug)] 9 | pub struct IoReg 10 | where T: Copy + BitAnd + BitAndAssign 11 | + BitOrAssign + Not { 12 | val: T, 13 | write_bits: T, 14 | } 15 | impl IoReg 16 | where T: Copy + BitAnd + BitAndAssign 17 | + BitOrAssign + Not { 18 | 19 | pub fn new(val: T, write_bits: T) -> IoReg { 20 | IoReg { val: val, write_bits: write_bits } 21 | } 22 | pub fn set(&mut self, new_val: T) { 23 | self.val &= !self.write_bits; 24 | self.val |= new_val & self.write_bits; 25 | } 26 | pub fn _bitadd_unchecked(&mut self, bits: T) { 27 | self.val |= bits; 28 | } 29 | pub fn _bitclr_unchecked(&mut self, bits: T) { 30 | self.val &= !bits; 31 | } 32 | pub fn set_unchecked(&mut self, new_val: T) { 33 | self.val = new_val; 34 | } 35 | pub fn get(&self) -> T { 36 | self.val 37 | } 38 | pub fn ref_mut<'a>(&'a mut self) -> &'a mut T { 39 | &mut self.val 40 | } 41 | 42 | pub fn mem_load(&self, buf: &mut [u8]) { 43 | let data = unsafe { bytes::from_val(&self.val) }; 44 | buf.copy_from_slice(data); 45 | } 46 | 47 | pub fn mem_save(&mut self, buf: &[u8]) { 48 | let data = unsafe { bytes::from_mut_val(&mut self.val) }; 49 | data.copy_from_slice(buf); 50 | } 51 | } 52 | 53 | pub trait IoRegAccess { 54 | fn read_reg(&mut self, offset: usize, buf: &mut [u8]); 55 | fn write_reg(&mut self, offset: usize, buf: &[u8]); 56 | } 57 | 58 | 59 | macro_rules! __iodevice__ { 60 | ($name:ident, { 61 | $(internal_state: $instate:path;)* 62 | regs: {$( 63 | $reg_offs:expr => $reg_name:ident: $reg_ty:ty { 64 | default = $reg_default:expr; 65 | write_bits = $reg_wb:expr; 66 | read_effect = $reg_reff:expr; 67 | write_effect = $reg_weff:expr; 68 | } 69 | )*} 70 | ranges: {$( 71 | $range_offs:expr;$range_size:expr => { 72 | read_effect = $range_reff:expr; 73 | write_effect = $range_weff:expr; 74 | } 75 | )*} 76 | }) => ( 77 | #[derive(Debug)] 78 | pub struct $name { 79 | $( $reg_name: $crate::io::regs::IoReg<$reg_ty>, )* 80 | $(_internal_state: $instate,)* 81 | } 82 | 83 | impl $name { 84 | #[allow(dead_code)] 85 | pub fn new($(_internal_state: $instate)*) -> $name { 86 | $name { 87 | $( $reg_name: $crate::io::regs::IoReg::new($reg_default, $reg_wb), )* 88 | $(_internal_state: { let val: $instate = _internal_state; val }, )* 89 | } 90 | } 91 | } 92 | 93 | impl $crate::io::regs::IoRegAccess for $name { 94 | fn read_reg(&mut self, offset: usize, buf: &mut [u8]) { 95 | #![allow(unused_comparisons)] 96 | trace!("Reading from {} at +0x{:X}", stringify!($name), offset); 97 | let buf_size = buf.len(); 98 | match offset { 99 | $( $reg_offs => { 100 | let reg_size = ::std::mem::size_of::<$reg_ty>(); 101 | assert!(buf_size % reg_size == 0); 102 | 103 | $reg_reff(&mut *self); 104 | self.$reg_name.mem_load(&mut buf[..reg_size]); 105 | if buf_size - reg_size > 0 { 106 | // Keep going 107 | trace!("{} byte read from {}+{:X} greater than reg size {}; including next register.", 108 | buf_size, stringify!($name), offset, reg_size); 109 | self.read_reg(offset + reg_size, &mut buf[reg_size..]); 110 | } 111 | })* 112 | 113 | $( _ if offset >= $range_offs && offset < $range_offs+$range_size => { 114 | assert!(offset + buf_size <= $range_offs + $range_size); 115 | $range_reff(&mut *self, offset-$range_offs, buf); 116 | })* 117 | 118 | o @ _ => panic!("Unhandled {} register read: {} bytes @ 0x{:X}", stringify!($name), buf_size, o) 119 | } 120 | } 121 | 122 | fn write_reg(&mut self, offset: usize, buf: &[u8]) { 123 | #![allow(unused_comparisons)] 124 | trace!("Writing to {} at +0x{:X}", stringify!($name), offset); 125 | let buf_size = buf.len(); 126 | match offset { 127 | $( $reg_offs => { 128 | let reg_size = ::std::mem::size_of::<$reg_ty>(); 129 | assert!(buf_size % reg_size == 0); 130 | 131 | self.$reg_name.mem_save(&buf[..reg_size]); 132 | $reg_weff(&mut *self); 133 | if buf_size - reg_size > 0 { 134 | // Keep going 135 | trace!("{} byte write to {}+{:X} greater than reg size {}; including next register.", 136 | buf_size, stringify!($name), offset, reg_size); 137 | self.write_reg(offset + reg_size, &buf[reg_size..]); 138 | } 139 | })* 140 | 141 | $( _ if offset >= $range_offs && offset < $range_offs+$range_size => { 142 | assert!(offset + buf_size <= $range_offs + $range_size); 143 | $range_weff(&mut *self, offset-$range_offs, buf); 144 | })* 145 | 146 | o @ _ => panic!("Unhandled {} register write: {} bytes @ 0x{:X}", stringify!($name), buf_size, o) 147 | } 148 | } 149 | } 150 | ) 151 | } 152 | 153 | macro_rules! __iodevice_desc_default__ { 154 | ($val:expr) => ($val); 155 | () => (0); 156 | } 157 | 158 | macro_rules! __iodevice_desc_wb__ { 159 | ($val:expr) => ($val); 160 | () => (!0); 161 | } 162 | 163 | macro_rules! __iodevice_desc_reg_eff__ { 164 | ($val:expr) => ($val); 165 | () => (|_|{}); 166 | } 167 | 168 | macro_rules! __iodevice_desc_range_eff__ { 169 | ($val:expr) => ($val); 170 | () => (|_, _, _|{}); 171 | } 172 | 173 | #[macro_export] 174 | macro_rules! iodevice { 175 | ($name:ident, { 176 | $(internal_state: $instate:path;)* 177 | regs: {$( 178 | $reg_offs:expr => $reg_name:ident: $reg_ty:ty { 179 | $(default = $reg_default:expr;)* 180 | $(write_bits = $reg_wb:expr;)* 181 | $(read_effect = $reg_reff:expr;)* 182 | $(write_effect = $reg_weff:expr;)* 183 | } 184 | )*} 185 | $(ranges: {$( 186 | $range_offs:expr;$range_size:expr => { 187 | $(read_effect = $range_reff:expr;)* 188 | $(write_effect = $range_weff:expr;)* 189 | } 190 | )*})* 191 | }) => ( 192 | __iodevice__!($name, { 193 | $(internal_state: $instate;)* 194 | regs: {$( 195 | $reg_offs => $reg_name: $reg_ty { 196 | default = __iodevice_desc_default__!($($reg_default),*); 197 | write_bits = __iodevice_desc_wb__!($($reg_wb),*); 198 | read_effect = __iodevice_desc_reg_eff__!($($reg_reff),*); 199 | write_effect = __iodevice_desc_reg_eff__!($($reg_weff),*); 200 | } 201 | )*} 202 | ranges: {$($( 203 | $range_offs;$range_size => { 204 | read_effect = __iodevice_desc_range_eff__!($($range_reff),*); 205 | write_effect = __iodevice_desc_range_eff__!($($range_weff),*); 206 | } 207 | )*)*} 208 | }); 209 | ); 210 | } 211 | 212 | 213 | #[cfg(test)] 214 | mod test { 215 | use super::*; 216 | 217 | iodevice!(MMCRegs, { 218 | regs: { 219 | 0x000 => reg0: u16 { } 220 | 0x002 => reg2: u16 { 221 | write_effect = |_dev| { panic!("while writing") }; 222 | } 223 | 0x004 => reg4: u16 { write_bits = 0; } 224 | } 225 | }); 226 | 227 | #[test] 228 | fn read_reg() { 229 | let mut mmc_regs = MMCRegs::new(); 230 | let mut buf = vec![0xFFu8; 2]; 231 | mmc_regs.read_reg(0x000, buf.as_mut_slice()); 232 | assert_eq!(buf, vec![0x00, 0x00]); 233 | } 234 | 235 | #[test] 236 | fn write_reg() { 237 | let mut mmc_regs = MMCRegs::new(); 238 | assert_eq!(mmc_regs.reg0.get(), 0x0000); 239 | 240 | let buf = vec![0xFFu8; 2]; 241 | mmc_regs.write_reg(0x000, buf.as_slice()); 242 | assert_eq!(mmc_regs.reg0.get(), 0xFFFF); 243 | } 244 | 245 | #[test] 246 | #[should_panic] 247 | fn write_effect() { 248 | let mut mmc_regs = MMCRegs::new(); 249 | let buf = vec![0xFFu8; 2]; 250 | mmc_regs.write_reg(0x002, buf.as_slice()); 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /libllama/src/io/sha.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use openssl::hash::{Hasher, MessageDigest}; 4 | 5 | use io::DmaTrigger; 6 | 7 | bf!(RegCnt[u32] { 8 | busy: 0:0, 9 | final_round: 1:1, 10 | _enable_irq0: 2:2, 11 | big_endian: 3:3, 12 | hash_mode: 4:5, 13 | clear_fifo: 8:8, 14 | _enable_fifo: 9:9, 15 | _enable_irq1: 10:10 16 | }); 17 | 18 | pub struct ShaDeviceState { 19 | hasher: Option, 20 | hash: [u8; 32], 21 | _dma_in: DmaTrigger, 22 | dma_out: DmaTrigger, 23 | } 24 | 25 | impl ShaDeviceState { 26 | pub fn new(dma_in: DmaTrigger, dma_out: DmaTrigger) -> Self { 27 | Self { 28 | hasher: None, 29 | hash: Default::default(), 30 | _dma_in: dma_in, 31 | dma_out 32 | } 33 | } 34 | } 35 | 36 | impl fmt::Debug for ShaDeviceState { 37 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 38 | write!(f, "ShaDeviceState {{ }}") 39 | } 40 | } 41 | 42 | // TODO: The following implementation does not yet completely work, and still needs 43 | // more hardware testing to determine the source of errors. 44 | 45 | fn reg_cnt_update(dev: &mut ShaDevice) { 46 | let cnt = RegCnt::alias_mut(dev.cnt.ref_mut()); 47 | let state = &mut dev._internal_state; 48 | trace!("Wrote 0x{:08X} to SHA CNT register!", cnt.val); 49 | trace!("SHA hasher state: {}", if state.hasher.is_some() { "active" } else { "inactive" }); 50 | if cnt.val & 0xFFFF00C0 != 0 { 51 | warn!("Wrote UNIMPLEMENTED bits 0x{:08X} to SHA CNT register!", cnt.val); 52 | } 53 | 54 | if cnt.final_round.get() == 1 { 55 | trace!("Reached end of SHA final round!"); 56 | if let Some(ref mut h) = state.hasher { 57 | let hash_slice = &*h.finish().unwrap(); 58 | state.hash = [0u8; 32]; 59 | trace!("SHA output hash: {:X?}", hash_slice); 60 | state.hash[0..hash_slice.len()].copy_from_slice(hash_slice); 61 | } 62 | 63 | state.dma_out.trigger(); 64 | cnt.final_round.set(0); 65 | } 66 | 67 | if cnt.busy.get() == 0 { 68 | state.hasher = None; 69 | } else if state.hasher.is_none() { 70 | // Create new hasher 71 | trace!("Starting SHA hasher"); 72 | 73 | let mode = match cnt.hash_mode.get() { 74 | 0b00 => MessageDigest::sha256(), 75 | 0b01 => MessageDigest::sha224(), 76 | _ => MessageDigest::sha1() 77 | }; 78 | state.hasher = Some(Hasher::new(mode).unwrap()); 79 | } 80 | 81 | cnt.busy.set(0); 82 | } 83 | 84 | fn reg_hash_read(dev: &mut ShaDevice, buf_pos: usize, dest: &mut [u8]) { 85 | let src_slice = &dev._internal_state.hash[buf_pos .. buf_pos + dest.len()]; 86 | dest.copy_from_slice(src_slice); 87 | trace!("Reading {} bytes from SHA HASH at +0x{:X}: {:X?}", dest.len(), buf_pos, dest); 88 | 89 | let cnt = RegCnt::new(dev.cnt.get()); 90 | assert_eq!(cnt.big_endian.get(), 1); 91 | } 92 | 93 | // TODO: Does a word written to any part of the hash just add on? What about 94 | // writing to the fifo in reverse? How does this work? 95 | fn reg_fifo_write(dev: &mut ShaDevice, buf_pos: usize, source: &[u8]) { 96 | trace!("Writing {} bytes to SHA FIFO at +0x{:X}: {:X?}", source.len(), buf_pos, source); 97 | 98 | let cnt = RegCnt::new(dev.cnt.get()); 99 | assert_eq!(cnt.big_endian.get(), 1); 100 | 101 | let hasher = match dev._internal_state.hasher { 102 | Some(ref mut h) => h, 103 | None => return 104 | }; 105 | 106 | hasher.update(source).unwrap(); 107 | } 108 | 109 | iodevice!(ShaDevice, { 110 | internal_state: ShaDeviceState; 111 | regs: { 112 | 0x000 => cnt: u32 { write_effect = reg_cnt_update; } 113 | 0x004 => blk_cnt: u32 { 114 | read_effect = |_| { 115 | warn!("STUBBED: read from SHA BLK_CNT register"); 116 | }; 117 | write_effect = |dev: &ShaDevice| { 118 | warn!("STUBBED: Write to SHA BLK_CNT register: 0x{:X}", dev.blk_cnt.get()); 119 | }; 120 | } 121 | } 122 | ranges: { 123 | 0x040;0x20 => { 124 | read_effect = reg_hash_read; 125 | write_effect = |_, _, _| unimplemented!(); 126 | } 127 | 0x080;0x40 => { 128 | read_effect = |_, _, buf: &mut [u8]| { 129 | // Boot9 XDMAs with this register as a source, into DSP memory. 130 | // Might reading from this register be a way to clear the FIFO? 131 | for b in buf { 132 | *b = 0; 133 | } 134 | warn!("STUBBED: read from SHA FIFO register"); 135 | }; 136 | write_effect = reg_fifo_write; 137 | } 138 | } 139 | }); 140 | 141 | -------------------------------------------------------------------------------- /libllama/src/ldr/ctr9.rs: -------------------------------------------------------------------------------- 1 | extern crate json; 2 | 3 | use std::fs::File; 4 | use std::io::Read; 5 | use std::path::{Path, PathBuf}; 6 | 7 | use ldr; 8 | use mem; 9 | use utils; 10 | 11 | #[derive(Debug, Error)] 12 | pub enum ErrorKind { 13 | #[error(non_std, no_from, msg_embedded)] 14 | JsonItemError(String), 15 | Io(::std::io::Error), 16 | Json(json::Error), 17 | } 18 | 19 | fn item_error(item: &str, filename: &str) -> ErrorKind { 20 | ErrorKind::JsonItemError(format!("invalid or missing item `{}` in file {}", item, filename)) 21 | } 22 | 23 | const DESC_FILENAME: &'static str = "desc.json"; 24 | 25 | pub struct Ctr9Loader { 26 | path: PathBuf, 27 | desc: Desc, 28 | } 29 | 30 | impl Ctr9Loader { 31 | pub fn from_folder(path: &Path) -> Result { 32 | let json = Ctr9Loader::load_desc_json(&path)?; 33 | 34 | Ok(Ctr9Loader { 35 | path: path.to_path_buf(), 36 | desc: Desc::from_json(&json)?, 37 | }) 38 | } 39 | 40 | fn load_desc_json(path: &Path) -> Result { 41 | let mut desc = File::open(path.join(DESC_FILENAME))?; 42 | let mut desc_str = String::new(); 43 | desc.read_to_string(&mut desc_str)?; 44 | Ok(json::parse(&desc_str)?) 45 | } 46 | } 47 | 48 | fn load_binfile(binfile: &DescBinfile, path: &Path, controller: &mut mem::MemController) { 49 | let mut file = File::open(path.join(&binfile.bin)).unwrap(); 50 | let mut vaddr = binfile.vaddr; 51 | 52 | let mut read_buf = [0u8; 1024]; 53 | loop { 54 | let size = file.read(&mut read_buf).unwrap(); 55 | if size == 0 { break; } 56 | controller.write_buf(vaddr, &read_buf[0..size]); 57 | vaddr += size as u32; 58 | } 59 | } 60 | 61 | impl ldr::Loader for Ctr9Loader { 62 | fn entrypoint9(&self) -> u32 { 63 | self.desc.entrypoint 64 | } 65 | 66 | fn entrypoint11(&self) -> u32 { 67 | self.desc.entry11 68 | } 69 | 70 | fn load9(&self, controller: &mut mem::MemController) { 71 | for binfile in self.desc.binfiles.iter() { 72 | load_binfile(binfile, &self.path, controller); 73 | } 74 | } 75 | 76 | fn load11(&self, controller: &mut mem::MemController) { 77 | for binfile in self.desc.binfiles11.iter() { 78 | load_binfile(binfile, &self.path, controller); 79 | } 80 | } 81 | } 82 | 83 | 84 | struct Desc { 85 | entrypoint: u32, 86 | entry11: u32, 87 | binfiles: Vec, 88 | binfiles11: Vec, 89 | } 90 | 91 | impl Desc { 92 | fn from_json(json: &json::JsonValue) -> Result { 93 | let entrypoint_str = json["entryPoint"].as_str() 94 | .ok_or(item_error("entryPoint", DESC_FILENAME)); 95 | let entrypoint = utils::from_hex(entrypoint_str?) 96 | .or(Err(item_error("entryPoint", DESC_FILENAME))); 97 | 98 | let entrypoint11_str = json["entryPoint11"].as_str() 99 | .ok_or(item_error("entryPoint11", DESC_FILENAME)); 100 | let entrypoint11 = utils::from_hex(entrypoint11_str?) 101 | .or(Err(item_error("entryPoint11", DESC_FILENAME))); 102 | 103 | // Load binfiles arrays into vec, make sure >1 binfile exists 104 | let mut binfiles = Vec::new(); 105 | for binfile in json["binFiles"].members() { 106 | binfiles.push(DescBinfile::from_json(binfile)?); 107 | } 108 | //if binfiles.len() == 0 { 109 | // bail!(ErrorKind::JsonItemError("binfiles[]".to_owned(), DESC_FILENAME.to_owned())) 110 | //} 111 | 112 | let mut binfiles11 = Vec::new(); 113 | for binfile in json["binFiles11"].members() { 114 | binfiles11.push(DescBinfile::from_json(binfile)?); 115 | } 116 | 117 | Ok(Desc { 118 | entrypoint: entrypoint?, 119 | entry11: entrypoint11?, 120 | binfiles: binfiles, 121 | binfiles11: binfiles11, 122 | }) 123 | } 124 | } 125 | 126 | struct DescBinfile { 127 | bin: String, 128 | vaddr: u32, 129 | } 130 | 131 | impl DescBinfile { 132 | fn from_json(json: &json::JsonValue) -> Result { 133 | let bin = json["bin"].as_str() 134 | .ok_or(item_error("binfiles[].bin", DESC_FILENAME)); 135 | let vaddr_str = json["vAddr"].as_str() 136 | .ok_or(item_error("binfiles[].vAddr", DESC_FILENAME)); 137 | let vaddr = utils::from_hex(vaddr_str?) 138 | .or(Err(item_error("binfiles[].vAddr", DESC_FILENAME))); 139 | 140 | Ok(DescBinfile { 141 | bin: bin?.to_owned(), 142 | vaddr: vaddr?, 143 | }) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /libllama/src/ldr/firm.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::{Read, Seek, SeekFrom}; 3 | use std::path::{Path, PathBuf}; 4 | 5 | use ldr; 6 | use mem; 7 | use utils::bytes; 8 | 9 | #[derive(Debug, Error)] 10 | pub enum ErrorKind { 11 | Io(::std::io::Error) 12 | } 13 | 14 | struct FirmSection { 15 | data_offs: usize, 16 | size: usize, 17 | dst_addr: u32, 18 | } 19 | 20 | impl FirmSection { 21 | fn from_header(header: &[u8; 0x30]) -> Option { 22 | let offs: u32 = unsafe { bytes::val_at_offs(header, 0x0) }; 23 | let dst: u32 = unsafe { bytes::val_at_offs(header, 0x4) }; 24 | let size: u32 = unsafe { bytes::val_at_offs(header, 0x8) }; 25 | if size == 0 { 26 | None 27 | } else { 28 | Some(FirmSection { 29 | data_offs: offs as usize, 30 | size: size as usize, 31 | dst_addr: dst 32 | }) 33 | } 34 | } 35 | } 36 | 37 | pub struct FirmLoader { 38 | filename: PathBuf, 39 | entry9: u32, 40 | entry11: u32, 41 | sections: Vec 42 | } 43 | 44 | impl FirmLoader { 45 | pub fn from_file(filename: &Path) -> Result { 46 | let mut file = File::open(filename)?; 47 | let mut header = [0u8; 0x200]; 48 | file.read_exact(&mut header)?; 49 | 50 | assert!(&header[..4] == b"FIRM"); 51 | 52 | let entry11: u32 = unsafe { bytes::val_at_offs(&header, 0x8) }; 53 | let entry9: u32 = unsafe { bytes::val_at_offs(&header, 0xC) }; 54 | let mut sections = Vec::new(); 55 | 56 | for i in 0..4 { 57 | let section: [u8; 0x30] = unsafe { bytes::val_at_offs(&header, 0x40 + 0x30*i) }; 58 | if let Some(section) = FirmSection::from_header(§ion) { 59 | sections.push(section); 60 | } 61 | } 62 | 63 | Ok(FirmLoader { 64 | filename: filename.to_owned(), 65 | entry9, 66 | entry11, 67 | sections 68 | }) 69 | } 70 | } 71 | 72 | impl ldr::Loader for FirmLoader { 73 | fn entrypoint9(&self) -> u32 { 74 | self.entry9 75 | } 76 | fn load9(&self, controller: &mut mem::MemController) { 77 | let mut file = File::open(&self.filename).unwrap(); 78 | 79 | for section in &self.sections { 80 | file.seek(SeekFrom::Start(section.data_offs as u64)).unwrap(); 81 | 82 | let mut vaddr = section.dst_addr; 83 | let mut read_amount = 0; 84 | let mut read_buf = [0u8; 1024]; 85 | loop { 86 | let read_buf_size = 1024.min(section.size - read_amount); 87 | let read_buf = &mut read_buf[..read_buf_size]; 88 | if read_buf_size == 0 { break } 89 | file.read_exact(read_buf).unwrap(); 90 | controller.write_buf(vaddr, read_buf); 91 | 92 | read_amount += read_buf_size; 93 | vaddr += read_buf_size as u32; 94 | } 95 | } 96 | } 97 | 98 | fn entrypoint11(&self) -> u32 { 99 | self.entry11 100 | } 101 | fn load11(&self, _controller: &mut mem::MemController) { 102 | // Unnecessary because FIRMs on the 3DS can only load into ARM9-accessible memory 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /libllama/src/ldr/mod.rs: -------------------------------------------------------------------------------- 1 | mod ctr9; 2 | mod firm; 3 | 4 | pub use self::ctr9::*; 5 | pub use self::firm::*; 6 | use mem; 7 | 8 | pub trait Loader { 9 | fn entrypoint9(&self) -> u32; 10 | fn entrypoint11(&self) -> u32; 11 | fn load9(&self, controller: &mut mem::MemController); 12 | fn load11(&self, controller: &mut mem::MemController); 13 | } 14 | 15 | use std::path::Path; 16 | 17 | pub fn make_loader(path: &Path) -> Box { 18 | match path.extension().and_then(|x| x.to_str()) { 19 | Some("ctr9") => Box::new(Ctr9Loader::from_folder(path).unwrap()), 20 | Some("firm") => Box::new(FirmLoader::from_file(path).unwrap()), 21 | Some(x) => panic!("Attempted to load with unknown extension {:?}", x), 22 | None => panic!("Attempted to load ambiguous file {:?}", path) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /libllama/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | #![allow(unused_parens)] 3 | 4 | #[macro_use] 5 | extern crate bitutils; 6 | #[macro_use] 7 | extern crate derive_error; 8 | extern crate indextree; 9 | #[macro_use] 10 | extern crate log; 11 | extern crate mio; 12 | extern crate openssl; 13 | extern crate parking_lot; 14 | extern crate arraydeque; 15 | 16 | #[macro_use] 17 | pub mod utils; 18 | 19 | pub mod clock; 20 | pub mod cpu; 21 | pub mod dbgcore; 22 | pub mod fs; 23 | pub mod gdbstub; 24 | pub mod hwcore; 25 | pub mod io; 26 | pub mod ldr; 27 | pub mod msgs; 28 | pub mod mem; 29 | -------------------------------------------------------------------------------- /libllama/src/msgs.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::hash::Hash; 3 | use std::thread; 4 | use std::sync::mpsc; 5 | 6 | pub fn make_idle_task(client: Client, state: S, idle_handler: FI) -> thread::JoinHandle<()> 7 | where M: Ident + Send + Clone + 'static, S: Send + 'static, FI: Fn(M, &mut S) -> bool + Send + 'static { 8 | thread::spawn(move || { 9 | let mut state = state; 10 | for msg in client.iter() { 11 | if !idle_handler(msg, &mut state) { 12 | return 13 | } 14 | } 15 | }) 16 | } 17 | 18 | 19 | pub trait Ident { 20 | type Identifier: Clone + Eq + Hash + Send; 21 | fn ident(&self) -> Self::Identifier; 22 | } 23 | 24 | type ClientId = &'static str; 25 | type TxMsgs = &'static [::Identifier]; 26 | type RxMsgs = &'static [::Identifier]; 27 | 28 | type Route = mpsc::Sender; 29 | type RouteSink = mpsc::Receiver; 30 | 31 | type GraphSpec = &'static [( 32 | ClientId, 33 | TxMsgs, 34 | RxMsgs 35 | )]; 36 | 37 | 38 | type ClientRoutes = HashMap::Identifier, Vec>>>; 39 | type ClientMap = HashMap, RouteSink)>; 40 | type MsgDests = HashMap<::Identifier, Vec>; 41 | 42 | pub struct MsgGraph { 43 | client_routes: ClientRoutes, 44 | clients: ClientMap 45 | } 46 | 47 | impl MsgGraph { 48 | pub fn new(graph_spec: GraphSpec) -> MsgGraph { 49 | let mut client_routes: ClientRoutes = HashMap::new(); 50 | let mut clients: ClientMap = HashMap::new(); 51 | let mut msg_dests: MsgDests = HashMap::new(); 52 | 53 | for (client, _, rx_msgs) in graph_spec { 54 | clients.insert(*client, mpsc::channel()); 55 | for rx_msg in rx_msgs.iter() { 56 | msg_dests.entry(rx_msg.clone()).or_insert(Vec::new()) 57 | .push(*client); 58 | } 59 | } 60 | 61 | for (client, tx_msgs, _) in graph_spec { 62 | let msg_routes = client_routes.entry(client).or_insert(HashMap::new()); 63 | 64 | for tx_msg in tx_msgs.iter() { 65 | let routes = msg_routes.entry(tx_msg.clone()).or_insert(Vec::new()); 66 | 67 | if let Some(dst_clients) = msg_dests.get(tx_msg) { 68 | for dst_client in dst_clients { 69 | let route = clients[dst_client].0.clone(); 70 | routes.push(route); 71 | } 72 | } 73 | } 74 | } 75 | 76 | MsgGraph { 77 | client_routes, 78 | clients 79 | } 80 | } 81 | 82 | pub fn client(&mut self, client: ClientId) -> Option> { 83 | let mut tx_map = HashMap::new(); 84 | 85 | for (msg, tx_routes) in self.client_routes.remove(client)? { 86 | let routes = tx_map.entry(msg.clone()).or_insert(Vec::new()); 87 | for tx in tx_routes { 88 | routes.push(tx); 89 | } 90 | } 91 | 92 | Some(Client { 93 | tx: tx_map, 94 | rx: self.clients.remove(client)?.1 95 | }) 96 | } 97 | } 98 | 99 | pub struct Client { 100 | tx: HashMap<::Identifier, Vec>>, 101 | rx: RouteSink 102 | } 103 | 104 | impl Client { 105 | pub fn send(&self, msg: M) { 106 | let vec = self.tx.get(&msg.ident()) 107 | .expect("Attempted to send unauthorized message!"); 108 | 109 | for dst in vec { 110 | let _ = dst.send(msg.clone()); 111 | } 112 | } 113 | pub fn recv(&self) -> Result { 114 | self.rx.recv() 115 | } 116 | pub fn try_recv(&self) -> Result { 117 | self.rx.try_recv() 118 | } 119 | pub fn iter(&self) -> mpsc::Iter { 120 | self.rx.iter() 121 | } 122 | pub fn try_iter(&self) -> mpsc::TryIter { 123 | self.rx.try_iter() 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /libllama/src/utils/bcast.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::{self, Ordering}; 2 | use std::sync::Arc; 3 | use std::cell::UnsafeCell; 4 | use std::marker::PhantomData; 5 | 6 | pub unsafe trait AtomicDat: Default { 7 | type DAT: Copy; 8 | fn atomic_load(&self, ord: Ordering) -> Self::DAT; 9 | fn atomic_store(&self, val: Self::DAT, ord: Ordering); 10 | } 11 | 12 | pub struct Broadcaster { 13 | val: Arc, 14 | disallow_sync: PhantomData>, 15 | } 16 | 17 | impl Broadcaster { 18 | pub fn new() -> (Self, Audience) { 19 | let val: Arc = Arc::new(Default::default()); 20 | let out = Self { 21 | val: val.clone(), 22 | disallow_sync: PhantomData 23 | }; 24 | (out, Audience { val }) 25 | } 26 | 27 | pub fn update(&mut self, val: T::DAT) { 28 | self.val.atomic_store(val, Ordering::Relaxed) 29 | } 30 | pub fn explicit_update(&mut self, val: T::DAT, ord: Ordering) { 31 | self.val.atomic_store(val, ord) 32 | } 33 | } 34 | 35 | #[derive(Clone)] 36 | pub struct Audience { 37 | val: Arc 38 | } 39 | 40 | impl Audience { 41 | pub fn val(&self) -> T::DAT { 42 | self.val.atomic_load(Ordering::Relaxed) 43 | } 44 | pub fn explicit_val(&self, ord: Ordering) -> T::DAT { 45 | self.val.atomic_load(ord) 46 | } 47 | } 48 | 49 | 50 | 51 | 52 | macro_rules! atomic_impl { 53 | ($($which:ty = $dat:ty);* $(;)*) => { $( 54 | unsafe impl AtomicDat for $which { 55 | type DAT = $dat; 56 | fn atomic_load(&self, ord: Ordering) -> Self::DAT { 57 | self.load(ord) 58 | } 59 | fn atomic_store(&self, val: Self::DAT, ord: Ordering) { 60 | self.store(val, ord) 61 | } 62 | } 63 | )* } 64 | } 65 | 66 | atomic_impl! { 67 | atomic::AtomicIsize = isize; 68 | atomic::AtomicUsize = usize; 69 | atomic::AtomicBool = bool; 70 | } 71 | -------------------------------------------------------------------------------- /libllama/src/utils/bytes.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::slice; 3 | 4 | pub fn from_u128(mut num: u128) -> [u8; 16] { 5 | let mut data = [0u8; 0x10]; 6 | for b in data.iter_mut().rev() { 7 | *b = num as u8; 8 | num >>= 8; 9 | } 10 | data 11 | } 12 | 13 | pub fn to_u128(data: &[u8]) -> u128 { 14 | assert!(data.len() <= 16); 15 | let mut new = 0u128; 16 | for b in data.iter() { 17 | new <<= 8; 18 | new |= *b as u128; 19 | } 20 | new 21 | } 22 | 23 | pub unsafe fn from_val<'a, T: Copy>(data: &'a T) -> &'a [u8] { 24 | slice::from_raw_parts(data as *const T as *const u8, mem::size_of::()) 25 | } 26 | 27 | pub unsafe fn from_mut_val<'a, T: Copy>(data: &'a mut T) -> &'a mut [u8] { 28 | slice::from_raw_parts_mut(data as *mut T as *mut u8, mem::size_of::()) 29 | } 30 | 31 | pub unsafe fn to_val(data: &[u8]) -> T { 32 | let mut out: T = mem::zeroed(); 33 | from_mut_val(&mut out).copy_from_slice(data); 34 | out 35 | } 36 | 37 | #[allow(non_snake_case)] 38 | pub fn Tpos(start: usize) -> ::std::ops::Range { 39 | start .. start + mem::size_of::() 40 | } 41 | 42 | pub unsafe fn val_at_offs(data: &[u8], offs: usize) -> T { 43 | let bytes = &data[Tpos::(offs)]; 44 | to_val::(bytes) 45 | } 46 | -------------------------------------------------------------------------------- /libllama/src/utils/cache.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::ops::{Deref, DerefMut}; 3 | 4 | #[derive(Copy, Clone)] 5 | union Zeroable { 6 | _unused: u8, 7 | val: T 8 | } 9 | 10 | impl Deref for Zeroable { 11 | type Target = T; 12 | #[inline(always)] 13 | fn deref(&self) -> &T { 14 | unsafe { &self.val } 15 | } 16 | } 17 | 18 | impl DerefMut for Zeroable { 19 | #[inline(always)] 20 | fn deref_mut(&mut self) -> &mut T { 21 | unsafe { &mut self.val } 22 | } 23 | } 24 | 25 | const CACHE_SIZE_BITS: usize = 6; 26 | const CACHE_SIZE: usize = 1 << CACHE_SIZE_BITS; 27 | 28 | type SrcFn = fn(&mut C, u32) -> T; 29 | type SinkFn = fn(&mut C, u32, &T); 30 | 31 | pub struct TinyCache { 32 | map_keys: [u32; CACHE_SIZE], 33 | map_vals: [Zeroable; CACHE_SIZE], 34 | dirty: [bool; CACHE_SIZE], 35 | src: SrcFn, 36 | sink: SinkFn, 37 | } 38 | 39 | impl TinyCache { 40 | pub fn new(src: SrcFn, sink: SinkFn) -> Self { 41 | TinyCache { 42 | map_keys: [!0; CACHE_SIZE], 43 | map_vals: [unsafe { mem::zeroed() }; CACHE_SIZE], 44 | dirty: [false; CACHE_SIZE], 45 | src: src, 46 | sink: sink 47 | } 48 | } 49 | 50 | fn flush(&mut self, idx: usize, ctx: &mut C) { 51 | if self.dirty[idx] { 52 | (self.sink)(ctx, self.map_keys[idx], &*self.map_vals[idx]); 53 | self.dirty[idx] = false; 54 | } 55 | } 56 | 57 | pub fn invalidate(&mut self, ctx: &mut C) { 58 | for i in 0..CACHE_SIZE { 59 | self.flush(i, ctx); 60 | } 61 | self.map_keys = [!0; CACHE_SIZE]; 62 | } 63 | 64 | pub fn key_to_index(key: u32) -> usize { 65 | let hash = (key.wrapping_mul(2654435761)) as usize; 66 | hash >> (32 - CACHE_SIZE_BITS) // shifted by 32 - log2(# buckets) 67 | } 68 | 69 | pub fn get_or(&mut self, key: u32, ctx: &mut C) -> &T { 70 | let idx = Self::key_to_index(key); 71 | if self.map_keys[idx] != key { 72 | self.flush(idx, ctx); 73 | self.map_keys[idx] = key; 74 | *self.map_vals[idx] = (self.src)(ctx, key); 75 | } 76 | &*self.map_vals[idx] 77 | } 78 | 79 | pub fn update_or(&mut self, key: u32, updater: F, ctx: &mut C) 80 | where F: Fn(u32, &mut T) { 81 | let idx = Self::key_to_index(key); 82 | if self.map_keys[idx] != key { 83 | self.flush(idx, ctx); 84 | self.map_keys[idx] = key; 85 | *self.map_vals[idx] = (self.src)(ctx, key); 86 | } 87 | updater(key, &mut self.map_vals[idx]); 88 | self.dirty[idx] = true; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /libllama/src/utils/fifo.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | 3 | #[derive(Debug, Clone)] 4 | pub struct Fifo { 5 | inner: VecDeque, 6 | max_len: usize, 7 | } 8 | 9 | impl Fifo { 10 | pub fn new(max_len: usize) -> Self { 11 | let mut inner = VecDeque::new(); 12 | inner.reserve_exact(max_len); 13 | Self { 14 | inner, 15 | max_len 16 | } 17 | } 18 | 19 | 20 | pub fn push(&mut self, item: T) -> bool { 21 | if self.len() >= self.max_len { 22 | return false 23 | } 24 | 25 | self.inner.push_back(item); 26 | true 27 | } 28 | 29 | pub fn pop(&mut self) -> Option { 30 | self.inner.pop_front() 31 | } 32 | 33 | pub fn len(&self) -> usize { 34 | self.inner.len() 35 | } 36 | 37 | pub fn free_space(&self) -> usize { 38 | self.max_len - self.len() 39 | } 40 | 41 | pub fn clear(&mut self) { 42 | self.inner.clear(); 43 | } 44 | 45 | pub fn drain(&mut self, dst: &mut [T]) -> usize { 46 | let drain_amount = dst.len().min(self.len()); 47 | for i in 0..drain_amount { 48 | dst[i] = self.pop().unwrap(); 49 | } 50 | drain_amount 51 | } 52 | 53 | pub fn full(&self) -> bool { 54 | self.len() == self.max_len 55 | } 56 | 57 | pub fn empty(&self) -> bool { 58 | self.len() == 0 59 | } 60 | } 61 | 62 | impl Fifo { 63 | pub fn clone_extend(&mut self, items: &[T]) -> usize { 64 | let space_left = self.max_len - self.len(); 65 | let copy_amount = items.len().min(space_left); 66 | 67 | self.inner.extend(items[..copy_amount].iter().cloned()); 68 | 69 | copy_amount 70 | } 71 | } 72 | 73 | #[cfg(test)] 74 | mod test { 75 | use super::*; 76 | 77 | #[test] 78 | fn fifo_bounds() { 79 | let mut fifo = Fifo::::new(16); 80 | assert!(fifo.empty()); 81 | assert!(fifo.pop().is_none()); 82 | 83 | for i in 0..16 { 84 | let ok = fifo.push(i); 85 | assert!(ok); 86 | } 87 | assert!(fifo.full()); 88 | assert!(!fifo.push(16)); 89 | 90 | for i in 0..16 { 91 | let out = fifo.pop().unwrap(); 92 | assert_eq!(out, i); 93 | } 94 | 95 | assert!(fifo.empty()); 96 | assert!(fifo.pop().is_none()); 97 | } 98 | 99 | #[test] 100 | fn fifo_drain() { 101 | let mut fifo = Fifo::::new(16); 102 | for i in 0..16 { 103 | fifo.push(i); 104 | } 105 | 106 | let mut drain_buf = [0; 16]; 107 | let amount = fifo.drain(&mut drain_buf[..4]); 108 | assert_eq!(amount, 4); 109 | assert_eq!(&drain_buf[..4], &(0..4).collect::>()[..]); 110 | assert_eq!(fifo.len(), 12); 111 | 112 | let amount = fifo.drain(&mut drain_buf); 113 | assert_eq!(amount, 12); 114 | assert_eq!(&drain_buf[..12], &(4..16).collect::>()[..]); 115 | assert!(fifo.empty()); 116 | } 117 | 118 | #[test] 119 | fn fifo_extend() { 120 | let mut fifo = Fifo::::new(16); 121 | let in_buf: Vec<_> = (0..16).collect(); 122 | 123 | let amount = fifo.clone_extend(&in_buf[..4]); 124 | assert_eq!(amount, 4); 125 | assert_eq!(fifo.len(), 4); 126 | 127 | let amount = fifo.clone_extend(&in_buf); 128 | assert_eq!(amount, 12); 129 | assert!(fifo.full()); 130 | 131 | let mut drain_buf = [0; 16]; 132 | let amount = fifo.drain(&mut drain_buf[..4]); 133 | assert_eq!(amount, 4); 134 | assert_eq!(&drain_buf[..4], &in_buf[..4]); 135 | 136 | let amount = fifo.drain(&mut drain_buf); 137 | assert_eq!(amount, 12); 138 | assert_eq!(&drain_buf[..12], &in_buf[..12]); 139 | } 140 | } -------------------------------------------------------------------------------- /libllama/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod num; 3 | mod strutils; 4 | 5 | pub mod bytes; 6 | pub mod cache; 7 | pub mod fifo; 8 | 9 | pub use self::strutils::*; 10 | -------------------------------------------------------------------------------- /libllama/src/utils/num.rs: -------------------------------------------------------------------------------- 1 | macro_rules! wrapping_sum { 2 | ($a:expr, $b:expr $(, $rest:expr)*) => ( 3 | $a.wrapping_add($b) 4 | $(.wrapping_add($rest))* 5 | ) 6 | } 7 | 8 | macro_rules! checked_sum { 9 | ($a:expr, $b:expr $(, $rest:expr)*) => ( 10 | $a.checked_add($b) 11 | $(.and_then(|x| x.checked_add($rest)))* 12 | ) 13 | } 14 | 15 | macro_rules! wrapping_diff { 16 | ($a:expr, $b:expr $(, $rest:expr)*) => ( 17 | $a.wrapping_sub($b) 18 | $(.wrapping_sub($rest))* 19 | ) 20 | } 21 | 22 | macro_rules! checked_diff { 23 | ($a:expr, $b:expr $(, $rest:expr)*) => ( 24 | $a.checked_sub($b) 25 | $(.and_then(|x| x.checked_sub($rest)))* 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /libllama/src/utils/strutils.rs: -------------------------------------------------------------------------------- 1 | use std::num::ParseIntError; 2 | 3 | pub fn from_hex(string: &str) -> Result { 4 | let slice = if string.starts_with("0x") { 5 | &string[2..] 6 | } else { 7 | &string[..] 8 | }; 9 | u32::from_str_radix(slice, 16) 10 | } -------------------------------------------------------------------------------- /libllama/tools/decoder-gen/_parser.py: -------------------------------------------------------------------------------- 1 | import lexer 2 | 3 | def adapt_strip_newlines(tstream): 4 | return (t for t in tstream if t.ttype != "newline") 5 | 6 | def return_to_iter(item, iterator): 7 | yield item 8 | for x in iterator: yield x 9 | 10 | def ensure_match_token(token, ttype, tval): 11 | if token.ttype == ttype and token.tval == tval: 12 | return 13 | raise RuntimeError("Token `{}` did not match expected {} `{}` at {}" 14 | .format(token.tval, ttype, tval, token.lineinfo)) 15 | 16 | def matching_unwrap_token(token, ttype): 17 | if token.ttype == ttype: 18 | return token.tval 19 | raise RuntimeError("Token `{}` is not a valid {} at {}" 20 | .format(token.tval, ttype, token.lineinfo)) 21 | 22 | def error_no_match(token): 23 | raise RuntimeError("Token `{}` of invalid type `{}` at {}" 24 | .format(token.tval, token.ttype, token.lineinfo)) 25 | 26 | 27 | class Decoder: 28 | @staticmethod 29 | def parse(tstream_full): 30 | tstream = adapt_strip_newlines(tstream_full) 31 | ensure_match_token(next(tstream), "keyword", "decoder") 32 | 33 | decoder = Decoder() 34 | decoder.ty = matching_unwrap_token(next(tstream), "label") 35 | decoder.name = matching_unwrap_token(next(tstream), "label") 36 | ensure_match_token(next(tstream), "bracket", "{") 37 | 38 | decoder.categories = [] 39 | peeked_next = next(tstream) 40 | while peeked_next.tval != "}": 41 | rebuilt_tstream_full = return_to_iter(peeked_next, tstream_full) 42 | decoder.categories.append(Category.parse(rebuilt_tstream_full)) 43 | peeked_next = next(tstream) 44 | 45 | return decoder 46 | 47 | 48 | class Category: 49 | @staticmethod 50 | def parse(tstream_full): 51 | tstream = adapt_strip_newlines(tstream_full) 52 | ensure_match_token(next(tstream), "keyword", "category") 53 | 54 | category = Category() 55 | category.definitions = [] 56 | while True: 57 | category.definitions.append(Definition.parse(tstream_full)) 58 | next_token = next(tstream) 59 | if next_token.tval != "or": 60 | break 61 | ensure_match_token(next_token, "keyword", "or") 62 | 63 | ensure_match_token(next_token, "bracket", "{") 64 | 65 | category.instructions = [] 66 | peeked_next = next(tstream) 67 | while peeked_next.tval != "}": 68 | rebuilt_tstream_full = return_to_iter(peeked_next, tstream_full) 69 | category.instructions.append(Instruction.parse(rebuilt_tstream_full)) 70 | peeked_next = next(tstream) 71 | 72 | return category 73 | 74 | 75 | class Instruction: 76 | @staticmethod 77 | def parse(tstream_full): 78 | tstream = adapt_strip_newlines(tstream_full) 79 | 80 | inst = Instruction() 81 | inst.name = matching_unwrap_token(next(tstream), "label") 82 | ensure_match_token(next(tstream), "separator", "=") 83 | inst.defn = Definition.parse(tstream) 84 | ensure_match_token(next(tstream_full), "newline", "") 85 | return inst 86 | 87 | 88 | class Definition: 89 | @staticmethod 90 | def parse(tstream_full): 91 | tstream = adapt_strip_newlines(tstream_full) 92 | ensure_match_token(next(tstream), "bracket", "[") 93 | 94 | definition = Definition() 95 | definition.bitgroups = [] 96 | while True: 97 | definition.bitgroups.append(BitGroup.parse(tstream_full)) 98 | next_token = next(tstream) 99 | if next_token.tval != ";": 100 | break 101 | ensure_match_token(next_token, "separator", ";") 102 | 103 | ensure_match_token(next_token, "bracket", "]") 104 | return definition 105 | 106 | 107 | class BitGroup: 108 | class Labeled: 109 | def __init__(self, label, size): 110 | self.lhs = label 111 | self.size = size 112 | class Literal: 113 | def __init__(self, val, size): 114 | self.lhs = val 115 | self.size = size 116 | class Void: 117 | def __init__(self, size): 118 | self.lhs = None 119 | self.size = size 120 | 121 | @staticmethod 122 | def parse(tstream_full): 123 | tstream = adapt_strip_newlines(tstream_full) 124 | lhs = next(tstream) 125 | ensure_match_token(next(tstream), "separator", ":") 126 | size = matching_unwrap_token(next(tstream), "literal") 127 | if lhs.ttype == "label": 128 | return BitGroup.Labeled(lhs.tval, size) 129 | elif lhs.ttype == "literal": 130 | return BitGroup.Literal(lhs.tval, size) 131 | elif lhs.ttype == "void": 132 | return BitGroup.Void(size) 133 | else: 134 | error_no_match(lhs) 135 | 136 | def parse(filename): 137 | tokens = lexer.tokenize(filename) 138 | decoder = Decoder.parse(iter(tokens)) 139 | return decoder -------------------------------------------------------------------------------- /libllama/tools/decoder-gen/decoder-gen.py: -------------------------------------------------------------------------------- 1 | import emitter 2 | import argparse 3 | 4 | parser = argparse.ArgumentParser(description='Generate a decoder given a decoder specification.') 5 | parser.add_argument('file', type=str, nargs=1, help='decoder specification file') 6 | 7 | args = parser.parse_args() 8 | emitter.generate(args.file[0]) -------------------------------------------------------------------------------- /libllama/tools/decoder-gen/emitter.py: -------------------------------------------------------------------------------- 1 | import _parser as parser 2 | 3 | def literal_to_int(lit): 4 | if lit.startswith("0b"): 5 | return int(lit[2:], base=2) 6 | elif lit.startswith("0x"): 7 | return int(lit[2:], base=16) 8 | else: 9 | return int(lit) 10 | 11 | class Constraint: 12 | def __init__(self, mask, equals): 13 | self.mask = mask 14 | self.equals = equals 15 | 16 | def __str__(self): 17 | return f"enc & 0x{self.mask:08X} == 0x{self.equals:08X}" 18 | 19 | def definition_to_constraint(defn): 20 | mask = 0 21 | req = 0 22 | for bitgroup in defn.bitgroups: 23 | bg_size = literal_to_int(bitgroup.size) 24 | mask <<= bg_size 25 | req <<= bg_size 26 | if isinstance(bitgroup, parser.BitGroup.Literal): 27 | mask |= 2**bg_size - 1 28 | binary = literal_to_int(bitgroup.lhs) 29 | assert(binary <= mask) 30 | req |= binary 31 | return Constraint(mask, req) 32 | 33 | def to_CamelCase(string): 34 | return "".join([s.title() for s in string.split('_')]) 35 | 36 | def generate(file): 37 | decoder = parser.parse(file) 38 | 39 | indentation = 0 40 | def pcode(s): 41 | print(' ' * indentation + s) 42 | def indent(): 43 | nonlocal indentation 44 | indentation += 2 45 | def unindent(): 46 | nonlocal indentation 47 | indentation -= 2 48 | 49 | pcode("#[allow(dead_code, unused_parens)]") 50 | pcode(f"pub fn disasm(enc: {decoder.ty}) -> String {{") 51 | indent() 52 | for category in decoder.categories: 53 | string = ") || (".join([ str(definition_to_constraint(defn)) 54 | for defn in category.definitions ]) 55 | pcode(f"if ({string}) {{") 56 | indent() 57 | 58 | for instr in category.instructions: 59 | constraint = definition_to_constraint(instr.defn) 60 | pcode(f"if {str(constraint)} {{") 61 | indent() 62 | 63 | pcode(f"return format!(\"{{:x?}}\", {to_CamelCase(instr.name)}::new(enc))") 64 | 65 | unindent() 66 | pcode("}") 67 | 68 | unindent() 69 | pcode("}") 70 | pcode("\"undef\".into()") 71 | unindent() 72 | pcode("}") 73 | 74 | pcode("#[allow(unused_parens)]") 75 | pcode(f"pub fn decode(enc: {decoder.ty}) -> InstFn {{") 76 | indent() 77 | for category in decoder.categories: 78 | string = ") || (".join([ str(definition_to_constraint(defn)) 79 | for defn in category.definitions ]) 80 | pcode(f"if ({string}) {{") 81 | indent() 82 | 83 | for instr in category.instructions: 84 | constraint = definition_to_constraint(instr.defn) 85 | pcode(f"if {str(constraint)} {{") 86 | indent() 87 | 88 | pcode(f"return unsafe {{ ::std::mem::transmute(interpreter::{instr.name}:: as usize) }}") 89 | 90 | unindent() 91 | pcode("}") 92 | 93 | unindent() 94 | pcode("}") 95 | pcode("interpreter::undef") 96 | unindent() 97 | pcode("}") 98 | 99 | for category in decoder.categories: 100 | for instr in category.instructions: 101 | pcode(f"bf!({to_CamelCase(instr.name)}[{decoder.ty}] {{") 102 | indent() 103 | 104 | pos = 0 105 | num_labeled = sum((1 for b in instr.defn.bitgroups if isinstance(b, parser.BitGroup.Labeled))) 106 | labeled_i = 0 107 | for bitgroup in reversed(instr.defn.bitgroups): 108 | comma = "," if labeled_i != num_labeled - 1 else "" 109 | size = literal_to_int(bitgroup.size) 110 | if isinstance(bitgroup, parser.BitGroup.Labeled): 111 | name = bitgroup.lhs 112 | start = pos 113 | pcode(f"{name}: {start}usize:{start + size - 1}usize{comma}") 114 | labeled_i += 1 115 | pos += size 116 | 117 | unindent() 118 | pcode("});") 119 | -------------------------------------------------------------------------------- /libllama/tools/decoder-gen/lexer.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | token_regex = re.compile( 4 | " " + 5 | "|\\/\\/.*?$" + # Comment 6 | "|(?Pdecoder|category|or)[ \\t$]" + 7 | "|(?P=|\\:|;)" + 8 | "|(?P[[\\]{}])" + 9 | "|(?P_)" + 10 | "|(?P(0b)?\\d+)" + 11 | "|(?P