Как проверить spi flash память

Обновлено: 04.07.2024

Эта статья продолжает тему, связанную с угрозой искажения BIOS материнской
платы вредоносными программами. В ранее опубликованном материале
"Проникновение в BIOS ROM"
N1 и N2 был
приведен пример программы, выполняющей запись в микросхему BIOS, рассмотрены
механизмы защиты, используемые производителями материнских плат и причины, по
которым данные механизмы часто оказываются неэффективными. Напомним, что эта
проблема возникла более 10 лет назад, когда в качестве носителя BIOS начали
использовать микросхемы Flash ROM, допускающие перезапись содержимого без
физического вмешательства в компьютер. Таким образом, возможность оперативной
перезаписи (обновления) BIOS, привела к побочному эффекту – риску его случайного
или преднамеренного искажения.

Казалось бы, сегодня на эту тему уже все сказано. Но время идет, появляются
новые типы микросхем Flash ROM и интерфейсов для их подключения. Это
сопровождается появлением новых методов защиты BIOS от несанкционированного
искажения, и, конечно, новых уязвимостей. В ранее опубликованном материале,
ссылка на который приведена в начале статьи, рассматриваемая тема раскрыта на
примере платформы, использующей микросхему SST 49LF004A, подключенную к
интерфейсу LPC (Low Pin Count). Сегодня, ему на смену пришел интерфейс SPI
(Serial Peripheral Interface). Напомним, что для перезаписи содержимого
микросхемы BIOS, программа должна взаимодействовать с двумя блоками регистров:
регистры интерфейса Flash ROM, расположенные в составе "южного моста" чипсета и
регистры, входящие в состав самой микросхемы Flash ROM. При переходе от
интерфейса LPC к SPI, архитектура двух указанных блоков существенно изменилась.
Поэтому есть смысл рассмотреть взаимодействие программ с микросхемой BIOS,
механизмы защиты и их уязвимости применительно к современным платформам.
Материал снабжен примерами на ассемблере. Для экспериментов использовалась
материнская плата Gigabyte GA-965P-S3, построенная на чипсете Intel 965,
описанном в 3. В качестве носителя BIOS на данной плате используется
микросхема SST 25VF080B, описанная в [5]. Эта микросхема имеет объем 8 Мегабит
(1 Мегабайт) и подключается к интерфейсу SPI. Некоторые микросхемы других
производителей, использующих тот же интерфейс, описаны в [4,6,7]. Описание
интерфейса SPI приведено в [8,9]. При взаимодействии с контроллером SPI, нам
потребуется использовать механизмы доступа к конфигурационному пространству,
описанные в документах 10.

Интерфейс SPI: историческая справка

Как известно, быстродействие микросхемы постоянного запоминающего устройства
(Flash ROM), используемой в качестве носителя BIOS, практически не влияет на
общую производительность компьютера. Это связано с тем, что при старте
материнской платы, BIOS переписывается (или распаковывается) в оперативную
память Shadow RAM и при обращении к BIOS в сеансе ОС, программы работают с
оперативной памятью, а не с микросхемой Flash ROM. Поэтому, в отличие от
процессора, памяти, графической подсистемы, эволюция которых сопровождается
ростом производительности, микросхемы Flash ROM и их интерфейсы развиваются в
другом направлении. Здесь главными критериями являются уменьшение стоимости,
потребляемой мощности и занимаемой площади на материнской плате. Главный метод
достижения этих целей – уменьшение количества проводников между микросхемой BIOS
и контроллером, обычно входящим в состав "южного моста" чипсета. С точки зрения
метода подключения микросхемы BIOS, можно выделить три поколения материнских
плат:

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

2) С появлением чипсетов Intel 810, 815, на основе "хабовой" архитектуры,
шина ISA ушла в прошлое. Ряд устройств материнской платы, ранее подключаемых к
ISA (контроллер гибких дисков, порты COM и LPT, микросхема BIOS), "переехали" на
интерфейс Low Pin Count или LPC, название которого переводится как "малое
количество сигналов". Сигналы адреса, данных и управления в этом интерфейсе
передаются по пяти линиям (не считая сигналов сброса, тактирования и линий
питания), в режиме мультиплексирования во времени. Микросхемы BIOS, используемые
в таких платформах, иногда называют Firmware Hub или FWH. Пример программы
стирания и записи микросхемы BIOS, приведенный в ранее опубликованной статье
"Проникновение в BIOS ROM" N1 и N2" предназначен для таких платформ.

Отметим, что интерфейс LPC также поддерживается современными чипсетами и
разработчик материнской платы может использовать для подключения микросхемы BIOS
как LPC, так и SPI. Сфера применения интерфейса SPI не ограничивается
материнскими платами компьютеров. Внимательный читатель мог обратить внимание на
то, что из-за перехода к последовательному формату данных, каждый из описанных
интерфейсов использует больше тактов для выполнения операций чтения и записи,
чем его предшественник, что чревато снижением быстродействия. Но этого не
происходит, так как указанное обстоятельство компенсируется ростом частоты и
оптимизацией форматов данных. Шина ISA работает на частотах 5-8MHz, LPC на
33MHz, SPI на 33-50 MHz.

Архитектура микросхемы SPI Flash ROM

Микросхемы SPI Flash, в частности SST25VF080B, используют три сигнала для
приема и передачи данных. SCK (Serial Clock) – вход для тактирования
передаваемых или принимаемых данных. SI (Serial Input) – вход для передачи
данных от контроллера к микросхеме, по этой линии побитно передаются адрес,
данные для записи, и коды команд управления микросхемой. SO (Serial Output) –
выход для передачи данных от микросхемы к контроллеру, по этой линии побитно
передаются читаемые данные, а также информация о состоянии микросхемы.
Контроллер управляет микросхемой посредством команд, инициирующих операции
чтения и записи данных, стирания, чтения идентификаторов микросхемы, чтения и
записи регистра статуса и т.п.

Назначение каждого из восьми контактов микросхемы, описание всех команд, а
также другие подробности содержатся в [5].

Архитектура контроллера SPI

Контроллер SPI, входящий в состав "южного моста" Intel ICH8, содержит более
20 регистров управления и состояния. Регистры адресуются в пространстве памяти с
использованием технологии memory-mapped I/O, рассмотренной в ранее
опубликованной статье "Устройства системной поддержки. Исследовательская работа
N 7". Описание всех регистров и команд контроллера SPI выходит за рамки
данной статьи, рассмотрим основные отличия программной модели интерфейса SPI от
его предшественника - интерфейса LPC, акцентируя внимание на регистрах,
используемых при выполнении основных операций – чтении идентификаторов, записи и
стирании Flash ROM.

Напомним, что физический доступ к микросхеме BIOS (не путать с областью
Shadow RAM) осуществляется посредством окна, примыкающего к верхней границе
диапазона 0-4GB. Например, Flash ROM размером 1 Мбайт будет доступен в диапазоне
FFF00000h-FFFFFFFFh. В платформах, использующих интерфейсы ISA и LPC, через тот
же диапазон, при выполнении операций стирания и записи выполняется передача
команд и записываемых данных для микросхемы Flash ROM, а также чтение ее
статуса. Платформа, использующая интерфейс SPI, рассматриваемая в данной статье,
также поддерживает чтение содержимого микросхемы BIOS через указанный диапазон,
но для передачи управляющей и статусной информации во время записи и стирания
Flash ROM, используется другой метод доступа. Информация для выполнения цикла на
шине SPI (адрес и данные) записывается в специальные регистры контроллера SPI
(Flash Address и Flash Data соответственно), затем в регистр Software Sequencing
Flash Control передается команда на выполнение цикла обращения к Flash ROM
(чтения или записи). После этого, через регистр Software Sequencing Flash Status
можно определить момент завершения операции и проконтролировать наличие ошибок.
Данные, прочитанные из Flash ROM, находятся в регистре Flash Data.

Бит 0 (SPI Cycle In Progress) – используется для определения момента
завершения операции на шине SPI. "0" означает, что шина SPI свободна и программа
может запускать следующий цикл. "1" означает, что шина SPI занята, выполняется
транзакция. Бит устанавливается и сбрасывается аппаратно, доступен только для
чтения.

Бит 1 (Reserved) – не используется.

Бит 2 (Cycle Done Status) – устанавливается в "1" аппаратно, при
завершении транзакции на шине SPI. Для сброса, программа должна записать "1" в
данный бит.

Бит 3 (Flash Cycle Error) – устанавливается в "1" аппаратно, если при
выполнении транзакции имели место ошибки: защита записи или попытка запуска
следующей транзакции до завершения предыдущей. Для сброса, программа должна
записать "1" в данный бит.

Бит 4 (Access Error Log) – устанавливается в "1" аппаратно, если имели
место попытки обращения к микросхеме BIOS, нарушающие права доступа к заданным
диапазонам адресов, запрограммированным в конфигурационных регистрах контроллера
SPI.

Биты 5-7 (Reserved) – не используются.

