Учим систему страничной адресации и обработке прерываний

    Приветствую. Сегодня поговорим обо всём понемногу. Введём в нашу наработку paging, разберёмся с прерываниями и их видами. Напишем несколько функций, добавим сие в код из предыдущего поста.


    1) Paging.
    Все, наверно, уже покопались в манах и знают, что за лепота этот paging. Вырываемся за пределы 1 Мб и можем адресовать все 4 гигабайта оперативки. Но зачем нам в нашей скромной поделке 4 Гб (тем более что не у всех есть столько RAM’а). Нам вполне будет достаточно 32Мб (ну, это тоже очень много, но давайте помечтаем). Итак, будем использовать 4Кб’айтные страницы. Так, теперь считаем – 1024 страницы описывают 4Мб. Нам, значит, нужно всего-то 1024*8 страниц. Иными словами нужно завести 8 таблиц и 1 каталог. Ещё, для удобства, страницы будут тождественными т.е логический и физический адреса совпадают. Теперь, давайте напишем функцию, которая сформирует всё необходимое.

    set_pages:

    .set_cat:
    mov edi,100000h;базовый адрес директории
    mov eax,101007h;базовый адрес таблицы страниц и флаги

    mov cx,8 ;8*4Мб=32Мб

    .fill_cat_usef: ;опишем таблицы страниц
    stosd
    add eax,1000h
    loop .fill_cat_usef

    mov cx,1016 ;а остальное забьём нулями
    xor eax,eax

    rep stosd

    mov eax,00000007h
    mov ecx,1024*8;32Mb

    .fill_page_table: ;теперь опишем страницы
    stosd
    add eax,1000h
    loop .fill_page_table

    ;End;
    mov eax,00100000h;1 Mb;и установим базовый адрес первого каталога в cr3
    mov cr3,eax
    mov eax,cr0
    or eax,80000000h

    mov cr0,eax

    ret


    Возможно у вас возникнет вопрос, зачем же забивать нулями другие каталоги? А не забываем, что располагать таблицы, как и каталоги, можно по адресам, кратным 1000h.

    2) Interrupts.
    И с прерываниями PM преподносит сюрпризы: нет больше 4-ых байтных векторов прерываний, как в RM. На их место пришли Interrupt Gates (для нас сейчас важны именно они), Trap Gates, Task Gates – 64-битные структуры, находящиеся в IDT. И структура такова:

    image

    Здесь назначение полей ясно (тем более, что некоторые из них встречались нам ещё в segment descriptor’ах). Мы пока рассмотрим только Interrupt Gates. IDT нужно составить до перехода в PM, загрузить размер и смещение в регистр IDTR (его структура аналогична структуре GDTR) командой lidt. Кстати, IDT – сегмент (скажем так, антипод GDT, которая просто в линейном адресном пространстве находится) т.е она располагается в определённом сегменте. Например, если вы до перехода в PM описали, к примеру, 4Кб памяти, а располагаете IDT за этими пределами, и разрешите прерывания после перехода в PM, то всё рухнет.
    Еще пара замечаний: Intel зарезервировала прерывания 0-31. Туда нужно класть обработчики для этих exception’ов. Среди них ошибка деления на 0 (#DE), исключение общей защиты #GP и другие не менее весёлые вещи. Поэтому нужно базовый адрес прерываний сместить т.е сделать так, чтобы IRQ0 занимало 32 позицию и.т.д. Сразу оговорюсь, что использовать будем старую-добрую микросхему i8259a. Конечно, можно использовать и APICAdvanced Programmable Interrupt Controller (кстати, его нужно ручками включить), но нам просто не придётся пользоваться преимуществами такового (расширение кол-ва аппаратных прерываний, приспособленность под многопроцессорность и.т.д). Надеюсь, все помнят, что такое ICW (Initialization Control Word) и OCW (Operation Control Word)? Давайте повторим, что для чего используется (структуру приводить не буду):
    ICW:
    1) Определение особенности последовательности приказов.
    2) Определение базового адреса (вот о чём я говорил).
    3) Связь контроллеров.
    4) Дополнительные особенности обработки прерываний.
    OCW:
    1) Управление регистром масок IMR.
    2) Управление приоритетом.
    3) Общее управление контроллером.

    Процедура инициализации выглядит следующим образом:

    Init_PIC:
    mov al,11h;ICW1 – прерывание по перепаду сигнала, схемы подключены каскадно
    out 20h,al;Мастеру
    out 0A0h,al;Слэйву

    mov al,20h;ICW2;базовый адрес (он абстрактный! Не путайте с адресом в RAM)
    out 21h,al;Мастеру одно значение
    mov al,28h;Слэйву другое
    out 0A1h,al

    mov al,04h;ICW3 – слэйв к входу IRQ2 подключён
    out 21h,al
    mov al,02h
    out 0A1h,al

    mov al,11h;ICW4 – сбрасываем флаг наличия прерывания вручную и используем камень Pentium
    out 21h,al
    mov al,01h
    out 0A1h,al
    ret


    Теперь построим IDT:

    IDT:
    dd 0,0; 0
    dd 0,0 ; 1
    dd 0,0
    dd 0,0 ; 3
    ;…. Убрал для краткости!
    dd 0,0 ; 12
    dw GP_handler and 0FFFFh,08h, 1000111000000000b, GP_handler shr 16 ; 13 #GP
    dd 0,0 ; 14
    ;…. Я просто для краткости так написал. Забейте хоть тем же dup’ом это свободное место,
    ;чтоб не писать наскучившее dd 0,0
    dd 0,0 ; 30
    dd 0,0 ; 31;Здесь заканчиваются зарезервированные Intel номера ;gate’ов
    dw timer and 0FFFFh, 08h, 1000111000000000b, timer shr 16 ; IRQ 0 - системный таймер
    dw keyboard and 0FFFFh , 08h, 1000111000000000b, keyboard shr 16 ; IRQ 1 - клавиатура - ;предлагаю вам самим написать обработчик
    dd 0,0;IRQ2
    dd 0,0;IRQ3
    dd 0,0;IRQ4
    dd 0,0;IRQ5
    dd 0,0;IRQ6

    label IDT_size at $-IDT

    IDTR dw IDT_size-1
    dd IDT+10000h


    Так, а зачем здесь пишем and’ы и shr’ы? – спросите вы. Вот тут трюк: мы же расположим весь код для PM за org’ом (смотрим код в предыдущей статье), в том числе и interrupt handlers… вот здесь и есть уловка: мы составляем 32-битный адрес из 2-х половинок, имея на руках лишь адрес обработчика. Вообще в этом нет ничего магического, просто нужно понимать, что за значение будет в этом двойном слове.
    Теперь возникает вопрос, а почему же на местах многих gate’ов нули? А вот почему – совершенно не нужно писать все обработчики, ведь это, мягко говоря тяжело. Проще написать обработчик #GP, ведь, не найдя gate для прерывания, процессор генерирует пресловутое General Protection Fault.
    Обработчики аппаратных прерываний вольны делать всё, что им вздумается, но должны сбрасывать флаг наличия прерывания – слать сигнал EOIEnd Of Interrupt (мы же так сконфигурировали PIC, верно?).
    К примеру вот так:

    mov al, 20h
    out 20h, al


    Давайте напишем простой обработчик для таймера:

    timer:
    push ax
    mov al, 20h
    out 20h, al
    pop ax
    jmp int_EOI

    int_EOI: ;вот здесь посылаем и Master'у и Slave'у EOI
    push ax
    mov al,20h
    out 20h,al
    out 0a0h,al
    pop ax
    iretd ;возврат из прерывания


    int_EOI удобно использовать для всех обработчиков прерываний.

    Ладно, теперь немного про исключения. Когда они происходят стек выглядит так:
    image

    Здесь error_code выталкиваем из стека и работаем с ним. Остальным займётся инструкция iretd.

    Замечу, что тут содержимое ещё зависит от того, переключали ли мы ring’ или нет.
    Структура error_code:

    image

    Где:
    1) EXT — показывает, что сбой произошёл в обработчике прерывания или исключения.
    2) IDT — когда установлен, показывает, что поля индекса относится к IDT.
    3) TI — когда бит IDT не установлен, показывает, что икать нужно в GDT или LDT.
    4) Segment Selector Index — показывает номер дескриптора (в GDT и LDT) или gate в IDT.

    Вообще индекс обработчика исключения полезная штука. Он нам пригодится, когда наша поделка станет побольше.

    Теперь давайте скомпануем код. Я схитрю: предоставлю вам возможность потренироваться. Давайте вы попробуете самостоятельно написать рабочий код. Функции мы уже написали.
    Дам несколько 'подсказок':
    1) IDT расположите до org'a (короче вместе с GDT).
    2) IDTR загружаем до перехода в PM.
    3) Обработчики прерываний распологаем за org'ом.
    4) К текстовой видеопамяти можно обращаться как раньше.
    5) Не забываем разрешить ВСЕ прерывания после того, как в PM проинициализируем PIC!
    6) Paging инициализируем уже в PM.
    И ещё, напишите простой обработчик для клавы.

    Если будут проблемы — обращайтесь. До следующего поста.
    ****Исправление****
    Функция set_pages сперва была написана неправильно т.к там описывалась всего 1 таблица, вместо 8. Код исправлен.

    Похожие публикации

    Средняя зарплата в IT

    110 000 ₽/мес.
    Средняя зарплата по всем IT-специализациям на основании 8 813 анкет, за 2-ое пол. 2020 года Узнать свою зарплату
    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 14

      +4
      Радует, что написание ОС на хабре продвинулось дальше двух постов. Спасибо.
        +2
        Я положу две ссылки:

        www.kolibrios.org/ — операционная система на ассемблере.

        l4ka.org/l4ka/l4-x2-r7.pdf — очень вкусный документ.

        Фишка в том, чтобы правильно приложить свои силы. В случае операционной системы, важен не язык, на котором она написана, важен не режим процессора (современные операционные системы портабельны), важны две вещи:
        1. система должна быть хорошо и правильно спроектирована;
        2. система должна поддерживать какой-либо стандарт.

        Если кто-нибудь будет отрицать, что проектирование не важно — в полемику вступать не буду — спорить нам не о чем — мы живём в разных мирах.

        Что касается стандарта, то среди разработчиков ОС большая конкуренция и «подсадить» на свою систему какое-либо значительное количество пользователей, а тем более разработчиков — нереально. Ну разве что у кого папа, дядя или брат — министр промышленности. Использование стандарта позволяет использовать уже существующее программное обеспечение и даёт хоть какой-то шанс найти последователей и соратников.

        Так вот, вторая ссылка — это спецификация системных вызовов микроядра L4. Существуют как минимум две реализации этой спецификации — Pistachio и OKL4. К чему я клоню? Реальная тема для тех, кто идеально знает и любит ассемблер, ориентируется в защищённом режиме х86 и желает написать операционную систему — напишите свою (самую оптимальную, быструю, красивую, минимальную) реализацию микроядра по спецификации, которую я привёл выше.
          0
          Да, проектирование — важная часть. Просто поймут ли меня, если сразу, ничего не оговаривая, начнём писать? В принципе ознакомление с PM можно прекратить, и понемногу рассказывать в следующих постах. И спасибо за ссылку на стандарт ядра.
        0
        64-битные структуры, находящиеся в IDT. И структура такова:

        Я один не вижу картинки?
          0
          Есть картинка.
            0
            Появилась, спасибо.
          +2
          опять дни чтения учебников по икс-восем-шестому процессору на хабре?
            0
            Нет. Краткий экскурс перед началом работы.
              +2
              От почему для арма никто не делает тут? интереснее ж, х86 давно вдоль и поперек, а на арме экспы мало еще в народе :)
                0
                О, Vilko не только на васме!
                Под ARM тож было бы интересно почитать. У меня лично пока даже возможности нет таковым заниматься…
                  0
                  И, скорее всего, заниматься не буду. Что-то неуютно я себя чувствую, что в NT'эшном ядре не разбирался. Скоро (как времени побольше чуть будет) за него примусь… а ARM может кто-нибудь другой подхватит… не думаю что в сети мало материала по данной теме.
                  0
                  Для ленивых на ARM'е есть Linux, который просто процветает. Кто посерьёзнее, те используют коммерческие realtime системы.

                  Не побоюсь прослыть занудой, но у меня «завалялась» раритетная дока:

                  «L4 eXperimental Kernel Reference Manual Version X.2
                  System Architecture Group Dept. of Computer Science
                  Universit¨at Karlsruhe
                  (L4Ka Team)
                  l4spec@l4ka.org
                  Document Revision 5
                  June 4, 2004»

                  У этой документации вот такая лицензия:

                  Copyright c
                  2001–2004 by System Architecture Group, Department of Computer Science, Universit¨at Karlsruhe.
                  THIS SPECIFICATION IS PROVIDED “AS IS” WITHOUT ANY WARRANTIES, INCLUDING ANY WARRANTY OF MERCHANTABILITY, NONINFRINGEMENT,
                  FITNESS FOR ANY PARTICULAR PURPOSE, OR ANY WARRANTY OTHERWISE ARISING OF ANY PROPOSAL, SPECIFICATION
                  OR SAMPLE.
                  Permission to copy and distribute verbatim copies of this specification in any medium for any purpose without fee or royalty is hereby granted. No right to create modifications or derivates is granted by this license. The L4Ka Team may make changes to this specification at any time, without notice. The latest
                  revision of this document is available at l4ka.org/.

                  Осталось пригласить юристов и лингвистов, чтобы истолковать derivates — относятся ли они только к самой документации или запрещают писать реализацию. Изучить прецеденты и…

                  narod.ru/disk/12675982001/l4-x2.pdf.html

                  ^^ по этой ссылке находится старая спецификация, которая в совместима с последующими ревизиями, но из неё ещё не убрана поддержка «ARM Interface».

                  Вот и думай, то ли платить за NICTA::L4-embedded, который который имеет десяток надстроек над красивым протоколом L4-x2, или сесть за учебники и выучить внутренности архитектуры ARM. Я то уже стар для таких подвигов — мне хватает С/С++ и воспоминаний о x86, но может авантюристы-любители ещё не перевелись? Разумеется, всё это имеет смысл, если понятие derivates из вышеприведённой лицензии не относится к реализации.

                    0
                    Таки юристы топик не читаю. А можно было бы интересно обновить обсуждение: «No right to create modifications or derivates is granted by this license. » Hey pal! What is «derivates»?
                0
                Что-то мне всё это напомнило xv6 http://pdos.csail.mit.edu/6.828/xv6/ — MIT'шную разработку, для того чтобы обучать студентов основам ОС-строения.
                Там, кстати, весьма доставляющие PDF'ки.

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

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