QEMU, soros portok szama

Fórumok

Sziasztok!

Egy `qemu-system-valami` rendszernek hogyan is tudom megmondani hogy hany (fizikainak latszo) soros portot adjon a guest-nek? Sima 8250/16550-es az tokeletes, a kernel az olyan hogy 4-et tud kezelni. De hiaba mondom hogy -serial /dev/ttyUSBx -serial /dev/ttySy -serial /dev/tntN ... valahogy mindig csak egy van... a fun az hogy lat 4 darab /dev/ttyS*-ot (0...3), a dmesg-ben is:

[ ...        ]
[    0.248096] Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
[ ...        ]
# 
~ # ls -l /dev/ttyS*
crw-------    1 0        0           4,  64 Jan  1 00:00 /dev/ttyS0
crw-------    1 0        0           4,  65 Jan  1 00:00 /dev/ttyS1
crw-------    1 0        0           4,  66 Jan  1 00:00 /dev/ttyS2
crw-------    1 0        0           4,  67 Jan  1 00:00 /dev/ttyS3

de az elso kivetelevel azokra mind Input/output error-t mond barmi. A kernel az `nr_uarts=4` modon lett inditva...

Mondjuk a dmesg ezen kivul annyit mond hogy a /dev/ttyS0 az a konzol maga:

[    0.250427] printk: console [ttyS0] disabled
[    0.251717] 10000000.serial: ttyS0 at MMIO 0x10000000 (irq = 1, base_baud = 230400) is a 16550A
[    0.287735] printk: console [ttyS0] enabled

ami valoban igy is van (a host oldalon latszik is szepen) de fentebb ez a "Serial: 8250/16550 driver, 4 ports" jonak tunik...

thx, A.

Hozzászólások

Akkor lehet, az lesz a baj. Esélyes, hogy egynél többhöz PCI kell, és az első csak azért működik, mert emulálva van a guest BIOS-bában, hogy ISA-ként is látszódjon. Ha van kedved ezzel szívni, compatmon0-ra kapcsolva (Ctrl+Alt+2) a qemu debugger-el kérd le az eszközlistát ("info devices" vagy valami ilyesmi), és akkor az is látszani fog pontosan, miként vannak leképezve a soros portok a guest-ben (jó eséllyel nem ISA). Na, amit ott látsz, ahhoz való kernel driverre lesz szükséged a guest OS-ben. Alapból a ttyS0-ttyS3 a sima ISA meghajtót használja, az 0x3F8, 0x2F8, 0x3E8, 0x2E8 IO portokon. Ha ezeken a portokon nincs semmi emulálva, akkor mindig hibát fogsz kapni a kernel drivertől. Az, hogy a guest dmesg-edben MMIO látszik, és nem is IO port, több, mint gyanús, hogy ez lesz a helyzet. A másik meg, az a 230400 baud, na a valódi 16550A csip sosem tudott ennyit (a maximuma 115200 Hz), ez is valami nagyon félrekonfolt dologra utal.
(Nekem egyébként szokott működni, hogy több -serial kapcsolót adok meg a qemu-nak, igaz, kettőnél többel még sose próbálkoztam.)

Igen, ez egy RISC-V alapu rendszer (RV32IMAC_Zicsr, pontosabban szolva), szoval a legegyszerubb RV32-es architektura amire mar lehet ertelmes RTOS-t es/vagy Linuxot huzni. Szoval I/O port nincs (a CSR-ek nem arra valoak), csak MMIO van. Es mivel MMIO van, ezert jo kerdes hogy hogyan a leghatekonyabb 8x1 byte-ot lemappelni. Az "early console" forraskodjaban meg busy wait alapu az MMIO, es annak a forraskodjabol (ld: ./linuxdrivers/tty/serial/8250/8250_early.c) latszik hogy tobbfele I/O modozat kozul lehet valasztani. Ugyanakkor egy nativ 32+32 bites (ILP32-es) hardver a leghatekonyabban ezt nem byte-szinten hanem word-szinten fogja kezelni - szoval az teljesen termeszetes hogy itt lehet egy address space szethuzas. Es mivel van erre boven hely, ezert ez nem gond. 