Бит 9 (SPI Cycle Go) – запуск транзакции на SPI. Запись "1" в этот бит
запускает выполнение транзакции, тип и содержание которой определяются
состоянием битов 10-31 этого же регистра.

Бит 10 (Atomic Cycle Sequence) – при передаче приказа на выполнение
транзакции, запись "1" в этот бит запрещает внешним событиям вмешиваться в
процессы, происходящие на интерфейсе SPI до тех пор, пока не будут выполнены три
фазы: передача префиксной команды, передача основной команды и завершение
выполнения основной команды.

Отметим, что таким образом реализуется один из механизмов защиты от
случайного искажения содержимого микросхемы BIOS. Так называемые префиксные
команды (например, Write Enable) должны предшествовать командам, изменяющим
содержимое микросхемы (например, Write, Erase). Получив код команды, содержащий
"1" в данном бите, все три фазы контроллер выполняет аппаратно, без
вмешательства процессора до завершения выполнения основной команды.

Бит 11 (Sequence Prefix Opcode Pointer) – при использовании префиксных
команд (когда бит 10 установлен в "1", см. выше), этот бит выбирает один из двух
байтов, который будет передан в качестве префиксной команды для микросхемы SPI
Flash. При "0" выбирается младший байт регистра Prefix Opcode Configuration
Register, при "1" – старший.

Бит 12-14 (Cycle Opcode Pointer) – это битовое поле выбирает один из
восьми байтов, который будет передан в качестве основной команды для микросхемы
SPI Flash. Указанные байты находятся в регистре Opcode Menu Configuration
Register.

Бит 15 (Reserved) – не используется.

Биты 16-21 (Data Byte Count) – задает количество байтов данных для
операций чтения или записи данных. Количество равно значению этого поля плюс 1.
Например: 000000b=1 байт, 111111b=64 байта.

Бит 22 (Data Cycle) – Задает наличие байтов данных в команде. "0"
означает, данные отсутствуют (например, в команде стирания), "1" означает, что
данные присутствуют (например, в команде чтения статуса, данными является
содержимое регистра статуса).

Биты 24-26 (SPI Cycle Frequency) – задает частоту битовой
синхронизации при выполнении транзакции на SPI. Используются два значения
(остальные зарезервированы): 000b=20MHz, 001b=33MHz.

Биты 27-31 (Reserved) – не используются.

Отметим, что предварительное заполнение восьми байтов регистра Opcode Menu
Configuration Register и двух байтов регистра Prefix Opcode Configuration
Register выполняет BIOS при старте платформы. Поэтому, при формировании битовых
полей, выбирающих код команды и код префикса, программа должна просканировать
указанные регистры и найти, под каким номером в них находятся коды требуемых
операций. Варианты заполнения могут быть разными, даже среди материнских плат
одной модели, использующих различные версии BIOS. Некоторые утилиты перезаписи
Flash изменяют содержимое указанных регистров.

Детальное описание всех регистров и команд контроллера SPI приводится в [3].
Описание команд микросхемы SPI Flash приводится в 6. Информация, изложенная
выше, также проиллюстрирована ассемблерными примерами, прилагаемыми к статье.

Механизмы доступа к Flash ROM и защита записи

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

  1. Настроить регистры "южного моста" чипсета, управляющие размещением
    микросхемы BIOS в адресном пространстве для обеспечения доступа к полному
    объему микросхемы.
  2. Выключить режим защиты записи BIOS, реализуемый средствами "южного моста"
    чипсета.
  3. Выключить режим защиты записи BIOS, реализуемый средствами микросхемы
    Flash ROM.
  4. Передать команду стирания или записи микросхеме Flash ROM.

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

Как было сказано выше, при выполнении операций записи и стирания, программный
доступ к микросхеме SPI Flash осуществляется посредством регистров контроллера
SPI, без использования диапазона адресов, в котором доступен образ микросхемы
BIOS. Поэтому перенастройка чипсета для доступа к диапазону FFF00000h-FFFFFFFFh
в данном примере не потребуется.

Практика показывает, что данный механизм "защиты от снятия защиты" обычно не
активируется разработчиками BIOS. Во всех материнских платах, исследованных
автором, бит BLE=0, поэтому для снятия защиты записи достаточно установить бит
BIOSWE=1, эта операция не будет перехвачена.

Микросхема Intel ICH8 также поддерживает защиту содержимого микросхемы BIOS,
путем задания адресных диапазонов, защищенных от чтения и (или) записи.
Теоретически, на базе данного механизма можно реализовать эффективную защиту,
так как запрет программно включается процедурами BIOS при старте платформы, и
может быть снят только при аппаратном сбросе. Но и этот механизм обычно не
активируется разработчиками BIOS.

