mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-11-25 07:06:52 +01:00
bootloader protection
This commit is contained in:
parent
33c6e2bbdc
commit
aba493dfc9
@ -8,10 +8,10 @@
|
||||
|
||||
|
||||
bool flash_program (uint32_t src, uint32_t dst, uint32_t length) {
|
||||
if (((src + length) >= FLASH_ADDRESS) && (src < (FLASH_ADDRESS + FLASH_SIZE))) {
|
||||
if (((src + length) > FLASH_ADDRESS) && (src < (FLASH_ADDRESS + FLASH_SIZE))) {
|
||||
return true;
|
||||
}
|
||||
if ((dst < FLASH_ADDRESS) || ((dst + length) >= (FLASH_ADDRESS + FLASH_SIZE))) {
|
||||
if ((dst < FLASH_ADDRESS) || ((dst + length) > (FLASH_ADDRESS + FLASH_SIZE))) {
|
||||
return true;
|
||||
}
|
||||
while (length > 0) {
|
||||
@ -29,15 +29,18 @@ void flash_wait_busy (void) {
|
||||
fpga_mem_read(FLASH_ADDRESS, 2, dummy);
|
||||
}
|
||||
|
||||
bool flash_erase_block (uint32_t offset) {
|
||||
if ((offset % FLASH_ERASE_BLOCK_SIZE) != 0) {
|
||||
bool flash_erase_block (uint32_t address) {
|
||||
if ((address % FLASH_ERASE_BLOCK_SIZE) != 0) {
|
||||
return true;
|
||||
}
|
||||
offset &= (FLASH_SIZE - 1);
|
||||
if ((address < FLASH_ADDRESS) || (address >= (FLASH_ADDRESS + FLASH_SIZE))) {
|
||||
return true;
|
||||
}
|
||||
address &= (FLASH_SIZE - 1);
|
||||
for (int i = 0; i < (FLASH_ERASE_BLOCK_SIZE / ERASE_BLOCK_SIZE); i++) {
|
||||
fpga_reg_set(REG_FLASH_SCR, offset);
|
||||
fpga_reg_set(REG_FLASH_SCR, address);
|
||||
while (fpga_reg_get(REG_FLASH_SCR) & FLASH_SCR_BUSY);
|
||||
offset += ERASE_BLOCK_SIZE;
|
||||
address += ERASE_BLOCK_SIZE;
|
||||
}
|
||||
flash_wait_busy();
|
||||
return false;
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
bool flash_program (uint32_t src, uint32_t dst, uint32_t length);
|
||||
void flash_wait_busy (void);
|
||||
bool flash_erase_block (uint32_t offset);
|
||||
bool flash_erase_block (uint32_t address);
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -6,6 +6,9 @@
|
||||
#include "vendor.h"
|
||||
|
||||
|
||||
#define SDRAM_ADDRESS (0x00000000UL)
|
||||
#define SDRAM_LENGTH (64 * 1024 * 1024)
|
||||
|
||||
#define UPDATE_MAGIC_START (0x54535055UL)
|
||||
#define BOOTLOADER_ADDRESS (0x04E00000UL)
|
||||
#define BOOTLOADER_LENGTH (0x001E0000UL)
|
||||
@ -191,6 +194,10 @@ update_error_t update_backup (uint32_t address, uint32_t *length) {
|
||||
uint32_t fpga_length;
|
||||
uint32_t bootloader_length;
|
||||
|
||||
if (address >= (SDRAM_ADDRESS + SDRAM_LENGTH)) {
|
||||
return UPDATE_ERROR_ADDRESS;
|
||||
}
|
||||
|
||||
*length = update_write_token(&address);
|
||||
|
||||
*length += update_prepare_chunk(&address, CHUNK_ID_MCU_DATA);
|
||||
@ -214,6 +221,10 @@ update_error_t update_backup (uint32_t address, uint32_t *length) {
|
||||
}
|
||||
*length += update_finalize_chunk(&address, bootloader_length);
|
||||
|
||||
if ((address + *length) > (SDRAM_ADDRESS + SDRAM_LENGTH)) {
|
||||
return UPDATE_ERROR_ADDRESS;
|
||||
}
|
||||
|
||||
return UPDATE_OK;
|
||||
}
|
||||
|
||||
@ -223,6 +234,13 @@ update_error_t update_prepare (uint32_t address, uint32_t length) {
|
||||
uint32_t data_address;
|
||||
uint32_t data_length;
|
||||
|
||||
if ((address >= (SDRAM_ADDRESS + SDRAM_LENGTH)) || (length > SDRAM_LENGTH)) {
|
||||
return UPDATE_ERROR_ADDRESS;
|
||||
}
|
||||
if (end_address > (SDRAM_ADDRESS + SDRAM_LENGTH)) {
|
||||
return UPDATE_ERROR_ADDRESS;
|
||||
}
|
||||
|
||||
if (update_check_token(&address)) {
|
||||
return UPDATE_ERROR_TOKEN;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ typedef enum {
|
||||
UPDATE_ERROR_SIZE,
|
||||
UPDATE_ERROR_UNKNOWN_CHUNK,
|
||||
UPDATE_ERROR_READ,
|
||||
UPDATE_ERROR_ADDRESS,
|
||||
} update_error_t;
|
||||
|
||||
|
||||
|
@ -10,10 +10,20 @@
|
||||
#include "version.h"
|
||||
|
||||
|
||||
#define BOOTLOADER_ADDRESS (0x04E00000UL)
|
||||
#define BOOTLOADER_LENGTH (1920 * 1024)
|
||||
|
||||
#define MEMORY_LENGTH (0x05002980UL)
|
||||
|
||||
#define RX_FLUSH_ADDRESS (0x07F00000UL)
|
||||
#define RX_FLUSH_LENGTH (1 * 1024 * 1024)
|
||||
|
||||
|
||||
enum rx_state {
|
||||
RX_STATE_IDLE,
|
||||
RX_STATE_ARGS,
|
||||
RX_STATE_DATA,
|
||||
RX_STATE_FLUSH,
|
||||
};
|
||||
|
||||
enum tx_state {
|
||||
@ -129,6 +139,21 @@ static bool usb_rx_cmd (uint8_t *cmd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool usb_validate_address_length (uint32_t address, uint32_t length, bool exclude_bootloader) {
|
||||
if ((address >= MEMORY_LENGTH) || (length > MEMORY_LENGTH)) {
|
||||
return true;
|
||||
}
|
||||
if ((address + length) > MEMORY_LENGTH) {
|
||||
return true;
|
||||
}
|
||||
if (exclude_bootloader) {
|
||||
if (((address + length) > BOOTLOADER_ADDRESS) && (address < (BOOTLOADER_ADDRESS + BOOTLOADER_LENGTH))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void usb_rx_process (void) {
|
||||
if (p.rx_state == RX_STATE_IDLE) {
|
||||
if (!p.response_pending && usb_rx_cmd(&p.rx_cmd)) {
|
||||
@ -229,17 +254,25 @@ static void usb_rx_process (void) {
|
||||
case 'm':
|
||||
p.rx_state = RX_STATE_IDLE;
|
||||
p.response_pending = true;
|
||||
p.response_info.dma_address = p.rx_args[0];
|
||||
p.response_info.dma_length = p.rx_args[1];
|
||||
if (usb_validate_address_length(p.rx_args[0], p.rx_args[1], false)) {
|
||||
p.response_error = true;
|
||||
} else {
|
||||
p.response_info.dma_address = p.rx_args[0];
|
||||
p.response_info.dma_length = p.rx_args[1];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
if (usb_dma_ready()) {
|
||||
if (!p.rx_dma_running) {
|
||||
fpga_reg_set(REG_USB_DMA_ADDRESS, p.rx_args[0]);
|
||||
fpga_reg_set(REG_USB_DMA_LENGTH, p.rx_args[1]);
|
||||
fpga_reg_set(REG_USB_DMA_SCR, DMA_SCR_DIRECTION | DMA_SCR_START);
|
||||
p.rx_dma_running = true;
|
||||
if (usb_validate_address_length(p.rx_args[0], p.rx_args[1], true)) {
|
||||
p.rx_state = RX_STATE_FLUSH;
|
||||
} else {
|
||||
fpga_reg_set(REG_USB_DMA_ADDRESS, p.rx_args[0]);
|
||||
fpga_reg_set(REG_USB_DMA_LENGTH, p.rx_args[1]);
|
||||
fpga_reg_set(REG_USB_DMA_SCR, DMA_SCR_DIRECTION | DMA_SCR_START);
|
||||
p.rx_dma_running = true;
|
||||
}
|
||||
} else {
|
||||
p.rx_state = RX_STATE_IDLE;
|
||||
p.response_pending = true;
|
||||
@ -258,6 +291,7 @@ static void usb_rx_process (void) {
|
||||
p.read_ready = false;
|
||||
} else {
|
||||
p.rx_args[1] -= length;
|
||||
p.rx_dma_running = false;
|
||||
p.read_length -= length;
|
||||
p.read_address += length;
|
||||
p.read_ready = true;
|
||||
@ -285,7 +319,11 @@ static void usb_rx_process (void) {
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
p.response_error = flash_erase_block(p.rx_args[0]);
|
||||
if (usb_validate_address_length(p.rx_args[0], FLASH_ERASE_BLOCK_SIZE, true)) {
|
||||
p.response_error = true;
|
||||
} else {
|
||||
p.response_error = flash_erase_block(p.rx_args[0]);
|
||||
}
|
||||
p.rx_state = RX_STATE_IDLE;
|
||||
p.response_pending = true;
|
||||
break;
|
||||
@ -336,10 +374,26 @@ static void usb_rx_process (void) {
|
||||
p.response_pending = true;
|
||||
p.response_error = true;
|
||||
p.response_info.data_length = 4;
|
||||
p.response_info.data[0] = 0xFF;
|
||||
p.response_info.data[0] = 0xFFFFFFFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p.rx_state == RX_STATE_FLUSH) {
|
||||
if (usb_dma_ready()) {
|
||||
if (p.rx_args[1] != 0) {
|
||||
uint32_t length = (p.rx_args[1] > RX_FLUSH_LENGTH) ? RX_FLUSH_LENGTH : p.rx_args[1];
|
||||
fpga_reg_set(REG_USB_DMA_ADDRESS, RX_FLUSH_ADDRESS);
|
||||
fpga_reg_set(REG_USB_DMA_LENGTH, length);
|
||||
fpga_reg_set(REG_USB_DMA_SCR, DMA_SCR_DIRECTION | DMA_SCR_START);
|
||||
p.rx_args[1] -= length;
|
||||
} else {
|
||||
p.rx_state = RX_STATE_IDLE;
|
||||
p.response_pending = true;
|
||||
p.response_error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_tx_process (void) {
|
||||
|
@ -42,8 +42,11 @@ enum Commands {
|
||||
/// Upload ROM (and save) to the SC64
|
||||
Upload(UploadArgs),
|
||||
|
||||
/// Download save and write it to file
|
||||
DownloadSave(DownloadSaveArgs),
|
||||
/// Download specific memory region and write it to file
|
||||
Download {
|
||||
#[command(subcommand)]
|
||||
command: DownloadCommands,
|
||||
},
|
||||
|
||||
/// Upload ROM, 64DD IPL and run disk server
|
||||
_64DD(_64DDArgs),
|
||||
@ -99,9 +102,15 @@ struct UploadArgs {
|
||||
tv: Option<TvType>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum DownloadCommands {
|
||||
/// Download save and write it to file
|
||||
Save(DownloadArgs),
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
struct DownloadSaveArgs {
|
||||
/// Path to the save file
|
||||
struct DownloadArgs {
|
||||
/// Path to the file
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
@ -269,7 +278,7 @@ fn handle_command(command: &Commands, port: Option<String>, remote: Option<Strin
|
||||
let result = match command {
|
||||
Commands::List => handle_list_command(),
|
||||
Commands::Upload(args) => handle_upload_command(connection, args),
|
||||
Commands::DownloadSave(args) => handle_download_save_command(connection, args),
|
||||
Commands::Download { command } => handle_download_command(connection, command),
|
||||
Commands::_64DD(args) => handle_64dd_command(connection, args),
|
||||
Commands::Debug(args) => handle_debug_command(connection, args),
|
||||
Commands::Dump(args) => handle_dump_command(connection, args),
|
||||
@ -352,17 +361,21 @@ fn handle_upload_command(connection: Connection, args: &UploadArgs) -> Result<()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_download_save_command(
|
||||
fn handle_download_command(
|
||||
connection: Connection,
|
||||
args: &DownloadSaveArgs,
|
||||
command: &DownloadCommands,
|
||||
) -> Result<(), sc64::Error> {
|
||||
let mut sc64 = init_sc64(connection, true)?;
|
||||
|
||||
let (mut file, name) = create_file(&args.path)?;
|
||||
match command {
|
||||
DownloadCommands::Save(args) => {
|
||||
let (mut file, name) = create_file(&args.path)?;
|
||||
|
||||
log_wait(format!("Downloading save [{name}]"), || {
|
||||
sc64.download_save(&mut file)
|
||||
})?;
|
||||
log_wait(format!("Downloading save [{name}]"), || {
|
||||
sc64.download_save(&mut file)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -391,7 +404,7 @@ fn handle_debug_command(connection: Connection, args: &DebugArgs) -> Result<(),
|
||||
if args.isv.is_some() {
|
||||
sc64.configure_is_viewer_64(args.isv)?;
|
||||
println!(
|
||||
"IS-Viewer configured and listening at offset [0x{:08X}]",
|
||||
"IS-Viewer configured and listening at ROM offset [0x{:08X}]",
|
||||
args.isv.unwrap()
|
||||
);
|
||||
}
|
||||
@ -433,16 +446,14 @@ fn handle_dump_command(connection: Connection, args: &DumpArgs) -> Result<(), sc
|
||||
|
||||
let (mut dump_file, dump_name) = create_file(&args.path)?;
|
||||
|
||||
let data = log_wait(
|
||||
log_wait(
|
||||
format!(
|
||||
"Dumping from [0x{:08X}] length [0x{:X}] to [{dump_name}]",
|
||||
args.address, args.length
|
||||
),
|
||||
|| sc64.dump_memory(args.address, args.length),
|
||||
|| sc64.dump_memory(&mut dump_file, args.address, args.length),
|
||||
)?;
|
||||
|
||||
dump_file.write(&data)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -542,7 +553,7 @@ fn handle_firmware_command(
|
||||
println!("{}", "Firmware metadata:".bold());
|
||||
println!("{}", metadata);
|
||||
|
||||
backup_file.write(&firmware)?;
|
||||
backup_file.write_all(&firmware)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -603,7 +614,11 @@ fn log_wait<F: FnOnce() -> Result<T, E>, T, E>(message: String, operation: F) ->
|
||||
print!("{}... ", message);
|
||||
stdout().flush().unwrap();
|
||||
let result = operation();
|
||||
println!("done");
|
||||
if result.is_ok() {
|
||||
println!("done");
|
||||
} else {
|
||||
println!("error!");
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ pub fn guess_save_type<T: Read + Seek>(
|
||||
let mut ed64_header = vec![0u8; 4];
|
||||
|
||||
reader.seek(SeekFrom::Start(0x3C))?;
|
||||
reader.read(&mut ed64_header)?;
|
||||
reader.read_exact(&mut ed64_header)?;
|
||||
|
||||
if &ed64_header[0..2] == b"ED" {
|
||||
return Ok((
|
||||
@ -38,7 +38,7 @@ pub fn guess_save_type<T: Read + Seek>(
|
||||
let mut pi_config = vec![0u8; 4];
|
||||
|
||||
reader.rewind()?;
|
||||
reader.read(&mut pi_config)?;
|
||||
reader.read_exact(&mut pi_config)?;
|
||||
|
||||
let endian_swapper = match &pi_config[0..4] {
|
||||
[0x37, 0x80, 0x40, 0x12] => |b: &mut [u8]| b.chunks_exact_mut(2).for_each(|c| c.swap(0, 1)),
|
||||
|
@ -29,10 +29,10 @@ enum DataType {
|
||||
}
|
||||
|
||||
pub trait Link {
|
||||
fn execute_command(&mut self, command: &mut Command) -> Result<Vec<u8>, Error>;
|
||||
fn execute_command(&mut self, command: &Command) -> Result<Vec<u8>, Error>;
|
||||
fn execute_command_raw(
|
||||
&mut self,
|
||||
command: &mut Command,
|
||||
command: &Command,
|
||||
timeout: Duration,
|
||||
no_response: bool,
|
||||
ignore_error: bool,
|
||||
@ -79,20 +79,16 @@ impl SerialLink {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serial_send_command(
|
||||
&mut self,
|
||||
command: &mut Command,
|
||||
timeout: Duration,
|
||||
) -> Result<(), Error> {
|
||||
let mut packet: Vec<u8> = Vec::new();
|
||||
packet.append(&mut b"CMD".to_vec());
|
||||
packet.append(&mut [command.id].to_vec());
|
||||
packet.append(&mut command.args[0].to_be_bytes().to_vec());
|
||||
packet.append(&mut command.args[1].to_be_bytes().to_vec());
|
||||
packet.append(&mut command.data);
|
||||
fn serial_send_command(&mut self, command: &Command, timeout: Duration) -> Result<(), Error> {
|
||||
let mut header: Vec<u8> = Vec::new();
|
||||
header.append(&mut b"CMD".to_vec());
|
||||
header.append(&mut [command.id].to_vec());
|
||||
header.append(&mut command.args[0].to_be_bytes().to_vec());
|
||||
header.append(&mut command.args[1].to_be_bytes().to_vec());
|
||||
|
||||
self.serial.set_timeout(timeout)?;
|
||||
self.serial.write_all(&packet)?;
|
||||
self.serial.write_all(&header)?;
|
||||
self.serial.write_all(&command.data)?;
|
||||
self.serial.flush()?;
|
||||
|
||||
Ok(())
|
||||
@ -138,7 +134,7 @@ impl SerialLink {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn send_command(&mut self, command: &mut Command) -> Result<(), Error> {
|
||||
fn send_command(&mut self, command: &Command) -> Result<(), Error> {
|
||||
self.serial_send_command(command, COMMAND_TIMEOUT)
|
||||
}
|
||||
|
||||
@ -151,13 +147,13 @@ impl SerialLink {
|
||||
}
|
||||
|
||||
impl Link for SerialLink {
|
||||
fn execute_command(&mut self, command: &mut Command) -> Result<Vec<u8>, Error> {
|
||||
fn execute_command(&mut self, command: &Command) -> Result<Vec<u8>, Error> {
|
||||
self.execute_command_raw(command, COMMAND_TIMEOUT, false, false)
|
||||
}
|
||||
|
||||
fn execute_command_raw(
|
||||
&mut self,
|
||||
command: &mut Command,
|
||||
command: &Command,
|
||||
timeout: Duration,
|
||||
no_response: bool,
|
||||
ignore_error: bool,
|
||||
|
@ -84,7 +84,6 @@ const SRAM_BANKED_LENGTH: usize = 3 * 32 * 1024;
|
||||
const FLASHRAM_LENGTH: usize = 128 * 1024;
|
||||
|
||||
const BOOTLOADER_ADDRESS: u32 = 0x04E0_0000;
|
||||
const BOOTLOADER_LENGTH: usize = 1920 * 1024;
|
||||
|
||||
const FIRMWARE_ADDRESS: u32 = 0x0010_0000; // Arbitrary offset in SDRAM memory
|
||||
const FIRMWARE_COMMAND_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
@ -96,11 +95,11 @@ const ISV_BUFFER_LENGTH: usize = 64 * 1024;
|
||||
|
||||
pub const MEMORY_LENGTH: usize = 0x0500_2980;
|
||||
|
||||
const MEMORY_WRITE_CHUNK_LENGTH: usize = 1 * 1024 * 1024;
|
||||
const MEMORY_CHUNK_LENGTH: usize = 1 * 1024 * 1024;
|
||||
|
||||
impl SC64 {
|
||||
fn command_identifier_get(&mut self) -> Result<Vec<u8>, Error> {
|
||||
let identifier = self.link.execute_command(&mut Command {
|
||||
let identifier = self.link.execute_command(&Command {
|
||||
id: b'v',
|
||||
args: [0, 0],
|
||||
data: vec![],
|
||||
@ -109,7 +108,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_version_get(&mut self) -> Result<(u16, u16), Error> {
|
||||
let version = self.link.execute_command(&mut Command {
|
||||
let version = self.link.execute_command(&Command {
|
||||
id: b'V',
|
||||
args: [0, 0],
|
||||
data: vec![],
|
||||
@ -120,7 +119,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_state_reset(&mut self) -> Result<(), Error> {
|
||||
self.link.execute_command(&mut Command {
|
||||
self.link.execute_command(&Command {
|
||||
id: b'R',
|
||||
args: [0, 0],
|
||||
data: vec![],
|
||||
@ -137,7 +136,7 @@ impl SC64 {
|
||||
let mut params: Vec<u8> = vec![];
|
||||
params.append(&mut [(disable as u8) << 0, seed].to_vec());
|
||||
params.append(&mut checksum.to_vec());
|
||||
self.link.execute_command(&mut Command {
|
||||
self.link.execute_command(&Command {
|
||||
id: b'B',
|
||||
args: args_from_vec(¶ms[0..8])?,
|
||||
data: vec![],
|
||||
@ -146,7 +145,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_config_get(&mut self, config_id: ConfigId) -> Result<Config, Error> {
|
||||
let data = self.link.execute_command(&mut Command {
|
||||
let data = self.link.execute_command(&Command {
|
||||
id: b'c',
|
||||
args: [config_id.into(), 0],
|
||||
data: vec![],
|
||||
@ -156,7 +155,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_config_set(&mut self, config: Config) -> Result<(), Error> {
|
||||
self.link.execute_command(&mut Command {
|
||||
self.link.execute_command(&Command {
|
||||
id: b'C',
|
||||
args: config.into(),
|
||||
data: vec![],
|
||||
@ -165,7 +164,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_setting_get(&mut self, setting_id: SettingId) -> Result<Setting, Error> {
|
||||
let data = self.link.execute_command(&mut Command {
|
||||
let data = self.link.execute_command(&Command {
|
||||
id: b'a',
|
||||
args: [setting_id.into(), 0],
|
||||
data: vec![],
|
||||
@ -175,7 +174,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_setting_set(&mut self, setting: Setting) -> Result<(), Error> {
|
||||
self.link.execute_command(&mut Command {
|
||||
self.link.execute_command(&Command {
|
||||
id: b'A',
|
||||
args: setting.into(),
|
||||
data: vec![],
|
||||
@ -184,7 +183,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_time_get(&mut self) -> Result<DateTime<Local>, Error> {
|
||||
let data = self.link.execute_command(&mut Command {
|
||||
let data = self.link.execute_command(&Command {
|
||||
id: b't',
|
||||
args: [0, 0],
|
||||
data: vec![],
|
||||
@ -193,7 +192,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_time_set(&mut self, datetime: DateTime<Local>) -> Result<(), Error> {
|
||||
self.link.execute_command(&mut Command {
|
||||
self.link.execute_command(&Command {
|
||||
id: b'T',
|
||||
args: args_from_vec(&vec_from_datetime(datetime)?[0..8])?,
|
||||
data: vec![],
|
||||
@ -202,7 +201,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_memory_read(&mut self, address: u32, length: usize) -> Result<Vec<u8>, Error> {
|
||||
let data = self.link.execute_command(&mut Command {
|
||||
let data = self.link.execute_command(&Command {
|
||||
id: b'm',
|
||||
args: [address, length as u32],
|
||||
data: vec![],
|
||||
@ -211,7 +210,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_memory_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {
|
||||
self.link.execute_command(&mut Command {
|
||||
self.link.execute_command(&Command {
|
||||
id: b'M',
|
||||
args: [address, data.len() as u32],
|
||||
data: data.to_vec(),
|
||||
@ -221,7 +220,7 @@ impl SC64 {
|
||||
|
||||
fn command_usb_write(&mut self, datatype: u8, data: &[u8]) -> Result<(), Error> {
|
||||
self.link.execute_command_raw(
|
||||
&mut Command {
|
||||
&Command {
|
||||
id: b'U',
|
||||
args: [datatype as u32, data.len() as u32],
|
||||
data: data.to_vec(),
|
||||
@ -234,7 +233,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_dd_set_block_ready(&mut self, error: bool) -> Result<(), Error> {
|
||||
self.link.execute_command(&mut Command {
|
||||
self.link.execute_command(&Command {
|
||||
id: b'D',
|
||||
args: [error as u32, 0],
|
||||
data: vec![],
|
||||
@ -243,7 +242,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_flash_wait_busy(&mut self, wait: bool) -> Result<u32, Error> {
|
||||
let erase_block_size = self.link.execute_command(&mut Command {
|
||||
let erase_block_size = self.link.execute_command(&Command {
|
||||
id: b'p',
|
||||
args: [wait as u32, 0],
|
||||
data: vec![],
|
||||
@ -252,7 +251,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_flash_erase_block(&mut self, address: u32) -> Result<(), Error> {
|
||||
self.link.execute_command(&mut Command {
|
||||
self.link.execute_command(&Command {
|
||||
id: b'P',
|
||||
args: [address, 0],
|
||||
data: vec![],
|
||||
@ -262,7 +261,7 @@ impl SC64 {
|
||||
|
||||
fn command_firmware_backup(&mut self, address: u32) -> Result<(FirmwareStatus, u32), Error> {
|
||||
let data = self.link.execute_command_raw(
|
||||
&mut Command {
|
||||
&Command {
|
||||
id: b'f',
|
||||
args: [address, 0],
|
||||
data: vec![],
|
||||
@ -282,7 +281,7 @@ impl SC64 {
|
||||
length: usize,
|
||||
) -> Result<FirmwareStatus, Error> {
|
||||
let data = self.link.execute_command_raw(
|
||||
&mut Command {
|
||||
&Command {
|
||||
id: b'F',
|
||||
args: [address, length as u32],
|
||||
data: vec![],
|
||||
@ -296,7 +295,7 @@ impl SC64 {
|
||||
|
||||
fn command_debug_get(&mut self) -> Result<FpgaDebugData, Error> {
|
||||
self.link
|
||||
.execute_command(&mut Command {
|
||||
.execute_command(&Command {
|
||||
id: b'?',
|
||||
args: [0, 0],
|
||||
data: vec![],
|
||||
@ -306,7 +305,7 @@ impl SC64 {
|
||||
|
||||
fn command_stack_usage_get(&mut self) -> Result<McuStackUsage, Error> {
|
||||
self.link
|
||||
.execute_command(&mut Command {
|
||||
.execute_command(&Command {
|
||||
id: b'%',
|
||||
args: [0, 0],
|
||||
data: vec![],
|
||||
@ -329,7 +328,8 @@ impl SC64 {
|
||||
let mut pi_config = vec![0u8; 4];
|
||||
|
||||
reader.rewind()?;
|
||||
reader.read(&mut pi_config)?;
|
||||
reader.read_exact(&mut pi_config)?;
|
||||
reader.rewind()?;
|
||||
|
||||
let endian_swapper = match &pi_config[0..4] {
|
||||
[0x37, 0x80, 0x40, 0x12] => {
|
||||
@ -353,8 +353,6 @@ impl SC64 {
|
||||
min(length, SDRAM_LENGTH)
|
||||
};
|
||||
|
||||
reader.rewind()?;
|
||||
|
||||
self.memory_write_chunked(reader, SDRAM_ADDRESS, sdram_length, Some(endian_swapper))?;
|
||||
|
||||
self.command_config_set(Config::RomShadowEnable(rom_shadow_enabled.into()))?;
|
||||
@ -427,18 +425,19 @@ impl SC64 {
|
||||
SaveType::Flashram => (SAVE_ADDRESS, FLASHRAM_LENGTH),
|
||||
};
|
||||
|
||||
let mut data = self.command_memory_read(address, save_length)?;
|
||||
|
||||
writer.write_all(&mut data)?;
|
||||
|
||||
Ok(())
|
||||
self.memory_read_chunked(writer, address, save_length)
|
||||
}
|
||||
|
||||
pub fn dump_memory(&mut self, address: u32, length: usize) -> Result<Vec<u8>, Error> {
|
||||
pub fn dump_memory<T: Write>(
|
||||
&mut self,
|
||||
writer: &mut T,
|
||||
address: u32,
|
||||
length: usize,
|
||||
) -> Result<(), Error> {
|
||||
if address + length as u32 > MEMORY_LENGTH as u32 {
|
||||
return Err(Error::new("Invalid dump address or length"));
|
||||
}
|
||||
self.command_memory_read(address, length)
|
||||
self.memory_read_chunked(writer, address, length)
|
||||
}
|
||||
|
||||
pub fn calculate_cic_parameters(&mut self) -> Result<(), Error> {
|
||||
@ -637,11 +636,22 @@ impl SC64 {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_bootloader(&mut self, reader: &mut dyn Read, length: usize) -> Result<(), Error> {
|
||||
if length > BOOTLOADER_LENGTH {
|
||||
return Err(Error::new("Bootloader length too big"));
|
||||
fn memory_read_chunked(
|
||||
&mut self,
|
||||
writer: &mut dyn Write,
|
||||
address: u32,
|
||||
length: usize,
|
||||
) -> Result<(), Error> {
|
||||
let mut memory_address = address;
|
||||
let mut bytes_left = length;
|
||||
while bytes_left > 0 {
|
||||
let bytes = min(MEMORY_CHUNK_LENGTH, bytes_left);
|
||||
let data = self.command_memory_read(memory_address, bytes)?;
|
||||
writer.write_all(&data)?;
|
||||
memory_address += bytes as u32;
|
||||
bytes_left -= bytes;
|
||||
}
|
||||
self.flash_program(reader, BOOTLOADER_ADDRESS, length, None)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn memory_write_chunked(
|
||||
@ -651,13 +661,19 @@ impl SC64 {
|
||||
length: usize,
|
||||
transform: Option<fn(&mut [u8])>,
|
||||
) -> Result<(), Error> {
|
||||
let mut data: Vec<u8> = vec![0u8; MEMORY_WRITE_CHUNK_LENGTH];
|
||||
for offset in (0..length).step_by(MEMORY_WRITE_CHUNK_LENGTH) {
|
||||
let chunk = reader.read(&mut data)?;
|
||||
if let Some(transform) = transform {
|
||||
transform(&mut data);
|
||||
let mut limited_reader = reader.take(length as u64);
|
||||
let mut memory_address = address;
|
||||
let mut data: Vec<u8> = vec![0u8; MEMORY_CHUNK_LENGTH];
|
||||
loop {
|
||||
let bytes = limited_reader.read(&mut data)?;
|
||||
if bytes == 0 {
|
||||
break;
|
||||
}
|
||||
self.command_memory_write(address + offset as u32, &data[0..chunk])?;
|
||||
if let Some(transform) = transform {
|
||||
transform(&mut data[0..bytes]);
|
||||
}
|
||||
self.command_memory_write(memory_address, &data[0..bytes])?;
|
||||
memory_address += bytes as u32;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -669,6 +669,7 @@ pub enum FirmwareStatus {
|
||||
ErrSize,
|
||||
ErrUnknownChunk,
|
||||
ErrRead,
|
||||
ErrAddress,
|
||||
}
|
||||
|
||||
impl Display for FirmwareStatus {
|
||||
@ -680,6 +681,7 @@ impl Display for FirmwareStatus {
|
||||
FirmwareStatus::ErrSize => "Invalid firmware size",
|
||||
FirmwareStatus::ErrUnknownChunk => "Unknown chunk in firmware",
|
||||
FirmwareStatus::ErrRead => "Firmware read error",
|
||||
FirmwareStatus::ErrAddress => "Invalid address or length provided",
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -694,6 +696,7 @@ impl TryFrom<u32> for FirmwareStatus {
|
||||
3 => Self::ErrSize,
|
||||
4 => Self::ErrUnknownChunk,
|
||||
5 => Self::ErrRead,
|
||||
6 => Self::ErrAddress,
|
||||
_ => return Err(Error::new("Unknown firmware status code")),
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user