286 пояснено: захищений режим, який врятував ПК — і мучив розробників

Було корисно?

Ви ще не жили по-справжньому, доки не бачили, як «цілком робоча» програма епохи DOS вибухає через те, що хтось припустив: пам’ять — це просто пам’ять. Саме з 80286 ( «286» ) це припущення почало втрачати силу. Він представив захищений режим — справжні дозволи, справжню ізоляцію, реальне адресне простір понад межу 1 МБ. Але також приніс купу гострих країв, які змушували розробників і операторів заробляти свою каву.

Якщо ви сьогодні керуєте продуктами в продакшені, історія 286 все ще релевантна. Захищений режим — це предок базового контракту, на який ми покладаємось: простір користувача не може записувати в пам’ять ядра, процеси не можуть топтатися по одна одній, а ОС має право застосовувати правила. 286 приніс цей контракт рано, неідеально і з однією відомою «фішкою», яка сформувала ціле десятиліття ПК‑пЗ: на 286 ввімкнути захищений режим було легко; надійно повернутися в реальний режим — ні.

Що змінив 286 (і чому це було важливо)

Intel 80286 (1982) часто пам’ятають як «процесор у IBM PC/AT». Це правда, але це применшує суть зміни. Саме з 286 лінійка x86 почала поводитися так, ніби прагне стати справжньою багатокористувацькою, багатозадачною системою. Не хобі‑машина, що запускає одну програму одночасно й довіряє їй ключі від міста.

Захищений режим на 286 ввів апаратні механізми, які сучасні операційники сприймають як належне:

  • Захист пам’яті: сегменти пам’яті можна маркувати з правами доступу. Жодних «ой, я записав у ОС».
  • Рівні привілеїв: кільцеві рівні привілею та контрольовані переходи через шлюзи. Ядро залишаєтьcя ядром.
  • Скарпель віртуальної пам’яті: ще не paging (це вже 386), але концептуальна рамка: трансляція адрес через таблиці, границі, привілеї.
  • Більший адресний простір: фізична адресація понад 1 МБ, до 16 МБ.

Але він також поставив обмеження, які для екосистеми ПК середини 80‑х були ніби побудувати підземку перед зведенням станцій. Більшість користувачів усе ще використовували DOS, який був тісно пов’язаний з реальним режимом. Розробники писали під DOS, бо там жили клієнти. А захищений режим на 286 прийшов з пасткою: раз увійшовши, повернутися в реальний режим було… незручно.

Це невідповідність — апаратна можливість проти реальності програмного забезпечення — і є серцевиною історії 286. Це також повторювана тема в продакшн‑інженерії: зміна платформи обіцяє безпеку й продуктивність, але шлях міграції — ось де народжується простій.

Реальний режим: тісна квартира, з якої ніхто не хотів виходити

Щоб зрозуміти, чому захищений режим 286 «мучив розробників», потрібна базова відправна точка: реальний режим 8086/8088. Реальний режим не є «поганим» абстрактно. Він мінімалістичний. Простий. Легко завантажується. Це режим, у якому CPU стартує при вмиканні, і саме цього очікує прошивка.

У реальному режимі:

  • Адреси формуються як segment:offset, з physical = segment * 16 + offset.
  • Доступно 1MB пам’яті (20‑бітний адресний простір).
  • Немає примусового розділення між ОС і додатками. Будь‑який код може записувати всюди.
  • Переривання та BIOS‑сервіси спроєктовані під цю модель.

DOS спирався на реальний режим, бо DOS спочатку був однозадачним середовищем для малих машин. Він віддавав абстрагування апаратури викликам BIOS, запускав програми, які вважали, що вони самі по собі, і покладався на «не роби цього» замість «тобі заборонено це робити».

Сегментація в реальному режимі — також дивний вид сили: вона дозволяє «переміщати» те ж саме вікно з офсетом по пам’яті, змінюючи регістр сегмента. Люди розробляли хитромудрі моделі пам’яті, оверлеї, EMS‑трюки та код, що трактував сегментацію як фічу. Потім з’явився захищений режим і сказав: «Сегментація все ще тут, але тепер серйозна».

Жарт №1: Реальний режим — це як запускати продакшен від root, бо «так швидше». І воно швидше, поки не починаєш втрачати дані.

286 захищений режим: сегментація дорослішає

Захищений режим на 286 зберігає ідею сегментації, але замінює обчислення «segment * 16» на систему, керовану таблицями. Замість того, щоб регістр сегмента містив адресу бази (приблизно), він містить селектор, який індексує дескриптор. Цей дескриптор каже CPU:

  • базову адресу сегмента (base)
  • межу сегмента (limit)
  • тип (type) — код/дані/системний
  • рівень привілею (privilege level) — хто може доступитися
  • біт присутності/валідності та інші поля управління

GDT, LDT і чому таблиці стали вашим життям

286 ввів таблиці дескрипторів:

  • GDT (Global Descriptor Table): системні дескриптори.
  • LDT (Local Descriptor Table): дескриптори на задачу (або процес).
  • IDT (Interrupt Descriptor Table): як переривання й виключення безпечно передають керування.

В термінах експлуатації: 286 перетворив відображення пам’яті на конфігурацію. Ви не просто «використовуєте пам’ять»; ви її визначаєте. Неправильні визначення не викинуть дружнє повідомлення — вони викличуть виняток.

Кільця привілеїв і контрольовані переходи

Захищений режим включає рівні привілеїв (кільця). 286 підтримує кільця 0–3. Ring 0 призначений для ядра ОС. Ring 3 — для додатків. Шлюзи (call gates, interrupt gates, task gates) дозволяють контрольований вхід до коду з вищими привілеями.

Ця механіка робить ідею «багатокористувача» переконливою. Крах у просторі користувача не повинен ламати всю машину. Але це також означає, що розробники не можуть легко «шахраювати». Деякі з найпоширеніших «оптимізацій» епохи DOS були фактично шахрайством: прямий доступ до апаратури, перезапис векторів переривань, модифікація структур BIOS. У захищеному режимі це стає незаконним, якщо ОС явно не дозволила.

Адресний простір: більший, але не так, як ви хотіли

286 може адресувати до 16 МБ фізичної пам’яті в захищеному режимі (24‑біт). Це великий стрибок від 1 МБ. Але 286 все ще фундаментально сегментований. Paging немає. Ви не отримуєте плоскої лінійної пам’яті з demand‑paging. Ви отримуєте сегментацію з лімітами.

Ось де розробники застрягли: вони хотіли більше пам’яті і сумісності з DOS + BIOS. 286 дав більше пам’яті й новий набір правил, але не дав простого гібридного режиму, щоб плавно входити й виходити. Цей хід став простішим з 386.

Проблема «неможливості повернення в реальний режим»: один біт, багато головного болю

Ось центр історії: на 286, як тільки ви встановили біт PE (Protection Enable) в CR0, щоб увійти в захищений режим, немає архітектурно чистої інструкційної послідовності, щоб очистити його і повернутися в реальний режим. Офіційний шлях Intel фактично був: скинути CPU.

Так, скинути. Тобто повернути процесор у стан при старті. Це не те, як пишуть плавну DOS‑програму, яка хоче на мить скористатися захищеним режимом, а потім викликати BIOS‑переривання.

Були обхідні шляхи, і вони були такими, що викликають у SRE мимовільний тик:

  • Перезапуск через triple fault: навмисно викликати каскад помилок, який скидає CPU. Швидко й брутально.
  • Скидання через контролер клавіатури (8042): переключити лінію reset процесора через контролер клавіатури. Також брутально, і ваш стан CPU втрачається.
  • Трюки BIOS/прошивки: деякі системи пропонували вендор‑специфічні шляхи. Надійність залежала від машини й ревізії BIOS.

Захищений режим був майбутнім, але DOS і BIOS‑сервіси були теперішнім. Це робило 286 перехідним CPU з дуже нетранзитивним робочим процесом. Ви могли створювати ОС для нього (і люди це робили), але не могли просто побудувати шари сумісності з DOS, які би плавно стрибали між режимами без насильницької зміни стану машини.

Це та частина, яка «мучила розробників»: справа не лише в різниці; вона ламала припущення про потік керування. ОС хотіла володіти машиною; додатки ери DOS хотіли володіти машиною. 286 зробив одного з цих учасників незадоволеним.

Перемикач A20: найдивніший вимикач в історії ПК

Якщо ви колись чули, як старі інженери бурмочуть про «A20», це спадщина сумісності, що пожерла апаратний дизайн.

8086 мав 20 ліній адреси (A0–A19) для адресації 1MB. Але через те, як працює арифметика segment:offset, адреси могли обертатися на межі 1MB таким чином, на який деякий софт випадково спирався. Коли IBM створювала PC/AT на базі 286, вони додали пам’ять понад 1MB, і тепер ті обертання перестали обертатися — й софт ламався.

Компромісом став перемикач A20: механізм, що примушував лінію адреси A20 бути нульовою, імітуючи стару поведінку обертання. Якщо A20 вимкнений, адреси понад 1MB обертаються назад у перший мегабайт. Якщо ввімкнений, ви можете правильно доступатися до розширеної пам’яті.

Перемикач A20 часто контролювали через контролер клавіатури (8042), що звучить, ніби це сатира. У вас є CPU, здатний на захищений режим і мегабайти RAM, а ви перемикаєте лінію адреси через чіп клавіатури, бо сумісність вимагала.

В експлуатаційному сенсі A20 — класична пастка «у мене працює на моїй машині»: різні чипсети й реалізації BIOS поводилися по‑різному, важливий був час, і деякі послідовності були крихкими під навантаженням або в нестандартних апаратних станах. Коли A20 працює некоректно, ви бачите шаблони корупції пам’яті, що виглядають як привиди. Це не привиди. Це обертання адрес.

Проєктні рішення, які мали значення в експлуатації

Епоха 286 — це час, коли «ПК» почало означати «загального призначення платформа», а не «одноразовий апарат». Але проєктні рішення мали компроміси, які чітко лягли в сучасні уроки експлуатації.

Сегментація — це політика, не лише адресація

У захищеному режимі сегментація стає інструментом примусу: база, ліміт, привілеї. Якщо ви неправильно налаштуєте дескриптори, ви не отримаєте «трохи неправильну поведінку». Ви отримаєте винятки. Це добре — fail fast — але лише якщо ви можете це відлагодити.

Сумісність не безкоштовна, це система

Перемикач A20 існує, бо софт залежав від невизначеної поведінки. Це не моральна вада; це те, що відбувається, коли платформа стає популярною. Якщо ви постачаєте фічу сумісності, ви також постачаєте її операційні витрати на роки.

Переходи стану — місце, де надійність гине

Нездатність чисто повернутися в реальний режим змусила розробників будувати переходи на основі скидання. Скидання означає втрату стану. Втрата стану вимагає ретельного збереження/відновлення, ідемпотентної ініціалізації та оборонного програмування. Іншими словами: 286 примусив мислення про надійність у софту, який культурно до цього не був готовий.

Цитата, яку варто тримати під рукою

«Надія — не стратегія.» — General Gordon R. Sullivan

Якщо ви коли-небудь намагалися «просто переключитися в захищений режим на секунду» на 286, ви це дізнаєтесь важким шляхом.

Цікаві факти та історичний контекст (коротко)

  1. Реліз 1982: 80286 з’явився в 1982 році і став процесором IBM PC/AT, закріпивши його як «бізнес‑ПК».
  2. Фізична адресація 16MB: У захищеному режимі 286 міг адресувати до 16MB (24‑біт), величезний стрибок від 8086.
  3. Пейджингу не було: 286 мав захист на основі сегментації, але не мав paging; «сучасна» плоска віртуальна пам’ять стала практичною з 80386.
  4. Тиск сумісності з реальним режимом: DOS і BIOS‑екосистеми змусили апарат зберігати поведінку, як обертання на 1MB, що породило A20.
  5. Ціль для OS/2: IBM і Microsoft спочатку планували OS/2 під машини класу 286; перехід на 386‑підхід став очевидним з потребою paging і кращої сумісності.
  6. Чистого повернення не було: Шлях ввімкнення захищеного режиму не мав відповідного чистого шляху для відключення; зазвичай поверталися через скидання.
  7. Таблиці дескрипторів — великий стрибок: GDT/LDT/IDT ввели більш ОС‑центричну архітектуру, зсунувши ПК у бік робочих станцій.
  8. A20 через 8042: Багато AT‑класових систем перемикали A20 через контролер клавіатури, що створило проблеми з таймінгом і надійністю, які й досі відлунюють у bootloader‑ах.
  9. Захищений режим 286 — не «необов’язковий» для серйозних ОС: Якщо ви хотіли захист пам’яті і мультизадачність, захищений режим був обов’язковим входом — навіть якщо решта ПЗ світу не була готова.

Три міні‑історії з корпоративних «трин»

Міні‑історія 1: Інцидент, спричинений хибним припущенням

Фінансова компанія успадкувала бізнес‑додаток, який модернізували поетапно: спочатку під швидші CPU, потім під більшу пам’ять. Цільовою платформою був парк AT‑класових машин у відділеннях, все ще сильно залежних від DOS для сумісності з периферією і вендорськими інструментами.

Розробник припустив, що API менеджера «розширеної пам’яті» означає плаский доступ понад 1MB. Вони написали буферний менеджер, який копіював записи змінної довжини в те, що вважалось суцільним регіоном, а потім зберігали far‑вказівники ніби сегментна арифметика поводилась однаково в усіх режимах.

Під час пілоту все працювало. У продакшені система почала видавати періодичну корупцію даних через кілька годин. Не крахи — гірше. База проходила поверхневі перевірки, але певні записи поверталися з перемішаними полями. Персонал відділень звинувачував мережу. Мережники звинувачували «старі ПК». Усі технічно праві і оперативно марні.

Корінна причина була в припущенні про межу режимів: частина коду виконувалась з A20 вимкненим під час деяких BIOS‑асистованих рутин. Записи буферного менеджера «понад 1MB» оберталися в перший мегабайт і інколи лягли на структури, які рідко чіпали — тому корупція виглядала випадковою.

Виправлення було непоказним: явно контролювати стан A20 перед доступом до розширеної пам’яті, додати захисні патерни й перевірку кордонів записів, і перестати зберігати вказівники, які залежать від тимчасових розташувань сегментів. Справжній урок був культурний: якщо ваша коректність залежить від апаратного засуву, у вас не коректність — у вас забобон.

Міні‑історія 2: Оптимізація, яка повернулась бумерангом

Виробнича фірма хотіла швидший інтерфейс на терміналах цеху. Додаток був DOS‑базований з кастомним графічним кодом, і вендор запропонував «активований режим», що використовував захищений режим для blitting і декомпресії. Презентація була проста: «більше пам’яті, більший кеш, менше читань з диска».

В лабораторії все було чудово. На заводі — хаос. Термінали іноді зависали на кілька секунд, потім відновлювали роботу. Іноді термінал перезавантажувався під час зміни. Оператори навчилися часто зберігати дані, що породжує вимірюваний стрес.

Інженери простежили це до обходу повернення в реальний режим. У прискореній збірці вмикався захищений режим, робилося завдання, потім шлях повернення до DOS/BIOS реалізовувався через скидання. Скидання було «м’яким», але все одно скидало достатньо стану, щоб іноді пристрої потребували реініціалізації. Зазвичай додаток відновлювався. Іноді ні, і термінал перезавантажувався.

Оптимізація була реальною — поки не зачепила край системи: периферію, виклики BIOS і таймінг. Вони відкотили прискорену збірку і зробили нудний кеш у звичайній пам’яті плюс розумніший доступ до диска. По бенчмарку це було повільніше, але в єдиному метриці, що важить — uptime під час зміни — воно було швидше.

Міні‑історія 3: Нудна, але правильна практика, що врятувала день

Страхова компанія експлуатувала ранню багатокористувацьку систему на машинах класу 286 з шаром ОС у захищеному режимі. Їхнє середовище було латкою: деякі сайти мали новіші машини, інші були «заморожені» через цикли закупівель. Платформ‑команда мала правило, яке звучало болісно консервативно: кожне розгортання включає тест‑світ перевірки режимів.

Суїта не була складною. Вона перевіряла налаштування таблиць дескрипторів, переходи привілеїв і поведінку A20 при повторних переключеннях. Вона логувала відмови і відмовлялася продовжувати, якщо машина відхилялась від очікуваної апаратної поведінки. Деякі команди скаржились, що це сповільнює розгортання. Вони помилялись, але голосно.

В одному кварталі закупили «сумісні» материнські плати. Вони завантажували DOS нормально. Багато додатків працювали. Але під навантаженням у захищеному режимі перемикання A20 було нестабільним через апаратну особливість чипсету. Без тест‑суїти ОС була б розгорнута, а відмови з’явились би як «випадкові крахи» тижнями пізніше.

Замість цього суїта впала миттєво у стейджингу. Вендора змусили замінити плати. Нудна практика — перевіряти інваріанти щоразу — попередила довготривалий інцидент, який зайняв би місяці та вихідні.

Практичні завдання: команди, виводи та ваш вибір

В 2026 ви не під’єднаєтесь по SSH до 286 (принаймні не легально), але ви можете діагностувати проблеми режимів в емуляторах, середовищах завантаження та сучасних системах, де переходи real/protected все ще мають місце під час завантаження. Нижченаведені завдання практичні для:

  • відладки bootloader‑ів та раннього підняття ядра
  • відтворення поведінки ери 286 в емуляції
  • валидації поведінки A20 і карт пам’яті
  • розуміння сегментації/припущень про захищений режим, які все ще протікають у коді прошивки

Завдання 1: Підтвердити переходи режиму CPU під час завантаження з QEMU + debug логами

cr0x@server:~$ qemu-system-i386 -M pc -cpu 286 -m 16M -drive file=disk.img,format=raw -d int,cpu_reset -no-reboot
...QEMU 8.x...
CPU Reset (CPU 0)
...INT: vector=0x10 ...
...

Що це означає: Ви запускаєте емулятор i386, налаштований під модель CPU, сумісну з 286. Дебаг‑флаги показують скиди та переривання.

Рішення: Якщо ваш шлях завантаження залежить від повернення в реальний режим без скиду, ви будуєте на піску. Заплануйте перехід на основі скиду або переробіть, щоб уникати викликів BIOS після захищеного режиму.

Завдання 2: Переглянути карту пам’яті гостя з хоста (QEMU monitor)

cr0x@server:~$ (echo "info mtree"; sleep 1) | socat - UNIX-CONNECT:/tmp/qemu-monitor.sock
memory
  0000000000000000-000000000009ffff (prio 0, i/o): ram
  00000000000f0000-00000000000fffff (prio 0, i/o): rom
  0000000000100000-0000000000ffffff (prio 0, i/o): ram

Що це означає: Низька пам’ять, ROM‑регіон і RAM понад 1MB змеплені. Ваша «розширена пам’ять» існує.

Рішення: Якщо розширеної пам’яті немає або вона дивно змеплена, не відлагоджуйте захищений режим — спершу виправте конфіг платформи.

Завдання 3: Виявити, чи ввімкнений A20 в середовищі завантаження Linux

cr0x@server:~$ dmesg | grep -i a20 | head
[    0.000000] BIOS-provided physical RAM map:
[    0.000000] ...

Що це означає: Сучасні ядра часто не виводять «A20 enabled», але відсутність явних рядків означає, що потрібні інші докази.

Рішення: Якщо ви відлагоджуєте bootloader/фірму, не довіряйте логам ядра як доказу. Валідируйте A20 раніше (у вашому коді завантажувача або через інструментацію емулятора).

