Портирование собственной ОС на Xen

    Пару лет назад на Мат-Мехе проходил студпроект по превращению Embox в идеальную платформу для столь модного нынче облака. Одной из очевидных частей этой задачи, было портирование на какую-нибудь платформу виртуализации, и выбор пал на Xen. В данной статье я расскажу о процессе портирования студентами ОС под Xen, добавлении новой платформы в Embox, ну и, конечно, зачем вообще всё это затевалось.

    Идея идеальной платформы для облака заключается в следующем:
    Есть концепция unikernel. Если коротко, то разработчик единственного приложения выбирает нужный набор функциональности для работы. Только эта функциональность включается в операционную систему, которая линкуется в единый образ вместе с приложением. При таком подходе, очевидно, экономятся ресурсы. Конечно, это единственное приложение обладает не очень большой функциональностью, но если в облако поместить множество различных приложений (по сути дела, сервисов), можно получить очень широкую функциональность.

    Портирование на Xen


    Проверка работоспособности Xen


    Любое портирование чего-либо на что-либо стоит начинать с проверки уже существующих решений. Под Xen уже работает довольно много операционных систем. Конечно, прежде всего нас интересует открытый Linux. У Xen есть одна особенность: прежде чем запускать что-то, нужно сначала научиться создавать экземпляры этой платформы и управлять ими. Другими словами, нужно научиться создавать отдельные виртуальные машины (домены).

    Для работы с Xen нужно установить соответствующие пакеты:

    sudo apt-get install xen-hypervisor-4.8-amd64 xen-tools

    После этого нужно перегрузиться и во время загрузки выбрать вариант вашей ОС на Xen (обычно стоит по умолчанию).

    Для проверки того, что теперь мы находимся под управлением гипервизора, достаточно проверить, что вывод команды cat /proc/cpuinfo | grep hypervisor не пустой. Аналогичным образом можно проверить вывод команды virt-what, он должен содержать домен xen-dom0

    Ещё один вариант проверки — использовать команду “xl”, которая нам потребуется в будущем

    Команда sudo xl list должна выводить что-то вроде этого:

    Name                                        ID   Mem VCPUs      State   Time(s)
    Domain-0                                     0   945     1     r-----      11.3

    Управление доменами


    В Xen есть несколько способов создания и управления виртуальными машинами.
    Первый — установка с помощью сторонних программ (например, virt-manager или oVirt). Обычно у них графический интерфейс, и программа сама <<копается>> в файлах системы, внося необходимые изменения.

    Второй способ — работа в консоли.

    В случае виртуализации Linux будем использовать xen-tools + xl (or xm). Это удобно тем, что многие дистрибутивы Linux уже подготовлены для паравиртуализации.

    Параметры с которыми создаются новые виртуальные машины, находятся в файле /etc/xen-tools/xen-tools.conf, их можно переопределить. Все параметры также можно переопределить при создании машины.

    Настройка сетевого окружения


    Есть несколько способов организации сети между доменами. Выбираем самую простую — Bridge Network (виртуальные машины будут видны в локальной сети как физические устройства наравне с dom0).

    Установим пакеты:

    sudo apt-get install bridge-utils

    Далее редактируем файл /etc/network/interfaces
    Было

    # The loopback network interface
    auto lo
    iface lo inet loopback
    
    # The primary network interface
    allow-hotplug enp5s0
    iface enp5s0 inet dhcp

    Стало

    # The loopback network interface
    auto lo
    iface lo inet loopback
    
    # The primary network interface
    allow-hotplug enp5s0
    iface enp5s0 inet manual
    
    auto xenbr0
    iface xenbr0 inet dhcp
            bridge_ports enp5s0


    Создание образа виртуальной машины.


    Пример создания виртуальной машины с Ubuntu:

    sudo xen-create-image --hostname test --dhcp --pygrub --dist precise --mirror="http://mirror.yandex.ru/ubuntu" --dir /srv/xen/test

    Флаг --pygrub указывает, что в виртуальной машине будет использоваться загрузчик PyGrub, позволяющий каждой виртуальной машине использовать своё собственное ядро вместо ядра из dom0. Параметр --dir указывает, что вы хотите использовать директорию для образов, а не отдельный раздел заранее разбитого диска.

    Вывод приблизительно следующий:
    General Information
    — Hostname: test
    Distribution: precise
    Mirror: mirror.yandex.ru/ubuntu
    Partitions: swap 512M (swap)
    / 4G (ext4)
    Image type: sparse
    Memory size: 256M
    Bootloader: pygrub

    Networking Information
    — IP Address: DHCP [MAC: 00:16:3E:8E:3C:E0]

    Creating partition image: /srv/xen/test/domains/test/swap.img
    Done

    Creating swap on /srv/xen/test/domains/test/swap.img
    Done

    Creating partition image: /srv/xen/test/domains/test/disk.img
    Done

    Creating ext4 filesystem on /srv/xen/test/domains/test/disk.img
    Done
    Installation method: debootstrap
    Done

    Running hooks
    Done

    No role scripts were specified. Skipping

    Creating Xen configuration file
    Done

    No role scripts were specified. Skipping
    Setting up root password
    Generating a password for the new guest.
    All done

    Logfile produced at:
    /var/log/xen-tools/test.log

    Installation Summary
    — Hostname: test
    Distribution: precise
    MAC Address: 00:16:3E:8E:3C:E0
    IP Address(es): dynamic
    SSH Fingerprint: SHA256:H49PEnPv0k0tw2faq1CStkR6KFlHF0GkUOWvYaeiqOU (DSA)
    SSH Fingerprint: SHA256:5gIsrTAriqEiwdkVCygOtLOi9uOd2DJWFBlJKxdJfUw (ECDSA)
    SSH Fingerprint: SHA256:SB+bTbkIUr2Qn019xT8AFtAKO5f6xlkbt8juVBq6zTE (RSA)
    Root Password: RJpaLfBFseH9YJX77ScxRwP

    Убедиться в том, что образ создан, можно с помощью команды:

    sudo xen-list-images

    В моем случае команда выдала

    Name: test
    Memory: 256 MB
    Config: /etc/xen/test.cfg

    Запуск (создание машины)


    sudo xl create /etc/xen/test.cfg

    Теперь виртуалка test появиться при вызове команды «sudo xl list»:

    Name                                        ID   Mem VCPUs    State    Time(s)
    Domain-0                                     0 15356     8     r-----    4283.8
    test                                         2   256     1     -b----       1.5

    Осталось подключиться к её консоли:

    sudo xl console test    

    Выход из консоли виртуальной машины осуществляется комбинацией "ctrl + ]"

    Полученный вывод
    [    0.000000] Initializing cgroup subsys cpuset
    [    0.000000] Initializing cgroup subsys cpu
    [    0.000000] Linux version 3.2.0-126-virtual (buildd@lcy01-11) (gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) ) #169-Ubuntu SMP Fri Mar 31 14:47:56 UTC 2017 (Ubuntu 3.2.0-126.169-virtual 3.2.79)
    [    0.000000] Command line: root=/dev/xvda2 ro elevator=noop root=/dev/xvda2 ro
    [    0.000000] KERNEL supported cpus:
    [    0.000000]   Intel GenuineIntel
    [    0.000000]   AMD AuthenticAMD
    [    0.000000]   Centaur CentaurHauls
    [    0.000000] ACPI in unprivileged domain disabled
    [    0.000000] Released 0 pages of unused memory
    [    0.000000] Set 0 page(s) to 1-1 mapping
    [    0.000000] BIOS-provided physical RAM map:
    [    0.000000]  Xen: 0000000000000000 - 00000000000a0000 (usable)
    [    0.000000]  Xen: 00000000000a0000 - 0000000000100000 (reserved)
    [    0.000000]  Xen: 0000000000100000 - 0000000010000000 (usable)
    [    0.000000] NX (Execute Disable) protection: active
    [    0.000000] DMI not present or invalid.
    [    0.000000] No AGP bridge found
    [    0.000000] last_pfn = 0x10000 max_arch_pfn = 0x400000000
    [    0.000000] init_memory_mapping: 0000000000000000-0000000010000000
    [    0.000000] RAMDISK: 0205c000 - 02c43000
    [    0.000000] NUMA turned off
    [    0.000000] Faking a node at 0000000000000000-0000000010000000
    [    0.000000] Initmem setup node 0 0000000000000000-0000000010000000
    [    0.000000]   NODE_DATA [000000000fffb000 - 000000000fffffff]
    [    0.000000] Zone PFN ranges:
    [    0.000000]   DMA      0x00000010 -> 0x00001000
    [    0.000000]   DMA32    0x00001000 -> 0x00100000
    [    0.000000]   Normal   empty
    [    0.000000] Movable zone start PFN for each node
    [    0.000000] early_node_map[2] active PFN ranges
    [    0.000000]     0: 0x00000010 -> 0x000000a0
    [    0.000000]     0: 0x00000100 -> 0x00010000
    [    0.000000] SFI: Simple Firmware Interface v0.81 http://simplefirmware.org
    [    0.000000] SMP: Allowing 1 CPUs, 0 hotplug CPUs
    [    0.000000] No local APIC present
    [    0.000000] APIC: disable apic facility
    [    0.000000] APIC: switched to apic NOOP
    [    0.000000] PM: Registered nosave memory: 00000000000a0000 - 0000000000100000
    [    0.000000] Allocating PCI resources starting at 10000000 (gap: 10000000:f0000000)
    [    0.000000] Booting paravirtualized kernel on Xen
    [    0.000000] Xen version: 4.8.3-pre (preserve-AD)
    [    0.000000] setup_percpu: NR_CPUS:64 nr_cpumask_bits:64 nr_cpu_ids:1 nr_node_ids:1
    [    0.000000] PERCPU: Embedded 27 pages/cpu @ffff88000fc00000 s78848 r8192 d23552 u2097152
    [    0.000000] Built 1 zonelists in Node order, mobility grouping on.  Total pages: 64395
    [    0.000000] Policy zone: DMA32
    [    0.000000] Kernel command line: root=/dev/xvda2 ro elevator=noop root=/dev/xvda2 ro
    [    0.000000] PID hash table entries: 1024 (order: 1, 8192 bytes)
    [    0.000000] xsave/xrstor: enabled xstate_bv 0x7, cntxt size 0x340
    [    0.000000] Checking aperture...
    [    0.000000] No AGP bridge found
    [    0.000000] Memory: 228408k/262144k available (6617k kernel code, 448k absent, 33288k reserved, 6579k data, 932k init)
    [    0.000000] SLUB: Genslabs=15, HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
    [    0.000000] Hierarchical RCU implementation.
    [    0.000000]     RCU dyntick-idle grace-period acceleration is enabled.
    [    0.000000] NR_IRQS:4352 nr_irqs:256 16
    [    0.000000] Console: colour dummy device 80x25
    [    0.000000] console [tty0] enabled
    [    0.000000] console [hvc0] enabled
    [    0.000000] allocated 2097152 bytes of page_cgroup
    [    0.000000] please try 'cgroup_disable=memory' option if you don't want memory cgroups
    [    0.000000] installing Xen timer for CPU 0
    [    0.000000] Detected 3194.398 MHz processor.
    [    0.004000] Calibrating delay loop (skipped), value calculated using timer frequency.. 6388.79 BogoMIPS (lpj=12777592)
    [    0.004000] pid_max: default: 32768 minimum: 301
    [    0.004000] Security Framework initialized
    [    0.004000] AppArmor: AppArmor initialized
    [    0.004000] Yama: becoming mindful.
    [    0.004000] Dentry cache hash table entries: 32768 (order: 6, 262144 bytes)
    [    0.004000] Inode-cache hash table entries: 16384 (order: 5, 131072 bytes)
    [    0.004000] Mount-cache hash table entries: 256
    [    0.004000] Initializing cgroup subsys cpuacct
    [    0.004000] Initializing cgroup subsys memory
    [    0.004000] Initializing cgroup subsys devices
    [    0.004000] Initializing cgroup subsys freezer
    [    0.004000] Initializing cgroup subsys blkio
    [    0.004000] Initializing cgroup subsys perf_event
    [    0.004000] CPU: Physical Processor ID: 0
    [    0.004000] CPU: Processor Core ID: 0
    [    0.006575] SMP alternatives: switching to UP code
    [    0.034623] Freeing SMP alternatives: 24k freed
    [    0.034647] ftrace: allocating 26699 entries in 105 pages
    [    0.036061] cpu 0 spinlock event irq 17
    [    0.036079] Performance Events:
    [    0.036082] no APIC, boot with the "lapic" boot parameter to force-enable it.
    [    0.036086] no hardware sampling interrupt available.
    [    0.036094] Broken PMU hardware detected, using software events only.
    [    0.036207] NMI watchdog disabled (cpu0): hardware events not enabled
    [    0.036229] Brought up 1 CPUs
    [    0.036343] devtmpfs: initialized
    [    0.036844] EVM: security.selinux
    [    0.036848] EVM: security.SMACK64
    [    0.036851] EVM: security.capability
    [    0.037364] Grant table initialized
    [    0.037410] print_constraints: dummy:
    [    0.057265] RTC time: 165:165:165, date: 165/165/65
    [    0.057318] NET: Registered protocol family 16
    [    0.057508] Extended Config Space enabled on 0 nodes
    [    0.057542] PCI: setting up Xen PCI frontend stub
    [    0.057542] bio: create slab <bio-0> at 0
    [    0.057542] ACPI: Interpreter disabled.
    [    0.057542] xen/balloon: Initialising balloon driver.
    [    0.057542] xen-balloon: Initialising balloon driver.
    [    0.057542] vgaarb: loaded
    [    0.057542] i2c-core: driver [aat2870] using legacy suspend method
    [    0.057542] i2c-core: driver [aat2870] using legacy resume method
    [    0.057542] SCSI subsystem initialized
    [    0.057542] usbcore: registered new interface driver usbfs
    [    0.057542] usbcore: registered new interface driver hub
    [    0.057542] usbcore: registered new device driver usb
    [    0.057542] PCI: System does not support PCI
    [    0.057542] PCI: System does not support PCI
    [    0.057542] NetLabel: Initializing
    [    0.057542] NetLabel:  domain hash size = 128
    [    0.057542] NetLabel:  protocols = UNLABELED CIPSOv4
    [    0.057542] NetLabel:  unlabeled traffic allowed by default
    [    0.057542] Switching to clocksource xen
    [    0.061230] AppArmor: AppArmor Filesystem Enabled
    [    0.061253] pnp: PnP ACPI: disabled
    [    0.062521] NET: Registered protocol family 2
    [    0.065648] IP route cache hash table entries: 2048 (order: 2, 16384 bytes)
    [    0.065807] TCP established hash table entries: 8192 (order: 5, 131072 bytes)
    [    0.065861] TCP bind hash table entries: 8192 (order: 5, 131072 bytes)
    [    0.065881] TCP: Hash tables configured (established 8192 bind 8192)
    [    0.065885] TCP reno registered
    [    0.065889] UDP hash table entries: 128 (order: 0, 4096 bytes)
    [    0.065895] UDP-Lite hash table entries: 128 (order: 0, 4096 bytes)
    [    0.065934] NET: Registered protocol family 1
    [    0.065967] platform rtc_cmos: registered platform RTC device (no PNP device found)
    [    0.066094] Trying to unpack rootfs image as initramfs...
    [    0.072210] audit: initializing netlink socket (disabled)
    [    0.190742] type=2000 audit(1518793913.856:1): initialized
    [    0.209702] Freeing initrd memory: 12188k freed
    [    0.213726] VFS: Disk quotas dquot_6.5.2
    [    0.213770] Dquot-cache hash table entries: 512 (order 0, 4096 bytes)
    [    0.213950] hugetlbfs: disabling because there are no supported hugepage sizes
    [    0.214050] fuse init (API version 7.17)
    [    0.214104] msgmni has been set to 469
    [    0.214342] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 253)
    [    0.214362] io scheduler noop registered (default)
    [    0.214367] io scheduler deadline registered
    [    0.214386] io scheduler cfq registered
    [    0.214439] pci_hotplug: PCI Hot Plug PCI Core version: 0.5
    [    0.214454] pciehp: PCI Express Hot Plug Controller Driver version: 0.4
    [    0.214460] acpiphp: ACPI Hot Plug PCI Controller Driver version: 0.5
    [    0.214672] Serial: 8250/16550 driver, 32 ports, IRQ sharing enabled
    [    0.215413] Linux agpgart interface v0.103
    [    0.216235] brd: module loaded
    [    0.216618] loop: module loaded
    [    0.217745] blkfront device/vbd/51714 num-ring-pages 1 nr_ents 32.
    [    0.218925] blkfront device/vbd/51713 num-ring-pages 1 nr_ents 32.
    [    0.219299] Fixed MDIO Bus: probed
    [    0.219322] tun: Universal TUN/TAP device driver, 1.6
    [    0.219329] tun: (C) 1999-2004 Max Krasnyansky <maxk@qualcomm.com>
    [    0.219390] PPP generic driver version 2.4.2
    [    0.219430] Initialising Xen virtual ethernet driver.
    [    0.222394] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
    [    0.222413] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
    [    0.222423] uhci_hcd: USB Universal Host Controller Interface driver
    [    0.222459] usbcore: registered new interface driver libusual
    [    0.222477] i8042: PNP: No PS/2 controller found. Probing ports directly.
    [    1.223457] i8042: No controller found
    [    1.223625] mousedev: PS/2 mouse device common for all mice
    [    1.263475] rtc_cmos rtc_cmos: rtc core: registered rtc_cmos as rtc0
    [    1.263520] rtc_cmos: probe of rtc_cmos failed with error -38
    [    1.263601] device-mapper: uevent: version 1.0.3
    [    1.263654] device-mapper: ioctl: 4.22.0-ioctl (2011-10-19) initialised: dm-devel@redhat.com
    [    1.263664] EFI Variables Facility v0.08 2004-May-17
    [    1.263886] TCP cubic registered
    [    1.263952] NET: Registered protocol family 10
    [    1.264555] NET: Registered protocol family 17
    [    1.264564] Registering the dns_resolver key type
    [    1.264668] registered taskstats version 1
    [    1.273569] blkfront: xvda2: flush diskcache: enabled
    [    1.274605] blkfront: xvda1: flush diskcache: enabled
    [    1.368068]   Magic number: 1:252:3141
    [    1.368107] /build/linux-JvAKgs/linux-3.2.0/drivers/rtc/hctosys.c: unable to open rtc device (rtc0)
    [    1.368118] BIOS EDD facility v0.16 2004-Jun-25, 0 devices found
    [    1.368121] EDD information not available.
    [    1.368534] Freeing unused kernel memory: 932k freed
    [    1.368687] Write protecting the kernel read-only data: 12288k
    [    1.373188] Freeing unused kernel memory: 1556k freed
    [    1.373812] Freeing unused kernel memory: 1172k freed
    Loading, please wait...
    Begin: Loading essential drivers ... done.
    [    1.402645] udevd[82]: starting version 175
    Begin: Running /scripts/init-premount ... done.
    Begin: Mounting root file system ... Begin: Running /scripts/local-top ... done.
    Begin: Running /scripts/local-premount ... done.
    [    1.506696] JBD2: Unrecognised features on journal
    [    1.506706] EXT4-fs (xvda2): error loading journal
    mount: mounting /dev/xvda2 on /root failed: Invalid argument
    Begin: Running /scripts/local-bottom ... done.
    done.
    Begin: Running /scripts/init-bottom ... mount: mounting /dev on /root/dev failed: No such file or directory
    done.
    mount: mounting /sys on /root/sys failed: No such file or directory
    mount: mounting /proc on /root/proc failed: No such file or directory
    Target filesystem doesn't have requested /sbin/init.
    No init found. Try passing init= bootarg.
    
    
    BusyBox v1.18.5 (Ubuntu 1:1.18.5-1ubuntu4) built-in shell (ash)
    Enter 'help' for a list of built-in commands.
    
    (initramfs)


    Посмотрим, что это за образ:

    (initramfs) uname -a
    Linux (none) 3.2.0-126-virtual #169-Ubuntu SMP Fri Mar 31 14:47:56 UTC 2017 x86_64 GNU/Linux
    (initramfs)

    Запуск маленькой ОС (Mini-OS)


    Из предыдущей части стало ясно, что для работы необходимо создать домен (инстанс). Создается он с помощью утилиты xl на основе файла конфигурации.

    Но если мы посмотрим на файл конфигурации
    #
    # Configuration file for the Xen instance test, created
    # by xen-tools 4.7 on Fri Feb 16 18:09:35 2018.
    #
    
    #
    #  Kernel + memory size
    #
    
    
    bootloader = '/usr/lib/xen-4.8/bin/pygrub'
    
    vcpus       = '1'
    memory      = '256'
    
    
    #
    #  Disk device(s).
    #
    root        = '/dev/xvda2 ro'
    disk        = [
                      'file:/srv/xen/test/domains/test/disk.img,xvda2,w',
                      'file:/srv/xen/test/domains/test/swap.img,xvda1,w',
                  ]
    
    
    #
    #  Physical volumes
    #
    
    
    #
    #  Hostname
    #
    name        = 'test'
    
    #
    #  Networking
    #
    dhcp        = 'dhcp'
    vif         = [ 'mac=00:16:3E:8E:3C:E0' ]
    
    #
    #  Behaviour
    #
    on_poweroff = 'destroy'
    on_reboot   = 'restart'
    on_crash    = 'restart'


    То увидим, что там необходимо наличие образов диска, присутствует сеть и так далее. Скорее всего, это слишком сложно для наших целей. Но Xen поддерживает и другие ОС. Базовой является так называемая Mini-OS . Причем она предназначена для тех, кто хочет портировать свои операционные системы на XEN, то есть это как раз наш вариант.
    Код на git-е лежит отдельно от XEN.

    Давайте сразу проделаем инструкции из README

    make
    sudo xl create -c domain_config

    Получим вывод
    Parsing config from domain_config
    Xen Minimal OS (pv)!
      start_info: 0x7d000(VA)
        nr_pages: 0x2000
      shared_inf: 0xdee73000(MA)
         pt_base: 0x80000(VA)
    nr_pt_frames: 0x5
        mfn_list: 0x6d000(VA)
       mod_start: 0x0(VA)
         mod_len: 0
           flags: 0x0
        cmd_line:
           stack: 0x2c6a0-0x4c6a0
    MM: Init
          _text: 0(VA)
         _etext: 0x18484(VA)
       _erodata: 0x1f000(VA)
         _edata: 0x1f252(VA)
    stack start: 0x2c6a0(VA)
           _end: 0x6cfd8(VA)
      start_pfn: 85
        max_pfn: 2000
    Mapping memory range 0x85000 - 0x2000000
    setting 0-0x1f000 readonly
    skipped 1000
    MM: Initialise page allocator for 93000(93000)-2000000(2000000)
        Adding memory range 94000-2000000
    MM: done
    Demand map pfns at 100000000000-108000000000.
    Initialising timer interface
    Initialising console ... done.
    gnttab_table mapped at 0x100000000000.
    Initialising scheduler
    Thread "Idle": pointer: 0x0x96078, stack: 0x0xa0000
    Thread "xenstore": pointer: 0x0x960d8, stack: 0x0xb0000
    xenbus initialised on irq 1
    Thread "shutdown": pointer: 0x0x96138, stack: 0x0xc0000
    kernel.c: dummy main: par=0


    Выход из консоли, как обычно, ctrl + ]

    Проверим что у нас запустилась какая-то машина

    sudo xl list
    Name                                        ID   Mem VCPUs    State    Time(s)
    Domain-0                                     0 15455     8     r-----    5117.7
    Mini-OS                                      2    32     1     -b----       0.0

    Для возврата в консоль

    sudo xl console Mini-OS

    Для удаление домена

    sudo xl  destroy Mini-OS

    Ну и посмотрим на содержимое domain_config в корне проекта
    #  -*- mode: python; -*-
    #============================================================================
    # Python configuration setup for 'xm create'.
    # This script sets the parameters used when a domain is created using 'xm create'.
    # You use a separate script for each domain you want to create, or
    # you can set the parameters for the domain on the xm command line.
    #============================================================================
    
    #----------------------------------------------------------------------------
    # Kernel image file.
    kernel = "mini-os.gz"
    
    # Initial memory allocation (in megabytes) for the new domain.
    memory = 32
    
    # A name for your domain. All domains must have different names.
    name = "Mini-OS"
    
    on_crash = 'destroy'


    Видно, что можно указывать не диски, а напрямую образ с помощью параметра kernel, а также не указывать настройки сети и другие параметры, которые нам не нужны на первом этапе.

    Портирование Embox на Xen


    Создание конфигурационного файла


    Для начала для запуска домена с Embox сделаем конфигурационный файл по образу и подобию Mini-OS.

    name = "embox"
    memory = 256
    kernel = "/tmp/xen_embox"

    Добавление архитектуры в Embox


    В Embox есть диплом Антона Козлова (antonkozlov) о переносе его на новую платформу “Портирование операционной системы с модульным HAL в пользовательский режим”. Точнее, диплом был не о переносе Embox, а об организации операционных систем вообще, и Embox использовался в качестве подопытного кролика. Конкретно про Xen есть курсовая Андрея Голикова “Портирование операционной системы Embox на платформу Xen”. Чтобы не пересказывать диплом и курсовую, ограничусь несколькими важными и специфическими для Xen частями.

    Всю необходимую информацию, как вы наверное догадались, можно получить из Mini-OS.

    Одно из первых, с чего начинается портирование, это карта памяти (нет, не SD-карточка, о которой вы, возможно, подумали, а о разных регионах памяти — memory map), традиционно описываемая в lds-скрипте. Линкеру нужно указать, где же должен лежать код программы, данные, точка входа в программу и так далее. В нашем случае к этому прибавляется ещё и то, что перед загрузкой образа должны считываться некоторые особенности из секций “.note.Xen”, точнее заголовок в ELF PT_NOTE формате. Карта памяти вместе с особенностями описана тут.

    В линкер-скрипт Embox для Xen добавлено следующее

    PHDRS {
        xen      PT_NOTE;
    }
    
    SECTIONS {
        .note : {
            *(.note)
        } :xen
    }

    Ну и ещё добавился ассемблерный файл, содержащий наполнение для этого заголовка

    #include <xen/elfnote.h>
    
    .section ".note", "a"
    
    #define ELFNOTE(type, desc) \
        .p2align 2; \
        .long 1f - 0f; \
        .long 3f - 2f; \
        .long type; \
    0:  .asciz "Xen"; \
    1:  .p2align 2; \
    2:   desc; \
    3:  .p2align 2;
    
    ELFNOTE(XEN_ELFNOTE_XEN_VERSION, .asciz "xen-3.0")
    ELFNOTE(XEN_ELFNOTE_LOADER, .asciz "generic")
    ELFNOTE(XEN_ELFNOTE_PAE_MODE, .asciz "yes")
    
    ELFNOTE(XEN_ELFNOTE_VIRT_BASE, .long 0x100000)
    ELFNOTE(XEN_ELFNOTE_PADDR_OFFSET, .long 0x100000)
    ELFNOTE(XEN_ELFNOTE_HYPERCALL_PAGE, .long 0x100000)
    

    Кроме этого, есть ещё пара структур, необходимых для взаимодействия между образом и Xen struct start_info и struct shared_info.
    struct start_info нужна, если мы хотим получать информацию о машине при старте образа, то есть без неё, в принципе, можно обойтись.

    *
     * `incontents 200 startofday_shared Start-of-day shared data structure
     * Xen/kernel shared data -- pointer provided in start_info.
     *
     * This structure is defined to be both smaller than a page, and the
     * only data on the shared page, but may vary in actual size even within
     * compatible Xen versions; guests should not rely on the size
     * of this structure remaining constant.
     */
    struct shared_info {
        struct vcpu_info vcpu_info[XEN_LEGACY_MAX_VCPUS];
    
        /*
         * A domain can create "event channels" on which it can send and receive
         * asynchronous event notifications. There are three classes of event that
         * are delivered by this mechanism:
         *  1. Bi-directional inter- and intra-domain connections. Domains must
         *     arrange out-of-band to set up a connection (usually by allocating
         *     an unbound 'listener' port and avertising that via a storage service
         *     such as xenstore).
         *  2. Physical interrupts. A domain with suitable hardware-access
         *     privileges can bind an event-channel port to a physical interrupt
         *     source.
         *  3. Virtual interrupts ('events'). A domain can bind an event-channel
         *     port to a virtual interrupt source, such as the virtual-timer
         *     device or the emergency console.
         *
         * Event channels are addressed by a "port index". Each channel is
         * associated with two bits of information:
         *  1. PENDING -- notifies the domain that there is a pending notification
         *     to be processed. This bit is cleared by the guest.
         *  2. MASK -- if this bit is clear then a 0->1 transition of PENDING
         *     will cause an asynchronous upcall to be scheduled. This bit is only
         *     updated by the guest. It is read-only within Xen. If a channel
         *     becomes pending while the channel is masked then the 'edge' is lost
         *     (i.e., when the channel is unmasked, the guest must manually handle
         *     pending notifications as no upcall will be scheduled by Xen).
         *
         * To expedite scanning of pending notifications, any 0->1 pending
         * transition on an unmasked channel causes a corresponding bit in a
         * per-vcpu selector word to be set. Each bit in the selector covers a
         * 'C long' in the PENDING bitfield array.
         */
        xen_ulong_t evtchn_pending[sizeof(xen_ulong_t) * 8];
        xen_ulong_t evtchn_mask[sizeof(xen_ulong_t) * 8];
    
        /*
         * Wallclock time: updated only by control software. Guests should base
         * their gettimeofday() syscall on this wallclock-base value.
         */
        uint32_t wc_version;      /* Version counter: see vcpu_time_info_t. */
        uint32_t wc_sec;          /* Secs  00:00:00 UTC, Jan 1, 1970.  */
        uint32_t wc_nsec;         /* Nsecs 00:00:00 UTC, Jan 1, 1970.  */
    
        struct arch_shared_info arch;
    
    };
    

    struct shared_info, как видно, содержит информацию о взаимодействии, то есть через нее мы будем получать события: прерывания, пришли данные и т. д.
    Располагается эта структура со смещением 4кб

    И наш линкер-скрипт выглядит так

    SECTIONS {
        .text : {
            . = ALIGN(0x1000);
            hypercall_page = .;
            . += 0x1000;
            xen_shared_info = .;
            . += 0x1000;
            _traps_text_start = .;
            *(.traps.*)
            _traps_text_end = .;
            *(.text)
            *(.text.*)
        }
    }

    Собственно, на этом хотелось бы закончить, поскольку разбор всего кода явно выходит за рамки одной статьи. Скажу лишь, что на сегодняшний момент студентами реализован запуск машины с отладочным выводом.
    Для того чтобы в этом убедиться, достаточно сделать в Embox следующее;

    make confload-xen/debug
    make
    ./scripts/xen/run_xen

    Создастся домен, и пойдет вывод самого Embox. Выход из консоли, как всегда, ctrl + ] и потом не забудьте удалить машину: sudo destroy embox.

    Естественно, сначала нужно установить окружение. Но для простоты, уже мы, а не студенты, добавили возможность поставить всё через vagrand. Это описано на wiki. Вам потребуется только VirtualBox и vagrand. Ставить нужно не под Xen-ом.

    В конце хотелось бы вновь затронуть тему обучения в IT. Как я уже писал в статье: “Как поймать программиста на списывании и стоит ли этим заниматься“, у нас в проекте мы пропагандируем погружение студентов в реальный проект. В этом случае студенты получают куда больше опыта, чем при выполнении учебных задач, пусть и довольно масштабных. И главное они не только обучаются какому-то конкретному языку, а учатся создавать продукты, решать задачи самостоятельно и попросту уметь мыслить. В качестве примера я сошлюсь на автора идеи про идеальную облачную платформу, Антона Козлова (antonkozlov), ведь именно на его диплом я ссылался ранее. По итогам обучения он смог не только писать код, но и предлагать собственные идеи, реализовывать их, разбивать задачу на составляющие и обучать студентов. Подобных примеров у нас много.

    P.S.: Видео с той конференции доступно тут.
    Embox
    100,44
    Открытая и свободная ОС для встроенных систем
    Поделиться публикацией

    Похожие публикации

    Комментарии 0

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое