Using QEMU UART interface
- 05 Aug, 2025

In the posts so far on the MistraSolutions blog I have covered different peripherals and interfaces used in embedded systems: I2C, SPI, USB.
One of the important interfaces that was not covered so far is the UART interface.
QEMU UART interface
QEMU supports UART interface for the serial console for Linux. When coupled with the correct Linux configuration, the UART interface will be available when QEMU is started.
The UART interface in QEMU is configured by passing the -serial <dev>
argument. The different options that are
supported can be checked in the
official documentation.
Some of the <dev>
options that can be interesting:
stdio
- most commonly used, the console will be printed straight to the current terminalnull
- no serial interface for the current UART port; this is most often used when multiple UART interfaces are to be used, or if console is not configured for the inteface 0, so the interfaces before interface to be used as console should be set tonone
(positional indexing is used, so if console should be on serial interface 2, then proper serial initialization would be-serial none -serial none -serial stdio
)pty:[name]
- create a pseudo-tty port on the host to be used for interacting with the QEMU; ifname
is passed then symbolic link will be created, as a random interface under/dev/pts/X
will be created- this is the option that will be used in this post
Enable second UART
Before second UART port can be used, we have to make sure that QEMU supports it. Looking at the Cubieboard source code, it currently supports only one UART port, so modification will be needed to enable other UART ports.
The following patch enables UART1-UART4 ports of the Allwinner A10 emulated processor
diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c
index f1b399759a..febd895695 100644
--- a/hw/arm/allwinner-a10.c
+++ b/hw/arm/allwinner-a10.c
@@ -156,9 +156,11 @@ static void aw_a10_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(SYS_BUS_DEVICE(&s->sata), 0, qdev_get_gpio_in(dev, 56));
/* FIXME use a qdev chardev prop instead of serial_hd() */
- serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2,
- qdev_get_gpio_in(dev, 1),
- 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN);
+ for (size_t i = 0; i < 4; i++) {
+ serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE + i * 0x400, 2,
+ qdev_get_gpio_in(dev, i + 1),
+ 115200, serial_hd(i), DEVICE_LITTLE_ENDIAN);
+ }
for (size_t i = 0; i < AW_A10_NUM_USB; i++) {
g_autofree char *bus = g_strdup_printf("usb-bus.%zu", i);
After QEMU change is done, the Linux device tree for Cubieboard also needs to be updated to enable access to the UART2. The following patch can be used
diff --git a/arch/arm/boot/dts/allwinner/sun4i-a10-cubieboard.dts b/arch/arm/boot/dts/allwinner/sun4i-a10-cubieboard.dts
index f71142432..1d4d7e711 100644
--- a/arch/arm/boot/dts/allwinner/sun4i-a10-cubieboard.dts
+++ b/arch/arm/boot/dts/allwinner/sun4i-a10-cubieboard.dts
@@ -247,6 +247,12 @@ &uart0 {
status = "okay";
};
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart1_pins>;
+ status = "okay";
+};
+
&usb_otg {
dr_mode = "otg";
status = "okay";
Configure QEMU UART
With UART enabled in QEMU and in the Linux, the QEMU can be started using the following command (this is the minimal command for this example)
qemu-system-arm \
-machine cubieboard \
-m 1G \
-drive file=sd.img,format=raw,if=sd \
-serial mon:stdio \
-serial pty:/tmp/qemu-uart \
-nographic
Once QEMU is started, we can use the microcom
utility to open an interactive console to interact with the /dev/ttyS1
microcom /dev/ttyS1
Now we can proceed with the Python utility on the host, to enable the other side of communication.
Python interactive example
A simple Python script that increments ASCII value of each character that is received by 1 and returns it to sender
follows (Pyserial
is the only dependency)
import sys
import serial
try:
ser = serial.Serial("/tmp/qemu-uart", baudrate=115200) # open serial port
except serial.SerialException:
print("QEMU not started")
sys.exit(1)
while True:
try:
recv_char = ser.read(1)
send_char = chr(ord(recv_char) + 1).encode()
print(f"Received {recv_char}, sending {send_char}")
ser.write(send_char)
except serial.SerialException:
print("QEMU is stopped, exiting")
break
except KeyboardInterrupt:
if ser.is_open:
ser.close()
break
Summary
UART interface is very useful and interesting examples can be made for QEMU emulation. The great thing is that the peripheral can be made outside of QEMU, so same QEMU instance can be used with different peripherals.