на каком выводе микросхемы spi flash будет такой сигнал при его нормальной работе 2pin
А каковы могут быть уровни сигналов у SPI-интерфейса?
Хочу попробовать почитать флэшку с SPI-интерфейсом. Подключить ее хочу к Wandboard. Однако, смущает, что в доке на Wandboard указано, что на разъеме SPI выведено питание +5В. У флэшки разумеется питание +3,3В. Вот и задумался. Может SPI-интерфейсы разные бывают в плане величин уровней сигналов? Не спалю ли я флэшку?
Ну и что это может означать на практике?
Скорее всего нет, обычно в доках пишут +3.3 tolerant +5.
Тебе надо преобразовать напряжение, просто повесив нагрузочный резистор для искомых +3,3V. А вообще ищи даташит на свою флешку часто напряжение питания может варьироваться.
страница номер 8 например.
Поставь 2 диода между выходом 5В и входом 3.3, будет выход 3.4, а вот уровни выхода 3.3 могут вход в 5B и не переключать. Вообщем пробовать надо.
Однако, смущает, что в доке на Wandboard указано, что на разъеме SPI выведено питание +5В.
В даташите на i.MX6 вроде как напруга в 5В выдается только на USB. Про spi там как-то мутно написано. Вроде он есть. Описаны тайминги. А на каких ногах чипа сидит что-то непонятно.
Схема wandboard тоже какая-то мутная. Хотя, может я просто ничего не понимаю в буржуйских схемах. Но похоже, что разъем подведен прямо на ножки чипа.
Что ты паришься? У всех МК SPI’ные ноги 5V-tolerant. Ты внимательно схему своего одноплатника посмотри. Если там нет 5V-tolerant входов, тупо воткни конвертер уровней (вплоть до самопального, а то и даже пару-тройку диодов в линию MISO, чтобы суммарно на них где-то 1.5-2В падало), с MOSI вряд ли проблемы будут. Разве что если ты железяку совсем уж на упоротом TTL подключаешь. Но я таких не видел давно.
А, еще вариант: воткни туда оптопары. Тогда точно ничего не пожжешь. Только если у тебя скорость будет приличная, скоростные оптопары дорого выйдут.
Питать флэш 3.3В. Поставить последовательно сигнальным линиям резисторы по 100 Ом (на случай если с той стороны 5В уровни). И все.
Надо смотреть следующее, как организовано питание процессора и его взаимодействие с внешней шиной. По схеме видно что шина SPI выведена на гребенку JP4 SPI через межплатный большой разъем напрямую с процессора. Смотрим что за проц, видим что у него раздельное питание для блоков, ядро максимум 1.5В, ввод-вывод 3.6В, остальные писать не буду они не нужны. Эта глава в даташитах обычно называется Absolute Maximum Rating. Таким образом видно, что если мы подадим напрямую напряжение на вывод превышающее +3.6В, например +5В, большая вероятность что вывод выйдет из строя. Теперь смотрим Operating Rating рабочее напряжение на вводе-выводе, оно может быть разным если запитать соответствующие группы ввода-вывода разным напряжением. Обычно пишут используются 1.8, 2.5, 3.3В. Наилучший выход, взять питание +3.3В с разъема JP3 LVDS, запитать им SPI память и подключить соответствующие сигналы SPI к разъему JP4 SPI.
В СУБЖ не МК, а скорее SOC с раздельным питанием блоков, у таких +5V tolerance не встречал. Может оно и не сдохнет, но здоровья не прибавит.
Обзор шины SPI и разработка драйвера ведомого SPI устройства для embedded Linux (Часть первая, обзорная)
В этой статье я хочу провести краткий обзор шины SPI (интерфейса, широко распространённого во встраиваемой технике, используемого для подключения различных устройств) и попытаюсь описать процесс создания драйвера протокольного уровня SPI устройства для Linux. Данный документ не претендует на роль полного руководства, а скорее преследует цель указать нужное направление. Так как статья не вошла в размер одного топика, мне пришлось разбить её на две части.
0. Вместо введения
Что это за статья?
Эта статья представляет собой компиляцию информации из различных источников, вольный перевод некоторых частей документации, а также мои собственные комментарии, дополнения и описания возникших проблем.
Первый подраздел описывает работу шины SPI, данная часть статьи конкретно к Linux никак не привязана, поэтому её можно читать тем, кому Linux не интересен, а нужно лишь получить информацию об этом интерфейсе.
Второй подраздел описывает структуры и механизмы лежащие в основе работы с SPI в Linux, его нужно прочесть для понимания того, о чём пойдёт речь в третьей и четвёртой частях.
Если вас не интересует мои переводы и дополнения, можете смело переходить сразу к пятой части, там можно найти информацию о том, где получить всю необходимую информацию по данному вопросу.
Если вы видите ссылки в названии какой-либо структуры или функции, можете открыть её в новой вкладке, так вы сможете попасть непосредственно на описание данной структуры/функции в официальной документации к ядру Linux.
Ошибки
Я не волшебник, я только учусь. Если найдёте какие-либо ошибки или неточности, пожалуйста, сообщите мне.
1. Что такое SPI?
Аббревиатура SPI означает «Serial Peripheral Interface» или в русском варианте «последовательный периферийный интерфейс». Название говорит само за себя, данный интерфейс используется для работы с различными периферийными устройствами. Например, это могут быть различные ЦАП/АЦП, потенциометры, датчики, расширители портов ввода/вывода (GPIO), различная память и даже более сложная периферия, такая как звуковые кодеки и контроллеры Ethernet.
С технической точки зрения SPI — это синхронная четырёхпроводная шина. Она представляет собой соединение двух синхронных сдвиговых регистров, которые является центральным элементом любого SPI устройства. Для соединения используется конфигурацию ведущий/ведомый. Только ведущий может генерировать импульсы синхронизации. В схеме всегда только один ведущий (в отличие от той же шины I2C, где возможен вариант с более чем одним ведущим), количество ведомых может быть различно. В общем случае выход ведущего соединяется со входом ведомого, и наоборот, выход ведомого соединяется со входом ведущего. При подаче импульсов синхронизации на выход SCK, данные выталкиваются ведущим с выхода MOSI, и захватываются ведомым по входу MISO. Таким образом если подать количество импульсов синхронизации соответствующее разрядности сдвигового регистра, то данные в регистрах обменяются местами. Отсюда следует что SPI всегда работает в полнодуплексном режиме. А вот нужны ли нам данные, полученные от устройства при записи какого-либо параметра, это уже другой вопрос. Часто бывает что данные полученные от устройства при записи в него данных являются мусором, в таком случае их просто игнорируют, но мы их получим вне зависимости от нашего желания.
Контроллер SPI, как правило, реализуется периферийным блоком в MCU или eMPU. В большинстве чипов он может работать как в режиме ведущего, так и в режиме ведомого. Но на данный момент Linux поддерживает только режим ведущего (Master).
Существует несколько способов включения SPI устройств.
Простейший из них вы видите на рисунке выше (спасибо Wikipedia за рисунки под свободной лицензией GFDL). В данном случае к ведущему все ведомые подключаются параллельно, за исключением сигнала выбора ведомого (
CS). Для каждого ведомого необходим отдельный сигнал выбора ведомого (на рисунке они обозначены как SSx). Для сигналов выбора ведомого могут использоваться как специально предназначенные для этого выходы SPI-контроллера, так и порты ввода/вывода общего назначения (GPIO) микроконтроллера.
CS — Chip Select (выбор микросхемы). С помощью данного сигнала происходит активация ведомого устройства. Обычно он является инверсным, то есть низкий уровень считается активным. Иногда его называют
SS (Slave Select, рус. «выбор ведомого»).
Частным случаем независимого подключения является вариант с одним единственным ведомым. В таком случае может возникнуть желание подтянуть сигнал
CS к земле, чтобы устройство всегда было в активном состоянии. Но делать это крайне не рекомендуется, так как ведомое устройство может использовать сигнал CS для инициализации или для других служебных целей.
Основное неудобство при независимом подключении ведомых в том, что для каждого из ведомых необходим отдельный сигнал
CS. Каскадная схема подключения, в зарубежной литературе называемая «daisy-chain» (можно перевести как «гирлянда»), лишена такого недостатка.
Как видно из рисунка выше, здесь используется общий сигнал выбора ведомого для всех ведомых. Выход каждого из ведомых соединяется со входом следующего. Выход последнего ведомого соединяется со входом ведущего, таким образом образуется замкнутая цепь. При таком подключении можно считать что последовательно соединённые устройства образуют один большой сдвиговый регистр. Соответственно, данные можно записать во все устройства «за один присест», предварительно собрав нужный пакет, объединяющий данные для каждого из устройств в порядке соответствующем физическому порядку соединения. Но тут есть один тонкий момент. Во-первых, все микросхемы должны поддерживать такой тип подключения; во-вторых, ядро Linux не поддерживает такой тип подключения, так что если всё же захотите его использовать, то вам придётся модифицировать существующие драйвера, либо же написать собственные.
CS и MISO не показаны. Но в данном случае они не так интересны, например, сигнал
CS представляет собой просто «провал» на всём протяжении передачи данных.
2. Обзор SPI подсистемы в Linux
Драйверы SPI в Linux делятся на две части. Первая — это драйверы SPI контроллеров, которые работают непосредственно с железом конкретно взятого контроллера. Такие драйверы определяют как настроить контроллер, какие действия предпринять при переходе в режим пониженного энергопотребления (suspend) и выходе из него(resume), выбор следующей передачи (spi_transfer) из очереди передач в сообщении (spi_message, об очередях чуть ниже) и отправка его непосредственно в порт, также определяется как активировать/деактивировать конкретное устройство посредством CS (функции cs_activate/cs_deactivate). В этой статье я не буду описывать данный тип драйверов. Как правило, они уже реализованы для тех MCU/eMPU на которые существует порт Linux, и лезть в них руками надо только в том случае, если вам нужна какая-то специфичная функция, вроде Chip Select Decoding, для возможности активации нужного ведомого устройства посредством внешней логики. Иногда это бывает полезно, например, в случае недостатка GPIO.
Вторая часть — это протокольные драйверы, используемые для работы с различными ведомыми устройствами, которые подключены к шине SPI. Данные драйверы называют «протокольными», потому что они лишь отправляют и получают различные данные от ведомых устройств, при этом не работая напрямую с каким-либо оборудованием. Именно данный тип драйверов нам наиболее интересен, так как позволяет добавить поддержку интересующего ведомого устройства в систему, его то мы и рассмотрим.
Большинство протокольных драйверов представляет собой модули ядра. Например, если устройство представляет собой аудиокодек подключаемый по SPI, то драйвер будет также использовать функции предоставляемые ALSA, а программы (например, madplay) смогут работать с ним посредством символьного устройства /dev/audio, не имея ни малейшего понятия о том как он аппаратно устроен и к какой шине подключен.
Также ядро предоставляет протокольный драйвер общего назначения, называемый spidev, с интерфейсом в виде символьного устройства. Он позволяет совершать полудуплексные обращения к ведомому SPI-устройству посредством стандартных системных вызовов read() и write(), устанавливать режим работы, а также производить полнодуплексный обмен данными посредством ioctl() вызовов.
transfers — связанный список передаваемых сегментов в транзакции (передач);
spi — указатель на spi устройство, в очереди которого стоит данное сообщение;
is_dma_maped — если данный флаг «истина», то предоставлены оба, dma и cpu виртуальные адреса для каждого буфера передачи;
complete — обратный вызов, вызываемый для извещения об окончании транзакции;
context — аргумент для обратного вызова complete();
actual_length — полное число байт, которые были переданы во всех успешных предачах;
status — 0 в случае успеха, либо отрицательное значение с errno в случае ошибки;
Структура spi_message используется для выполнения атомарной последовательности передач данных, каждая из которых представлена структурой spi_transfer. Последовательность передач «атомарна» в том смысле, что шина SPI не может быть использована для передачи другого сообщения spi_message до тех пор, пока не будет полностью отправлено предыдущее. На некоторых системах, многие такие последовательности могут быть выполнены как единая запрограммированная DMA передача. На всех системах данные сообщения ставятся в очередь, и могут быть завершены уже после транзакций с другими устройствами. Все обращения к отдельно взятому ведомому устройству всегда выполняются в FIFO порядке.
Структура struct spi_transfer описывает отдельную передачу в связанном списке сообщения и определяет пару буферов для чтения/записи.
tx_buf — указатель на буфер данных в пространстве памяти ядра, которые необходимо передать, либо NULL;
rx_buf — указатель на буфер данных в пространстве памяти ядра, в который данные следует считать, либо NULL;
len — размер буферов rx и tx в байтах;
tx_dma — DMA адрес tx_buf, используется если установлен параметр spi_message.is_dma_mapped;
rx_dma — DMA адрес rx_buf, используется если установлен параметр spi_message.is_dma_mapped;
speed_hz — устанавливает скорость для передачи, отличную от установленной по-умолчанию для устройства. Если данное значение равно 0, то используется скорость по-умолчанию, указанная в поле max_speed_hz структуры spi_device.
bits_per_word — устанавливает количество бит на слово, отличное от определённого по умолчанию. Если данное значение равно 0, то используется значенние по-умолчанию, указанное в поле bits_per_word структуры spi_device.
delay_usecs — время ожидания в микросекундах, после того как был отправлен последний бит передачи и перед тем как сменить состояние chipselect’а, либо начать передачу следующей передачи в очереди. Будьте крайне осторожны с данным параметром, нужно смотреть в какой части драйвера контроллера реализуется задержка. Например, для чипов серии at91 она реализована в обработчике прерывания, так что её использование чревато последствиями.
При инициализации структуры spi_transfer существует очень важный момент, они обязательно должны быть выделены в области памяти доступной для DMA через kmalloc, kzalloc и иже с ними. Если master-драйер использует dma, то при использовании статически объявленных массивов драйвер будет падать при попытке передачи.
При передаче данных по SPI количество записанных бит всегда равно количеству считанных. Протокольные драйверы всегда должны предоставлять указатели на буферы tx_buf и/или rx_buf. В некоторых случаях они могут предоставлять DMA адреса для передаваемых данных.
Возможность переопределения скорости передачи данных и количества бит на слово для каждой передачи в отдельности зависит от конкретной реализации драйвера и аппаратных возможностей контроллера. Например, для контроллера SPI в чипах серии at91 возможность переопределения полей speed_hz и bits_per_word не предусмотрена, поэтому они должны быть всегда установлены в 0, иначе вы получите ошибку при попытке передачи данных.
Если указатель на tx_buf установлен как NULL, то SPI контроллер будет выталкивать нули при заполнении буфера rx_buf. В случае, когда rx_buf установлен в NULL, считываемые данные будут игнорироваться. Количество выталкиваемых (и захватываемых) байтов всегда равно len. Попытка вытолкнуть только часть слова приведёт к ошибке. (Например, при попытке выталкивании трёх байт и длине слова 16 бит или 20 бит, в первом случае будет использовано 2 байта на слово, во втором — 4 байта).
Данные для передачи всегда хранятся в порядке специфичном для данной аппаратной платформы. При отправке/считывании данных происходит автоматическое конвертирование порядка байт из специфичного для SPI (обычно big-endian, за исключением случая когда выставлен параметр SPI_LSB_FIRST) в аппаратно-специфичный порядок для данного CPU. Например, если параметр bits_per_word равен 16, то буферы будут занимать по 2N байт, и содержать по N слов с длиной 16 бит каждое, хранящемся в байтовом порядке, специфичным для данного CPU.
В том случае, если размер слова не является степенью двойки, то представление слова в памяти включает дополнительные биты. Слова, хранящиеся в памяти для протокольного драйвера всегда являются выровненными по правому краю (right-justified), так что дополнительные биты всегда будут являться старшими разрядами.
Для наглядности снова приведу осциллограмму:
В данном случае tx-буфер содержит значение 0xf98e, установленное значение bits_per_word соответствует 12 битам на слово. Устройство работает в SPI_MODE_0. На рисунке синяя линия соответствует выходу MOSI контроллера, а жёлтая — SCK. Здесь хорошо видно что при отправке пришло только 0x098e, старшие четыре бита были отброшены, так как они считаются дополнительными. Если совсем просто, то одно 12-битное слово занимает в памяти два байта, а разница между размером слова в памяти и его действительным размером составляет 2*8 — 12 = 4 бита, которые отбрасываются при передаче.
SPI не поддерживает какого-либо механизма автоматического обнаружения устройств. К тому же, в большинстве случаев, SPI устройства не предусматривают горячее подключение/отключение, поэтому они, как правило, просто распаиваются непосредственно на плате. В связи с этим данные устройства считаются специфичными для конкретной платы (board-specific). Параметры для таких устройств указываются в файле платы: arch/. /mach-*/board-*.c.
Например, вот так будет выглядеть установка параметров для аудиокодека tlv320aic23b для отладочной платы SK-AT91SAM9260:
где modalias – название драйвера ядра, отвечающего за обслуживание устройства (в нашем случае “tlv320aic23b”);
chip_select – номер соответсвующего chip select’а;
max_speed_hz – максимальная частота в Гц;
mode – режим SPI, определяемый константами SPI_MODE_0… SPI_MODE_3, также через операцию битового “или” могут быть добавлены флаги SPI_CS_HIGH (устанавливает активным высокий уровень для chipselect-а ), SPI_NO_CS (передача данных без активации CS в принципе). Полный список возможных флагов можно посмотреть в описании структуры spi_device;
bus_num – номер шины (как правило, соответсвует номеру SPI контроллера в даташите на MCU/eMPU).
Также структура spi_board_info содержит следующие поля, не инициализированные в примере выше:
const void *platform_data – данное поле предназначено для хранения указателя на данные специфичные для конкретного драйвера;
void *controller_data – для некоторых контроллеров необходима информация о настройке устройства, например, DMA;
int irq – зависит от подключения устройства.
Все поля структуры spi_board_info устанавливают соответствующие поля структуры spi_device.
В случае необходимости установки параметров для других SPI устройств, в масив добавляются ещё аналогичые элементы.
Данные структуры хранят информацию, которая не может быть всегда определена драйверами. Информация, которая может быть определена функцией probe() драйвера (например, количество бит на слово), в данную структуру не включается.
Стоит заметить, что всё же существует возможность горячего подключения ведомых SPI устройств. В этом случае используют функцию spi_busnum_to_master() для получения указателя на структуру spi_master по номеру шины SPI и дальнейшего перебора устройств на шине. Но данная тема выходит за рамки данной статьи.
Данная статья является кратким дискурсом по шине SPI и не должна восприниматься как точная техническая документация. Рассматривается только полнодуплексный вариант применения.
Общие сведения:
Несмотря на то, что интерфейс называется 4-х проводным, для подключения нескольких ведомых понадобится по одному проводу SS для каждого ведомого (в полнодуплексной реализации). Сигналы MISO, MOSI и SCK являются общими для всех устройств на шине. Ведущий посылает сигнал SS для того ведомого, обмен данными с которым будет осуществляться. Простыми словами, все ведомые, кроме выбранного ведущим будут игнорировать данные на шине. SS является инверсным (active-low), что означает что ведущему необходимо прижать эту линию для выбора ведомого.
Подключение:
SPI на Arduino:
Arduino UNO/Piranha UNO/Arduino ULTRA
На Arduino UNO/Piranha UNO/Arduino ULTRA выводы аппаратного SPI расположены на 10, 11, 12 и 13 выводах, а так же эти выводы соединены с колодкой ICSP (in circuit serial programmer):
Сигнал | Вывод |
---|---|
SS | 10 |
MOSI | 11 |
MISO | 12 |
SCK | 13 |
Arduino MEGA
На Arduino MEGA выводы аппаратного SPI расположены на 50, 51, 52 и 53 выводах, а так же эти выводы соединены с колодкой ICSP (in circuit serial programmer):
Сигнал | Вывод |
---|---|
SS | 53 |
MOSI | 51 |
MISO | 50 |
SCK | 52 |
Пример для Arduino
В этих примерах мы соединим две Arduino по SPI по следующей схеме:
В одну плату необходимо загрузить скетч ведущего, а в другую скетч ведомого. Для проверки работы необходимо открыть проследовательный монитор той платы, в которую загружен скетч ведомого.
Arduino UNO в качестве ведущего:
Arduino UNO в качестве ведомого:
После соединения двух Arduino по SPI и загрузки соответствующих скетчей, мы будем получать следующее сообщение в мониторе последовательного порта ведомого микроконтроллера раз в секунду:
SPI на Raspberry Pi
На Raspberry Pi выводы аппаратного SPI расположены на выводах GPIO7, GPIO8, GPIO9, GPIO10, GPIO11:
Подробное описание как это сделать можно посмотреть по ссылке Raspberry Pi, включаем I2C, SPI
Пример работы с SPI на Python:
В отличие от Arduino для Raspberry не существует простых решений для работы в режиме ведомого. Подробней ознакомиться с работой чипа BCM Raspberry можно в технической документации на официальном сайте, стр. 160.
Для проверки работы сценария можно подключить Raspberry по SPI к Arduino со скетчем из примера выше через преобразователь уровней или Trema+Expander Hat:
Подробнее о SPI
Параметры
Существуют четыре режима работы SPI, зависящие от полярности (CPOL) и фазы (CPHA) тактирования:
Режим | Полярность | Фаза | Фронт тактирования | Фронт установки бита данных |
---|---|---|---|---|
SPI_MODE0 | 0 | 0 | Спадающий | Нарастающий |
SPI_MODE1 | 0 | 1 | Нарастающий | Спадающий |
SPI_MODE2 | 1 | 0 | Нарастающий | Спадающий |
SPI_MODE3 | 1 | 1 | Спадающий | Нарастающий |
В Arduino IDE для установки режима необходимо передать функции, возвращающей объект настроек параметр режима работы SPI_MODE, например:
Для выбора режима работы SPI на Raspberry Pi необходимо вызвать дескриптор объекта SpiDev().mode и присвоить ему битовые значения CPOL и CPHA, например:
Скорость передачи данных
Скорость передачи данных устанавливается ведущим и может меняться «на лету». Программист в силах указать лишь максимальную скорость передачи данных.
Проникновение в BIOS ROM: осваиваем SPI Flash №2
Содержание статьи
Микросхемы SPI Flash ROM, в частности микросхема SST 25VF080B, используемая в
рассматриваемой платформе поддерживают сигнал защиты записи WP# (Write Protect).
В отличие от одноименного сигнала микросхем LPC Flash, для SPI Flash этот сигнал
не управляет непосредственно разрешением записи информации, а действует в
сочетании с битами BP0-BP3 (Block Protect) и BPL (Block Protect Lock) регистра
статуса. Четыре бита в регистре статуса BP0-BP3 выбирают один из 16 вариантов
защиты микросхемы BIOS, среди этих вариантов – полный запрет записи по всем
адресам, разрешение записи по всем адресам, а также варианты, обеспечивающие
избирательную защиту блоков. Разумеется, сама по себе такая защита не
эффективна, поскольку программа может изменить содержимое битов BP0-BP3 и
разрешить запись. Чтобы этого не произошло, используется бит BPL и внешний
сигнал защиты записи WP#. Установка бита BPL=1 делает биты BPL и BP0-BP3
недоступными для перезаписи. Данная защита действует только при наличии сигнала
логического «0» на вход WP#, а при наличии логической «1» на входе WP#, биты BPL
и BP0-BP3 доступны для перезаписи независимо от состояния бита BPL.
Таким образом, защитой содержимого микросхемы BIOS можно программно управлять
посредством регистра статуса, а объектом аппаратной защиты, реализуемой сигналом
WP#, является содержимое регистра статуса, а не записанная информация.
Подробности в [5].
Для активации данного механизма защиты, BIOS при старте платформы должен
записать значение битов BP0-BP3 в соответствии с требуемым режимом защиты, а
также установить бит BPL=1 для предотвращения последующей модификации битов
BP0-BP3 и BPL. Затем требуется обеспечить подачу логического «0» на вход WP#.
Желательно, чтобы метод программного управления сигналом WP# был уникален для
каждой модели материнской платы, это осложнит работу «вирусописателей».
К сожалению, и этот механизм защиты, поддерживаемый микросхемами SPI Flash,
реально не используется разработчиками BIOS и материнских плат. Во всех
платформах, исследованных автором, в регистре статуса Flash, бит BPL=0, то есть
защита не используется и статусный регистр доступен для перезаписи.
Примечание
Символ «#» после названия сигнала означает, что активным уровнем является
низкий. Например, рассматриваемый сигнал защиты записи WP# (Write Protect),
интерпретируется следующим образом: Логический «0» означает наличие защиты
записи, логическая «1» – отсутствие защиты. В цифровой схемотехнике такие
сигналы называют инверсными.
Запуск операций стирания и записи содержимого микросхемы SPI Flash
выполняется в два этапа. Сначала передается команда Write Enable, затем команда,
непосредственно запускающая стирание или запись. Если заданная
последовательность операций не соблюдена, содержимое микросхемы не изменяется.
Такой механизм эффективно защищает BIOS от случайного искажения в результате
программного сбоя, однако препятствием для «вирусописателей» он не является, так
как требуемая последовательность операций описана в открытой документации на
микросхемы SPI Flash, например 6.
Меры предосторожности
При отладке предлагаемых процедур, выполняющих стирание и перезапись
содержимого микросхемы Flash ROM, неизбежно существует риск искажения BIOS с
последующим отсутствием старта материнской платы. Примеры, содержащиеся в
прилагаемом каталоге WORK, рассчитаны на специалистов, имеющих квалификацию и
оборудование для восстановления BIOS, если это потребуется. Идеальное рабочее
место для отладки предлагаемых программ – это материнская плата, в которой
микросхема BIOS установлена в «панельку», допускающую извлечение микросхемы без
использования пайки, в сочетании с программатором. Исходное содержимое
микросхемы желательно сохранить в виде двоичного файла. Заметим, что если будет
искажен BIOS Boot Block, процедуры самовосстановления, рекламируемые
производителями материнских плат, окажутся неработоспособными. Напомним, что во
время рабочего сеанса используется копия BIOS в Shadow RAM, поэтому если
содержимое микросхемы BIOS будет искажено, это проявится только при очередной
перезагрузке. Таким образом, чтобы избежать необходимости аварийного
восстановления BIOS, перед сбросом или выключением питания следует восстановить
исходное содержимое Flash ROM.
Категорически не рекомендуется проводить эксперименты на основном рабочем
компьютере, лучше собрать стенд, используя, например, старую плату, оставшуюся
после апгрейда.
Замечания по совместимости
Операции, рассматриваемые в примерах 1-3, требуют программирования
конфигурационных регистров и ресурсов платформы, реализация которых различается
в разных чипсетах. Разумеется, рассмотреть все наборы системной логики в рамках
одной статьи невозможно, поэтому автор остановился на конкретном примере:
материнская плата Gigabyte GA-965P-S3 (чипсет Intel 965, микросхема BIOS SST
25VF080B). Примеры 1-3 предназначены только для плат с указанной элементной
базой. Используя предлагаемый материал, заинтересованный читатель может
реализовать библиотеку процедур для поддержки более широкой номенклатуры
платформ.
Пример 1. Процедура чтения идентификатора и регистра статуса Flash ROM.
Как и в ранее опубликованных статьях данного цикла, в целях монопольного и
беспрепятственного взаимодействия программы с оборудованием при работе с
предлагаемым примером, автор применил «древнюю» технологию отладки под DOS.
Аргументация такого шага и рекомендации по организации рабочего места приведены
в ранее опубликованной статье «64-битный
режим под DOS: исследовательская работа № 1″. Прежде чем рассматривать
процедуры стирания и записи содержимого микросхемы BIOS, рассмотрим более
простой пример – чтение идентификатора и статуса этой микросхемы.
Каталог WORK\ID_FLASH содержит следующие файлы:
ASM_TD.BAT – обеспечивает ассемблирование, линковку и запуск программы
под отладчиком. При запуске TASM и TLINK используются опции, обеспечивающие
добавление отладочной информации в EXE файл.
ASM_EXE.BAT – обеспечивает ассемблирование и линковку. Генерируется
EXE файл.
FLASH_ID.ASM – основной модуль программы.
A20.INC – Библиотека для управления и контроля состояния адресной
линии A20 с использованием выходного порта контроллера клавиатуры.
BIGREAL.INC – Библиотека поддержки режима Big Real Mode, для
использования 32-битных адресов в диапазоне 0-4 Гб. Этот режим, как и управление
адресной линией A20, необходимы для работы данной программы, так как в
рассматриваемой платформе, регистры контроллера SPI используют технологию
Memory-mapped I/O, то есть адресуются в пространстве памяти.
SPIFLASH.INC – Библиотека процедур для взаимодействия с микросхемой
BIOS, подключенной к интерфейсу SPI. Реализация контроллера SPI в различных
чипсетах отличается, данная библиотека предназначена для поддержки микросхемы
Intel ICH8, то есть «южного моста» чипсета Intel 965. Для полного понимания
работы процедур данной библиотеки, следует обратиться к документу [3].
OUTPUT.INC – Библиотека процедур для вывода текстовых сообщений с
использованием функций DOS и перевода шестнадцатеричных чисел в текстовое
представление.
DATASEGS.INC – Сегменты данных. Содержит сегмент переменных, сегмент
текстовых строк и сегмент стека.
Рассмотрим выполнение программы чтения идентификатора и статуса микросхемы
BIOS. Нумерация пунктов приведенного ниже алгоритма соответствует нумерации
пунктов в комментариях к исходному тексту программы. См. файл
WORK\ID_FLASH\flash_id.asm.
1) Установка адреса стека, загрузка регистров SS и SP.
2) Выдача сообщения о начале выполнения программы.
3) Проверка режима работы процессора (Real или Protected). Так как данная
программа использует непосредственное взаимодействие с регистрами чипсета и
другими системными ресурсами, процессор должен быть в режиме Real Mode. Если
процессор в режиме Protected Mode, переходим к пункту 15 (завершение с ошибкой).
4) Подготовка к адресации памяти выше 1 MB. Для этого разрешаем адресную
линию A20 и включаем режим Big Real Mode, обеспечивающий использование 32-битной
адресации в реальном режиме. Это необходимо для доступа к регистрам диапазона
SPIBAR. Подробности в [3]. Если при управлении адресной линией A20 возникли
ошибки, переходим к пункту 15 (завершение с ошибкой).
5) Детектирование чипсета материнской платы, получение адреса диапазона RCBA
(Root Complex Base Address). Вначале проверяем наличие поддержки функций
PCIBIOS, затем, используя PCIBIOS, проверяем наличие «южного моста» Intel ICH8
по кодам PCI Vendor ID, PCI Device ID. Если чипсет не распознан, переходим к
пункту 14 (завершение с ошибкой). Напомним, что текущая версия программы
поддерживает платформы только с данным типом «южного моста». Считываем базовый
адрес диапазона RCBA.
6) Установка разрешения циклов записи для интерфейса SPI. Устанавливаем бит 0
в регистре BIOS_CNTL. Подробности в [3].
7) Установка указателей и сегментных регистров. DS:EBX далее будет
использоваться как базовый адрес блока регистров контроллера SPI (SPIBAR). Этот
указатель является входным параметром для подпрограмм SPI_Read и
Scan_Opcode_Menu. По правилам Big Real Mode, DS=0, а EBX содержит 32-битный
физический адрес. Базовый адрес блока регистров управления контроллера SPI равен
RCBA+3020h. Сегмент ES будет использоваться для генерации строк текстовых
сообщений.
8) Считываем и визуализируем идентификатор микросхемы Flash ROM.
Идентификатор содержит два байта. Первый байт – Vendor ID (синоним Manufacturer
ID) идентифицирует производителя микросхемы, в нашем примере он равен BFh, этот
код присвоен фирме SST. Второй байт – тип микросхемы, для 25VF080B он равен 8Eh.
Процедура чтения идентификатора состоит из передачи микросхеме кода команды (90h
для команды Read ID) и считывания двух байт идентификатора.
Напомним, что контроллер SPI не позволяет непосредственно передать код
команды микросхеме Flash ROM, это делается косвенно, посредством блока регистров
OpCode Menu. Каждый байт 8-байтного блока OpCode Menu содержит один из кодов
команд для микросхемы Flash ROM (чтение идентификатора, чтение статуса, запись,
стирание, и т.п.). Этот блок инициализирует BIOS при старте платформы. Для
передачи команды, программа должна передать контроллеру номер байта в 8-байтном
блоке OpCode Menu, содержащего код этой команды. Так как расположение кодов в
ячейках OpCode Menu зависит от реализации BIOS и может быть произвольным,
программа сначала должна просканировать OpCode Menu и найти номер ячейки, в
которой хранится код требуемой команды (90h для Read ID). Эту функцию выполняет
подпрограмма Scan_OpCode_Menu. Затем, полученный 3-битный номер ячейки
используется как битовое поле COP (Cycle Opcode Pointer) при формировании
32-битного кода операции для контроллера SPI. Кроме COP, код операции также
содержит информацию о количестве передаваемых байт данных, частоте битовой
синхронизации и т.п. Сформированный 32-битный код операции является входным
параметром для подпрограммы SPI_Read, которая записывает его в регистр Software
Sequencing Flash Control Register, инициируя начало выполнения транзакции на
шине SPI. Перед этим необходимо записать адрес в регистр Flash Address Register.
Затем, циклически опрашиваем регистр Software Sequencing Flash Status Register
для определения момента завершения транзакции. После этого, из регистра Flash
Data 0 Register, считываем полученные данные, в нашем случае это два байта
идентификатора микросхемы Flash ROM. Подробности в [3,5].
9) Считываем и визуализируем регистр состояния микросхемы Flash ROM. Этот
регистр содержит один байт, его побитное описание приведено в [5]. Код команды
Read Status – 05h, количество читаемых байт – 1. В остальном, данная процедура
аналогична процедуре чтения идентификатора, описанной в пункте 8.
10) Восстанавливаем исходное значение регистра BIOS_CNTL, сохраненное перед
его перенастройкой в пункте 6.
11) Восстанавливаем исходное состояние адресной линии A20 и выключаем режим
32-битной адресации (Big Real Mode).
12) Вывод сообщения об успешном выполнении программы и завершение. Эта точка
выхода используется при отсутствии ошибок.
13) Начало последовательности, используемой для завершения программы при
ошибках. Восстанавливаем исходное значение регистра BIOS_CNTL, сохраненное перед
его перенастройкой в пункте 6. Сюда управление передается, если произошли ошибки
после перенастройки регистра BIOS_CNTL и требуется его восстановление перед
завершением программы.
14) Восстанавливаем исходное состояние адресной линии A20 и выключаем режим
32-битной адресации (Big Real Mode).
15) Вывод сообщения об ошибках и завершение программы. При передаче
управления на этот пункт, регистр BP должен содержать указатель на строку
сообщения об ошибке.
Пример 2. Процедура чтения Flash ROM.
Ассемблерный пример, выполняющий чтение содержимого микросхемы BIOS в файл,
находится в каталоге WORK\RD_FLASH. Программа выполняет
сохранение области адресного пространства FFF00000h-FFFFFFFFh в файл размером 1
Мб и фиксированным именем BIOS.BIN. Файл создается в текущем каталоге. Размер
сохраняемого блока не зависит от реального размера используемой микросхемы BIOS,
распознавание типа и размера микросхемы не реализовано в данной версии.
Поддерживаются только материнские платы с «южным мостом» Intel ICH8. Для
гарантированного включения полного объема микросхемы BIOS в адресное
пространство, программа использует установку битов FWH_F8_EN, FWH_F0_EN в
регистре FWH_DEC_EN1, чем и обусловлена ее привязка к конкретному чипсету.
Подробности в [3].
Отметим, что в большинстве платформ, для чтения содержимого SPI Flash можно
использовать «старый» метод доступа посредством окна в адресном пространстве,
это и реализовано в данном примере. А операции, требующие передачи команд
микросхеме SPI Flash (чтение идентификатора или статуса, запись, стирание) могут
быть выполнены только с использованием регистров контроллера SPI. Такой метод
использован в примерах 1 и 3.
Пример 3. Процедура стирания и перезаписи Flash ROM.
Ассемблерный пример, выполняющий стирание и перезапись микросхемы BIOS,
находится в каталоге WORK\WR_FLASH. Программа выполняет
запись содержимого файла с фиксированным именем BIOS.BIN и размером 1 Мб в
микросхему BIOS. Файл должен находиться в текущем каталоге.
Заключение
В очередной раз подчеркну, что целью данного цикла публикаций не является
обучение «вредителей». Материалы будут полезны специалистам по информационной
безопасности, разработчикам аппаратного и системного программного обеспечения.
Выработка механизмов для противодействия какой-либо угрозе невозможна без
детального изучения самой угрозы.
Вся приведенная информация получена из открытых источников, список которых
прилагается.
Источники информации
Электронные документы, доступные на сайте
developer.intel.com.
1) Intel Low Pin Count (LPC) Interface Specification. Revision 1.1. Document
Number 251289-001.
2) Intel P965 Express Chipset Family Datasheet – For the Intel 82P965 Memory
Controller Hub (MCH). Document Number: 313053-001.
3) Intel I/O Controller Hub 8 (ICH8) Family Datasheet – For the Intel 82801HB
ICH8 and 82801HR ICH8R I/O Controller Hubs. Document Number: 313056-001.
Электронные документы, доступные на сайте
www.superflash.com или
ssti.com.
4) 1 Mbit SPI Serial Flash SST25VF010 Data Sheet. Document Number:
S71233-01-000.
5) 8 Mbit SPI Serial Flash SST25VF080B Data Sheet. Document Number:
S71296-01-000.
Электронные документы, доступные на сайте
winbond.com.tw.
6) W25X10A, W25X20A, W25X40A, W25X80A 1M-bit, 2M-bit, 4M-bit and 8M-bit
Serial Flash Memory with 4KB sectors and dual output SPI Data Sheet.
Электронные документы, доступные на сайте
macronix.com.
7) MX25L802 8M-bit [8Mx1] CMOS Serial Flash EEPROM Data Sheet.
Электронные документы, доступные на сайте
datakey.com.
8) SPI EEPROM Interface Specification. Part Number 223-0017-004 Revision H.
Электронные документы, доступные на сайте
vtitechnologies.com.
9) SPI Interface Specification. Technical Note 15.