# Исправление Slide значений KASLR

  • Поддерживаемая версия: 0.6.9

Этот раздел предназначен для пользователей, которые хотят понять и исправить ошибки "Couldn't allocate runtime area". Чаще всего это встречается на Z390, X99 и X299.

  • Примечание: Clover больше не поддерживается в этом руководстве, поэтому требуется OpenCore

# Что же такое KASLR?

Что ж, KASLR расшифровывается как Kernel adress space layout randomization (рус. - Рандомизация размещения/компоновки адресного пространства ядра), используется в целях безопасности. В частности, это делает более сложным определение расположения важных объектов в памяти, поскольку это всегда случайное значение как между компьютерами, так и между загрузками. Более подробное описание KASLR (opens new window)

Это становится проблемой, когда вы вводите (introduce) устройства либо с небольшими Memory Map, либо же просто вводите слишком много устройств. Вероятно, есть место для работы ядра, и также есть свободное место, куда ядро не помещается полностью. Здесь подходит slide=xxx. Вместо того, чтобы позволить macOS выбирать случайную область для работы при каждой загрузке, мы ограничим ее той областью, которая, как мы знаем, будет работать.

# И для кого эта информация?

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

Error allocating 0x1197b pages at 0x0000000017a80000 alloc type 2
Couldn't allocate runtime area

С различными вариациями:

Only 244/256 slide values are usable!

Или даже краш во время работы macOS:

panic(cpu 6 caller 0xffffff801fc057ba): a freed zone element has been modified in zone kalloc.4096: expected 0x3f00116dbe8a46f6 but found 0x3f00116d00000000

Веселая часть этих ошибок заключается в том, что они могут быть случайными, а также причина, почему 20-кратное включение и выключение питания вашего ПК также может решить проблему, но только временно.

Забавный факт: Поиск области для работы занимает около 31 мс, а ручная установка слайд значения может в среднем сократить время загрузки на 0.207%!!!

# Так как же мне это исправить?

Реальное решение этой проблемы довольно простое. Что вам понадобится:

И также нам нужно настроить наш config.plist -> Booter:

  • AvoidRuntimeDefrag: YES
    • Исправляет рантайм сервисы UEFI, такие как дата, время, NVRAM, управление питанием, т.д.
  • DevirtualiseMmio: YES
    • Уменьшает объем Stolen памяти, расширяет возможные значения для slide=N, и очень полезен при устранении проблем с выделением памяти (Memory Allocation) на Z390.
  • EnableSafeModeSlide: YES
    • Позволяет использовать слайд значения в безопасном режиме
  • ProtectUefiServices: NO
    • Защищает UEFI сервисы от переопределения прошивкой, в основном актуально для виртуальных машин, 300 серии и более новых систем, таких как Ice Lake и Comet Lake
  • ProvideCustomSlide: YES
    • Это даёт уверенность, что ядро будет выбирать только хорошие регионы и избегать тех, которые могут привести к сбоям загрузки. Оно по-прежнему выбирает случайно, но не включает плохие регионы при рандомизации.
  • RebuildAppleMemoryMap: YES
    • Создает Memory Map совместимую с macOS, может сломаться с некоторыми OEM прошивками ноутбуков, поэтому если вы получили сбой ранней загрузки - отключите это. Это гарантирует, что наша Memory Map будет соответствовать соответствовать тому, что ожидает ядро.

# Подготовка BIOS

