Те кто следит за нашим проектом могли заметить, что в каталоге с архитектурами появилась папка e2k, содержащая реализацию поддержки отечественных процессоров с архитектурой Эльбрус. Серия статей о портировании Embox на отечественные платформы была бы неполной без рассказа об этой архитектуре.
Сделаю пару замечаний по содержимому статьи. Во-первых, процесс освоения нами данной архитектуры находится на начальной стадии, нам удалось запустить Embox на данной платформе, но мы еще не реализовали много необходимых частей, об этом речь пойдет в будущих публикациях. Во вторых, данная архитектура – сложная, и
для детального описания требуется гораздо больше текста, чем позволяет формат
одной статьи. Поэтому предлагаем воспринимать данную статью как вводную,
содержащую минимум технической информации о самой архитектуре.
Приступим.
Объект исследований – макет встраиваемой системы на Эльбрус
Поскольку мы занимаемся Embox (а он, если кто не в курсе, ориентирован на встроенные системы), нас интересовал прежде всего вариант, который самим МЦСТ позиционируется в том числе для встраиваемых систем. Обратившись в МЦСТ мы выяснили, что в компании заинтересованы в применении своих процессоров для встраиваемых систем. Одним из последних решений для данного сегмента является плата E4C-COM . В процессе общения с МЦСТ стало понятно, что для портированая и освоения архитектуры, можно воспользоваться любой из имеющихся машин, и нам во временное пользование дали вычислитель под названием Монокуб. Вообще, монокуб не совсем то, к чему мы привыкли в embedded systems. Обычно во встраиваемых системах используют одноплатные компьютеры, чип — система на кристалле или вовсе микроконтроллер, монокуб же является полноценным компьютером, но поскольку он прошел испытания по “климатике и механике”, то его все-таки можно считать embedded system.
Компилятор, сборка, заливка образа
После получения системного блока, естественно, встал вопрос — как заливать образ. В МЦСТ используют свой BIOS (системный загрузчик первого уровня). По умолчанию устанавливается ОС Эльбрус (т.е. Debian с модификациями). Нам же интересно запускать собственный образ. На наше счастье загрузчик МЦСТ умеет запускать образы по сети. Для этого используется протокол ATA over Ethernet.
После того, как нам помогли настроить стенд и запустить внешний образ по сети, мы приступили к разработке собственного образа. Для этого нам потребовался компилятор. Компилятор в открытом доступе не нашли, но поскольку мы подписали NDA, нам выдали бинарники под Линукс. Компилятор оказался вполне себе gcc-совместимым, и нам ничего не пришлось менять, естественно за исключением флагов компиляции, которые у нас вынесены в отдельный конфигурационный файл. Что очень предсказуемо, ведь линукс, пусть и с модификациями, собирается этим компилятором.
Пара технических вопросов
Те, кто занимался такой специфичной деятельностью, как портирование ОС на какую-либо платформу, знают, что первое, что нужно сделать — это правильно разместить программный код в памяти. То есть написать линкер-скрипт (lds) и реализовать стартовый код. С линкер-скриптом довольно быстро разобрались, но вот при реализации стартового кода, мы столкнулись с первой магией, которую так до конца и не поняли. Дело в том, что у Эльбруса есть режим x86-совместимости и по адресу 0x00FF0000 лежит код, на который просто дам ссылку, поскольку мы его позаимствовали из примера МЦСТ. Линкер скрипт содержит
.bootinfo : {
_bootinfo_start = .;
/* . = 0x1000000 - 0x10000; */
/* 0x00FF0000 */
*(.x86_boot)
. = _bootinfo_start + 0x10000;
_bootinfo_end = .;
} SECTION_REGION(bootinfo)
.text : {
_start = .;
/* 0x01000000 */
*(.e2k_entry);
Сам же стартовый код написан даже не на ассемблере, а просто на Си. Он положен в секцию, размещенную по адресу 0x01000000, что кстати вполне соответствует стартовому адресу обычных x86-машин — там по этому адресу лежит заголовок multiboot либо другой заголовок.
Для того, чтобы удостовериться, что стартовый код и адреса верны, нужно добиться какого-нибудь вывода. Если получится вывести какой-нибудь символ, то, скорее всего, не возникнет проблем с выводом строк. Используя этот вывод, уже можно будет использовать привычный printf() для отладки. К тому же, большинство платформ предоставляет возможность выводить символы, делая простую запись в определенный регистр (т.к. загрузчик уже, скорее всего, настроил UART как надо).
В нашем компьютере используется контроллер последовательного порта am85с30 (он же z85с30 мы достаточно быстро нашли, как вывести один символ, а этого достаточно для работы нашего printf-а. Тут же столкнулись со странной проблемой — часть символов, выводимых printf, будто бы дублировалась, но при этом иногда перемешивалась. Например, при попытке вывести Hello, world! Получалось что-то вроде Hhelellloo,, woworrlldd. Сейчас кажется очевидным, что дело в многоядерности, но сначала мы долго ковырялись в самом драйвере. В нашем монокубе стоит двухъядерный Эльбрус-2С+ (1891ВМ7Я) (четыре DSP ядра не в счет) и загрузчик активизирует все процессорные ядра. В итоге, чтобы не возиться с многоядерностью (SMP), все ядра кроме первого отправляем в бесконечный цикл. Для этого мы ввели переменную для номера процессора и с помощью атомарного сложения инкрементируем ее. Нулевое ядро продолжает работать, а другие ядра зацикливаются.
cpuid = __e2k_atomic32_add(1, &last_cpuid);
if (cpuid > 1) {
/* XXX currently we support only single core */
while(1);
}
/* copy of trap table */
memcpy((void*)0, &_t_entry, 0x1800);
kernel_start();
Вызов kernel_start() — это уже передача управления нашему коду.
Атомарное сложение мы тоже позаимствовали, для нас оно смахивает на магию. Но, как известно, работает — не трогай!
#define WMB_AFTER_ATOMIC ".word 0x00008001n"
".word 0x30000084n"
#define __e2k_atomic32_add(__val, __addr)
({
int __rval;
asm volatile ("n1:"
"ntldw,0 %[addr], %[rval], mas=0x7"
"ntadds %[rval], %[val], %[rval]"
"nt{"
"ntstw,2 %[addr], %[rval], mas=0x2"
"ntibranch 1b ? %%MLOCK"
"nt}"
WMB_AFTER_ATOMIC
: [rval] "=&r" (__rval), [addr] "+m" (*(__addr))
: [val] "ir" (__val)
: "memory");
__rval;
})
Еще одной магией, которую пришлось позаимствовать, является некий код, который обязателен для всех ядер. А именно
static inline void e2k_wait_all(void) {
_Pragma ("no_asm_inline")
asm volatile ("wait ttrap = %0, ma_c = %1, fl_c = %2, ld_c = %3, "
"st_c = %4, all_e = %5, all_c = %6"
: : "i" (0), "i" (1), "i" (1), "i" (0), "i" (0), "i" (1), "i" (1) : "memory");
}
В итоге, после написания стартового кода у нас не только появились сообщения, выводимые с помощью printk, но и стали загружаться модули, что вообще то не очень тривиально для не совсем стандартных компиляторов. Так что еще раз отмечу, что на этот раз совместимость с gcc очень сильно порадовала.
Следующим шагом обычно является запуск контроллера прерываний и таймера, но подумав о том, что нам придется реализовать не только поддержку этих устройств, но и архитектурный код обработчиков прерываний, мы решили, что можно начать с периферии. Монокуб имеет шину PCIe, для программистов это выглядит как обычный PCI. Нас интересовали прежде всего два устройства: контроллер дисплея и сетевой контроллер.
В монокубе используется графический контроллер из серии sm750. Это графический контроллер для встроенных применений, имеет на борту поддержку 2d-графики. Микросхема напаяна прямо на материнскую плату, насколько я понял. Исходники для драйвера под линукса можно найти тут.
После найденного драйвера казалось, что наши проблемы закончились, оставалось только реализовать контроллер для PCI. точнее операции чтения/записи конфигурационного пространства PCI, чтобы узнать параметры. Реализацию этих функций пришлось опять заимствовать. В итоге, записи чтения сводились к макросам типа
/*
* Do load with specified MAS
*/
#define _E2K_READ_MAS(addr, mas, type, size_letter, chan_letter)
({
register type res;
asm volatile ("ld" #size_letter "," #chan_letter " t0x0, [%1] %2, %0"
: "=r" (res)
: "r" ((__e2k_ptr_t) (addr)),
"i" (mas));
res;
})
#define _E2K_WRITE_MAS(addr, val, mas, type, size_letter, chan_letter)
({
asm volatile ("st" #size_letter "," #chan_letter " t0x0, [%0] %2, %1"
:
: "r" ((__e2k_ptr_t) (addr)),
"r" ((type) (val)),
"i" (mas)
: "memory");
})
Тут есть некоторое понимание происходящего. У Эльбруса несколько альтернативных адресных пространств, как, например, в SPARC архитектуре. Идентификация идет с помощью идентификатора адресного пространства. То есть одна и та же команда ld попадает по разным внутренним адресам, еще и сгенерит операции чтения разной длины (8, 16, 32, 64 бита). Если в SPARC это отдельная команда lda/sta то в Эльбрусе за счет параметров, это команда ld. Из SPARC архитектуры позаимствовали регистровые окна. Более подробный рассказ я отложу для последующих статей.
В итоге, у нас все получилось с PCI. Мы смогли получить все необходимые данные, перенесли к себе графический драйвер, но тут столкнулись со следующей проблемой. Для отрисовки картинки в видеопамять нужно было писать два раза. Все указывало на cache. Для того чтобы решить эту проблему, нужно было разобраться с MMU, а это, как говорится, с кондачка не решается, как, в принципе, и многие другие проблемы, с которыми мы столкнулись и еще не раз столкнемся во время освоения данной архитектуры.
Мы продвинулись и в других направлениях: прерывания и системные вызовы, но об этом мы также расскажем в следующих статьях данной подсерии. В завершение данной статьи я просто приведу вывод в консоль (по последовательному порту).
Выводы
Как я говорил в предисловии, я хочу сосредоточиться в первую очередь не на технических деталях, а на общих ощущениях. Так вот, ощущения противоречивые, хотя конечно больше положительные. С одной стороны, процессор существует и является очень интересным с точки зрения архитектурных особенностей. На основе этого процессора выпускаются вычислительные системы, есть ПО, достаточно высокого качества. Как я сказал, к компилятору не было претензий (до определенного момента, который опишу чуть позже), есть полноценный Линукс (ОС “Эльбрус”). Я лично видел, как в самом МЦСТ, разработчик творил прямо на десктопе с архитектурой Эльбрус.
Но с другой стороны, я не понимаю, почему с таким упорством пытаются сделать из данного процессора банальную замену интеловским x86. Ведь нигде в мире не используют процессоры на основе VLIW архитектуры в качестве универсальных персональных компьютеров. VLIW в силу своих архитектурных особенностей является крутой “числодробилкой”, на ней делают DSP, делали сервера itanium, делали графические карты. Нет экскаватором конечно можно вырыть ямку для посадки деревца, но стоит ли.
Главной же проблемой препятствующей развитию архитектуры, на мой взгляд является закрытость всей экосистемы. Да, для того чтобы получить описание системы команд, нужно просто подписать NDA, но ведь этого мало. Архитектура незнакомая и очень сложная. Да, я всегда считал, что какое-то базовое ПО должно разрабатываться прямо у производителя процессоров, ну или в тесном сотрудничестве с ним. По этому принципу у ПК на Эльбрусах есть комплект ПО с ОС “Эльбрус”. Но все же слишком наивно считать, что одна компания, пусть даже крупная, может обеспечить качественную поддержку всем составляющим: процессор, компилятор, средства разработки и отладки, системное ПО (ОС различные), прикладное ПО, …. такое не под силу даже Интелу. Мир давно идет в сторону так называемой коллаборации или совместной разработки.
Приведу пример проблемы с компилятором, на которую мы наткнулись. Драйвер последовательного порта в какой то момент перестал выводить символы, причем, на первый взгляд, ничего не изменилось. Оказалось, что мы убрали неиспользуемую отладочную функцию, которую вставили для того, чтобы через дизассемблер понять, как на ассемблере передать аргументы в функцию. То есть, если функция присутствует, все нормально, если нет, то пропадает вывод. Сначала грешили на выравнивание, но оказалось, что функцию можно перенести и в конец Си-шника, поэтому все символы из драйвера оказывались на тех же местах, что и раньше, но проблема воспроизводилась. Пока данную проблему не решили, продолжаем исследовать, чтобы показать разработчикам компилятора из МЦСТ или понять, где мы ошиблись.
Приведенную проблему, на мой взгляд, можно было избежать, если было больше сторонних пользователей. Как минимум, проблема выявилась бы раньше, или, можно было бы просто нагуглить, что мы сделали не так.
Проблему закрытости осознают и в МЦСТ, поскольку процесс открытия несекретных вещей все-таки пошел. Например, я видел на нескольких конференциях работу Альт-Линукса на ПК серии Эльбрус. Вот фотки дисплея с одной из конференций в этом году (извините что плохо видно, было темно). Мы тоже подключились с освоению. Надеемся, что мы будем полезны МЦСТ, поскольку, как выясняется, некоторые изюминки архитектуры Эльбрус не могут быть поддержаны в Линукс (или затраты очень большие), например, тегированная память.
Еще один важный момент, когда мы обсуждали проблему закрытости с разработчиками МЦСТ, они возразили, что, например, исходники ядра линукса открыты долгое время, однако только мы и разработчики компании Доломант задавали вопросы и как то их использовали.
Кроме того, по моей информации, в компании МЦСТ собираются организовать стенд
доступный удаленно. На котором можно будет собрать и запустить ПО на ПК с архитектурой Эльбрус. Если есть подобный интерес и хочется использовать стенд, то следует обратиться, например, ко мне: описать, как планируется его использовать, сколько времени нужно и так далее, ведь для совместного доступа с изменением ПО, нужно организовать расписание. Данные я передам в МЦСТ или свяжу желающих с организаторами.
Полезные ссылки:
Адрес рассылки для пользователей — user[at]mcst.ru
Краткое описание архитектуры Эльбрус
Исходники ядра Линукс
Исходники Embox
P.S. Всем желающим мы покажем, то что у нас получилось, на IT-фестивале TechTrain 1-2 сентября.
Автор: abondarev