Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to modify memory from Peripheral handlers #103

Open
max-r-b opened this issue Apr 11, 2022 · 6 comments
Open

Unable to modify memory from Peripheral handlers #103

max-r-b opened this issue Apr 11, 2022 · 6 comments

Comments

@max-r-b
Copy link

max-r-b commented Apr 11, 2022

Hi,

I am trying to make a dummy flash controller for an ARM cortex-m3 based MCU that I emulate with QEMU. The goal is to retrieve the different parameters (address, value, size) and then modify the corresponding memory region that I allocated using avatar.add_memory_range(). I took inspiration from the example in the repo avatar2/avatar2_example so I made read and write handlers in which I perform the different actions depending on the offset and the values.

My problem is that when I use qemu.write_memory() in my controller read/write handlers, nothing happens. I can read the memory afterwards and I notice no changes. However, when I modify the memory using the same API after I reach a breakpoint, the memory is modified as expected.

Any idea how I could achieve these memory modifications in my controller?

Here is an extract of my code:

    def do_flash_op(self):
        if self.opcode == FlashOpcode.NONE:
            print("Warning: FlashOp: wrong op code, doing nothing")

        elif self.opcode == FlashOpcode.WRITE or self.opcode == FlashOpcode.ERASE:
            print("Write flash at " + hex(self.flashAddr) + " (" + hex(self.nwords) + " words) in bank " + hex(self.bank))
            for x in range(0, self.nwords):
                print(qemu.write_memory(self.flashAddr + x, 4, self.wordsToWrite[x]))
        else:
            print("Read bank " + str(self.bank) + " (info=" + str(self.isInfo) +") op " + str(self.opcode) + " words " + str(self.nwords) + " at " + hex(self.flashAddr))


    def handle_read(self, offset, size):
        if (offset == 0xc):
            return self.offsetValue
        return 0x00 # TODO: read info bank

    def handle_write(self, offset, size, value):
        if offset == 0x4 or offset == 0x8: # Control register
            if offset == 0x4:
                self.bank = 0
            else:
                self.bank = 1

            if value == 0x31415927: # Erase
                self.opcode = FlashOpcode.ERASE
            elif value == 0x27182818: # Write
                self.opcode = FlashOpcode.WRITE
            elif value == 0x16021765: # Read (only for info bank)
                self.opcode = FlashOpcode.READ
            self.parse_offset()
            self.do_flash_op()
            self.reset_regs()

Thanks

@mariusmue
Copy link
Member

Heya,

Thanks for opening the issue! Unfortunately, without a complete example to run as a test case, it's somewhat hard to reproduce, especially as we don't know your target setup.

Either way, from my gut feeling, I think the following is happening:

  • when the firmware writes to the peripheral, a remote memory request is issued to your peripheral
  • during a forwarded memory access, your target is still in the targetstate.RUNNING state
  • however, gdb (which is used under the hood for talking to the qemu target) does not like to modify memory of a running thing
  • hence changing mem from your peripheral handler fails.

To move forward, I see 3 ways:
a) use the pypanda target and modify memory with the raw=True argument. This uses the pypanda interface and bypasses qemu, hence, allowing to modify the memory either way.
b) Before doing the write access, explicitly stop the QemuTarget (Note: I'm not sure if this is working with the current version of avatar2.)
c) Adding a new qemu-monitor command to modify memory while the target is running and use this as API instead.

In any case, pretty cool project, so far we haven't worked on flash controller emulation!

@max-r-b
Copy link
Author

max-r-b commented Apr 11, 2022

Thanks for the quick reply :)

I already tested b), but no luck (my Peripheral code stop being executed after I launch qemu.stop()).

I am now looking at solution a), but I get a segfault this time (apparently after reaching SVC 0).

Also using this target (and Panda target as well), a dummy UART peripheral is not working properly: an interrupt should be triggered from the firmware code by writing in a Cortex-M3 specific address (0xe000ef00). I can see instructions corresponding to the write at this address, but I notice no interruption (but this was working as expected with QemuTarget)... The firmware is suppose to set its own interrupt table using one of these Cortex-M specific address, maybe this could explain the crash. Any gut feeling on this issue? :)

For information, I am using the Dockerfile and the version 1.4.7.

@mariusmue
Copy link
Member

mariusmue commented Apr 11, 2022

Regarding b: did you also try with qemu.stop(blacking=False)?

The interrupt issue is very likely because on the stock configurable machine, we do not initialize the interrupt controller - which would be in-line with segfaulting for SVC 0, as this should trigger in interrupt?

For the time being our solution was to always "hack-in" the irq controller by checking against the cpu model, as we did for instance here for pretender or here for FirmWire.
While we added this to upstream avatar-qemu for the Cortex-m3, it's not present in our configurable machine port for panda, and definitely something we should contribute back.

For the time being, can you add an according hack to Panda and try building it? It may fix the interrupt/svc issue.

@max-r-b
Copy link
Author

max-r-b commented Apr 11, 2022

Thank you again for the assistance.

Using qemu.stop(blacking=False), I have an exception when calling qemu.cont()

Exception: cont() requested but Target is RUNNING

As for the irq hack, thanks for pointing it out, I didn't know about it. I will see if it is not too difficult to do it for Panda.

@mariusmue
Copy link
Member

Sorry, I had a typo, it should've been blocking, not blacking. Either way, for Panda, if you don't succeed please get back here and I can check what I can do.

@max-r-b
Copy link
Author

max-r-b commented Apr 11, 2022

Sorry, I copied your typo here, but I used blocking my script.
I end up with the exception from my previous message when I try to call qemu.cont

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants