company_banner

Виртуализация⁰

    Классическая теория эффективной виртуализации и обзор состояния индустрии в целом описаны в моей предыдущей публикации. В этой статье речь пойдёт о поддержке виртуализации в широком смысле в архитектуре Intel IA-32.


    Черепаха на спине черепахи на спине черепахи на спине… — космологическая теория, которой придерживаются создатели виртуальных машин.

    Эта статья — нулевая в небольшой серии о технологиях Intel, помогающих представить компьютер не тем, чем он в реальности является, для программ (в т.ч. операционных систем, BIOS и прошивок), на нём запущенных.
    В ней не будет говориться о настройке конкретных VMM, прозрачной миграции виртуальных машин, создании невидимых руткитов и многих других интереснейших вещах, произрастающих из этой фрактальной и потому неисчерпаемой темы. Мой взгляд будет с позиции системного программиста, занимающегося разработкой операционных систем и firmware или мониторов виртуальных машин и симуляторов, а также всех им сочувствующих.


    Я начну с описания трёх режимов работы процессоров Intel IA-32, появившившихся ещё в 20 веке, когда настоящая виртуализация упоминалась лишь в контексте мэйнфреймов, имевших совсем других производителей и архитектуру. Это будет в некотором роде исторический экскурс. Два из этих режимов всё ещё часто используются, и системным программистам неизменно приходится иметь с ними дело.

    Защищённый режим


    Protected mode (и его разновидности) — наверное, самый привычный и вездесущий режим, с которым взаимодействует большинство современных пользователей персональных и мобильных компьютеров на процессорах IA-32. По сравнению с самым первым (однозадачным) защищённость этого режима проявляется в двух механизмах:
    • Виртуальная память. Пользовательским приложениям показывают огромный плоский массив длиной в 2³² или больше байт. По одному такому масиву на каждую программу, и делиться им ни с кем не надо. Тогда как на самом деле операционная система располагает несколько другим ресурсом — памятью физической — и ловко незаметно подкладывает лишь те участки, к которым приложения обращаются в данный момент.
    • Гибкий механизм обработки исключений. Приложения по умолчанию не видят ни внешних прерываний, ни активности ОС по обеспечению работы виртуальной памяти. Более того, они могут даже попытаться исполнить несуществующие на данном ЦПУ инструкции, и ОС может незаметно для них отработать нужную семантику вместо отсутствующего железа (при такой эмуляции, конечно, проявится немалый проигрыш в скорости). Для рядового приложения ОС проявляет себя только через одну-две инструкции системных вызовов, которые для приложения выглядят как своеобразные вызовы сторонних процедур.

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

    VM86


    Virtual 8086 — режим, который был должен помочь перевести пользователей приложений MS-DOS на новые, современные многозадачные ОС и на новые процессоры, их поддерживающие. На этот раз реальная железка, которую виртуализовывали, существовала — это был Intel 8086. Защищённый режим, введённый в 80286 процессоре, оказался непохожим и потому крайне неудобным для запуска приложений, ожидавших честный 8086. Новых приложений для защищённого режима ещё не написали, а от старых DOS-программ никто отказываться не хотел. В следующем продукте Intel — 80386 — появился особый тип аппаратной задачи, более точно создающий привычную для них среду, с 20-битными адресами и сегментами, возможностью прямой записи в устройства и т.п. И снова специальный программный монитор следит за всеми исполняющимися в VM86-режиме приложениями, прозрачно для них подкладывая страницы памяти, эмулируя устройства, а иногда и просто не разрешая выполнять операции: если одна DOS-программа решила перезагрузить компьютер, то лучше не давать ей это сделать.

    Необходимость добавления VM86-режима была обусловлена экономическими причинами: невозможно было сразу переписать (и даже просто убедить всех в необходимости) все DOS-приложения так, чтобы они использовали возможности защищённого режима. Сейчас, когда прикладное ПО сразу пишется для него, необходимость в VM86 понизилась. Однако все современные процессоры всё равно поддерживают режим Virtual 8086: некоторый код, такой как Option ROMs периферийных устройств, вполне может ожидать наличие старого доброго 8086.

    SMM


    Наименее известный простому человеку — это System Management Mode. Придуман он был также в эпоху 80386, когда люди задумались о необходимости управления энергопотреблением портативных систем, был опубликован стандарт APM, а затем все осознали, что популярная в то время MS-DOS вряд ли сможет быть под него адаптирована. Поэтому функциональность управления питанием решили спрятать от ОС в специальный SMM-режим, в который процессор входит при поступлении особого типа прерывания — #SMI (System Management Interrupt). По отношению к системным ресурсам SMM очень похож на реальный режим 8086 (кто найдёт различие — прошу обозначить в комментариях к статье). Отличается он полной непробиваемостью: из SMM невозможно выйти ни по обычному, ни по немаскируемому, казалось бы, прерыванию, а только по желанию самого кода, с помощью инструкции RSM. Заблокировать же вход в него ОС не может никак: даже если она находится в самой критичной фазе своей работы — обработчике прерываний, — пришедший #SMI без рассуждений вытеснит текущий контекст в отдельную область памяти (чтобы при RSM из неё его же и восстановить) и загрузит состояние, необходимое для входа в SMM. Это особенно опасно для систем реального времени. Увидеть содержимое кода обработчика #SMI в памяти, т.н. SMRAM, тоже совсем непросто: попытки доступов вернут или ошибку, или же… чистый лист.

    И всё бы ничего, если бы после использования для нужд APM создатели BIOS больше никогда не возвращались к SMM. Однако совершать действия без необходимости кооперации с ОС — очень привлекательная возможность. И вот в обработчик SMM начали пихать всё подряд: обработку ошибок аппаратуры, слежение за температурными датчиками, сервисы для защиты информации, эмуляцию PS/2 клавиатуры и мыши из их USB-вариантов, противное пикание через динамики, если на клавиатуру прыгнула кошка, и, конечно же, необнаруживаемые руткиты.
    Режим этот живёт и здравствует, и следущие в моём рассказе способы виртуализации вынуждены учитывать его особенности.
    Лично я имел несчастье пострадать от корявого SMM-кода: на моём старом нетбуке на пятой минуте работы после включения жёсткий диск переводился в какой-то энергосберегающий режим, совершенно неожиданно для Linux, которая при этом начинала писать жалобные сообщения в dmesg, а потом замирала.

    Продолжение следует

    В следующей, первой части статьи я расскажу о настоящей виртуализации для IA-32 — Intel VTx, её принципах работы и эволюции. После единицы, как известно, следует двойка, а поэтому и во второй части будет о чём рассказать. На самом деле, там начнётся самое интересное.

    Спасибо за внимание!
    • +30
    • 22,1k
    • 7
    Intel 194,90
    Компания
    Поделиться публикацией
    Похожие публикации
    Комментарии 7
    • +1
      Раз статья 0, позволю себе немного оффтопный вопрос.

      Что будет, если при переходе в защищенный режим загрузить в SS селектор сегмента, который описан как сегмент данных (Бит ED = 0 в байте AR дескриптора, т.е. признак «рост вниз» отключен)? Точнее — я знаю, что ничего не случится, стек будет работать как обычно, но зачем тогда этот бит?
      • 0
        В документации говорится, что для стека можно использовать сегменты, растущие как вверх, так и вниз. Разница только в том, что expand-down удобнее для стеков, ёмкость которых планируется динамически изменять. Для статически выделенных оба значения бита E хороши.

        Вот выдержка из Intel SDM, том 3A, глава 3, секция 3.4.5.1 «Code- and Data-Segment Descriptor Types», страницы 1997 и 1998:
        If the size of a stack segment needs to be changed dynamically, the stack segment can be an expand-down data segment (expansion direction flag set). Here, dynamically changing the segment limit causes stack space to be added to the bottom of the stack. If the size of a stack segment is intended to remain static, the stack segment may be either an expand-up or expand-down type.
        • 0
          То-есть, сегмент, который «растет вниз» действительно растет вниз, от базового адреса в сторону младших, т.е. не last address=base + size, а last address = base-size, так что ли?
          • 0
            Ниже тов. CleverMouse совершенно правильно всё описал. Вообще в документации Intel вполне ясно это всё описывается, однако не объясняется, почему всё так, а не иначе.

            Я планирую написать отдельную заметку про преобразования адресов IA-32 и в ней краешком упомянуть сегментацию. Но если этот вопрос интересен, то можно сделать и отдельный (и наверное довольно длинный) пост про сегментацию.
            • 0
              Ага, спасибо. Я изучал PM по русскоязычным учебникам ассемблера, там очень плохо было это описано. Потом уже добрался до англоязычного руководства по 386, но именно этот вопрос тогда уже не интересовал. А сейчас внезапно вспомнил. Писал тогда свою ОС, вспоминаю проект с теплотой.
        • +1
          Бит ED не имеет отношения к тому, как будет использоваться селектор — хоть для стека, хоть для данных. Он влияет на проверки сегментной защиты — если ED=0, то сегментная защита разрешает адреса от 0 до лимита, если ED=1, то сегментная защита разрешает адреса от лимита+1 до максимального. Во время проектирования — в 286-х — сегментная защита была единственным средством защиты, и expand-down сегменты предполагалось использовать для растущих стеков — условно, когда в начале работы программы ей выделяется N байт «вверху» сегмента с SS с выбросом исключения, когда SP опускается ниже выделенного размера, и при необходимости программа запрашивает у системы больше стекового пространства, уменьшая лимит. Отсюда и фразы в документации про «dynamic stack segments», хотя, я повторюсь, ED не имеет отношения к собственно стеку. Потом появилась страничная защита, которая, хоть и с двумя уровнями привилегий против 4-х сегментных, оказалась намного удобней, и flat-модель памяти, в которой сегментные проверки фактически отключены, но выкидывать разные странные биты было уже поздно в интересах совместимости.
        • 0
          не туда

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое