Кросс-компиляция x86 -> arm. Установка Gentoo на RealView Emulation board.

Представляю отчет об установке Gentoo на отладочную плату RealView Emulation Board. Были получены следующие результаты: Gentoo собрана методом кросс-компиляции для архитектуры arm и загружена на плату RealView. Настроен загрузчик u-boot: ядро скачивается по tftp, корневая файловая система монтируется по NFS.

  1. Оборудование
    • Плата RealView Emulation Board (arm, Gentoo)
    • Сервер разработки, онже хост-машина (x86, Slackware, Gentoo из chroot)
  2. Плата подключается к серверу с помощью СОМ порта. Также обе машины подключены к сети по ethernet. Для доступа к COM могут быть использованы программы типа minicom или kermit. Я использровал minicom, потому что только её получилось приспособить для посылки данных на удаленный терминал.

    Самый простой и удобный способ загрузить linux на плату - воспользоваться схемой "tftp+nfs". Это означает, что на сервере должны быть подняты демоны tftp (для загрузки ядра) и NFS (для экспорта корневой файловой системы). Об этом - см. man :) Ну все, теперь о главном:

  3. Установка Gentoo

    Существует два основных способа установки Gentoo:

    1. Установить систему, используя stage3 архивы, после чего загрузиться с "настоящего" процессора. Поскольку у меня нет никакой готовой системы, пригодной для запуска arm-бинарников, то понадобилась бы виртуальная машина. Qemu вполне подойдет. Этот способ не должен вызвать затруднений, но я не буду искать легуих путей :) Ну да, кроме шуток, скорость компиляции будет довольно медленной, а кроме того, вместе с ситемой придется таскать компилятор.
    2. Скомпилировать систему кросс-компилятором. Если кто не знает - это когда компилятор выполняется на на одной платформе (x86), а код генерирует для другой (arm). Все необходимые средства для этого предусмотрены в portage.

    Итак, идем вторым путем. Я выбрал для себя два основных ресурса, описывающих процесс. Это Gentoo Embedded Handbook (к сожалению, на момент написания этой статьи, книга была несколько устаревшей), и эта инструкция от разработчкиков проекта по портированию Gentoo на смартфон OpenMoko.

    Рекомендую внимательно ознакомиться с Embedded hanbdook`ом, после чего смело следовать инструкциям от OpenMoko. Чтобы не повторяться, привожу только свои комментарии в форме "путевых записок":

    • Выбор директории $SYSROOT целевой системы.
      Решил следовать рекомендациям, и выбрал /usr/arm-module2-linux-gnueabi как корневое дерево для целевой системы.
    • Обертки над emerge.
      В Handbook'е предлагается самостоятельно сделать обертку над emerge (и назвать её xmerge). В данный момент, эта работа уже проделана - в более свежей инструкции от OpenMoko советуют использовать

      emerge crossdev-wrappers
      emerge-wrapper --init
      

      В результате последней команды создадутся символические ссылки вида /usr/bin/emerge-* (например /usr/bin/emerge-arm-module2-linux-gnueabi), которые делают то же, что и обычный emerge, но только для целевых систем с корнем в /usr/* (в моем случе /usr/arm-module2-linux-gnueabi).

      Кроме того, устанавливается скрипт /usr/arm-module2-linux-gnueabi/etc/portage/bashrc, затыкающий дырки в libtool при кросс-компиляции. Это довольно аккуратный хак, который позволит компилировать libtoolize`нутые программы, которых на самом деле, довольно много. Этот хак можно отключить (читать тут), но тогда проблем сильно прибавится. К сожалению, хак затруднит нативную сборку пакетов на целевой системе.

    • Выбор профиля.
      Тут возмрожны варианты, я взял профиль от OpenMoko. Также можно выбрать профиль embedded из стандартной поставки, но с содержимым репозитория OpenMoko ознакомиться все равно очень полезно. Например, из-за файлика portage-flags-host/arm-linux-gnueabi (см. дальше).
    • Отключение тестов configure, требующих выполнения кода на целевой платформе.
      Многоие пакеты хотят выполнить тестовые программы на целевом процессоре на стадии configure. Поскольку в условиях кросс-комиляции это невозможно, тесты нужно пропускать. Для этого необходимо заранее спрогнозировать их результаты и определять специальные переменные окружения, которые имеют имена наподобие "ac_cv_func_malloc_0_nonnull".

      Думаю, что самым легким решением проблемы будет использовать файл portage-flags-host/arm-linux-gnueabi из репозитория оверлея OpenMoko.
      Куда его девать? Либо определить все переменные из него перед запуском emerge-* как-нибудь так:

      `cat <путь_к_portage-flags-host/arm-linux-gnueabi> | grep -v '^#' | sed 's/\([a-zA-Z]\+\)/export \1/'`
      

      Либо просто воспользоваться иструкциями OpenMoko, кинув ссылку на файл с переменными в волшебное место:

      cd /usr/share/crossdev/include/site
      ln -s <путь_к_portage-flags-host/arm-linux-gnueabi> arm-linux-gnueabi
      

      Теперь основные пакеты должны собраться без проблем. Но если при очередном emerge`е появилось сообщение об ошибке, говорящее что-то про недоступность тестов, значит пришла пора добавить определение новой перемнной. Алгоритм поиска её имени следующий: Окрываем подробный лог сборки (config.log) и нахдим в нем текст ошибки. В отличии от сокращенного варианта emerge, в этом сообщении будет указан номер строки ./configure. Чтож, открываем файл configure на этой строке и смотрим чуть выше. Обычно там бывает проверка некой переменной. Если её значение не пусто, то тест не выполняентся. Это как раз то, что нам надо, дальше дело техники: в гугле узнаем, за что отвечает переменная, выбираем ей занчение, затем пополняем файл-список и повторяем emerge.

    • Проблема с запуском bash на целевой системе.
      Забегая вперед, скажу, что при первом запуске системы, bash отказывался стартовать, поскольку не мог найти библиотеку, обычно идущую в составе gcc. В моем же случае, gcc был установлен только на хост машине, и потому оказывался недоступным при загрузке с платы. Я решил проблему ручным копированием /usr/lib/gcc* в /usr/arm-module2-linux-gnueabi/usr/lib/gcc и созданием нужных симлинков. Идея не очень хороша. Думаю, что обойти проблему можно было бы, отказавшись от bash в пользу busybox. Это одна из тех вещей, которые обычно определяют в профиле, а пройиль OpenMoko, который я выбрал, решил использовать классический набор утилит.
    • И, наконец, Проблема с libtool.
      Проблема заключается в том, что при кросс-компиляции, некоторые пакеты пытаются линковаться с библиотеками, установленными в базовой системе (в /usr/lib), вместо того, чтобы слинковаться со "своими" библиотеками (в моем случае - из /usr/arm-module2-linux-gnueabi/usr/lib). Виноват libtool, разработчики которого забыли о том, что путь установки библиотеки не всегда совпадает с путем, по которому к ней будут обращаться. А теперь плохая новость - часто программы идут с _собственной_ версией libtool, и решить проблему "одним ударом" не получится. Вот пример сообщения об ошибке, которое появилось при попытке собрать gstreamer "по-честному":

      /usr/lib/libgobject-2.0.so: could not read symbols: File in wrong format
      

      Авторы crossdev-wrappers предлагают следующий вариант решения проблемы: с помощью скрипта /usr/arm-module2-linux-gnueabi/etc/portage/bashrc, который подкладывается их wrappers`ами, они применяют патч к каждому пакету, который собирается для целевой системы (в данном случае - для всех пакетов с корнем в /usr/arm-module2-linux-gnueabi). Патч находит все *la файлы пакета - в них libtool ведет своеобразную базу данных о библиотеках - и прибавляет куда надо это самое /usr/arm-module2-linux-gnueabi. Достоинство решения - универсальность. Ведь база данных - единственное общее свойство всех libtool из разных пакетов. Недостаток - теперь воспользоваться libtoolом "изнутри" системы сложнее - ведь там все библиотеки будут располагаться в /usr/lib. Думаю, что это не очень фатально, поскольку в случае необходимости можно будет придумать обходной маневр с использованием симлинкa /usr/arm-module2-linux-gnueabi -> /.

      Вобщем, я сделал для себя два вывода:
      (1) libtool - зло, (2) нужно ждать ебилдов

      Напоследок, вот некторые ссылки по теме:

      http://www.nabble.com/Cross-compile-and-libtool-td13912879.html
      http://bugs.gentoo.org/show_bug.cgi?id=262298
      http://bugs.gentoo.org/show_bug.cgi?id=272089

  4. Когда все настроено в соответствии с руководствами, можно запускать emerge-* system. В моем случае -

    emerge-arm-module2-linux-gnueabi system
    

    Процесс, как известно, не быстрый, поэтому можно попытаться расслабиться. Этому будут мешать разнообразные баги, которые обходятся отключением различных use-флагов. Например, у меня не собралась программа file с USE="python", а также ncurses с USE="unicode". Жаль, очень жаль. Меняем флаги и перезапускаем emerge.

  5. Сборка ядра

    Сборку ядра можно пропустить, взяв готовый бинарник с сайта поддержки RealView. Если без сборки ядра не обойтись, то тамже лежат типовые конфиги (которые /usr/src/linux/.config). Они послужат хорошей отправной точкой при разработке. В конце концов, отладочная плата представляет интерес в первую очередь для разработчиков новых устройств, а пересборка ядра - их любимое занятие:)

    Стоит отметить, что для u-boot ядро должно быть преобразрвано в формат uImage. Для этого используется программа, скачиваемая все с тогоже сайта поддержки RealView.

  6. Настройка загрузчика отладочной платы

    Плата RealView Emulation board грузится в три этапа. Вначале управление получает системный загрузчик "BootMonitor" (прошивается производителем, если слетит - то плате хана). Он, являясь маленькой однозадачной операционной системой, запускает u-boot (основной загрузчик) как внешнее приложение. Наконец, u-boot скачивает по tftp ядро линукса и передает управление ему.
    u-boot может быть скомпилирован в различных конфигурациях, от которых зависит набор встроенных команд. Я воспользовался готовым u-boot, взятым с официального сайта Realview.

    1. Настройка системного загрузчика "BootMonitor".
      TODO
    2. Прошивка загрузчика u-boot.
      TODO
    3. Настройка переменных окружения u-boot.
      U-boot, также как и BootMonitor, является маленькой операционной системой, поскольку может запускать различные задачи по команде пользователя. Команды подаются с помощью командной строки, их описания есть документации u-boot. Вот самые полезные из них:

      • setenv <переменная> <значение> - Установить переменную окружения
      • printenv - Распечатать все переменные окружения
      • tftp - Загрузить ядро с сервера по протоколу tftp
      • bootm - Передать управления загруженному ядру Linux
    4. Команды tftp и bootm не имеют аргументов, но используют значения одних переменных окружения и присваивают значения другим. Словом, дело тонкое, нужно быть внимательным. Вот мой файл команд, настраивающий все u-boot для загрузки линукса:

      setenv bootdelay 2
      setenv loadaddr 0x200000
      
      # Настройки сетевого интерфейса u-boot
      # Имена переменных должны быть именно такими (см. документацию 
      # к команде tftf)
      setenv netmask 255.255.255.0
      setenv gatewayip 192.168.0.1
      setenv ipaddr 192.168.0.10
      # Адрес tftp-сервера (хост-машины), с которого будет взято ядро
      setenv serverip 192.168.0.2
      setenv ethaddr 00:02:F7:00:2F:DE
      
      # Путь к uImage-файлу ядра, на tftp сервере с адресом serverip
      setenv bootfile /home/smironov/filesystem_bin_armv6vfp_min/boot/uImage-2.6.28-arm1crypto_test
      
      # Переменные, формирующие командную строку ядра (для удобства)
      setenv rootpath /home/smironov/arm-module2-linux-gnueabi
      setenv ba_common mem=256M console=ttyAMA0 debug
      setenv ba_ip ip=$(ipaddr):$(serverip):$(gatewayip):$(netmask):armboard2::off
      setenv ba_nfs root=/dev/nfs nfsroot=$(serverip):$(rootpath)
      
      # Вся командная строка целиком
      setenv bootargs $(ba_common) $(ba_ip) $(ba_nfs)
      

      Осталось взять этот файл, и выполнить его на u-boot, предварительно удалив комментарии, которых u-boot не умеет. Проблема в том, что дефолтный убут не умеет исполнять файлы, не имеет прямого доступа к файлам на хост-машине и вообще вещь довольно ограниченная. Можно, конечно, просто скопировать командный файл на консоль (методом Ctrl+C, Ctrl+V). Но это неудобно - хочется автоматизировать процесс. Первое, что приходит в голову - выводить файл на СОМ-порт (я использовал USB2COM переходник, поэтому мой порт называется /dev/ttyUSB0) как-нибудь так:

      cat command-file | grep -v '^#' > /dev/ttyUSB0
      

      Однако, мне не удалось добиться нормального вывода, вместо текста до платы доходил "мусор" (предполагаю, что проблема крылась в некорректно заданных режимах порта, но проверка скоростей и танцы с stty ни к чему не привели). В тоже время, minicom и его примитивный скриптовый движок runscript работали как часы. Поэтому, я сделал скрипт под названием miniboot для передачи текстового файла на удаленный терминал:

      #!/bin/bash
      # Sends file via minicom
      
      function die()
      {
      	echo "Error: $@" 2>&1
      	exit -1
      }
      
      function usage()
      {
      	echo 'Usage:'
      	echo 'miniboot [script] BOARD'
      	echo 'script : text files (e.g. with u-boot commands). ' \
      	'Without this argument miniboot will use ~/.minibootrc file'
      	echo 'BOARD : minicom session name ("blah" for /etc/minirc.blah)'
      	exit -1
      }
      
      script=$1
      if [ -f "$script" ] ; then
      	shift
      elif [ "$script" == "--help" ] ; then
      	usage
      else
      	script=~/.minibootrc
      fi
      test -f "$script" || die "Can't open file $script. Try $0 --help"
      
      args=$@
      test -n "$args" || die "minicom arguments are empty. Try $0 --help"
      
      target=~/.miniscript
      echo -n > $target
      
      linesize=40
      ((linesize2=linesize+1))
      
      IFS=$'\n'
      
      echo "print Start loading..." >> $target
      # Domn runscript doesn't allow long command lines, so we'll slice them.
      for l in `cat $script` ; do 
      	if echo "$l" | grep '^#' >/dev/null ; then continue; fi
      
      	while ((`echo $l | wc -c`>$linesize)) ; do
      		echo $l | cut -b -$linesize | sed 's/\(.*\)/send \1\\c/ ; s/\$/\\\$/g' >> $target
      		l=`echo $l | cut -b $linesize2-`
      	done
      	echo $l | sed 's/\(.*\)/send \1/ ; s/\$/\\\$/g' >> $target
      done
      echo "send " >> $target
      
      # Running minicom script engine
      exec minicom -S $target $args
      

      Теперь, создав файл с командами u-boot можно "отправить его на выполнение". Достаточно включить плату, убедиться, что u-boot загружен и готов к приему команд, и можно выполнять скрипт. К примеру, для отправки файла loadgentoo.uboot на устройство, коннект к которому описан в профиле minicom с именем /etc/minirc.newboard, следует набрать:

      miniboot loadgentoo.uboot newboard
      

      В результате откроется minicom, который сначала выполнит скрипт, а потом перейдет в обычный режим работы. Теперь нужно убедиться, что все переменные корректно проинициаллизировались (набрав "printenv" после пригоашения ввода u-boot), и можно приступать к запуску ядра. Набираем tftp (u-boot обращается к нашему tftp-серверу и берет ядро) и bootm (u-boot передает управление ядру, которое подхватывает командную строку и лезет на NFS за корневой файловой системой). все, на консоли загрузачный хаос, на мониторе (если он подключен к плате) - пингвин.

Скорость компиляции в qemu на

Скорость компиляции в qemu на athlon 64 3200+ сопоставима (может даже меньше), чем у xScale 400 Mhz. Вот только оперативки больше.

qemu

Qemu не подойдет :( Потому как xscale нефига не поддерживаться да и оперативки там можно задать максимум 128 МБ

Working on Gentoo Linux for Asus P535 and Qtopia :-)

материал хороший,

материал хороший, спасибо.
поправьте только ссылку на оверлей OpenMoko.
Если это тот же репозиторий что и подключается по layman -a openmoko, подскажите, где файл указанный в статье найти.

Настройки просмотра комментариев

Выберите нужный метод показа комментариев и нажмите "Сохранить установки".