Montag, 24. Februar 2014

OS X Virtualisierung mit KVM

Virtualisierung von OSX mit KVM

Virtualisierung von OSX mit KVM

1. Einleitung

Dank der außerordentlichen Vorarbeit von Alexander Graf und Gabriel L. Somlo ist es überhaupt möglich OS X als KVM Gast auszuführen. Insbesondere die von Gabriel L. Somlo verfasste Dokumentation führte mich durch die grundlegenden Schritte zur Virtualisierung von OS X. Deswegen werde ich wann immer es möglich und sinvoll erscheint auf diese Dokumentation verweisen.
Sinn und Zweck dieses Artikels ist viel mehr Möglichkeiten aufzuzeigen ein virtualisiertes OS X möglichst komfortabel zu verwalten und für einen Endanwender erreichbar zu machen.

2. Vorbereitung

Zunächst müssen qemu, KVM und Seabios aus den jeweiligen git Repositorien bezogen, gepatched und anschließend kompiliert werden. Der Vorgang wird detailiert in der bereits erwähnten Dokumentation von Herrn Somlo beschrieben. Anbei folgen ausschließlich die nötigen Befehle, um dies zu erreichen.

KVM

  mkdir -p /home/$(whoami)/OSXGUEST;
  cd /home/$(whoami)/OSXGUEST
  wget http://www.contrib.andrew.cmu.edu/~somlo/OSXKVM/kvm-polarity-20140205.patch
  git clone git://git.kernel.org/pub/scm/virt/kvm/kvm.git
  (cd kvm; patch -p1 < ../kvm-polarity-20140205.patch)
    
  git clone git://git.kiszka.org/kvm-kmod.git
  cd kvm-kmod
  ./configure
  make LINUX=../kvm clean sync all
  
  modprobe -r kvm_intel
  cp ./x86/kvm*.ko /lib/modules//kernel/arch/x86/kvm/
  modprobe kvm_intel
            

qemu

  cd /home/$(whoami)/OSXGUEST
  git clone git://git.qemu.org/qemu.git;
  wget http://www.contrib.andrew.cmu.edu/~somlo/OSXKVM/qemu-e1000-20131222.patch
  cd qemu
  patch -p1 > ../qemu-e1000-20131222.patch
  ./configure --prefix=/home/$(whoami)/OSXGUEST --target-list=x86_64-softmmu
  make clean; make; make install
            

Seabios

  cd /home/$(whoami)/OSXGUEST
  git clone git://git.seabios.org/seabios.git
  wget http://www.contrib.andrew.cmu.edu/~somlo/OSXKVM/seabios-dmi-cumulative-20140204.patch
  cd seabios
  patch -p1 < ../seabios-dmi-cumulative-20140204.patch
  make
  cp out/bios.bin /home/$(whoami)/OSXGUEST/share/qemu/bios-mac.bin
            
Nun wird nur noch der Chameleon Bootloader benötigt. Dieser kann aus einem SVN Repositorium bezogen und kompiliert werden oder alternativ als von Gabriel L. Somlo kompiliertes binäres Image heruntergeladen werden. Das Image möchte ich an dieser Stelle nicht direkt verlinken, sondern verweise erneut auf die Dokumentation von Herrn Somlo. Dieser sind ebenfalls alle oben aufgeführten Befehle entnommen. Im folgenden wird davon ausgegangen, dass sich das Chameleon Image im angelegten OSXGUEST Ordner im Homeverzeichnis befindet.

Installation / Start

  # Image Erstellung
  cd /home/$(whoami)/OSXGUEST
  qemu-img create -f qcow2 mac_hdd.img 30G
    
  # Start der VM
  bin/qemu-system-x86_64 -enable-kvm -m 2048 -cpu core2duo -M q35 \
  -usb -device usb-kbd -device usb-mouse \
  -device isa-applesmc,osk="insert-real-64-byte-OSK-string-here" \
  -bios bios-mac.bin -kernel ./chameleon_svn2360_boot \
  -device ide-drive,bus=ide.2,drive=MacHDD \
  -drive id=MacHDD,if=none,file=./mac_hdd.img \
  -monitor stdio
    
  # mit Installationsmedium:
  -device ide-drive,bus=ide.0,drive=MacDVD \
  -drive id=MacDVD,if=none,snapshot=on,file=./osx10.6.iso
    

3. Verwaltung mit libvirt

Bis zur dieser Stelle habe ich kompakt zusammengefasst, was in bereits erwähnter Dokumentation beschrieben steht. Doch will man die frisch erstellte VM wirklich nutzen, sollte man zumindest den Startbefehl als Skript speichern. Unbefriedigend wird diese Lösung jedoch, will man seine VM über libvirt verwalten. Libvirt setzt zwar auf qemu auf, allerdings werden virtuelle Maschinen in XML Dateien deklariert.
Naheliegend wäre alle Startparameter in die XML Deklaration zu übersetzen. Doch leider sind hier Grenzen gesetzt. Der Parameter '-bios' wird z.B. bislang nicht durch libvirt unterstützt. Nach einiger Recherche fand ich einen kreativen Lösungsansatz auf der libvirt-users Mailingliste. Die libvirt XML Option <emulator> kann dazu genutzt werden jedes beliebige Programm beim Start einer virtuellen Maschine aufzurufen. Normalerweise wird hier die zu nutzende qemu Version angegeben. Doch unter Beachtung einiger Bindestücke (Name, libvirt Socket, VNC Port) kann hier auch ein Startskript für qemu 'zwischengeschoben' werden. Im Gegensatz zum Autor des Startskriptes übernehme ich die libvirt Parameter nicht, sondern ergänze den qemu Aufruf um die nötigen Optionen. Dies hat den Grund, dass ich auf mehrere Probleme durch von libvirt standardmäßig übergebene Optionen gestoßen bin. Für die Zukunft wäre es denkbar, alle problematischen Optionen zu identifizieren und im Skript herauszufiltern. Dadurch würde die manuelle VNC Port Vergabe überflüssig. Deswegen habe ich den entsprechenden Code nicht gelöscht, sondern nur die Nutzung der ALLARGS Variable unterbunden.

start_osx.sh

  #!/bin/bash   
         
  printf "%s" "$*" | grep -q boot
  
  if [ $? -ne 0 ]; then
    exec /home/kvm/OSXGUEST/bin/qemu-system-x86_64 "$@"
  fi
  
  ALLARGS=""

  while [ -n "$1" ]; do
        if [ "$1" = "-uuid" ]; then
                shift
        else
                ALLARGS="$ALLARGS $1"
        fi
        shift
  done

  exec /home/YOURUSERNAME/OSXGUEST/bin/qemu-system-x86_64 \
    -enable-kvm -m 2048 -cpu core2duo \
    -usbdevice keyboard -usbdevice mouse \
    -net user -net nic \
    -bios bios-mac.bin -kernel /home/YOURUSERNAME/OSXGUEST/chameleon_svn2360_boot \
    -device isa-applesmc,osk="nope, you're not getting that one here" \
    -device ahci,id=ide \
    -device ide-drive,bus=ide.2,drive=MacHDD \
    -drive id=MacHDD,if=none,file=/home/YOURUSERNAME/OSXGUEST/mac_hdd.img \
    -vga cirrus \
    -nodefconfig -nodefaults -chardev socket,id=charmonitor,path=/var/lib/libvirt/qemu/unnamed.monitor,server,nowait -mon chardev=charmonitor,id=monitor,mode=control \
    -k de -vnc 0.0.0.0:55 -name unnamed
Erstellt man nun eine passende XML Datei zu diesem Startskript, muss darauf geachtet werden, dass libvirt ein mindest Maß an definierten Optionen erwartet. Außerdem sind VNC Port, Name und Emulator an das Startskript anzupassen. Für unseren Fall sähe die XML Datei wie folgt aus:

osx.xml

<domain type="kvm"> <name>unnamed</name> <uuid>f2fa7141-1e00-75f5-31db-c224409238fd</uuid> <memory>2097152</memory> <currentmemory>2097152</currentmemory> <vcpu>1</vcpu> <os> <type arch="x86_64" machine="pc-1.0">hvm</type> <boot dev="hd"> </boot></os> <clock offset="utc"> <on_poweroff>destroy</on_poweroff> <on_reboot>restart</on_reboot> <on_crash>destroy</on_crash> <devices> <emulator>/home/YOURUSERNAME/OSXGUEST/start_osx.sh</emulator> <input bus="usb" type="mouse" /> <input bus="ps2" type="mouse" /> <graphics autoport="no" listen="0.0.0.0" port="5955" type="vnc"> <listen address="0.0.0.0" type="address"> </listen></graphics> <video> <model heads="1" type="cirrus" vram="9216"> <address bus="0x00" domain="0x0000" function="0x0" slot="0x02" type="pci"> </address> </model></video> <memballoon model="virtio"> <address bus="0x00" domain="0x0000" function="0x0" slot="0x05" type="pci"> </address> </memballoon> </devices> </clock></domain> Fast fertig! Die erstellte XML Datei muss nur noch für libvirt definiert werden (uuid muss einzigartig sein!):
  virsh define osx.xml

4. webvirtmgr als Frontend für libvirt

Als Frontend für libvirt habe ich mich gegen Virt-Manager entschieden, da ich einen plattformunabhängigen und möglichst einfachen Zugriff auf die von libvirt verwalteten Maschinen ermöglichen wollte. Ziel war es eine auf libvirt fußende, webbasierte Alternative zum Virt-Manager zu finden. Dabei bin ich auf webvirtmgr gestoßen, welcher, der Name lässt es bereits vermuten, genau dies bietet. webvirtmgr ist eine in Django implementierte Weboberfläche, welche zur Verwaltung eines KVM Hosts libvirt nutzt. Der VNC Zugriff erfolgt via noVNC - ein HTML 5 VNC Client.
Eine Anleitung zur Installation findet man hier.

5. Troubleshooting

100% CPU Last

Ursache hierfür ist, dass OS X Versionen bis 10.7 die von KVM nicht unterstützten CPU Befehle MWAIT und MONITOR nutzen. In unserer gepatchten Version werden diese durch NOP Befehle emuliert, was den Bootvorgang zwar ermöglicht, aber eine hohe CPU Last verursacht. Weitere Informationen gibt es hier. Um die Ausführung der rechenintensiven Befehle zu verhindern, muss im Gastsysteme folgende Datei gelöscht werden:
  sudo rm /System/Library/Extensions/AppleIntelCPUPowerManagement.kext
        

Einfrieren des Systems nach einiger Zeit

Scheinbar wird dies durch eine oder mehrere Energiesparfunktionen von OSX ausgelöst. Deaktiviert man alle besteht das Problem nicht mehr.

Mauszeiger Offset

Sollte durch die qemu Option '-usbdevice tablet' zu beheben sein. Leider bringt OS X keinen passenden Treiber mit. Eine Möglichkeit zum Umgehen ist die Nutzung des OS X internen VNC Servers. Dazu muss TCP Port 5900 der virtuellen Maschine erreichbar sein. Leicht realisieren lässt sich dies mittels Port Forwarding durch qemu. Um den OSX internen VNC Server zu verwenden, sollte man außerdem die an qemu übergebene Grafikkarte von 'cirrus' auf 'std' ändern. Zumindest bei mir kam es sonst zu unschönen Anzeigefehlern.

Keine Kommentare: