Learning paging and interrupt handling

    Greetings. Today we’ll talk a little about everything. We will introduce paging into our operating time , we will deal with interrupts and their types. We will write several functions, add this to the code from the previous post.


    1) Paging.
    All, probably, have already rummaged in mans and know what paging is for a nonsense. We break out beyond 1 MB and can address all 4 gigabytes of RAM. But why do we need 4 GB in our modest craft (especially since not everyone has so much RAM). 32MB will be enough for us (well, that’s also a lot, but let's dream). So, we will use 4K pages. So, now we think - 1024 pages describe 4MB. We, therefore, need only 1024 * 8 pages. In other words, you need to have 8 tables and 1 directory. Also, for convenience, the pages will be identical, i.e. the logical and physical addresses coincide. Now, let's write a function that will form everything we need.

    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


    Perhaps you have a question, why zerk other directories? But do not forget that you can arrange tables, like directories, at addresses that are multiples of 1000h.

    2) Interrupts.
    And with interrupts, PM brings surprises: there are no more than 4 byte interrupt vectors, as in RM . In their place came Interrupt Gates (they are important for us now), Trap Gates , Task Gates - 64-bit structures located in IDT . And the structure is as follows:

    image

    Here, the purpose of the fields is clear (especially since some of them have already been encountered in segment descriptors). We will only consider Interrupt Gates for now . IDTYou need to make up before moving to PM , load the size and offset into the IDTR register (its structure is similar to the GDTR structure ) with the lidt command . By the way, IDT is a segment (let's say, the antipode of GDT , which is just in the linear address space), i.e. it is located in a certain segment. For example, if you described, for example, 4K memory before transferring to PM, and you have IDT outside these limits and enable interrupts after switching to PM, then everything will crash.
    A couple more points: Intel reserved interrupts 0-31. There you need to put handlers for these exception'ov. Among them, the error of dividing by 0 (#DE), the exception of the general protection of #GP, and other no less fun things. Therefore, you need to shift the base address of interrupts, i.e. make IRQ0 occupy 32nd position, etc. I must make a reservation right away that we will use the good old i8259a chip . Of course, you can use APIC - Advanced Programmable Interrupt Controller (by the way, you need to turn it on with handles), but we just don’t have to take advantage of it (expanding the number of hardware interrupts, adaptability for multiprocessing, etc.). I hope everyone remembers what ICW ( Initialization Control Word is) and OCW ( Operation Control Word )? Let's repeat what is used for (I will not give the structure):
    ICW:
    1) Determining the features of the sequence of orders.
    2) Determining the base address (this is what I was talking about).
    3) Communication controllers.
    4) Additional features of interrupt handling.
    OCW:
    1) Management of the register of masks IMR.
    2) Priority management.
    3) General control of the controller.

    The initialization procedure is as follows: Now let's build an IDT :

    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:
    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


    So, why do we write and'y and shr'y here? - you ask. Here’s the trick: we’ll put all the code for PM behind org (see the code in the previous article), including interrupt handlers ... here’s the trick: we make up a 32-bit address from 2 halves, having on hands only the address of the handler. In general, there is nothing magical about it, you just need to understand what meaning will be in this double word.
    Now the question is, why are there many zeros in the places of many gate 's? And here’s why - you don’t have to write all the handlers at all, because it's hard to say the least. It's easier to write a #GP handler , because, not finding a gate to interrupt, the processor generates the notorious General Protection Fault .
    The hardware interrupt handlers are free to do whatever they want, but they must clear the flag for the presence of an interrupt - send an EOI signal - End Of Interrupt (we also configured PIC, right?).
    For example, like this: Let's write a simple handler for the timer: int_EOI is convenient to use for all interrupt handlers. Okay, now a little about exceptions. When they happen, the stack looks like this: Here, we push the error_code from the stack and work with it. The rest will deal with the iretd instruction . I note that here the content still depends on whether we switched ring 'or not. Structure error_code: Where:

    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 ;возврат из прерывания





    image






    image


    1) EXT - indicates that a failure occurred in the interrupt or exception handler.
    2) IDT - when installed, indicates that the index field refers to the IDT .
    3) TI - when the IDT bit is not set, indicates that you need to hiccup in GDT or LDT .
    4) Segment Selector Index - shows the descriptor number (in GDT and LDT ) or gate in IDT .

    In general, an exception handler index is a useful thing. It will be useful to us when our craft becomes larger.

    Now let's compose the code. I'll cheat: give you the opportunity to practice. Let’s try to write working code yourself. We have already written the functions.
    I will give a few 'hints':
    1) Place the IDT before org (in short, along with GDT).
    2) We load the IDTR before going to PM.
    3) Interrupt handlers are located at org.
    4) Text video memory can be accessed as before.
    5) Do not forget to allow ALL interrupts after we initialize the PIC in PM!
    6) Paging is initialized already in PM.
    And yet, write a simple handler for Claudia.

    If there are problems, please contact. Until the next post.
    **** Bugfix ****
    The set_pages function was first spelled incorrectly because only 1 table was described there, instead of 8. The code has been fixed.

    Also popular now: