Comments 17
Для UEFI платформы первый эксперимент провалился, а дальше пока не было у меня времени и плат для экспериментов. Провалился по простой причине — в современных микросхемах с большой ёмкостью используется QuadSPI, с этом режиме третий вывод не используется для защиты от записи, а используется для чтения-записи в режиме Quad. Возможно, это не у всех микросхем есть такая функция. Но на той 25Q64FVAIQ не получилось сделать защиту, нужна микосхема без IQ в конце, или какая-то другая. Время появится — буду пробовать ещё.
Меня больше интересует не то, как эмулировать NVRAM или как вынести его на отдельную мс, а тот случай, когда NVRAM уже содержит нужные значения, и при этом закрыть возможность записи — будет ли корректно работать UEFI, загрузчик, ОС? Да пусть даже не совсем корректно (например, нельзя будет изменить порядок загрузки или ещё что) — но хотя бы просто смогла бы запуститься ОС.
У меня такая система уже есть и работает, но в ней изначально задумывалось посадочное место под второй чип. Можно сделать такую же конфигарацию на системах AMD с одним чипом, либо договариваться с Intel об отключении ME, но тут мне уже NDA не позволяет продолжать.
Мне кажется, что и оставлять для записи даже отдельную мс не нужно — пару раз попадались десктопные платы, которые не стартовали нормально из-за мусора (нет, не в SPI флешке) в CMOS, или даже не мусора, а каких-то некорректных записей. Так же и с NVRAM может случиться — если вдруг содержимое каких-то переменных будет испорчено — кто знает, как себя поведёт UEFI — хорошо, если просто не загрузится, а ведь может и что-то аппаратно испортить (ну там частоты не те выставить или ещё что).
Получается, если хотим полностью защищённую систему от записи — нужно систему на BIOS, с чипом SPI, и ОС на SD-карте с блокировкой от записи (и проверенный картридер, а то некоторые игнорируют защёлку).
Ещё одна мысль пришла — а почему производители не учитывают тот факт, что в SPI чипах можно выбрать несколько разных регионов для защиты от записи, при этом остальное будет незащищено? Тогда можно было бы основную firmware писать в те области, которые будут после записи защищены, а для NVRAM и прочего выделять место, которое будет доступно для записи, ну и после этого WP# на землю. При этом можно что угодно сколько угодно писать, и основная прошивка никак не пострадает. Хотя с учётом того, что бывает просто износ, и UEFI проверяет корректность записи… тоже мало что даст.
Платы, не стартующие из за мусора в CMOS, я тоже видел, причем иногда они даже но кнопку питания не реагируют, до того залипли. С NVRAM шансы на такой исход пониже, все-таки там форматы поинтереснее, чем голый SRAM, и валидация похитрее.
Польностью защищенную систему стоит собирать на процессоре AMD без PSP (на eKabini, например), вместо BIOS лучше использовать Coreboot/Libreboot с reproducible builds, а NVRAM не использовать вообще, задавая все настройки при компиляции.
Почему не пользуются защитой отдельных регионов? Никто заморачиваться не хочет, т.к. тот же уровень защиты обеспечивают PR-регистры чипсета, которые при этом намного проще программируются и от производителя SPI-чипа не зависят.
— «договариваться с Intel об отключении ME» нам Интел ответил «увы это на всегда» (вырезали/вычистили все, но система получала резет каждые 30 минут)
По поводу МЕ — NDA мешает рассказывать, что Intel сказал нам, могу только сказать, что на рынке Embedded и Industrial достаточно плат с отключенным МЕ, только танцев с бубном вокруг его отключения и работы системы без него с каждым поколением становится все больше.
Как устроен механизм wear leveling для SPI-NOR flash микросхем?
Зависит от реализации, но в большинстве своем достаточно тупое, зато работающее "пишем в хранилище, пока оно не заполнилось, когда заполнилось - убираем мусор". Биты на NOR flash можно менять произвольно только с Erase Polarity (т.е. с 1) на 0, поэтому переменные делают так, чтобы их статут с "валидная" на "удалена" можно было менять сменой одного бита с 1 на 0, для этого обычно и используется поле State в заголовке, о котором вы спрашивали в соседней теме. Т.е. запись в переменную получается в виде "прошелся по всем переменным, если нашел уже одну с таким же именем, и она валидная - поставь ей признак "удаляется прямо сейчас", найди место под новую переменную в конце хранилища, если оно есть - пиши туда заголовок новой переменной, если записался - ставь признак "заголовок валидный", пиши новое тело переменной, если записалось, ставь признак "тело валидное", если все нормально - ставь в предыдущую переменную в заголовок признак "удалена", и так далее. Если место кончилось, можно все валидные переменные из хранилища по одной переписать в память, или в другую копию хранилища (обычно их не менее двух, потому что так они переживают повреждение заголовка у самого хранилища, а не только у переменных).
Надеюсь, что смог на пальцах пояснить, если нет - исходники стандартной реализации из EDK2 доступны для изучения:
https://github.com/tianocore/edk2/blob/1f026ababf350746c6071c0873d9d1c8824029ca/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c и вокруг - драйвер для UEFI NVRAM.
https://github.com/tianocore/edk2/blob/7c0ad2c33810ead45b7919f8f8d0e282dae52e71/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.c - отдельный FTW-драйвер, используемый предыдущим для безопасной работы процедуры Reclaim, т.е. сборки мусора, для которой не нужно дополнительное хранилище такого же размера, а достаточно меньшего по размеру FTW-блока.
Устройство NVRAM в UEFI-совместимых прошивках, часть вторая