("info devices" vagy valami ilyesmi)

Aha, igen, egy ilyet talaltam:

QEMU 7.2.9 monitor - type 'help' for more information
(qemu) info chardev
serial1: filename=serial
parallel0: filename=null
compat_monitor0: filename=stdio
serial0: filename=serial

Itt megjelenik az a ket chardev amit megadtam:

$ qemu-system-riscv32 -m 512M -nographic -machine virt \
        -kernel ./Image2 \
        -initrd ./initramfs.cpio.gz \
        -device pci-serial-4x \
        -serial /dev/tnt0 \
        -serial /dev/tnt2 \
        -append "console=ttyS0 nr_uarts=4"

A /dev/tnt{0,1} paron ott van a konzol, be tudok lepni a Linuxra, minden oke. De a /dev/ttyS1-et (vagy barmelyik 0< soros portot) nem tudom elerni. Szoval jo lenne valami MMIO mappingot latni :)
 

De a /dev/ttyS1-et (vagy barmelyik 0< soros portot) nem tudom elerni

Ahogy máshol írom a topikban, ez amiatt van, mert  a riscv32 system emulator "virt" board-ja (más szóval "machine type"-ja) nem rendelkezik 1-nél több "alaplapi" soros porttal. Az érintett QEMU forrás a "hw/riscv/virt.c"; ebben az van, hogy

static const MemMapEntry virt_memmap[] = {
    /* ... */
    [VIRT_UART0] =        { 0x10000000,         0x100 },
    /* ... */
};

és a VIRT_UART0 az egyetlen ilyen.

Aztán lásd a forrásfile-ban a különféle hivatkozásokat a VIRT_UART0 makróra. Például a create_fdt_uart() függvény felelős azért, hogy az egyetlen soros portot megjelenítse a DTB-ben a guest számára. Valamint az egyetlen serial_mm_init() hívás a virt_machine_init() alaplap-inicializációs függvényben gondoskodik arról (többek között), hogy a guest-physical address space-ben a fent idézett MMIO tartománynak az egyetlen emulált soros port feleljen meg.

Ahogy máshol említem a thread-ben, az aarch64 system emulátor virt board-ja is hasonlóan nézett ki sokáig, de nemrég Peter Maydell megcsinálta ahhoz a második soros portot, én pedig az edk2-ben (= guest firmware) csináltam meg hozzá a guest firmware-beli támogatást:

https://bugzilla.tianocore.org/show_bug.cgi?id=4577

... Hoppá, várjunk csak! Most látom (teljesen meglepve), hogy Peter patch-ei nem is lettek a QEMU-ba beolvasztva! Ezt egyáltalán nem értem, mert én szénné teszteltem a feature-t. Mindjárt írok neki, hogy mi van.

Én így használom, ( 1-nél többre sose volt szükség ):

-serial tcp:127.0.0.1:232,server,nowait,nodelay

Ezt berakod többször, csak a portot kell átírni, majd a host-ról a TCP portra  kell csatlakozni ( pl.: telnet ).

USB soros átlakítót azt máshogyan kell átadni ( utána majd a guest-os betölti a drivert hozzá):
https://pve.proxmox.com/wiki/USB_Devices_in_Virtual_Machines

Igen, koszi, ez a resze halistennek megy - haszaltam igy is soros port kihuzasat de most ez a /dev/tnt[01], [23], ... parositas nagyon hatekony (pl a QEMU leallitasa - ujrainditasa utan a masik fel nem szakad le, ilyesmi). Ezt a /dev/tnt* trukkot hasznalom mas (sajat) SoC emulatorokhoz is, ott is bevalt hasonlo okokbol. A TCP-s valtozat meg allando-fix telepites-uzemeltetes mellett hatekony :) 

