initial commit
This commit is contained in:
commit
32ced713c3
42 changed files with 1986 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
**.o
|
||||||
|
**.bin
|
||||||
|
zalupa.iso
|
||||||
45
Makefile
Normal file
45
Makefile
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
# Настройки
|
||||||
|
CC = gcc
|
||||||
|
AS = nasm
|
||||||
|
LD = ld
|
||||||
|
|
||||||
|
# Добавь эти флаги в CFLAGS:
|
||||||
|
CFLAGS = -m32 -ffreestanding -O2 -Wall -Wextra \
|
||||||
|
-fno-stack-protector -fno-exceptions \
|
||||||
|
-fno-pie -fno-stack-protector \
|
||||||
|
-fno-asynchronous-unwind-tables \
|
||||||
|
-mno-sse -mno-sse2 -mno-sse3 -mno-3dnow -mno-mmx \
|
||||||
|
-I./src
|
||||||
|
ASFLAGS = -f elf32
|
||||||
|
LDFLAGS = -m elf_i386 -T linker.ld -nostdlib
|
||||||
|
|
||||||
|
# Файлы
|
||||||
|
SOURCES = $(wildcard src/*.c)
|
||||||
|
ASM_SOURCES = $(wildcard src/*.asm)
|
||||||
|
OBJECTS = src/boot.o $(SOURCES:.c=.o) $(ASM_SOURCES:.asm=.o)
|
||||||
|
TARGET = kernel.bin
|
||||||
|
|
||||||
|
all: $(TARGET) iso
|
||||||
|
|
||||||
|
# Сборка ядра
|
||||||
|
$(TARGET): $(OBJECTS)
|
||||||
|
$(LD) $(LDFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
%.o: %.asm
|
||||||
|
$(AS) $(ASFLAGS) $< -o $@
|
||||||
|
|
||||||
|
# Сборка ISO
|
||||||
|
iso: $(TARGET)
|
||||||
|
mkdir -p iso/boot/
|
||||||
|
cp $(TARGET) iso/boot/
|
||||||
|
grub-mkrescue -o zalupa.iso iso/
|
||||||
|
|
||||||
|
run: iso
|
||||||
|
qemu-system-i386 -vga std -cdrom zalupa.iso -serial stdio -monitor none #-d int,cpu_reset -no-reboot -no-shutdown
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f src/*.o $(TARGET) zalupa.iso
|
||||||
|
rm -rf iso/boot/$(TARGET)
|
||||||
75
README.md
Normal file
75
README.md
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
<img src="assets/logo.png" style="width: 100%; height: auto;">
|
||||||
|
<h1 align="center">ZalupaCalc 2</h1>
|
||||||
|
---
|
||||||
|
|
||||||
|
ZalupaCalc 2 - это инновационная операционная система, превращающая старые ПК в... калькуляторы. Ваш ПК не будет напрягаться вычислениями, т.к. из ОС выкинуто все, что НЕ НУЖНО калькулятору.
|
||||||
|
|
||||||
|
## Роадмап
|
||||||
|
|
||||||
|
### 1. Ядро
|
||||||
|
|
||||||
|
- [x] Оно работает
|
||||||
|
- [x] IDT, GDT
|
||||||
|
- [x] Драйвер UART COM (чтение)
|
||||||
|
- [x] Драйвер клавиатуры PS/2 (базовый)
|
||||||
|
- [x] Драйвер клавиатуры PS/2 (нормальный, с Shift, Caps Lock и прочим)
|
||||||
|
- [x] Драйвер мыши
|
||||||
|
- [x] Драйвер таймера
|
||||||
|
- [x] Физ. менеджер памяти
|
||||||
|
- [x] Вирт. менеджер памяти
|
||||||
|
- [x] Аллокатор памяти
|
||||||
|
- [x] PC Speaker
|
||||||
|
- [x] Куча ядра
|
||||||
|
|
||||||
|
### 2. Графика
|
||||||
|
|
||||||
|
- [ ] Вывод на экран попиксельно
|
||||||
|
- [ ] Вывод на экран прямоугольников и прочих фигур
|
||||||
|
- [ ] Вывод на экран текста
|
||||||
|
- [ ] Двойная буферизация
|
||||||
|
- [ ] Вывод курсора мыши на экран, движение
|
||||||
|
- [ ] Кнопки (touch zones, массив в памяти где будут координаты областей и действия при нажатии)
|
||||||
|
|
||||||
|
### 3. Математика
|
||||||
|
|
||||||
|
- [ ] Простые операции (число операнд число, базовые операнды)
|
||||||
|
- [ ] Дроби, float
|
||||||
|
- [ ] Выражения (более сложные математические операции со скобками и расставлением порядка действий)
|
||||||
|
- [ ] Больше операндов
|
||||||
|
- [ ] Инженерные функции (sin, cos и че там еще у крутых математиков)
|
||||||
|
|
||||||
|
### 4. Калькулятор
|
||||||
|
|
||||||
|
- [ ] Окно
|
||||||
|
- [ ] Рабочий ввод с клавиатуры в калькулятор
|
||||||
|
- [ ] Рабочий ввод с мыши в калькулятор (кнопки калькулятора)
|
||||||
|
|
||||||
|
### 5. Мелочевка
|
||||||
|
|
||||||
|
- [ ] Часы в углу экрана
|
||||||
|
- [ ] Логотип загрузки
|
||||||
|
- [ ] Анимации
|
||||||
|
|
||||||
|
### 6. Сохранение данных
|
||||||
|
|
||||||
|
- [ ] Драйвер ATA PIO (чтение)
|
||||||
|
- [ ] Драйвер ATA PIO (запись)
|
||||||
|
- [ ] Парсер MBR
|
||||||
|
- [ ] Запись в MBR
|
||||||
|
- [ ] Собственная ФС (скорее хранилище данных) с настройками калькулятора, историей операций итд...
|
||||||
|
- [ ] Установщик
|
||||||
|
|
||||||
|
### 7. Рофляные фукции
|
||||||
|
|
||||||
|
- [ ] Напоминания о намазе
|
||||||
|
- [ ] Офлайн казино
|
||||||
|
- [ ] Офлайн покер
|
||||||
|
- [ ] Офлайн блекджек
|
||||||
|
- [ ] Сапер
|
||||||
|
- [ ] Пасьянс косынка
|
||||||
|
|
||||||
|
## Где что есть что?
|
||||||
|
|
||||||
|
- `assets/` - ассеты для README и системы
|
||||||
|
- `iso/` - корень ISO
|
||||||
|
- `src/` - там сурсы
|
||||||
BIN
assets/logo.bmp
Normal file
BIN
assets/logo.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
assets/logo.png
Normal file
BIN
assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
7
iso/boot/grub/grub.cfg
Normal file
7
iso/boot/grub/grub.cfg
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
set timeout=0
|
||||||
|
set default=0
|
||||||
|
|
||||||
|
menuentry "ZalupaCalc 2" {
|
||||||
|
multiboot /boot/kernel.bin
|
||||||
|
boot
|
||||||
|
}
|
||||||
32
linker.ld
Normal file
32
linker.ld
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
ENTRY(_start)
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
/* Начинаем с 1 МБ */
|
||||||
|
. = 1M;
|
||||||
|
|
||||||
|
.text : AT(ADDR(.text))
|
||||||
|
{
|
||||||
|
/* Принудительно размещаем заголовок Multiboot на первой позиции */
|
||||||
|
KEEP(*(.multiboot))
|
||||||
|
*(.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
.rodata : ALIGN(4K)
|
||||||
|
{
|
||||||
|
*(.rodata)
|
||||||
|
}
|
||||||
|
|
||||||
|
.data : ALIGN(4K)
|
||||||
|
{
|
||||||
|
*(.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
.bss : ALIGN(4K)
|
||||||
|
{
|
||||||
|
*(COMMON)
|
||||||
|
*(.bss)
|
||||||
|
}
|
||||||
|
|
||||||
|
_kernel_end = .;
|
||||||
|
}
|
||||||
45
src/boot.asm
Normal file
45
src/boot.asm
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
[bits 32]
|
||||||
|
section .multiboot
|
||||||
|
|
||||||
|
MULTIBOOT_HEADER_MAGIC equ 0x1BADB002
|
||||||
|
; Бит 0: выравнивание, Бит 1: meminfo, Бит 2: графический запрос
|
||||||
|
MULTIBOOT_HEADER_FLAGS equ 0x00000007
|
||||||
|
MULTIBOOT_HEADER_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
|
||||||
|
|
||||||
|
header:
|
||||||
|
dd MULTIBOOT_HEADER_MAGIC
|
||||||
|
dd MULTIBOOT_HEADER_FLAGS
|
||||||
|
dd MULTIBOOT_HEADER_CHECKSUM
|
||||||
|
; Эти поля обязательны для флага 0x00000004:
|
||||||
|
dd 0 ; header_addr (0 для ELF)
|
||||||
|
dd 0 ; load_addr
|
||||||
|
dd 0 ; load_end_addr
|
||||||
|
dd 0 ; bss_end_addr
|
||||||
|
dd 0 ; entry_addr
|
||||||
|
; Теперь поля графики:
|
||||||
|
dd 0 ; mode_type (0 = линейный графический режим)
|
||||||
|
dd 1024 ; width
|
||||||
|
dd 768 ; height
|
||||||
|
dd 32 ; depth
|
||||||
|
|
||||||
|
section .text
|
||||||
|
global _start
|
||||||
|
extern kernel_main
|
||||||
|
|
||||||
|
_start:
|
||||||
|
mov esp, stack_top
|
||||||
|
push eax ; magic
|
||||||
|
push ebx ; mbi (здесь будет указатель на структуру с инфой о видео!)
|
||||||
|
|
||||||
|
call kernel_main
|
||||||
|
|
||||||
|
.hang:
|
||||||
|
cli
|
||||||
|
hlt
|
||||||
|
jmp .hang
|
||||||
|
|
||||||
|
section .bss
|
||||||
|
align 16
|
||||||
|
stack_bottom:
|
||||||
|
resb 16384
|
||||||
|
stack_top:
|
||||||
46
src/gdt.c
Normal file
46
src/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
src/gdt.h
Normal file
25
src/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
|
||||||
24
src/gdt_asm.asm
Normal file
24
src/gdt_asm.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
|
||||||
83
src/heap.c
Normal file
83
src/heap.c
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
#include "heap.h"
|
||||||
|
#include "vmm.h"
|
||||||
|
#include "pmm.h"
|
||||||
|
#include "serial.h"
|
||||||
|
|
||||||
|
static heap_chunk_t* heap_start_chunk = NULL;
|
||||||
|
|
||||||
|
void kheap_init(void) {
|
||||||
|
kprint_serial("HEAP: Initializing Kernel Heap...\n");
|
||||||
|
|
||||||
|
// 1. Выделяем одну физ. страницу под старт кучи
|
||||||
|
void* phys_page = pmm_alloc_page();
|
||||||
|
|
||||||
|
// 2. Мапим её на виртуальный адрес HEAP_START
|
||||||
|
// Нам нужны флаги Present (1) + Write (2) = 3
|
||||||
|
vmm_map_page((uint32_t)phys_page, HEAP_START, 3);
|
||||||
|
|
||||||
|
// 3. Создаем базовый блок на всю эту страницу
|
||||||
|
heap_start_chunk = (heap_chunk_t*)HEAP_START;
|
||||||
|
heap_start_chunk->size = PAGE_SIZE - sizeof(heap_chunk_t);
|
||||||
|
heap_start_chunk->is_free = 1;
|
||||||
|
heap_start_chunk->next = NULL;
|
||||||
|
|
||||||
|
kprint_serial("HEAP: Kernel Heap initialized successfully at 0x00400000!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void* kmalloc(size_t size) {
|
||||||
|
if (size == 0) return NULL;
|
||||||
|
|
||||||
|
// Выравниваем размер по 4 байта для стабильности на x86
|
||||||
|
size = (size + 3) & ~3;
|
||||||
|
|
||||||
|
heap_chunk_t* current = heap_start_chunk;
|
||||||
|
|
||||||
|
// Ищем подходящий свободный блок (First Fit)
|
||||||
|
while (current != NULL) {
|
||||||
|
if (current->is_free && current->size >= size) {
|
||||||
|
|
||||||
|
// Если блок сильно больше, чем надо, дробим его
|
||||||
|
// sizeof(heap_chunk_t) + 4 байта минимально на остаток
|
||||||
|
if (current->size >= size + sizeof(heap_chunk_t) + 4) {
|
||||||
|
heap_chunk_t* new_chunk = (heap_chunk_t*)((uint32_t)current + sizeof(heap_chunk_t) + size);
|
||||||
|
new_chunk->size = current->size - size - sizeof(heap_chunk_t);
|
||||||
|
new_chunk->is_free = 1;
|
||||||
|
new_chunk->next = current->next;
|
||||||
|
|
||||||
|
current->size = size;
|
||||||
|
current->next = new_chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
current->is_free = 0;
|
||||||
|
|
||||||
|
// Возвращаем указатель на память СРАЗУ ЗА заголовком
|
||||||
|
return (void*)((uint32_t)current + sizeof(heap_chunk_t));
|
||||||
|
}
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// По-хорошему, если памяти не хватило, нужно выделить еще страницу из PMM
|
||||||
|
// и расширить кучу. Но для первого теста выкинем панику.
|
||||||
|
kprint_serial("HEAP ERROR: OUT OF KERNEL HEAP MEMORY!\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kfree(void* ptr) {
|
||||||
|
if (ptr == NULL) return;
|
||||||
|
|
||||||
|
// Смещаемся назад, чтобы получить доступ к заголовку блока
|
||||||
|
heap_chunk_t* chunk = (heap_chunk_t*)((uint32_t)ptr - sizeof(heap_chunk_t));
|
||||||
|
chunk->is_free = 1;
|
||||||
|
|
||||||
|
// Склеиваем последовательные свободные блоки (Coalescing), чтобы не было фрагментации
|
||||||
|
heap_chunk_t* current = heap_start_chunk;
|
||||||
|
while (current != NULL) {
|
||||||
|
if (current->is_free && current->next && current->next->is_free) {
|
||||||
|
current->size += sizeof(heap_chunk_t) + current->next->size;
|
||||||
|
current->next = current->next->next;
|
||||||
|
// Не шагаем вперед, проверяем текущий блок еще раз с новым next
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/heap.h
Normal file
26
src/heap.h
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef HEAP_H
|
||||||
|
#define HEAP_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
// Начальный виртуальный адрес кучи ядра (3 МБ)
|
||||||
|
#define HEAP_START 0x00300000
|
||||||
|
|
||||||
|
// Структура заголовка блока памяти
|
||||||
|
typedef struct heap_chunk {
|
||||||
|
size_t size; // Размер полезных данных в байтах
|
||||||
|
uint8_t is_free; // Флаг: 1 - свободен, 0 - занят
|
||||||
|
struct heap_chunk* next; // Указатель на следующий блок
|
||||||
|
} heap_chunk_t;
|
||||||
|
|
||||||
|
// Инициализация кучи
|
||||||
|
void kheap_init(void);
|
||||||
|
|
||||||
|
// Выделение памяти
|
||||||
|
void* kmalloc(size_t size);
|
||||||
|
|
||||||
|
// Освобождение памяти
|
||||||
|
void kfree(void* ptr);
|
||||||
|
|
||||||
|
#endif
|
||||||
74
src/idt.c
Normal file
74
src/idt.c
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
#include "idt.h"
|
||||||
|
#include "io.h"
|
||||||
|
#include "serial.h"
|
||||||
|
#include "timer.h"
|
||||||
|
#include "keyboard.h"
|
||||||
|
#include "mouse.h"
|
||||||
|
|
||||||
|
struct idt_entry idt[256];
|
||||||
|
struct idt_ptr idtp;
|
||||||
|
|
||||||
|
extern void* isr_table[]; // Убедись, что это массив указателей на isr0-isr31
|
||||||
|
extern void irq0(), irq1(), irq12(), idt_load();
|
||||||
|
|
||||||
|
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(); extern void isr22(); extern void isr23();
|
||||||
|
extern void isr24(); extern void isr25(); extern void isr26(); extern void isr27();
|
||||||
|
extern void isr28(); extern void isr29(); extern void isr30(); extern void isr31();
|
||||||
|
|
||||||
|
void* isr_table[] = {
|
||||||
|
isr0, isr1, isr2, isr3, isr4, isr5, isr6, isr7,
|
||||||
|
isr8, isr9, isr10, isr11, isr12, isr13, isr14, isr15,
|
||||||
|
isr16, isr17, isr18, isr19, isr20, isr21, isr22, isr23,
|
||||||
|
isr24, isr25, isr26, isr27, isr28, isr29, isr30, isr31
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void idt_init(void) {
|
||||||
|
idtp.limit = (sizeof(struct idt_entry) * 256) - 1;
|
||||||
|
idtp.base = (uint32_t)&idt;
|
||||||
|
|
||||||
|
for (int i = 0; i < 32; i++)
|
||||||
|
idt_set_gate(i, (uint32_t)isr_table[i], 0x08, 0x8E);
|
||||||
|
|
||||||
|
idt_set_gate(32, (uint32_t)irq0, 0x08, 0x8E);
|
||||||
|
idt_set_gate(33, (uint32_t)irq1, 0x08, 0x8E);
|
||||||
|
idt_set_gate(44, (uint32_t)irq12, 0x08, 0x8E);
|
||||||
|
|
||||||
|
idt_load();
|
||||||
|
}
|
||||||
|
|
||||||
|
void isr_handler(struct registers regs) {
|
||||||
|
kprint_serial("Exception: ");
|
||||||
|
kprint_serial_dec(regs.int_no);
|
||||||
|
kprint_serial("\nSystem Halted.\n");
|
||||||
|
while(1) asm("hlt");
|
||||||
|
}
|
||||||
|
|
||||||
|
void irq_handler(struct registers regs) {
|
||||||
|
if (regs.int_no == 32) {
|
||||||
|
timer_handler();
|
||||||
|
} else if (regs.int_no == 33) {
|
||||||
|
keyboard_handler();
|
||||||
|
} else if (regs.int_no == 44) {
|
||||||
|
mouse_handler();
|
||||||
|
} else {
|
||||||
|
kprint_serial("Unknown IRQ: ");
|
||||||
|
kprint_serial_dec(regs.int_no);
|
||||||
|
kprint_serial("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regs.int_no >= 40) outb(0xA0, 0x20); // Slave
|
||||||
|
outb(0x20, 0x20); // Master
|
||||||
|
}
|
||||||
36
src/idt.h
Normal file
36
src/idt.h
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#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;
|
||||||
|
// Эти два поля ДОЛЖНЫ идти именно в таком порядке,
|
||||||
|
// чтобы 32 попало в int_no
|
||||||
|
uint32_t int_no;
|
||||||
|
uint32_t 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
|
||||||
5
src/idt_asm.asm
Normal file
5
src/idt_asm.asm
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
global idt_load
|
||||||
|
extern idtp
|
||||||
|
idt_load:
|
||||||
|
lidt [idtp]
|
||||||
|
ret
|
||||||
86
src/interrupt.asm
Normal file
86
src/interrupt.asm
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
; interrupt.asm
|
||||||
|
[extern isr_handler]
|
||||||
|
[extern irq_handler]
|
||||||
|
|
||||||
|
%macro ISR_NOERR 1
|
||||||
|
global isr%1
|
||||||
|
isr%1:
|
||||||
|
cli
|
||||||
|
push 0
|
||||||
|
push %1
|
||||||
|
jmp common_isr_stub
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
%macro ISR_ERR 1
|
||||||
|
global isr%1
|
||||||
|
isr%1:
|
||||||
|
cli
|
||||||
|
push %1
|
||||||
|
jmp common_isr_stub
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
; --- Исключения (0-31) ---
|
||||||
|
%assign i 0
|
||||||
|
%rep 32
|
||||||
|
%if i == 8 || (i >= 10 && i <= 14) || i == 17
|
||||||
|
ISR_ERR i
|
||||||
|
%else
|
||||||
|
ISR_NOERR i
|
||||||
|
%endif
|
||||||
|
%assign i i+1
|
||||||
|
%endrep
|
||||||
|
|
||||||
|
; --- Аппаратные прерывания (IRQ -> Векторы 32, 33, 44) ---
|
||||||
|
%macro IRQ 2
|
||||||
|
global irq%1
|
||||||
|
irq%1:
|
||||||
|
cli
|
||||||
|
push 0
|
||||||
|
push %2
|
||||||
|
jmp common_irq_stub
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
IRQ 0, 32
|
||||||
|
IRQ 1, 33
|
||||||
|
IRQ 12, 44
|
||||||
|
|
||||||
|
common_isr_stub:
|
||||||
|
pusha
|
||||||
|
mov ax, ds
|
||||||
|
push eax
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
mov es, ax
|
||||||
|
mov fs, ax
|
||||||
|
mov gs, ax
|
||||||
|
push esp
|
||||||
|
call isr_handler
|
||||||
|
pop eax
|
||||||
|
mov ds, ax
|
||||||
|
mov es, ax
|
||||||
|
mov fs, ax
|
||||||
|
mov gs, ax
|
||||||
|
popa
|
||||||
|
add esp, 8
|
||||||
|
iret
|
||||||
|
|
||||||
|
common_irq_stub:
|
||||||
|
pusha
|
||||||
|
mov ax, ds
|
||||||
|
push eax
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
mov es, ax
|
||||||
|
mov fs, ax
|
||||||
|
mov gs, ax
|
||||||
|
push esp
|
||||||
|
call irq_handler
|
||||||
|
pop eax
|
||||||
|
pop eax
|
||||||
|
mov ds, ax
|
||||||
|
mov es, ax
|
||||||
|
mov fs, ax
|
||||||
|
mov gs, ax
|
||||||
|
popa
|
||||||
|
add esp, 8
|
||||||
|
iret
|
||||||
48
src/io.c
Normal file
48
src/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
src/io.h
Normal file
56
src/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
|
||||||
121
src/keyboard.c
Normal file
121
src/keyboard.c
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
#include "keyboard.h"
|
||||||
|
#include "serial.h"
|
||||||
|
#include "io.h"
|
||||||
|
|
||||||
|
// Состояние модификаторов клавиатуры
|
||||||
|
static int is_e0 = 0;
|
||||||
|
static int shift_pressed = 0;
|
||||||
|
static int caps_lock_active = 0;
|
||||||
|
|
||||||
|
// Скан-коды управления (Set 1)
|
||||||
|
#define SCAN_LSHIFT 0x2A
|
||||||
|
#define SCAN_RSHIFT 0x36
|
||||||
|
#define SCAN_CAPS_LOCK 0x3A
|
||||||
|
|
||||||
|
// Скан-коды стрелок (после 0xE0)
|
||||||
|
#define SCAN_UP 0x48
|
||||||
|
#define SCAN_DOWN 0x50
|
||||||
|
#define SCAN_LEFT 0x4B
|
||||||
|
#define SCAN_RIGHT 0x4D
|
||||||
|
|
||||||
|
// Таблица 1: Обычные символы (без Shift)
|
||||||
|
static const char kbdus_lower[128] = {
|
||||||
|
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
|
||||||
|
'\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
|
||||||
|
0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0,
|
||||||
|
'\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0,
|
||||||
|
' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, '-', 0, 0, 0, '+', 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Таблица 2: Символы при зажатом Shift
|
||||||
|
static const char kbdus_upper[128] = {
|
||||||
|
0, 27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b',
|
||||||
|
'\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n',
|
||||||
|
0, 'A', 's', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0,
|
||||||
|
'|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, '*', 0,
|
||||||
|
' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, '_', 0, 0, 0, '+', 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
void keyboard_init(void) {
|
||||||
|
shift_pressed = 0;
|
||||||
|
caps_lock_active = 0;
|
||||||
|
is_e0 = 0;
|
||||||
|
kprint_serial("[INFO] Full keyboard driver with Modifiers initialized.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void keyboard_handler(void) {
|
||||||
|
uint8_t status = inb(0x64);
|
||||||
|
|
||||||
|
// Бит 5 равен 1, если данные пришли от мыши (Auxiliary Device)
|
||||||
|
// Мышь не должна обрабатываться здесь!
|
||||||
|
if (status & 0x20) {
|
||||||
|
// Очистим порт, чтобы не висел, но не обрабатываем как клаву
|
||||||
|
inb(0x60);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если мы здесь, значит это точно клавиатура
|
||||||
|
if (!(status & 0x01)) return; // Нет данных
|
||||||
|
|
||||||
|
uint8_t scancode = inb(0x60);
|
||||||
|
|
||||||
|
// Обработка префикса 0xE0
|
||||||
|
if (scancode == 0xE0) {
|
||||||
|
is_e0 = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обработка Break Code (отпускание)
|
||||||
|
if (scancode & 0x80) {
|
||||||
|
uint8_t released_code = scancode & 0x7F;
|
||||||
|
if (released_code == SCAN_LSHIFT || released_code == SCAN_RSHIFT) {
|
||||||
|
shift_pressed = 0;
|
||||||
|
}
|
||||||
|
is_e0 = 0; // Сброс префикса
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обработка стрелок (расширенные)
|
||||||
|
if (is_e0) {
|
||||||
|
is_e0 = 0;
|
||||||
|
switch (scancode) {
|
||||||
|
case SCAN_UP: /* ... */ break;
|
||||||
|
case SCAN_DOWN: /* ... */ break;
|
||||||
|
case SCAN_LEFT: /* ... */ break;
|
||||||
|
case SCAN_RIGHT: /* ... */ break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обычные модификаторы
|
||||||
|
if (scancode == SCAN_LSHIFT || scancode == SCAN_RSHIFT) {
|
||||||
|
shift_pressed = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scancode == SCAN_CAPS_LOCK) {
|
||||||
|
caps_lock_active = !caps_lock_active;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Печать символов
|
||||||
|
if (scancode < 128) {
|
||||||
|
char c = 0;
|
||||||
|
int use_upper = shift_pressed;
|
||||||
|
char base_char = kbdus_lower[scancode];
|
||||||
|
int is_alpha = (base_char >= 'a' && base_char <= 'z');
|
||||||
|
|
||||||
|
if (is_alpha && caps_lock_active) use_upper = !use_upper;
|
||||||
|
|
||||||
|
c = (use_upper) ? kbdus_upper[scancode] : kbdus_lower[scancode];
|
||||||
|
|
||||||
|
if (c != 0) {
|
||||||
|
char str[2] = {c, 0};
|
||||||
|
kprint_serial(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/keyboard.h
Normal file
10
src/keyboard.h
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef KEYBOARD_H
|
||||||
|
#define KEYBOARD_H
|
||||||
|
|
||||||
|
// Инициализация (пока просто выведем лог, что всё ок)
|
||||||
|
void keyboard_init(void);
|
||||||
|
|
||||||
|
// Основной обработчик прерывания, который мы вызовем из idt.c
|
||||||
|
void keyboard_handler(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
171
src/kmain.c
Normal file
171
src/kmain.c
Normal file
|
|
@ -0,0 +1,171 @@
|
||||||
|
#include "serial.h"
|
||||||
|
#include "mouse.h"
|
||||||
|
#include "timer.h"
|
||||||
|
#include "speaker.h"
|
||||||
|
#include "gdt.h"
|
||||||
|
#include "idt.h"
|
||||||
|
#include "heap.h"
|
||||||
|
#include "pmm.h"
|
||||||
|
#include "vmm.h"
|
||||||
|
#include "pic.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "string.h"
|
||||||
|
#include "multiboot.h"
|
||||||
|
|
||||||
|
extern uint32_t _kernel_end; // Символ 'end' прописан в конце любого стандартного linker.ld
|
||||||
|
uint32_t kernel_end = (uint32_t)&_kernel_end;
|
||||||
|
|
||||||
|
void kernel_main(uint32_t mboot_addr, uint32_t magic) {
|
||||||
|
log_info(LOG_NORMAL, "Initializing serial...");
|
||||||
|
serial_init();
|
||||||
|
if (magic != MULTIBOOT_BOOTLOADER_MAGIC) {
|
||||||
|
log_error(LOG_NORMAL, "fuck, i got invalid multiboot magic. IS THAT REALLY THE MULTIBOOT YOU TALKED ABOUT?");
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
struct multiboot_info* mbi = (struct multiboot_info*)mboot_addr;
|
||||||
|
if (!(mbi->flags & (1 << 6))) {
|
||||||
|
log_error(LOG_NORMAL, "fuck, i haven't got a memory map. IS THAT REALLY THE MULTIBOOT YOU TALKED ABOUT?");
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
log_info(LOG_NORMAL, "Initializing GDT...");
|
||||||
|
gdt_init();
|
||||||
|
log_info(LOG_NORMAL, "Initializing IDT...");
|
||||||
|
idt_init();
|
||||||
|
log_info(LOG_NORMAL, "Remapping PIC...");
|
||||||
|
pic_remap(32, 40);
|
||||||
|
log_info(LOG_NORMAL, "Unmasking IRQ 0... (timer)");
|
||||||
|
pic_clear_mask(0);
|
||||||
|
log_info(LOG_NORMAL, "Unmasking IRQ 1... (keyboard)");
|
||||||
|
pic_clear_mask(1);
|
||||||
|
log_info(LOG_NORMAL, "Unmasking IRQ 2... (slave pic)");
|
||||||
|
pic_clear_mask(2);
|
||||||
|
log_info(LOG_NORMAL, "Unmasking IRQ 12... (mouse)");
|
||||||
|
pic_clear_mask(12);
|
||||||
|
log_info(LOG_NORMAL, "Registering timer at 100Hz");
|
||||||
|
timer_init(100);
|
||||||
|
log_info(LOG_NORMAL, "Registering mouse");
|
||||||
|
mouse_init();
|
||||||
|
kprint_serial("--------------------\n");
|
||||||
|
log_info(LOG_NORMAL, "Enabling PMM...");
|
||||||
|
pmm_init(mbi, (uint32_t)&_kernel_end);
|
||||||
|
kprint_serial("--- Memory Map From GRUB ---");
|
||||||
|
struct multiboot_mmap_entry* mmap = (struct multiboot_mmap_entry*)mbi->mmap_addr;
|
||||||
|
|
||||||
|
while((uint32_t)mmap < mbi->mmap_addr + mbi->mmap_length) {
|
||||||
|
char addr_buf[32];
|
||||||
|
char len_buf[32];
|
||||||
|
|
||||||
|
itoa((uint32_t)mmap->addr, addr_buf, 16);
|
||||||
|
itoa((uint32_t)mmap->len, len_buf, 10);
|
||||||
|
|
||||||
|
kprint_serial("Region: Base=0x");
|
||||||
|
kprint_serial(addr_buf);
|
||||||
|
kprint_serial(" Size=");
|
||||||
|
kprint_serial(len_buf);
|
||||||
|
|
||||||
|
if (mmap->type == MULTIBOOT_MEMORY_AVAILABLE) {
|
||||||
|
kprint_serial(" [FREE RAM]\n");
|
||||||
|
} else {
|
||||||
|
kprint_serial(" [RESERVED]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Переходим к следующей записи (учитываем поле size)
|
||||||
|
mmap = (struct multiboot_mmap_entry*)((uint32_t)mmap + mmap->size + sizeof(mmap->size));
|
||||||
|
}
|
||||||
|
kprint_serial("--- trying to allocate some fucking memory... ---\n");
|
||||||
|
kprint_serial("page 1... ");
|
||||||
|
void* p1 = pmm_alloc_page();
|
||||||
|
kprint_serial("page 2...");
|
||||||
|
void* p2 = pmm_alloc_page();
|
||||||
|
kprint_serial("ok!");
|
||||||
|
char buf[32];
|
||||||
|
kprint_serial("PMM TEST: Page 1 allocated at 0x");
|
||||||
|
itoa((uint32_t)p1, buf, 16);
|
||||||
|
kprint_serial(buf);
|
||||||
|
kprint_serial("\n");
|
||||||
|
|
||||||
|
kprint_serial("PMM TEST: Page 2 allocated at 0x");
|
||||||
|
itoa((uint32_t)p2, buf, 16);
|
||||||
|
kprint_serial(buf);
|
||||||
|
kprint_serial("\n");
|
||||||
|
|
||||||
|
kprint_serial("freeing the pages back... page 1... ");
|
||||||
|
pmm_free_page(p1);
|
||||||
|
kprint_serial("page 2... ");
|
||||||
|
pmm_free_page(p2);
|
||||||
|
kprint_serial("it don't died!\n");
|
||||||
|
|
||||||
|
kprint_serial("\n--- PMM MEMORY STATS ---\n");
|
||||||
|
|
||||||
|
pmm_get_stats(); // Просто запускаем сбор, она запишет всё в глобальную переменную
|
||||||
|
|
||||||
|
// Берем указатель на глобальную переменную
|
||||||
|
pmm_stats_t* my_stats = pmm_get_stats_ptr();
|
||||||
|
|
||||||
|
kprint_serial("Total Memory: ");
|
||||||
|
itoa(my_stats->total_memory >> 20, buf, 10);
|
||||||
|
kprint_serial(buf);
|
||||||
|
kprint_serial(" MB\n");
|
||||||
|
|
||||||
|
kprint_serial("Used Memory: ");
|
||||||
|
itoa(my_stats->used_memory >> 10, buf, 10);
|
||||||
|
kprint_serial(buf);
|
||||||
|
kprint_serial(" KB\n");
|
||||||
|
|
||||||
|
kprint_serial("Free Memory: ");
|
||||||
|
itoa(my_stats->free_memory >> 20, buf, 10);
|
||||||
|
kprint_serial(buf);
|
||||||
|
kprint_serial(" MB\n");
|
||||||
|
|
||||||
|
kprint_serial("------------------------\n\n");
|
||||||
|
log_info(LOG_NORMAL, "Enabling VMM...");
|
||||||
|
vmm_init();
|
||||||
|
log_info(LOG_NORMAL, "Enabling KHeap...");
|
||||||
|
kheap_init();
|
||||||
|
|
||||||
|
kprint_serial("--- HEAP TESTING ---\n");
|
||||||
|
|
||||||
|
// Выделяем три разных куска памяти
|
||||||
|
int* arr1 = (int*)kmalloc(10 * sizeof(int));
|
||||||
|
char* str = (char*)kmalloc(64);
|
||||||
|
int* arr2 = (int*)kmalloc(5 * sizeof(int));
|
||||||
|
char hbuf[32];
|
||||||
|
kprint_serial("Allocated arr1 at virtual address: 0x");
|
||||||
|
itoa((uint32_t)arr1, hbuf, 16);
|
||||||
|
kprint_serial(hbuf);
|
||||||
|
kprint_serial("\n");
|
||||||
|
kprint_serial("Allocated str at virtual address: 0x");
|
||||||
|
itoa((uint32_t)str, hbuf, 16);
|
||||||
|
kprint_serial(hbuf);
|
||||||
|
kprint_serial("\n");
|
||||||
|
// Запишем туда данные, чтобы проверить, что маппинг страниц не падает при записи
|
||||||
|
arr1[0] = 1337;
|
||||||
|
str[0] = 'H';
|
||||||
|
str[1] = 'A';
|
||||||
|
str[2] = 'I';
|
||||||
|
str[3] = '\0';
|
||||||
|
kprint_serial("Data integrity check: str is ");
|
||||||
|
kprint_serial(str);
|
||||||
|
kprint_serial("\n");
|
||||||
|
// Освобождаем память обратно
|
||||||
|
kfree(arr1);
|
||||||
|
kfree(str);
|
||||||
|
kfree(arr2);
|
||||||
|
|
||||||
|
kprint_serial("Kfree done. It don't died twice!\n");
|
||||||
|
log_info(LOG_NORMAL, "Enabling IRQ...");
|
||||||
|
__asm__ __volatile__("sti");
|
||||||
|
speaker_beep(100, 10);
|
||||||
|
speaker_beep(125, 10);
|
||||||
|
speaker_beep(150, 10);
|
||||||
|
speaker_beep(175, 10);
|
||||||
|
speaker_beep(200, 10);
|
||||||
|
speaker_beep(225, 10);
|
||||||
|
speaker_beep(250, 10);
|
||||||
|
speaker_beep(275, 10);
|
||||||
|
speaker_beep(300, 10);
|
||||||
|
speaker_beep(325, 10);
|
||||||
|
speaker_beep(350, 400);
|
||||||
|
log_info(LOG_NORMAL, "OK!");
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
27
src/logger.c
Normal file
27
src/logger.c
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
#include "logger.h"
|
||||||
|
#include "serial.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(prefix);
|
||||||
|
kprint_serial(": ");
|
||||||
|
kprint_serial(message);
|
||||||
|
kprint_serial("\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
src/logger.h
Normal file
21
src/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
|
||||||
128
src/mouse.c
Normal file
128
src/mouse.c
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
#include "mouse.h"
|
||||||
|
#include "io.h"
|
||||||
|
#include "string.h"
|
||||||
|
#include "serial.h" // Для kprint_serial
|
||||||
|
|
||||||
|
#define PS2_DATA 0x60
|
||||||
|
#define PS2_STATUS 0x64
|
||||||
|
#define PS2_COMMAND 0x64
|
||||||
|
|
||||||
|
static void ps2_wait_write(void) {
|
||||||
|
while (inb(PS2_STATUS) & 0x02);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ps2_wait_read(void) {
|
||||||
|
while (!(inb(PS2_STATUS) & 0x01));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mouse_write(uint8_t data) {
|
||||||
|
ps2_wait_write();
|
||||||
|
outb(PS2_COMMAND, 0xD4); // Сигнал: шлем команду именно мыши
|
||||||
|
ps2_wait_write();
|
||||||
|
outb(PS2_DATA, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t mouse_read(void) {
|
||||||
|
ps2_wait_read();
|
||||||
|
return inb(PS2_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mouse_init(void) {
|
||||||
|
uint8_t status;
|
||||||
|
|
||||||
|
kprint_serial("Initializing PS/2 Mouse...\n");
|
||||||
|
|
||||||
|
// Включаем второй порт PS/2 (мышь)
|
||||||
|
ps2_wait_write();
|
||||||
|
outb(PS2_COMMAND, 0xA8);
|
||||||
|
io_wait();
|
||||||
|
|
||||||
|
// Читаем Command Byte, чтобы включить прерывания
|
||||||
|
ps2_wait_write();
|
||||||
|
outb(PS2_COMMAND, 0x20);
|
||||||
|
ps2_wait_read();
|
||||||
|
status = inb(PS2_DATA) | 2; // Бит 1 — прерывания IRQ12 от мыши
|
||||||
|
status &= ~0x20; // Сбрасываем Бит 5 — активируем Mouse Clock
|
||||||
|
|
||||||
|
// Записываем Command Byte обратно
|
||||||
|
ps2_wait_write();
|
||||||
|
outb(PS2_COMMAND, 0x60);
|
||||||
|
ps2_wait_write();
|
||||||
|
outb(PS2_DATA, status);
|
||||||
|
|
||||||
|
// Настраиваем саму мышь
|
||||||
|
mouse_write(0xF6); // Загрузить дефолтные параметры
|
||||||
|
mouse_read(); // Читаем ACK (0xFA)
|
||||||
|
|
||||||
|
mouse_write(0xF4); // Включаем передачу пакетов (Data Reporting)
|
||||||
|
mouse_read(); // Читаем ACK (0xFA)
|
||||||
|
|
||||||
|
kprint_serial("Mouse initialized successfully.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t mouse_cycle = 0;
|
||||||
|
static uint8_t mouse_packet[3];
|
||||||
|
static int mouse_x = 40; // Стартуем по центру текстового экрана 80x25
|
||||||
|
static int mouse_y = 12;
|
||||||
|
|
||||||
|
void mouse_handler(void) {
|
||||||
|
uint8_t status = inb(PS2_STATUS);
|
||||||
|
|
||||||
|
// Проверяем, что в буфере реально есть данные и они от мыши (бит 5 выставлен)
|
||||||
|
if (!(status & 0x01) || !(status & 0x20)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ДЕБАГ: выводим каждый байт
|
||||||
|
kprint_serial("M_BYTE: ");
|
||||||
|
kprint_serial_hex(status);
|
||||||
|
kprint_serial("\n");
|
||||||
|
|
||||||
|
mouse_packet[mouse_cycle++] = inb(PS2_DATA);
|
||||||
|
|
||||||
|
if (mouse_cycle == 3) {
|
||||||
|
mouse_cycle = 0;
|
||||||
|
|
||||||
|
// Бит 3 первого байта всегда должен быть равен 1 (проверка синхронизации)
|
||||||
|
if (!(mouse_packet[0] & 0x08)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t flags = mouse_packet[0];
|
||||||
|
int offset_x = (int)mouse_packet[1];
|
||||||
|
int offset_y = (int)mouse_packet[2];
|
||||||
|
|
||||||
|
// Обработка знаков смещения (8 бит -> 32 бита с сохранением знака)
|
||||||
|
if (flags & 0x10) offset_x |= 0xFFFFFF00;
|
||||||
|
if (flags & 0x20) offset_y |= 0xFFFFFF00;
|
||||||
|
|
||||||
|
mouse_x += offset_x;
|
||||||
|
mouse_y -= offset_y; // Инвертируем Y, так как в VGA координатах 0 верх
|
||||||
|
|
||||||
|
// Ограничиваем рамками консоли 80x25
|
||||||
|
if (mouse_x < 0) mouse_x = 0;
|
||||||
|
if (mouse_x >= 80) mouse_x = 79;
|
||||||
|
if (mouse_y < 0) mouse_y = 0;
|
||||||
|
if (mouse_y >= 25) mouse_y = 24;
|
||||||
|
|
||||||
|
// Чек кнопок
|
||||||
|
int left_click = flags & 0x01;
|
||||||
|
int right_click = flags & 0x02;
|
||||||
|
|
||||||
|
// Выводим инфу в UART через itoa
|
||||||
|
char buf[16];
|
||||||
|
|
||||||
|
kprint_serial("Mouse: X=");
|
||||||
|
itoa(mouse_x, buf, 10);
|
||||||
|
kprint_serial(buf);
|
||||||
|
|
||||||
|
kprint_serial(" Y=");
|
||||||
|
itoa(mouse_y, buf, 10);
|
||||||
|
kprint_serial(buf);
|
||||||
|
|
||||||
|
if (left_click) kprint_serial(" [LEFT]");
|
||||||
|
if (right_click) kprint_serial(" [RIGHT]");
|
||||||
|
|
||||||
|
kprint_serial("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/mouse.h
Normal file
9
src/mouse.h
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef MOUSE_H
|
||||||
|
#define MOUSE_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void mouse_init(void);
|
||||||
|
void mouse_handler(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
47
src/multiboot.h
Normal file
47
src/multiboot.h
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
#ifndef MULTIBOOT_H
|
||||||
|
#define MULTIBOOT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
|
||||||
|
|
||||||
|
struct multiboot_mmap_entry {
|
||||||
|
uint32_t size;
|
||||||
|
uint64_t addr;
|
||||||
|
uint64_t len;
|
||||||
|
#define MULTIBOOT_MEMORY_AVAILABLE 1
|
||||||
|
#define MULTIBOOT_MEMORY_RESERVED 2
|
||||||
|
#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3
|
||||||
|
#define MULTIBOOT_MEMORY_NVS 4
|
||||||
|
#define MULTIBOOT_MEMORY_BADRAM 5
|
||||||
|
uint32_t type;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct multiboot_info {
|
||||||
|
uint32_t flags;
|
||||||
|
uint32_t mem_lower;
|
||||||
|
uint32_t mem_upper;
|
||||||
|
uint32_t boot_device;
|
||||||
|
uint32_t cmdline;
|
||||||
|
uint32_t mods_count;
|
||||||
|
uint32_t mods_addr;
|
||||||
|
uint32_t num;
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t addr;
|
||||||
|
uint32_t shndx;
|
||||||
|
uint32_t mmap_length;
|
||||||
|
uint32_t mmap_addr; // <-- Вот это нам нужно! Адрес карты памяти
|
||||||
|
uint32_t drives_length;
|
||||||
|
uint32_t drives_addr;
|
||||||
|
uint32_t config_table;
|
||||||
|
uint32_t boot_loader_name;
|
||||||
|
uint32_t apm_table;
|
||||||
|
uint32_t vbe_control_info;
|
||||||
|
uint32_t vbe_mode_info;
|
||||||
|
uint16_t vbe_mode;
|
||||||
|
uint16_t vbe_interface_seg;
|
||||||
|
uint16_t vbe_interface_off;
|
||||||
|
uint16_t vbe_interface_len;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
#endif
|
||||||
76
src/pic.c
Normal file
76
src/pic.c
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
#include "pic.h"
|
||||||
|
#include "io.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
#define PIC1_COMMAND 0x20
|
||||||
|
#define PIC1_DATA 0x21
|
||||||
|
#define PIC2_COMMAND 0xA0
|
||||||
|
#define PIC2_DATA 0xA1
|
||||||
|
|
||||||
|
#define ICW1_INIT 0x10 // Инициализация
|
||||||
|
#define ICW1_IC4 0x01 // Будет ICW4
|
||||||
|
#define ICW4_8086 0x01 // Режим 8086/8186
|
||||||
|
|
||||||
|
void pic_remap(uint8_t master_offset, uint8_t slave_offset) {
|
||||||
|
log_info(LOG_DETAIL, "Remapping PIC...");
|
||||||
|
|
||||||
|
// Сохраняем текущие маски прерываний (какое железо сейчас отключено)
|
||||||
|
uint8_t a1 = inb(PIC1_DATA);
|
||||||
|
uint8_t a2 = inb(PIC2_DATA);
|
||||||
|
|
||||||
|
// Свисток к началу инициализации (ICW1)
|
||||||
|
outb(PIC1_COMMAND, ICW1_INIT | ICW1_IC4);
|
||||||
|
io_wait();
|
||||||
|
outb(PIC2_COMMAND, ICW1_INIT | ICW1_IC4);
|
||||||
|
io_wait();
|
||||||
|
|
||||||
|
// Задаем новые смещения векторов (ICW2)
|
||||||
|
outb(PIC1_DATA, master_offset); // Master IRQ 0-7 -> вешаем на master_offset (32)
|
||||||
|
io_wait();
|
||||||
|
outb(PIC2_DATA, slave_offset); // Slave IRQ 8-15 -> вешаем на slave_offset (40)
|
||||||
|
io_wait();
|
||||||
|
|
||||||
|
// Связываем Master и Slave между собой (ICW3)
|
||||||
|
outb(PIC1_DATA, 4); // Сообщаем Master, что Slave сидит на IRQ2 (00000100б = 4)
|
||||||
|
io_wait();
|
||||||
|
outb(PIC2_DATA, 2); // Сообщаем Slave его каскадный ID (2)
|
||||||
|
io_wait();
|
||||||
|
|
||||||
|
// Включаем режим 8086 (ICW4)
|
||||||
|
outb(PIC1_DATA, ICW4_8086);
|
||||||
|
io_wait();
|
||||||
|
outb(PIC2_DATA, ICW4_8086);
|
||||||
|
io_wait();
|
||||||
|
|
||||||
|
// Восстанавливаем сохраненные маски
|
||||||
|
outb(PIC1_DATA, a1);
|
||||||
|
outb(PIC2_DATA, a2);
|
||||||
|
|
||||||
|
log_info(LOG_DETAIL, "PIC remapped");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Запретить прерывание от конкретного IRQ
|
||||||
|
void pic_set_mask(uint8_t irq_line) {
|
||||||
|
uint16_t port;
|
||||||
|
if (irq_line < 8) {
|
||||||
|
port = PIC1_DATA;
|
||||||
|
} else {
|
||||||
|
port = PIC2_DATA;
|
||||||
|
irq_line -= 8;
|
||||||
|
}
|
||||||
|
uint8_t value = inb(port) | (1 << irq_line);
|
||||||
|
outb(port, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Разрешить прерывание от конкретного IRQ
|
||||||
|
void pic_clear_mask(uint8_t irq_line) {
|
||||||
|
uint16_t port;
|
||||||
|
if (irq_line < 8) {
|
||||||
|
port = PIC1_DATA;
|
||||||
|
} else {
|
||||||
|
port = PIC2_DATA;
|
||||||
|
irq_line -= 8;
|
||||||
|
}
|
||||||
|
uint8_t value = inb(port) & ~(1 << irq_line);
|
||||||
|
outb(port, value);
|
||||||
|
}
|
||||||
13
src/pic.h
Normal file
13
src/pic.h
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef PIC_H
|
||||||
|
#define PIC_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Перемапливание базовых векторов прерываний для Master и Slave PIC
|
||||||
|
void pic_remap(uint8_t master_offset, uint8_t slave_offset);
|
||||||
|
|
||||||
|
// Маскирование/размаскирование конкретной линии IRQ
|
||||||
|
void pic_set_mask(uint8_t irq_line);
|
||||||
|
void pic_clear_mask(uint8_t irq_line);
|
||||||
|
|
||||||
|
#endif
|
||||||
119
src/pmm.c
Normal file
119
src/pmm.c
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
#include "pmm.h"
|
||||||
|
#include "string.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "serial.h"
|
||||||
|
|
||||||
|
// Указатель на начало нашей битовой карты в памяти
|
||||||
|
static uint32_t* pmm_bitmap = NULL;
|
||||||
|
static uint32_t pmm_bitmap_size = 0;
|
||||||
|
static uint32_t total_pages = 0;
|
||||||
|
|
||||||
|
static pmm_stats_t global_stats;
|
||||||
|
|
||||||
|
// Вспомогательные инлайны для быстрой работы с битами ядра
|
||||||
|
static inline void bitmap_set(uint32_t page_index) {
|
||||||
|
pmm_bitmap[page_index / 32] |= (1 << (page_index % 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bitmap_clear(uint32_t page_index) {
|
||||||
|
pmm_bitmap[page_index / 32] &= ~(1 << (page_index % 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int bitmap_test(uint32_t page_index) {
|
||||||
|
return (pmm_bitmap[page_index / 32] & (1 << (page_index % 32))) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pmm_init(struct multiboot_info* mbi, uint32_t kernel_end) {
|
||||||
|
// Рассчитываем менеджер на 128 МБ памяти
|
||||||
|
uint32_t mem_size = 128 * 1024 * 1024;
|
||||||
|
total_pages = mem_size / PAGE_SIZE;
|
||||||
|
|
||||||
|
// Саму карту пихаем прямо за концом ядра (куда указывает линкер)
|
||||||
|
pmm_bitmap = (uint32_t*)kernel_end;
|
||||||
|
pmm_bitmap_size = total_pages / 8; // Размер карты в байтах (4096 байт)
|
||||||
|
|
||||||
|
uint32_t bitmap_end = kernel_end + pmm_bitmap_size;
|
||||||
|
|
||||||
|
// Шаг 1: Помечаем вообще всё как занятое (1). Безопасность превыше всего.
|
||||||
|
memset(pmm_bitmap, 0xFF, pmm_bitmap_size);
|
||||||
|
|
||||||
|
// Шаг 2: Парсим карту памяти от GRUB и открываем доступные регионы
|
||||||
|
struct multiboot_mmap_entry* mmap = (struct multiboot_mmap_entry*)mbi->mmap_addr;
|
||||||
|
|
||||||
|
while((uint32_t)mmap < mbi->mmap_addr + mbi->mmap_length) {
|
||||||
|
// Если GRUB говорит, что этот кусок RAM свободен для юзания
|
||||||
|
if (mmap->type == MULTIBOOT_MEMORY_AVAILABLE) {
|
||||||
|
uint32_t start_addr = (uint32_t)mmap->addr;
|
||||||
|
uint32_t end_addr = start_addr + (uint32_t)mmap->len;
|
||||||
|
|
||||||
|
for (uint32_t addr = start_addr; addr < end_addr; addr += PAGE_SIZE) {
|
||||||
|
uint32_t page_idx = addr / PAGE_SIZE;
|
||||||
|
if (page_idx < total_pages) {
|
||||||
|
bitmap_clear(page_idx); // Ставим 0 (страница свободна)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Шагаем к следующей записи в таблице Multiboot
|
||||||
|
mmap = (struct multiboot_mmap_entry*)((uint32_t)mmap + mmap->size + sizeof(mmap->size));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Шаг 3: Жестко защищаем саму ОС!
|
||||||
|
// Всё, что лежит от физического адреса 0x0 до bitmap_end (код ядра, стек, сама битовая карта),
|
||||||
|
// мы принудительно забиваем единицами, чтобы аллокатор туда никогда не залез.
|
||||||
|
for (uint32_t addr = 0; addr < bitmap_end; addr += PAGE_SIZE) {
|
||||||
|
bitmap_set(addr / PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info(LOG_NORMAL, "pmm works.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void* pmm_alloc_page(void) {
|
||||||
|
// Тупо линейно ищем первый попавшийся бит, равный 0
|
||||||
|
for (uint32_t i = 0; i < total_pages; i++) {
|
||||||
|
if (!bitmap_test(i)) {
|
||||||
|
bitmap_set(i); // Застолбили страницу (поставили 1)
|
||||||
|
return (void*)(i * PAGE_SIZE); // Считаем её реальный физический адрес
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error(LOG_NORMAL, "PMM: FUCK, WHERE IS ALL OUR FUCKING MEMORY?!");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pmm_free_page(void* addr) {
|
||||||
|
uint32_t page_idx = ((uint32_t)addr) / PAGE_SIZE;
|
||||||
|
if (page_idx < total_pages) {
|
||||||
|
bitmap_clear(page_idx); // Сбросили в 0, страница снова чиста
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pmm_get_stats(void) {
|
||||||
|
kprint_serial("Someone wants memory stats! Running the test!\n");
|
||||||
|
uint32_t used = 0;
|
||||||
|
uint32_t free = 0;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < total_pages; i++) {
|
||||||
|
if (bitmap_test(i)) {
|
||||||
|
used++;
|
||||||
|
} else {
|
||||||
|
free++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kprint_serial("Okay, i am collected the stats. ");
|
||||||
|
|
||||||
|
// Пишем в ГЛОБАЛЬНУЮ переменную
|
||||||
|
global_stats.total_pages = total_pages;
|
||||||
|
global_stats.used_pages = used;
|
||||||
|
global_stats.free_pages = free;
|
||||||
|
global_stats.total_memory = total_pages << 12;
|
||||||
|
global_stats.used_memory = used << 12;
|
||||||
|
global_stats.free_memory = free << 12;
|
||||||
|
|
||||||
|
kprint_serial("Goodbye!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавим функцию-геттер, которая просто вернет указатель на эту глобальную память
|
||||||
|
pmm_stats_t* pmm_get_stats_ptr(void) {
|
||||||
|
return &global_stats;
|
||||||
|
}
|
||||||
30
src/pmm.h
Normal file
30
src/pmm.h
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
#ifndef PMM_H
|
||||||
|
#define PMM_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "multiboot.h"
|
||||||
|
|
||||||
|
#define PAGE_SIZE 4096
|
||||||
|
|
||||||
|
// Инициализация: передаем инфу от GRUB и адрес конца ядра из линкера
|
||||||
|
void pmm_init(struct multiboot_info* mbi, uint32_t kernel_end);
|
||||||
|
|
||||||
|
// Выделить одну страницу памяти (возвращает физический адрес)
|
||||||
|
void* pmm_alloc_page(void);
|
||||||
|
|
||||||
|
// Освободить страницу памяти по адресу
|
||||||
|
void pmm_free_page(void* addr);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t total_memory; // Всего памяти в байтах
|
||||||
|
uint32_t used_memory; // Занято в байтах
|
||||||
|
uint32_t free_memory; // Свободно в байтах
|
||||||
|
uint32_t total_pages; // Всего страниц
|
||||||
|
uint32_t used_pages; // Занято страниц
|
||||||
|
uint32_t free_pages; // Свободно страниц
|
||||||
|
} pmm_stats_t;
|
||||||
|
|
||||||
|
void pmm_get_stats(void);
|
||||||
|
pmm_stats_t* pmm_get_stats_ptr(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
60
src/serial.c
Normal file
60
src/serial.c
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
#include "io.h" // Подключаем наши порты
|
||||||
|
#include <stdint.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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void kprint_serial_hex(uint32_t num) {
|
||||||
|
const char hex_chars[] = "0123456789ABCDEF";
|
||||||
|
char buffer[11]; // "0x" + 8 символов + '\0'
|
||||||
|
|
||||||
|
buffer[0] = '0';
|
||||||
|
buffer[1] = 'x';
|
||||||
|
buffer[10] = '\0';
|
||||||
|
|
||||||
|
// Заполняем с конца (8 символов)
|
||||||
|
for (int i = 9; i >= 2; i--) {
|
||||||
|
buffer[i] = hex_chars[num & 0xF];
|
||||||
|
num >>= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
kprint_serial(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kprint_serial_dec(uint32_t num) {
|
||||||
|
if (num == 0) {
|
||||||
|
kprint_serial("0");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char buf[12];
|
||||||
|
int i = 10;
|
||||||
|
buf[11] = '\0';
|
||||||
|
while (num > 0) {
|
||||||
|
buf[i--] = (num % 10) + '0';
|
||||||
|
num /= 10;
|
||||||
|
}
|
||||||
|
kprint_serial(&buf[i + 1]);
|
||||||
|
}
|
||||||
20
src/serial.h
Normal file
20
src/serial.h
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef SERIAL_H
|
||||||
|
#define SERIAL_H
|
||||||
|
#include <stdint.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);
|
||||||
|
|
||||||
|
void kprint_serial_hex(uint32_t num);
|
||||||
|
void kprint_serial_dec(uint32_t num);
|
||||||
|
|
||||||
|
#endif
|
||||||
48
src/speaker.c
Normal file
48
src/speaker.c
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
#include "speaker.h"
|
||||||
|
#include "timer.h" // Теперь здесь виден extern uint32_t system_ticks
|
||||||
|
#include "io.h"
|
||||||
|
|
||||||
|
void sleep_ms(uint32_t ms) {
|
||||||
|
// 1 тик = 10 мс на частоте 100 Гц
|
||||||
|
uint32_t ticks_to_wait = ms / 10;
|
||||||
|
if (ticks_to_wait == 0) ticks_to_wait = 1; // Защита от слишком маленького интервала
|
||||||
|
|
||||||
|
uint32_t start_ticks = system_ticks;
|
||||||
|
|
||||||
|
// Ждем, пока разница тиков не достигнет нужного значения
|
||||||
|
while ((system_ticks - start_ticks) < ticks_to_wait) {
|
||||||
|
// Аппаратно засыпаем до следующего прерывания PIT
|
||||||
|
__asm__ __volatile__("hlt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void speaker_play(uint32_t frequency) {
|
||||||
|
if (frequency == 0) return;
|
||||||
|
|
||||||
|
uint32_t divisor = 1193182 / frequency;
|
||||||
|
|
||||||
|
// Настраиваем Канал 2 PIT (порт 0x43, режим 0xB6)
|
||||||
|
outb(0x43, 0xB6);
|
||||||
|
|
||||||
|
// Передаем делитель в порт данных Канала 2 (0x42)
|
||||||
|
outb(0x42, (uint8_t)(divisor & 0xFF));
|
||||||
|
outb(0x42, (uint8_t)((divisor >> 8) & 0xFF));
|
||||||
|
|
||||||
|
// Подключаем PIT к динамику через порт управления системными функциями
|
||||||
|
uint8_t speaker_state = inb(0x61);
|
||||||
|
if ((speaker_state & 3) != 3) {
|
||||||
|
outb(0x61, speaker_state | 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void speaker_stop(void) {
|
||||||
|
// Отключаем динамик, сбрасывая 0 и 1 биты
|
||||||
|
uint8_t speaker_state = inb(0x61) & 0xFC;
|
||||||
|
outb(0x61, speaker_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void speaker_beep(uint32_t frequency, uint32_t duration_ms) {
|
||||||
|
speaker_play(frequency);
|
||||||
|
sleep_ms(duration_ms); // Наш халяльный sleep по прерываниям
|
||||||
|
speaker_stop();
|
||||||
|
}
|
||||||
15
src/speaker.h
Normal file
15
src/speaker.h
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef SPEAKER_H
|
||||||
|
#define SPEAKER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Включить звук заданной частоты (в Герцах)
|
||||||
|
void speaker_play(uint32_t frequency);
|
||||||
|
|
||||||
|
// Выключить звук
|
||||||
|
void speaker_stop(void);
|
||||||
|
|
||||||
|
// Издать короткий писк (бип) заданной частоты и длительности
|
||||||
|
void speaker_beep(uint32_t frequency, uint32_t duration);
|
||||||
|
|
||||||
|
#endif
|
||||||
183
src/string.c
Normal file
183
src/string.c
Normal file
|
|
@ -0,0 +1,183 @@
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
// Заполнить кусок памяти определенным байтом (очень нужно для очистки буферов)
|
||||||
|
void* memset(void* dest, int val, size_t count) {
|
||||||
|
uint8_t* temp = (uint8_t*)dest;
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
temp[i] = (uint8_t)val;
|
||||||
|
}
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Скопировать кусок памяти (будем использовать для скроллинга VGA экрана)
|
||||||
|
void* memcpy(void* dest, const void* src, size_t count) {
|
||||||
|
uint8_t* d = (uint8_t*)dest;
|
||||||
|
const uint8_t* s = (const uint8_t*)src;
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
d[i] = s[i];
|
||||||
|
}
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Скопировать кусок памяти с учетом возможного пересечения областей
|
||||||
|
void* memmove(void* dest, const void* src, size_t count) {
|
||||||
|
uint8_t* d = (uint8_t*)dest;
|
||||||
|
const uint8_t* s = (const uint8_t*)src;
|
||||||
|
|
||||||
|
if (d < s) {
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
d[i] = s[i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (size_t i = count; i > 0; i--) {
|
||||||
|
d[i - 1] = s[i - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сравнение блоков памяти
|
||||||
|
int memcmp(const void* s1, const void* s2, size_t count) {
|
||||||
|
const uint8_t* p1 = (const uint8_t*)s1;
|
||||||
|
const uint8_t* p2 = (const uint8_t*)s2;
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
if (p1[i] != p2[i]) {
|
||||||
|
return p1[i] - p2[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Длина строки
|
||||||
|
size_t strlen(const char* str) {
|
||||||
|
size_t len = 0;
|
||||||
|
while (str[len] != '\0') {
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Копирование строки
|
||||||
|
char* strcpy(char* dest, const char* src) {
|
||||||
|
size_t i = 0;
|
||||||
|
while ((dest[i] = src[i]) != '\0') {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конкатенация строк
|
||||||
|
char* strcat(char* dest, const char* src) {
|
||||||
|
size_t dest_len = strlen(dest);
|
||||||
|
size_t i = 0;
|
||||||
|
while ((dest[dest_len + i] = src[i]) != '\0') {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сравнение строк (понадобится, когда будем парсить команды в терминале)
|
||||||
|
int strcmp(const char* s1, const char* s2) {
|
||||||
|
while (*s1 && (*s1 == *s2)) {
|
||||||
|
s1++;
|
||||||
|
s2++;
|
||||||
|
}
|
||||||
|
return *(const unsigned char*)s1 - *(const unsigned char*)s2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Строка в число
|
||||||
|
int atoi(const char* str) {
|
||||||
|
int res = 0;
|
||||||
|
int sign = 1;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
// Пропускаем пробелы, если они есть
|
||||||
|
while (str[i] == ' ' || str[i] == '\t') {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Учитываем знак
|
||||||
|
if (str[i] == '-') {
|
||||||
|
sign = -1;
|
||||||
|
i++;
|
||||||
|
} else if (str[i] == '+') {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Считаем само число
|
||||||
|
while (str[i] >= '0' && str[i] <= '9') {
|
||||||
|
res = res * 10 + (str[i] - '0');
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sign * res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Число в строку (поддерживает base = 10 и base = 2)
|
||||||
|
char* itoa(int value, char* str, int base) {
|
||||||
|
int i = 0;
|
||||||
|
int is_negative = 0;
|
||||||
|
|
||||||
|
// Защита от дурака
|
||||||
|
if (base < 2 || base > 36) {
|
||||||
|
str[0] = '\0';
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обрабатываем 0
|
||||||
|
if (value == 0) {
|
||||||
|
str[i++] = '0';
|
||||||
|
str[i] = '\0';
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Отрицательные числа обрабатываем только для десятичной системы
|
||||||
|
if (value < 0 && base == 10) {
|
||||||
|
is_negative = 1;
|
||||||
|
value = -value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Переводим число в строку (но она получится задом наперед)
|
||||||
|
while (value != 0) {
|
||||||
|
int rem = value % base;
|
||||||
|
str[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0';
|
||||||
|
value = value / base;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем минус
|
||||||
|
if (is_negative) {
|
||||||
|
str[i++] = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
str[i] = '\0';
|
||||||
|
|
||||||
|
// Переворачиваем строку в нормальный вид
|
||||||
|
int start = 0;
|
||||||
|
int end = i - 1;
|
||||||
|
while (start < end) {
|
||||||
|
char temp = str[start];
|
||||||
|
str[start] = str[end];
|
||||||
|
str[end] = temp;
|
||||||
|
start++;
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Число в красивый Hex вид (например, 255 -> "0x000000ff")
|
||||||
|
char* itox(uint32_t value, char* str) {
|
||||||
|
char* hex_digits = "0123456789abcdef";
|
||||||
|
|
||||||
|
str[0] = '0';
|
||||||
|
str[1] = 'x';
|
||||||
|
str[10] = '\0'; // 2 символа на "0x" + 8 символов на 32-битное число
|
||||||
|
|
||||||
|
// Заполняем с конца
|
||||||
|
for (int i = 9; i >= 2; i--) {
|
||||||
|
str[i] = hex_digits[value & 0xF];
|
||||||
|
value >>= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
28
src/string.h
Normal file
28
src/string.h
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef STRING_H
|
||||||
|
#define STRING_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Работа с памятью
|
||||||
|
void* memset(void* dest, int val, size_t count);
|
||||||
|
void* memcpy(void* dest, const void* src, size_t count);
|
||||||
|
void* memmove(void* dest, const void* src, size_t count);
|
||||||
|
int memcmp(const void* s1, const void* s2, size_t count);
|
||||||
|
|
||||||
|
// Работа со строками
|
||||||
|
size_t strlen(const char* str);
|
||||||
|
char* strcpy(char* dest, const char* src);
|
||||||
|
char* strcat(char* dest, const char* src);
|
||||||
|
int strcmp(const char* s1, const char* s2);
|
||||||
|
|
||||||
|
// Перевод строки в число
|
||||||
|
int atoi(const char* str);
|
||||||
|
|
||||||
|
// Перевод числа в строку (десятичную)
|
||||||
|
char* itoa(int value, char* str, int base);
|
||||||
|
|
||||||
|
// Специальный быстрый аналог для Hex (16-ричной системы), незаменим для дампов памяти
|
||||||
|
char* itox(uint32_t value, char* str);
|
||||||
|
|
||||||
|
#endif
|
||||||
27
src/timer.c
Normal file
27
src/timer.c
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
#include "timer.h"
|
||||||
|
#include "io.h"
|
||||||
|
#include "serial.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
// Глобальный счетчик тиков системы
|
||||||
|
uint32_t system_ticks = 0;
|
||||||
|
|
||||||
|
void timer_init(uint32_t frequency) {
|
||||||
|
log_info(LOG_NORMAL, "Initializing PIT Timer...");
|
||||||
|
|
||||||
|
// Вычисляем делитель частоты
|
||||||
|
uint32_t divisor = 1193182 / frequency;
|
||||||
|
|
||||||
|
// Шлем команду: Канал 0, Режим 3 (генератор меандра), доступ по очереди Младший/Старший байт
|
||||||
|
outb(0x43, 0x36);
|
||||||
|
|
||||||
|
// Раскалываем 16-битный делитель на два байта и шлем в порт данных Канала 0 (0x40)
|
||||||
|
outb(0x40, (uint8_t)(divisor & 0xFF));
|
||||||
|
outb(0x40, (uint8_t)((divisor >> 8) & 0xFF));
|
||||||
|
|
||||||
|
log_info(LOG_NORMAL, "Timer registered");
|
||||||
|
}
|
||||||
|
|
||||||
|
void timer_handler(void) {
|
||||||
|
system_ticks++;
|
||||||
|
}
|
||||||
12
src/timer.h
Normal file
12
src/timer.h
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef TIMER_H
|
||||||
|
#define TIMER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Делаем счетчик видимым для всего ядра
|
||||||
|
extern uint32_t system_ticks;
|
||||||
|
|
||||||
|
void timer_init(uint32_t frequency);
|
||||||
|
void timer_handler(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
75
src/vmm.c
Normal file
75
src/vmm.c
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
#include "vmm.h"
|
||||||
|
#include "pmm.h"
|
||||||
|
#include "serial.h"
|
||||||
|
|
||||||
|
// 1. Обязательное выравнивание для CR3 и таблиц страниц
|
||||||
|
uint32_t page_directory[1024] __attribute__((aligned(4096)));
|
||||||
|
uint32_t first_page_table[1024] __attribute__((aligned(4096)));
|
||||||
|
|
||||||
|
extern void vmm_enable_paging(uint32_t* page_directory);
|
||||||
|
|
||||||
|
void vmm_init(void) {
|
||||||
|
kprint_serial("VMM: Initializing paging...\n");
|
||||||
|
|
||||||
|
// 2. Очищаем каталог (ставим Not Present)
|
||||||
|
for (int i = 0; i < 1024; i++) {
|
||||||
|
page_directory[i] = 0x00000002; // Read/Write, Not Present
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Identity Map для первых 4 МБ
|
||||||
|
// i * 4096 дает физический адрес страницы
|
||||||
|
for (uint32_t i = 0; i < 1024; i++) {
|
||||||
|
first_page_table[i] = (i * 0x1000) | 0x3; // Present + RW
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Указываем каталогу на таблицу
|
||||||
|
// (uint32_t)first_page_table — это уже физический адрес,
|
||||||
|
// если ядро по 1МБ и мы не используем сложные смещения.
|
||||||
|
page_directory[0] = ((uint32_t)first_page_table) | 0x3;
|
||||||
|
|
||||||
|
kprint_serial("DEBUG: page_directory is at: 0x");
|
||||||
|
kprint_serial_hex((uint32_t)page_directory);
|
||||||
|
kprint_serial("\n");
|
||||||
|
|
||||||
|
kprint_serial("DEBUG: page_directory[0] contains: 0x");
|
||||||
|
kprint_serial_hex(page_directory[0]);
|
||||||
|
kprint_serial("\n");
|
||||||
|
|
||||||
|
kprint_serial("VMM: Paging configured. Enabling CR0...\n");
|
||||||
|
|
||||||
|
// 5. Включаем
|
||||||
|
vmm_enable_paging(page_directory);
|
||||||
|
|
||||||
|
kprint_serial("VMM: FUCK, IT WORKS!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void vmm_map_page(uint32_t phys, uint32_t virt, uint32_t flags) {
|
||||||
|
// Выковыриваем индексы из виртуального адреса
|
||||||
|
uint32_t pd_idx = virt >> 22; // Старшие 10 бит — индекс каталога
|
||||||
|
uint32_t pt_idx = (virt >> 12) & 0x3FF; // Средние 10 бит — индекс таблицы
|
||||||
|
|
||||||
|
if (!(page_directory[pd_idx] & 1)) {
|
||||||
|
// 1. Выделяем физическую страницу под НОВУЮ таблицу
|
||||||
|
uint32_t phys_table = (uint32_t)pmm_alloc_page();
|
||||||
|
|
||||||
|
// 2. Ставим её в каталог (Present + Write)
|
||||||
|
page_directory[pd_idx] = (phys_table & 0xFFFFF000) | 3;
|
||||||
|
|
||||||
|
// 3. Очищаем новую таблицу (важно!)
|
||||||
|
uint32_t* new_table = (uint32_t*)(page_directory[pd_idx] & 0xFFFFF000);
|
||||||
|
for(int i=0; i<1024; i++) new_table[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Достаем адрес таблицы страниц из каталога (обнуляем нижние 12 бит флагов)
|
||||||
|
uint32_t* table = (uint32_t*)(page_directory[pd_idx] & 0xFFFFF000);
|
||||||
|
|
||||||
|
// Запихиваем физический адрес и флаги в таблицу страниц
|
||||||
|
// PAGE_SIZE у нас 4096, поэтому phys должен быть выровнен (нижние 12 бит затрутся флагами)
|
||||||
|
table[pt_idx] = (phys & 0xFFFFF000) | flags;
|
||||||
|
|
||||||
|
// ВАЖНО: Процессор кэширует таблицы в TLB (Translation Lookaside Buffer).
|
||||||
|
// Если мы изменили маппинг существующей страницы, процессор об этом не узнает.
|
||||||
|
// Нужно сбросить кэш для этого адреса инструкцией invlpg:
|
||||||
|
__asm__ __volatile__("invlpg (%0)" : : "r"(virt) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
17
src/vmm.h
Normal file
17
src/vmm.h
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef VMM_H
|
||||||
|
#define VMM_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Элемент каталога/таблицы страниц
|
||||||
|
#define PAGE_PRESENT 0x1 // Страница есть в памяти
|
||||||
|
#define PAGE_WRITE 0x2 // Можно писать (Read/Write)
|
||||||
|
#define PAGE_USER 0x4 // Доступно из Ring 3 (User mode)
|
||||||
|
|
||||||
|
// Инициализация страничного обмена
|
||||||
|
void vmm_init(void);
|
||||||
|
|
||||||
|
// Отобразить виртуальный адрес на физический
|
||||||
|
void vmm_map_page(uint32_t phys, uint32_t virt, uint32_t flags);
|
||||||
|
|
||||||
|
#endif
|
||||||
13
src/vmm_asm.asm
Normal file
13
src/vmm_asm.asm
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
global vmm_enable_paging
|
||||||
|
vmm_enable_paging:
|
||||||
|
mov eax, [esp+4] ; Получили аргумент (phys_directory)
|
||||||
|
mov cr3, eax ; Загрузили CR3
|
||||||
|
|
||||||
|
mov eax, cr0
|
||||||
|
or eax, 0x80000000 ; Включили пейджинг
|
||||||
|
mov cr0, eax
|
||||||
|
|
||||||
|
; ВОТ ЭТО ОБЯЗАТЕЛЬНО:
|
||||||
|
jmp 0x08:.flush ; Far jump сбрасывает пайплайн CPU
|
||||||
|
.flush:
|
||||||
|
ret
|
||||||
Loading…
Reference in a new issue