Как определить нужны ли для работы модуля устройства файлы firmware

Обновлено: 04.07.2024

Наверняка вы слышали фразы вроде: «обновил прошивку телефона», «у фотоаппарата проблемы с прошивкой» или «пиратская прошивка». Сейчас эти фразы означают любой софт, который управляет железом на низком уровне.

О чём речь

Сам термин «прошивка» появился примерно 70 лет назад: это было время, когда ещё не было интегрированных микросхем и микроэлектроники, а вычислительные машины уже были. И вот тогда вместо флеш-памяти и жёстких дисков использовали память на магнитных сердечниках.

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

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

Чтобы собрать рабочий модуль памяти, нужно было продевать множество проводов через множество бубликов, что напоминало процесс прошивания иголкой и ниткой. Процесс был ручным: сидели инженеры и шили себе память. Отсюда и слово — «прошивка».

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

Что такое прошивка

Память на магнитных сердечниках

Зачем нужна прошивка

Прошивка в электронике отвечает за работу всего устройства — телефона, фотоаппарата, умной колонки, часов или охранного модуля. Сама прошивка хранится либо в той же микросхеме в блоке памяти, либо в отдельном чипе, если для неё нужно много места.

Когда устройство включается, происходит такое:

  1. На микросхему подаётся напряжение.
  2. Микросхема подготавливает память и все модули для работы прошивки.
  3. После этого она идёт туда, где лежит прошивка, и запускает оттуда команды по очереди.

👉 Получается, что прошивка — это программа, которая отвечает за работу всего устройства на самом низком уровне.

Зачем менять прошивку

Иногда производитель устройства не добавляет в него некоторые возможности. В этом случае программисты извлекают файл прошивки из устройства, разбираются, из чего он состоит, и смотрят, можно ли в нём что-то изменить, чтобы сделать устройство лучше.

История из жизни одного производителя смартфонов: при производстве телефонов используется один модуль камеры и в дешёвых, и в дорогих моделях. Чтобы они не конкурировали между собой, производитель может в прошивке дешёвой модели отключить оптическую стабилизацию или ночную съёмку. В итоге покупатели знают, что если нужна ночная съёмка, то нужно взять дорогую модель, потому что она это умеет, а дешёвая нет.

Но если в прошивке убрать это ограничение и загрузить её на дешёвый телефон, то он тоже станет снимать и со стабилизацией, и ночной режим появится. В итоге за те же деньги пользователь может получить новые возможности устройства. Но теряет гарантию производителя.

Другой пример — из мира фотоаппаратов. У старых камер Canon есть стандартная прошивка, у неё много ограничений. Разработчики придумали альтернативную прошивку Magic Lantern, которая делает из камеры настоящего зверя для съёмки видео.

Прошивка — не для всей электроники

Есть много устройств, которые прошить не получится: либо нужно распаивать микросхему, либо она защищена от записи. Чаще всего это простая бытовая техника: пульты от телевизоров, миксеры, микроволновки, детские игрушки и тому подобное.

👉 Общее правило такое: если устройство можно подключить к интернету или к компьютеру — скорее всего, его можно прошить. Работает это не всегда, но чаще всего так.

А можно ничего не прошивать и пользоваться как есть?

Можно, иногда даже нужно, если прошивка отвечает за важные системы, например за работу домашнего газового котла, автомобильных систем безопасности или кардиостимулятора.


С железом нередко бывает так, что одного драйвера в пространстве ядра ОС для его работы недостаточно. Нужна также прошивка (firmware), которая загружается в само устройство. Точный формат и назначения прошивки зачастую известны только производителю: иногда это программа для микроконтроллера или FPGA, а иногда просто набор данных. Пользователю это не важно, главное, что устройство не работает, если ОС не загрузит в него прошивку.

