EmbeddedRelated.com
Forums
Memfault Beyond the Launch

How to connect GPIO in QEMU-emulated nachine to an object in host?

Started by Unknown March 20, 2020
I need to connect the GPIO pins in the ARM machine emulated in QEMU to the GUI objects in application working on the host machine.

For example, the level on the output GPIO should be reflected by a color of a rectangle. The input GPIO should be connected to a button. When the button in GUI is pressed, the input GPIO should be read as zero (otherwise as one) etc. Of course the input GPIOs should be also capable of generating the interrupts.

In fact it would be perfect to connect the emulated pin to a pipe or socket so that a change of the state caused by QEMU would produce a message sent to the host, and the appropriate message sent by the host should trigger the appropriate change of the state of GPIO in QEMU (and possibly generate an interrupt).

I have created a few own peripherials for QEMU (e.g, https://github.com/wzab/qemu/blob/ster3/hw/misc/wzab_sysbus_enc1.c ) but implementation of such GPIO seems to be not trivial.

Up to now I have found that material: https://sudonull.com/post/80905-Virtual-GPIO-driver-with-QEMU-ivshmem-interrupt-controller-for-Linux but it uses relatively old QEMU. Additionally, the proposed solution is compatible only with the old sysfs-based method of handling GPIOs.

Are there any existing solutions of that problem?
I'll appreciate any hints or suggestions.

TIA & Regards,
Wojtek
I think that the application implementing the GUI could use msgpack ( https://msgpack.org/ ) protocol to communicate the QEMU.
So whenever the QEMU changes the state of the pin, it sends a message contining two fields:
Direction: (In, Out)
State: (High, Low, High Impedance)

Whenever somebody changes the state of the pin in the GUI, similar message is sent to QEMU, but it should contain only one field:
State: (High, Low)

I assume that the logic that resolves collisions and generates the random state when somebody tries to read the not connected input should be implemented in the GUI application.

What do you think about it?
In a version of QEMU modified by Xilinx I have found something that either maybe a solution, or at least provides means to find the solution.

These are the files with names starting with "remote-port" in the https://github.com/Xilinx/qemu/tree/master/include/hw and https://github.com/Xilinx/qemu/tree/master/hw/core directories.
According to https://www.xilinx.com/support/documentation/sw_manuals/xilinx2017_4/ug1169-xilinx-qemu.pdf (chapter Remote-Port, page 37), the remote-port is intended for cosimulation with System-C. It is not clear if it can be easily interfaced with user designed GUI...
I have found a newer solution based on  https://sudonull.com/post/80905-Virtual-GPIO-driver-with-QEMU-ivshmem-interrupt-controller-for-Linux
It is available in the https://github.com/maquefel/virtual_gpio_basic repository. However, it is not clear if it is libgpiod compatible.
I have managed to modify the model of the MPC8XXX GPIO in QEMU so that it works together with the GUI written in Python. The communication is established via POSIX message queues. The code is available at alt.sources group https://groups.google.com/forum/#!topic/alt.sources/_EbOdnQKZ18 
The full project that uses the modified MPC8XXX in an emulated Vexpress A9 board is available in  in https://github.com/wzab/BR_Internet_Radio repository
in branch "gpio".
The code is just a "proof of the concept" so it lacks error checking, and probably is buggy, but it works for me.
I hope that somebody may find it useful and develop the concept further.

With best regards,
Wojtek

PS. The concept is inspired by the "remote port" solution introduced by Xilinx in their version of QEMU (see the files with names starting with "remote-port" in the https://github.com/Xilinx/qemu/tree/master/include/hw and https://github.com/Xilinx/qemu/tree/master/hw/core directories.)
My code handles input and output, but still has a problem with interrupts. When I enable interrupt for a pin, then I get error in QEMU:

buildroot-2020.02/output/build/host-qemu-4.2.0/accel/tcg/tcg-all.c:40:tcg_handle_interrupt: assertion failed: (qemu_mutex_iothread_locked()) 

Obviously I must modify handling of input so that interrupts are not triggered when mutex is locked.
OK. It appeared that the problem was trivial. I have learned the solution from https://lxr.missinglinkelectronics.com/qemu/hw/misc/edu.c#L350

I simply had to call qemu_mutex_lock_iothread(); before calling qemu_set_irq() and qemu_mutex_unlock_iothread() after, when generating IRQs in my thread:

static void * remote_gpio_thread(void * arg)
{
    //Here we receive the data from the queue 
    const int MSG_MAX = 8192;
    char buf[MSG_MAX];
    gpio_msg * mg = (gpio_msg *)&buf;
    mqd_t mq = mq_open("/to_qemu",O_CREAT | O_RDONLY,0x660,NULL);
    if(mq<0) {
        perror("I can't open mq");
        exit(1);
    }
    while(1) {
        int res = mq_receive(mq,buf,8192,NULL);
        if(res<0) {
            perror("I can't receive");
            exit(1);
        }
        if(res != sizeof(gpio_msg)) continue;
        if((int) mg->magick[0]*256+mg->magick[1] != REMOTE_GPIO_MAGICK) {
            printf("Wrong message received");
        }
        if(mg->pin < 32) {
            qemu_mutex_lock_iothread();
            mpc8xxx_gpio_set_irq(arg,mg->pin,mg->state);
            qemu_mutex_unlock_iothread();
        }
    }
}
The version of my emulated GPIO connected to the GUI is quite mature. It even simulates the bouncing effect in buttons and switches. You may find it in https://github.com/wzab/BR_Internet_Radio/tree/gpio 

Regards,
Wojtek
I have found a similar interesting solution: https://github.com/evplatt/mse_report described in:
https://repositories.lib.utexas.edu/bitstream/handle/2152/46169/PLATT-MASTERSREPORT-2016.pdf

Regards,
Wojtek

Memfault Beyond the Launch