Első probléma, hogy a soros portok száma az emulált board-tól (machine type-tól) függ. Például a "qemu-system-aarch64 -M virt" nem is olyan régen kapott támogatást a 2. soros porthoz; azt megelőzően csak 1 volt neki. Tehát ha az adott board nem támogat 1-nél több soros portot, akkor akármennyit megadhatsz a parancssorban, nem fog menni.

A modern szintaxis pedig az, hogy

-serial chardev:ser0 \
-serial chardev:ser1 \
-serial chardev:ser2 \
-serial chardev:ser2 \
-chardev serial,id=ser0,path=/dev/ttyS0 \
-chardev serial,id=ser1,path=/dev/ttyS1 \
-chardev serial,id=ser2,mux=on,path=/dev/ttyUSB0

Az első négy opció a frontend konfig (amit a guest lát).

A második három opció a backend konfig (ami a host-on történik).

A fenti példában a guest-nek négy soros portot adunk. Ebből az első kettő két különböző backend-re hivatkozik (ser0 és ser1 azonosítójú backend-ekkel), mely utóbbiak rendre a host /dev/ttyS0 és /dev/ttyS1 device-aihoz vannak kötve. Az utolsó kettő frontend (vagyis guest-side serial port) pedig osztozik a host oldalon a /dev/ttyUSB0 device-on, és hogy éppen melyik van vele összekötve, azt a "Ctrl-a c" kombinációval lehet váltogatni a /dev/ttyUSB0 felől. (Vagyis ha az éppen aktív frontend felé beviszel egy "Ctrl-a c"-t a backend felől, akkor a frontend nem fogja megkapni, hanem a következő frontend aktiválódik.)

Oh, koszi, lehet hogy akkor ez lesz :/ Sot, eselyes is mert kozben ezt talaltam:

QEMU 7.2.9 monitor - type 'help' for more information
(qemu) info mtree 
[...]
memory-region: system
[...]
    0000000010000000-0000000010000007 (prio 0, i/o): serial
    0000000010001000-00000000100011ff (prio 0, i/o): virtio-mmio
[...]

Ez ugye megerositi azt hogy:

[    0.251717] 10000000.serial: ttyS0 at MMIO 0x10000000 (irq = 1, base_baud = 230400) is a 16550A

Meg azt is hogy a 8250-es 8 regisztere az byte-szinten (0 bitshift mellett) van elosztva.

Node a lenyeg az az hogy latszik hogyha tobb -serial-t adok meg neki, akkor is csak egy ilyen i/o: serial modult ir ki az `info mtree`, illetve a port=... argumentumot sem veszi be. Mondjuk ettol fuggetlenul ezek az `info ...` akarmik elegge szegenyesnek tunnek, pl az is furcsa hogy az `info irq` semmit nem ir ki (pedig ez a 8250/16550-es az azert tud megszakitasokat kezelni). Szoval siman lehet, tenyleg, hogy ebbe a `qemu-system-riscv32`-be bele van vasalva az 1 szem soros port, egy adott cimre, oszt ennyi.

Ez egy nagyon minimal Linux, egyelore "lentrol felfele" epitem fel (azaz elso korben csak egy /init, ami csak busybox, az is initramfs-bol, a kernelbol hardveresen minden hianyzik, csak soros port az egyetlen fizikai device amit tamogat, stb). A kulvilag fele egy szem soros konzol van csak, es ezt akarom novelni egy plusz csatornaval, amin keresztul valami mast huznek ki (pl internetet, slattach-csel).

Koszi, ezt a virtio-t megnezem! Legalabbis azt latom hogy a kernel az tamogatja (egy CONFIG_VIRTIO_CONSOLE-t talaltam). Es a host oldalarol azt is latom hogy van - vagyis azt latom hogy valami van: 

    0000000010000000-0000000010000007 (prio 0, i/o): serial
    0000000010001000-00000000100011ff (prio 0, i/o): virtio-mmio
    0000000010002000-00000000100021ff (prio 0, i/o): virtio-mmio
    0000000010003000-00000000100031ff (prio 0, i/o): virtio-mmio
    0000000010004000-00000000100041ff (prio 0, i/o): virtio-mmio
    [...]

