Чи потрібні інструкції x86, щоб їхнє власне кодування, а також всі їх аргументи були присутніми в пам'яті одночасно?


64

Я намагаюся розібратися, чи можна запустити VM Linux, оперативну пам’ять якої підтримує лише одна фізична сторінка.

Щоб імітувати це, я змінив обробку помилок вкладеної сторінки в KVM, щоб видалити даний біт з усіх записів вкладених таблиць сторінок (NPT), за винятком того, що відповідає поточній несправності сторінки.

Намагаючись запустити гостьова Linux, я помітив, що інструкції по збірці, що використовують операнди пам'яті, як

add [rbp+0x820DDA], ebp

вести до циклу помилок сторінки, поки я не відновлю даний біт для сторінки, що містить інструкцію, а також для сторінки, на яку посилається в операнді (у цьому прикладі [rbp+0x820DDA]).

Мені цікаво, чому це так. Чи не повинен ЦП отримувати доступ до сторінок пам'яті послідовно, тобто спочатку прочитайте інструкцію, а потім отримайте доступ до операнду пам'яті? Або x86 вимагає, щоб сторінка інструкцій та всі сторінки операндів були доступні одночасно?

Я тестую AMD Zen 1.


2
Чому б ти хотів це зробити?
СС Енн

11
Просто з технічного інтересу :)
savvybug

14
Захист до веселої ідеї проекту.
труба

10
Це божевільно на рівні "завантажувати Linux на емуляторі 486, що працює в JavaScript у браузері". Я це люблю.
chrylis -на страйк-

3
Хе, мабуть, я поставив це питання до того ж логічного висновку, про який ви вже думали, про мінімальний робочий набір для гарантованого прогресу вперед. Я вже відповів на це, перш ніж ви додали новий перший абзац до питання. : PI додав декілька посилань та більше деталей у кількох місцях (наприклад, ходувач сторінки може кешувати деякі записи гостьових сторінок-каталогів всередині), оскільки це питання привертає до себе більшу увагу, ніж я очікував, завдяки чомусь якимось чином перейшов до HNQ.
Пітер Кордес

Відповіді:


56

Так, вони вимагають машинного коду та всіх операндів пам'яті.

Чи не повинен ЦП отримувати доступ до сторінок пам'яті послідовно, тобто спочатку прочитайте інструкцію, а потім отримайте доступ до операнду пам'яті?

Так, це логічно те, що відбувається, але виняток помилок сторінки перериває цей двоступеневий процес і відкидає будь-який прогрес. Процесор не може згадати, яка інструкція була в середині, коли сталася помилка сторінки.

Коли обробник помилок сторінки повертається після обробки дійсної помилки сторінки, RIP = адреса інструкції про несправності, тому процесор повторно виконує її з нуля .

Було б законно, щоб ОС модифікувала машинний код несправної інструкції і очікувала, що вона виконає іншу інструкцію після того, як стоїть iretобробник сторінки-помилки (або будь-який інший виняток або обробник переривання). Тому AFAIK архітектурно вимагає, щоб процесор повторно вибирав код з CS: RIP у випадку, про який ви говорите. (Якщо припустити, що він навіть повертається до несправного CS: RIP, а не планує інший процес під час очікування диска з помилкою жорсткої сторінки або доставки SIGSEGV до обробника сигналу з недійсної помилки сторінки.)

Можливо, це також архітектурно потрібно для входу / виходу гіпервізора. І навіть якщо це прямо не заборонено на папері, це не так, як працюють процесори.

@torek зауважує, що деякі (CISC) мікропроцесори частково декодують інструкції та скидають стан мікрорегістру на помилку сторінки , але x86 - це не так.


Кілька інструкцій є переривчими і можуть зробити частковий прогрес, як-от rep movs(memcpy in can) та інші строкові інструкції, або зібрати вантажі / розкидати магазини. Але єдиним механізмом є оновлення архітектурних регістрів, таких як RCX / RSI / RDI для рядків ops, або регістри призначення та маски для зборів (наприклад, посібник для AVX2vpgatherdd ). Якщо збереження опкоду / декодування призводить до деякого прихованого внутрішнього реєстру та його перезавантаження після iret від сторінки обробки помилок. Це інструкції, які роблять кілька окремих доступу до даних.

Також майте на увазі, що x86 (як і більшість ISA) гарантує, що інструкції є атомними wrt. переривання / винятки: вони або повністю відбуваються, або взагалі не трапляються до переривання. Переривання інструкції по збірці під час роботи . Так, наприклад add [mem], reg, потрібно буде скинути навантаження, якщо складова частина виникла, навіть без lockпрефікса.


Найгірше число присутніх гостьових просторів користувальницьких сторінок, щоб досягти прогресу, може бути 6 (плюс окремі підрядки таблиці гостьового ядра для кожної з них):

  • movsqабо movsw2-байтна інструкція, що охоплює межу сторінки, тому обидві сторінки потрібні для її розшифровки.
  • qword source операнд [rsi]також розділення сторінки
  • qword призначення операнд [rdi]також розділення сторінки

Якщо будь-яка з цих 6 сторінок помилка, ми повернемося до прямої.

rep movsdце також 2-байтна інструкція, і прогрес на одному кроці з неї матиме ту саму вимогу. Подібні випадки на зразок push [mem]або pop [mem]можуть бути сконструйовані з нерівним стеком.

Однією з причин (або побічних переваг) для / виготовлення накопичувальних вантажів / розкидання сховищ «переривчастим» (оновлення вектора масок з їх прогресом) є уникнення збільшення цього мінімального сліду для виконання однієї інструкції. Також для підвищення ефективності поводження з декількома несправностями під час одного збору або розсіювання.


@Brandon в коментарях зазначає, що гостю знадобляться таблиці сторінок у пам'яті , а розділення сторінки в просторі користувачів також можуть бути розбиттями 1GiB, тому дві сторони знаходяться в різних під деревах верхнього рівня PML4. Для того, щоб досягти прогресу, HW-сторінка повинна торкатися всіх цих сторінок гостьової таблиці. Ситуація, така патологічна, навряд чи трапиться випадково.

TLB (і внутрішні пристрої прогулянок сторінок ) дозволяють кешувати деякі дані таблиці сторінок, і не потрібно перезапускати прогулянку сторінки з нуля, якщо ОС не зробила invlpgабо встановила новий каталог сторінок верхнього рівня CR3. Жодне з них не є необхідним при зміні сторінки з неприсутньої на теперішню; x86 на папері гарантує, що він не потрібен (тому "негативне кешування" відсутніх PTE заборонено, принаймні не видно програмному забезпеченню). Таким чином, процесор може не VMexit, навіть якщо деякі сторінки гостьових фізичних сторінок таблиці фактично відсутні.

Лічильники продуктивності PMU можуть бути включені та налаштовані таким чином, що інструкція також потребує події perf для запису в буфер PEBS для цієї інструкції. Маска лічильника, налаштована на врахування лише інструкцій з простору користувача, а не в ядро, цілком може бути, що він намагається переповнювати лічильник і зберігати зразок у буфері кожного разу, коли ви повертаєтесь у простір користувачів, створюючи помилку сторінки.


15
Найгіршим випадком для однієї інструкції може бути щось на зразок " push dword [foo" (або навіть просто call [foo]) зі всім нерівним узгодженням "Межі таблиці вказівників каталогу сторінок" (додавання до 6 сторінок, 6 таблиць сторінок, 6 каталогів сторінок, 6 PDPT і один PML4); з увімкненою та налаштованою функцією "точного відбору проб на основі подій з буфером PEBS" процесора так, що pushвикликає додавання даних моніторингу продуктивності до буфера PEBS. Для консервативного "мінімуму сторінок, наданих господарем, щоб гість міг досягти успіху в патологічних випадках", я хотів би принаймні 16 сторінок.
Брендан

4
Зауважте, що подібні речі завжди були поширеними в архітектурах CISC-y. Деякі мікропроцесори частково розшифровують інструкції та скидають стан мікрорегістру на помилку сторінки, але інші не / та вимагають, щоб операнди адреси для інструкцій "петля-у" (DBRA на m68k, MOVC3 / MOVC5 на Vax тощо) знаходилися в регістрах подібних до вашого прикладу REP MOVS.
torek

1
@Brendan: хтось порахував найгірший випадок із інструкції VAX приблизно на 50 сторінках. Я забуваю деталі, але ви, очевидно, поставите саму інструкцію на межі сторінки, використовуйте щось на зразок пошуку таблиці перекладу з таблицею, що охоплює межу сторінки, використовуйте (rX) [rY] з непрямими на межах сторінки, і так далі. Властиві інструкції містили до 6 операндів (завантажуючи їх у r0-r5), і всі шість можуть бути подвійними непрямими, я думаю.
torek

3
ОС може змінити інструкцію, але вона також може змінитися EIP. Тож виникає логічне подальше запитання. Яка мінімальна кількість сторінок потрібна, якщо прийняти інтелектуальну схему виправлення інструкцій? Наприклад, скопіюйте нестандартне значення у вирівняний буфер нуля, емулюйте інструкцію та IRET до наступної інструкції.
MSalters

1
Сторінка, що містить інструкцію ОС, iretтакож повинна знаходитись у пам'яті. Це однобайтова інструкція, тому одна додаткова сторінка. Адреса переривання обробки помилок сторінки також повинна зберігатися в пам'яті, але це може бути та сама сторінка, що і вище.
Стиг Хеммер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.