Причина, по которой нам нужно сбросить Memory Map, заключается в том, что мы хотим, чтобы она была более детерминированной, имеется ввиду, чтобы при каждой загрузке было меньше вариаций, поэтому у нас будет меньше крайних случаев (Memory Map не всегда согласованы при загрузке). Для подготовки:

  • Обновите BIOS(чрезвычайно важно, поскольку известно, что ранние версии BIOS имеют проблемы с Memory Map, в особенности Z390)
  • Сбросьте CMOS
  • Включите необходимые настройки в BIOS:
    • Above4GDecoding: Позволяет устройствам использовать области памяти размером более 4 ГБ, что означает, что в macOS будет больше места для размещения; может быть проблематичным на некоторых X99, X299, поэтому рекомендуется тестировать с и без.
      • Примечание: В BIOS поддерживаемых Resizable BAR Support, включение Above4G разблокирует эту опцию. Убедитесь, что BAR support отключен, если эта опция конечно же присутствует.
    • Boot Options -> Windows8.1/10 mode: Это гарантирует, что не будет загружаться старый устаревший мусор. Интересный факт: other OS разработан только для загрузки старых версий Windows, а не для других ОС
  • Отключите как можно больше ненужных устройств в BIOS (это означает, что при каждой загрузке будет меньше изменений в Memory Map, и поэтому меньше шансов на сбой загрузки). Общие настройки:
    • CSM: Для поддержки Legacy, добавляет кучу ненужного нам мусора. Это также может сломать Shell, и вы не сможете загрузиться в него.
    • Intel SGX: Software Guard Extensions, занимает много места и ничего не делает в macOS.
    • Parallel Port: macOS даже не видит параллельные порты.
    • Serial Port: Хотелось бы знать, насколько много тех, кто отлаживает ядро...
    • iGPU: В некоторых системах такие раздутые Memory Map, что iGPU просто не влезает.
    • Thunderbolt: У многих хаков нет рабочего Thunderbolt, платы, у которых нет Thunderbolt, но имеют эту опцию, просто тратят больше места.
    • LED lighting: Извини, приятель, время уходить.
    • Legacy USB: Больше Legacy Чепухи.

# Тестовая загрузка

С нашими скорректированными настройками EFI, config.plist и BIOS, пришло время опробовать наши новые настройки. Если у вас все еще есть проблемы, похоже, нам нужно глубоко погрузиться и рассчитать значение нашего Slide

# Нахождение Slide значения

Теперь вам нужно открыть EFI Shell в выбранном вами менеджере загрузок и запустить memmap. Это даст вам список всех страниц и их размеров. Вот тут-то и начинается веселье.

Пример того, что вы увидите:

Type Start End # Pages Attributes
RT_Data 0000000000000000 0000000000000FFF 0000000000000001 800000000000000F
Available 0000000000001000 0000000000057FFF 0000000000000057 000000000000000F
Reserved 0000000000058000 0000000000058FFF 0000000000000001 000000000000000F
Available 0000000000059000 000000000008FFFF 0000000000000037 000000000000000F
RT_Code 0000000000090000 0000000000090FFF 0000000000000001 800000000000000F
Available 0000000000091000 000000000009DFFF 000000000000000D 000000000000000F
Reserved 000000000009E000 000000000009FFFF 0000000000000002 000000000000000F
Available 0000000000100000 000000005B635FFF 000000000005B536 000000000000000F
BS_Data 000000005B636000 000000005B675FFF 0000000000000040 000000000000000F
Available 000000005B676000 000000006AF77FFF 000000000000F902 000000000000000F
LoaderCode 000000006AF78000 000000006B155FFF 00000000000001DE 000000000000000F
BS_Data 000000006B156000 000000006B523FFF 00000000000003CE 000000000000000F
ACPI_NVS 000000006B524000 000000006B524FFF 0000000000000001 000000000000000F
BS_Data 000000006B526000 000000006B625FFF 0000000000000100 000000000000000F
Available 000000006B626000 000000006B634FFF 000000000000000F 000000000000000F

Теперь вам может быть интересно, как, черт возьми, мы конвертируем это в значение слайда, ну, это довольно просто. Нас интересует самое наибольшее доступное значение в столбце Start. В этом примере мы видим, что 000000006B626000 является нашим самым большим значением, обратите внимание, что они находятся в HEX, поэтому, если есть несколько значений, близких друг к другу, вам может потребоваться конвертировать их в десятичное значение. Для вычисления Slide значение (встроенный калькулятор macOS имеет функцию программирования, нажав ⌘ + 3):

000000006B626000 = 0x6B626000

(0x6B626000 - 0x100000)/0x200000 = 0x35A

И чтобы убедиться, что это правильно:

0x100000 + (0x35A * 0x200000) = 0x6B500000

Всякий раз, когда возвращаемое значение не оригинальное (0x6B500000 против 0x6B626000), просто добавьте +1 к конечному Slide значению. Это связано с округлением. Так, например, конвертированный в десятичное счисление 0x35A становится 858, а +1 даст вам slide=859.

Но подождите, это же больше 256!

Это верно. Это вызвано Memory Map включающими в себя сектора Above4GDecoding, которые не могут быть использованы. Поэтому вам нужно будет спускаться вниз по списку, пока вы не найдете небольшое достаточное значение(для нас, это будет 0000000000100000)

