QEMU позволяет запускать программы, собранные для архитектур, отличающихся от архитектуры хост-машины, как известно. Чуть менее известно то, что с его помощью можно запустить эмулятор всей ЭВМ целиком и установить в него целую операционную систему иной архитектуры. И если с запуском x86_64 никаких проблем не возникнет (а на обычных PC по факту это просто виртуализация, а не эмуляция), то при попытке запустить эмулятор для ARM может обнаружиться, что систему туда так просто не установить: у эмулятора ARM нет ничего похожего на BIOS, который любезно организует старт установки с виртуального дисковода или флешки, а после установки – будет передавать загрузку в grub или его аналоги.
Попробуем организовать эмулятор для ARMv7 при помощи QEMU 4.20 и установим какой-нибудь Linux.
Для начала нам понадобится скачать так называемый netboot образ системы для armhf, состоящий из двух компонент: vmlinuz и initrd.gz. После ряда экспериментов выяснилось, что современные инсталляторы Ubuntu по умолчанию непригодны для этих целей, так что возьмем Debian Buster[1].
Подготовим файл образа “диска”, в который будем устанавливать систему:
qemu-img create -f qcow2 debian.qcow2 8G
В ряде инструкций рекомендуется подготовить сеть, подняв на хост-машине tap-интерфейс, что позволит иметь возможность достучаться с хост-машины в гостевую, но для упрощения ситуации запустим сеть в usermode – она работает достаточно быстро и для ряда задач её хватит.
Собираем файлы в одном месте и запускаем эмулятор со следующими параметрами:
qemu-system-arm -smp 4 -m 1024 -M virt \ -kernel vmlinuz \ -initrd initrd.gz \ -append "root=/dev/ram" \ -netdev user,id=n1 -device virtio-net-device,netdev=n1 \ -drive if=none,file=debian.img,format=qcow2,id=hd \ -device virtio-blk-device,drive=hd \ -no-reboot
Параметры:
- smp – количество ядер;
- m – объем памяти;
- M – тип машины, virt – абстрактно-виртуальный ARM;
- netdev – тип сети, user – самый простой вариант, не требующий настройки хост-машины;
- device virtio-net-device – устройство “сетевой карты”, и выбирать следует именно это, а не упомянутое в различных инструкциях, т.к. иначе с огромной вероятностью вы наткнетесь на сообщение о том, что инсталлятор системы не обнаружил никаких сетевых устройств в системе;
- drive if=none,file=debian.img,format=qcow2 – образ жесткого диска;
- device virtio-blk-device – устройство “жесткого диска”, аналогично сети, этот вариант обнаруживается установщиком, многие другие – нет;
- no-reboot – перезапуск системы приведет к остановке эмулятора.
Если запускаем qemu-system-arm в консоли без графического интерфейса, то дописываем “-nographic”.
Устанавливаем Debian как обычно. На количестве ядер не следует экономить, т.к. в процессе установки есть этапы, которые, по всей видимости, могут неплохо параллелиться, а однопоточная производительность эмулятора далека от производительности хост-машины: в среднем на установку системы уходило около часа на четырехъядерном Intel Xeon 3.5 GHz.
Ближе к концу установки инсталлятор ругнется на невозможность установки grub – так и должно быть в этом эмуляторе.
Далее, когда инсталлятор сообщит о завершении, выберите “Go back” и из главного меню установщика выберите пункт про вход в консоль. В консоли целевой диск, куда производилась установка, будет доступен по пути /target. Поскольку у эмулятора нет загрузчика, нам понадобится вытащить уже из установленной системы соответствующие ей vmlinuz и initrd.img. Для этого можно сделать chroot /target и, используя набор стандартных утилит – например, scp – откопировать файлы, находящиеся в /target/boot (а после chroot так просто в /boot) куда-нибудь вовне эмулятора. Если установленных утилит не хватает, то уже в chroot-е можно легко использовать apt и apt-get для установки недостающего. После копирования выходим из консоли и в меню инсталлятора выбираем пункт завершения установки. Когда установщик выполнит перезагрузку, эмулятор просто выключится согласно опции no-reboot.
Конечно, в качестве альтернативы можно вынуть vmlinuz и initrd из установленной системы и при помощи средств конвертации и распаковки qcow2 файлов – таких инструкций в интернете уйма. В этом случае прежде, чем что-то сделать с образом диска, не забывайте остановить эмулятор, чтобы не повредить образ.
Причина же, по которой изначально не удалось использовать современные образы Ubuntu, состоит в том, что в них после установки по малопонятной причине не генерируется файл initrd.img – в директории /target/boot есть битые симлинки на него, тогда как сам initrd отсутствует. [Update: Способ исправления был найден в комментариях на gist.github.com[2]]
Итак, после описанных действий у вас должен оказаться на руках образ debian.img с установленной ОС и файлы vmlinuz и initrd.img, взятые из этого же образа. Старые vmlinuz и initrd.gz с netboot-инсталлятором можно удалить – они больше не понадобятся. Теперь для запуска системы нужно переписать аргументы qemu-system-arm на следующие:
qemu-system-arm -smp 4 -m 1024 -M virt \ -kernel vmlinuz \ -initrd initrd.img \ -append "root=/dev/vda2" \ -netdev user,id=n1 -device virtio-net-device,netdev=n1 \ -drive if=none,file=debian.img,format=qcow2,id=hd \ -device virtio-blk-device,drive=hd \ -no-reboot
Вот, в общем-то, и всё.
Для того, чтобы зайти извне в такую гостевую машину по SSH, можно заменить -netdev user,id=n1 на -netdev user,hostfwd=tcp::10022-:22,id=n1, и тогда при подключении к порту 10022 на хост-машине соединение будет перенаправлено на 22 порт гостевой машины. Либо воспользоваться ssh-туннелем, подняв из гостевой машины подключение к удаленному ssh-серверу с дополнительным параметром -R 10022:localhost:22 – в этом случае порт 10022 на удаленном сервере будет перенаправлен на порт 22 гостевой машины.
Теперь можно пробовать собирать программы нативным компилятором нужной архитектуры, а не кросс-компиляцией. Ну или зачем там вы ещё запускали этот эмулятор.