diff --git a/rfel/assets/payloads/spi_d1.bin b/rfel/assets/payloads/spi_d1.bin index dc5bda3..e9615f8 100644 Binary files a/rfel/assets/payloads/spi_d1.bin and b/rfel/assets/payloads/spi_d1.bin differ diff --git a/rfel/src/consts.rs b/rfel/src/consts.rs new file mode 100644 index 0000000..d126fd1 --- /dev/null +++ b/rfel/src/consts.rs @@ -0,0 +1,22 @@ +pub const SPI_CMD_END: u8 = 0x00; +pub const SPI_CMD_INIT: u8 = 0x01; +pub const SPI_CMD_SELECT: u8 = 0x02; +pub const SPI_CMD_DESELECT: u8 = 0x03; +pub const SPI_CMD_FAST: u8 = 0x04; +pub const SPI_CMD_TXBUF: u8 = 0x05; +pub const SPI_CMD_RXBUF: u8 = 0x06; +pub const SPI_CMD_SPINOR_WAIT: u8 = 0x07; +pub const SPI_CMD_SPINAND_WAIT: u8 = 0x08; +pub const OPCODE_READ_PAGE_TO_CACHE: u8 = 0x13; +pub const OPCODE_READ_PAGE_FROM_CACHE: u8 = 0x03; +pub const OPCODE_WRITE_ENABLE: u8 = 0x06; +pub const OPCODE_BLOCK_ERASE: u8 = 0xd8; +pub const OPCODE_PROGRAM_LOAD: u8 = 0x02; +pub const OPCODE_PROGRAM_EXEC: u8 = 0x10; +pub const OPCODE_RESET: u8 = 0xff; + +pub const OPCODE_RDID: u8 = 0x9f; +pub const OPCODE_GET_FEATURE: u8 = 0x0f; +pub const OPCODE_SET_FEATURE: u8 = 0x1f; +pub const FEATURE_PROTECT: u8 = 0xa0; +pub const FEATURE_STATUS: u8 = 0xc0; diff --git a/rfel/src/lib.rs b/rfel/src/lib.rs index 9c9fc1e..dacfe2d 100644 --- a/rfel/src/lib.rs +++ b/rfel/src/lib.rs @@ -1,5 +1,6 @@ pub mod chips; pub mod cli; +pub mod consts; pub mod fel; pub mod ops; pub mod progress; diff --git a/rfel/src/ops/spinand.rs b/rfel/src/ops/spinand.rs index e82b67e..16a78f3 100644 --- a/rfel/src/ops/spinand.rs +++ b/rfel/src/ops/spinand.rs @@ -3,23 +3,12 @@ use std::fmt; use std::time::{Duration, Instant}; use crate::chips::Chip; +use crate::consts::*; use crate::fel::Fel; use crate::progress::Progress; -use crate::spi::{self, SpiError, SpiSession}; +use crate::spi::{self, Command, SpiError, SpiSession}; const WAIT_TIMEOUT: Duration = Duration::from_secs(5); -const OPCODE_RDID: u8 = 0x9f; -const OPCODE_GET_FEATURE: u8 = 0x0f; -const OPCODE_SET_FEATURE: u8 = 0x1f; -const FEATURE_PROTECT: u8 = 0xa0; -const FEATURE_STATUS: u8 = 0xc0; -const OPCODE_READ_PAGE_TO_CACHE: u8 = 0x13; -const OPCODE_READ_PAGE_FROM_CACHE: u8 = 0x03; -const OPCODE_WRITE_ENABLE: u8 = 0x06; -const OPCODE_BLOCK_ERASE: u8 = 0xd8; -const OPCODE_PROGRAM_LOAD: u8 = 0x02; -const OPCODE_PROGRAM_EXEC: u8 = 0x10; -const OPCODE_RESET: u8 = 0xff; #[derive(Debug)] pub enum SpinandError { @@ -125,6 +114,7 @@ pub fn write( mut progress: Option<&mut Progress>, ) -> SpinandResult<()> { let mut state = SpinandState::new(chip, fel)?; + state.erase_range(fel, address, address + data.len() as u64, None)?; let mut processed = 0u64; let total = data.len() as u64; println!( @@ -220,16 +210,13 @@ impl<'chip> SpinandState<'chip> { fn erase_block(&mut self, fel: &Fel<'_>, address: u64) -> SpinandResult<()> { let page_size = self.info.page_size as u64; let pa = u32::try_from(address / page_size).map_err(|_| SpinandError::AddressOverflow)?; - self.write_enable(fel)?; - self.wait_ready(fel)?; - let tx = [ - OPCODE_BLOCK_ERASE, - ((pa >> 16) & 0xff) as u8, - ((pa >> 8) & 0xff) as u8, - (pa & 0xff) as u8, - ]; - spi::transfer(fel, &self.session, Some(&tx), None)?; - self.wait_ready(fel) + let mut command = Command::new(); + command.enable_write(); + command.wait_ready_nand(); + command.block_erase(pa); + command.wait_ready_nand(); + command.exec(fel, &self.session)?; + Ok(()) } fn read_range_segment( @@ -301,7 +288,15 @@ impl<'chip> SpinandState<'chip> { if bytes_left_in_page == 0 { break; } + let mut commands = Command::new(); let chunk = data.len().min(bytes_left_in_page).min(self.chunk_limit()); + commands.enable_write(); + commands.wait_ready_nand(); + commands.program_load(&self.session, column as u16, &data[..chunk]); + commands.wait_ready_nand(); + commands.program_exec(page); + commands.wait_ready_nand(); + commands.exec(fel, &self.session)?; self.program_load(fel, column as u16, &data[..chunk])?; self.wait_ready(fel)?; data = &data[chunk..]; @@ -317,9 +312,6 @@ impl<'chip> SpinandState<'chip> { break; } } - - self.program_exec(fel, page)?; - self.wait_ready(fel)?; } Ok(()) } @@ -480,6 +472,7 @@ impl<'chip> SpinandState<'chip> { Ok(()) } + #[allow(unused)] fn program_exec(&mut self, fel: &Fel<'_>, page: u32) -> SpinandResult<()> { let tx = [ OPCODE_PROGRAM_EXEC, diff --git a/rfel/src/spi.rs b/rfel/src/spi.rs index a0de7b9..2bf4805 100644 --- a/rfel/src/spi.rs +++ b/rfel/src/spi.rs @@ -1,16 +1,10 @@ use std::fmt; use crate::chips::{Chip, ChipError, ChipSpi, SpiContext}; +use crate::consts::*; use crate::fel::Fel; use crate::transfer::{read_all, write_all}; -const SPI_CMD_END: u8 = 0x00; -const SPI_CMD_INIT: u8 = 0x01; -const SPI_CMD_SELECT: u8 = 0x02; -const SPI_CMD_DESELECT: u8 = 0x03; -const SPI_CMD_TXBUF: u8 = 0x05; -const SPI_CMD_RXBUF: u8 = 0x06; - #[derive(Debug)] pub enum SpiError { Chip(ChipError), @@ -19,6 +13,85 @@ pub enum SpiError { LengthOverflow, } +#[derive(Debug)] +pub struct Command { + commands: Vec, + data: Vec, +} + +impl Command { + pub fn new() -> Self { + Self { + commands: vec![], + data: vec![], + } + } + pub fn wait_ready_nor(&mut self) { + self.commands + .extend_from_slice(&[SPI_CMD_SELECT, SPI_CMD_SPINOR_WAIT, SPI_CMD_DESELECT]); + } + pub fn wait_ready_nand(&mut self) { + self.commands + .extend_from_slice(&[SPI_CMD_SELECT, SPI_CMD_SPINAND_WAIT, SPI_CMD_DESELECT]); + } + pub fn enable_write(&mut self) { + self.commands.extend_from_slice(&[ + SPI_CMD_SELECT, + SPI_CMD_FAST, + 1, + OPCODE_WRITE_ENABLE, + SPI_CMD_DESELECT, + ]); + } + pub fn program_load(&mut self, session: &SpiSession<'_>, column: u16, data: &[u8]) { + let swap_base = session.context.swap_base; + self.data.push(OPCODE_PROGRAM_LOAD); + self.data.push(((column >> 8) & 0xff) as u8); + self.data.push((column & 0xff) as u8); + self.data.extend_from_slice(data); + self.commands.push(SPI_CMD_SELECT); + self.commands.push(SPI_CMD_TXBUF); + self.commands.extend_from_slice(&swap_base.to_le_bytes()); + self.commands + .extend_from_slice(&((data.len() + 3) as u32).to_le_bytes()); + self.commands.push(SPI_CMD_DESELECT); + } + pub fn program_exec(&mut self, page: u32) { + self.commands.extend_from_slice(&[ + SPI_CMD_SELECT, + SPI_CMD_FAST, + 4, + OPCODE_PROGRAM_EXEC, + ((page >> 16) & 0xff) as u8, + ((page >> 8) & 0xff) as u8, + (page & 0xff) as u8, + SPI_CMD_DESELECT, + ]); + } + pub fn block_erase(&mut self, pa: u32) { + self.commands.extend_from_slice(&[ + SPI_CMD_SELECT, + SPI_CMD_FAST, + 4, + OPCODE_BLOCK_ERASE, + ((pa >> 16) & 0xff) as u8, + ((pa >> 8) & 0xff) as u8, + (pa & 0xff) as u8, + SPI_CMD_DESELECT, + ]); + } + pub fn exec(&mut self, fel: &Fel<'_>, session: &SpiSession<'_>) -> Result<(), SpiError> { + let swap_base = session.context.swap_base; + + self.commands.push(SPI_CMD_END); + if self.data.len() != 0 { + write_all(fel, swap_base, &self.data[..]); + } + session.run_commands(fel, &self.commands)?; + Ok(()) + } +} + impl fmt::Display for SpiError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self {