В свободных операционных системах прошивки нередко вызывают споры. Многие из них распространяются под несвободными лицензиями и без исходного кода. Авторы OpenBSD и ряда дистрибутивов GNU/Linux считают это проблемой и со свободой, и с безопасностью и принципиально не включают такие прошивки в установочный образ.

Если у тебя есть устройство, которое требует прошивки, и тебе нужно, чтобы оно работало, вопрос лицензии прошивки становится чисто академическим — от необходимости иметь ее в системе и загружать ты никуда не уйдешь.

Полный набор из linux-firmware занимает более 500 Мбайт в распакованном виде. При этом каждой отдельно взятой системе требуется только небольшая часть этих файлов, остальное — мертвый груз.

Даже в современном мире с дисками на несколько терабайт еще много случаев, когда размер имеет значение: встраиваемые системы, образы для загрузки через PXE и подобное. Хорошо, если о board support package позаботился кто-то другой, но это не всегда так.

Если ты точно знаешь полный список нужного железа, можно извлечь файлы вручную. Впрочем, даже в этом случае найти нужные файлы может быть непросто — linux-firmware представляет собой не очень структурированную кучу файлов, и списка соответствия файлов именам модулей ядра там нет. А если ты хочешь дать пользователям возможность легко собрать свой образ, тут и вовсе нет выбора — нужно автоматическое решение.

В этой статье я расскажу о своем способе автоматической сборки. Он неидеален, но автоматизирует большую часть работы, что уже неплохо. Писать скрипт будем на Python 3.

Примеры кода в статье упрощенные. Готовый и работающий скрипт ты можешь найти на GitHub.

К примеру, можно им просмотреть список прошивок для включенных в .config драйверов сетевых карт Realtek.

Основы

В ядре Linux нет глобального списка прошивок и кода для их загрузки. Каждый модуль загружает свои прошивки с помощью функций из семейства request_firmware . Логично, если учесть, что процедура загрузки прошивки у каждого устройства разная. Соответственно, информация о нужных прошивках разбросана по множеству отдельных файлов с исходным кодом.

На первый взгляд, некоторую надежду дает опция сборки FIRMWARE_IN_KERNEL . Увы, на деле она встраивает в файл с ядром только файлы, которые ты явно укажешь в EXTRA_FIRMWARE . Так что файлы все равно сначала придется найти.

Поиск по вызовам request_firmware() тоже не очень перспективен. Некоторые модули поддерживают несколько разных прошивок, да и имя файла часто хранится в переменной. В качестве примера можно посмотреть на фрагмент кода из драйвера сетевой карты Intel e100.

К счастью для нас, модули должны указывать нужные им файлы прошивок с помощью макроса MODULE_FIRMWARE() . Пример можно найти в e100. Этот макрос определен в файле include/linux/module.h .

Именно из вывода этого макроса берется информация о прошивках, которую можно увидеть в выводе утилиты modinfo .

В ряде случаев можно было бы обойтись одной modinfo . Если у тебя есть собранное ядро и возможность его загрузить, ты можешь просмотреть вывод modinfo для каждого нужного модуля. Это не всегда удобно или вообще возможно, так что мы продолжим искать решение, для которого понадобится только исходный код ядра.

Здесь и далее будем считать, что все ненужные модули отключены в конфиге сборки ядра (Kconfig). Если мы собираем образ для конкретной системы или ограниченного набора систем, это вполне логичное предположение.

Ищем исходники модулей

Чтобы собрать список имен файлов прошивок, мы сначала составим список всех файлов исходного кода ядра, где они используются. Затем мы прогоним эти файлы через препроцессор из GCC, чтобы раскрыть все макросы, и извлечем собственно имена нужных файлов.

Находим все включенные в конфиге модули

Это самая простая часть. Конфиг сборки ядра имеет простой формат «ключ — значение» вроде CONFIG_IWLWIFI=m . Значение может быть n (не собирать), y (встроить в ядро) или m (собрать в виде модуля).

