summaryrefslogtreecommitdiffstats
path: root/apps/plugins/iriver_flash.c
diff options
context:
space:
mode:
authorJames Buren <braewoods+rb@braewoods.net>2020-11-11 10:54:05 +0000
committerJames Buren <braewoods+rb@braewoods.net>2020-11-11 13:30:09 +0000
commit4aa749b59db9fb46a47a0bcff4f19a6f0552b2aa (patch)
tree39784c771741613847e83dce95b122f27cf485e6 /apps/plugins/iriver_flash.c
parentc4254d10fc61779656dd5692c2f6333692ba73f8 (diff)
downloadrockbox-4aa749b59d.tar.gz
rockbox-4aa749b59d.tar.bz2
rockbox-4aa749b59d.zip
iriver_flash: refactor code to be easier to understand
This overhauls most of the code to be easier to understand in terms of the interactions with the flash. I found the original to be rather confusing with how it kept switching between byte and word offsets. My solution was to make all external access to the flash in terms of sectors and bytes. Whatever the flash uses internally is now handled by the subroutines for performing the erase, program, and verify operations. This helps make it far more consistent for the code that actually uses these operations as they do not need to concern themselves with word sizes and offsets anymore. As a side effect of this change the flash operations are now done entirely by subroutines; even the batch operations that used to use custom loops. Additionally some functions were merged with other functions in order to reduce the amount of functions as well as consolidating common code fragments. Change-Id: I4698e920a226a3bbe8070004a14e5848abdd70ec
Diffstat (limited to 'apps/plugins/iriver_flash.c')
-rw-r--r--apps/plugins/iriver_flash.c1123
1 files changed, 612 insertions, 511 deletions
diff --git a/apps/plugins/iriver_flash.c b/apps/plugins/iriver_flash.c
index 9d1c3456a6..e5da55c7ed 100644
--- a/apps/plugins/iriver_flash.c
+++ b/apps/plugins/iriver_flash.c
@@ -9,7 +9,8 @@
*
* !!! DON'T MESS WITH THIS CODE UNLESS YOU'RE ABSOLUTELY SURE WHAT YOU DO !!!
*
- * Copyright (C) 2006 by Miika Pekkarinen
+ * Copyright (C) 2020 by James Buren (refactor + H300 support)
+ * Copyright (C) 2006 by Miika Pekkarinen (original + H100/H120 support)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -22,93 +23,185 @@
****************************************************************************/
#include "plugin.h"
-/* cfi_program_word() relies on writing to address 0, which normally is illegal.
- So we need this to ensure we don't helpfully optimize it away into a TRAP
- when compiled with -fdelete-null-pointer-checks, which is enabled by default
- at -Os with our current gcc 4.9.x toolchain.
-*/
+/*
+ * Flash commands may rely on null pointer dereferences to work correctly.
+ * Disable this feature of GCC that may interfere with proper code generation.
+ */
#pragma GCC optimize "no-delete-null-pointer-checks"
-/* All CFI flash routines are copied and ported from firmware_flash.c */
+enum firmware
+{
+ FIRMWARE_ROCKBOX, /* all .iriver firmwares */
+ FIRMWARE_ROMDUMP, /* a debug romdump */
+ FIRMWARE_ORIGINAL, /* an unscrambled original firmware */
+};
+
+#define WORD_SIZE 2
+#define BOOT_VECTOR_SIZE 8
+#define BOOT_SECTOR_OFFSET 0
+#define SECTOR_SIZE 4096
+#define BOOTLOADER_MAX_SIZE 65536
+#define BOOTLOADER_SECTORS (BOOTLOADER_MAX_SIZE / SECTOR_SIZE)
+#define RAM_IMAGE_RAW_SIZE (FLASH_ROMIMAGE_ENTRY - FLASH_RAMIMAGE_ENTRY)
+#define RAM_IMAGE_MAX_SIZE (RAM_IMAGE_RAW_SIZE - sizeof(struct flash_header))
+#define RAM_IMAGE_SECTORS (RAM_IMAGE_RAW_SIZE / SECTOR_SIZE)
+#define ROM_IMAGE_RAW_SIZE (BOOTLOADER_ENTRYPOINT - FLASH_ROMIMAGE_ENTRY)
+#define ROM_IMAGE_MAX_SIZE (ROM_IMAGE_RAW_SIZE - sizeof(struct flash_header))
+#define ROM_IMAGE_SECTORS (ROM_IMAGE_RAW_SIZE / SECTOR_SIZE)
+#define ROM_IMAGE_RELOCATION (FLASH_ROMIMAGE_ENTRY + sizeof(struct flash_header))
+#define WHOLE_FIRMWARE_SECTORS (BOOTLOADER_ENTRYPOINT / SECTOR_SIZE)
+#define FIRMWARE_OFFSET 544
+
+#if BOOTLOADER_ENTRYPOINT + BOOTLOADER_MAX_SIZE != FLASH_SIZE
+#error "Bootloader is not located at the end of flash."
+#endif
-uint8_t* audiobuf;
-ssize_t audiobuf_size;
+#if FLASH_ROMIMAGE_ENTRY < FLASH_RAMIMAGE_ENTRY
+#error "RAM image must be located before the ROM image."
+#endif
+
+#if BOOTLOADER_ENTRYPOINT < FLASH_ROMIMAGE_ENTRY
+#error "ROM image must be located before the bootloader."
+#endif
-#if !defined(IRIVER_H100_SERIES) && !defined(IRIVER_H300_SERIES)
-#error this platform is not (yet) flashable
+#if FLASH_SIZE == 2048 * 1024
+#define ROMDUMP "/internal_rom_000000-1FFFFF.bin"
+#elif FLASH_SIZE == 4096 * 1024
+#define ROMDUMP "/internal_rom_000000-3FFFFF.bin"
#endif
-#if CONFIG_KEYPAD == IRIVER_H100_PAD
-#define KEY1 BUTTON_OFF
-#define KEY2 BUTTON_ON
-#define KEY3 BUTTON_SELECT
-#define KEYNAME1 "[Stop]"
-#define KEYNAME2 "[On]"
-#define KEYNAME3 "[Select]"
+#ifdef IRIVER_H100
+#define MODEL (const uint8_t[]) { 'h', '1', '0', '0' }
+#define ORIGINAL "/ihp_100.bin"
+#elif defined(IRIVER_H120)
+#define MODEL (const uint8_t[]) { 'h', '1', '2', '0' }
+#define ORIGINAL "/ihp_120.bin"
+#elif defined(IRIVER_H300)
+#define MODEL (const uint8_t[]) { 'h', '3', '0', '0' }
+#define ORIGINAL "/H300.bin"
+#else
+#error "Unsupported target."
#endif
struct flash_info
{
- uint16_t manufacturer;
- uint16_t id;
+ uint16_t vendor;
+ uint16_t product;
uint32_t size;
- char name[32];
+ char name[16];
};
-#if defined(IRIVER_H100_SERIES) || defined(IRIVER_H300_SERIES)
-#define SEC_SIZE 4096
-#define BOOTLOADER_ERASEGUARD (BOOTLOADER_ENTRYPOINT / SEC_SIZE)
-enum sections {
- SECT_RAMIMAGE = 1,
- SECT_ROMIMAGE = 2,
-};
-
-static volatile uint16_t* FB = (uint16_t*)0x00000000; /* Flash base address */
+/* checks if the region has a valid bootloader */
+static bool detect_valid_bootloader(const void* ptr, uint32_t size)
+{
+ static const struct
+ {
+ uint32_t size;
+ uint32_t crc32;
+ }
+ bootloaders[] =
+ {
+#ifdef IRIVER_H100
+ { 48760, 0x2efc3323 }, /* 7-pre4 */
+ { 56896, 0x0cd8dad4 }, /* 7-pre5 */
+#elif defined(IRIVER_H120)
+ { 63788, 0x08ff01a9 }, /* 7-pre3, improved failsafe functions */
+ { 48764, 0xc674323e }, /* 7-pre4. Fixed audio thump & remote bootup */
+ { 56896, 0x167f5d25 }, /* 7-pre5, various ATA fixes */
+#elif defined(IRIVER_H300)
#endif
+ {0}
+ };
-#ifdef IRIVER_H100 /* iRiver H110/H115 */
-#define MODEL "h100"
-#elif defined(IRIVER_H120) /* iRiver H120/H140 */
-#define MODEL "h120"
-#elif defined(IRIVER_H300) /* iRiver H320/H340 */
-#define MODEL "h300"
-#endif
+ for (size_t i = 0; bootloaders[i].size != 0; i++)
+ {
+ uint32_t crc32;
+
+ if (size != 0 && size != bootloaders[i].size)
+ continue;
+
+ crc32 = rb->crc_32(ptr, bootloaders[i].size, 0xFFFFFFFF);
+ if (crc32 == bootloaders[i].crc32)
+ return true;
+ }
+
+ return false;
+}
+
+/* get read-only access to flash at the given offset */
+static const void* flash(uint32_t offset)
+{
+ const uint16_t* FB = (uint16_t*) FLASH_BASE;
+ return &FB[offset / WORD_SIZE];
+}
-/* read the manufacturer and device ID */
-static void cfi_read_id(struct flash_info* pInfo)
+/* queries the rom for information and returns it if it is known */
+static bool flash_get_info(const struct flash_info** out_info)
{
- FB[0x5555] = 0xAA; /* enter command mode */
+ static const struct flash_info roms[] =
+ {
+ { 0x00BF, 0x2782, 2048 * 1024, "SST39VF160" },
+ { 0x00BF, 0x235B, 4096 * 1024, "SST39VF3201" },
+ {0}
+ };
+ static struct flash_info unknown_rom = {0};
+ volatile uint16_t* FB = (uint16_t*) FLASH_BASE;
+ uint16_t vendor;
+ uint16_t product;
+
+ /* execute the software ID entry command */
+ FB[0x5555] = 0xAA;
FB[0x2AAA] = 0x55;
- FB[0x5555] = 0x90; /* enter ID mode */
- rb->sleep(HZ/100); /* we only need to sleep 150ns but 10ms is the minimum */
+ FB[0x5555] = 0x90;
+ rb->sleep(HZ / 100);
- pInfo->manufacturer = FB[0];
- pInfo->id = FB[1];
+ /* copy the IDs from the previous command */
+ vendor = FB[0];
+ product = FB[1];
- FB[0x5555] = 0xAA; /* enter command mode */
+ /* execute the software ID exit command */
+ FB[0x5555] = 0xAA;
FB[0x2AAA] = 0x55;
- FB[0x5555] = 0xF0; /* exit ID mode */
- rb->sleep(HZ/100); /* we only need to sleep 150ns but 10ms is the minimum */
+ FB[0x5555] = 0xF0;
+ rb->sleep(HZ / 100);
+
+ /* search for a known match */
+ for (size_t i = 0; roms[i].size != 0; i++)
+ {
+ if (roms[i].vendor == vendor && roms[i].product == product)
+ {
+ *out_info = &roms[i];
+ return true;
+ }
+ }
+
+ /* return only the vendor / product ids if unknown */
+ unknown_rom.vendor = vendor;
+ unknown_rom.product = product;
+ *out_info = &unknown_rom;
+ return false;
}
/* wait until the rom signals completion of an operation */
-static bool cfi_wait_for_rom(volatile uint16_t* pAddr)
+static bool flash_wait_for_rom(uint32_t offset)
{
- const unsigned MAX_TIMEOUT = 0xffffff; /* should be sufficient for most targets */
- const unsigned RECOVERY_TIME = 64; /* based on 140MHz MCF 5249 */
- uint16_t old_data = *pAddr & 0x0040; /* we only want DQ6 */
- volatile unsigned i; /* disables certain optimizations */
+ const size_t MAX_TIMEOUT = 0xFFFFFF; /* should be sufficient for most targets */
+ const size_t RECOVERY_TIME = 64; /* based on 140MHz MCF 5249 */
+ volatile uint16_t* FB = (uint16_t*) FLASH_BASE;
+ uint16_t old_data = FB[offset / WORD_SIZE] & 0x0040; /* we only want DQ6 */
+ volatile size_t i; /* disables certain optimizations */
+ bool result;
/* repeat up to MAX_TIMEOUT times or until DQ6 stops flipping */
for (i = 0; i < MAX_TIMEOUT; i++)
{
- uint16_t new_data = *pAddr & 0x0040; /* we only want DQ6 */
- if(old_data == new_data)
+ uint16_t new_data = FB[offset / WORD_SIZE] & 0x0040; /* we only want DQ6 */
+ if (old_data == new_data)
break;
old_data = new_data;
}
- bool result = i != MAX_TIMEOUT;
+ result = i != MAX_TIMEOUT;
/* delay at least 1us to give the bus time to recover */
for (i = 0; i < RECOVERY_TIME; i++);
@@ -116,646 +209,654 @@ static bool cfi_wait_for_rom(volatile uint16_t* pAddr)
return result;
}
-/* erase the sector which contains the given address */
-static bool cfi_erase_sector(volatile uint16_t* pAddr)
+/* erase the sector at the given offset */
+static bool flash_erase_sector(uint32_t offset)
{
- FB[0x5555] = 0xAA; /* enter command mode */
+ volatile uint16_t* FB = (uint16_t*) FLASH_BASE;
+
+ /* execute the sector erase command */
+ FB[0x5555] = 0xAA;
FB[0x2AAA] = 0x55;
- FB[0x5555] = 0x80; /* erase command */
- FB[0x5555] = 0xAA; /* enter command mode */
+ FB[0x5555] = 0x80;
+ FB[0x5555] = 0xAA;
FB[0x2AAA] = 0x55;
- *pAddr = 0x30; /* erase the sector */
+ FB[offset / WORD_SIZE] = 0x30;
- return cfi_wait_for_rom(pAddr);
+ return flash_wait_for_rom(offset);
}
-/* address must be in an erased location */
-static bool cfi_program_word(volatile uint16_t* pAddr, uint16_t data)
+/* program a word at the given offset */
+static bool flash_program_word(uint32_t offset, uint16_t word)
{
- FB[0x5555] = 0xAA; /* enter command mode */
+ volatile uint16_t* FB = (uint16_t*) FLASH_BASE;
+
+ /* execute the word program command */
+ FB[0x5555] = 0xAA;
FB[0x2AAA] = 0x55;
- FB[0x5555] = 0xA0; /* byte program command */
- *pAddr = data; /* write the word data */
+ FB[0x5555] = 0xA0;
+ FB[offset / WORD_SIZE] = word;
- return cfi_wait_for_rom(pAddr);
+ return flash_wait_for_rom(offset);
}
-/* fills in the struct with data about the flash rom */
-static void cfi_get_flash_info(struct flash_info* pInfo)
+/* bulk erase of adjacent sectors */
+static void flash_erase_sectors(uint32_t offset, uint32_t sectors,
+ bool progress)
{
- uint32_t size = 0;
- const char* name = "";
-
- cfi_read_id(pInfo);
-
- switch (pInfo->manufacturer)
- {
- /* SST */
- case 0x00BF:
- switch (pInfo->id)
- {
- case 0x2782:
- size = 2048 * 1024; /* 2 MiB */
- name = "SST39VF160";
- break;
- case 0x235B:
- size = 4096 * 1024; /* 4 MiB */
- name = "SST39VF3201";
- break;
- }
- break;
- }
+ for (uint32_t i = 0; i < sectors; i++)
+ {
+ flash_erase_sector(offset + i * SECTOR_SIZE);
- pInfo->size = size;
- rb->strcpy(pInfo->name, name);
+ /* display a progress report if requested */
+ if (progress)
+ {
+ rb->lcd_putsf(0, 3, "Erasing... %u%%", (i + 1) * 100 / sectors);
+ rb->lcd_update();
+ }
+ }
}
-/***************** User Interface Functions *****************/
-static int wait_for_button(void)
+/* bulk program of bytes */
+static void flash_program_bytes(uint32_t offset, const void* ptr,
+ uint32_t len, bool progress)
{
- int button;
+ const uint8_t* data = ptr;
- do
+ for (uint32_t i = 0; i < len; i += WORD_SIZE)
{
- button = rb->button_get(true);
- } while (IS_SYSEVENT(button) || (button & BUTTON_REL));
+ uint32_t j = i + 1;
+ uint32_t k = ((j < len) ? j : i) + 1;
+ uint16_t word = (data[i] << 8) | (j < len ? data[j] : 0xFF);
+
+ flash_program_word(offset + i, word);
- return button;
+ /* display a progress report if requested */
+ if (progress && ((i % SECTOR_SIZE) == 0 || k == len))
+ {
+ rb->lcd_putsf(0, 4, "Programming... %u%%", k * 100 / len);
+ rb->lcd_update();
+ }
+ }
}
-/* helper for DoUserDialog() */
-static void ShowFlashInfo(const struct flash_info* pInfo)
+/* bulk verify of programmed bytes */
+static bool flash_verify_bytes(uint32_t offset, const void* ptr,
+ uint32_t len, bool progress)
{
- if (!pInfo->manufacturer)
- {
- rb->lcd_puts(0, 0, "Flash: M=???? D=????");
- rb->lcd_puts(0, 1, "Impossible to program");
- }
- else
+ const uint8_t* FB = flash(offset);
+ const uint8_t* data = ptr;
+
+ /* don't use memcmp so we can provide progress updates */
+ for (uint32_t i = 0; i < len; i++)
{
- rb->lcd_putsf(0, 0, "Flash: M=%04x D=%04x",
- pInfo->manufacturer, pInfo->id);
+ uint32_t j = i + 1;
- if (pInfo->size)
- {
- rb->lcd_puts(0, 1, pInfo->name);
- rb->lcd_putsf(0, 2, "Size: %u KB", pInfo->size / 1024);
- }
- else
+ if (FB[i] != data[i])
+ return false;
+
+ /* display a progress report if requested */
+ if (progress && ((i % SECTOR_SIZE) == 0 || j == len))
{
- rb->lcd_puts(0, 1, "Unsupported chip");
+ rb->lcd_putsf(0, 5, "Verifying... %u%%", j * 100 / len);
+ rb->lcd_update();
}
}
- rb->lcd_update();
+ return true;
}
+/* print information about the flash chip */
static bool show_info(void)
{
- struct flash_info fi;
+ static const struct flash_info* fi = NULL;
rb->lcd_clear_display();
- cfi_get_flash_info(&fi);
- ShowFlashInfo(&fi);
- if (fi.size == 0) /* no valid chip */
+
+ if (fi == NULL)
+ flash_get_info(&fi);
+
+ rb->lcd_putsf(0, 0, "Flash: V=%04x P=%04x", fi->vendor, fi->product);
+
+ if (fi->size != 0)
+ {
+ rb->lcd_puts(0, 1, fi->name);
+ rb->lcd_putsf(0, 2, "Size: %u KB", fi->size / 1024);
+ }
+ else
{
- rb->splash(HZ*3, "Sorry!");
- return false; /* exit */
+ rb->lcd_puts(0, 1, "Unknown chip");
+ }
+
+ rb->lcd_update();
+
+ if (fi->size == 0)
+ {
+ rb->splash(HZ * 3, "Sorry!");
+ return false;
}
return true;
}
-static bool confirm(const char* msg)
+/* confirm a user's choice */
+static bool confirm_choice(const char* msg)
{
+ long button;
rb->splashf(0, "%s ([PLAY] to CONFIRM)", msg);
- bool ret = (wait_for_button() == BUTTON_ON);
+ do
+ button = rb->button_get(true);
+ while (IS_SYSEVENT(button) || (button & BUTTON_REL));
show_info();
- return ret;
+ return (button == BUTTON_ON);
}
-static off_t load_firmware_file(const char* filename, uint32_t* out_checksum)
+/* all-in-one firmware loader */
+static bool load_firmware(const char* filename, enum firmware firmware,
+ const void** data, size_t* data_len)
{
- int fd;
- off_t len;
- uint32_t checksum;
- char model[4];
- uint32_t sum;
+ bool result = false;
+ const char* msg = NULL;
+ int fd = -1;
+ off_t fd_len;
+ uint8_t* buffer;
+ size_t buffer_len;
fd = rb->open(filename, O_RDONLY);
if (fd < 0)
- return -1;
-
- len = rb->filesize(fd);
- if (audiobuf_size < len)
- {
- rb->splash(HZ*3, "Aborting: Out of memory!");
- rb->close(fd);
- return -2;
- }
-
- if (rb->read(fd, &checksum, sizeof(checksum)) != sizeof(checksum))
{
- rb->splash(HZ*3, "Aborting: Read failure");
- rb->close(fd);
- return -3;
+ msg = "Aborting: open failure";
+ goto bail;
}
- if (rb->read(fd, model, sizeof(model)) != sizeof(model))
- {
- rb->splash(HZ*3, "Aborting: Read failure");
- rb->close(fd);
- return -4;
- }
+ /* get file and buffer lengths and acquire the buffer */
+ fd_len = rb->filesize(fd);
+ buffer = rb->plugin_get_audio_buffer(&buffer_len);
- len -= FIRMWARE_OFFSET_FILE_DATA;
-
- if (rb->read(fd, audiobuf, len) != len)
+ /* ensure there's enough space in the buffer */
+ if ((size_t) fd_len > buffer_len)
{
- rb->splash(HZ*3, "Aborting: Read failure");
- rb->close(fd);
- return -5;
+ msg = "Aborting: out of memory";
+ goto bail;
}
- rb->close(fd);
-
- /* Verify the checksum */
- sum = MODEL_NUMBER;
- for (off_t i = 0; i < len; i++)
- sum += audiobuf[i];
-
- if (sum != checksum)
- {
- rb->splash(HZ*3, "Aborting: Checksums mismatch!");
- return -6;
- }
-
- /* Verify the model */
- if (rb->memcmp(model, MODEL, sizeof(model)) != 0)
+ /* all known firmwares are less than or equal to FLASH_SIZE */
+ if (fd_len > FLASH_SIZE)
{
- rb->splash(HZ*3, "Aborting: Models mismatch!");
- return -7;
+ msg = "Aborting: firmware too big";
+ goto bail;
}
- if (out_checksum != NULL)
- *out_checksum = checksum;
-
- return len;
-}
-
-static const uint32_t valid_bootloaders[][2] = {
- /* Size-8, CRC32 */
-#ifdef IRIVER_H100 /* iRiver H110/H115 */
- { 48760, 0x2efc3323 }, /* 7-pre4 */
- { 56896, 0x0cd8dad4 }, /* 7-pre5 */
-#elif defined(IRIVER_H120) /* iRiver H120/H140 */
- { 63788, 0x08ff01a9 }, /* 7-pre3, improved failsafe functions */
- { 48764, 0xc674323e }, /* 7-pre4. Fixed audio thump & remote bootup */
- { 56896, 0x167f5d25 }, /* 7-pre5, various ATA fixes */
-#elif defined(IRIVER_H300) /* iRiver H320/H340 */
-#endif
- {}
-};
-
-/* check if the bootloader is a known good one */
-static bool detect_valid_bootloader(const uint8_t* pAddr, uint32_t len)
-{
- for (size_t i = 0; valid_bootloaders[i][0]; i++)
+ /* rockbox firmware specific code */
+ if (firmware == FIRMWARE_ROCKBOX)
{
- if (len != valid_bootloaders[i][0])
- continue;
+ uint32_t checksum;
+ uint8_t model[4];
+ uint32_t sum;
- uint32_t crc32 = rb->crc_32(pAddr, valid_bootloaders[i][0], 0xffffffff);
- if (crc32 == valid_bootloaders[i][1])
- return true;
- }
+ /* subtract the header length */
+ fd_len -= sizeof(checksum) + sizeof(model);
- return false;
-}
+ /* sanity check the length */
+ if (fd_len < WORD_SIZE)
+ {
+ msg = "Aborting: firmware too small";
+ goto bail;
+ }
-static int get_section_address(int section)
-{
- if (section == SECT_RAMIMAGE)
- return FLASH_RAMIMAGE_ENTRY;
- else if (section == SECT_ROMIMAGE)
- return FLASH_ROMIMAGE_ENTRY;
- else
- return -1;
-}
+ /* read the various parts */
+ if (
+ rb->read(fd, &checksum, sizeof(checksum)) != sizeof(checksum) ||
+ rb->read(fd, model, sizeof(model)) != sizeof(model) ||
+ rb->read(fd, buffer, fd_len) != fd_len
+ )
+ {
+ msg = "Aborting: read failure";
+ goto bail;
+ }
-int flash_rockbox(const char *filename, int section)
-{
- struct flash_header hdr;
- int pos, i, len/*, rc */;
- unsigned long checksum, sum;
- unsigned char *p8;
- uint16_t *p16;
+ /* calculate the checksum */
+ sum = MODEL_NUMBER;
+ for (off_t i = 0; i < fd_len; i++)
+ sum += buffer[i];
- if (get_section_address(section) < 0)
- return -1;
+ /* verify the checksum */
+ if (sum != checksum)
+ {
+ msg = "Aborting: checksum mismatch";
+ goto bail;
+ }
- p8 = (char *)BOOTLOADER_ENTRYPOINT;
- if (!detect_valid_bootloader(p8, 0))
- {
- rb->splash(HZ*3, "Incompatible bootloader");
- return -1;
+ /* verify the model */
+ if (rb->memcmp(model, MODEL, sizeof(model)) != 0)
+ {
+ msg = "Aborting: model mismatch";
+ goto bail;
+ }
}
- if (!rb->detect_original_firmware())
- {
- if (!confirm("Update Rockbox flash image?"))
- return -2;
- }
- else
+ /* romdump specific code */
+ if (firmware == FIRMWARE_ROMDUMP)
{
- if (!confirm("Erase original firmware?"))
- return -3;
- }
-
- len = load_firmware_file(filename, &checksum);
- if (len <= 0)
- return len * 10;
-
- pos = get_section_address(section);
+ /* the romdump should be exactly the same size as the flash */
+ if (fd_len != FLASH_SIZE)
+ {
+ msg = "Aborting: firmware size incorrect";
+ goto bail;
+ }
- /* Check if image relocation seems to be sane. */
- if (section == SECT_ROMIMAGE)
- {
- uint32_t *p32 = (uint32_t *)audiobuf;
+ /* exclude boot vector and boot loader regions */
+ fd_len = BOOTLOADER_ENTRYPOINT - BOOT_VECTOR_SIZE;
- if (pos+sizeof(struct flash_header) != *p32)
+ /* skip the boot vector */
+ if (rb->lseek(fd, BOOT_VECTOR_SIZE, SEEK_SET) != BOOT_VECTOR_SIZE)
{
- rb->splashf(HZ*10, "Incorrect relocation: 0x%08lx/0x%08lx",
- *p32, pos+sizeof(struct flash_header));
- return -1;
+ msg = "Aborting: lseek failure";
+ goto bail;
}
+ /* read everything up to the boot loader */
+ if (rb->read(fd, buffer, fd_len) != fd_len)
+ {
+ msg = "Aborting: read failure";
+ goto bail;
+ }
}
- /* Erase the program flash. */
- for (i = 0; i + pos < BOOTLOADER_ENTRYPOINT && i < len + 32; i += SEC_SIZE)
+ /* original firmware specific code */
+ if (firmware == FIRMWARE_ORIGINAL)
{
- /* Additional safety check. */
- if (i + pos < SEC_SIZE)
- return -1;
-
- rb->lcd_putsf(0, 3, "Erasing... %d%%", (i+SEC_SIZE)*100/len);
- rb->lcd_update();
+ uint32_t boot_vector[2];
- /*rc = */cfi_erase_sector(FB + (i + pos)/2);
- }
-
- /* Write the magic and size. */
- rb->memset(&hdr, 0, sizeof(struct flash_header));
- hdr.magic = FLASH_MAGIC;
- hdr.length = len;
- // rb->strncpy(hdr.version, rb->rbversion , sizeof(hdr.version)-1);
- p16 = (uint16_t *)&hdr;
+ /* subtract the offset and the size of the boot vector */
+ fd_len -= FIRMWARE_OFFSET + sizeof(boot_vector);
- rb->lcd_puts(0, 4, "Programming...");
- rb->lcd_update();
+ /* sanity check the length */
+ if (fd_len < WORD_SIZE)
+ {
+ msg = "Aborting: firmware too small";
+ goto bail;
+ }
- pos = get_section_address(section)/2;
- for (i = 0; i < (long)sizeof(struct flash_header)/2; i++)
- {
- cfi_program_word(FB + pos, p16[i]);
- pos++;
- }
+ /* skip the leading bytes, whatever they are */
+ if (rb->lseek(fd, FIRMWARE_OFFSET, SEEK_SET) != FIRMWARE_OFFSET)
+ {
+ msg = "Aborting: lseek failure";
+ goto bail;
+ }
- p16 = (uint16_t *)audiobuf;
- for (i = 0; i < len/2 && pos + i < (BOOTLOADER_ENTRYPOINT/2); i++)
- {
- if (i % SEC_SIZE == 0)
+ /* read the various parts */
+ if (
+ rb->read(fd, boot_vector, sizeof(boot_vector)) != sizeof(boot_vector) ||
+ rb->read(fd, buffer, fd_len) != fd_len
+ )
{
- rb->lcd_putsf(0, 4, "Programming... %d%%", (i+1)*100/(len/2));
- rb->lcd_update();
+ msg = "Aborting: read failure";
+ goto bail;
}
- cfi_program_word(FB + pos + i, p16[i]);
+ /* verify the boot vector */
+ if (boot_vector[0] != 0x10017ff0 || boot_vector[1] != 0x00000008)
+ {
+ msg = "Aborting: not an original firmware";
+ goto bail;
+ }
}
- /* Verify */
- rb->lcd_puts(0, 5, "Verifying");
- rb->lcd_update();
-
- p8 = (char *)get_section_address(section);
- p8 += sizeof(struct flash_header);
- sum = MODEL_NUMBER;
- for (i = 0; i < len; i++)
- sum += p8[i];
-
- if (sum != checksum)
- {
- rb->splash(HZ*3, "Verify failed!");
- /* Erase the magic sector so bootloader does not try to load
- * rockbox from flash and crash. */
- if (section == SECT_RAMIMAGE)
- cfi_erase_sector(FB + FLASH_RAMIMAGE_ENTRY/2);
- else
- cfi_erase_sector(FB + FLASH_ROMIMAGE_ENTRY/2);
- return -5;
- }
+ /* write the resulting buffer and length in the output parameters */
+ *data = buffer;
+ *data_len = fd_len;
- rb->splash(HZ*2, "Success");
+ /* mark success */
+ result = true;
- return 0;
+bail: /* common exit code */
+ if (fd >= 0)
+ rb->close(fd);
+ if (msg != NULL)
+ rb->splash(HZ * 3, msg);
+ return result;
}
+/* prints fatal error if a critical failure occurs */
static void show_fatal_error(void)
{
- rb->splash(HZ*30, "Disable idle poweroff, connect AC power and DON'T TURN PLAYER OFF!!");
- rb->splash(HZ*30, "Contact Rockbox developers as soon as possible!");
- rb->splash(HZ*30, "Your device won't be bricked unless you turn off the power");
- rb->splash(HZ*30, "Don't use the device before further instructions from Rockbox developers");
+ rb->splash(HZ * 30, "Disable idle poweroff, connect AC power and DON'T TURN PLAYER OFF!");
+ rb->splash(HZ * 30, "Contact Rockbox developers as soon as possible!");
+ rb->splash(HZ * 30, "Your device won't be bricked unless you turn off the power!");
+ rb->splash(HZ * 30, "Don't use the device before further instructions from Rockbox developers!");
}
-int flash_bootloader(const char *filename)
+/* flash a bootloader */
+static bool flash_bootloader(const char* filename)
{
- char *bootsector;
- int pos, i, len/*, rc*/;
- unsigned long checksum, sum;
- unsigned char *p8;
- uint16_t *p16;
+ bool result = false;
+ const char* msg = NULL;
+ bool show_fatal = false;
+ const void* data;
+ size_t data_len;
+ static uint8_t boot_sector[SECTOR_SIZE];
- bootsector = audiobuf;
- audiobuf += SEC_SIZE;
- audiobuf_size -= SEC_SIZE;
+ /* load the firmware */
+ if (!load_firmware(filename, FIRMWARE_ROCKBOX, &data, &data_len))
+ goto bail;
- if (!confirm("Update bootloader?"))
- return -2;
-
- len = load_firmware_file(filename, &checksum);
- if (len <= 0)
- return len * 10;
-
- if (len > 0xFFFF)
+ /* the bootloader can only be so big */
+ if (data_len > BOOTLOADER_MAX_SIZE)
{
- rb->splash(HZ*3, "Too big bootloader");
- return -1;
+ msg = "Aborting: bootloader too large";
+ goto bail;
}
- /* Verify the crc32 checksum also. */
- if (!detect_valid_bootloader(audiobuf, len))
+ /* only support known bootloaders */
+ if (!detect_valid_bootloader(data, data_len))
{
- rb->splash(HZ*3, "Incompatible/Untested bootloader");
- return -1;
+ msg = "Aborting: bootloader is invalid";
+ goto bail;
}
- rb->lcd_puts(0, 3, "Flashing...");
- rb->lcd_update();
+ /* ask before doing anything dangerous */
+ if (!confirm_choice("Update bootloader?"))
+ goto bail;
- /* Backup the bootloader sector first. */
- p8 = (char *)FB;
- rb->memcpy(bootsector, p8, SEC_SIZE);
+ /* copy the original boot sector */
+ rb->memcpy(boot_sector, flash(BOOT_SECTOR_OFFSET), SECTOR_SIZE);
- /* Erase the boot sector and write a proper reset vector. */
- cfi_erase_sector(FB);
- p16 = (uint16_t *)audiobuf;
- for (i = 0; i < 8/2; i++)
- cfi_program_word(FB + i, p16[i]);
+ /* update the boot vector */
+ rb->memcpy(boot_sector, data, BOOT_VECTOR_SIZE);
- /* And restore original content for original FW to function. */
- p16 = (uint16_t *)bootsector;
- for (i = 8/2; i < SEC_SIZE/2; i++)
- cfi_program_word(FB + i, p16[i]);
+ /* erase the boot sector */
+ flash_erase_sector(BOOT_SECTOR_OFFSET);
- /* Erase the bootloader flash section. */
- for (i = BOOTLOADER_ERASEGUARD; i < BOOTLOADER_ERASEGUARD+16; i++)
- /*rc =*/ cfi_erase_sector(FB + (SEC_SIZE/2) * i);
+ /* erase the bootloader sectors */
+ flash_erase_sectors(BOOTLOADER_ENTRYPOINT, BOOTLOADER_SECTORS, false);
- pos = BOOTLOADER_ENTRYPOINT/2;
- p16 = (uint16_t *)audiobuf;
- for (i = 0; i < len/2; i++)
- cfi_program_word(FB + pos + i, p16[i]);
+ /* program the new boot sector */
+ flash_program_bytes(BOOT_SECTOR_OFFSET, boot_sector, SECTOR_SIZE, false);
- /* Verify */
- p8 = (char *)BOOTLOADER_ENTRYPOINT;
- sum = MODEL_NUMBER;
- for (i = 0; i < len; i++)
- sum += p8[i];
+ /* program the new bootloader */
+ flash_program_bytes(BOOTLOADER_ENTRYPOINT, data, data_len, false);
- if (sum != checksum)
+ /* verify the new boot sector */
+ if (!flash_verify_bytes(BOOT_SECTOR_OFFSET, boot_sector, SECTOR_SIZE, false))
{
- rb->splash(HZ*3, "Verify failed!");
- show_fatal_error();
- return -5;
+ msg = "Boot sector corrupt!";
+ show_fatal = true;
+ goto bail;
}
- p8 = (char *)FB;
- for (i = 0; i < 8; i++)
+ /* verify the new bootloader */
+ if (!flash_verify_bytes(BOOTLOADER_ENTRYPOINT, data, data_len, false))
{
- if (p8[i] != audiobuf[i])
- {
- rb->splash(HZ*3, "Bootvector corrupt!");
- show_fatal_error();
- return -6;
- }
+ msg = "Verify failed!";
+ show_fatal = true;
+ goto bail;
}
- rb->splash(HZ*2, "Success");
+ /* report success */
+ rb->splash(HZ * 3, "Success!");
- return 0;
+ /* mark success */
+ result = true;
+
+bail: /* common exit code */
+ if (msg != NULL)
+ rb->splash(HZ * 3, msg);
+ if (show_fatal)
+ show_fatal_error();
+ return result;
}
-int flash_original_fw(int len)
+/* flash a rockbox ram / rom image */
+static bool flash_rockbox(const char* filename, uint32_t offset)
{
- unsigned char reset_vector[8];
- int pos, i, rc;
- unsigned char *p8;
- uint16_t *p16;
+ bool result = false;
+ const char* msg = NULL;
+ const void* data;
+ size_t data_len;
+ struct flash_header header;
- rb->lcd_puts(0, 3, "Critical section...");
- rb->lcd_update();
-
- p8 = (char *)FB;
- rb->memcpy(reset_vector, p8, sizeof reset_vector);
+ /* load the firmware */
+ if (!load_firmware(filename, FIRMWARE_ROCKBOX, &data, &data_len))
+ goto bail;
- /* Erase the boot sector and write back the reset vector. */
- cfi_erase_sector(FB);
- p16 = (uint16_t *)reset_vector;
- for (i = 0; i < (long)sizeof(reset_vector)/2; i++)
- cfi_program_word(FB + i, p16[i]);
-
- rb->lcd_puts(0, 4, "Flashing orig. FW");
- rb->lcd_update();
-
- /* Erase the program flash. */
- for (i = 1; i < BOOTLOADER_ERASEGUARD && (i-1)*4096 < len; i++)
+ /* sanity check that the offset was set correctly */
+ if (offset != FLASH_RAMIMAGE_ENTRY && offset != FLASH_ROMIMAGE_ENTRY)
{
- rc = cfi_erase_sector(FB + (SEC_SIZE/2) * i);
- rb->lcd_putsf(0, 5, "Erase: 0x%03x (%d)", i, rc);
- rb->lcd_update();
+ msg = "Aborting: invalid image offset";
+ goto bail;
}
- rb->lcd_puts(0, 6, "Programming");
- rb->lcd_update();
-
- pos = 0x00000008/2;
- p16 = (uint16_t *)audiobuf;
- for (i = 0; i < len/2 && pos + i < (BOOTLOADER_ENTRYPOINT/2); i++)
- cfi_program_word(FB + pos + i, p16[i]);
+ /* ensure there's enough room for the ram / rom image */
+ if (
+ (offset == FLASH_RAMIMAGE_ENTRY && data_len > RAM_IMAGE_MAX_SIZE) ||
+ (offset == FLASH_ROMIMAGE_ENTRY && data_len > ROM_IMAGE_MAX_SIZE)
+ )
+ {
+ msg = "Aborting: ram / rom image too large";
+ goto bail;
+ }
- rb->lcd_puts(0, 7, "Verifying");
- rb->lcd_update();
+ /* check for bootloader that can load rockbox from ram / rom */
+ if (!detect_valid_bootloader(flash(BOOTLOADER_ENTRYPOINT), 0))
+ {
+ msg = "Aborting: incompatible bootloader";
+ goto bail;
+ }
- /* Verify reset vectors. */
- p8 = (char *)FB;
- for (i = 0; i < 8; i++)
+ /* rom image specific checks */
+ if (offset == FLASH_ROMIMAGE_ENTRY)
{
- if (p8[i] != reset_vector[i])
+ uint32_t relocation = *((const uint32_t*) data);
+
+ /* sanity check of the image relocation */
+ if (relocation != ROM_IMAGE_RELOCATION)
{
- rb->splash(HZ*3, "Bootvector corrupt!");
- show_fatal_error();
- break;
+ msg = "Aborting: invalid image relocation";
+ goto bail;
}
}
- /* Verify */
- p8 = (char *)0x00000008;
- for (i = 0; i < len; i++)
+ /* ask before doing anything dangerous */
+ if (!rb->detect_original_firmware())
{
- if (p8[i] != audiobuf[i])
- {
- rb->splash(HZ*3, "Verify failed!");
- rb->splashf(HZ*10, "at: 0x%08x", i);
- return -5;
- }
+ if (!confirm_choice("Update Rockbox flash image?"))
+ goto bail;
+ }
+ else
+ {
+ if (!confirm_choice("Erase original firmware?"))
+ goto bail;
}
- rb->splash(HZ*2, "Success");
+ /* erase all ram / rom image sectors */
+ if (offset == FLASH_RAMIMAGE_ENTRY)
+ flash_erase_sectors(offset, RAM_IMAGE_SECTORS, true);
+ else if (offset == FLASH_ROMIMAGE_ENTRY)
+ flash_erase_sectors(offset, ROM_IMAGE_SECTORS, true);
+
+ /* prepare the header */
+ header.magic = FLASH_MAGIC;
+ header.length = data_len;
+ rb->memset(&header.version, 0x00, sizeof(header.version));
+
+ /* program the header */
+ flash_program_bytes(offset, &header, sizeof(header), false);
+
+ /* program the ram / rom image */
+ flash_program_bytes(offset + sizeof(header), data, data_len, true);
+
+ /* verify the header and ram / rom image */
+ if (
+ !flash_verify_bytes(offset, &header, sizeof(header), false) ||
+ !flash_verify_bytes(offset + sizeof(header), data, data_len, true)
+ )
+ {
+ msg = "Verify failed!";
+ /*
+ * erase the ram / rom image header to prevent the bootloader
+ * from trying to boot from it
+ */
+ flash_erase_sector(offset);
+ goto bail;
+ }
- return 0;
+ /* report success */
+ rb->splash(HZ * 3, "Success!");
+
+ /* mark success */
+ result = true;
+
+bail: /* common exit code */
+ if (msg != NULL)
+ rb->splash(HZ * 3, msg);
+ return result;
}
-int load_original_bin(const char *filename)
+/* flash whole firmware; common code for romdump / original */
+static bool flash_whole_firmware(const void* data, size_t data_len)
{
- unsigned long magic[2];
- int len, rc;
- int fd;
+ bool result = false;
+ const char* msg = NULL;
+ bool show_fatal = false;
+ uint8_t boot_vector[BOOT_VECTOR_SIZE];
- if (!confirm("Restore original firmware (bootloader will be kept)?"))
- return -2;
+ /* copy the original boot vector */
+ rb->memcpy(boot_vector, flash(BOOT_SECTOR_OFFSET), BOOT_VECTOR_SIZE);
- fd = rb->open(filename, O_RDONLY);
- if (fd < 0)
- return -1;
+ /* erase everything except the bootloader */
+ flash_erase_sectors(BOOT_SECTOR_OFFSET, WHOLE_FIRMWARE_SECTORS, true);
+
+ /* program the original boot vector */
+ flash_program_bytes(BOOT_SECTOR_OFFSET, boot_vector, BOOT_VECTOR_SIZE, false);
+
+ /* program the whole firmware */
+ flash_program_bytes(BOOT_SECTOR_OFFSET + BOOT_VECTOR_SIZE, data, data_len, true);
- len = rb->filesize(fd) - 0x228;
- rb->lseek(fd, 0x220, SEEK_SET);
- rb->read(fd, magic, 8);
- if (magic[1] != 0x00000008 || len <= 0 || len > audiobuf_size)
+ /* verify the new boot vector */
+ if (!flash_verify_bytes(BOOT_SECTOR_OFFSET, boot_vector, BOOT_VECTOR_SIZE, false))
{
- rb->splash(HZ*2, "Not an original firmware file");
- rb->close(fd);
- return -1;
+ msg = "Boot vector corrupt!";
+ show_fatal = true;
+ goto bail;
}
- rc = rb->read(fd, audiobuf, len);
- rb->close(fd);
-
- if (rc != len)
+ /* verify the new firmware */
+ if (!flash_verify_bytes(BOOT_SECTOR_OFFSET + BOOT_VECTOR_SIZE, data, data_len, true))
{
- rb->splash(HZ*2, "Read error");
- return -2;
+ msg = "Verify failed!";
+ goto bail;
}
- if (len % 2)
- len++;
+ /* report success */
+ rb->splash(HZ * 3, "Success!");
+
+ /* mark success */
+ result = true;
- return flash_original_fw(len);
+bail: /* common exit code */
+ if (msg != NULL)
+ rb->splash(HZ * 3, msg);
+ if (show_fatal)
+ show_fatal_error();
+ return result;
}
-int load_romdump(const char *filename)
+/* flash rom dumps */
+static bool flash_romdump(const char* filename)
{
- int len, rc;
- int fd;
+ const void* data;
+ size_t data_len;
- if (!confirm("Restore firmware section (bootloader will be kept)?"))
- return -2;
+ /* load the firmware */
+ if (!load_firmware(filename, FIRMWARE_ROMDUMP, &data, &data_len))
+ return false;
- fd = rb->open(filename, O_RDONLY);
- if (fd < 0)
- return -1;
-
- len = rb->filesize(fd) - 8;
- if (len <= 0)
- return -1;
+ /* ask before doing anything dangerous */
+ if (!confirm_choice("Restore firmware section (bootloader will be kept)?"))
+ return false;
- rb->lseek(fd, 8, SEEK_SET);
- rc = rb->read(fd, audiobuf, len);
- rb->close(fd);
+ return flash_whole_firmware(data, data_len);
+}
- if (rc != len)
- {
- rb->splash(HZ*2, "Read error");
- return -2;
- }
+/* flash original firmware */
+static bool flash_original(const char* filename)
+{
+ const void* data;
+ size_t data_len;
- if (len % 2)
- len++;
+ /* load the firmware */
+ if (!load_firmware(filename, FIRMWARE_ORIGINAL, &data, &data_len))
+ return false;
- if (len > BOOTLOADER_ENTRYPOINT - 8)
- len = BOOTLOADER_ENTRYPOINT - 8;
+ /* ask before doing anything dangerous */
+ if (!confirm_choice("Restore original firmware (bootloader will be kept)?"))
+ return false;
- return flash_original_fw(len);
+ return flash_whole_firmware(data, data_len);
}
-/* Kind of our main function, defines the application flow. */
-static void DoUserDialog(const char* filename)
+/* main function of plugin */
+static void iriver_flash(const char* filename)
{
- /* check whether we're running from ROM */
- uint16_t* RB = (uint16_t*) rb;
- if (RB >= FB && RB < FB + (FLASH_SIZE / sizeof(*FB)))
+ /* refuse to run from ROM */
+ const uint8_t* RB = (uint8_t*) rb;
+ const uint8_t* FB = (uint8_t*) flash(0);
+ if (RB >= FB && RB < FB + FLASH_SIZE)
{
- rb->splash(HZ*3, "Not from ROM");
- return; /* exit */
+ rb->splash(HZ * 3, "Refusing to run from ROM");
+ return;
}
- /* refuse to work if the power may fail meanwhile */
+ /* refuse to run with low battery */
if (!rb->battery_level_safe())
{
- rb->splash(HZ*3, "Battery too low!");
- return; /* exit */
+ rb->splash(HZ * 3, "Refusing to run with low battery");
+ return;
}
+ /* print information about flash; exit if not supported */
if (!show_info())
- return; /* exit */
+ return;
+ /* exit if no filename was provided */
if (filename == NULL)
{
- rb->splash(HZ*3, "Please use this plugin with \"Open with...\"");
- return; /* exit */
+ rb->splash(HZ * 3, "Please use this plugin with \"Open with...\"");
+ return;
}
- audiobuf = rb->plugin_get_audio_buffer((size_t *)&audiobuf_size);
-
- if (rb->strcasestr(filename, "/rockbox.iriver"))
- flash_rockbox(filename, SECT_RAMIMAGE);
- else if (rb->strcasestr(filename, "/rombox.iriver"))
- flash_rockbox(filename, SECT_ROMIMAGE);
- else if (rb->strcasestr(filename, "/bootloader.iriver"))
+ /* choose what to do with the file */
+ if (rb->strcasestr(filename, "/bootloader.iriver") != NULL)
flash_bootloader(filename);
- else if (rb->strcasestr(filename, "/ihp_120.bin"))
- load_original_bin(filename);
- else if (rb->strcasestr(filename, "/internal_rom_000000-1FFFFF.bin"))
- load_romdump(filename);
+ else if (rb->strcasestr(filename, "/rockbox.iriver") != NULL)
+ flash_rockbox(filename, FLASH_RAMIMAGE_ENTRY);
+ else if (rb->strcasestr(filename, "/rombox.iriver") != NULL)
+ flash_rockbox(filename, FLASH_ROMIMAGE_ENTRY);
+ else if (rb->strcasestr(filename, ROMDUMP) != NULL)
+ flash_romdump(filename);
+ else if (rb->strcasestr(filename, ORIGINAL) != NULL)
+ flash_original(filename);
else
- rb->splash(HZ*3, "Unknown file type");
+ rb->splash(HZ * 3, "Unknown file type");
}
-
-/***************** Plugin Entry Point *****************/
-
+/* plugin entry point */
enum plugin_status plugin_start(const void* parameter)
{
- int oldmode;
+ /* need to disable memguard to write to flash */
+ int mode = rb->system_memory_guard(MEMGUARD_NONE);
- /* now go ahead and have fun! */
- oldmode = rb->system_memory_guard(MEMGUARD_NONE); /*disable memory guard */
+ /* setup LCD font */
rb->lcd_setfont(FONT_SYSFIXED);
- DoUserDialog(parameter);
+
+ /* run the main entry function */
+ iriver_flash(parameter);
+
+ /* restore LCD font */
rb->lcd_setfont(FONT_UI);
- rb->system_memory_guard(oldmode); /* re-enable memory guard */
+
+ /* restore original memory guard setting */
+ rb->system_memory_guard(mode);
return PLUGIN_OK;
}