Завдання 4: Визначити, чи ви в VM/емуляторі, що може маскувати помилки A20

cr0x@server:~$ systemd-detect-virt
kvm

Що це означає: Ви віртуалізовані; низькорівневі особливості можуть бути абстраговані або реалізовані «занадто правильно».

Рішення: Якщо баг проявляється лише на фізичному залізі (або лише в одному емуляторі), вважайте підозрюваними A20 і шляхи скидання. Відтворіть у якнайменше двох середовищах.

Завдання 5: Підтвердити флаги CPU та архітектурні деталі (корисно при тестуванні обмежень 286)

cr0x@server:~$ lscpu | sed -n '1,12p'
Architecture:                         x86_64
CPU op-mode(s):                       32-bit, 64-bit
Byte Order:                           Little Endian
Vendor ID:                            GenuineIntel
Model name:                           Intel(R) Xeon(R) CPU
CPU family:                           6
Model:                                85
Stepping:                             7

Що це означає: Очевидно, ви не на 286. Але ви підтверджуєте, що потрібна емуляція для тестування поведінки 286.

Рішення: Якщо хтось пропонує «просто тестувати на запасному ПК», зупиніть їх. Баги переходів режимів залежать від таймінгу й платформи. Використовуйте детерміновану емуляцію плюс принаймні одну реальну ціль.

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

cr0x@server:~$ file bootloader.bin
bootloader.bin: DOS/MBR boot sector

Що це означає: Це boot sector, ймовірно 16‑бітний. Не доказ, що він ніколи не входить в protected mode, але підказка.

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

Завдання 7: Дизасемблювати код завантажувача, щоб знайти послідовності ввімкнення захищеного режиму

cr0x@server:~$ ndisasm -b 16 bootloader.bin | grep -E "lgdt|lidt|mov cr0|smsw|lmsw" | head
0000003A  0F0116          lgdt [0x1601]
00000040  0F20C0          mov eax,cr0
00000043  6683C801        or eax,byte +0x1
00000047  0F22C0          mov cr0,eax

Що це означає: Цей код встановлює PE=1 в CR0 через послідовність типу 386+. На реальному 286 частіше можна побачити використання lmsw; емулятори можуть приймати ширший набір інструкцій.

Рішення: Якщо ви дійсно таргетуєте 286, переконайтесь, що набір інструкцій відповідає. Дивно багато «286» загрузчиків тихо припускаються 386‑інструкцій.

Завдання 8: Підтвердити наявність і адекватність GDT у образі ядра (груба евристика)

cr0x@server:~$ strings -a kernel.bin | grep -i -E "gdt|ldt|idt" | head
GDT
IDT

Що це означає: Слабкий доказ: символи/рядки можуть вводити в оману, але це натякає, що образ містить логіку налаштування захищеного режиму.

Рішення: Якщо ви не бачите жодного натяку на таблиці дескрипторів у системі, яка стверджує, що використовує захищений режим, підозрівайте конфігурацію збірки або неправильний артефакт, що розгортається.

Завдання 9: Перевірити на непередбачувані скиди, які можуть бути «хаком» повернення в реальний режим

cr0x@server:~$ qemu-system-i386 -M pc -cpu 286 -m 16M -drive file=disk.img,format=raw -d cpu_reset -no-reboot 2>&1 | head -n 20
CPU Reset (CPU 0)
CPU Reset (CPU 0)
CPU Reset (CPU 0)

Що це означає: Кілька скидів під час того, що має бути одним шляхом завантаження. Це явна ознака переходів режимів на основі скиду або циклу triple fault.

Рішення: Якщо скиди — частина вашого дизайну, зробіть їх явними та керованими. Якщо вони випадкові, ви маєте критичний дефект у налаштуванні IDT/дескрипторів або проблеми зі стеком.

Завдання 10: Виявити поведінку triple fault, корелюючи «відсутній вивід» зі циклами скидання

cr0x@server:~$ qemu-system-i386 -M pc -cpu 286 -m 16M -drive file=disk.img,format=raw -d int,cpu_reset -no-reboot 2>&1 | sed -n '1,40p'
CPU Reset (CPU 0)
...INT: vector=0x0d ...
CPU Reset (CPU 0)
...INT: vector=0x08 ...
CPU Reset (CPU 0)

Що це означає: Ви бачите винятки (наприклад #GP, vector 0x0d) з наступними скидами. Це узгоджується з помилкою, яку неможливо обробити, бо шлях обробника теж падає.

Рішення: Перестаньте оптимізувати. Спочатку побудуйте мінімальний IDT з відомо‑робочими обробниками, перш ніж робити щось складне. Ваше перше завдання — «не скидатись несподівано».

Завдання 11: Перевірити, що збірка таргетує 16‑бітні/286 обмеження (без випадкових 386 опкодів)

cr0x@server:~$ objdump -D -b binary -m i8086 bootloader.bin | head -n 20
bootloader.bin:     file format binary

Disassembly of section .data:
00000000 <.data>:
   0:	fa                   	cli
   1:	31 c0                	xor    %ax,%ax
   3:	8e d8                	mov    %ax,%ds

Що це означає: Ви використовуєте дизасемблер в режимі 8086. Якщо ви бачите нісенітницю, можливо код не є чисто 16‑бітним або містить 386+ кодування.

Рішення: Якщо дизасемблювання не має сенсу, підтвердіть налаштування асемблера і таргет CPU. «Працює в емуляторі» загрузчик, який використовує неправильні інструкції, — це майбутній інцидент з довгою впливовістю.

Завдання 12: Виявити конфлікти регіонів пам’яті під час завантаження, що імітують «баги захищеного режиму»

cr0x@server:~$ dmesg | grep -E "BIOS-e820|reserved|System RAM" | head -n 15
[    0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable
[    0.000000] BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000000f0000-0x00000000000fffff] reserved
[    0.000000] BIOS-e820: [mem 0x0000000000100000-0x000000003fffffff] usable

Що це означає: Прошивка маркує певні низькопам’ятні регіони як резервовані. На старих системах ці регіони священні: BIOS data area, ROM, вікна пристроїв.

Рішення: Якщо ваш код у захищеному режимі розміщує таблиці/стек у резервованій низькій пам’яті, ви побачите «випадкові» помилки і корупцію. Перемістіть критичні структури у відомо безпечні регіони і задокументуйте макет.

Завдання 13: Переконатися, що ваш шлях ініціалізації випадково не викликає BIOS‑переривання після перемикання режимів

cr0x@server:~$ ndisasm -b 16 stage2.bin | grep -n "cd 10" | head
412:00000334  CD10              int 0x10

Що це означає: int 0x10 — це BIOS‑відео переривання, призначене для реального режиму. Якщо ця інструкція доступна після PE=1, ви ризикуєте катастрофою.

Рішення: Або робіть роботу BIOS до перемикання режиму, або реалізуйте virtual 8086 / thunking (не на 286), або пишіть нативні драйвери. Не «просто спробуйте».

Завдання 14: Використати логування емулятора для верифікації завантажень дескрипторів

cr0x@server:~$ qemu-system-i386 -M pc -cpu 286 -m 16M -drive file=disk.img,format=raw -d cpu -no-reboot 2>&1 | grep -E "LGDT|LLDT|LIDT" | head
LGDT  base=00008f00 limit=0037
LIDT  base=00009000 limit=03ff

Що це означає: Емулятор показує завантаження регістрів таблиць дескрипторів з base/limit значеннями.

Рішення: Якщо base/limit вказують у підозрілі регіони (ROM або нижче вашого загрузчика), зупиніться і виправте макет. Таблиці дескрипторів мають жити в стабільній RAM, яку не буде перезаписано.

Жарт №2: Відладжувати захищений режим без відомо‑хорошого IDT — як розгортати в п’ятницю без rollback. Можна, але не треба.

Швидкий план діагностики: знайти вузьке місце швидко

Це секція «що перевірити найперше, коли все горить». Використовуйте її, коли шлях завантаження, схожий на 286, зависає, скидається, корумпує пам’ять або поводиться по‑різному на різних машинах/емуляторах.

Перше: доведіть, в якому режимі ви реально знаходитесь

  • Перевірте послідовність ввімкнення захищеного режиму у вашому коді завантажувача (шукайте lmsw або маніпуляції CR0 і far jump).
  • Корелюйте з логами емулятора щодо скидів і винятків. Несподівані скиди часто означають triple fault.

Результат: Якщо ви не входите в захищений режим, припиніть діагностику багів захищеного режиму. Виправте потік керування.

Друге: валідуйте таблиці дескрипторів перед будь‑чим іншим

  • Підтвердьте, що GDT base/limit вказують у діючу RAM і не будуть перезаписані пізніше.
  • Переконайтесь, що дескриптори коду і даних мають розумні base/limit і коректні біти привілеїв.
  • Налаштуйте мінімальний IDT з обробником, що зупиняє або логуватиме.

Результат: Якщо винятки стають «обробленими», ви перетворили хаос на відлагоджувану систему.

Третє: ставтесь до A20 як до жорсткої залежності, а не як до бажаного

  • Зробіть ввімкнення A20 явним і перевіряйте його (у коді або через відомі тест‑патерни).
  • Не змішуйте логіку «A20 можливо ввімкнений» з записами в розширену пам’ять. Це породжує тиху корупцію.

Результат: Якщо корупція зникає при примусовому ввімкненні A20, ваш баг сумісності, а не «випадковий».

Четверте: виключіть виклики BIOS після перемикання режимів

  • Аудитуйте наявність int 0x10, int 0x13 тощо після входу в захищений режим.
  • Або зробіть роботу BIOS раніше, або напишіть нативні драйвери.

Результат: Якщо ви перестанете викликати BIOS у неправильному режимі, зависання зникнуть і надійність повернеться.

П’яте: вирішіть, чи скиди — це дизайн чи дефект

  • Якщо ви спираєтесь на скидання для повернення в реальний режим, реалізуйте збереження/відновлення стану й зробіть шлях скидання детермінованим.
  • Якщо ні — вважайте будь‑який скидання критичним дефектом і виправляйте обробку винятків.

Типові помилки: симптом → корінна причина → виправлення

1) Симптом: випадкова корупція даних після використання «розширеної пам’яті»

Корінна причина: A20 вимкнений або нестабільно переключається; адреси понад 1MB обертаються в низьку пам’ять.

Виправлення: Зробіть ввімкнення A20 явним; перевірте через тест на аліасинг пам’яті; уникайте BIOS‑шляхів або коду, що неявно змінює стан A20.

2) Симптом: миттєве перезавантаження після переходу в захищений режим

Корінна причина: Triple fault через недійсний IDT, поганий дескриптор або некоректно встановлений стек для нового режиму.

Виправлення: Завантажте мінімальний IDT перед увімкненням PE; переконайтесь, що сегменти коду/даних валідні; встановіть SS:SP у безпечний стек захищеного режиму; тестуйте з логами емулятора винятків.

3) Симптом: працює в емуляторі, ламається на конкретному залізі

Корінна причина: Емулятор детермінований або реалізовує A20/скидання по‑іншому; реальне залізо має таймінгові обмеження і чипсетні особливості.

Виправлення: Додайте затримки/полінг там, де потрібно (особливо для 8042‑A20); тестуйте на принаймні одному репрезентативному фізичному пристрої; не покладайтеся на невизначений таймінг.

4) Симптом: код у захищеному режимі працює, але BIOS‑сервіси зависають

Корінна причина: Виклики BIOS з захищеного режиму на 286 без належного thunking (який обмежений на 286).

Виправлення: Виконуйте виклики BIOS у реальному режимі до переключення; або тримайте реальний режимний stub і переходьте через контрольоване скидання; найкраще — писати нативні рутині.

5) Симптом: дивні помилки ліміту при доступі до буферів «в межах»

Корінна причина: Неправильно налаштований сегментний ліміт (дескриптор занадто малий) або використаний неправильний селектор після far call/return.

Виправлення: Визначте дескриптори з правильними base/limit; централізуйте визначення селекторів; додайте assert‑и в debug‑збірках, що перевіряють селектори перед використанням.

6) Симптом: періодичний крах під навантаженням, стабільний при кроковому відлагоджуванні

Корінна причина: Таймінгово‑залежний контроль A20 через 8042, або залежність від неініціалізованої пам’яті таблиць, яка «працює випадково» при уповільненні.

Виправлення: Полінуйте біти статусу контролера правильно; zero‑ініціалізуйте пам’ять таблиць; уникайте самомодифікуючого ініціалізаційного коду; тестуйте на повній швидкості з логуванням.

7) Симптом: «оптимізація» через прямий доступ до апаратури ламається під ОС у захищеному режимі

Корінна причина: Додаток очікує привілеї ring‑0; захищений режим примусово блокує доступ до портів вводу/виводу.

Виправлення: Перенесіть доступ до апаратури в драйвер/сервіс в ring‑0; відкрийте стабільне API; не постачайте додатки, що залежать від невизначених привілеїв.

Чеклісти / покроковий план

Чекліст: безпечне підняття захищеного режиму (стиль 286)

  1. Заблокуйте макет пам’яті: вирішіть, де жити GDT/IDT/stack; уникайте резервованих низькопам’ятних регіонів.
  2. Побудуйте мінімальний GDT: null‑дескриптор + сегмент коду + сегмент даних.
  3. Побудуйте мінімальний IDT: обробник для поширених помилок, що зупиняє/логуватиме; зробіть це перед ввімкненням PE.
  4. Явно ввімкніть A20: не припускайте, що BIOS уже ввімкнув його.
  5. Переключіться в захищений режим: встановіть PE і виконайте required far jump, щоб очистити prefetch і коректно завантажити CS.
  6. Перезавантажте сегментні регістри: DS/ES/SS з валідними селекторами; встановіть безпечний стек.
  7. Робіть по одному кроку: виведіть у debug‑порт, переключіть відомий I/O‑пін або запишіть відому пам’ятну послідовність. Підтвердіть стабільність.
  8. Тільки потім додавайте складність: переключення задач, використання LDT, розділення привілеїв.

Чекліст: вирішуємо, чи зберігати сумісність з DOS/BIOS

  1. Інвентаризація залежностей від BIOS‑переривань: відео, диск, клавіатура тощо.
  2. Класифікуйте кожну залежність: чи її можна виконати перед перемиканням режиму, замінити нативним драйвером, чи вилучити?
  3. Виберіть стратегію переходу:
    • Якщо потрібно повертатися в реальний режим на 286: проектуйте для переходів на основі скиду і реініціалізації стану.
    • Якщо ви контролюєте всю ОС: залишайтесь у захищеному режимі; уникайте BIOS після завантаження.
  4. Тестуйте поведінку A20 під навантаженням: повторні цикли ввімк/вимк, якщо ваш дизайн це торкається.
  5. Запишіть інваріанти: «A20 має бути увімкнений перед записом у розширену пам’ять» — це не дрібниця; це SLO.

