Емуляція Лінуса Торвальда: Створіть свою власну операційну систему з 0 (V)

У цій п'ятій частині ми побачимо таблицю, дуже схожу на GDT як в теорії, так і у використанні, ми маємо на увазі IDT. IDT розшифровується як Таблиця опису переривань y - таблиця, яка використовується для обробки переривань, що виникають. Наприклад, хтось робить ділення на 0, викликається функція, що відповідає за обробку. Цими функціями є ISR (Процедури обслуговування переривань). Тож давайте створимо IDT та додамо трохи ISR.

Спочатку ми збираємось оголосити структури, що відповідають IDT:

struct Entry{
uint16_t base_low;
uint16_t sel;
uint8_t always0;
uint8_t flags;
uint16_t base_high;
} __attribute__((packed));
struct Ptr{
uint16_t limit;
uint32_t base;
} __attribute__((packed));

Як можна побачити, якщо порівняти його з GDT, структура Ptr ідентична, а запис досить схожий. Тому функції розміщення запису (SetGate) та встановлення (Install) дуже схожі.

void ND::IDT::SetGate(uint8_t num,uint32_t base,uint16_t sel, uint8_t flags)
{
idt[num].base_low=(base & 0xFFFF);
idt[num].base_high=(base >> 16) & 0xFFFF;
idt[num].sel=sel;
idt[num].always0=0;
idt[num].flags=flags;
}

Встановити:

idtptr.limit=(sizeof(struct ND::IDT::Entry)*256)-1;
idtptr.base=(uint32_t)&idt;
ND::Memory::Set(&idt,0,sizeof(struct ND::IDT::Entry)*256);
ND::IDT::Flush();

Якщо ми подивимось, то побачимо, що функція встановлення використовує функцію ND :: Memory :: Set, яку ми оголосили в іншому дописі. Ми також можемо побачити, як ми ще не робимо жодних викликів до SetGate, і ми викликаємо ND :: IDT :: Flush, для цієї функції ми знову використовуємо оператор asm volatile:

asm volatile("lidtl (idtptr)");

Якщо все йде добре, і ми робимо естетичну композицію, це має виглядати так:

NextDivel-RTD

Гаразд, зараз ми почнемо заповнювати IDT перериваннями. Тут я збираюся створити лише одну, але для решти це було б так само. Я збираюся зробити ділення на нуль перерви. Як ви знаєте з математики, число не можна ділити на 0. Якщо це відбувається в процесорі, генерується виняток, оскільки він не може продовжуватися. В IDT перше переривання у списку (0) відповідає цій події.

Ми додаємо це між налаштуваннями пам’яті та змивом у функції встановлення IDT:

ND::IDT::SetGate(0,(unsigned)ND::ISR::ISR1,0x08,0x8E);

Функцією зворотного виклику буде ND :: ISR :: ISR1, що досить просто, хоча ми повинні використовувати ASM:

void ND::ISR::ISR1()
{
asm volatile(
"cli \n"
"pushl 0 \n"
"pushl 0 \n"
"jmp ND_ISR_Common \n"
);
}

Ми визначимо ND_ISR_Common як функцію мовою C. Для збереження файлів та покращення читабельності ми можемо використовувати зовнішній "C" {}:

extern "C"
void ND_ISR_Common()
{
asm volatile(
"pusha \n"
"push %ds \n"
"push %es \n"
"push %fs \n"
"push %gs \n"
"movw $0x10, %ax \n"
"movw %ax, %ds \n"
"movw %ax, %es \n"
"movw %ax, %fs \n"
"movw %ax, %gs \n"
"movl %esp, %eax \n"
"push %eax \n"
"movl $ND_ISR_Handler, %eax \n"
"call *%eax \n"
"popl %eax \n"
"popl %ds \n"
"popl %es \n"
"popl %fs \n"
"popl %gs \n"
"popa \n"
"addl 8, %esp \n"
"iret \n"
);
}

Цей код в ASM може бути трохи важким для розуміння, але це тому, що ми збираємось оголосити структуру в C для доступу до даних, що генеруються перериванням. Очевидно, що якщо ви цього не хотіли, ви можете просто викликати ядро ​​паніки в ND :: ISR :: ISR1 або щось подібне. Конструкція має таку форму, що:

struct regs{
uint32_t ds;
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;
uint32_t int_no, err_code;
uint32_t eip, cs, eflags, useresp, ss;
};

І нарешті, ми робимо функцію ND_ISR_Handler (також із посиланням C), в якій ми демонструємо паніку ядра та невеликий опис помилки відповідно до тієї, яку ми маємо у списку помилок.

extern "C"
void ND_ISR_Handler(struct regs *r)
{
if(r->int_no < 32) { ND::Panic::Show(exception_messages[r->int_no]);
for(;;);
}
}

Добре, і завдяки цьому ми вже можемо впоратися з цим перериванням. З рештою переривань це могло б статися подібним, за винятком того, що є деякі, які повертають параметри, і ми використовували б структуру reg для його отримання. Однак ви можете здивуватися, звідки ми знаємо, чи справді це працює. Щоб перевірити, чи це працює, ми збираємося ввести простий рядок після ND :: IDT :: Install ():

int sum=10/0;

Якщо ми скомпілюємо, це дасть нам попередження, і якщо ми спробуємо його виконати, ми отримаємо гарний екран:

NextDivel-ISR


І цим закінчується ця публікація, я думаю, що вона одна з найширших, але цілком функціональних.


Залиште свій коментар

Ваша електронна адреса не буде опублікований. Обов'язкові для заповнення поля позначені *

*

*

  1. Відповідальний за дані: Мігель Анхель Гатон
  2. Призначення даних: Контроль спаму, управління коментарями.
  3. Легітимація: Ваша згода
  4. Передача даних: Дані не передаватимуться третім особам, за винятком юридичних зобов’язань.
  5. Зберігання даних: База даних, розміщена в мережі Occentus Networks (ЄС)
  6. Права: Ви можете будь-коли обмежити, відновити та видалити свою інформацію.

  1.   Мезодаблер - сказав він

    Я пішов до LFS, він більш безперервний.

  2.   eliotime3000 - сказав він

    Святе ... У всякому разі, підручники хороші.

  3.   sc - сказав він

    Дуже добре, я стежу за цим з самого початку. Не могли б ви прикріпити коди до кожного трейлера?

    1.    AdrianArroyoStreet - сказав він

      У вас є всі вихідні коди, доступні на GitHub: http://github.com/AdrianArroyoCalle/next-divel Звідти ви можете завантажити ZIP, TAR.GZ або просто використовувати git.

  4.   нюансований - сказав він

    Ха-ха-ха дуже добре! Схвалити! 🙂