MMU, memory mapping (DIGIC 7 and later)

Started by srsa, October 08, 2020, 12:54:10 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

srsa

For tech details, see ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition. Understanding and using this does require knowledge of ARM assembly.

It's now known that the main ARM cores in DIGIC 7, 8, X are ARM Cortex A9.

There have been occasional comments about using the MMU, but I don't remember seeing any actual research or code.
So, here's this as a start.

The Cortex A9 can address 4GB memory. About the half of that can be RAM.
All DIGIC 7 and later cameras seem to have a very similar memory layout, see this post from a1ex for example.
Most of physical memory is mapped to the same virtual address - except for the first 4kB.

When reviewing the memory mapping related routines on my D7 and D8 cameras, I found that the main firmware (EOS and PowerShot) uses the ROM translation tables when it starts.
The ROM tables are located at the start of ROM (address 0xe0000000), their complete size is 0x4900 bytes.
They consist of the following:
- 0x4000 bytes of L1 table for both cores, describing logical addresses from 32MB upwards
- 0x400 bytes of L2 table describing logical address range 0...1024kB, core0
- 0x400 bytes of L2 table describing logical address range 0...1024kB, core1
- 0x80 bytes of L1 table describing logical address range 0...32MB, core0
- 0x80 bytes of L1 table describing logical address range 0...32MB, core1

It turned out recently that DIGIC X models use a RAM copy of the tables. I have not done any review on a DIGIC X firmware, so it could be that my D7 and D8 findings do not apply there.

So, below is for DIGIC 7 and 8.

There is a RAM copy of translation tables. On PowerShot firmware models, their location is at 0xdffc0000, EOS firmware uses an _uncached_ dynamically allocated RAM copy.
The firmware switches to the RAM copy whenever it is about to write into ROM (and switches back when it's done). I _think_ that mostly happens just before a clean shutdown.
If it turns out that it can happen during normal camera operation, one will have to patch the routines responsible for switching the translation tables.
For my current experiment on my D7 camera, I don't do that.

I have to note that there is a tradeoff between fine-grained memory mapping and CPU performance. The less L2 tables, the better performance.
The Canon tables are pretty efficient, supersections (16MB granularity) are used mostly.

In my current implementation, I settled for 64kB granularity when doing ROM replacement.




Replacing parts of ROM with modified RAM copy.


Since the firmware doesn't change mapping while it runs, it is possible to make a modified static mapping at boot time (when autoexec.bin executes).
Making the modified tables is a job for one CPU core, but both cores need to be set to the new tables.
It is also important to make sure that both cores have their caches in sync before they are set to use the new tables.

In order to be able to replace larger amounts of ROM, one needs RAM that is not used by any part of the camera.
- RAM for the translation tables (0x4900 bytes for copying the Canon tables and 0x400 bytes for each L2 table).
The Canon tables need to be copied to a 16kB-aligned address, the alignment of L2 tables is 0x400 bytes.
- RAM for the replaced ROM pages, 64kB (aligned) for each page.

To find that RAM, one can start with reviewing the output of smemShowFix, looking for holes, preferably in the common section. The hole needs to be verified by reviewing the disassembly/disassemblies, looking for addresses that fall into the hole's range.
In some cases, the firmware of other (ARM, Xtensa) cores needs to be reviewed too.
The other way of making sure a memory area is unused is dumping the suspected hole's content to a file before/after doing all sorts of camera operations, and then comparing the dumps.

When one or more suitable holes are found, one can start doing what I did in my EOS M100 port.
I wrote a few words about that here.
The (currently) latest source can be found attached to this post (the 7z archive).
Files of interest are
platform/m100/sub/mmuutils.c
platform/m100/sub/100a/boot.c (usage example)
platform/m100/sub/100a/replaced.c (usage example)
platform/m100/sub/100a/stubs_entry_2.S (related stubs)
See also comments in those files.

Some other notes:
Changes made to the ROM content are only visible to the main ARM cores. For example, it is not possible to modify ROM data that is later transferred from ROM by DMA.


I'm sure there is a lot I did not mention, I'll try to answer questions. Corrections and different approaches are also welcome.

turtius


c_joerg

At the moment CHDK cannot be reached at all ...
EOS R

srsa

Quote from: jack001214 on October 15, 2020, 11:25:21 AM
The chdk links are dead.
The CHDK forum is up again, links should be working.
Anyone interested in the topic is advised to read my followup posts in the M100 thread. At the moment, the remap-based M100 port is having some unexplained issues. If anyone with more thorough knowledge about using ARM MMU could comment, that would be most welcome.

edit:
The unexplained issues are now gone in the latest source. They were not caused by memory remap after all.

names_are_hard

It took me a long time to get round to it, but thanks a lot for the code srsa!  After a lot of ARM manual reading, and working from your examples, I can now remap memory on 200D (and very probably all D78X).



Code will live here when I tidy it up and push: https://github.com/reticulatedpines/magiclantern_simplified/tree/mmu_investigation

EDIT: this commit has a working PoC for 200D, easy to adapt for other cams: https://github.com/reticulatedpines/magiclantern_simplified/commit/2ee5ccd0751d37bd03335f52f23e8673938893b4