Совместный запуск Linux и baremetal OS

в 5:02, , рубрики: baremetal os, linux, Realtime, x86, Блог компании Intel, Программинг микроконтроллеров, системное программирование, метки: , , ,

Недавно я выложил под BSD лицензией небольшой проект на 8 килострочек C кода. Официально это коллекция бенчмарков для моих клиентов — вендоров промавтоматики. Код очень специфический, и, на первый взгляд, малоприменим за пределами узкой области PLC и motion control. Но есть небольшая изюминка, на которой я не очень акцентировал внимание в статье на IDZ. В поставку бенчмарков включена baremetal среда для их исполнения. В этом посте я опишу, что это такое, и как ее можно использовать.

Что такое baremetal OS? Это небольшой код, инициализирующий ядро/ядра, и передающий управление пользовательской программе, которую по какой-то причине требуется запустить без нормальной ОС и даже без ОСРВ. Естественно, в код этой программы приходится статически линковать библиотеки и драйвера для всех необходимых ей устройств… Этой весной на Хабре уже был цикл статей, посвященный созданию baremetal OS. Поэтому я не буду повторяться и описывать процедуры загрузки. Однако для полного понимания этого поста желательно перечитать две ключевых статьи той серии. В этой описано, как собрать, загрузить и запустить произвольную программу вместо ОС сразу из GRUB. А здесь — как запустить свой код сразу на нескольких ядрах. Еще могу порекомендовать англоязычные первоисточники — osdev и самые знаменитые примеры — tutorial'ы от Bran и от James Molloy.

Во второй статье подробно описано, как запустить код инициализации нового ядра. Для этого надо послать на него Init IPI(Inter-Processor-Interrupt) и потом еще два Startup IPI. Отправление IPI инициируется простой записью нескольких байт по определенному физическому адресу. А что будет, если написать небольшой Linux user mode bootloader — программу, которая копирует бинарник по определенному адресу, и посылает другому ядру Init IPI, 2xSIPI, чтобы он загрузился в этот бинарник?

Думаете, будет kernel panic? Ведь на этом ядре Linux уже выполняет процессы пользователя, idle процесс или прерывание. На самом деле сразу все не упадет, но система начнет работать нестабильно и вскоре зависнет. А что будет, если загрузить Linux только с одним ядром, или заоффлайнить остальные ядра на работающей системе? (Например, вот так:
$ sudo echo 0 > /sys/devices/system/cpu/cpu1/online
И повторить для каждого ядра, кроме 0.

В этом случае конкуренции с планировщиком задач можно не опасаться. Но всю доступную физическую память Linux mm при загрузке уже нарезал на bucket'ы и справедливо считает своей. К счастью, и здесь есть воркэраунд. Даже два — можно написать модуль ядра, который вежливо попросит немного непрерывной физической памяти, поменять linker скрипт, чтобы положить .text и .data области бинарника в эту память. А можно сделать еще проще — попросить Linux при загрузке зарезервировать немного физической памяти по определенному адресу. Например, при загрузке ядра с опцией memmap=0x80M$0x18000000 ОС выделит 128 мегабайт непрерывной физической памяти, начиная с адреса 0x18000000. К сожалению, ядра Linux начиная с 3.1 иногда падают в самом начале загрузки с этим параметром. Сейчас как раз пытаюсь найти причину.

Voilà, теперь мы можем держать Linux на нескольких ядрах, и произвольные baremetal программы на всех остальных. Но как это может пригодиться? Пусть у нас уже есть задача запустить какой-то код без ОС. Например, мне нужны были бенчмарки, работающие на разных x86 платформах и измеряющие разброс между средним временем выполнения кода, и наихудшим временем. x86 — вообще отнюдь не образец детерминированности, а если добавить еще и операционную систему… Даже если это компактная и шустрая ОСРВ, все равно не сразу очевидно, откуда берется latency и jitter. К тому же у каждого моего клиента своя ОСРВ, некоторые ненамного сложнее baremetal. Или если есть код с микроконтроллера, чувствительный к задержкам, который и так работает без ОС, и надо его просто портануть-перекинуть на одно из ядер x86 процессора. Если просто включить его kernel thread'ом на ядре, а все остальное на нем запретить, его все равно будут прерывать. С RT-Linux патчем все получается лучше, но он добавляет latency и иногда не совместим с другими полезными патчами.

А чем Linux и baremetal вместе лучше, чем чистый baremetal образ, загружаемый бутлоадером (GRUB)? А с Linux удобнее! Сравните перезагрузку всей железки или просто пару shell команд в командной строке Linux хоста. Аналогично с отладкой — с Linux'а можно прекрасно читать-писать память baremetal OS, диагностировать железки в процессе работы. (Конечно, если осторожно!) Есть и некоторые минусы у этого подхода. Для запуска baremetal OS с Linux надо знать ACPI ID ядра, которому посылается IPI. Если вы думаете, что у первого ядра номер 0, у второго — 1, и т.д., то вы — оптимист. Я видел очень разные варианты :). Придется либо делать энумерацию, это описано в одном из постов, на которые я сослался, либо подбирать варианты. (И не забываем [выключать] Hyperthreading!). Другая возможная засада — все же недетерминизм. Если отключить в Linux все ядра, кроме первого, и на нем ничего серьезного не запускать, то измерить влияние Linux на производительность ядер, исполняющих baremetal код невозможно. Но 100% гарантии все же нет — при желании или случайно можно через общие ресурсы (кэш, контроллер памяти) сильно влиять на производительность baremetal кода. Например, попробуйте попереключать VGA console (Ctrl-Alt-F1, Ctrl-Alt-F2), измеряя производительность baremetal OS.

Если кому-то интересно, как оно все работает, или есть идеи, как это можно практически использовать (лицензия BSD позволяет) — скачивайте исходники с IDZ или github (там свежее). Сама baremetal OS лежит в tools/src/baremetal. В первом каталоге — phymem лежит тул для чтения и записи физической памяти. А во втором — smallos — собственно рантайм. Там все заточено на прогон бенчмарков, так что придется применить напильник, чтобы их отрезать — просто выкинуть из smallos/Makefile ссылки на bench/*.o, и заменить в smallos/apps/mwaitTester.c код вызова бенчмарков своим кодом. Linux usermode бутлоадер лежит в smallos/linux_boot. Удачи!

Автор: izard

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js