mindez kozvetlenul a 8250-es MMIO blokkja felett, kicsit furcsa leosztasban de van 8 ilyen blokk. Meg akkor az eleszteset megnezem... hatha :)

Az "info mtree" kimenetében a virtio-mmio transzport blokkokat látod, ezekre lehet ráültetni virtio device-okat. Jelen esetben 1 db virtio-serial device-ra lenne szükség, 1 db nem-konzol virtio-serial port-tal.

A frontend konfiguráció úgy nézne ki, hogy

-device virtio-serial-device,id=vser0 \
-device virtserialport,bus=vser0.0,nr=1,chardev=ch0,name=foobar \

a backend konfig pedig (mondjuk)

-chardev serial,id=ch0,path=/dev/ttyS0 \

A guest-ben elvileg kell majd látnod egy "/dev/virtio-ports/foobar" symlink-et, ami a "../vport1p1"-re mutat (ami már egy char device).

Ez egyébként egy nagyon egyszerű device, tehát egy valódi soros terminálhoz képest kevés jellemzővel rendelkezik; legjobb esetben is csak cols*rows meta-információt (= karakteres konzol felbontását) tudja átvinni: https://docs.oasis-open.org/virtio/virtio/v1.1/cs01/virtio-v1.1-cs01.ht…

"Internet behúzására" jó lehet a virtio-net-device, illetve a virtio socket device.

Koszi! Igy mar tenyleg jol elindul a QEMU, a kernelt is ujraforditottam:

linux-6.1.92$ cat .config | grep VIRTIO | grep "^CONFIG"
CONFIG_BLK_MQ_VIRTIO=y
CONFIG_VIRTIO_CONSOLE=y
CONFIG_VIRTIO_ANCHOR=y
CONFIG_VIRTIO=y

de sajna sem az `info mtree`-ben, sem a guest-ben nem latszik semmi valtozas. A kernelnek csak a "console=ttyS0 nr_uarts=4" command line van atpasszolva, ahol ezutobbi opcio nyilvan ertelmetlen azalapjan amit irtal ha a csak a VIRT_UART0 van definialva. Lehet hogy kell meg mas command line argument is? Itten a kutatkodas kozben valahogy atjott az hogy a DTS/DTB-t is le tudom kerni amit a QEMU atpasszol - de aztan most hirtelen nem talalom hogy hogy is volt... ha ott hianyzik az megmagyaraz(hat) dolgokat. 

[...]

