#!/bin/sh # plasma-start-qemu.sh — arranca Plasma en una VM QEMU con render por SOFTWARE (kms_swrast/llvmpipe) # sobre el DRM de virtio-gpu (/dev/dri/card0). Derivado de plasma-start-metal.sh pero SIN hardware iris: # en QEMU no hay GPU real, así que kwin presenta por el DRM de virtio con EGL/GBM de software. # # Se instala como /usr/bin/plasma-start en la imagen QEMU y lo AUTO-LANZA /etc/profile (console-getty # autologin). Trae los arreglos de la campaña KDE 2026-07: QT_QUICK_BACKEND=software (plasmashell no crea # contexto GL sin él), QT_QPA_FONTDIR (mata los tofu; qtbase se construyó sin fontconfig), kdeglobals + # QT_QPA_PLATFORMTHEME=kde (iconos + estilo Breeze), kactivitymanagerd (plasmashell aborta sin él). # # Logs: /tmp/{kwin,kamd,plasmashell}.log + tee a /dev/console. Salir: matar kwin. set -u # getty ejecuta este script DIRECTO (no vía login shell) ⇒ PATH viene vacío: mkdir/tr/cat/dbus-daemon # no se hallan y kwin nunca levanta. Seteamos PATH explícito (busybox + userland KDE) ANTES de todo. export PATH=/usr/bin:/bin:/usr/sbin:/sbin # tee a la consola gráfica (tty0) Y al serial (ttyS0) — el host captura ttyS0 con -serial file: say() { echo "== plasma-qemu :: $*" | tee -a /dev/console /dev/ttyS0 2>/dev/null; } dump() { echo "---- $1 ----" > /dev/ttyS0 2>/dev/null; cat "$1" > /dev/ttyS0 2>/dev/null; } export LD_LIBRARY_PATH=/usr/lib64:/usr/lib/qt6:/usr/lib:/lib export QT_PLUGIN_PATH=/usr/lib64/qt6/plugins:/usr/lib/qt6/plugins export QML2_IMPORT_PATH=/usr/lib64/qt6/qml:/usr/lib/qt6/qml export QML_IMPORT_PATH=$QML2_IMPORT_PATH export XDG_DATA_DIRS=/usr/share # ⚠ XDG_MENU_PREFIX: sin esto el MENÚ DE APLICACIONES sale VACÍO, y el fallo no se ve al arrancar. # Medido el 2026-09-09 arrancando la imagen KDE: el serial dice # "applications.menu" not found in QList("/etc/xdg/menus") # y el escritorio pinta perfecto igual. **No falta el fichero**: `plasma-workspace` instala # `/etc/xdg/menus/plasma-applications.menu`. Lo que falta es el PREFIJO que le dice a Plasma que # busque ese nombre y no `applications.menu` a secas. Es la etiqueta contra el hecho: el error # nombra un fichero que nunca existió con ese nombre en ninguna distro con Plasma. export XDG_MENU_PREFIX=plasma- export XDG_RUNTIME_DIR=/run/user/0 export XDG_CONFIG_HOME=/root/.config # konsole toma el intérprete de $SHELL, NO de /etc/passwd, y este getty no es un login shell ⇒ sin # esto la terminal abre con «Warning: Could not find '', starting '/bin/sh' instead. Please check # your profile settings.» en rojo. El shell caía bien de casualidad; el aviso era real. export SHELL=/bin/sh export XDG_CACHE_HOME=/root/.cache export HOME=/root # --- render por software sobre virtio-gpu --- export LIBGL_ALWAYS_SOFTWARE=1 export GALLIUM_DRIVER=llvmpipe # GBM_ALWAYS_SOFTWARE=1 — EL FIX del segfault (MURO 2, verificado en fuente mesa 24.0.9). virtio-gpu-pci # SIN 3D expone un nodo KMS-only (no render-capable). Con MESA_LOADER_DRIVER_OVERRIDE solo, gbm carga # kms_swrast pero deja gbm_dri->software=FALSE (dri_screen_create tiene éxito ⇒ nunca cae al fallback # dri_screen_create_sw, el único que setea software=true). Entonces dri2_initialize_drm entra al bloque # `if(!software)` y llama gbm_dri->mesa->queryCompatibleRenderOnlyDeviceFd() — NULL en el driver software # ⇒ SIGSEGV. GBM_ALWAYS_SOFTWARE=1 fuerza dri_screen_create_sw ⇒ software=true ⇒ se salta ese bloque. export GBM_ALWAYS_SOFTWARE=1 # kms_swrast (NO swrast): kwin abre card0 como dispositivo KMS y crea un gbm device sobre él ⇒ necesita # el driver de software para KMS (dumb buffers). Con 'swrast' a secas gbm_create_device falla ("Failed # to create gbm device"). kms_swrast_dri.so está en /usr/lib/dri. export MESA_LOADER_DRIVER_OVERRIDE=kms_swrast export LIBGL_DRIVERS_PATH=/usr/lib/dri export QT_QUICK_BACKEND=software # LP_NUM_THREADS=1: llvmpipe SINCRÓNICO (sin pool de threads del rasterizador) — el JIT multi-thread de # llvmpipe segfaultea al componer la 1ra superficie en esta VM. KWIN_DRM_NO_AMS=1: desactiva atomic # modesetting (algunos virtio-gpu lo rompen). Ambos son fixes conocidos de kwin/llvmpipe en VMs. export LP_NUM_THREADS=1 # KWIN_DRM_NO_AMS (legacy modeset) fue una corazonada del segfault (que resultó ser GBM_ALWAYS_SOFTWARE). # Dejamos atomic (virtio soporta atomic nativo; legacy es el frágil). # export KWIN_DRM_NO_AMS=1 # # EL FIX DEL NEGRO: virtio-gpu ADVIERTE soportar drmModeAddFB2WithModifiers pero RECHAZA el modifier # explícito (aún LINEAR=0) con EINVAL ⇒ "Failed to create a framebuffer" ⇒ sin output layer ⇒ NEGRO. # El modeset inicial usa un buffer dumb (sin modifiers) y SÍ pinta (por eso se ve el wallpaper un instante), # pero los buffers del compositor van por AddFB2WithModifiers y fallan ⇒ vuelve a negro. KWIN_DRM_USE_MODIFIERS=0 # fuerza a kwin a usar drmModeAddFB2 plano (modifier implícito) que virtio-gpu sí acepta. export KWIN_DRM_USE_MODIFIERS=0 # KWIN_COMPOSE=Q — compositor QPainter (raster de Qt directo al buffer, SIN escena llvmpipe). El # compositor GL por defecto usa el rasterizador de llvmpipe, cuyo hilo SEGFAULTEA en lp_rast_clear_color # → util_fill_rect al limpiar el color buffer (backtrace del core.llvmpipe-0): kwin pinta unos frames y # muere → "vuelve a negro". QPainter no arma escenas llvmpipe ⇒ esquiva ese crash. (El segfault viejo con # =Q era el de gbm-init, ya resuelto por GBM_ALWAYS_SOFTWARE.) llvmpipe sigue disponible para gbm/EGL. export KWIN_COMPOSE=Q export QT_QPA_PLATFORM=wayland export QT_QPA_PLATFORMTHEME=kde export QT_QPA_FONTDIR=/usr/share/fonts/dejavu # cursor: kwin busca el tema "default" y no lo halla ("Failed to load cursor theme") ⇒ cursor invisible. # Apuntamos al breeze_cursors del rootfs (115 cursores) por env + kcminputrc (kwin lee [Mouse] cursorTheme). export XCURSOR_THEME=breeze_cursors export XCURSOR_PATH=/usr/share/icons:/usr/share/pixmaps export XCURSOR_SIZE=24 # El tema ya carga (sin "Failed to load cursor theme") pero el cursor SIGUE invisible: kwin usa el plano # de cursor por HARDWARE del DRM, y en virtio-gpu con render software ese plano no se presenta (aparece y # desaparece). KWIN_FORCE_SW_CURSOR=1 ⇒ kwin dibuja el cursor DENTRO del framebuffer principal (software) # ⇒ visible y estable en VMs sin GPU real. export KWIN_FORCE_SW_CURSOR=1 # --- DIAGNÓSTICO del segfault-al-1er-composite (MURO 2) --- # Sin gdb en la imagen: subimos el logging categorizado de kwin (kwin_core/drm/scene/opengl) + mesa/egl # para ver EN QUÉ FASE muere (bind EGL, alloc GBM, JIT llvmpipe, o el paint real). DIAG=0 para apagarlo. if [ "${DIAG:-1}" = 1 ]; then export QT_LOGGING_RULES="kwin_*=true;qt.qpa.*=true" export EGL_LOG_LEVEL=debug export LIBGL_DEBUG=verbose export MESA_DEBUG=1 export GALLIUM_HUD= # no HUD, sólo para dejar constancia export KWIN_DRM_DEBUG=1 fi mkdir -p "$XDG_RUNTIME_DIR" "$XDG_CONFIG_HOME" "$XDG_CACHE_HOME" /run/dbus /root chmod 700 "$XDG_RUNTIME_DIR" [ -s /etc/machine-id ] || cat /proc/sys/kernel/random/uuid 2>/dev/null | tr -d - > /etc/machine-id # kdeglobals: iconos + estilo Breeze (sin esto Qt cae a hicolor vacío = escritorio pelado) cat > "$XDG_CONFIG_HOME/kdeglobals" < "$XDG_CONFIG_HOME/kcminputrc" </dev/null | tr '\n' ' ')" [ -e /dev/dri/card0 ] || say "!! no hay /dev/dri/card0 — ¿virtio-gpu no cargó? (dmesg | grep -i drm)" # D-Bus de SISTEMA: sin él, kf.solid/udisks2 spamea "Not connected to D-Bus server" y powerdevil + los # applets de dispositivos/energía no inician. Escucha en el path estándar /run/dbus/system_bus_socket # (los clientes lo hallan por DBUS_SYSTEM_BUS_ADDRESS por defecto ⇒ no exportamos nada). Corre como root # y hace setuid a messagebus (uid 81, sembrado en el passwd de la imagen). No hay udisksd/upowerd reales # en el rootfs ⇒ no habrá discos/batería, pero el bus existe y solid deja de errorear en cada poll. mkdir -p /run/dbus /var/lib/dbus cp -f /etc/machine-id /var/lib/dbus/machine-id 2>/dev/null || true if [ ! -S /run/dbus/system_bus_socket ]; then if dbus-daemon --system --fork 2>/tmp/dbus-system.log; then say "system bus ON (/run/dbus/system_bus_socket)" else say "!! system bus NO arrancó:"; dump /tmp/dbus-system.log fi fi # D-Bus de sesión if [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]; then DBUS_SESSION_BUS_ADDRESS=$(dbus-daemon --session --fork --print-address 2>/dev/null) export DBUS_SESSION_BUS_ADDRESS fi say "dbus: ${DBUS_SESSION_BUS_ADDRESS:-NO ARRANCO}" # --- CORE DUMP del segfault (MURO 2): sin gdb en la imagen, sacamos un core y lo leemos con el gdb del # host apuntando al rootfs fundido. Localiza el frame culpable (libLLVM/llvmpipe vs kwin vs mesa). --- if [ "${DIAG:-1}" = 1 ]; then ulimit -c unlimited 2>/dev/null || true # /core en la RAÍZ ext4 (NO /tmp: es tmpfs=RAM y se pierde al matar QEMU). Tras el crash hacemos sync # explícito ⇒ el core persiste en la imagen aunque después mate QEMU con SIGKILL. echo '/core.%e.%p' > /proc/sys/kernel/core_pattern 2>/dev/null || true say "core dumps ON (pattern=$(cat /proc/sys/kernel/core_pattern 2>/dev/null), ulimit -c=$(ulimit -c))" fi # SÓLO el card de virtio-gpu: el efifb/simpledrm sobrevive como 2º DRM ("Unknown-1", read-only) y kwin # intenta manejar ambos outputs ⇒ conflicto de presentación ⇒ pinta y vuelve a NEGRO. Detectamos el card # cuyo driver es virtio_gpu (robusto al numerado card0/card1 que cambia entre boots) y se lo pasamos a kwin. VIRTIO_CARD= for c in /dev/dri/card*; do n=$(basename "$c") drv=$(readlink -f "/sys/class/drm/$n/device/driver" 2>/dev/null) case "$drv" in *virtio*) VIRTIO_CARD="$c"; break;; esac done if [ -n "$VIRTIO_CARD" ]; then export KWIN_DRM_DEVICES="$VIRTIO_CARD" say "KWIN_DRM_DEVICES=$VIRTIO_CARD (ignorando efifb/simpledrm)" else say "!! no hallé card virtio_gpu; kwin verá todos los DRM (posible doble output)" fi # Compositor sobre el DRM de virtio (software EGL/GBM vía kms_swrast). SIN --virtual: presenta al display. say "lanzando kwin_wayland (DRM virtio, software GL)..." /usr/bin/kwin_wayland --drm >/tmp/kwin.log 2>&1 & KWIN=$! # stream EN VIVO del kwin.log al serial (busybox sed NO soporta -u ⇒ nada de sed/pipes: tail -f directo). ( echo "==== kwin.log (stream vivo) ===="; tail -f /tmp/kwin.log ) > /dev/ttyS0 2>/dev/null & TAILPID=$! i=0 while [ $i -lt 60 ]; do [ -S "$XDG_RUNTIME_DIR/wayland-0" ] && break kill -0 $KWIN 2>/dev/null || { say "!! kwin murio:"; dump /tmp/kwin.log; exec /bin/sh; } i=$((i+1)); sleep 0.5 done if [ -S "$XDG_RUNTIME_DIR/wayland-0" ]; then say "compositor OK (wayland-0)" else say "!! kwin no expuso wayland-0:"; dump /tmp/kwin.log; exec /bin/sh fi export WAYLAND_DISPLAY=wayland-0 # kactivitymanagerd: plasmashell ABORTA sin el KAMD=$(ls /usr/lib*/libexec/kactivitymanagerd 2>/dev/null | head -1) [ -n "$KAMD" ] && { "$KAMD" >/tmp/kamd.log 2>&1 & sleep 2; say "kactivitymanagerd lanzado"; } say "lanzando plasmashell..." /usr/bin/plasmashell >/tmp/plasmashell.log 2>&1 & sleep 8 say "procesos: $(ps axo comm 2>/dev/null | grep -E 'kwin|plasma' | sort -u | tr '\n' ' ')" say "escritorio arriba (o mira /tmp/plasmashell.log). Ctrl-C mata la sesion." dump /tmp/plasmashell.log # monitor de estado: cada 15s dice si kwin/plasmashell siguen vivos ⇒ si vuelve a negro, sabremos si # fue plasmashell que murió (containment) o kwin/output. (busybox pgrep -c cuenta procesos.) ( n=0; while sleep 15; do n=$((n+1)) say "STATUS +$((n*15))s: kwin=$(pgrep kwin_wayland 2>/dev/null | wc -l) plasmashell=$(pgrep plasmashell 2>/dev/null | wc -l)" done ) & wait $KWIN RC=$? # kwin salió ⇒ volcá su log para saber por qué (backend QPainter, DRM, modeset…) say "kwin salió (código $RC) — volcando kwin.log:" dump /tmp/kwin.log # copiar los logs a la ext4 raíz y SYNC ⇒ core + logs persisten en la imagen para leer desde el host # con debugfs/gdb (el SIGKILL de QEMU no sincroniza; sin este sync se pierde todo lo de esta sesión). cp /tmp/kwin.log /kwin.log 2>/dev/null || true cp /tmp/plasmashell.log /plasmashell.log 2>/dev/null || true say "cores en /: $(ls -la /core.* 2>/dev/null | tr '\n' ' ' || echo NINGUNO)" sync; sync say "== disco SYNCED (core+logs en la raíz). serial CONGELADO. matá QEMU desde el host. ==" sleep 3600