Источники информации

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.

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.

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.

7) MX25L802 8M-bit [8Mx1] CMOS Serial Flash EEPROM Data Sheet.

8) SPI EEPROM Interface Specification. Part Number 223-0017-004 Revision H.

SPI (англ. Serial Peripheral Interface, SPI bus — последовательный периферийный интерфейс, шина SPI) — последовательный синхронный стандарт передачи данных в режиме полного дуплекса, разработанный компанией Motorola для обеспечения простого и недорогого сопряжения микроконтроллеров и периферии. SPI также иногда называют четырёхпроводным (англ. four-wire) интерфейсом.

SPI является синхронным интерфейсом, в котором любая передача синхронизирована с общим тактовым сигналом, генерируемым ведущим устройством (процессором, контроллером). Принимающая (ведомая) периферия синхронизирует получение битовой последовательности с тактовым сигналом. К одному последовательному периферийному интерфейсу ведущего устройства-микросхемы может присоединяться несколько микросхем. Ведущее устройство выбирает ведомое для передачи, активируя сигнал «выбор кристалла» (англ. chip select) на ведомой микросхеме. Периферия, не выбранная контроллером, не принимает участия в передаче по SPI.



В SPI используются четыре цифровых сигнала:

MOSI (SI, DO, SDO, DOUT) — выход ведущего, вход ведомого (англ. Master Out Slave In). Служит для передачи данных от ведущего устройства ведомому.

MISO (SO, DI, SDI, DIN) — вход ведущего, выход ведомого (англ. Master In Slave Out). Служит для передачи данных от ведомого устройства ведущему.

SCLK (DCLOCK, CLK, SCK) — последовательный тактовый сигнал (англ. Serial Clock). Служит для передачи тактового сигнала для ведомых устройств.

CS или SS — выбор микросхемы, выбор ведомого (англ. Chip Select, Slave Select).

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

  • CPOL=0 показывает, что сигнал синхронизации начинается с низкого уровня, так что передний фронт является нарастающим, а задний — падающим.
  • CPOL=1, показывает, что сигнал синхронизации начинается с высокого уровня, таким образом передний фронт является падающим, а задний — нарастающим.
  • CPHA=0 показывает, что необходимо производить выборку по переднему фронту
  • CPHA=1 показывает, что выборку данных необходимо производить по заднему фронту.

Пунктирной линией показывает момент передачи данных.

Режим 0 / Mode 0


В этом режиме пассивным уровнем линии SCK является «0», а передача дынных производится по заднему (падающему) фронту.

Режим 1 / Mode 1


В этом режиме пассивным уровнем линии SCK является «0», а передача дынных производится по переднему (нарастающему) фронту.

Режим 2 / Mode 2


В этом режиме пассивным уровнем линии SCK является «1», а передача дынных производится по переднему (нарастающему) фронту.

Режим 3 / Mode 3


В этом режиме пассивным уровнем линии SCK является «1», а передача дынных производится по заднему (падающему) фронту.

От теории перейдем к практике.

Как всегда начнем с объявления необходимых переменных.

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

Для начала обмена необходимо перевести линию CS (SS) в «0».
Окончанием связи служит перевод линии CS в «1».

Ну и конечно же функции Передачи и Приема.

Память Atmel DataFlash AT45DB081D.

  • Страничная Flash память;
  • Емкость — 8 Мбит;
  • Организация — 4096 страниц по 256 или 264 байта;
  • Высокоскоростной SPI интерфейс обмена;
  • Напряжение питания от 2,5 или 2,7 до 3,6 В;
  • Максимальное входное напряжение до 6,25 В, что позволяет подключать ее напрямую к 5-ти вольтовому контроллеру;
  • Два буфера SRAM памяти по 256 или 264 байта каждый;
  • Принцип работы «чтение-модификация-запись»;
  • Программная и аппаратная защита от записи.

Несколько особенностей при работе с данной памятью.

1. По умолчанию вся память организована по 264 байта на страницу. Ее можно реорганизовать на 256 байт на страницу, НО это возможно сделать только один раз и обратно на 264 сменить не удастся. Так что не будем на этом заморачиваться, тем более что это никак нам не мешает. Также у памяти есть специальные 4 байта в которые можно записать идентификатор (например серийный номер изделия), но запись в них так же однократная и перезаписи не подлежит.

  • RESET — аппаратный сброс микросхемы;
  • WP (Write Protection) — аппаратная защита от перезаписи.

