Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified rfel/assets/payloads/spi_d1.bin
Binary file not shown.
22 changes: 22 additions & 0 deletions rfel/src/consts.rs
Original file line number Diff line number Diff line change
@@ -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;
1 change: 1 addition & 0 deletions rfel/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod chips;
pub mod cli;
pub mod consts;
pub mod fel;
pub mod ops;
pub mod progress;
Expand Down
45 changes: 19 additions & 26 deletions rfel/src/ops/spinand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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!(
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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..];
Expand All @@ -317,9 +312,6 @@ impl<'chip> SpinandState<'chip> {
break;
}
}

self.program_exec(fel, page)?;
self.wait_ready(fel)?;
}
Ok(())
}
Expand Down Expand Up @@ -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,
Expand Down
87 changes: 80 additions & 7 deletions rfel/src/spi.rs
Original file line number Diff line number Diff line change
@@ -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),
Expand All @@ -19,6 +13,85 @@ pub enum SpiError {
LengthOverflow,
}

#[derive(Debug)]
pub struct Command {
commands: Vec<u8>,
data: Vec<u8>,
}

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 {
Expand Down
Loading