Переход в пользовательский режим с использованием iret

Я пишу небольшую ОС, которая выполнит некоторый код в пользовательском режиме (уровень привилегий 3). Из этого кода пользовательского уровня я хочу вызвать прерывание обратно в ОС, которая печатает сообщение. Сейчас мне все равно, как мой обработчик прерываний принимает аргументы или что-то в этом роде, я просто хочу, чтобы обработчик прерываний сообщил мне (пользователю), что код выполнен.

Мой вопрос: как мне запустить код в пользовательском режиме? У меня есть функция, которая устанавливает таблицу локального дескриптора с сегментом кода и сегментом данных (оба с привилегиями пользовательского режима). Я не понимаю, как я должен загружать эти сегменты в cs, ss и ds. Я успешно загружаю свой LDT, но я не знаю, как его использовать. Я слышал, что я должен использовать iret, но я не понимаю, как именно.

Другой вопрос, который у меня есть, - это то, как должен работать обработчик прерываний. Скажем, я устанавливаю обработчик прерываний для вектора 0x40, который я хочу напечатать "привет, пользовательский режим!". Я знаю, как настроить обработчик прерываний, но я не совсем понимаю, как будет переключен контекст при вводе обработчика прерываний ядра из пользовательского режима. Я знаю, что регистр cs должен измениться, так как моя подпрограмма будет выполняться из сегмента кода, указанного в моей записи IDT. Я также понимаю, что селектор стека, вероятно, тоже изменится, но я не могу быть уверен в этом.

Может ли кто-нибудь объяснить мне, какие изменения в контексте происходят при вызове шлюза прерывания?

Ответ 1

Как добраться до кольца 3 можно с помощью iret, потому что способ его работы документирован. Когда вы получаете прерывание, процессор толкает:

  • Сегмент стека и указатель (ss: esp), как 4 слова
  • EFLAGS
  • Сегмент кода возврата и указатель инструкции (cs: eip), как 4 слова
  • Код ошибки, если требуется.

iret работает, отменяя шаги 1-3 (ISR отвечает за отмену шага 4, если это необходимо). Мы можем использовать этот факт, чтобы добраться до кольца 3, нажав требуемую информацию в стек и выдав инструкцию iret. Убедитесь, что у вас есть правильный CPL в сегментах кода и стека (в каждом из них должны быть установлены два младших бита). Однако iret не изменяет ни одного из сегментов данных, поэтому вам нужно будет вручную их изменить. Для этого вы используете инструкцию mov, но вы не сможете читать данные за пределами стека между выполнением этого и переключением колец.

cli
mov   ax, Ring3_DS
mov   ds, eax
push  dword Ring3_SS
push  dword Ring3_ESP
pushfd
or    dword [esp], 0x200 // Set IF in EFLAGS so that interrupts will be reenabled in user mode
push  dword Ring3_CS
push  dword Ring3_EIP
iret

Если вам нужен полный рабочий пример, см. этот учебник.


Когда выдается прерывание, процессор считывает ваш IDT, чтобы получить правильный сегмент кода и указатель инструкции для ISR. Затем он смотрит на ваш TSS, чтобы найти новый сегмент стека и указатель. Он соответствующим образом изменяет ss и esp, а затем ставит старые значения в новый стек. Он не меняет ни одного из регистров сегмента данных. Вы должны сделать это вручную, если вам нужно получить доступ к памяти в вашем ISR.

Ответ 2

Вы также можете сделать retf. Дальнейший возврат к менее привилегированному сегменту кода приведет к выходу новых ss и sp из привилегированного стека.

Просто убедитесь, что вы даете много результатов для дальних вызовов и irets для прерываний. Единственное различие между ними - наличие флагов в стеке, но разумно не смешивать их.

Кроме того, не забывайте, что исключения иногда вызывают коды ошибок в стеке.