3. Обмен данными происходит по 4-х проводному интерфейсу SPI. Микросхема может работать в режимах SPI 0 и 3, а также двух собственных режимах Atmel. Чтобы во всем этом не разбираться будем использовать обычный SPI mode 0.

4. Так как память страничная, то как такового доступа к отдельным ячейкам нет. Все операции чтения/записи происходят по принципу «чтение-модификация-запись» с использованием 2 специальных буферов. Использование 2 буферов помогает ускорить и упростить некоторые операции (пока один буфер записывается в память, второй заполняется новыми данными).

5. После подачи питания и перед обращением к памяти необходимо сделать гарантированную задержку в 20 миллисекунд.

Особенности страничной памяти.

  • определить страницу и расположение на странице интересующего нас байта (адресация сквозная так что это не так сложно);
  • считать необходимую страницу во внутренний буфер;
  • прочитать из внутреннего буфера необходимые данные (или весь буфер).
  • первоначально нужно определить страницу памяти в которую нужно записать данные, и расположение начала записи на странице. Так же нужно удостовериться что массив данных не выйдет за пределы страницы (если выходит то массив нужно разбить на 2 и записать на разные страницы памяти);
  • считать содержимое нужной страницы памяти во внутренний буфер;
  • начиная с определенного места произвести «модификацию» в буфере;
  • записать буфер в страницу памяти.

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

Адрес байта в памяти записывается тремя байтами:
x x x P P P P P P P P P P P P B B B B B B B B B
где x x x — резерв (не используется, =0);
P P P P P P P P P P P P — адрес страницы (12 бит), может принимать значения от 0 до 4095;
B B B B B B B B B — адрес байта на странице (9 бит), может принимать значения от 0 до 263/255.

В качестве примера вычислим расположение байта под номером 353 246.
Для определения номера страницы разделим число на 264:
353246 / 264 = 1338,053 = 1338 = 01010 0111010
А теперь определим адрес байта на странице:
353246 — (1338*264) = 353246 — 353232 = 14 = 000001110
Таким образом получается адрес: 00001010 01110100 00001110 или 0Ah 74h 0Eh.

Ну что же приступаем, глядишь на деле все и проясниться.

Итак, первая команда которую необходимо рассмотреть — это Status Register Read (D7h). Она необходима для определения готовности памяти. Так как внутренний процесс записи может занимать продолжительное время, то для определения его окончания нужно считать Status регистр и проверить в нем старший бит RDY, если там «1» то память готова.

Оформим это в отдельную функцию.


Использовать ее можно вот так:


А для определения окончания записи, вот так:

Команда очистки памяти.


Этот процесс достаточно длительный и может занимать от 7 до 22 секунд.

Рассмотрим остальные команды на примере чтения пяти байт из памяти, начиная с адреса 353246 (адрес страницы — 1338, адрес байта — 14) в массив unsigned char MEMbuff[5];. Работать будем через Buffer 1.

1. Считать необходимую страницу во внутренний буфер. Адрес страницы 1338 = 00001010 01110100 или 0Ah 74h, остальные биты «0».


2. Прочитать из внутреннего буфера необходимые данные, начиная с адреса 14 = 00001110 или 0Eh, остальные биты «0».


1 Dummy Byte — 1 пустой (произвольный) байт необходим памяти для подготовки данных, этакая небольшая временная задержка.

После этих двух операций пять считанных байт, начиная с адреса 353246, будут записаны в массив MEMbuff.

А теперь попробуем записать те же пять байт в ту же область памяти. На этот раз работать будем через Buffer 2.

1. Считать необходимую страницу во внутренний буфер. Адрес страницы 1338 = 00001010 01110100 или 0Ah 74h, остальные биты «0».


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

2. Начиная с адреса 14 = 00001110 или 0Eh, произвести «модификацию» в буфере 2.


Хотя в даташите ничего не сказано про 1 Dummy Byte, правильно память заработала только с ним.

3. Записать буфер 2 в страницу памяти.

Проверка.

Так как модели для протеуса не нашлось, будем экспериментировать в железе.

Для индикации работы подключим к RC2 анод светодиода, катод на землю.

Проверочная программа.
1. Зажигает светодиод. Выполняет очистку памяти. После окончания очистки гасит светодиод.
2. Записывает 5 байт в память.
3. Читает 5 байт из памяти.
4. Считанные данные записываются в EEPROM память контроллера. Светодиод начинает мигать.

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

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