Loading test code into RAM

Introduction

Warning -- This code is wrong and I don't know why. I was able to reflash my original image after using this code, but it wasn't able to load a RAM-based image. In trying to figure out why it didn't work, I managed to toy around and create an image that is now unable to load new ihp_120.hex files. This means my iRiver is now in a state where it works just fine, except I can't load new firmware. Here ends my testing until I can pick up an IriverBDM.

Here's my attempt to modify the 1.40US firmware to run code out of RAM. I haven't tested this particular patch yet, but I've done similar stuff to my firmware with quite a bit of success. It may inspire a more principled and "RockBox Developer Blessed" approach.

The goal of this modification is to do a very minimal tweak to the original firmware in order to be able to test RockBox (or other test code) on the iRiver without risking putting a non-functional image onto the box. Hopefully if this is done right, the worst case scenario for bad test code would be that you'd have to use the Reset button to reboot the box, at which point you'd return to the functional image.

There are a number of bits and pieces that need to be setup in order to three parts to this setup. The following sections will go through each of the issues to the extent that I've been able to figure them out.

Loading a file into memory

Well, thankfully we don't have to do this work. We can use the firmware loading process (very, very carefully) to take care of this step. If you've been watching the IriverMemoryMap page, you'll see that a number of the pieces of that process are already figured out. In particular, the function at 0x3102284C loads the "IHP_120.HEX" file into RAM and then decodes it. If we insert a function call that checks the in-RAM image to see if it's a RockBox RAM-based image before the firmware decode runs, we're golden. I've isolated the function call at 0x310228A8 as the perfect spot. Instead of calling the "load IHP_120.HEX" routine, we can call our own function which in turn calls that routine, does a check for a RockBox magic number, then returns back to the caller. The caller expects to get the length of the file as a return value. Anything else is an error. There are a lot of other checks in this function that try to avoid loading in a bogus image, but I figure returning 0 from our "ramload" function would be good in the case where our test code returns.

We end up with a pretty simple C function with only a single dependency on the location of the "load firmware file" function in the 1.40US firmware:

The scenario for usage here is that the contents of IHP_120.HEX would be either a regular firmware image to be flashed into RAM (so the ROCK_MAGIC wouldn't match, so we return the file length to the flash routine) or a RAMLOAD image (MAGIC will match, so we call into 4 bytes into the image file).

Finding a spot for this function

DaveHooper: If you need to find a large space for a new function in the firmware, without overwriting existing functions, perhaps you could use the image locations? (e.g. where the startup logo or boot logo is stored. These should each offer you a couple of hundred bytes). Changing the size of the firmware image of course is not recommended, unless you also patch the Ramcopy functions to copy the correct lengths of firmware section into RAM.

Now, we need a space within the firmware image to write such a function. I was really nervous about changing the size of the firmware image, so I found a large enough function that wasn't being used on my IHP-140 -- the code that scans for "IMPFLASH.BIN" at 0x3103B924. I checked through the call graphs and was fairly confident that on the 140 architecture this function call was not used. Just in case, I decided to stub it out, returning "failure":

This leaves from 0x3103B930 - 0x3103B9E6 (182 bytes) of space that we can use to put our RAMLOAD function above. That's an easy fit.

Putting it all together

I wrote a script to wire this all together. You need the CrossCompiler built (the script assums it's in /build/m68k-elf/bin, but that's an easy tweak), and the ramload.c. Most importantly, you need the 1.40US version of ihp_120.hex. Since this twiddles (although ever so slightly) with the firmware loading code, I'm was averse to trying it until we've got positive confirmation from someone with a working IriverBDM. I've done a similar patch to my firmware, to output ID3 tags out of the serial port, so all the pieces except for the ramload function are operational.

I worked up the nerver and was able to reflash my original image after using this code, but it wasn't able to load a RAM-based image.

DaveHooper: How could you tell your RAM-based image wasn't loading? I think that if your RAM-based image was just a tweaked iRiver firmware, it wouldn't work (or it would certainly end up doing strange things e.g. copying itself over itself in RAM while running the memorycopy code at initialization). Or was your RAM-based image something more simple e.g. just output a few bytes to serial and then return?

PaulS?: The latter is correct. The RAM-based image was something simple that outputted a few bytes to the serial port and returned.

In trying to figure out why it didn't work, I managed to toy around and create and flash a different image that is now unable to load new ihp_120.hex files. This means my iRiver is now in a state where it works just fine, except I can't load new firmware. Here ends my testing until I can pick up an IriverBDM. If I had to guess, I would say that my first problem (with the build in the attachment) was that there's some code in the loader that makes sure the "IHP_120.HEX" file is larger than 512 bytes. My little test was shorter than that. I'm not as sure why the next image fails, but it might be that calling puts() in this routine does bad things.

After a properly functioning version of this firmware, you can now load code into RAM, by creating a binary that begins with the bytes 0x52, 0x4f, 0x43, 0x4b. The following bytes should be the entrypoint of the code. It's probably good form to start out with position independent code, and probably not to rely on any code in the rest of the box (unless you write some code to make sure which firmware revision you're running on).