Embedded controller что это
Многообразный мир embedded systems и место Embox в нем
Проекту Embox уже исполнилось 9 лет, но многие не понимают, что это такое и
с чем его едят зачем он нужен. Некоторые из тех, кто слышал о проекте и знают, что это операционная система, считают, что Embox — это “отечественная ОС”. Действительно, задумывался Embox как попытка сделать “свою” ОС с “блекджеком и шлюпками”, но главное — это “блэкджек и шлюпки”. То есть, во главу угла ставились некие характеристики или их сочетание, которых не хватало в других проектах.
Конечно, писать универсальную ОС даже с какими-то фишками никто не собирался. Слоган Embox — “Essential toolbox for embedded development” — подразумевает, что проект нацелен на embedded systems. Однако данное понятие очень широко, к нему относят: интернет вещей (IoT) и роботов, различные малинки (RaPi) и бортовые системы, ардуинки и АСУ-ТП, …. Список, как вы понимаете, можно продолжать очень долго, в нем есть места, где прекрасно живет Linux, а есть места, где Linux избыточен и используются различные маленькие RTOS. В данной статье я хотел бы поговорить об embedded-мире во всем его многообразии, ну и о месте Embox в нем.
Одноплатные компьютеры
Промышленные компьютеры
Начнем с одноплатных компьютеров. Многие из них выполнены в промышленном исполнении. Большинство построены на процессорах с архитектурами ARM и x86. Многие думают, что x86-процессоры не используются в данном сегменте, а все ограничивается малинками, биглбордами, бананами и так далее. Но задолго до RaPi были другие одноплатные машины, нацеленные на сегмент промышленных PC, так называемый форм фактор PC/104. Они уступали по производительности обычным десктопам, ведь предназначались для задач, в которых надежность превалирует над функциональностью. По той же причине Linux не часто использовался в качестве ОС для этих аппаратных платформ, там были различные проприетарные ОС со свойствами реального времени (VxWorks, QNX, LynxOS и так далее ). Я не написал «ОСРВ» (к коим я отношу все три приведенных ОС) из тех соображений, что довольно часто на этих аппаратных платформах располагался Windows CE и его развитие Windows Embedded. И отнести весь этот зоопарк к ОСРВ у меня язык не поворачиваются.
Потребительские одноплатники
Малинки же задали совсем другой тренд. Они, по сути, нацелены не на промышленные системы реального времени, а на потребительский сегмент, в котором ценится соотношение цена/функциональность, а по этому параметру малинки (и аналоги) существенно опережают своих конкурентов. Ведь при покупке за условные 30-50 долларов, вы получаете полноценный системник, с помощью которого вы легко средствами Linux сделаете устройство с довольно сложным функционалом. Это очень полезно для прототипирования или DIY. Плюс, конечно, малинку можно использовать как PC или маленький сервер. Поэтому часто в качестве ОС используются готовые дистрибутивы Linux, в первую очередь, конечно, Raspbian — Debian для Raspberry Pi, ну или дистрибутив со смешным для русскоязычных названием Pidora — Fedora для RaspberryPi. Для других аналогичных платформ также есть готовые дистрибутивы, предоставляемые как производителями оборудования, так и сообществами (производителями) ОС. Согласитесь, когда нужно сделать прототип, проще всего взять готовое, наставить пакетов, написать функционал на python. В результате быстро получить работающий прототип. Пример — робот, который распознает линию с помощью OpenCV.
Linux в embedded-устройствах
Но мир embedded гораздо шире, чем стандартные ARM-овые одноплатники. Более того, они составляют относительно маленькую часть устройств, и их основной вклад — это популяризация и упрощение входа в разработку устройств подобного класса. Серийные устройства создаются на базе тех же процессоров (систем на кристалле) или аналогичных, но платы проектируются под задачу (проект, устройство). Следовательно, стандартные дистрибутивы, как минимум, избыточны, ведь в них зачастую используется какой-нибудь менеджер пакетов, и можно легко доставить очень много всего интересного (но ненужного для решения конкретной задачи). Embedded-устройства же обычно поставляются с законченной функциональностью, и даже называется это firmware (прошивка). Для создания прошивок существует другой класс Linux-дистрибутивов. Подобные дистрибутивы позволяют “установить” нужные пакеты статически — собрав их в корневую файловую систему, а не динамически — с помощью менеджера пакетов из репозитория. Обычно эти дистрибутивы могут собираться не только прикладные приложения, и библиотеки, но и ядро в заданной конфигурации. А часто еще и кросс-компилятор, ведь компилировать на самом устройстве, как минимум не эффективно.
Yocto project
OpenWRT
Еще одним известным проектом по созданию прошивок на основе Linux является OpenWRT. Уверен, что те, кто развлекается с кастомизацией роутеров, про него слышали. На основе этого проекта для различных роутеров делаются прошивки, которые представляют из себя один бинарник, включающий и ядро, и корневую файловую систему. Использование firmware (а не универсальных дистрибутивов) в embedded-системах связано с идеей о том, что функциональность конечной системы известна на момент ее разработки, то есть даже если обновляется версия роутера, прошивка меняется целиком со всей функциональностью (или заменяется часть этой прошивки специальным образом). Устанавливать, например, офисные пакеты, обычно не нужно, а часто вообще запрещено, поскольку это может внести свою неопределенность. Такой подход позволяет кроме всего прочего существенно сэкономить на аппаратной части. Те же роутеры, например, имеют куда менее мощный процессор и куда меньший объем памяти, чем универсальные железки.
Системы реального времени
Возвращаясь к теме промышленных вычислителей необходимо обсудить термин — “система реального времени”. Многие считают, что системы реального времени более быстрые. Это заблуждение. Вероятно, связано оно с историческими предпосылками. Ведь сам термин возник, когда машины были медленные. И пользователь замечал, что реакция системы может отставать от его действий. Термин “реальное время” означал, что система должна была быть отзывчивой на любые воздействия, в том числе действия оператора. Но на современных вычислительных машинах пользователь (оператор) вряд ли заметит торможение. В подавляющем большинстве случаев, при нажатии на менюшку, иконку, кнопку, мы сразу увидим перерисовку экрана, если конечно все в порядке (интернет есть, процесс не завис и т.д.). А вот если случилось что-то непредвиденное, например, связь пропала, мы увидим, чем отличаются (должны отличаться) системы реального времени. Обычный смартфон мы просто перезагрузим. Но если эта система управляет электростанцией, то сами понимаете, такое не всегда возможно. Отсюда делаем вывод, что система реального времени должна предсказуемо, а не быстро, реагировать на любое событие или совокупность событий, вне зависимости от своего состояния и окружения.
Linux в системах реального времени
Естественно, были (есть и будут) попытки сделать из Linux систему реального времени. Самая известная — это RTLinux, изначально это был патч к Linux, заменяющий оригинальный “полностью честный планировщик”, точнее вставляющий собственный, который одной из задач ставил планировщик Linux. Данный планировщик работал по статическим приоритетам задач, соответственно работал гораздо предсказуемее. Но это был уже не Linux, точнее функциональность Linux была не в режиме реального времени.
ARINC-653
Еще одним подходом для обеспечения реального времени, в какой-то степени схожим с RT-патчем для Linux, является подход требуемый в стандарте ARINC653 или так называемом подходе MILS. Данный подход подразумевает, что система проектируется слоями, нижний слой подразумевает очень легкий гипервизор, на основе которого в статически заданных разделах, выполняются задачи разной степени критичности. Очень легким я назвал гипервизор поскольку подразумевается, что он обладает самой высокой критичностью и следовательно его код (алгоритмы) должен быть проверен максимально полно (в идеале, должно быть математически доказуемо отсутствие необработанных ситуаций ). Следовательно кода должно быть как можно меньше. Ну, а Linux, как вы наверное поняли, располагается в собственном разделе.
uCLinux
Попытки применить Linux во встроенных системах начались давно. Одной из первых была попытка использовать Linux в системах, где нет аппаратной поддержки виртуальной памяти (MMU). Данный проект назывался uCLinux и его вкладом в ядро Linux стал режим NOMMU или MMU-less.
Linux в системах реального времени
Подводя небольшой итог под попытками использования Linux в системах реального времени, нужно ответить на вопрос, почему же так происходит. То есть с одной стороны Linux не особо (на данный момент и в чистом виде) приспособлен для систем реального времени, а с другой — постоянно происходят попытки сделать это. Причем это происходит за счет внесения ограничений (замена планировщика, внесение гипервизора, ограничения на использование виртуальной памяти и так далее). Ответ, на мой взгляд, таится в наличии у Linux гигантской кодовой базы. Это и драйвера, это и функциональные приложения и библиотеки. Очевидно, что если хочется создать надежную систему, следует как можно больше использовать готовые части, ведь разработка новых, будь то функциональная логика или драйвер, всегда содержит вероятность внесения ошибки. А поскольку к современным системах реального времени предъявляют достаточно высокие требования по функционалу, то переиспользование готового функционала из Linux становиться все более соблазнительным. Иными словами, доработка Linux до системы реального времени уже не кажется такой уж затратной по сравнению с разработкой функциональности, пусть и на основе операционной системы реального времени, ведь надежностью должна обладать вся система, а не только ее часть в виде ядра операционной системы.
Windows в embedded-устройствах
Хочу ненадолго вернуться к Windows. На заре моей карьеры у меня случилась дискуссия с более опытным разработчиком о том, что Windows нельзя использовать в надежных системах. На что он возразил, что если протестировать уже законченную систему с необходимым функциональным программным обеспечением, и запретить любые изменения: обновления, установку ПО и т. д., то система будет достаточно надежной для многих задач, в том числе и для той, которую мы решали. Сейчас у меня нет сомнений, что мой оппонент был прав, а не я. Более того, даже древний MS-DOS очень долгое время использовался в промышленных системах. Дело в том, что многозадачность, которая кажется такой необходимой, вносит неопределенность. А если запускать ПО, которое полностью контролирует всю систему, то можно добиться куда большей детерминированности поведения. Иными словами, если в системе крутится неопределенное количество задач, то добиться определенности в работе всех функций системы, вряд ли удастся. Поэтому самым простым способом для увеличения предсказуемости системы является ограничение ее функциональности, а следовательно — отказ от универсальности во время исполнения. Что мы, собственно, и наблюдаем в упомянутых выше примерах использования Linux в системах реального времени.
Микроконтроллеры
Пример с MS-DOS в качестве базовой ОС для промышленных систем реального времени приводит нас к мысли, что если использовать только свое ПО, то можно добиться предсказуемого поведения всей системы. И это действительно так! Правда, нужно сделать оговорку, что это возможно только если функциональность системы небольшая и четко зафиксированная. Если вся функциональность системы заключается в управлении мотором с помощью GPIO и получением (передачей) ограниченного набора команд по UART-интерфейсу, то не обязательно использовать ОС, можно создать прошивку (то, что называется bare-metal). Этот подход достаточно часто используется в микроконтроллерах. Но поскольку и микроконтроллеры стали большим (32-битный ARM с несколькими кБ ОЗУ против 8 битных AVR-ок с сотней байт ОЗУ), то и запросы по функционалу выросли. Сейчас при разработок прошивок используют как минимум ПО от производителей, которое позволяет использовать какие-то готовые примеры для работы с микроконтроллером, например STM32Cube.
RTOSes
Но поскольку требования к функциональности постоянно растут, прошивки для микроконтроллеров все чаще делаются на основе так называемых RTOS. В отличие от больших операционных систем реального времени, это относительно небольшие открытые проекты, предоставляющие полный доступ ко всему коду в системе. То есть, идет совмещение концепций: с одной стороны, используется уже готовый код, а с другой стороны, у разработчика есть полный доступ ко всему в системе, можно сказать, прошивка bare-metal.
RTOS для микроконтроллеров довольно много. Поэтому обо всех рассказать не получится. Выделю только несколько, на мой взгляд, интересных проектов и кратко расскажу про их особенности.
FreeRTOS
Наверное, одной из самых популярных проектов RTOS сейчас является FreeRTOS. Некоторые говорят, что это не полноценная ОС, а только планировщик. Но если учесть приведенные выше рассуждения о bare-metal, большое количество поддерживаемых микроконтроллеров и то, что написано и интегрировано много прикладного ПО, то небольшой функционал выглядит скорее как достоинство, чем недостаток. Это и позволило стать де-факто стандартом для микроконтроллеров, как написано на сайте проекта.
Contiki
Contiki — RTOS, разработанная Адамом Данкелсом (Adam Dunkels), создателем таких известных TCP/IP стеков, как lwIP и uIP. Я бы сказал, что вокруг сетевого стека и построена вся ОС. Наличие поддержки IPv6 и небольшие требования по ресурсам делает этот проект интересным для создания различного рода беспроводных устройств.
RTEMS
RTEMS Real-Time Executive for Multiprocessor Systems. Изначально разрабатывалась для военных, акроним расшифровывался как Real-Time Executive for Missile Systems, а затем Real-Time Executive for Military Systems. Довольно старый, но еще живой открытый проект. Яркий представитель подхода libOS. Когда разрабатываемое ПО линкуется с уже собранной ОС, которая представляет из себя не только ядро, но и все доступные службы. Компилируется и поставляется в качестве библиотеки к компилятору, что довольно удобно на ранних стадиях разработки.
eCos Embedded Configurable Operating System. Так же довольно старый проект, ранее очень популярный. Основная идея — сделать очень конфигурируемую ОС, то есть включать в ядро только то, что нужно.
Другие RTOSes
Список можно продолжать довольно долго. Об еще одном проекте NuttX упомяну ниже. А тем, кому интересен более подробный список советую посмотреть википедию. Для микроконтроллеров я бы еще отметил ChibiOS/RT, MicroC/OS (µC/OS), Nut/OS, RIOT. Но конечно, все зависит от задачи.
Arduino
Думаю разговор об embedded был бы неполным без упоминания Arduino. Ведь как и RaPi они очень распространены и сильно способствовали популяризации микроконтроллеров. Сам я довольно скептически отношусь к теме arduino, поэтому пропущу критику фанатов данной технологии. А вот по поводу того, что это очень интересная технология, могу привести пример хлебопечки, описанный на хабре. Очень приятное решение. Ну или уже приведенный пример с роботом, распознающим линию по openCV, да там также используется arduino.
Микроядро
Я еще ни разу не упомянул о концепции микроядра, которая, как многие знают, делает систему надежной и предсказуемой. Это с одной стороны правда, а с другой, как всегда, не совсем. Точнее, конечно правда, но вера в то, что данная концепция (архитектура), сразу превратит вашу систему в систему жесткого реального времени — заблуждение. Это скорее такой маркетинговый слоган: «мы система реального времени потому, что построены по принципу микроядра». Но ведь принцип микроядра всего лишь позволяет упростить разработку ПО, так как большинство частей выносится в пользовательское пространство. Но что делать, если у вас сломался какой то сервер, необходимый для работы? Вы его перезагрузите, но на это требуется время, а если у вас его нет? К тому же, классическая микроядерная архитектура имеет и свои недостатки! Она, например, медленная, поскольку происходит очень много системных вызовов (передач сообщений между серверами и прикладным ПО). Иначе бы все давно перешли на чистую микроядерную архитектуру, а кто, например, видел проекты на L4, а они есть. Ну, и конечно, многие помнят спор Линуса Торвальдса с Эндрю Таненбаумом.
То есть концепция микроядра не является серебрянной пулей. Но как очень хорошая идея применяется в той или иной степени в большинстве современных ОС. А создание надежной системы в итоге все равно зависит от конечного разработчика и возможностей, которые предоставляет операционная система по ее построению.
Место Embox в мире embedded systems
Я уже довольно много рассуждал о разных аспектах embedded-мира, но так и не добрался до места Embox в нем. Ну что же, могу сказать, что в описанных выше примерах, возможно и нет необходимости применять Embox. Более того, обычно мы пользователей спрашиваем, зачем им нужен Embox? Если применение Embox не дает никаких преимуществ, мы советуем попробовать, что-нибудь из списка выше или ещё что-то (например, Android). Но есть ряд случаев, в которых применение Embox дает ощутимый выигрыш.
Система для разработки оборудования
Начну с первого применения Embox. Он тогда еще даже не был Embox-ом, а представлял из себя некий Си и ассемблерный код, который позволял очень быстро запуститься и выполнить утилитарный код. В тот момент это был проект для помощи инженерам в отладке аппаратуры, разрабатываемой на ПЛИС-ах. Он умел очень быстро запускаться, гораздо быстрее, чем аналогичный код, написанный с помощью уже упомянутого RTEMS. Это важно, поскольку он использовался еще и на этапе потактовой симуляции. Плюс его стали применять и на реальном “железе”, для этого был написан небольшой интерпретатор, который умел вызывать пользовательские команды. Позже данное направление получило развитие, и был портирован интерпретатор языка TCL, поскольку он привычен для разработчиков ПЛИС. А так как в определенной конфигурации команды имеют прямой доступ к оборудованию (ко всему адресному пространству), то разработчики получили возможность делать достаточно сложные тесты.
Сертифицируемый Linux
Одним из первых сторонних применений было довольно специфическое требование на сертифицированность кода. Было некое устройство, обладающее ограниченным функционалом, включающим в себя: работу с сокетами (UDP), файловой системой, и еще некоторый функционалом. Весь функционал был реализован заказчиком как ПО на базе Linux. Данное устройство было не x86 и не ARM. Имело свою периферию. Требовалось сертифицировать код, который используется в устройстве, поскольку сертифицированные дистрибутивы там не могли быть использованы. Попытка обрезания ядра Linux, привела к порядка 500 тыс. строк кода, плюс ещё были проблемы с #ifdef и другими макросами. И заказчик, оценив стоимость сертификации данного изделия, попросил рассмотреть другие варианты. Embox на тот момент уже имел сетевой стек, файловую систему и было принято решение доработать его с учетом требований сертификации. Так у нас появился язык описания модулей Mybuild, мы в какой то степени решили проблему с макросами, некоторые другие проблемы. В итоге мы добились того, что в конечном образе у нас находится только используемый (заявленный в конфигурации) код, а его для конкретной задачи обычно требуется не очень много.
Linux без Linux
Идея запускать POSIX-приложения на системах с небольшими ресурсами является основной для ещё одного открытого проекта — NuttX. В каких-то моментах данный проект превосходит Embox, в каких-то — наоборот. Примеры с Qt и SIP-телефоном, насколько я знаю, NuttX не по зубам. Но проект действительно очень интересен.
Безопасный Linux
Если несколько с другой стороны взглянуть на статическую сборку образа маленьких RTOS, то есть всю функциональность определить на момент создания, то можно заметить, что получается естественная защита от вредоносного кода, то есть вирусов. Конечно, в Linux можно сделать очень хорошую систему безопасности, но всё-таки основные проблемы с безопасностью связаны с человеческим фактором (слабые пароли, утеря пароля, повышение прав пользователя и т.д). То есть, при получении рутовых прав, вся защита, по сути, становится не актуальной и можно установить, что угодно куда угодно. В случае же статической сборки злоумышленник может пользоваться только уже имеющимся функционалом. Да, он, возможно сможет получить статистику или сделать какую-то настройку, но, согласитесь, это куда меньшее зло, чем в случае установки вредоносных программ. Плюс, на универсальных системах стоимость системы защиты, скажем так, не нулевая. В нашем же случае это достается гораздо дешевле, поскольку заложено уже на этапе проектирования.
Linux реального времени
Я довольно много посвятил вопросу использованию Linux в качестве системы реального времени. Объяснил почему этого трудно достичь, а также почему попытки предпринимаются. Так вот, вернемся к уже приведенной ссылке на статью о роботе с распознаванием линии по OpenCV. Если заглянули по ссылке, то возможно обратили внимание, что OpenCV крутится на RaPi, а управление моторами происходит на отдельной плате Arduino. На это есть пара причин: первая — планировщик, вторая — то, что управление происходит из пользовательского режима, а в нем у Linux нет доступа к оборудованию. Как вы наверное догадались, в Embox обе проблемы можно решить гораздо проще чем в Linux. И на одной плате с достаточными ресурсами можно и OpenCV запустить, и моторами управлять.
Было несколько устройств, которые сочетали в себе функциональность Linux и работу в реальном времени. Пример — станок ЧПУ, который управлялся по веб-интерфейсу, коротко описанный мной в статье. Ну и если мы делаем роботов на нескольких платах, то это мультиагентные системы.
Internet of Things
Embox, как и фактически все RTOS для микроконтроллеров, имеет низкие требования по ресурсам. Вот уже описанный на хабре пример игрушки на stm32vl-discovery. Запускался Embox даже на 16-разрядном MSP-430 c 512 байтами ОЗУ. Но если взглянуть, например, на код из статьи с игрушкой, можно заметить, что там используются не стандартные POSIX-потоки, а собственная реализация бесстековых потоков (light threads). Естественно, если пойти дальше и реализовать весь код самостоятельно, уверен, можно добиться и лучшего результата по затратам памяти. Но “умные” датчики это только одна из частей IoT. Они передают данные на какие то более мощные узлы. И делают они это по какому-то протоколу. Но если на этом узле тоже будет запущен Embox, и код библиотеки, реализующий протокол общения, будет общим, то это упростит разработку. Ведь во-первых, код общий, и даже если есть ошибка в реализации протокола, то она нивелируется тем, что один и тот же код будет работать на обеих сторонах общения. А во-вторых, код можно отладить на платформе с большими ресурсами, что куда более привычно и просто.
Немного подробнее об тяжелой судьбе проекта можно узнать посмотрев видео с одного из наших первых первых выступлений на CodeFreeze.
Заключение
Данная статья раскрывает лишь небольшой сегмент в многообразном embedded мире. Не упомянуты такие важные темы как бытовая техника, автомобили, приборостроение, АСУ-ТП. Но как я уже сказал в начале, в статье я хочу всего лишь “поговорить” об особенностях embedded. Надеюсь, статья была достаточно интересной, и прочитав ее, вы узнали что-то новое! И давайте обсудим особенности данной сферы в комментариях.
Встроенный контроллер одного ноутбука и его наследство
В этом материале я хочу немного рассказать о том, что происходит во встроенном контроллере (Embedded Controller, EC) моего ноутбука. Речь идёт о мультиконтроллере IT8586E, основанном на Intel 8051. Он встроен в ноутбук Lenovo Ideapad 310-15IKB. Но, прежде чем переходить к деталям, полагаю, нелишним будет поговорить о том, что это вообще такое — встроенные контроллеры.
Краткая история встроенных контроллеров на платформах x86
Сначала был создан компьютер IBM PC. Многих это возмутило, данный шаг компании IBM был широко признан неразумным.
В материнскую плату этого компьютера было встроено множество периферийных устройств, вроде Intel 8259 (программируемый контроллер прерываний) или Intel 8253 (программируемый таймер). Обращаться к этим контроллерам можно было, пользуясь инструкциями in/out ядра x86 (и, на самом деле, к контроллерам 8259 и 8253 всё ещё можно обращаться на современных x86-процессоров, но теперь они входят в состав кристалла CPU). Один из контроллеров IBM PC, Intel 8255 (программируемый контроллер интерфейса периферийных устройств), отвечал за взаимодействие с клавиатурой.
Где-то в конце 1980-х периферийные устройства всё чаще и чаще объединяли, включая в состав одной микросхемы несколько таких устройств. Например — это чипы северного моста и южного моста. Один из таких чипов, Super I/O-контроллер, отвечал за взаимодействие с низкоскоростными устройствами, вроде последовательных портов, параллельных портов и контроллеров дисководов гибких дисков. В состав этого чипа часто входил и контроллер клавиатуры.
Ноутбукам, кроме прочего, требовалась особая система управления питанием. Производители портативных компьютеров взглянули на контроллер клавиатуры, на то, что он способен перезагружать компьютер, и сказали: «На самом деле — это замечательно. Давайте сделаем то, что нам нужно, по похожей схеме». В результате к контроллеру клавиатуры были добавлены два порта с похожим интерфейсом, позволяющим отправлять на устройства команды и получать от них данные. И, фактически, во многих платформах эта возможность реализована в том же самом контроллере, который входит в состав Super I/O-чипа.
В середине 1990-х заметным явлением стал стандарт ACPI (Advanced Configuration and Power Interface, усовершенствованный интерфейс управления конфигурацией и питанием). Он определял стандартизированный интерфейс для EC. А именно, через его командный интерфейс можно было обращаться к 8-битному адресному пространству переменных, определяемых производителями оборудования. Эти переменные можно было описывать с помощью языка AML (ACPI Machine Language), средствами AML можно было описывать и код, позволяющий воздействовать на эти переменные.
В конце 2000-х годов микросхемы южного и северного мостов были объединены с CPU. Правда, надо отметить, что интеграция южного моста (PCH, Platform Controller Hub) в CPU, по всей видимости, имеет место лишь на мобильных платформах. Но в ноутбуках EC всё ещё представлен отдельным чипом, который, кроме того, реализует функционал Super I/O-контроллера и контроллера клавиатуры. Он, например, решает следующие задачи:
Схема материнской платы моего ноутбука
Так как EC интенсивно взаимодействует с разными устройствами — неплохо было бы знать о том, что это за устройства. Мне в решении этой задачи повезло — кто-то выложил в интернет так называемые boardview-файлы к интересующей меня плате. В таких файлах содержатся схемы печатных плат, в частности, сведения о компонентах, расположенных на плате, и о том, как они связаны. Между компонентами материнских плат имеется так много соединений, что анализ схемы их связей может оказаться весьма сложной задачей. Я попытался представить связи между компонентами платы в упрощённой форме, результат моих трудов показан на следующей схеме (тут нет большинства линий, имеющих отношение к питанию).
Схема связей компонентов платы (оригинал)
CPU соединён с EC с использованием шины LPC (Low Pin Count), которая, по сути, представляет собой замену шины ISA, применявшейся в более старых компьютерах, в физической реализации которой используется меньше линий связи. Процессор и контроллер взаимодействуют, в основном, по LPC. Но между EC и CPU имеется множество соединений (показанных синими линиями без стрелок), используемых в особых целях. Например — это линия SCI, предназначенная для вызова прерывания от EC в CPU.
Прошивка EC
Раздобыть прошивку контроллера несложно. Я уже извлекал образ BIOS раньше, для других нужд. После его обработки с помощью утилиты cpu_rec в моём распоряжении оказалось примерно 160 Кб 8051-кода из самого начала образа, расположенного до кода, имеющего отношения к UEFI. Контроллер 8051 имеет 16-битное адресное пространство, а значит 160 Кб кода ему не соответствуют. Получается, что речь идёт о так называемой banked-прошивке. То есть — одни части («блоки» или «банки») прошивки в адресном пространстве подвергаются динамической замене на другие части путём воздействия на некие регистры.
Размеры блоков прошивки обычно, в большинстве контроллеров 8051, составляют либо 32, либо 64 Кб. Выяснить их размер при работе с конкретным контроллером можно, поискав повторяющиеся фрагменты кода. Дело в том, что в разных блоках прошивки обычно имеются одинаковые фрагменты, представляющие код общего назначения (быстро найти такие фрагменты можно попробовать, прибегнув к автокорреляции). Оказалось, что в моём случае речь идёт о блоках кода размером 32 Кб.
После того, как я всё это выяснил, меня посетила блестящая идея, которая заключалась в том, чтобы посмотреть даташит исследуемого компонента. Даташит на мой контроллер найти не удалось, но мне попались документы на IT8502E, описывающие устройство достаточно близкое к тому, которое было у меня. В нём, что было очень кстати, подробно описывалось большинство I/O-механизмов и, похоже, описание функционирования прошивки, в основном, соответствовало той прошивке, что была у меня.
При просмотре даташита я наткнулся на упоминание отладочного интерфейса I2C, но он, правда, не был документирован. После некоторых изысканий я нашёл проект ECSpy, который представляет собой Rust-реализацию отладчика для EC, созданную силами компании System76, которая работает над собственной прошивкой для EC.
Отладчик даёт доступ на чтение и запись к оперативной памяти и регистрам ввода/вывода EC. Доступ к регистрам отличается дополнительным ограничением, которое заключается в том, что операция записи чётко определена лишь для триггерных регистров (в результате, например, операция, вызывающая изменение состояния конечного автомата, не сработает).
Ещё в даташите сказано, что возможность отладки нужно включать с помощью регистра, но она и так включена. Доступ к ней осуществляется посредством I/O-портов x86 через Super I/O-чип, поэтому ей можно пользоваться из пользовательского пространства, без необходимости писать драйвер.
Для реверс-инжиниринга тех частей прошивки, которые отвечают за взаимодействие с различными устройствами, могут пригодиться спецификации этих устройств. Я, работая над этим проектом, прочитал часть спецификаций ACPI, SMBus и Smart Battery. В процессе исследования кода прошивки используются следующие ресурсы и инструменты: boardview-файлы, даташиты, спецификации компонентов, отладчик Ghidra, обычный браузер, применяемый для поиска дополнительных сведений вроде скан-кодов, исходный код Linux-драйвера для EC, дизассемблированный машинный ACPI-код, отладчик для EC и его исходный код. В таких делах весьма кстати могут оказаться несколько мониторов, использование которых позволяет ускорить сопоставление информации, полученной из разных источников.
Большой объём работы выполняется в коде главного цикла, ответственном за обработку событий таймера. Таймер в EC запрограммирован так, что он выдаёт прерывание раз в одну миллисекунду. Потом, на основе прерываний таймера, формируются события, которые происходят, например, каждые 10, 50, 100 мс. Обновление ACPI-переменных выполняется, в основном, в этом контексте.
POST-карты в современных ноутбуках
Затем, вместе с тактовыми импульсами, осуществляется последовательный вывод результирующего значения на GPIO-пины.
Вышеприведённая поисковая таблица может показаться вам знакомой. Дело в том, что она позволяет преобразовывать полубайты в шестнадцатеричные числа для их вывода на 7-сегментном дисплее. Если взглянуть на схему материнской платы, там можно увидеть две линии — EC_TX и EC_RX (эти названия, вероятно, не очень удачны, так как одна из них — это линия данных, а другая — тактовая линия, обе они используются лишь для отправки данных). Они ведут к Wi-Fi-чипу, который подключён к плате через разъём M.2. Но эти линии, на самом деле, ни к чему на Wi-Fi-карте не присоединены.
Это наводит на мысль о том, что существуют некие POST-платы, которые можно подключать к разъёму M.2 для решения проблем, возникающих в ходе загрузки ноутбуков. Так как я знаком с протоколом обмена данными, используемым в M.2-картах, я попытался сделать собственный вариант такой платы (она, в целом, представляет собой два последовательно соединённых сдвиговых регистра, подключённых к 7-сегментным дисплеям). Посмотрите — какая симпатичная у меня получилась штука.
Самодельная POST-плата для разъёма M.2
Но я, к сожалению, видимо что-то напутал, так как эта плата, в итоге, так и не заработала, а мне не очень-то хотелось тратить время на поиск и исправление ошибок, допущенных при её создании.
Клавиатурные механизмы
Можно подумать, что вышеописанная задача решается очень легко, что заключается она в просмотре некоей поисковой таблицы и в отправке хосту результатов. Но, увы, скан-коды PS/2 — это настоящий бардак.
Существует три различных набора скан-кодов, между которыми нет ничего общего. EC использует набор №2. Одиночное нажатие и отпускание клавиши не всегда приводит к генерированию одного байта скан-кода.
Если промежуточное значение меньше 0x80, это значит, что оно просто соответствует обычному однобайтовому PS/2 скан-коду. А если говорить о других значениях, то они, по уже рассмотренной нами схеме, используются в роли индексов в поисковой таблице. Результирующее значение определяет функцию, которая используется для обработки скан-кода. Полученные скан-коды затем помещают в 16-байтовый кольцевой буфер, байты, содержащиеся в котором, если это возможно, потом отправляют хосту.
Таинственный фрагмент кода
Зачем встроенному контроллеру SHA-1? Если посмотреть на то, что именно вызывает SHA1-код, то окажется, что он используется при взаимодействии с батареей по SMBus:
Где хранится код?
Во многих прошивках для 8051 обычно имеется код, реализующий возможности отладки, и мой — не исключение. Перед функциями, имеющими отношение к контроллеру клавиатуры и к EC, имеется множество функций, которые явно реализуют отладочные возможности. Одна из них — это команда контроллера клавиатуры, предназначенная для чтения из EC данных с адресов флеш-памяти с применением регистра.
Если сделать оттуда дамп прошивки, то получится образ, который отличается от оригинала. Изначально я предполагал, что EC использует образ из флеш-памяти BIOS, так как он хранится в этой памяти, и EC к этой памяти подключён. Но оказалось, что это, на самом деле, не так. К этой памяти напрямую подключены и CPU, и EC. В результате, в том случае, если и тот и другой одновременно попытаются прочесть из неё данные, на шине неизбежно возникнет конфликт. Прошивка, на самом деле, хранится в самом EC. В IT8502 этой возможности не было (это запутало меня ещё сильнее, так как я пользовался даташитом именно для такого контроллера).
Запуск моего собственного кода в EC
Вероятно, стоит сказать о том, что у 8051 имеется некоторое количество различных адресных пространств:
Я этого делать не собирался, так как подобные действия несут в себе риск «окирпичивания» устройства. EC можно перепрограммировать, используя входы/выходы клавиатурной матрицы в роли параллельного порта, но я не собирался заниматься и этим.
Ещё одна проблема, связанная с записью данных во флеш-память, заключается в том, что её содержимое проверяется с использованием контрольной суммы. В прошивке имеется сигнатура, которая указывает на то, где начинается контрольная сумма, и в ней имеются два байта, которые различаются в разных редакциях прошивки. Подобное характерно для контрольных сумм. Если дело обстоит именно так — то неясно, где именно начинается и заканчивается контрольная сумма, и то, как именно она вычисляется.
Правда, для выполнения собственного кода нет нужды выполнять запись данных во флеш-память. В EC для этого имеется другой механизм. А именно — адреса 0x0000-0x1000 в адресном пространстве XDATA — это обычная оперативная память, которую можно отобразить на произвольную область адресного пространства CODE, изменив содержимое кое-каких регистров.
Схема из даташита, на которой показано 5 областей, пронумерованных от 0 до 4, которые могут быть отображены на адресное пространство кода. А именно, речь идёт об областях 0x0000-0x0800, 0x0800-0x0c00, 0x0c00-0x0e00, 0x0e00-0x0f00 и 0x0f00-0x1000
В результате для выполнения собственного кода достаточно лишь выполнить отображение одного из этих фрагментов на память с кодом. Оперативная память в диапазоне 0x0000-0x0e00 используется самой прошивкой, в результате остаются блоки 3 и 4, размер каждого из которых составляет 256 байт.
Правда, чтобы осуществить отображение этой памяти на память с кодом, нужно выполнить запись в регистры и в оперативную память из пространства XDATA. Есть одна отладочная функция, позволяющая писать данные в XDATA, но она содержит ошибки и может писать данные только по адресам, где байт из верхней части адреса является таким же, как и байт из его нижней части. Тут имеется ещё и интерфейс I2C, который достаточно хорошо подходит для решения задачи записи данных в SRAM.
Я сомневался по поводу записи данных в порты ввода/вывода, так как было сказано, что это подходит лишь для триггерных регистров. Но в регистрах, используемых для отображения памяти, имеется ещё и бит для запуска DMA-транзакции (так как этот чип, конечно, поддерживает DMA). В любом случае, не было способа проверить то, что отображение памяти осуществляется именно так, как мне нужно, так как нет отладочной функции, позволяющей читать данные из адресного пространства CODE.
К счастью, был и другой путь: функционал отображения памяти используется для перезаписи флеш-памяти (так как никто не заинтересован в том, чтобы работа программы завершилась бы с ошибкой, когда будет достигнута область с кодом, выполняющим запись данных). Используя I2C можно отредактировать содержимое SRAM в адресном пространстве XDATA, поместив туда отладочный код, и подстроить механизм записи данных во флеш-память так, чтобы он обращался бы к этому отладочному коду.
В любом случае, эту проблему легко обойти, так как между адресами, всё равно, имеется взаимно однозначное соответствие, а значит — код можно просто записать в те места пространства XDATA, которые будут соответствовать нужным местам памяти. Оказалось, что I2C-запись в регистры, отвечающие за отображение памяти, тоже работает. В результате для выполнения отладочного кода с его последующей записью мне не нужно было бы прибегать к режиму перезаписи флеш-памяти.
Итоги
На сегодня это всё, а в следующий раз я расскажу об исследовании прошивки ноутбучного Wi-Fi-модуля (RTL8821AE) и о разработке небольшого кейлоггера, основанного исключительно на возможностях Intel 8051.
Занимались ли вы разработкой прошивок для контроллеров, основанных на Intel 8051?