Преди известно време инсталирах на един от своите компютри 16 GB оперативна памет. Дънната платка е Foxconn P55MX с Core i5 750. Можеше и да заменя този стар процесор, но засега той работи добре и е достатъчен за работата, която му възлагам.

Но ето кое е интересното. Това дъно официално не поддържа 16 GB оперативна памет. В спецификацията на производителя се казва, че максимумът RAM за нея е 8 GB. На самото дъно има два слота за оперативна памет и ми се стори, че 8 GB плочки са били рядкост по времето на неговото излизане и може би може нещо да се направи. Както и да е, реших да пробвам. В много случаи дънните платки поддържат повече памет, отколкото заявява производителят.

Убедих се, че съм инсталирал най-новата версия на BIOS (версия 946F1P06) и поставих две плочки по 8 GB. След това заредих Ubuntu версия 16.04 и всичко заработи перфектно. Тоест, моята теория, че дъното поддържа повече оперативна памет, отколкото е показано в спецификациите, се оказа вярна. Бързо забравих за това. Хареса ми да работя с толкова RAM и се зарадвах, че моите усилия дадоха такъв резултат.

Но след няколко месеца на същата конфигурация се наложи да пусна Windows 10. Компютърът основно работи под управлението на Linux, но в един от дяловете на диска инсталирах Windows 10, за да тествам нов софтуер. И точно тук започна най-интересното.

Когато на екрана излезе GRUB, аз избрах в менюто Windows 10 и натиснах Enter. Началният екран на Windows се появи за момент и веднага след това излезе синия екран на смъртта.

Стоп кодът бе ACPI_BIOS_ERROR. Потърсих в Google и си изясних, че това е някакъв проблем с ACPI таблиците в BIOS-а. Между другото, именно ACPI таблиците съобщават на операционната система как да работи с хардуера на конфигурацията. Опитът да започна да инсталирам Windows 10 от флаш стик доведе до същата грешка. Ясно е, че Foxconn правилно е указал в спецификациите, че тази дънна платка поддържа само 8 GB RAM. Извадих едната плоча 8 GB оперативна памет и това веднага доведе до успешно зареждане. RAM тестовете също минаха отлично и стана ясно, че моите планки памет работят както трябва.

Опитах се да се свържа с техническата поддръжка на Foxconn за някакво обновява на BIOS-а, но не получих отговор. Адресът на електронната поща е даден в сайта на компанията, но не работи. Може би Foxconn вече не се занимава с дънни платки и няма поддръжка.

Друг на мое място навярно би се предал и би се примирил с 8 GB RAM или би си купил направо нов компютър. Но аз си знам, че това дъно дълго време си работеше перфектно с 16 GB в средата на Linux. Реших да задълбоча в ACPI и пробвам с някои настройки на BIOS-а.

Намерих интересна опция в BIOS-а, даваща възможност за промяна на някои настройки на паметта. Един от тези параметри е „функцията за пренастройване на паметта“ (Memory Remap Feature), която бе включена. В документацията на BIOS-а пише, че тази опция дава възможност за скриване на паметта, необходима за PCI, в областта над общата физическа памет. Търсенето в интернет показа, че тази опция трябва да е включена при използването на 64-битова операционна система. Просто заради експеримента изключих тази опция. Windows веднага зареди. Но каза, че може да използва по-малко от 4 GB RAM. Но и това си е приятно: намерих начин да вляза в ОС Windows, без да отварям компютъра и да вадя едната RAM планка.

В Ubuntu се получи съвсем същото. С изключена функция за преназначаване на паметта, системата ме ограничи до 4 GB. Реших по-подробно да изуча грешката ACPI_BIOS_ERROR и причините за нейното възникване. Натъкнах се на ето този отличен документ за настройка на драйверите на Microsoft, който обяснява как да се направят проверките на грешката ACPI_BIOS_ERROR.

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

След добавянето на нов запис в системния регистър, отново включих в BIOS-а функцията за преназначаване на паметта и стартирах Windows 10. Този път BSOD показа четири допълнителни кода в горната лява част на екрана:

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

Добре. Информацията е много, на места не е съвсем ясна, но нека да започнем отнякъде. Намерих данни, как извикването E820 на BIOS предоставя информация за областите памет. След това се върнах в Linux и прегледах с помощта на dmesg цялата информация за зареждането и стартирането на ядрото, като особено внимание отделих на E820 и ACPI. Ето какво намерих:

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 с изключена функция за преназначаване на паметта. В този случай изчезна последната e820 област – от 0x100000000 до 0x42fffffffff. Съобщението за конфликт изчезна, но се появи нов хардуерен ресурс с прозорец в паметта от 0x400000000 до 0xfffffffff.

Какво в крайна сметка разбрахме? Linux работи с 16 GB, понеже не обръща внимание на конфликта и игнорира проблемния PCI диапазон, предоставен от ACPI, докато Windows с отвращение вдига ръце и пуска синия екран, който казва „Имате проблем с BIOS-а„. Не можем да обвиняваме Windows понеже наистина има припокриване и е възможно объркване.

Тук вече си помислих да се откажа. Последните 768 MB памет на адреси от 0x400000000 до 0x42fffffffff са съпоставени с началото на огромна област от паметта, която дънната платка използва за PCI. Ясно е, че ако дънната платка е в очакване там да има PCI, то нещата могат много да загрубеят. Но това означава, че това дъно поддържа 15,25 GB RAM. Нали така?

Но чакайте, в Linux всичко работи просто отлично, без тази допълнителна област за съпоставяне с PCI. А какво ще стане, ако някак променим ACPI таблиците по такъв начин, че големият диапазон за PCI да започне от 0x430000000 вместо от 0x400000000, тоест, точно след края на физическата оперативна памет. В този случай конфликтът би изчезнал, а по-голямата част от блока памет, необходим за PCI, ще си остане достъпен.

Ръкавицата е хвърлена

Задълбочих се в ACPI таблиците. За щастие Linux дава възможност за съвсем лесното правене на дъмпове и дори има специални инструменти за това. А таблиците съвсем лесно могат да бъдат видени и в sysfs:

/sys/firmware/acpi/tables

Тук са всичките. Много се зарадвах, когато разбрах, че GRUB дава възможност за замяна на ACPI таблиците с други техни версии. Ето защо, ако разберем коя точно таблица се използва, можем да поставим нейната модифицирана версия в GRUB. На теория, по този начин и Windows ще работи без никакви проблеми.

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

binwalk -R 'x00x00x00x00x04x00x00x00' *

В OEMB излезе едно съвпадение. Следващата 64-битова дума след него бе 0x1000000000, което е малко повече от крайния адрес в съобщението за конфликт. Изключително многообещаващо. OEMB таблицата е специална, понеже в съответствие със ACPI спецификациите, това не е стандартна таблица. Linux показва съобщение за недействителна контролна сума, но не мисля, че това има някакво значение. Предполагам разбрахте какво направих по-нататък.

Направих копие на OEMB таблицата и замених байта 0x00, намиращ се непосредствено преди байтовете 0x04, с 0x30, за да променя значението на 0x430000000 (помним, че работим с little-endian). Това променено копие записах във файла /boot/oemb.dat. След това използвах GRUB за подмяна на оригиналната таблица с моето копие, като временно вмъкнах съответната команда като точка от стартовото меню чрез следния ред:

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

Идеята е в това, че този ред казва на GRUB да зареди всички ACPI таблици, освен таблицата OEMB, а след това да зареди файла /boot/oemb.dat, като по този начин става замяната на старата OEMB таблица с новата.

ОК, заредих Linux и…

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

Проклетото съобщение остана. WTF? Предположих, че PCI диапазонът е зададен още някъде, но никъде не открих конкретни адреси. Проверих, че новата OEMB таблица успешно се е заредила и продължих да изследвам нещата.

Този път реших да използвам iasl за декомпилиране на DSDT таблицата. Обследването показа, че DSDT трябва да съдържа метод с име _CRS, който е отговорен за създаването на тази таблица.

iasl -d DSDT

Във файла .dsl наистина намерих метода_CRS, свързан с PCI шината, но всичко бе твърде сложно. DSDT таблицата съдържа код и не е лесно да се открият необходимите значения. Интерпретирах този код доколкото можах и видях, че метода_CRS зарежда информация от друга таблица в паметта, която започва от 0xCF78E064. Отново погледнах лога за зареждането на Linux и намерих следното:

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) в друга област на паметта. Проблемът е в това, че в DSDT таблицата твърдо е зададен адресът 0xCF78E064, където би трябвало да е OEMB. И системата не вижда моята нова таблица.

Лесно бих могъл да променя DSDT, която да указва към новата OEMB таблица. Но това е лоша идея, понеже може да излезе нова версия на GRUB, в която местоположението на OEMB таблицата е променено.

Спрях се на друга идея. В GRUB има вградени команди за четене и запис на байтове, думи и двойни думи. А какво би станало, ако по време на зареждането GRUB самостоятелно си променя OEMB таблицата? В наши дни, всички BIOS-и са компресирани и най-вероятно тези таблици се декомпресират в оперативната памет и теоретично е възможно да бъдат променяни техните значения.

Така и направих. Като временен тест добавих в GRUB следната команда:

write_byte 0xCF78E0B5 0x30

Тя подменя байта 0x00, който се намира непосредствено преди байта 0x04, със значението 0x30, като по този начин първоначалният 64-битов адрес стана 0x0000000430000000. Не обнових контролната сума, понеже Linux и така издаваше съобщения, че тя не е вярна, и спира дотам.

Рестартирах в 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]

Пълен успех! RAM прозорецът 0x430000000-0xfffffffffff се появи като допустим и предупреждението за конфликт изчезна. След като направих необходимите тестове и се убедих, че Linux работи перфектно, опитах да заредя Windows с помощта на същия трик.

Работи идеално! Сега мога да зареждам на това дъно Windows 10 с 16 GB RAM, ако използвам GRUB, в който съм поставил описаната по-горе команда write_byte. Зареждащата програма на Windows 10 очевидно няма да работи. Ясно е, че ако реша да преинсталирам Windows, ще трябва да извадя едната планка памет. Но ето че сега всичко работи и ме устройва!

За да оставя направените от мен временни промени постоянни, създадох файла /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.

Може би това не е съвсем безопасно. Но виждам, че абсолютно всички RAM тестове минават без проблеми. Тъй като Linux отлично работи с 16 GB оперативна памет, тези въпроси не ме безпокоят особено много. Може би, ако бъдат инсталирани няколко PCI/PCIe карти плюс още нещо друго, може да възникнат проблеми, но в моя случай всичко е наред. Очевидно е, че вашата система ще бъде по-различна и точно това хакване няма да може директно да се използва. Но методът ще е същият.

Мисля, че това бе интересно проучване, което си струва да бъде споделено. Надявам се, че и вие разбрахте нещо от този пост. А аз научих много от всичко това и ще използвам наученото в своята работа.