diff --git a/sw/controller/src/flash.c b/sw/controller/src/flash.c index 8d32ca3..8ab204a 100644 --- a/sw/controller/src/flash.c +++ b/sw/controller/src/flash.c @@ -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; diff --git a/sw/controller/src/flash.h b/sw/controller/src/flash.h index d2b2cb1..a54c535 100644 --- a/sw/controller/src/flash.h +++ b/sw/controller/src/flash.h @@ -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 diff --git a/sw/controller/src/update.c b/sw/controller/src/update.c index 52549cf..521e6a3 100644 --- a/sw/controller/src/update.c +++ b/sw/controller/src/update.c @@ -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; } diff --git a/sw/controller/src/update.h b/sw/controller/src/update.h index b6f029c..1b2ff4d 100644 --- a/sw/controller/src/update.h +++ b/sw/controller/src/update.h @@ -13,6 +13,7 @@ typedef enum { UPDATE_ERROR_SIZE, UPDATE_ERROR_UNKNOWN_CHUNK, UPDATE_ERROR_READ, + UPDATE_ERROR_ADDRESS, } update_error_t; diff --git a/sw/controller/src/usb.c b/sw/controller/src/usb.c index a694694..2ea6a3b 100644 --- a/sw/controller/src/usb.c +++ b/sw/controller/src/usb.c @@ -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) { diff --git a/sw/deployer/src/main.rs b/sw/deployer/src/main.rs index ed8a987..457c500 100644 --- a/sw/deployer/src/main.rs +++ b/sw/deployer/src/main.rs @@ -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, } +#[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, remote: Option 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 Result, 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 } diff --git a/sw/deployer/src/n64.rs b/sw/deployer/src/n64.rs index 7c6d460..f6b25ae 100644 --- a/sw/deployer/src/n64.rs +++ b/sw/deployer/src/n64.rs @@ -18,7 +18,7 @@ pub fn guess_save_type( 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( 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)), diff --git a/sw/deployer/src/sc64/link.rs b/sw/deployer/src/sc64/link.rs index 5be16cf..642e108 100644 --- a/sw/deployer/src/sc64/link.rs +++ b/sw/deployer/src/sc64/link.rs @@ -29,10 +29,10 @@ enum DataType { } pub trait Link { - fn execute_command(&mut self, command: &mut Command) -> Result, Error>; + fn execute_command(&mut self, command: &Command) -> Result, 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 = 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 = 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, Error> { + fn execute_command(&mut self, command: &Command) -> Result, 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, diff --git a/sw/deployer/src/sc64/mod.rs b/sw/deployer/src/sc64/mod.rs index dfae9ae..c14da27 100644 --- a/sw/deployer/src/sc64/mod.rs +++ b/sw/deployer/src/sc64/mod.rs @@ -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, 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 = 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 { - 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 { - 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, 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) -> 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, 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 { - 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 { 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 { 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 { 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, Error> { + pub fn dump_memory( + &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, ) -> Result<(), Error> { - let mut data: Vec = 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 = 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(()) } diff --git a/sw/deployer/src/sc64/types.rs b/sw/deployer/src/sc64/types.rs index 0b75199..158e87d 100644 --- a/sw/deployer/src/sc64/types.rs +++ b/sw/deployer/src/sc64/types.rs @@ -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 for FirmwareStatus { 3 => Self::ErrSize, 4 => Self::ErrUnknownChunk, 5 => Self::ErrRead, + 6 => Self::ErrAddress, _ => return Err(Error::new("Unknown firmware status code")), }) }