Я пытаюсь выяснить, возможно ли запустить виртуальную машину Linux, чья память поддерживается только одной физической страницей.
Чтобы смоделировать это, я изменил обработчик ошибок вложенной страницы в KVM, чтобы удалить текущий бит из всех записей вложенной таблицы страниц (NPT), кроме той, которая соответствует текущей обработанной ошибке страницы.
Пытаясь запустить гостевую систему Linux, я заметил, что инструкции по сборке, использующие операнды памяти, такие как
add [rbp+0x820DDA], ebp
приводить к циклу сбоя страницы, пока я не восстановлю текущий бит для страницы, содержащей инструкцию, а также для страницы, на которую ссылается операнд (в этом примере [rbp+0x820DDA]
).
Мне интересно, почему это так. Разве процессор не должен обращаться к страницам памяти последовательно, то есть сначала читать инструкцию, а затем обращаться к операнду памяти? Или x86 требует, чтобы страница инструкций и все страницы операндов были доступны одновременно?
Я тестирую на AMD Zen 1.
Ответы:
Да, они требуют машинного кода и всех операндов памяти.
Да, это логично, что происходит, но исключение ошибки страницы прерывает этот двухэтапный процесс и отклоняет любой прогресс. Процессор никак не может вспомнить, какую инструкцию он выполнял в момент возникновения сбоя страницы.
Когда обработчик сбоя страницы возвращается после обработки допустимого сбоя страницы, RIP = адрес ошибочной инструкции, поэтому ЦП пытается выполнить его с нуля .
Было бы законно, чтобы ОС модифицировала машинный код ошибочной инструкции и ожидала, что она выполнит другую команду после
iret
обработчика ошибок страницы (или любого другого исключения или обработчика прерываний). Так что AFAIK архитектурно требует, чтобы процессор возвращал выборку кода из CS: RIP в случае, если вы говорите. (Предполагая, что он даже возвращается к отказавшему CS: RIP вместо планирования другого процесса в ожидании сбоя диска на жесткой странице или доставки SIGSEGV обработчику сигнала при сбое неверной страницы.)Вероятно, это также архитектурно необходимо для входа / выхода гипервизора. И даже если это явно не запрещено на бумаге, это не то, как работают процессоры.
@torek отмечает, что некоторые (CISC) микропроцессоры частично декодируют инструкции и выдают состояние микрорегистра при сбое страницы , но x86 не такой.
Несколько инструкций являются прерываемыми и могут выполнять частичный прогресс, например
rep movs
(memcpy в банке) и другие строковые инструкции, или собирать хранилища загрузки / разброса. Но единственным механизмом является обновление архитектурных регистров, таких как RCX / RSI / RDI для строковых операций, или регистров назначения и маски для сборок (например, руководство для AVX2vpgatherdd
). Отсутствие кода операции / декодирования приводит к некоторому скрытому внутреннему регистру и перезапускает его после iret из обработчика ошибок страницы. Это инструкции, которые делают несколько отдельных обращений к данным.Также имейте в виду, что x86 (как и большинство ISA) гарантирует, что инструкции являются атомарными. прерывания / исключения: они либо полностью происходят, либо не происходят вообще, до прерывания. Прерывание инструкции по сборке во время ее работы . Так, например
add [mem], reg
, потребуется сбросить нагрузку, если часть магазина вышла из строя, даже безlock
префикса.В худшем случае число гостевых страниц пользовательского пространства для продвижения вперед может составлять 6 (плюс отдельные поддеревья таблицы гостевого ядра для каждой из них):
movsq
илиmovsw
2-байтовая инструкция, охватывающая границу страницы, поэтому для ее декодирования необходимы обе страницы.[rsi]
также разделенный на страницы[rdi]
также разделенный на страницыЕсли какая-то из этих 6 страниц ошибается, мы вернемся к исходной точке.
rep movsd
также является 2-байтовой инструкцией, и выполнение шага на одном шаге будет иметь такое же требование. Подобные случаи, такие какpush [mem]
илиpop [mem]
могут быть построены со смещенным стеком.Одна из причин (или побочных преимуществ) для / создания "прерывистости" сборочных нагрузок / хранилищ разброса (обновление вектора маски по мере их продвижения) состоит в том, чтобы избегать увеличения этой минимальной площади для выполнения одной инструкции. Также для повышения эффективности обработки нескольких неисправностей во время одной сборки или разброса.
@Brandon указывает в комментариях, что гостю понадобятся его таблицы страниц в памяти , и разделение страниц в пользовательском пространстве также может быть разделением в 1 ГБ, поэтому обе стороны находятся в разных поддеревьях верхнего уровня PML4. Для продвижения по странице HW необходимо коснуться всех этих страниц таблицы гостевых страниц. Такая патологическая ситуация вряд ли случится случайно.
TLB (и внутренним элементам обхода страниц) разрешено кэшировать некоторые данные таблицы страниц, и не требуется перезапуск обхода страниц с нуля, если ОС не
invlpg
установила или не установила новый каталог страниц верхнего уровня CR3. Ничто из этого не является необходимым при изменении страницы с отсутствия на настоящее; x86 на бумаге гарантирует, что он не нужен (поэтому «отрицательное кэширование» отсутствующих PTE не разрешено, по крайней мере, невидимо для программного обеспечения). Таким образом, процессор не может выйти из VMexit, даже если некоторые из гостевых физических страниц таблицы страниц на самом деле отсутствуют.Счетчики производительности PMU могут быть включены и настроены таким образом, что для инструкции также требуется событие perf для записи в буфер PEBS для этой инструкции. С маской счетчика, сконфигурированной для подсчета только инструкций пользовательского пространства, а не ядра, вполне возможно, что он продолжает пытаться переполнить счетчик и сохранять сэмпл в буфере каждый раз, когда вы возвращаетесь в пользовательское пространство, вызывая ошибку страницы.
источник
push dword [foo
" (или даже простоcall [foo]
), когда все смещено по "границе таблицы указателей каталогов страниц" (добавление до 6 страниц, 6 таблиц страниц, 6 каталогов страниц, 6 PDPT и одного PML4); с включенной и настроенной функцией ЦП «точная выборка на основе событий с буфером PEBS», позволяющаяpush
добавлять данные мониторинга производительности в буфер PEBS. Для консервативного «минимального количества страниц, предоставляемых хостом, чтобы гость мог прогрессировать в патологических случаях», я хотел бы по крайней мере 16 страниц.EIP
. Таким образом, есть логический дополнительный вопрос. Какое минимальное количество страниц требуется при условии интеллектуальной схемы исправлений инструкций? Например, скопируйте невыровненное значение в выровненный рабочий буфер, эмулируйте инструкцию и IRET для следующей инструкции.iret
инструкцией ОС также должна быть в памяти. Это однобайтовая инструкция, поэтому одна дополнительная страница. Адрес прерывания обработчика ошибок страницы также должен быть в памяти, но это может быть та же страница, что и выше.