Megneztem, a DTS-ben csak ilyenek lesznek:

                virtio_mmio@10001000 {
                        interrupts = <0x01>;
                        interrupt-parent = <0x03>;
                        reg = <0x00 0x10001000 0x00 0x1000>;
                        compatible = "virtio,mmio";

(es mindez 8x, a 0x10001000 ... 0x10008000 cimeken). Szoval semmi sem utal a DTB/DTS-ben arra hogy ez igy egy valami lenne :/

A device tree-ben a virtio_mmio regiszter blokkoknál konkrétabbat nem fogsz látni; a konkrét virtio eszköztípusokat a kernel driver(ek)nek kell felderíteniük, a virtio protokoll szerint.

Az info mtree-ben viszont látszani kellene a változásnak!

Említetted azt is, hogy a guest userland teljesen le van csupaszítva. Így valószínűleg systemd/udev hiányában nem jönnek létre a vport1p1 és társai device node-ok, valamint a rájuk mutató /dev/virtio-ports/* symlink-ek.

... Hm, ennek a "drivers/char/Kconfig" ellentmond:

config VIRTIO_CONSOLE
	tristate "Virtio console"
	depends on TTY
	select HVC_DRIVER
	select VIRTIO
	help
	  Virtio console for use with hypervisors.

	  Also serves as a general-purpose serial device for data
	  transfer between the guest and host.  Character devices at
	  /dev/vportNpn will be created when corresponding ports are
	  found, where N is the device number and n is the port number
	  within that device.  If specified by the host, a sysfs
	  attribute called 'name' will be populated with a name for
	  the port which can be used by udev scripts to create a
	  symlink to the device.

Ezek szerint a vport1p1-nek létre kellene jönnie... valóban, a kernel forrásban ("drivers/char/virtio_console.c") benne van a device node létrehozása.

Hm hm hm... A következő be van állítva a guest kernel-ed konfigjában?

config VIRTIO_MMIO
	tristate "Platform bus driver for memory mapped virtio devices"
	depends on HAS_IOMEM && HAS_DMA
	select VIRTIO
	help
	 This drivers provides support for memory mapped virtio
	 platform device driver.

 	 If unsure, say N.

Ez szabályozza a virtio-mmio transport driver-t (drivers/virtio/virtio_mmio.c), és enélkül semmi nem fog ráugrani azokra a bizonyos virtio_mmio regiszter blokkokra a device tree-ben.

Szerk.: ja most látom a fenti grep-ed kimenetéből, hogy a VIRTIO_MMIO tényleg nincs beállítva; akkor azt javaslom.

Szerk.: ja most látom a fenti grep-ed kimenetéből, hogy a VIRTIO_MMIO tényleg nincs beállítva; akkor azt javaslom.

Hm... ezt most nezem, de a 6.1.92-esben meg nincs benne egyatalan. Megneztem a masik mainline-t (6.6.32), abban mar benne van. Ugyhogy akkor forditok egy ilyet. Lehet hogy akkor az MMIO support az ujabb, es a VIRTIO az alapbol PCI-re van kihegyezve. A RISC-V meg ugye kozvetlenul MMIO alapu. 

Szerk: igen, kozben a 6.6.32-es is megy, es ha megadom a "virtio_mmio.device=0x200@0x10001000:4:0" cmdline-t a kernelnek, akkor meg is jelenik a dmesg-ben az hogy:

[    0.266855] virtio-mmio 10001000.virtio_mmio: can't request region for resource [mem 0x10001000-0x10001fff]
[    0.267246] virtio-mmio: probe of 10001000.virtio_mmio failed with error -16

Szoval valoszinuleg ezen az "az info mtree-ben viszont látszani kellene a változásnak" dolgon bukik el a dolog, amit irsz. Mert ott ugye tovabbra sem jelenik (miert is kerulne bele, csak a guest kernel frissitesevel). Most egyebkent igy inditom az egeszet:

#Image=./Image-6.1.92
Image=./Image-6.6.32

/usr/bin/qemu-system-riscv32 -m 512M -nographic -machine virt \
        -kernel ${Image} \
        -initrd ./initramfs.cpio.gz \
        -append "console=ttyS0 virtio_mmio.device=0x200@0x10001000:4:0" \
        -device virtio-serial,id=vser0 \
        -device virtserialport,bus=vser0.0,chardev=ch0,name=foobar \
        -chardev serial,id=ch0,path=/dev/tnt2 \
        -serial /dev/tnt0

Szoval igen, itt meg nyomozni kell :) 

...megvan a probléma; én a korábbi kommentemben azt írtam, hogy "-device virtio-serial-device", a Te parancssorodban viszont az áll, hogy "-device virtio-serial". Mind a két opció értelmes, az utóbbi azonban a pcie.0 buszra teszi rá a virtio serial device-t, tehát PCIe transzportot fog használni, és nem MMIO-t.

Miután kijavítod a device model nevét "virtio-serial"-ról "virtio-serial-device"-ra, az "info qtree" monitor parancs kimenetében ezt fogod látni:

  dev: virtio-mmio, id ""
    [...]
    mmio 0000000010008000/0000000000000200
    bus: virtio-mmio-bus.7
      type virtio-mmio-bus
      dev: virtio-serial-device, id "vser0"
        [...]
        bus: vser0.0
          type virtio-serial-bus
          dev: virtserialport, id ""
            [...]

Aha, koszi, alakul! Igen, azt lattam en is hogy a 0x10008000-as cimre pakolta (miert, kerdem en), es igy modositva a command line-t mar a kernel is a 0x10008000-as cimen kezdi keresni - de egyelore sikertelenul:

[    0.195194] virtio-mmio: Registering device virtio-mmio.0 at 0x10008000-0x100081ff, IRQ 16.
[...]
[    0.255124] virtio-mmio 10008000.virtio_mmio: can't request region for resource [mem 0x10008000-0x10008fff]
[    0.255601] virtio-mmio: probe of 10008000.virtio_mmio failed with error -16

A /proc/iomem meg most ezt mondja:

10000000-100000ff : serial
10008000-100081ff : virtio-mmio.0
  10008000-100081ff : virtio-mmio.0 virtio-mmio.0
80000000-8001ffff : Reserved
80400000-9fffffff : System RAM
  80401000-81cde51f : Kernel image
    80401000-80822e4b : Kernel code
    81400000-817fffff : Kernel rodata
    81c00000-81c9d4d7 : Kernel data
    81c9e000-81cde51f : Kernel bss

Ez ugy hellyel-kozzel ertelmesnek tunik, de lehet hogy a jo az nem ilyen :)

A következő nálam működik:

qemu-system-riscv32 \
  -nodefaults \
  \
  -machine virt \
  -m 512M \
  \
  -kernel Image-6.6.32 \
  -initrd ./initramfs.cpio.gz \
  -append 'console=ttyS0' \
  \
  -device virtio-serial-device,id=vser0 \
  -device virtserialport,bus=vser0.0,chardev=ch0 \
  -chardev socket,id=ch0,server=on,wait=on,path=sock \
  \
  -serial chardev:ch1 \
  -mon chardev=ch1 \
  -chardev stdio,id=ch1,mux=on \

Ez azt csinálja, hogy a qemu stdin-jén/stdout-ján a monitor és a guest normál soros portja van multiplexelve, valamint létrehoz 1db virtio-serial port-ot a guest-ben, amely a host oldalon a "sock" nevű UNIX domain socket-tel van összekötve. A qemu létrehozza (bind-olja) ezt a socket-et, de addig nem kezdi futtatni a guest-et, amíg a socket-re rá nem csatlakozik egy másik (host) processz.

Nevezett másik host processz legyen ez (másik terminálból futtatandó):

socat STDIO UNIX-CONNECT:sock

Amikor pedig a guest-ben elérjük a root shell-t, akkor ott futtassuk az alábbit:

socat GOPEN:/dev/vport0p1 STDIO

Amit az egyik socat-nál begépelünk (+Enter), az a másikon megjelenik, tehát a virtio-serial port működik.

Ah, teljesen jo... akkor csak a virtio-serial-device leirason kellett egy kicsit valtoztatni. Ha jol latom, pont a kernel command line kavarta meg a dolgot, tkp csak azt kellett kiszedni az enyembol es mar jo is lett. Es az a vicc hogyha a cmdline-ban a 0x10001000-s cimet adom meg, akkor _jo_, ha meg azt ahova tenylegesen be is teszi, a 0x10008000-asat, akkor meg rossz... szoval nyeh... 

Koszi megegyszer :)

a kutatkodas kozben valahogy atjott az hogy a DTS/DTB-t is le tudom kerni amit a QEMU atpasszol

Ehhez külön hozzáfűzném, hogy a QEMU által generált DTB-t a QEMU közvetlenül ki tudja írni file-ba, egyrészt a "-machine dumpdtb=FILENAME" parancssori opcióval (amikor is a QEMU aztán nem is indul el rendesen), másrészt normál futás közben a "dumpdtb FILENAME" monitor paranccsal.