Нас интересуют только ключи, а какое значение там, y или m , нам не важно. Поэтому мы можем выгрести нужные строки регулярным выражением (.*)=(?:y|m) . В модуле re из Python синтаксис (. ) используется для незахватывающих групп (non-capturing group), так что захвачена будет только часть в скобках из (.*)= .

Продолжение доступно только участникам

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

Системное ядро Linux способно к модификации за счёт расширения функциональных возможностей. Это достигается несколькими способами, но самым оптимальным является подключение загружаемых модулей ядра. Этот способ позволяет вести разработку нужного функционала для ядра независимо от самого ядра. Процесс подключения модулей к ядру, как правило, никаких трудностей не вызывает (в отличие от конфигурирования и компиляции). Если конечно модули разработаны грамотно и должным образом протестированы. Системным администраторам стоит помнить, что если нужно «расширить» ядро под требуемый функционал, то в первую очередь следует воспользоваться вариантом в виде загружаемых модулей ядра.

Зачем нужны загружаемые модули ядра?

С помощью загружаемых модулей ядра довольно просто добавлять в систему драйверы, поддержку новых технологий и возможностей для архитектуры Linux. Это не требует затрагивания компонентов, уже включенных в ядро. Не нужно модифицировать исходный код ядра и возиться с его компиляцией. К тому же, это экономит память и дисковое пространство под само ядро. Ведь можно загружать только те модули, которые используются фактически.

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

Среди всех UNIX-подобных операционных систем наиболее приспособленными для работы с загружаемыми модулями ядра (а также поддерживающими самый широкий спектр оборудования и аппаратных платформ, в том числе и новинок) являются системы Linux и Solaris.

Особенности работы с модулями ядра Linux

Как уже было отмечено, Linux способна предоставить системным администраторам довольно широкие возможности для работы с загружаемыми модулями ядра. Так например в Linux любой системный компонент возможно сделать загружаемым модулем за исключением двух системных объектов: драйвера устройства, используемого для работы корневой файловой системы (ФС) и драйвера мыши типа PS/2.

Все загружаемые модули хранятся (и устанавливаются) в каталог /lib/modules/версия_ядра. Здесь версия_ядра может определена командой:

Для то, чтобы получить список загруженных и используемых в данный момент модулей ядра, можно воспользоваться командой lsmod:

Как видно из данного вывода, в системе настроена поддержка брандмауэра iptables, а также используются модули для интеллектуального управления платформой IMPI – Intelligent Platform Management Interface. В колонке «Size» указывается размер в байтах, а в колонке «Used by» (кем используется) приводится количество связей на данный модуль от других компонентов, а также список самих этих компонентов.

Даже если системное ядро было сконфигурировано и скомпилировано с настройками и комплектностью по-умолчанию, то при использовании команды lsmod, как правило будет выведен довольно длинный список модулей. Если известно точное или примерное имя модуля, который необходимо проверить (используется он или нет), то вместе с командой lsmod удобно использовать и команду grep:

В данном выводе показаны результаты для используемых ядром модулях драйвера amdgpu.

Загрузка и подключение модулей ядра

Пусть имеется загружаемый модуль ядра для некоторого устройства. Например есть файл модуля somedevice.ko для устройства somedevice. Чтобы загрузить этот модуль для ядра прямо во время работы системы следует использовать команду insmod:

При помощи этой же команды можно также и задавать конфигурационные параметры, необходимые для требуемых или специфических режимов работы модулей:

Для удаления модуля нужно либо перезапустить систему или указать в явном запросе выгружаемый модуль с помощью команды rmmod:

Следует заметить, что несмотря на то, что команду rmmod хоть и можно использовать в любой момент времени, однако модуль может быть удалён только в случае, если с ним не связано ни одной действующей ссылки, которые указываются в столбце «Used by» вывода команды lsmod.

Чтобы каждый раз поле перезапуска системы не загружать требуемые модули, в Linux существует возможность подключать их автоматически с помощью файла /etc/modprobe.conf и соответствующей команды modprobe. Эта команда является «обёрткой» команды insmod. Она способна определять порядок загрузки и выгрузки модулей, их параметры, а также зависимости от других модулей и/или параметров. Всю эту информацию команда modprobe читает из файла /etc/modprobe.conf, который, кстати способна и сама генерировать.

Чтобы сгенерировать файл modprobe.conf в соответствии с текущим набором загруженных модулей (которые необходимо загрузить заранее), нужно использовать команду:

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

Ключевые слова path определяют расположение конкретных модулей в файловой системе, а aliases задают привязку файловых систем, сетевых протоколов, старших номеров блочных и символьных устройств к требуемому модулю.

Значения для строк options администраторы должны задавать самостоятельно, поскольку эти инструкции командой modprobe автоматически не генерируются. Например, чтобы задать для модуля устройства somedevice адрес его ввода-вывода, а также вектор прерываний, можно это сделать таким образом:

Стоит отметить также, что для команды modprobe доступны ключевые слова install и remove, при помощи которых можно указывать команды, которые будут выполняться во время загрузки и установки модулей ядра соответственно.

Как можно видеть, в процедуре управления модулями нет ничего слишком сложного, однако это тот случай, когда системным администраторам следует проявлять особую аккуратность и осторожность, проверяя каждый модуль, который планируется использовать в системе.

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Как вы знаете из статьи что такое ядро Linux, ядро является монолитным. Это значит, что весь исполняемый код сосредоточен в одном файле. Такая архитектура имеет некоторые недостатки, например, невозможность установки новых драйверов без пересборки ядра. Но разработчики нашли решение и этой проблеме, добавив систему модулей.

Ядро Linux позволяет драйверам оборудования, файловых систем, и некоторым другим компонентам быть скомпилированными отдельно - как модули, а не как часть самого ядра. Таким образом, вы можете обновлять драйвера не пересобирая ядро, а также динамически расширять его функциональность. А еще это значит, что вы можете включить в ядре только самое необходимое, а все остальное подключать с помощью модулей. Это очень просто.

Модули ядра Linux

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

Модули ядра Linux собираются только под определенную версию ядра, есть способ запуска модуля независимо от версии ядра, если они совместимы с помощью dkms, но об этом мы поговорим позже.

Находятся все модули в папке /lib/modules/. Учитывая, что модули рассчитаны только для определенной версии ядра, то в этой папке создается отдельная подпапка, для каждой установленной в системе версии ядра. В этой папке находятся сами модули и дополнительные конфигурационные файлы, модули отсортированы по категориям, в зависимости от назначения например:


Перед тем как переходить к практике, давайте коротко рассмотрим основные команды для управления модулями.

  • lsmod - посмотреть загруженные модули
  • modinfo - информация о модуле
  • insmod - загрузить модуль
  • rmmod - удалить модуль

Работа с модулями ядра Linux выполняется, в основном, с помощью этих команд, но могут использовать и другие.

Все модули

Такая задача возникает нечасто, но если вы хотите посмотреть все установленные модули ядра Linux в системе, делается очень просто. Все модули расположены в папке /lib/modules, а поэтому очень просто вычислить их все одной командой, или даже просто зайти в папку файловым менеджером и посмотреть.

В Ubuntu команда будет выглядеть вот так:

dpkg -S *.ko | grep /lib/modules


Можно смастерить такую конструкцию с помощью find:

find /lib/modules -name *.ko

Можем искать только для текущего ядра:

find /lib/modules/$(uname -r) -name *.ko


Также, все модули записаны в конфигурационном файле /lib/modules/modules.aliases, поэтому мы можем просто посмотреть его содержимое:

Если хотим проверить установлен ли определенный модуль ядра Linux, отфильтруем вывод любой из команд с помощью grep:

find /lib/modules -name *.ko | grep vbox


Что загружено?

Все информация о загруженных модулях хранится в файле /proc/modules, мы можем ее вывести командой:


Но для этого дела есть более цивилизованные методы. Это утилита lsmod и modinfo. Чтобы посмотреть загруженные модули ядра linux выполните:

Удобно проверять загружен ли модуль с помощью grep:

sudo lsmod | grep vbox

А более подробную информацию о каждом модуле можно получить с помощью утилиты modinfo:

Здесь вы можете увидеть файл модуля, его лицензию, автора и зависимости. Зависимости - это те модули, которые должны быть загружены для его нормальной работы. К сожалению, не для всех модулей доступно нормальное описание, но вы можете попробовать посмотреть описание зависимостей модуля.

Запуск модулей ядра

Загрузить модуль ядра Linux можно с помощью команд modprobe или insmod. Например, загрузим модуль vboxdrv

sudo modprobe vboxdrv

Чтобы загрузить модуль ядра linux с помощью insmod необходимо передать адрес файла модуля:

sudo insmod /lib/modules/4.1.20-11-default/weak-updates/misc/vboxdrv.ko

Напоминаю, что его можно узнать с помощью команды modinfo. Запуск модуля ядра Linux предпочтительно выполнять с помощью modprobe, поскольку эта команда не только находит файл модуля в файловой системе, но и загружает все его зависимости.

Удаление модулей ядра

Здесь аналогично две команды - modprobe, позволяет удалить модуль если ей передать опцию -r, а также есть команда rmmod. Начнем с modprobe:

sudo modprobe -r vboxdrv

Другая команда в этом случае выглядит немного проще:

sudo rmmod vboxdrv

Если вы получили ошибку во время выгрузки модуля, например: rmmod: ERROR: Module vboxdrv is in use by: vboxnetadp vboxnetflt, значит он еще используется другими модулями, и сначала нужно выгрузить их. В данном случае это vboxnetadp и vboxnetflt. Правильно отработавшая команда не должна ничего возвращать.

rmmod vboxnetadp vboxnetflt

Блокирование загрузки модулей

Иногда, во время загрузки системы для используемых нами устройств, загружаются не те модули ядра Linux, они либо не поддерживают нужную функциональность либо конфликтуют с другими модулями. Ярким примером можно назвать загрузку драйвера b43 вместо brcmsmac для беспроводных адаптеров Broadcom. Чтобы решить эту проблему вы можете добавлять модули в черный список. Для этого достаточно добавить одну строчку в файл /etc/modprobe.d/blacklist.conf:

sudo vi /etc/modprobe.d/blacklist.conf

Этот код добавит в черный список модуль b43.

Автозагрузка модулей

Кроме чёрного списка существует отдельный каталог, в котором можно настроить автоматическую загрузку модулей при старте системы. Это /etc/modules.load.d/. Этот каталог тоже содержит конфигурационные файлы с расширением *.conf, в которых перечислены все модули, которые надо загружать при старте системы. Для добавления своего модуля можно воспользоваться файлом /etc/modules.load.d/modules.conf. Например, добавим brcmsmac:

sudo vi /etc/modules.load.d/modules.conf

Установка модулей ядра Linux

Собранные для этой версии ядра модули вы можете просто скопировать в нужную папку, собственно, мы так и поступаем, когда собираем ядро из исходников. Но с проприетарными драйверами и другими внешними драйверами, не поставляемыми в комплекте с ядром дело обстоит иначе. Эти модули поддерживают несколько версий ядра, но для их установки используется специальная технология - DKMS (Dynamic Kernel Module Support). Причем модуль, установленный таким образом один раз, будет пересобираться для каждой новой версии ядра автоматически. Обычно такие модули поставляются в виде пакетов, которые устанавливаются как и все другие приложения пакетным менеджером. Ручная установка модулей с помощью dkms выходит за рамки данной статьи.

Выводы

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

Читайте также: