Хабрахабр

[Перевод] Как втиснуть 16 ГБ памяти на материнскую плату, которая не поддерживает такой объём

Некоторое время назад я поставил на один из своих компьютеров 16 ГБ памяти. На нём стоит материнка Foxconn P55MX с Core i5 750. Можно было бы и заменить этот старый CPU, но он пока нормально работает и делает всё, что мне нужно.

Материнская плата официально не поддерживает 16 ГБ RAM. Вот что интересно. На плате только два слота, поэтому у меня возникло подозрение, что планки 8 ГБ просто были редкостью в то время, когда вышла материнская плата. Спецификации на вышеупомянутой странице указывают, что поддерживается максимум 8 ГБ. Во многих случаях материнские платы поддерживают больше RAM, чем официально заявляет производитель.
Я убедился, что установлена последняя версия BIOS (версия 946F1P06) и вставил две своих планки по 8 гигабайт. Я всё равно решил попробовать. 04, и всё работало отлично. Затем загрузил Ubuntu 16. Мне нравилось работать с дополнительной RAM и я был счастлив, что игра окупилась. Я решил, что моя теория о том, что плата поддерживает больше памяти, чем заявлено в документации, оказалась правильной, и забыл об этом.

В основном, компьютер работает под Linux. Но несколько месяцев спустя я попытался загрузить Windows 10. Тогда-то и началось самое интересное. Лишь иногда нужно загрузить Windows, чтобы что-то проверить.

Экран загрузки Windows ненадолго появился, а затем меня сразу приветствовал синий экран смерти. Когда появился GRUB, я выбрал в меню Windows 10 и нажал Enter.

Я много погуглил и выяснил, что суть в какой-то проблеме с ACPI-таблицами в BIOS. Стоп-код: ACPI_BIOS_ERROR. Попытка загрузки с установочного USB-накопителя Windows привела к той же ошибке. Среди прочего, таблицы ACPI сообщают операционной системе, как настроить оборудование. Этот компьютер действительно не поддерживает 16 ГБ оперативной памяти. Так что, думаю, Foxconn не врал. Тесты RAM тоже прошли отлично, так что дело не в плохой планке памяти. Возврат к 8 ГБ привёл к успешной загрузке.

Адрес электронной почты указан на их сайте, но он не работает. Я попытался связаться с поддержкой Foxconn насчёт исправления BIOS, но не получил ответа. Похоже, они также не оказывают поддержки. Возможно, Foxconn больше не занимается материнскими платами.

Но я не хотел так легко сдаваться. В этот момент обычный человек просто сдался бы, смирившись с 8 ГБ памяти или купив новый компьютер. Поэтому я начал изучать ACPI и экспериментировать с настройками BIOS. Я знал, что теоретически компьютер может использовать 16 ГБ, потому что он отлично работал в Linux.

Одним из параметров была «функция переназначения памяти» (Memory Remap Feature). Я нашёл интересный раздел BIOS, где можно играть с некоторыми настройками памяти. В документации по BIOS говорится, что опция позволяет «перекрывать память PCI» для мэппинга выше общей физической памяти. Она была включена. Просто ради эксперимента я её отключил, и Windows реально загрузилась! Поиск в интернете указал, что его нужно включить при загрузке в 64-разрядной ОС. Но это было приятно: у меня появился способ попасть в Windows без необходимости физически удалять планку памяти. Однако сказала, что может использовать менее 4 ГБ оперативной памяти.

С отключённой функцией переназначения памяти она ограничила меня менее чем 4 ГБ RAM. В Ubuntu то же самое. Я решил подробнее изучить ошибку ACPI_BIOS_ERROR и её причины — и наткнулся на этот отличный документ для отладки драйверов Microsoft, который объясняет проверку ошибок ACPI_BIOS_ERROR. На данный момент я был уверен, что происходит какая-то проблема с отображением памяти.

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

BSOD теперь показал четыре дополнительных кода в верхнем левом углу: После соответствующей правки реестра я снова включил функцию переназначения памяти в BIOS и загрузил Windows.

Таким образом, параметр 1 равен 0x0000000000000002. Прекрасно! Поскольку параметры 2, 3 и 4 выглядят как сумасшедшие значения, это, наверное, указатели. В документации Microsoft написано, что параметр 1, равный 0x02, означает проблему с обработкой списка ресурсов для корневых шин PCI. И если тут одни указатели, Microsoft говорит, что проблема в том, что область декодирования PCI перекрывается со списком областей памяти, возвращаемых интерфейсом BIOS E820.

Информации много, но можно с чего-то начать исследование. Окей. Затем вернулся в Linux и просмотрел всю информацию о запуске ядра с помощью команды dmesg, уделяя особое внимание E820 и ACPI. Я нашёл информацию, как вызов E820 BIOS предоставляет информацию об областях памяти. Вот что нашлось:

BIOS-e820: [mem 0x0000000000000000-0x000000000009ebff] usable
BIOS-e820: [mem 0x000000000009ec00-0x000000000009ffff] reserved
BIOS-e820: [mem 0x00000000000e0000-0x00000000000fffff] reserved
BIOS-e820: [mem 0x0000000000100000-0x00000000cf77ffff] usable
BIOS-e820: [mem 0x00000000cf780000-0x00000000cf78dfff] ACPI data
BIOS-e820: [mem 0x00000000cf78e000-0x00000000cf7cffff] ACPI NVS
BIOS-e820: [mem 0x00000000cf7d0000-0x00000000cf7dffff] reserved
BIOS-e820: [mem 0x00000000cf7ed000-0x00000000cfffffff] reserved
BIOS-e820: [mem 0x00000000fee00000-0x00000000fee00fff] reserved
BIOS-e820: [mem 0x00000000ffb00000-0x00000000ffffffff] reserved
BIOS-e820: [mem 0x0000000100000000-0x000000042fffffff] usable

Позже я увидел это:

acpi PNP0A08:00: ignoring host bridge window [mem 0x400000000-0xfffffffff
window] (conflicts with System RAM [mem 0x100000000-0x42fffffff])
PCI host bridge to bus 0000:00
pci_bus 0000:00: root bus resource [io 0x0000-0x0cf7 window]
pci_bus 0000:00: root bus resource [io 0x0d00-0xffff window]
pci_bus 0000:00: root bus resource [mem 0x000a0000-0x000bffff window]
pci_bus 0000:00: root bus resource [mem 0x000d0000-0x000dffff window]
pci_bus 0000:00: root bus resource [mem 0xd0000000-0xdfffffff window]
pci_bus 0000:00: root bus resource [mem 0xf0000000-0xfed8ffff window]
pci_bus 0000:00: root bus resource [bus 00-ff]

Ага! Видите предупреждение о конфликте? Я бы его не заметил, но после установки памяти Linux начал выводить это сообщение при каждой загрузке. Я попытался загрузиться в Linux с отключённой функцией переназначения памяти в BIOS. В этом случае пропала последняя область e820 с 0x100000000 до 0x42fffffffff, и, таким образом, сообщение о конфликте также исчезло, а в списке появился ещё один «корневой ресурс шины» с окном главного моста с 0x400000000 до 0xfffffffff.

Linux работает с 16 ГБ, потому что замечает конфликт и игнорирует конфликтующий диапазон PCI, который предоставляет ACPI, в то время как Windows с отвращением поднимает руки и выблёвывает синий экран: «У вашего BIOS проблема!» Не могу винить Windows. Итак, что получается. Действительно, существует перекрытие, поэтому можно понять, что она путается.

Последние 768 МБ памяти с 0x400000000 до 0x42fffffffff сопоставлены с началом огромной области пространства памяти, которую материнская плата использует для PCI. В этот момент я не был уверен, стоит ли продолжать. Таким образом, материнская плата поддерживает только 15,25 ГБ RAM, правильно? Ясно, что если материнская плата ожидает там PCI, может произойти что-то действительно плохое.

Что, если как-то изменить таблицы ACPI, чтобы большой диапазон PCI начинался с 0x430000000 вместо 0x400000000, то есть сразу после окончания физической RAM. Но… в Linux всё работает просто отлично, без поддержки этой дополнительной области сопоставления PCI! Тогда конфликт бы исчез, а большая часть окна сопоставления PCI по-прежнему осталась доступной.

Вызов принят.

К счастью, Linux позволяет очень легко делать дампы. Я начал копаться в таблицах ACPI. Есть специальные инструменты для этого, но таблицы можно легко найти в sysfs:

/sys/firmware/acpi/tables

Вот они все. Меня также обрадовало, что в GRUB есть возможность заменить ваши таблицы ACPI новыми версиями. Поэтому если выяснить, какая таблица задействована, можно установить новую версию этой таблицы с помощью GRUB. Теоретически, Windows будет счастлива этим.

Скорее всего, это значение с обратным порядком байтов (little-endian) и размером 64 бита, так что я запустил binwalk для поиска по всем файлам таблиц: Среди других инструментов, я использовал iasl, чтобы разобрать различные таблицы ACPI и найти значение 0x400000000 для замены.

binwalk -R '\x00\x00\x00\x00\x04\x00\x00\x00' *

В таблице OEMB нашёлся один результат. Следующее 64-разрядное слово после него было 0x1000000000, чуть больше, чем конечный адрес в сообщении конфликта окна главного моста. Очень многообещающая зацепка. Таблица OEMB является специальной, поскольку в соответствии со спецификациями ACPI это не стандартная таблица. Linux жалуется на недействительную контрольную сумму, но я не думаю, что это имеет значение. Полагаю, вы догадываетесь, что я сделал дальше.…

Эту изменённую копию я поместил в файл /boot/oemb.dat. Я сделал копию таблицы OEMB, заменив байт 0x00 непосредственно перед байтом 0x04 на 0x30, чтобы изменить значение на 0x430000000 (помните, что это обратный порядок). Затем использовал GRUB для замены таблицы OEMB моей копией, временно вставив следующую команду в список команд загрузки (ввод буквы 'e' в GRUB после выбора Ubuntu):

acpi --exclude=OEMB /boot/oemb.dat

Идея в том, что она говорит GRUB загрузить все таблицы ACPI, кроме таблицы OEMB, а затем загрузить содержимое /boot/oemb.dat и добавить его в качестве дополнительной таблицы. Это эффективно заменит старую таблицу OEMB новой таблицей OEMB.

Хорошо, я загрузил Linux и…

acpi PNP0A08:00: ignoring host bridge window [mem 0x400000000-0xfffffffff
window] (conflicts with System RAM [mem 0x100000000-0x42fffffff])

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

Отслеживание показало, что таблица DSDT должна содержать метод под названием _CRS, который отвечает за создание этой таблицы. На этот раз я решил использовать iasl для декомпиляции таблицы DSDT.

iasl -d DSDT

В файле .dsl я действительно нашёл метод _CRS, связанный с шиной PCI, и он выглядел довольно сложным. Таблица DSDT содержит фактический код, поэтому значения таблицы искать непросто. В моем случае метод _CRS оказался довольно сложным. Я интерпретировал код насколько смог и понял, что метод _CRS загружает информацию из другой таблицы в памяти, начиная с 0xCF78E064. Я снова посмотрел лог загрузки Linux в dmesg и нашёл это:

ACPI: Early table checksum verification disabled
ACPI: RSDP 0x00000000000F9820 000014 (v00 ACPIAM)
ACPI: RSDT 0x00000000CF780000 000044 (v01 012110 RSDT0821 20100121 MSFT 00000097)
ACPI: FACP 0x00000000CF780200 000084 (v01 012110 FACP0821 20100121 MSFT 00000097)
ACPI: DSDT 0x00000000CF780460 006FE7 (v01 946F1 946F1P06 00000000 INTL 20051117)
ACPI: FACS 0x00000000CF78E000 000040
ACPI: APIC 0x00000000CF780390 00008C (v01 012110 APIC0821 20100121 MSFT 00000097)
ACPI: MCFG 0x00000000CF780420 00003C (v01 012110 OEMMCFG 20100121 MSFT 00000097)
ACPI: OEMB 0x00000000CF78E040 000082 (v01 012110 OEMB0821 20100121 MSFT 00000097)
ACPI: HPET 0x00000000CF78A460 000038 (v01 012110 OEMHPET 20100121 MSFT 00000097)
ACPI: GSCI 0x00000000CF78E0D0 002024 (v01 012110 GMCHSCI 20100121 MSFT 00000097)
ACPI: DMAR 0x00000000CF790100 000090 (v01 AMI OEMDMAR 00000001 MSFT 00000097)
ACPI: SSDT 0x00000000CF7917C0 000363 (v01 DpgPmm CpuPm 00000012 INTL 20051117)

Ага! Он загружает информацию из таблицы OEMB. Моя догадка была верна с самого начала. Так почему же не сработала замена таблицы OEMB?

Чего я не понимал, так это того, что если вы пытаетесь изменить таблицы, то GRUB перемещает большинство из них, включая OEMB, в другую область памяти. Я снова посмотрел на лог dmesg после замены таблицы OEMB. Поэтому-то новая таблица не видна системе, она по-прежнему смотрит на исходную таблицу. Проблема в том, что таблица DSDT жёстко закодирована на просмотр адреса 0xCF78E064 для таблицы OEMB. Ух.

Первым порывом было изменить DSDT, чтобы перевести её на новое расположение таблицы OEMB, но я чувствовал, что в перспективе это плохая идея, потому что новые версии GRUB могут изменить местоположение в памяти, где хранится пользовательская таблица OEMB.

В GRUB есть эквиваленты команд write_byte, write_word, write_dword и read_. Я остановился на другой идее. В наше время BIOS'ы сжатые. Что если GRUB на лету будет изменять исходную таблицу OEMB? Вероятно, таблицы загружаются в RAM, поэтому теоретически можно изменять значения.

В качестве временного теста я добавил следующую команду в последовательность загрузки GRUB: Так я и сделал.

write_byte 0xCF78E0B5 0x30

Я не обновлял контрольную сумму таблицы OEMB, потому что Linux и так скулил, что контрольная сумма неверна, поэтому, очевидно, ему всё равно. Она заменяет байт 0x00 непосредственно перед байтом 0x04 на значение 0x30, преобразуя 64-разрядный начальный адрес PCI этого конечного диапазона в 0x0000000430000000.

Я перезагрузился в Linux и с тревогой проверил лог dmesg для PCI.

PCI host bridge to bus 0000:00
pci_bus 0000:00: root bus resource [io 0x0000-0x0cf7 window]
pci_bus 0000:00: root bus resource [io 0x0d00-0xffff window]
pci_bus 0000:00: root bus resource [mem 0x000a0000-0x000bffff window]
pci_bus 0000:00: root bus resource [mem 0x000d0000-0x000dffff window]
pci_bus 0000:00: root bus resource [mem 0xd0000000-0xdfffffff window]
pci_bus 0000:00: root bus resource [mem 0xf0000000-0xfed8ffff window]
pci_bus 0000:00: root bus resource [mem 0x430000000-0xfffffffff window]
pci_bus 0000:00: root bus resource [bus 00-ff]

Успех! Окно 0x430000000-0xfffffffffff появилось как новое допустимое окно в списке, и предупреждение о конфликте исчезло. После проверки, что Linux всё ещё работает нормально, я попытался загрузиться в Windows с тем же хаком.

Теперь я могу загрузиться в Windows с 16 ГБ RAM, если использовать GRUB в качестве загрузчика с вышеупомянутой командой write_byte. Сработало! И если я когда-нибудь переустановлю Windows, вероятно, придётся временно вытащить одну планку RAM, чтобы загрузился установщик. Загрузчик Windows 10, очевидно, не будет работать. Но это работает!

Чтобы навсегда добавить исправление в GRUB, я создал файл /etc/grub.d/00_patchbios следующего содержания:

# This file patches the BIOS in my Foxconn P55MX motherboard to work
# properly when I have 16 GB of RAM installed. It's a nasty hack.
# Basically, the BIOS is hardcoded in the OEMB ACPI table
# to have a PCI address range from 0x400000000 to 0xfffffffff, but
# that overlaps with 16 GB of RAM being installed, because the RAM
# uses up (among other ranges) 0x100000000 to 0x42fffffff.
# This patch changes the table to actually list a PCI range of:
# 0x430000000 to 0xfffffffff
echo "write_byte 0xCF78E0B5 0x30"

Затем сделал скрипт исполняемым и запустил sudo update-grub. Теперь патч автоматически применяется при запуске GRUB.

Действительно ли материнская плата аппаратно запрограммирована смотреть на эти адреса для PCI или что-то ещё. Честно не знаю, насколько это безопасно. Поскольку Linux отлично работает с 16 гигабайтами RAM, меня эти вопросы не слишком беспокоят. Знаю только, что она проходит мои тесты RAM. Очевидно, что ваша система может отличаться, и этот хак нельзя напрямую применить на других материнских платах, потому что таблица ACPI у каждого BIOS немного отличается. Возможно, если установлено больше карт PCI/PCIe или что-то ещё, то возникнут проблемы, но в моём случае, похоже, всё в порядке.

Надеюсь, вы что-то узнали из этого поста. Думаю, это был весёлый опыт, чтобы поделиться с миром! Конечно, я и сам многому научился в процессе.

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»