Покроково: тріаж «цикла перезавантаження в захищеному режимі»

  1. Увімкніть логування емулятора для скидів і винятків.
  2. Підтвердіть, що ви завантажуєте IDT перед встановленням PE.
  3. Валідуйте GDT base/limit і наявність дескрипторів з коректними типами.
  4. Підтвердіть far jump після ввімкнення PE і що CS селектор вказує на сегмент коду.
  5. Встановіть SS:SP у відомо‑хороший стек захищеного режиму на ранньому етапі.
  6. Якщо все ще перезавантажується, додайте маленький обробник для #GP і #DF, що зупиняє виконання; зупиніть цикл і прочитайте стан.

FAQ

1) Чому захищений режим «врятував ПК»?

Він ввів апаратно‑захищені межі. Це фундамент для стабільних мультизадачних ОС, безпечної ізоляції і можливості виконувати складні робочі навантаження без того, щоб одна програма володіла всією машиною.

2) Чому він «мучив розробників» саме на 286?

Бо DOS і BIOS були еко‑середовищем реального режиму, а 286 не пропонував чистого, швидкого, архітектурно благословенного шляху назад у реальний режим після ввімкнення захисту. Розробники мусили обирати: сумісність чи можливості — або хитромудрі переходи.

3) Чи міг 286 запускати справжню операційну систему?

Так. Захищений режим існує спеціально для підтримки ОС‑фіч: захист пам’яті, розділення привілеїв, структуровані переривання. Просто світ ПЗ ПК все ще дуже залежав від конвенцій реального режиму.

4) У чому практична різниця між сегментацією в реальному режимі і в захищеному?

Сегментація в реальному режимі — арифметична; в захищеному режимі — політика, яку реалізують дескриптори (base, limit, privileges). У захищеному режимі CPU може заборонити доступ до пам’яті, до якої не варто доступатися.

5) Чому перемикач A20 такий важливий?

Бо якщо A20 вимкнений, пам’ять понад 1MB відображається у низьку пам’ять. Це може тихо корумпувати критичні структури. Це класичний механізм зворотної сумісності з різким режимом відмови.

6) Чому DOS просто не перейшов у захищений режим і не вирішив проблему?

Припущення дизайну DOS — однозадачність, залежність від BIOS, прямий доступ до апаратури — не узгоджуються з обмеженнями захищеного режиму. До того ж величезна встановлена база і тиск сумісності були колосальними. Еволюція йшла шарами: менеджери пам’яті, extender‑и і зрештою нові ОС.

7) Що 386 змінив, щоб зробити захищений режим більш зручним?

386 додав paging і зробив переходи режимів і стратегії сумісності значно гнучкішими (включно з virtual 8086 mode). Це дозволило практичну історію «запустити старе і будувати нове».

8) Чи це всього ще релевантно, якщо ми всі на x86‑64?

Так. Ваша машина все одно проходить стадії завантаження через сумісні з реальним режимом етапи, усе ще має справу зі припущеннями прошивки і залежить від чистих меж привілеїв. Конкретика змінилась; патерни відмов — ні.

9) Яка одна найкраща звичка, щоб уникнути багів типу 286?

Зробіть інваріанти явними і тестуйте їх: стан A20, розміщення таблиць дескрипторів, наявність IDT, і «без викликів BIOS після переключення режиму». Трактуйте їх як guardrail‑и для продакшену.

Наступні кроки, які реально використовувати

Якщо ви будуєте або відлагоджуєте щось, що торкається раннього завантаження, прошивки або низькорівневих переходів x86, серйозно сприйміть урок 286: можливість без безпечного плану переходу стає генератором інцидентів.

  1. Запишіть правила меж режимів (що виконується в реальному режимі, що — в захищеному, і що заборонено після перемикання).
  2. Додайте мінімальний IDT якомога раніше, щоб винятки були діагностовані, а не перетворювалися на цикли скидання.
  3. Зробіть роботу з A20 явною і перевіряйте її повторюваним тест‑патерном — не покладайтеся на фольклор.
  4. Тестуйте у двох середовищах: детермінована емуляція для відладки, плюс принаймні одна фізична ціль, щоб впіймати таймінг/чипсетні особливості.
  5. Віддавайте перевагу нудній коректності перед хитромудрими переходами. Хитромудрий шлях зазвичай — це скидання в плащі‑тренчі.

286 був поворотним моментом: він намагався потягнути ПК у світ, де ОС керує машиною. Це був правильний напрям. Але це було й брудно. Якщо ви керуєте продакшн‑системами, це має звучати дивно знайомо.

← Попередня
Ubuntu 24.04: обмеження завантаження PHP — виправте upload_max_filesize там, де це справді важливо (випадок №10)
Наступна →
Ubuntu 24.04: Продуктивність упала після оновлення — перші 6 перевірок, що виявляють винуватця

Залишити коментар