1

I was playing around with TTYs, and for qemu-system-x86_64 -M pc, whenever I pass the -serial option it crates a new TTY and I can attach a serial like ttyS0 and ttyS1 to it from Linux.

For ARM however, I can't get past the first ttyAMA0. If I add -serial, it does not show on info qtree and the kernel boot messages only find one:

9000000.pl011: ttyAMA0 at MMIO 0x9000000 (irq = 54, base_baud = 0) is a PL011 rev1

Is it possible, and if not, is there a specific design reason for that?

I'm on QEMU v3.0.0. From the source it does not seem possible: https://github.com/qemu/qemu/blob/v3.0.0/hw/arm/virt.c#L138 as there is only one UART in the memory map:

[VIRT_GIC_REDIST] =         { 0x080A0000, 0x00F60000 },
[VIRT_UART] =               { 0x09000000, 0x00001000 },
[VIRT_RTC] =                { 0x09010000, 0x00001000 },

I'm doing this because I'm trying to see if connecting from a different serial will make KGDB work. On x86_64 I can connect to ttyS0, but I thought it was worth giving this a try on ARM. See also: https://stackoverflow.com/questions/22004616/how-to-debug-the-linux-kernel-with-qemu-and-kgdb/44197715#44197715

3

this is my multiple serial support patch for virt board in qemu 4.0.1 (official git commit is f9bec78), please enjoy it :-)

diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index bf9c0bc..bdc7094 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -474,11 +474,11 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
 }

 static void
-build_spcr(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
+build_spcr(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms, int uart)
 {
     AcpiSerialPortConsoleRedirection *spcr;
-    const MemMapEntry *uart_memmap = &vms->memmap[VIRT_UART];
-    int irq = vms->irqmap[VIRT_UART] + ARM_SPI_BASE;
+    const MemMapEntry *uart_memmap = &vms->memmap[uart];
+    int irq = vms->irqmap[uart] + ARM_SPI_BASE;
     int spcr_start = table_data->len;

     spcr = acpi_data_push(table_data, sizeof(*spcr));
@@ -741,8 +741,14 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
      */
     scope = aml_scope("\\_SB");
     acpi_dsdt_add_cpus(scope, vms->smp_cpus);
-    acpi_dsdt_add_uart(scope, &memmap[VIRT_UART],
-                       (irqmap[VIRT_UART] + ARM_SPI_BASE));
+    acpi_dsdt_add_uart(scope, &memmap[VIRT_UART0],
+                       (irqmap[VIRT_UART0] + ARM_SPI_BASE));
+    acpi_dsdt_add_uart(scope, &memmap[VIRT_UART1],
+                       (irqmap[VIRT_UART1] + ARM_SPI_BASE));
+    acpi_dsdt_add_uart(scope, &memmap[VIRT_UART2],
+                       (irqmap[VIRT_UART2] + ARM_SPI_BASE));
+    acpi_dsdt_add_uart(scope, &memmap[VIRT_UART3],
+                       (irqmap[VIRT_UART3] + ARM_SPI_BASE));
     acpi_dsdt_add_flash(scope, &memmap[VIRT_FLASH]);
     acpi_dsdt_add_fw_cfg(scope, &memmap[VIRT_FW_CFG]);
     acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO],
@@ -806,7 +812,16 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
     build_mcfg(tables_blob, tables->linker, vms);

     acpi_add_table(table_offsets, tables_blob);
-    build_spcr(tables_blob, tables->linker, vms);
+    build_spcr(tables_blob, tables->linker, vms, VIRT_UART0);
+
+    acpi_add_table(table_offsets, tables_blob);
+    build_spcr(tables_blob, tables->linker, vms, VIRT_UART1);
+
+    acpi_add_table(table_offsets, tables_blob);
+    build_spcr(tables_blob, tables->linker, vms, VIRT_UART2);
+
+    acpi_add_table(table_offsets, tables_blob);
+    build_spcr(tables_blob, tables->linker, vms, VIRT_UART3);

     if (nb_numa_nodes > 0) {
         acpi_add_table(table_offsets, tables_blob);
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index ce2664a..e3006c6 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -125,12 +125,15 @@ static const MemMapEntry base_memmap[] = {
     [VIRT_GIC_ITS] =            { 0x08080000, 0x00020000 },
     /* This redistributor space allows up to 2*64kB*123 CPUs */
     [VIRT_GIC_REDIST] =         { 0x080A0000, 0x00F60000 },
-    [VIRT_UART] =               { 0x09000000, 0x00001000 },
+    [VIRT_UART0] =              { 0x09000000, 0x00001000 },
     [VIRT_RTC] =                { 0x09010000, 0x00001000 },
     [VIRT_FW_CFG] =             { 0x09020000, 0x00000018 },
     [VIRT_GPIO] =               { 0x09030000, 0x00001000 },
     [VIRT_SECURE_UART] =        { 0x09040000, 0x00001000 },
     [VIRT_SMMU] =               { 0x09050000, 0x00020000 },
+    [VIRT_UART1] =              { 0x09080000, 0x00001000 },
+    [VIRT_UART2] =              { 0x09090000, 0x00001000 },
+    [VIRT_UART3] =              { 0x090a0000, 0x00001000 },
     [VIRT_MMIO] =               { 0x0a000000, 0x00000200 },
     /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
     [VIRT_PLATFORM_BUS] =       { 0x0c000000, 0x02000000 },
@@ -161,11 +164,14 @@ static MemMapEntry extended_memmap[] = {
 };

 static const int a15irqmap[] = {
-    [VIRT_UART] = 1,
+    [VIRT_UART0] = 1,
     [VIRT_RTC] = 2,
     [VIRT_PCIE] = 3, /* ... to 6 */
     [VIRT_GPIO] = 7,
     [VIRT_SECURE_UART] = 8,
+    [VIRT_UART1] = 9,
+    [VIRT_UART2] = 10,
+    [VIRT_UART3] = 11,
     [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
     [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
     [VIRT_SMMU] = 74,    /* ...to 74 + NUM_SMMU_IRQS - 1 */
@@ -684,11 +690,16 @@ static void create_uart(const VirtMachineState *vms, qemu_irq *pic, int uart,
     hwaddr base = vms->memmap[uart].base;
     hwaddr size = vms->memmap[uart].size;
     int irq = vms->irqmap[uart];
+    
+   if(chr == NULL)
+       return;
+   
     const char compat[] = "arm,pl011\0arm,primecell";
     const char clocknames[] = "uartclk\0apb_pclk";
+    
     DeviceState *dev = qdev_create(NULL, "pl011");
     SysBusDevice *s = SYS_BUS_DEVICE(dev);
-
+   
     qdev_prop_set_chr(dev, "chardev", chr);
     qdev_init_nofail(dev);
     memory_region_add_subregion(mem, base,
@@ -710,9 +721,9 @@ static void create_uart(const VirtMachineState *vms, qemu_irq *pic, int uart,
     qemu_fdt_setprop(vms->fdt, nodename, "clock-names",
                          clocknames, sizeof(clocknames));

-    if (uart == VIRT_UART) {
+    if (uart == VIRT_UART0) {
         qemu_fdt_setprop_string(vms->fdt, "/chosen", "stdout-path", nodename);
-    } else {
+    } else if ((uart != VIRT_UART1) && (uart != VIRT_UART2) && (uart != VIRT_UART3))  {
         /* Mark as not usable by the normal world */
         qemu_fdt_setprop_string(vms->fdt, nodename, "status", "disabled");
         qemu_fdt_setprop_string(vms->fdt, nodename, "secure-status", "okay");
@@ -1616,11 +1627,14 @@ static void machvirt_init(MachineState *machine)

     fdt_add_pmu_nodes(vms);

-    create_uart(vms, pic, VIRT_UART, sysmem, serial_hd(0));
+    create_uart(vms, pic, VIRT_UART0, sysmem, serial_hd(0));
+    create_uart(vms, pic, VIRT_UART1, sysmem, serial_hd(1));
+    create_uart(vms, pic, VIRT_UART2, sysmem, serial_hd(2));
+    create_uart(vms, pic, VIRT_UART3, sysmem, serial_hd(3));

     if (vms->secure) {
         create_secure_ram(vms, secure_sysmem);
-        create_uart(vms, pic, VIRT_SECURE_UART, secure_sysmem, serial_hd(1));
+        create_uart(vms, pic, VIRT_SECURE_UART, secure_sysmem, serial_hd(4));
     }

     vms->highmem_ecam &= vms->highmem && (!firmware_loaded || aarch64);
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 507517c..5f6228f 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -65,7 +65,10 @@ enum {
     VIRT_GIC_ITS,
     VIRT_GIC_REDIST,
     VIRT_SMMU,
-    VIRT_UART,
+    VIRT_UART0,
+    VIRT_UART1,
+    VIRT_UART2,
+    VIRT_UART3,
     VIRT_MMIO,
     VIRT_RTC,
     VIRT_FW_CFG,

config and build

./configure --enable-kvm --enable-sdl --enable-debug --enable-debug-stack-usage --target-list=aarch64-softmmu,arm-softmmu,x86_64-softmmu,i386-softmmu
make && make install

run

qemu-system-aarch64 \
-cpu cortex-a57 \
-smp 16 \
-m 1G \
-machine virt,gic-version=3,virtualization=on \
-nographic \
-net nic,model=virtio,macaddr=fa:16:3e:4d:58:6f \
-net tap,ifname=tap-jh,script=vm-ifup,downscript=vm-ifdown \
-append "root=/dev/vda mem=768M console=ttyAMA0" \
-kernel arm64-vmlinux.img \
-initrd arm64-initrd.img \
-drive file=arm64-rootfs.img,format=raw,id=disk,if=none \
-device virtio-blk-device,drive=disk \
-monitor tcp::45459,server,nowait \
-serial tcp::45458,server,nowait \
-serial tcp::45457,server,nowait \
-serial tcp::45456,server,nowait \
-serial chardev:s0 -chardev stdio,id=s0,mux=on,logfile=qemu-s0.log,signal=off
  • Awesome.......! – 41754 Nov 1 at 7:13
1

Peter has clarified that it is not currently supported and why at http://lists.nongnu.org/archive/html/qemu-discuss/2018-11/msg00001.html :

No, we only have a single UART on the virt board (because at the time I wrote the code I didn't see a reason for having more). We've had requests for a second UART before. The problem with adding it is that it will break UEFI booting, because if you have two UARTS in the dtb then Linux will use the first listed but UEFI will use the second, so commandlines which used to work will stop working.

This is probably handleable by either:

  • only creating the 2nd UART if given a -machine option to specifically ask for it
  • creating the 2nd UART on-demand based on how many -serial options were passed

but the first of those is clunky and I'm a bit worried the second will have unexpected side effects (eg via however libvirt starts QEMU.)

So it kind of went on the "wishlist" feature list. (The actual code required is probably only a dozen or so lines, it's figuring out the best mechanism for determining whether to create the second UART that's hard.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.