Как реально реализован защищенный режим в CPU?

Я знаю, что такое реальный и защищенный режим выполнения процессора. Но как CPU реализует защищенный режим? AFAIK cpu делает все, что программа говорит ему делать. Где реализовано поведение защищенного режима? Играет ли в этом какую-то роль ОС?


4

В регистре есть бит, который указывает, находится ли ЦП в настоящее время в режиме ядра или в режиме пользователя. Программный код не может изменять этот бит напрямую: он должен использовать специальные инструкции, которые делают больше, чем просто изменяют этот бит. Некоторые инструкции можно использовать только в режиме ядра; попытка использовать их в пользовательском режиме вместо этого запускает ловушку.

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

Все это было бы бесполезно если пользовательский код может изменить содержимое памяти, содержащей код ядра или данные. Это не может быть связано с блоком управления памятью (MMU). Одна из функций MMU — ограничить доступ к памяти в данный момент. Память может быть помечена как доступная только для ядра, а переключение между режимом ядра и пользовательским режимом может изменить таблицу прав доступа. Конфигурация MMU может быть изменена только при работе в режиме ядра. Таким образом, ядро ​​может настроить память так, чтобы оно могло получить доступ к своему собственному коду и данным, но пользовательский код не мог.

Ядро фактически поддерживает одну конфигурацию MMU для каждого пользовательского процесса. Перед переключением в пользовательский режим ядро ​​изменяет конфигурацию MMU в соответствии с конфигурацией процесса, который будет выполняться после переключения в пользовательский режим.

Практически любой достаточно продвинутый процессор следует этой базовой модели: не только любой ПК, произведенный за последние 30 лет, но также любой смартфон и даже множество встраиваемых устройств.

Я многое упростил. В реальном мире может быть более двух уровней привилегий, больше режимов процессора, больше аспектов конфигурации MMU и т. Д. Вы понимаете, как это работает. Если вы хотите разобраться в коде ядра или написать свой собственный, вам нужно будет прочитать о конкретной архитектуре процессора более подробно.

«Реальный режим» — исторический аспект процессоров x86.. Последние пару десятилетий это не было актуально в реальном мире, за исключением некоторых редких встраиваемых систем x86 и некоторых людей, пишущих загрузчики или использующих действительно устаревшее устаревшее программное обеспечение.

Улучшить этот ответ
ответ дан 22 августа 2018 в 22:44
  • Пользовательский режим и защищенный режим — это одно и то же, и еще кое-что. В реальном режиме программа имеет доступ ко всей памяти, или она просто имитирует среду 1 Мб с 20-контактным адресным процессором (где он и был назначен насколько мне известно) ? — humble_wolf 05 сен. ’18 в 2:39
  • @humble_wolf Забудьте о реальном режиме и защищенном режиме. Реальный режим — это вещь с обратной совместимостью на 32-битных процессорах x86, которая используется очень недолго во время загрузки (и никогда не используется на современных ПК с 64-битным процессором и загрузчиком UEFI). Только защищенный режим актуален для реального мира в 21 веке. Пользовательский режим и режим ядра — это несвязанная концепция, которая существует (с некоторыми вариациями) на большинстве процессоров сегодня. Код ядра работает в режиме ядра, а программный код — в пользовательском режиме. — Жиль ‘SO — перестань быть злым’ 6 сен ’18 в 22:04
  • Хорошо, и еще кое-что, только потому, что ОС запускается первой на процессоре, она может обеспечить защищенный режим, я прав? — humble_wolf 7 сен ’18 в 3:15
  • 1
    @humble_wolf ОС (точнее, ядро) запускается в режиме ядра. Он настраивает вещи (векторы прерываний, MMU,…) так, чтобы только код ядра мог работать в режиме ядра. Первый фрагмент кода, который запускается на ЦП, называется загрузчиком, и он также работает в режиме ядра и загружает ядро ​​(или следующий этап загрузчика). Действительно, с точки зрения безопасности, тот, кто запускается первым, решает, что происходит в системе, и отвечает за настройку системы так, чтобы только доверенный код работал в привилегированном режиме. — Жиль ‘SO — перестань быть злым’ 7 сен 2018 в 6:56
  • Итак, загрузчики могут быть ахиллесовой пятой по отношению к. безопасность в компьютере? Может быть заражен, чтобы нанести какой-либо вред компьютеру? — humble_wolf 07 сен ’18 в 9:12
| показать 1 дополнительный комментарий

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

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

Все это было бы бесполезно если пользовательский код может изменить содержимое памяти, содержащей код ядра или данные. Это не может быть связано с блоком управления памятью (MMU). Одна из функций MMU — ограничить доступ к памяти в данный момент. Память может быть помечена как доступная только для ядра, а переключение между режимом ядра и пользовательским режимом может изменить таблицу прав доступа. Конфигурация MMU может быть изменена только при работе в режиме ядра. Таким образом, ядро ​​может настроить память так, чтобы оно могло получить доступ к своему собственному коду и данным, но пользовательский код не мог.

Ядро фактически поддерживает одну конфигурацию MMU для каждого пользовательского процесса. Перед переключением в пользовательский режим ядро ​​изменяет конфигурацию MMU в соответствии с конфигурацией процесса, который будет выполняться после переключения в пользовательский режим.

Практически любой достаточно продвинутый процессор следует этой базовой модели: не только любой ПК, произведенный за последние 30 лет, но также любой смартфон и даже множество встраиваемых устройств.

Я многое упростил. В реальном мире может быть более двух уровней привилегий, больше режимов процессора, больше аспектов конфигурации MMU и т. Д. Вы понимаете, как это работает. Если вы хотите разобраться в коде ядра или написать свой собственный, вам нужно будет прочитать о конкретной архитектуре процессора более подробно.

«Реальный режим» — исторический аспект процессоров x86. Последние пару десятилетий это не было актуально в реальном мире, за исключением некоторых редких встраиваемых систем x86 и некоторых людей, пишущих загрузчики или запускающих какое-то действительно старое устаревшее программное обеспечение.


2

Защищенный режим и реальный режим — это два подрежима Legacy Mode архитектуры x86-64. Одно из различий между ними заключается в том, как вычисляются физические адреса. Еще одно важное отличие заключается в том, что в защищенном режиме вводится концепция уровней привилегий и атрибутов защиты чтения/записи/выполнения для изоляции и защиты различных программ друг от друга.. ЦП использует значение CR0 [PE] (CR0 — это просто регистр) при каждом доступе к памяти (чтение данных из памяти, запись данных в память и выборка кода), чтобы определить, находится ли он в реальном или защищенном режиме. Если CR0 [PE] равен 0, ЦП находится в реальном режиме. В противном случае, если CR0 [PE] равен 1, ЦП находится в защищенном режиме. ОС может изменить режим с помощью инструкции MOV CRO . Все инструкции, следующие за MOV CRO , будут выполнены в новом режиме. Для получения дополнительной информации вы можете обратиться к Intel SDM Volume 3.

Улучшите этот ответ
отредактировано 22 августа 2018 в 22:11
ответил 22 августа 2018 в 21:59
  • 1
    Я думаю » Одно из различий между ними заключается в том, как вычисляются физические адреса «. неадекватно описывает, в чем разница по отношению к безопасности. Не сомневаюсь, что ваш ответ технически правильный, но это безопасность.SE 🙂 Опять же, это довольно широкий вопрос. — Маартен Бодевес, 22 августа 2018, 22:09
  • 1
    @MaartenBodewes Согласен. Я добавил немного подробностей. — Хади Брайс 22 авг., ’18 в 22:12
добавить комментарий |

Защищенный режим и реальный режим — это два подрежима унаследованного режима архитектуры x86-64. Одно из различий между ними заключается в том, как вычисляются физические адреса. Еще одно важное отличие состоит в том, что защищенный режим вводит понятие уровней привилегий и атрибутов защиты чтения/записи/выполнения для изоляции и защиты различных программ друг от друга. ЦП использует значение CR0 [PE] (CR0 — это просто регистр) при каждом доступе к памяти (чтение данных из памяти, запись данных в память и выборка кода), чтобы определить, находится ли он в реальном или защищенном режиме. Если CR0 [PE] равен 0, CPU находится в реальном режиме. В противном случае, если CR0 [PE] равен 1, ЦП находится в защищенном режиме. ОС может изменить режим с помощью инструкции MOV CRO . Все инструкции, следующие за MOV CRO , будут выполнены в новом режиме. Для получения дополнительной информации вы можете обратиться к Intel SDM Volume 3.



РАЗРАБОТКА ОПЕРАЦИОННОЙ СИСТЕМЫ ПЕРЕХОДИТ В ЗАЩИЩЕННЫЙ РЕЖИМ

В В предыдущем разделе этого руководства по написанию вашей собственной игрушечной операционной системы мы обсудили память и сосредоточились на 21-й адресной строке («строка A20»), которую необходимо включить, прежде чем мы сможем получить доступ к полные 4 ГБ памяти, что необходимо для перехода в защищенный режим. Пришло время перейти в защищенный режим.

Фактически, все, что мы сделали в последних нескольких статьях, — это подготовка к переходу в защищенный режим. Мы настроили глобальную таблицу дескрипторов (GDT), таблицу дескрипторов прерываний (IDT) и включили линию A20. Остается только перейти в защищенный режим, в котором мы наконец-то сможем выполнить 32-битный код, чтобы сосредоточиться на ядре.

Эта статья является частью серия по разработке операционной системы для игрушек.

Просмотр указателя серии

В предыдущем разделе этого руководства для написания собственной игрушки операционной системы , мы обсудили память и сосредоточились на 21-й адресной строке («строка A20»), которую необходимо включить, прежде чем мы сможем получить доступ ко всем 4 ГБ памяти, что является предварительным условием для входа в защищенный режим. Пришло время перейти в защищенный режим.

Фактически, все, что мы сделали в последних нескольких статьях, — это подготовка к переходу в защищенный режим. Мы настроили глобальную таблицу дескрипторов (GDT), таблицу дескрипторов прерываний (IDT) и включили линию A20. Остается только перейти в защищенный режим, где мы, наконец, сможем выполнить 32-битный код, чтобы сосредоточиться на нашем ядре.

Эта статья является частью серии по разработке игрушечной операционной системы.

Просмотр указателя серии

Регистры управления

То, что мы видели до сих пор при развертывании наших собственных загрузчиков первого и второго этапов, — это знакомые регистры процессора: AX , BX , CX , DX , такие сегменты, как CS , DS , ES , SS , указатель инструкции IP и указатель стека SP . Процессоры 80386+ фактически вводят некоторые новые регистры, которые станут важными, когда мы перейдем к 32-битному программированию.

Во-первых, существующие регистры становятся шире. Если раньше у нас был доступ к AX (шириной 16 бит), скоро у нас будет доступ к EAX (шириной 32 бита), а также к EBX , ECX и EDX . Мы также получим дополнительные сегментные регистры ( FS и GS ). Точно так же указатель инструкции становится EIP (снова 32 бита) и так далее. Это замечательно и не требует особых объяснений.

Однако мы получаем и другие регистры. Процессор Intel 80386 оснащен набором регистров управления, и нам понадобится один из них для переключения в защищенный режим, так что мы можем поговорить об этом сейчас. Эти регистры управления изменяют или управляют поведением ЦП. Это включает в себя управление прерываниями, переключение режима адресации, пейджинг и управление сопроцессором. Новые регистры называются CR0 , CR1 , CR2 , CR3 и CR4 .

Первый контрольный регистр, CR0 , имеет различные контрольные флаги, которые изменяют базовую работу процессора..

Bit Имя Полный имя Описание
31 PG Пейджинг Если 1, включить пейджинг и использовать регистр CR3, иначе отключить пейджинг
30 CD Отключить кеш Глобально включает/отключает кеш памяти
29 NW Без записи через Глобально включает/отключает кэширование обратной записи
18 AM Маска выравнивания Проверка выравнивания включена, если Установлен AM, установлен флаг AC (в регистре EFLAGS) и уровень привилегий равен 3
16 WP Защита от записи Определяет, может ли ЦП писать на страницы, отмеченные только для чтения
5 NE Числовая ошибка Включить внутренний отчет об ошибках с плавающей запятой x87, если он установлен, иначе включает обнаружение ошибок x87 в стиле ПК
4 ET Тип расширения На модели 386 он позволял указать, был ли внешний математический сопроцессор 80287 или 80387
3 TS Задача переключена Позволяет сохранять контекст задачи x87 при переключении задачи только после использования инструкции x87
2 EM Emulation Если установлено, модуль с плавающей запятой x87 отсутствует, если он отключен, присутствует FPU x87
1 MP Монитор сопроцессора Управляет взаимодействием инструкций WAIT/FWAIT с флагом TS в CR0
0 PE Включить защищенный режим Если 1, система находится в защищенном режиме, иначе система находится в реальном режиме

Некоторые из этих битов станут важными для нас позже, но пока нас интересует самый первый бит: бит PE . Он включает защищенный режим.

Переключение в защищенный режим

Чтобы переключиться в защищенный режим, все, что нам нужно сделать, это включить бит PE в CR0 , например:

  .macro mGoProtected mov eax, cr0 или eax, 1 mov cr0, eax.endm   

Очистка очереди предварительной выборки

Установив бит PE в регистре CR0 , мы только что перешли в защищенный режим. Это означает, что все инструкции теперь в 32-битном формате. В результате некоторые из них кодируются по-другому. Некоторые инструкции могут занимать больше байтов в своей двоичной форме, другие — меньше, а другие остаются неизменными. В любом случае, мы пока не можем продолжать выполнение кода из-за очереди предварительной выборки .

Видите ли, процессоры созданы, чтобы быть быстрыми. Одна из уловок, которая делает процессоры еще более быстрыми, — это заставить процессор загружать из памяти ряд инструкций, которые должны выполняться одновременно, а не одну. Это называется предварительной выборкой. В конце концов, ЦП в процессоре Intel 80386 может читать 4 байта (32 бита) одновременно из памяти, а это вполне может быть более одной инструкции. По техническим причинам даже больше может быть прочитано и декодировано до того, как оно будет фактически выполнено ЦП.

Следствием этого является то, что ЦП мог прочитать некоторые инструкции из памяти, когда он все еще находился в состоянии бит, декодировал их и теперь готов к их выполнению. Они не будут работать, потому что процессор теперь находится в защищенном 32-битном режиме!

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

  .macro mClearPrefetchQueue jmp clear_prefetch_queue: nop nop clear_prefetch_queue: .endm  

Есть некоторые nop после перехода, чтобы дважды убедиться, что очередь предварительной выборки полностью опустошена.

Настройка регистров 80386

Мы поговорили о разговоре, теперь пора прогуляться. Затем мы настроим сегменты памяти, которые будет использовать наш будущий код ядра. Это больше не делается путем ввода адресов памяти, а путем указания номеров селекторов. Мы установим все наши сегменты данных ( ds , es , fs и gs ), а также сегмент стека ( ss ) для использования селектора 2 из глобальной таблицы дескрипторов, который соответствует сегменту данных, который мы определили в нашем GDT:

  .macro mSetup386Segments mov ax, 0x10 # Смещение байта для селектора 2 mov ds, ax # (помните, каждый дескриптор составляет 8 байтов) mov es, ax mov fs, ax mov gs, ax mov ss  , ax mov esp, 0x2ffff # Установить стек на возрастание вниз от 0x30000.endm  

Переход к ядру

Да! Предполагая, что наш загрузчик второй ступени ранее загрузил наш образ ядра в память по линейному адресу 0x20000 (с использованием того же чтения FAT в процедурах чтения файлов, которые мы уже разработали для загрузчика первой ступени), теперь мы можем перейти к нему и начать выполняя его.

Переход к ядру должен выполняться с помощью 32-битной инструкции перехода. Здесь мы сталкиваемся с небольшой загвоздкой. Весь код в нашем загрузчике второго уровня — это 16-разрядный код, потому что он компилируется именно так. Следовательно, мы не можем фактически указать 32-битный длинный переход; он будет скомпилирован как 16-битный переход. Чтобы обойти это, мы сами закодируем инструкцию длинного перехода, как это сделал бы 32-битный ассемблер.

Наша инструкция длинного перехода перейдет на адрес линейной памяти 0x20000 в первом селекторе. GDT (наш сегмент кода) со смещением 0x8:

  .macro mJumpToKernel .byte 0x66 .byte 0xEA .int 0x20000 # смещение .word 0x0008 # селектор word.endm   

Это передаст управление коду ядра, который нам еще предстоит написать. Если вы любите приключения, почему бы не написать небольшую 32-битную ассемблерную программу, которая помещает значение 0x41 в линейный адрес 0xb8000? Это отобразит букву «A» в верхнем левом углу экрана и может быть выполнено в защищенном режиме (вы больше не можете использовать прерывания BIOS для записи на экран).

Собственно, мы все равно сделаем это в следующей части этого руководства!

Резюме

Уф! Это была настоящая поездка, но теперь мы достигли защищенного режима и готовы написать простое ядро. По крайней мере, на этом этапе вся память машины и функции защищенного режима будут в нашем распоряжении.

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

  • Мы включили PE-бит в регистре CR0, таким образом переключившись в защищенный режим.
  • Мы очистили очередь предварительной выборки, так что нет 16 -битные инструкции остались в ЦП, которые больше не могут быть выполнены
  • Мы настроили регистры для использования 32-битной программой ядра
  • Мы выполнили длинный переход к коду ядра

Переходите к следующей части этого руководства!

Оцените статью
logicle.ru
Добавить комментарий