Опыт полезной модификации UEFI: возвращаем Thinkpad W520 законную поддержку быстрой памяти

Для начала предыстория:

Некоторое время назад я приобрёл на Ebay б/у ноутбук Lenovo Thinkpad W520. Как известно, W-серия — это очень мощные ноутбуки, железки в которых дадут фору многим более современным машинам. Конечно же, я начал его обустраивать под себя, и, в частности, решил проапгрейдить имеющуюся память всем, что было в наличии, а в наличии было немало: 2 свободные планки DDR3–1600 из старого ноута — 4 и 8 гигабайт. Посмотрев на то, что было установлено продавцом, я обнаружил, что из 3 установленных планок 2 — DDR3–1600, а одна — 1333. Учитывая, что первые две были по 8 гигабайт, а последняя — 2 гигабайта, от неё я и решил избавиться. Рассчитывая получить после апгрейда 8+8+8+4=28 гигабайт DDR3–1600 в рабочем ноуте, я залил всё слюной и всё это быстренько подключил. И получил 28 гигабайт… DDR3–1333. «Что за…», подумал я и полез в гугл.
После непродолжительных поисков я обнаружил, что как официально, так и неофициально W520 поддерживает только DDR3–1333, а владельцы более быстрой памяти зря тратили на неё деньги. Мне стало немного обидно за всех таких владельцев, и я решил попробовать избавиться от этой несправедливости, тем более, что контроллер памяти, нынче, как известно, в процессоре, и установленный в моей модели Intel Core i7 2720QM официально поддерживает DDR3–1600.

А теперь немного интересных подробностей.

Решение любой сложной задачи стоит начинать со сбора имеющейся информации. Тут мне довольно быстро повезло, нашлась интересная страничка на Thinkwiki, утверждавшая, что нужный мне модифицированный BIOS очень даже существует, но его делает какой-то ушлый мой соотечественник по имени Oleh, и просит за это 1500 рублей :) Я не против, есть спрос — есть и предложение, но мы вроде сами с усами, и прошить и припаять, и в коде разобраться умеем, попробуем сэкономить полтора килорубля на мороженное.
Собственно, задачу за меня почти решили, страничка на Thinkwiki содержит ссылку на довольно известный ресурс BIOS-Mods, где патч фактически подан на блюдечке, надо сделать так:

По смещению 0×1810 заменить последовательность байт

8B 85 C8 FC FF FF 8B 48 09 66 C7 41 01 35 05 C6 85 C2 FC FF FF 0C
на
66 0F 1F 44 00 00 0F 1F 00 66 0F 1F 44 00 00 0F 1F 80 00 00 00 00


Но при попытке этим решением воспользоваться «в лоб», меня ждал облом — такой последовательности байт в прошивке не оказалось совсем, ни по этому смещению, ни по какому-то другому.
Пришлось засучить рукава и копать глубже, то есть всё-таки разобраться, что же этот код делает, и на что его надо исправить.
Прежде всего, скажу, что последовательность 66 0F 1F 44 00 00 0F 1F 00 66 0F 1F 44 00 00 0F 1F 80 00 00 00 00 не делает ничего. То есть её с тем же успехом можно заменить на последовательность nop (0×90), как мне позже указал CodeRush, но это я забегаю вперёд. Пока примем к сведению, что эта последовательность именно отключает искомый код, а не меняет его.
Я исходил из того, что патч, который описан на BIOS-Mods — правильный, просто от другой модели ноутбука, и в моей что-то будет немного не так.
Исходный код для исправления в файле SandyBridgePhx.efi выглядит так:

hex asm
8B 95 C8 FC FF FF mov edx, [ebp+var_338]
8B 42 09 mov eax, [edx+9]
66 C7 41 01 35 05 mov word ptr [eax+1], 535h
C6 85 C2 FC FF FF 0C mov [ebp+var_33E], 0Ch


535h здесь — не что иное, как тактовая частота в мегагерцах, 1333 в десятичной системе счисления. Поскольку это характерное значение, я выковырял SandyBridgePhx.efi из прошивки UEFI Tool-ом, взял radare2 и поискал 535h в этом файле (не забываем про little endian):

Листинг radare2
[0x00000000]> /x 3505
Searching 2 bytes in [0x0-0x2406]
hits: 2
0x000017d6 hit0_0 3505
0x00001839 hit0_1 3505

Почему-то наши 1333 мегагерца встречаются 2 раза совсем рядом. Смотрим, что же там такое:

Смещение hex asm
0×000017c9 8B 85 C8 FC FF FF mov eax, dword [ebp — 0×338]
0×000017cf 8B 48 09 mov ecx, dword [eax + 9]
0×000017d2 ~ 66 C7 41 01 35 05 mov word [ecx + 1], 0×535
0×000017d8 C6 85 C2 FC FF FF mov byte [ebp — 0×33e], 0xc


Тильдой помечена строчка с искомым значением. Видно, что код в целом соответствует искомому, единственное что отличается — это регистры. В общем, вроде бы всё просто, но есть же второе значение… Идем по смещению 0×00001839 и видим там точно такую же последовательность команд, 1 в 1. WTF? Что патчить-то? Да и самое главное, почему мне надо именно отключить этот код, а не поменять 1333 на 1600, скажем? Для объяснения этого моих рудиментарных знаний x86 ассемблера не хватало. Я обратился за помощью к приятелю-реверсеру Rumata888. Он, в силу профессии, имеет доступ к нормальной лицензионной версии IDA Pro, чем не преминул воспользоваться :)
Буквально один скриншот из IDA, который объяснил мне всё:

38575bcb238640bd8914ba29b3652374.png

42Bh, 535h, 640h и 74Bh это, как вы уже наверное догадались, частоты памяти: 1067, 1333, 1600 и 1867 МГц соответственно. То есть это, понятное дело, блоки, которые выставляют нужную частоту памяти (скорей всего, читая её из SPD и сохранённых настроек). А вот дальше происходит самое интересное: вне зависимости от того, что мы там навыставляли, происходит переход по смещению 182Сh. И выполняется та самая вторая последовательность команд, которая сбила меня с толку. Дамы и господа, перед нами самый обыкновенный костыль: сначала добросовестно анализируем, что же за частоту надо выставить, а затем берём кувалду и намертво прибиваем 1333 МГц. Естественно, от костыля надо избавиться (что и сделали на BIOS-Mods), и после этого всё заработает.
После того, как ясность наступила, я, дабы подкрепить уверенность, попросил CodeRush, как главного публичного специалиста по UEFI, проверить, всё ли я сделал как надо, на что получил уже упомянутый совет, что всё выглядит нормально, но лучше выкинуть хитромудрый участок (66 0F 1F 44 00 00 0F 1F 00 66 0F 1F 44 00 00 0F 1F 80 00 00 00 00) и просто забить ненужный костыль nop-ами. Ну и рекомендацию предохраняться сделать бэкап, лучше программатором.
Дальше были затруднения, из-за которых патч пришлось отложить почти на месяц. Сначала я ждал посылку с программатором и прищепку для прошивки SPI-Flash без выпаивания, потом пытался после бэкапа программатором прошить модифицированный файл прошивки при помощи виндового софта. Сразу сообщаю: ни фига не вышло. По-видимому, шить можно только в случае разницы версий, а 1.42 поверх 1.42 фирменный софт шить не будет :(Пришлось сделать всё самым дубовым способом: поправить слитый из флэшки бэкап и залить его обратно программатором.

Краткая инструкция по самостоятельному исправлению фатального недостатка.

Теперь резюме, для тех, кому не хочется читать мою писанину выше, рабочий способ разблокировки памяти на Thinkpad W520(и других схожих моделях, в частности T520 и, вероятно, X220 и ещё какие-то модели на Sandy Bridge):

  1. Покупаем/ищем вышеупомянутые прищепку и программатор. Естественно, программатор можно использовать и другой, в частности очень популярные Raspberry Pi, а прищепку заменить выпаиванием-впаиванием микросхемы SPI Flash. Но это уж на ваше усмотрение — вдруг у вас навык пайки Grandmaster и паяльная станция всегда под рукой. Тем, кто с пайкой не дружит, прищепки очень рекомендую.
  2. Подключаем (или выпаиваем и подключаем) флэшку, не перепутайте подключение, 1-й провод на прищепке выделен, на микросхеме 1-й контакт отмечен точкой. Сама флэшка находится тут (отмечено стрелкой):
    0b138d560bfd444f8aaa425750808d4c.jpeg
    Инструкция, как туда добраться, с картинками
  3. Сливаем бэкап флэшки, на всякий случай проверяем, что он не пустой и размер правильный, флэшка у W520 размером 64Мбита, т.е. 8 мебибайт или мегабайт, если вы ретроград. Ещё лучше использовать функцию проверки у прошивальщика, если она есть. Если нет — может взять другой софт?
  4. Открываем Hex-редактор, ищем Hex-последовательность байт
    Вот такую

    8B 85 C8 FC FF FF 8B 48 09 66 C7 41 01 35 05 C6 85 C2 FC FF FF


    Находим её в бэкапе 4 раза. Последние два, что ближе к концу, трогать не стоит — они для инициализации памяти в аварийном режиме (файл SandyBridgePhxCrisis.efi, если вдруг кому интересно). Из первых двух, как следует из текста выше, нас интересует второе вхождение этой последовательности. У меня на W520 искомое место начинается со смещения 53A834h.
  5. Заменяем все указанные байты на nop (90h).
  6. Прошиваем свой бэкап обратно в флэшку, проверяем, что всё прошилось правильно (вот тут уж функция проверки абсолютно незаменима).
  7. Запаиваем флэшку обратно (или просто снимаем прищепку :)), собираем ноутбук, загружаемся, запускаем CPU-Z делаем вот такой скриншот:
    dabea02ed57246c4aa3ad2822a2f8b82.png
  8. PROFIT!

Выражаю благодарность CodeRush и Rumata888, без них бы этой заметки никогда бы не было, а я так и остался бы неучем.
Если кто-то возьмётся последовать моему примеру, готов опубликовать готовые рецепты (что менять, по какому смещению) для других моделей ноутбуков.

© Habrahabr.ru