И чтобы было немного яснее, вот формула:

(HEX - 0x100000)/0x200000 = Значение Slide в HEX

0x100000 + (Значение Slide в HEX * 0x200000) = Ваше изначальное HEX значение(если нет, то добавьте +1 к Slide значению)

Теперь перейдите в свой config.plist и добавьте своё слайд значение к остальным аргументам загрузки(для нас, это будет slide=0 при использовании 0x100000). Если это значение по-прежнему выдаёт вам ошибки, вы можете перейти ко второму по велечине Start значению и так далее.

Иногда вы можете обнаружить, что при вычислении слайда, вы получаете сверхмалые значения, такие как slide=-0.379150390625, когда это происходит, округлите это до slide=0.

А для пользователей, у которых возникают проблемы с поиском значения слайда, вы можете также ввести $slide [вставьте наибольшее значение #Pages] в канале #Sandbox на Discord-сервере r/Hackintosh (opens new window)

Но это таааак тяжело

Ну, не волнуйтесь, ведь есть более простое решение. После запуска memmap в оболочке запустите:

shell> fs0: //replace with your USB

fs0:\> dir //to verify this is the right directory, if not try fs1 and so on

Directory of fs0:\
01/01/01 3:30p   EFI

fs0:\> memmap > memmap.txt

Это добавит файл memmap.txt в корень вашего EFI, после чего вы сможете перетащить в канал #Sandbox Discord-сервера r/Hackintosh и написать $slide [вставьте ссылку на memmap.txt]

# Использование DevirtualiseMmio

DevirtualiseMmio - довольно интересный квирк, особенно в том, что он преодолевает огромное препятствие с многими системами PCI устройств, такими как некоторые Z390 платы и практические все HEDT платы, такие как X99 и X299. Как это происходит? Он берет MMIO области и удаляет рантайм атрибуты, позволяя тем самым использовать их в качестве пространства для удобного размещения ядра. В сочетании с квирком ProvideCustomSlide, мы можем сохранить функцию безопасности слайда, а также получить загружаемую машину.

Для чрезвычайно проблематичных систем, таких как Threadripper TRX40 19H, нам нужно найти определенные обрасти, которые не требуются для правильной работы. Вот тут-то и вступает в игру MmioWhitelist. Обратите внимание, что белый список не требуется для большинства систем.

Если вы запустите отладочную версию OpenCore со включенным DevirtualiseMmio, вы заметите в своих логах это:

21:495 00:009 OCABC: MMIO devirt start
21:499 00:003 OCABC: MMIO devirt 0x60000000 (0x10000 pages, 0x8000000000000001) skip 0
21:503 00:003 OCABC: MMIO devirt 0xFE000000 (0x11 pages, 0x8000000000000001) skip 0
21:506 00:003 OCABC: MMIO devirt 0xFEC00000 (0x1 pages, 0x8000000000000001) skip 0
21:510 00:003 OCABC: MMIO devirt 0xFED00000 (0x1 pages, 0x8000000000000001) skip 0
21:513 00:003 OCABC: MMIO devirt 0xFEE00000 (0x1 pages, 0x800000000000100D) skip 0
21:516 00:003 OCABC: MMIO devirt 0xFF000000 (0x1000 pages, 0x800000000000100D) skip 0
21:520 00:003 OCABC: MMIO devirt end, saved 278608 KB
  • Примечание: См. Отладка OpenCore о том, как включить логирование в файл

Итак, у нас есть 6 регионов, через которые нам нужно пройти и посмотреть, какие из них плохие. Лучше всего заблокировать все MMIO области кроме одного и попробовать каждый регион, чтобы получить список хороших регионов.

Теперь давайте возьмем вышеприведенный пример и создадим наш собственный MmioWhitelist, нам нужно сначала преобразовать адрес из шестнадцатеричного в десятичное значение:

  • MMIO devirt 0x60000000 -> 1610612736
  • MMIO devirt 0xFE000000 -> 4261412864
  • MMIO devirt 0xFEC00000 -> 4273995776
  • MMIO devirt 0xFED00000 -> 4275044352
  • MMIO devirt 0xFEE00000 -> 4276092928
  • MMIO devirt 0xFF000000 -> 4278190080

Когда все сделано, должно выглядеть примерно так: