MULTIPLE: 2 изменения
CHORE: удалено лишнее ROADMAP: закрыто 2 пункта
This commit is contained in:
parent
d56df0b358
commit
884b58f304
16 changed files with 634 additions and 45 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
|||
**.o
|
||||
**.bin
|
||||
**.iso
|
||||
|
|
|
|||
98
Makefile
98
Makefile
|
|
@ -1,57 +1,73 @@
|
|||
NASM = nasm
|
||||
GCC = gcc
|
||||
CC = gcc
|
||||
CFLAGS = -m32 -ffreestanding -O2 -Wall -Wextra -Iinclude
|
||||
|
||||
AS = nasm
|
||||
ASFLAGS = -f elf32
|
||||
|
||||
LD = ld
|
||||
GRUB_RESCUE = grub-mkrescue
|
||||
LDFLAGS = -m elf_i386 -T linker.ld
|
||||
|
||||
# Исходники
|
||||
BOOT_SRC = boot/boot.asm
|
||||
KERNEL_SRCS = kernel/kmain.c
|
||||
DRIVER_SRCS = drivers/vga.c
|
||||
TARGET = iso/boot/kernel.bin
|
||||
ISO_IMAGE = peredOS.iso
|
||||
|
||||
# Автоматически собираем список всех объектных файлов
|
||||
BOOT_OBJ = boot/boot.o
|
||||
OBJS = kernel/kmain.o drivers/vga.o
|
||||
# Жесткий список всех объектов, идущих в линкер
|
||||
OBJ = boot/boot.o \
|
||||
kernel/kmain.o \
|
||||
kernel/gdt.o \
|
||||
kernel/gdt_asm.o \
|
||||
kernel/idt.o \
|
||||
kernel/interrupt.o \
|
||||
kernel/logger.o \
|
||||
kernel/io.o \
|
||||
drivers/serial.o \
|
||||
drivers/vga.o
|
||||
|
||||
ISO_DIR = iso
|
||||
KERNEL_BIN = $(ISO_DIR)/boot/kernel.bin
|
||||
ISO_OUT = peredOS.iso
|
||||
|
||||
NASM_FLAGS = -f elf32
|
||||
GCC_FLAGS = -m32 -ffreestanding -O2 -Wall -Wextra -c
|
||||
LD_FLAGS = -m elf_i386 -T linker.ld
|
||||
GRUB_FLAGS =
|
||||
|
||||
all: $(ISO_OUT)
|
||||
|
||||
# Сборка ISO
|
||||
$(ISO_OUT): $(KERNEL_BIN)
|
||||
@echo "[+] Крафтим ISO..."
|
||||
$(GRUB_RESCUE) $(GRUB_FLAGS) -o $(ISO_OUT) $(ISO_DIR)
|
||||
# Дефолтное правило (просто сборка бинарника)
|
||||
all: $(TARGET)
|
||||
@echo "[+] Сборка бинарника успешно завершена!"
|
||||
|
||||
# Линковка ядра
|
||||
$(KERNEL_BIN): $(BOOT_OBJ) $(OBJS)
|
||||
$(TARGET): $(OBJ)
|
||||
@echo "[+] Линкуем ядро..."
|
||||
@mkdir -p $(ISO_DIR)/boot
|
||||
$(LD) $(LD_FLAGS) $(BOOT_OBJ) $(OBJS) -o $(KERNEL_BIN)
|
||||
$(LD) $(LDFLAGS) $(OBJ) -o $(TARGET)
|
||||
|
||||
# Компиляция ассемблера
|
||||
$(BOOT_OBJ): $(BOOT_SRC)
|
||||
$(NASM) $(NASM_FLAGS) $(BOOT_SRC) -o $(BOOT_OBJ)
|
||||
|
||||
# Универсальное правило для компиляции Си-файлов в kernel/
|
||||
# Компиляция Си-файлов ядра
|
||||
kernel/%.o: kernel/%.c
|
||||
@echo "[+] Компилируем ядро: $<"
|
||||
$(GCC) $(GCC_FLAGS) $< -o $@
|
||||
@echo "[+] Компилируем ядро (C): $<"
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
# Универсальное правило для компиляции Си-файлов в drivers/
|
||||
# Компиляция ассемблера GDT
|
||||
kernel/gdt_asm.o: kernel/gdt.asm
|
||||
@echo "[+] Компилируем gdt.asm..."
|
||||
$(AS) $(ASFLAGS) kernel/gdt.asm -o kernel/gdt_asm.o
|
||||
|
||||
# Компиляция ассемблера IDT
|
||||
kernel/interrupt.o: kernel/interrupt.asm
|
||||
@echo "[+] Компилируем interrupt.asm..."
|
||||
$(AS) $(ASFLAGS) kernel/interrupt.asm -o kernel/interrupt.o
|
||||
# Компиляция драйверов
|
||||
drivers/%.o: drivers/%.c
|
||||
@echo "[+] Компилируем драйвер: $<"
|
||||
$(GCC) $(GCC_FLAGS) $< -o $@
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
run: all
|
||||
qemu-system-i386 -cdrom $(ISO_OUT)
|
||||
# Компиляция загрузочного сектора
|
||||
boot/boot.o: boot/boot.asm
|
||||
@echo "[+] Компилируем бутлоадер..."
|
||||
$(AS) $(ASFLAGS) boot/boot.asm -o boot/boot.o
|
||||
|
||||
# Сборка полноценного ISO-образа через GRUB
|
||||
iso: $(TARGET)
|
||||
@echo "[+] Собираем загрузочный ISO-образ..."
|
||||
grub-mkrescue -o $(ISO_IMAGE) iso/
|
||||
|
||||
# Честный запуск через ISO в QEMU
|
||||
run: iso
|
||||
@echo "[+] Запускаем peredOS в QEMU через CD-ROM..."
|
||||
qemu-system-i386 -cdrom $(ISO_IMAGE) -serial stdio
|
||||
|
||||
# Очистка мусора
|
||||
clean:
|
||||
rm -f boot/*.o kernel/*.o drivers/*.o $(KERNEL_BIN) $(ISO_OUT)
|
||||
@echo "[-] Очистка объектных файлов и образов..."
|
||||
rm -f boot/*.o kernel/*.o drivers/*.o $(TARGET) $(ISO_IMAGE)
|
||||
|
||||
.PHONY: all run clean
|
||||
.PHONY: all clean run iso
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@
|
|||
|
||||
- [x] Оно работает
|
||||
- [x] Драйвер VGA
|
||||
- [ ] Прерывания (GDT/IDT)
|
||||
- [x] Прерывания (GDT/IDT)
|
||||
- [x] UART отладка
|
||||
- [ ] Драйвер таймера
|
||||
- [ ] Драйвер клавиатуры
|
||||
- [ ] Нормальный драйвер VGA (скролл итд)
|
||||
|
|
|
|||
27
drivers/serial.c
Normal file
27
drivers/serial.c
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#include "../kernel/io.h" // Подключаем наши порты
|
||||
|
||||
#define COM1 0x3F8
|
||||
|
||||
void serial_init(void) {
|
||||
outb(COM1 + 1, 0x00);
|
||||
outb(COM1 + 3, 0x80);
|
||||
outb(COM1 + 0, 0x03);
|
||||
outb(COM1 + 1, 0x00);
|
||||
outb(COM1 + 3, 0x03);
|
||||
outb(COM1 + 2, 0xC7);
|
||||
}
|
||||
|
||||
int is_transmit_empty(void) {
|
||||
return inb(COM1 + 5) & 0x20;
|
||||
}
|
||||
|
||||
void serial_putc(char c) {
|
||||
while (is_transmit_empty() == 0);
|
||||
outb(COM1, c);
|
||||
}
|
||||
|
||||
void kprint_serial(const char* str) {
|
||||
for (int i = 0; str[i] != '\0'; i++) {
|
||||
serial_putc(str[i]);
|
||||
}
|
||||
}
|
||||
16
drivers/serial.h
Normal file
16
drivers/serial.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef SERIAL_H
|
||||
#define SERIAL_H
|
||||
|
||||
// Инициализация COM1 порта на скорость 38400 бод, 8N1
|
||||
void serial_init(void);
|
||||
|
||||
// Проверка, пуст ли передающий буфер порта
|
||||
int is_transmit_empty(void);
|
||||
|
||||
// Отправка одного символа в COM1
|
||||
void serial_putc(char c);
|
||||
|
||||
// Вывод строки (логов) в последовательный порт
|
||||
void kprint_serial(const char* str);
|
||||
|
||||
#endif
|
||||
24
kernel/gdt.asm
Normal file
24
kernel/gdt.asm
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
bits 32
|
||||
|
||||
global gdt_flush
|
||||
|
||||
gdt_flush:
|
||||
; Берем адрес gdtp, переданный как аргумент функции (лежит в стеке)
|
||||
mov eax, [esp + 4]
|
||||
lgdt [eax] ; Загружаем новую GDT
|
||||
|
||||
; Обновляем сегменты данных. 0x10 — это смещение Kernel Data в нашей GDT (индекс 2 * 8 = 16 = 0x10)
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
|
||||
; Делаем дальний прыжок, чтобы обновить регистр CS.
|
||||
; 0x08 — это смещение Kernel Code (индекс 1 * 8 = 8 = 0x08).
|
||||
; Процессор прыгнет на метку .flush, но уже с новым селектором CS.
|
||||
jmp 0x08:.flush
|
||||
|
||||
.flush:
|
||||
ret
|
||||
46
kernel/gdt.c
Normal file
46
kernel/gdt.c
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#include "gdt.h"
|
||||
|
||||
// Наша таблица GDT и указатель на неё
|
||||
struct gdt_entry gdt[5];
|
||||
struct gdt_ptr gdtp;
|
||||
|
||||
// Ассемблерная функция для загрузки регистра GDTR и обновления сегментов
|
||||
extern void gdt_flush(uint32_t);
|
||||
|
||||
// Внутренняя функция для сборки дескриптора из человеческих параметров
|
||||
static void gdt_set_gate(int32_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) {
|
||||
gdt[num].base_low = (base & 0xFFFF);
|
||||
gdt[num].base_middle = (base >> 16) & 0xFF;
|
||||
gdt[num].base_high = (base >> 24) & 0xFF;
|
||||
|
||||
gdt[num].limit_low = (limit & 0xFFFF);
|
||||
gdt[num].granularity = (limit >> 16) & 0x0F;
|
||||
|
||||
gdt[num].granularity |= gran & 0xF0;
|
||||
gdt[num].access = access;
|
||||
}
|
||||
|
||||
void gdt_init(void) {
|
||||
// Настраиваем указатель GDT
|
||||
gdtp.limit = (sizeof(struct gdt_entry) * 5) - 1;
|
||||
gdtp.base = (uint32_t)&gdt;
|
||||
|
||||
// 1. Нулевой дескриптор (обязателен, всегда нули)
|
||||
gdt_set_gate(0, 0, 0, 0, 0);
|
||||
|
||||
// 2. Сегмент кода ядра (Kernel Code)
|
||||
// База = 0, Лимит = 0xFFFFF, Доступ = 0x9A, Гранулярность = 0xCF (страничная разметка, 4ГБ)
|
||||
gdt_set_gate(1, 0, 0xFFFFF, 0x9A, 0xCF);
|
||||
|
||||
// 3. Сегмент данных ядра (Kernel Data)
|
||||
// Всё то же самое, но Доступ = 0x92 (разрешена запись, это сегмент данных)
|
||||
gdt_set_gate(2, 0, 0xFFFFF, 0x92, 0xCF);
|
||||
|
||||
// 4. Юзерский код (Ring 3) — пока заглушка, заюзаем в Части 2
|
||||
gdt_set_gate(3, 0, 0, 0, 0);
|
||||
// 5. Юзерские данные (Ring 3) — пока заглушка, заюзаем в Части 2
|
||||
gdt_set_gate(4, 0, 0, 0, 0);
|
||||
|
||||
// Загружаем GDT в процессор
|
||||
gdt_flush((uint32_t)&gdtp);
|
||||
}
|
||||
25
kernel/gdt.h
Normal file
25
kernel/gdt.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef GDT_H
|
||||
#define GDT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Структура одного дескриптора сегмента (8 байт)
|
||||
struct gdt_entry {
|
||||
uint16_t limit_low; // Младшие 16 бит лимита
|
||||
uint16_t base_low; // Младшие 16 бит базы
|
||||
uint8_t base_middle; // Следующие 8 бит базы
|
||||
uint8_t access; // Байт доступа (права, кольцо защиты)
|
||||
uint8_t granularity; // Гранулярность и старшие 4 бита лимита
|
||||
uint8_t base_high; // Старшие 8 бит базы
|
||||
} __attribute__((packed));
|
||||
|
||||
// Структура указателя для инструкции lgdt (6 байт)
|
||||
struct gdt_ptr {
|
||||
uint16_t limit; // Размер всей GDT в байтах минус 1
|
||||
uint32_t base; // Физический адрес начала массива gdt_entry
|
||||
} __attribute__((packed));
|
||||
|
||||
// Функции для вызова из ядра
|
||||
void gdt_init(void);
|
||||
|
||||
#endif
|
||||
82
kernel/idt.c
Normal file
82
kernel/idt.c
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#include "idt.h"
|
||||
|
||||
// Сам массив IDT на 256 векторов и указатель на него
|
||||
struct idt_entry idt[256];
|
||||
struct idt_ptr idtp;
|
||||
|
||||
// Процессорные исключения (0-21)
|
||||
extern void isr0(); extern void isr1(); extern void isr2(); extern void isr3();
|
||||
extern void isr4(); extern void isr5(); extern void isr6(); extern void isr7();
|
||||
extern void isr8(); extern void isr9(); extern void isr10(); extern void isr11();
|
||||
extern void isr12(); extern void isr13(); extern void isr14(); extern void isr15();
|
||||
extern void isr16(); extern void isr17(); extern void isr18(); extern void isr19();
|
||||
extern void isr20(); extern void isr21();
|
||||
|
||||
// Аппаратные прерывания железа (IRQ)
|
||||
extern void irq0(); extern void irq1();
|
||||
|
||||
// Внешняя функция на ассемблере для выполнения 'lidt'
|
||||
extern void idt_load();
|
||||
|
||||
// Заполнение полей одного дескриптора
|
||||
void idt_set_gate(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;
|
||||
}
|
||||
|
||||
// Главная функция инициализации IDT
|
||||
void idt_init(void) {
|
||||
// Настраиваем указатель IDTR
|
||||
idtp.limit = (sizeof(struct idt_entry) * 256) - 1;
|
||||
idtp.base = (uint32_t)&idt;
|
||||
|
||||
// Зануляем всю таблицу перед использованием
|
||||
for (int i = 0; i < 256; i++) {
|
||||
idt_set_gate(i, 0, 0, 0);
|
||||
}
|
||||
|
||||
// 0x08 — Селектор сегмента кода ядра
|
||||
// 0x8E — Флаги: Present, Ring 0, 32-bit Interrupt Gate
|
||||
|
||||
// Пихаем исключения процессора в IDT
|
||||
idt_set_gate(0, (uint32_t)isr0, 0x08, 0x8E); idt_set_gate(1, (uint32_t)isr1, 0x08, 0x8E);
|
||||
idt_set_gate(2, (uint32_t)isr2, 0x08, 0x8E); idt_set_gate(3, (uint32_t)isr3, 0x08, 0x8E);
|
||||
idt_set_gate(4, (uint32_t)isr4, 0x08, 0x8E); idt_set_gate(5, (uint32_t)isr5, 0x08, 0x8E);
|
||||
idt_set_gate(6, (uint32_t)isr6, 0x08, 0x8E); idt_set_gate(7, (uint32_t)isr7, 0x08, 0x8E);
|
||||
idt_set_gate(8, (uint32_t)isr8, 0x08, 0x8E); idt_set_gate(9, (uint32_t)isr9, 0x08, 0x8E);
|
||||
idt_set_gate(10, (uint32_t)isr10, 0x08, 0x8E); idt_set_gate(11, (uint32_t)isr11, 0x08, 0x8E);
|
||||
idt_set_gate(12, (uint32_t)isr12, 0x08, 0x8E); idt_set_gate(13, (uint32_t)isr13, 0x08, 0x8E);
|
||||
idt_set_gate(14, (uint32_t)isr14, 0x08, 0x8E); idt_set_gate(15, (uint32_t)isr15, 0x08, 0x8E);
|
||||
idt_set_gate(16, (uint32_t)isr16, 0x08, 0x8E); idt_set_gate(17, (uint32_t)isr17, 0x08, 0x8E);
|
||||
idt_set_gate(18, (uint32_t)isr18, 0x08, 0x8E); idt_set_gate(19, (uint32_t)isr19, 0x08, 0x8E);
|
||||
idt_set_gate(20, (uint32_t)isr20, 0x08, 0x8E); idt_set_gate(21, (uint32_t)isr21, 0x08, 0x8E);
|
||||
|
||||
// Вешаем аппаратные прерывания (после перемапливания PIC они будут тут)
|
||||
idt_set_gate(32, (uint32_t)irq0, 0x08, 0x8E); // Таймер PIT
|
||||
idt_set_gate(33, (uint32_t)irq1, 0x08, 0x8E); // Клавиатура
|
||||
|
||||
// Загружаем IDTR в процессор
|
||||
idt_load();
|
||||
}
|
||||
|
||||
// Сюда сходятся все исключения процессора (0-31)
|
||||
void isr_handler(struct registers regs) {
|
||||
// Пока у нас нет printf, тут будет пусто или заглушка для отладки.
|
||||
// Когда процессор словит ошибку (например деление на 0), он вызовет эту функцию.
|
||||
for(;;);
|
||||
}
|
||||
|
||||
// Сюда сходятся все прерывания от железа (32-47)
|
||||
void irq_handler(struct registers regs) {
|
||||
// Если прерывание пришло от чипа PIC2 (IRQ 8-15, векторы 40-47), шлем ему EOI (End of Interrupt)
|
||||
if (regs.int_no >= 40) {
|
||||
__asm__ __volatile__("outb %%al, %%dx" :: "a"(0x20), "d"(0xA0));
|
||||
}
|
||||
// В любом случае шлем EOI главному чипу PIC1 (порты 0x20/0x20)
|
||||
__asm__ __volatile__("outb %%al, %%dx" :: "a"(0x20), "d"(0x20));
|
||||
|
||||
// Сюда в будущем добавим вызовы конкретных драйверов таймера и клавы
|
||||
}
|
||||
33
kernel/idt.h
Normal file
33
kernel/idt.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef IDT_H
|
||||
#define IDT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Структура одного элемента таблицы IDT (8 байт)
|
||||
struct idt_entry {
|
||||
uint16_t base_low; // Младшие 16 бит адреса функции-обработчика
|
||||
uint16_t sel; // Сегментный селектор кода (0x08 — Kernel Code в GDT)
|
||||
uint8_t always0; // Всегда 0, зарезервировано процессором
|
||||
uint8_t flags; // Флаги доступа (0x8E для Ring 0 прерываний)
|
||||
uint16_t base_high; // Старшие 16 бит адреса функции-обработчика
|
||||
} __attribute__((packed));
|
||||
|
||||
// Структура «указателя» для инструкции lidt (6 байт)
|
||||
struct idt_ptr {
|
||||
uint16_t limit; // Размер таблицы в байтах минус 1 (256 * 8 - 1)
|
||||
uint32_t base; // Физический адрес начала массива структур idt_entry
|
||||
} __attribute__((packed));
|
||||
|
||||
// Структура, передаваемая в Си-обработчик, содержащая состояние всех регистров
|
||||
struct registers {
|
||||
uint32_t ds; // Сегмент данных
|
||||
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // Напушено инструкцией pushad
|
||||
uint32_t int_no, err_code; // Номер прерывания и код ошибки (если есть)
|
||||
uint32_t eip, cs, eflags, useresp, ss; // Автоматически пушится процессором
|
||||
};
|
||||
|
||||
// Объявляем функции для вызова извне
|
||||
void idt_init(void);
|
||||
void idt_set_gate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags);
|
||||
|
||||
#endif
|
||||
145
kernel/interrupt.asm
Normal file
145
kernel/interrupt.asm
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
bits 32
|
||||
|
||||
; Экспортируем функции для Си
|
||||
global idt_load
|
||||
global isr0, isr1, isr2, isr3, isr4, isr5, isr6, isr7, isr8, isr9, isr10
|
||||
global isr11, isr12, isr13, isr14, isr15, isr16, isr17, isr18, isr19, isr20, isr21
|
||||
global irq0, irq1
|
||||
|
||||
; Импортируем Си-обработчики
|
||||
extern idtp
|
||||
extern isr_handler
|
||||
extern irq_handler
|
||||
|
||||
; Инструкция загрузки регистра таблицы прерываний
|
||||
idt_load:
|
||||
lidt [idtp]
|
||||
ret
|
||||
|
||||
; Макрос для исключений, КОТОРЫЕ НЕ ПУШАТ код ошибки (подставляем ложный 0)
|
||||
%macro ISR_NOERRCODE 1
|
||||
idt_stub_isr_%1:
|
||||
push byte 0 ; Ложный код ошибки
|
||||
push byte %1 ; Номер прерывания
|
||||
jmp isr_common_stub
|
||||
%endmacro
|
||||
|
||||
; Макрос для исключений, КОТОРЫЕ САМИ ПУШАТ код ошибки
|
||||
%macro ISR_ERRCODE 1
|
||||
idt_stub_isr_%1:
|
||||
; Процессор уже напушил код ошибки сам
|
||||
push byte %1 ; Номер прерывания
|
||||
jmp isr_common_stub
|
||||
%endmacro
|
||||
|
||||
; Макрос для прерываний от железа (IRQ)
|
||||
%macro IRQ 2
|
||||
idt_stub_irq_%2:
|
||||
push byte 0 ; Ложный код ошибки
|
||||
push byte %1 ; Номер прерывания (вектор в IDT, например 32)
|
||||
jmp irq_common_stub
|
||||
%endmacro
|
||||
|
||||
; Генерируем заглушки с помощью макросов
|
||||
ISR_NOERRCODE 0
|
||||
ISR_NOERRCODE 1
|
||||
ISR_NOERRCODE 2
|
||||
ISR_NOERRCODE 3
|
||||
ISR_NOERRCODE 4
|
||||
ISR_NOERRCODE 5
|
||||
ISR_NOERRCODE 6
|
||||
ISR_NOERRCODE 7
|
||||
ISR_ERRCODE 8
|
||||
ISR_NOERRCODE 9
|
||||
ISR_ERRCODE 10
|
||||
ISR_ERRCODE 11
|
||||
ISR_ERRCODE 12
|
||||
ISR_ERRCODE 13
|
||||
ISR_ERRCODE 14
|
||||
ISR_NOERRCODE 15
|
||||
ISR_NOERRCODE 16
|
||||
ISR_ERRCODE 17
|
||||
ISR_NOERRCODE 18
|
||||
ISR_NOERRCODE 19
|
||||
ISR_NOERRCODE 20
|
||||
ISR_ERRCODE 21
|
||||
|
||||
IRQ 32, 0 ; IRQ0 - Таймер
|
||||
IRQ 33, 1 ; IRQ1 - Клавиатура
|
||||
|
||||
; Перенаправляем глобальные имена на сгенерированные макросами точки
|
||||
isr0: jmp idt_stub_isr_0
|
||||
isr1: jmp idt_stub_isr_1
|
||||
isr2: jmp idt_stub_isr_2
|
||||
isr3: jmp idt_stub_isr_3
|
||||
isr4: jmp idt_stub_isr_4
|
||||
isr5: jmp idt_stub_isr_5
|
||||
isr6: jmp idt_stub_isr_6
|
||||
isr7: jmp idt_stub_isr_7
|
||||
isr8: jmp idt_stub_isr_8
|
||||
isr9: jmp idt_stub_isr_9
|
||||
isr10: jmp idt_stub_isr_10
|
||||
isr11: jmp idt_stub_isr_11
|
||||
isr12: jmp idt_stub_isr_12
|
||||
isr13: jmp idt_stub_isr_13
|
||||
isr14: jmp idt_stub_isr_14
|
||||
isr15: jmp idt_stub_isr_15
|
||||
isr16: jmp idt_stub_isr_16
|
||||
isr17: jmp idt_stub_isr_17
|
||||
isr18: jmp idt_stub_isr_18
|
||||
isr19: jmp idt_stub_isr_19
|
||||
isr20: jmp idt_stub_isr_20
|
||||
isr21: jmp idt_stub_isr_21
|
||||
|
||||
irq0: jmp idt_stub_irq_0
|
||||
irq1: jmp idt_stub_irq_1
|
||||
|
||||
; Общая точка сборки для Исключений Процессора
|
||||
isr_common_stub:
|
||||
pushad ; Пушит edi, esi, ebp, esp, ebx, edx, ecx, eax
|
||||
|
||||
mov ax, ds ; Сохраняем сегмент данных
|
||||
push eax
|
||||
|
||||
mov ax, 0x10 ; Загружаем ядерный сегмент данных в регистры
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
|
||||
call isr_handler
|
||||
|
||||
pop eax ; Восстанавливаем оригинальный сегмент данных
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
|
||||
popad ; Восстанавливаем регистры общего назначения
|
||||
add esp, 8 ; Вычищаем из стека код ошибки и номер прерывания
|
||||
iretd ; Возвращаемся из прерывания
|
||||
|
||||
; Общая точка сборки для Прерываний Железа (IRQ)
|
||||
irq_common_stub:
|
||||
pushad
|
||||
|
||||
mov ax, ds
|
||||
push eax
|
||||
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
|
||||
call irq_handler
|
||||
|
||||
pop eax
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
|
||||
popad
|
||||
add esp, 8
|
||||
iretd
|
||||
48
kernel/io.c
Normal file
48
kernel/io.c
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#include <stdint.h>
|
||||
|
||||
static inline void outb(uint16_t port, uint8_t val) {
|
||||
__asm__ __volatile__("outb %0, %1" : : "a"(val), "Nd"(port));
|
||||
}
|
||||
|
||||
// Чтение байта из порта
|
||||
static inline uint8_t inb(uint16_t port) {
|
||||
uint8_t ret;
|
||||
__asm__ __volatile__("inb %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* --- ВОРД (16 бит) --- */
|
||||
|
||||
// Запись ворда в порт (понадобится для звука AC97 и диска)
|
||||
static inline void outw(uint16_t port, uint16_t val) {
|
||||
__asm__ __volatile__("outw %0, %1" : : "a"(val), "Nd"(port));
|
||||
}
|
||||
|
||||
// Чтение ворда из порта
|
||||
static inline uint16_t inw(uint16_t port) {
|
||||
uint16_t ret;
|
||||
__asm__ __volatile__("inw %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* --- ДВОЙНОЙ ВОРД (32 бита) --- */
|
||||
|
||||
// Запись двойного ворда в порт (для PCI сканера в Части 4)
|
||||
static inline void outl(uint16_t port, uint32_t val) {
|
||||
__asm__ __volatile__("outl %0, %1" : : "a"(val), "Nd"(port));
|
||||
}
|
||||
|
||||
// Чтение двойного ворда из порта
|
||||
static inline uint32_t inl(uint16_t port) {
|
||||
uint32_t ret;
|
||||
__asm__ __volatile__("inl %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* --- ТОРМОЗ ДЛЯ СТАРЫХ МАТЕРИНОК --- */
|
||||
|
||||
// Короткая задержка (io_wait). Нужна при общении с контроллером прерываний PIC,
|
||||
// чтобы старый чип успел переварить команду. Сбрасываем мусорный байт в неиспользуемый порт 0x80.
|
||||
static inline void io_wait(void) {
|
||||
__asm__ __volatile__("outb %%al, $0x80" : : "a"(0));
|
||||
}
|
||||
56
kernel/io.h
Normal file
56
kernel/io.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#ifndef IO_H
|
||||
#define IO_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* --- БАЙТ (8 бит) --- */
|
||||
|
||||
// Запись байта в порт
|
||||
static inline void outb(uint16_t port, uint8_t val) {
|
||||
__asm__ __volatile__("outb %0, %1" : : "a"(val), "Nd"(port));
|
||||
}
|
||||
|
||||
// Чтение байта из порта
|
||||
static inline uint8_t inb(uint16_t port) {
|
||||
uint8_t ret;
|
||||
__asm__ __volatile__("inb %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* --- ВОРД (16 бит) --- */
|
||||
|
||||
// Запись ворда в порт (понадобится для звука AC97 и диска)
|
||||
static inline void outw(uint16_t port, uint16_t val) {
|
||||
__asm__ __volatile__("outw %0, %1" : : "a"(val), "Nd"(port));
|
||||
}
|
||||
|
||||
// Чтение ворда из порта
|
||||
static inline uint16_t inw(uint16_t port) {
|
||||
uint16_t ret;
|
||||
__asm__ __volatile__("inw %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* --- ДВОЙНОЙ ВОРД (32 бита) --- */
|
||||
|
||||
// Запись двойного ворда в порт (для PCI сканера в Части 4)
|
||||
static inline void outl(uint16_t port, uint32_t val) {
|
||||
__asm__ __volatile__("outl %0, %1" : : "a"(val), "Nd"(port));
|
||||
}
|
||||
|
||||
// Чтение двойного ворда из порта
|
||||
static inline uint32_t inl(uint16_t port) {
|
||||
uint32_t ret;
|
||||
__asm__ __volatile__("inl %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* --- ТОРМОЗ ДЛЯ СТАРЫХ МАТЕРИНОК --- */
|
||||
|
||||
// Короткая задержка (io_wait). Нужна при общении с контроллером прерываний PIC,
|
||||
// чтобы старый чип успел переварить команду. Сбрасываем мусорный байт в неиспользуемый порт 0x80.
|
||||
static inline void io_wait(void) {
|
||||
__asm__ __volatile__("outb %%al, $0x80" : : "a"(0));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,9 +1,19 @@
|
|||
#include "../drivers/vga.h"
|
||||
#include "../drivers/serial.h"
|
||||
#include "gdt.h"
|
||||
#include "idt.h"
|
||||
#include "logger.h"
|
||||
|
||||
void kernel_main(void) {
|
||||
log_info(LOG_NORMAL, "Initializing serial...");
|
||||
serial_init();
|
||||
log_info(LOG_NORMAL, "Initializing VGA...");
|
||||
vga_init();
|
||||
vga_set_color(VGA_COLOR_WHITE, VGA_COLOR_LIGHT_GREEN);
|
||||
vga_println("--- BOOT PEREDOS ---");
|
||||
vga_println("---END BOOT PEREDOS---");
|
||||
log_info(LOG_NORMAL, "Setting color white on black");
|
||||
vga_set_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK);
|
||||
log_info(LOG_NORMAL, "Initializing GDT...");
|
||||
gdt_init();
|
||||
log_info(LOG_NORMAL, "Initializing IDT...");
|
||||
idt_init();
|
||||
while (1);
|
||||
}
|
||||
|
|
|
|||
38
kernel/logger.c
Normal file
38
kernel/logger.c
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#include "logger.h"
|
||||
#include "../drivers/serial.h"
|
||||
#include "../drivers/vga.h"
|
||||
|
||||
void logger_init(void) {
|
||||
serial_init();
|
||||
log_info(LOG_NORMAL, "Logger ready");
|
||||
}
|
||||
|
||||
void klog(log_level_t level, const char* prefix, const char* message) {
|
||||
// 1. Форматируем и отправляем в Serial в любом случае
|
||||
kprint_serial("[");
|
||||
kprint_serial(prefix);
|
||||
kprint_serial("] ");
|
||||
kprint_serial(message);
|
||||
kprint_serial("\n");
|
||||
|
||||
// 2. Если уровень LOG_NORMAL, дублируем вывод на экран VGA
|
||||
if (level == LOG_NORMAL) {
|
||||
vga_print("[");
|
||||
vga_print(prefix);
|
||||
vga_print("] ");
|
||||
vga_print(message);
|
||||
vga_print("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void log_info(log_level_t level, const char* message) {
|
||||
klog(level, "INFO", message);
|
||||
}
|
||||
|
||||
void log_warn(log_level_t level, const char* message) {
|
||||
klog(level, "WARN", message);
|
||||
}
|
||||
|
||||
void log_error(log_level_t level, const char* message) {
|
||||
klog(level, "ERROR", message);
|
||||
}
|
||||
21
kernel/logger.h
Normal file
21
kernel/logger.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef LOGGER_H
|
||||
#define LOGGER_H
|
||||
|
||||
// Уровни логирования
|
||||
typedef enum {
|
||||
LOG_NORMAL, // И в VGA, и в Serial (для важных событий системы)
|
||||
LOG_DETAIL // Только в Serial (детальная отладка, дампы регистров и т.д.)
|
||||
} log_level_t;
|
||||
|
||||
// Инициализация подсистемы
|
||||
void logger_init(void);
|
||||
|
||||
// Универсальная функция логирования
|
||||
void klog(log_level_t level, const char* prefix, const char* message);
|
||||
|
||||
// Удобные обёртки для повседневного использования
|
||||
void log_info(log_level_t level, const char* message);
|
||||
void log_warn(log_level_t level, const char* message);
|
||||
void log_error(log_level_t level, const char* message);
|
||||
|
||||
#endif
|
||||
Loading…
Reference in a new issue