This commit is contained in:
alexvoste
2026-05-07 02:22:25 +03:00
commit 1a9fd27a31
226 changed files with 29188 additions and 0 deletions
+199
View File
@@ -0,0 +1,199 @@
#ifndef ACPI_H
#define ACPI_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <limine.h>
typedef struct {
char signature[4];
uint32_t length;
uint8_t revision;
uint8_t checksum;
char oem_id[6];
char oem_table_id[8];
uint32_t oem_revision;
uint32_t creator_id;
uint32_t creator_revision;
} __attribute__((packed)) acpi_sdt_header_t;
typedef struct {
char signature[8];
uint8_t checksum;
char oem_id[6];
uint8_t revision;
uint32_t rsdt_address;
} __attribute__((packed)) acpi_rsdp_t;
typedef struct {
acpi_rsdp_t rsdp_v1;
uint32_t length;
uint64_t xsdt_address;
uint8_t extended_checksum;
uint8_t reserved[3];
} __attribute__((packed)) acpi_rsdp2_t;
typedef struct {
acpi_sdt_header_t header;
uint32_t sdt_pointers[];
} __attribute__((packed)) acpi_rsdt_t;
typedef struct {
acpi_sdt_header_t header;
uint64_t sdt_pointers[];
} __attribute__((packed)) acpi_xsdt_t;
typedef struct {
uint8_t address_space_id;
uint8_t register_bit_width;
uint8_t register_bit_offset;
uint8_t access_size;
uint64_t address;
} __attribute__((packed)) acpi_gas_t;
typedef struct {
acpi_sdt_header_t header;
uint32_t firmware_ctrl;
uint32_t dsdt;
uint8_t reserved;
uint8_t preferred_power_management_profile;
uint16_t sci_interrupt;
uint32_t smi_command_port;
uint8_t acpi_enable;
uint8_t acpi_disable;
uint8_t s4bios_req;
uint8_t pstate_control;
uint32_t pm1a_event_block;
uint32_t pm1b_event_block;
uint32_t pm1a_control_block;
uint32_t pm1b_control_block;
uint32_t pm2_control_block;
uint32_t pm_timer_block;
uint32_t gpe0_block;
uint32_t gpe1_block;
uint8_t pm1_event_length;
uint8_t pm1_control_length;
uint8_t pm2_control_length;
uint8_t pm_timer_length;
uint8_t gpe0_length;
uint8_t gpe1_length;
uint8_t gpe1_base;
uint8_t cstate_control;
uint16_t worst_c2_latency;
uint16_t worst_c3_latency;
uint16_t flush_size;
uint16_t flush_stride;
uint8_t duty_offset;
uint8_t duty_width;
uint8_t day_alarm;
uint8_t month_alarm;
uint8_t century;
uint16_t boot_architecture_flags;
uint8_t reserved2;
uint32_t flags;
acpi_gas_t reset_reg;
uint8_t reset_value;
uint16_t arm_boot_architecture_flags;
uint8_t fadt_minor_version;
uint64_t x_firmware_ctrl;
uint64_t x_dsdt;
acpi_gas_t x_pm1a_event_block;
acpi_gas_t x_pm1b_event_block;
acpi_gas_t x_pm1a_control_block;
acpi_gas_t x_pm1b_control_block;
acpi_gas_t x_pm2_control_block;
acpi_gas_t x_pm_timer_block;
acpi_gas_t x_gpe0_block;
acpi_gas_t x_gpe1_block;
} __attribute__((packed)) acpi_fadt_t;
typedef struct {
acpi_sdt_header_t header;
uint32_t local_apic_address;
uint32_t flags;
uint8_t entries[];
} __attribute__((packed)) acpi_madt_t;
#define MADT_ENTRY_LAPIC 0
#define MADT_ENTRY_IOAPIC 1
#define MADT_ENTRY_ISO 2
#define MADT_ENTRY_NMI 4
#define MADT_ENTRY_LAPIC_ADDR 5
#define MADT_ENTRY_IOAPIC_MMIO 6
typedef struct {
uint8_t type;
uint8_t length;
} __attribute__((packed)) madt_entry_header_t;
typedef struct {
madt_entry_header_t header;
uint8_t processor_id;
uint8_t apic_id;
uint32_t flags;
} __attribute__((packed)) madt_lapic_entry_t;
typedef struct {
madt_entry_header_t header;
uint8_t ioapic_id;
uint8_t reserved;
uint32_t ioapic_address;
uint32_t global_system_interrupt_base;
} __attribute__((packed)) madt_ioapic_entry_t;
typedef struct {
madt_entry_header_t header;
uint8_t bus;
uint8_t source;
uint32_t global_system_interrupt;
uint16_t flags;
} __attribute__((packed)) madt_iso_entry_t;
typedef struct {
acpi_sdt_header_t header;
uint8_t hardware_rev_id;
uint8_t comparator_count : 5;
uint8_t counter_size : 1;
uint8_t reserved : 1;
uint8_t legacy_replacement : 1;
uint16_t pci_vendor_id;
uint8_t address_space_id;
uint8_t register_bit_width;
uint8_t register_bit_offset;
uint8_t reserved2;
uint64_t address;
uint8_t hpet_number;
uint16_t minimum_tick;
uint8_t page_protection;
} __attribute__((packed)) acpi_hpet_t;
typedef struct {
acpi_sdt_header_t header;
uint64_t reserved;
} __attribute__((packed)) acpi_mcfg_t;
typedef struct {
uint64_t base_address;
uint16_t pci_segment_group;
uint8_t start_pci_bus;
uint8_t end_pci_bus;
uint32_t reserved;
} __attribute__((packed)) mcfg_entry_t;
typedef struct {
acpi_sdt_header_t header;
uint8_t definition_block[];
} __attribute__((packed)) acpi_ssdt_t;
void acpi_init(void);
bool acpi_is_available(void);
void* acpi_find_table(const char* signature, uint64_t index);
void acpi_print_tables(void);
extern volatile struct limine_rsdp_request rsdp_request;
void acpi_shutdown(void);
void acpi_reboot(void);
#endif
+113
View File
@@ -0,0 +1,113 @@
#ifndef APIC_H
#define APIC_H
#include <stdint.h>
#include <stdbool.h>
#include "../acpi/acpi.h"
#define LAPIC_ID 0x0020
#define LAPIC_VERSION 0x0030
#define LAPIC_TPR 0x0080
#define LAPIC_EOI 0x00B0
#define LAPIC_SIVR 0x00F0
#define LAPIC_ISR0 0x0100
#define LAPIC_TIMER 0x0320
#define LAPIC_THERMAL 0x0330
#define LAPIC_PERFORMANCE 0x0340
#define LAPIC_LINT0 0x0350
#define LAPIC_LINT1 0x0360
#define LAPIC_ERROR 0x0370
#define LAPIC_TIMER_ICR 0x0380
#define LAPIC_TIMER_CCR 0x0390
#define LAPIC_TIMER_DCR 0x03E0
#define LAPIC_ENABLE (1 << 8)
#define LAPIC_SPURIOUS_VECTOR 0xFF
#define LAPIC_TIMER_MASKED (1 << 16)
#define LAPIC_TIMER_PERIODIC (1 << 17)
#define LAPIC_TIMER_DIV1 0x0
#define LAPIC_TIMER_DIV2 0x1
#define LAPIC_TIMER_DIV4 0x2
#define LAPIC_TIMER_DIV8 0x3
#define LAPIC_TIMER_DIV16 0x4
#define LAPIC_TIMER_DIV32 0x5
#define LAPIC_TIMER_DIV64 0x6
#define LAPIC_TIMER_DIV128 0x7
#define IOAPIC_ID 0x00
#define IOAPIC_VERSION 0x01
#define IOAPIC_ARB 0x02
#define IOAPIC_REDIR_START 0x10
#define IOAPIC_INT_MASKED (1 << 16)
#define IOAPIC_TRIGGER_LEVEL (1 << 15)
#define IOAPIC_POLARITY_LOW (1 << 13)
#define IOAPIC_DELIVERY_FIXED 0x0
#define IOAPIC_DELIVERY_NMI 0x4
#define HPET_CAPABILITIES 0x000
#define HPET_PERIOD 0x004
#define HPET_CONFIG 0x010
#define HPET_INTERRUPT_STATUS 0x020
#define HPET_MAIN_COUNTER 0x0F0
#define HPET_TIMER0_CONFIG 0x100
#define HPET_TIMER0_COMPARATOR 0x108
#define HPET_ENABLE_CNF (1ULL << 0)
#define HPET_LEGACY_CNF (1ULL << 1)
#define HPET_TN_INT_ENABLE_CNF (1 << 2)
#define HPET_TN_INT_TYPE_CNF (1 << 3)
#define HPET_TN_PERIODIC_CNF (1 << 4)
#define HPET_TN_32BIT_CNF (1 << 8)
#define HPET_TN_FSB_ENABLE_CNF (1 << 14)
#define HPET_TN_FSB_INT_DEL_CNF (1 << 15)
void apic_init(void);
bool apic_is_available(void);
bool hpet_is_available(void);
bool hpet_init(void);
void lapic_write(uint32_t reg, uint32_t value);
uint32_t lapic_read(uint32_t reg);
void lapic_eoi(void);
void lapic_enable(void);
uint32_t lapic_get_id(void);
void lapic_timer_init(uint32_t vector, uint32_t count, bool periodic, uint8_t divisor);
void lapic_timer_stop(void);
uint32_t lapic_timer_get_current(void);
void lapic_send_ipi(uint32_t target_lapic_id, uint8_t vector);
void lapic_send_ipi_to_all_but_self(uint8_t vector);
void lapic_send_nmi_to_all_but_self(void);
void ioapic_write(uintptr_t base, uint32_t reg, uint32_t value);
uint32_t ioapic_read(uintptr_t base, uint32_t reg);
uint32_t ioapic_get_max_redirects(uintptr_t base);
void ioapic_redirect_irq(uint8_t irq, uint8_t vector, uint32_t flags);
void ioapic_mask_irq(uint8_t irq);
void ioapic_unmask_irq(uint8_t irq);
void apic_setup_irq(uint8_t irq, uint8_t vector, bool mask, uint32_t flags);
void apic_timer_calibrate(void);
uint64_t hpet_read_counter(void);
uint64_t hpet_get_frequency(void);
uint64_t hpet_elapsed_ns(void);
void hpet_sleep_ns(uint64_t nanoseconds);
void hpet_sleep_us(uint64_t microseconds);
void hpet_sleep_ms(uint64_t milliseconds);
extern uintptr_t lapic_base;
extern uintptr_t ioapic_base;
extern uintptr_t hpet_base;
extern uint32_t hpet_period;
extern uint64_t g_hpet_boot_counter;
void ipi_reschedule_all(void);
void ipi_reschedule_cpu(uint32_t lapic_id);
void ipi_reschedule_single(uint32_t target_lapic_id);
void ipi_tlb_shootdown_broadcast(const uintptr_t* addrs, size_t count);
void ipi_tlb_shootdown_single(uint32_t target_lapic_id, uintptr_t addr);
#endif
+77
View File
@@ -0,0 +1,77 @@
#ifndef ATA_H
#define ATA_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#define ATA_PRIMARY_IO 0x1F0
#define ATA_PRIMARY_CTRL 0x3F6
#define ATA_SECONDARY_IO 0x170
#define ATA_SECONDARY_CTRL 0x376
#define ATA_REG_DATA 0x00
#define ATA_REG_ERROR 0x01
#define ATA_REG_FEATURES 0x01
#define ATA_REG_SECCOUNT 0x02
#define ATA_REG_LBA_LO 0x03
#define ATA_REG_LBA_MID 0x04
#define ATA_REG_LBA_HI 0x05
#define ATA_REG_DRIVE 0x06
#define ATA_REG_STATUS 0x07
#define ATA_REG_COMMAND 0x07
#define ATA_REG_ALT_STATUS 0x00
#define ATA_REG_DEV_CTRL 0x00
#define ATA_SR_BSY 0x80
#define ATA_SR_DRDY 0x40
#define ATA_SR_DF 0x20
#define ATA_SR_DSC 0x10
#define ATA_SR_DRQ 0x08
#define ATA_SR_CORR 0x04
#define ATA_SR_IDX 0x02
#define ATA_SR_ERR 0x01
#define ATA_CMD_READ_PIO 0x20
#define ATA_CMD_READ_PIO_EXT 0x24
#define ATA_CMD_WRITE_PIO 0x30
#define ATA_CMD_WRITE_PIO_EXT 0x34
#define ATA_CMD_CACHE_FLUSH 0xE7
#define ATA_CMD_CACHE_FLUSH_EXT 0xEA
#define ATA_CMD_IDENTIFY 0xEC
#define ATA_CMD_IDENTIFY_PACKET 0xA1
#define ATA_DRIVE_MASTER 0xA0
#define ATA_DRIVE_SLAVE 0xB0
#define ATA_LBA_BIT 0x40
#define ATA_MAX_DRIVES 4
#define ATA_SECTOR_SIZE 512
typedef struct {
bool present;
bool is_atapi;
bool lba48;
uint16_t io_base;
uint16_t ctrl_base;
uint8_t drive_select;
uint8_t irq;
uint64_t sectors;
uint64_t size_bytes;
char model[41];
char serial[21];
char firmware[9];
uint16_t identify[256];
} ata_drive_t;
void ata_init(void);
ata_drive_t *ata_get_drive(int index);
int ata_get_drive_count(void);
int ata_read_sectors(ata_drive_t *drive, uint64_t lba, uint32_t count, void *buffer);
int ata_write_sectors(ata_drive_t *drive, uint64_t lba, uint32_t count, const void *buffer);
int ata_flush(ata_drive_t *drive);
#endif
+38
View File
@@ -0,0 +1,38 @@
#ifndef BLKDEV_H
#define BLKDEV_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#define BLKDEV_MAX 8
#define BLKDEV_NAME_MAX 32
#define BLKDEV_SECTOR_SIZE 512
typedef struct blkdev blkdev_t;
typedef struct blkdev_ops {
int (*read_sectors) (blkdev_t *dev, uint64_t lba, uint32_t count, void *buf);
int (*write_sectors)(blkdev_t *dev, uint64_t lba, uint32_t count, const void *buf);
int (*flush) (blkdev_t *dev);
} blkdev_ops_t;
struct blkdev {
char name[BLKDEV_NAME_MAX];
bool present;
uint64_t sector_count;
uint64_t size_bytes;
uint32_t sector_size;
const blkdev_ops_t *ops;
void *priv;
};
int blkdev_register(blkdev_t *dev);
blkdev_t *blkdev_get_by_name(const char *name);
blkdev_t *blkdev_get(int index);
int blkdev_count(void);
void blkdev_init(void);
int blkdev_read(blkdev_t *dev, uint64_t offset, void *buf, size_t len);
int blkdev_write(blkdev_t *dev, uint64_t offset, const void *buf, size_t len);
#endif
+11
View File
@@ -0,0 +1,11 @@
#ifndef DISK_H
#define DISK_H
#include "../drivers/blkdev.h"
void disk_init(void);
int disk_mount(const char *devname, const char *path);
int disk_umount(const char *path);
int disk_format(const char *devname, const char *label);
#endif
+52
View File
@@ -0,0 +1,52 @@
#ifndef PARTITION_H
#define PARTITION_H
#include <stdint.h>
#include <stdbool.h>
#include "blkdev.h"
#define MBR_SIGNATURE 0xAA55
#define MBR_SIGNATURE_OFF 510
#define MBR_PARTITION_OFF 446
#define MBR_MAX_PARTITIONS 4
#define MBR_TYPE_EMPTY 0x00
#define MBR_TYPE_FAT12 0x01
#define MBR_TYPE_FAT16_S 0x04
#define MBR_TYPE_EXTENDED 0x05
#define MBR_TYPE_FAT16 0x06
#define MBR_TYPE_FAT32_CHS 0x0B
#define MBR_TYPE_FAT32_LBA 0x0C
#define MBR_TYPE_FAT16_LBA 0x0E
#define MBR_TYPE_LINUX 0x83
#define MBR_TYPE_ESP 0xEF
typedef struct __attribute__((packed)) {
uint8_t boot_flag;
uint8_t chs_start[3];
uint8_t type;
uint8_t chs_end[3];
uint32_t lba_start;
uint32_t sector_count;
} mbr_partition_t;
typedef struct __attribute__((packed)) {
uint8_t bootstrap[440];
uint32_t disk_signature;
uint16_t reserved;
mbr_partition_t partitions[4];
uint16_t signature;
} mbr_t;
_Static_assert(sizeof(mbr_t) == 512, "mbr size must be 512");
int partition_scan(blkdev_t *disk);
int partition_write_mbr(blkdev_t *disk, const mbr_partition_t parts[4],
uint32_t disk_signature);
int partition_read_mbr(blkdev_t *disk, mbr_t *out);
blkdev_t *partition_get(const char *name);
#endif
+91
View File
@@ -0,0 +1,91 @@
#ifndef PS2_H
#define PS2_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#define PS2_DATA_PORT 0x60
#define PS2_STATUS_PORT 0x64
#define PS2_CMD_PORT 0x64
#define PS2_STATUS_OUTPUT_FULL (1 << 0)
#define PS2_STATUS_INPUT_FULL (1 << 1)
#define PS2_CMD_READ_CONFIG 0x20
#define PS2_CMD_WRITE_CONFIG 0x60
#define PS2_CMD_DISABLE_PORT2 0xA7
#define PS2_CMD_ENABLE_PORT2 0xA8
#define PS2_CMD_TEST_PORT2 0xA9
#define PS2_CMD_SELF_TEST 0xAA
#define PS2_CMD_TEST_PORT1 0xAB
#define PS2_CMD_DISABLE_PORT1 0xAD
#define PS2_CMD_ENABLE_PORT1 0xAE
#define PS2_CMD_WRITE_PORT2 0xD4
#define PS2_CFG_PORT1_IRQ (1 << 0)
#define PS2_CFG_PORT2_IRQ (1 << 1)
#define PS2_CFG_PORT1_XLAT (1 << 6)
#define PS2_KEY_RELEASE_BIT 0x80
#define SC_LSHIFT 0x2A
#define SC_RSHIFT 0x36
#define SC_CAPS 0x3A
#define SC_LCTRL 0x1D
#define SC_LALT 0x38
#define SC_ESCAPE 0x01
#define SC_F1 0x3B
#define SC_F2 0x3C
#define SC_F3 0x3D
#define SC_F4 0x3E
#define SC_F5 0x3F
#define SC_F6 0x40
#define SC_F7 0x41
#define SC_F8 0x42
#define SC_F9 0x43
#define SC_F10 0x44
#define SC_F11 0x57
#define SC_F12 0x58
#define MOUSE_BTN_LEFT (1 << 0)
#define MOUSE_BTN_RIGHT (1 << 1)
#define MOUSE_BTN_MIDDLE (1 << 2)
#define MOUSE_X_SIGN (1 << 4)
#define MOUSE_Y_SIGN (1 << 5)
#define MOUSE_X_OVERFLOW (1 << 6)
#define MOUSE_Y_OVERFLOW (1 << 7)
typedef enum {
MOUSE_SCROLL_NONE = 0,
MOUSE_SCROLL_UP,
MOUSE_SCROLL_DOWN,
} mouse_scroll_t;
typedef struct {
int32_t x, y;
bool btn_left, btn_right, btn_middle;
mouse_scroll_t scroll;
} mouse_state_t;
typedef struct {
bool shift, caps_lock, ctrl, alt;
} kb_state_t;
#define KB_BUF_SIZE 64
typedef struct {
char buf[KB_BUF_SIZE];
uint8_t head, tail;
} kb_buf_t;
bool ps2_init(void);
const kb_state_t* ps2_kb_get_state(void);
const mouse_state_t* ps2_mouse_get_state(void);
bool kb_buf_empty(void);
char kb_buf_getc(void);
bool kb_buf_try_getc(char *out);
bool kb_buf_has_ctrlc(void);
void kb_buf_consume_ctrlc(void);
#endif
+17
View File
@@ -0,0 +1,17 @@
#ifndef TIMER_H
#define TIMER_H
#include <stdint.h>
#include <stdbool.h>
bool timer_init(void);
uint64_t timer_get_ticks(void);
void timer_sleep_ms(uint64_t milliseconds);
void timer_sleep_us(uint64_t microseconds);
void timer_sleep_ns(uint64_t nanoseconds);
extern volatile uint32_t g_ctrlc_pending;
#endif
+118
View File
@@ -0,0 +1,118 @@
#ifndef ELF_H
#define ELF_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "../memory/vmm.h"
#include "../memory/paging.h"
#define ELF_MAGIC 0x464C457F
#define EI_CLASS 4
#define EI_DATA 5
#define EI_VERSION 6
#define EI_OSABI 7
#define EI_NIDENT 16
#define ELFCLASS64 2
#define ELFDATA2LSB 1
#define EV_CURRENT 1
#define ET_EXEC 2
#define ET_DYN 3
#define EM_X86_64 62
#define PT_NULL 0
#define PT_LOAD 1
#define PT_DYNAMIC 2
#define PT_INTERP 3
#define PT_NOTE 4
#define PT_PHDR 6
#define PT_TLS 7
#define PF_X (1 << 0)
#define PF_W (1 << 1)
#define PF_R (1 << 2)
#define SHT_NULL 0
#define SHT_PROGBITS 1
#define SHT_SYMTAB 2
#define SHT_STRTAB 3
#define SHT_RELA 4
#define SHT_NOBITS 8
typedef struct {
uint8_t e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint64_t e_entry;
uint64_t e_phoff;
uint64_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
} __attribute__((packed)) elf64_ehdr_t;
typedef struct {
uint32_t p_type;
uint32_t p_flags;
uint64_t p_offset;
uint64_t p_vaddr;
uint64_t p_paddr;
uint64_t p_filesz;
uint64_t p_memsz;
uint64_t p_align;
} __attribute__((packed)) elf64_phdr_t;
typedef struct {
uint32_t sh_name;
uint32_t sh_type;
uint64_t sh_flags;
uint64_t sh_addr;
uint64_t sh_offset;
uint64_t sh_size;
uint32_t sh_link;
uint32_t sh_info;
uint64_t sh_addralign;
uint64_t sh_entsize;
} __attribute__((packed)) elf64_shdr_t;
typedef enum {
ELF_OK = 0,
ELF_ERR_NULL,
ELF_ERR_TOO_SMALL,
ELF_ERR_BAD_MAGIC,
ELF_ERR_NOT_64,
ELF_ERR_NOT_LE,
ELF_ERR_BAD_VERSION,
ELF_ERR_NOT_EXEC,
ELF_ERR_WRONG_ARCH,
ELF_ERR_NO_LOAD,
ELF_ERR_MAP_FAIL,
ELF_ERR_NO_MEM,
} elf_error_t;
typedef struct {
uintptr_t entry;
vmm_pagemap_t* pagemap;
uintptr_t load_base;
uintptr_t stack_top;
size_t stack_size;
elf_error_t error;
uintptr_t load_end;
} elf_load_result_t;
elf_load_result_t elf_load(const void* data, size_t size, size_t stack_sz);
void elf_unload(elf_load_result_t* result);
const char* elf_strerror(elf_error_t err);
#endif
+9
View File
@@ -0,0 +1,9 @@
#ifndef DEVFS_H
#define DEVFS_H
#include "vfs.h"
vnode_t *devfs_create_root(void);
void devfs_register(const char *name, vnode_t *node);
#endif
+161
View File
@@ -0,0 +1,161 @@
#ifndef EXT2_H
#define EXT2_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "../drivers/blkdev.h"
#include "vfs.h"
#define EXT2_SUPER_MAGIC 0xEF53
#define EXT2_SUPER_OFFSET 1024
#define EXT2_ROOT_INO 2
#define EXT2_GOOD_OLD_REV 0
#define EXT2_DYNAMIC_REV 1
#define EXT2_VALID_FS 1
#define EXT2_ERROR_FS 2
#define EXT2_S_IFSOCK 0xC000
#define EXT2_S_IFLNK 0xA000
#define EXT2_S_IFREG 0x8000
#define EXT2_S_IFBLK 0x6000
#define EXT2_S_IFDIR 0x4000
#define EXT2_S_IFCHR 0x2000
#define EXT2_S_IFIFO 0x1000
#define EXT2_FT_UNKNOWN 0
#define EXT2_FT_REG_FILE 1
#define EXT2_FT_DIR 2
#define EXT2_FT_CHRDEV 3
#define EXT2_FT_BLKDEV 4
#define EXT2_FT_FIFO 5
#define EXT2_FT_SOCK 6
#define EXT2_FT_SYMLINK 7
#define EXT2_NDIR_BLOCKS 12
#define EXT2_IND_BLOCK 12
#define EXT2_DIND_BLOCK 13
#define EXT2_TIND_BLOCK 14
#define EXT2_N_BLOCKS 15
#define EXT2_NAME_LEN 255
typedef struct __attribute__((packed)) {
uint32_t s_inodes_count;
uint32_t s_blocks_count;
uint32_t s_r_blocks_count;
uint32_t s_free_blocks_count;
uint32_t s_free_inodes_count;
uint32_t s_first_data_block;
uint32_t s_log_block_size;
uint32_t s_log_frag_size;
uint32_t s_blocks_per_group;
uint32_t s_frags_per_group;
uint32_t s_inodes_per_group;
uint32_t s_mtime;
uint32_t s_wtime;
uint16_t s_mnt_count;
uint16_t s_max_mnt_count;
uint16_t s_magic;
uint16_t s_state;
uint16_t s_errors;
uint16_t s_minor_rev_level;
uint32_t s_lastcheck;
uint32_t s_checkinterval;
uint32_t s_creator_os;
uint32_t s_rev_level;
uint16_t s_def_resuid;
uint16_t s_def_resgid;
uint32_t s_first_ino;
uint16_t s_inode_size;
uint16_t s_block_group_nr;
uint32_t s_feature_compat;
uint32_t s_feature_incompat;
uint32_t s_feature_ro_compat;
uint8_t s_uuid[16];
char s_volume_name[16];
char s_last_mounted[64];
uint32_t s_algo_bitmap;
uint8_t s_prealloc_blocks;
uint8_t s_prealloc_dir_blocks;
uint16_t s_padding1;
uint8_t s_journal_uuid[16];
uint32_t s_journal_inum;
uint32_t s_journal_dev;
uint32_t s_last_orphan;
uint32_t s_hash_seed[4];
uint8_t s_def_hash_version;
uint8_t s_reserved_pad[3];
uint32_t s_default_mount_options;
uint32_t s_first_meta_bg;
uint8_t s_reserved[760];
} ext2_superblock_t;
_Static_assert(sizeof(ext2_superblock_t) == 1024, "ext2 superblock size");
typedef struct __attribute__((packed)) {
uint32_t bg_block_bitmap;
uint32_t bg_inode_bitmap;
uint32_t bg_inode_table;
uint16_t bg_free_blocks_count;
uint16_t bg_free_inodes_count;
uint16_t bg_used_dirs_count;
uint16_t bg_pad;
uint8_t bg_reserved[12];
} ext2_group_desc_t;
_Static_assert(sizeof(ext2_group_desc_t) == 32, "ext2 group desc size");
typedef struct __attribute__((packed)) {
uint16_t i_mode;
uint16_t i_uid;
uint32_t i_size;
uint32_t i_atime;
uint32_t i_ctime;
uint32_t i_mtime;
uint32_t i_dtime;
uint16_t i_gid;
uint16_t i_links_count;
uint32_t i_blocks;
uint32_t i_flags;
uint32_t i_osd1;
uint32_t i_block[EXT2_N_BLOCKS];
uint32_t i_generation;
uint32_t i_file_acl;
uint32_t i_dir_acl;
uint32_t i_faddr;
uint8_t i_osd2[12];
} ext2_inode_t;
_Static_assert(sizeof(ext2_inode_t) == 128, "ext2 inode size");
typedef struct __attribute__((packed)) {
uint32_t inode;
uint16_t rec_len;
uint8_t name_len;
uint8_t file_type;
char name[EXT2_NAME_LEN];
} ext2_dir_entry_t;
typedef struct {
blkdev_t *dev;
ext2_superblock_t sb;
ext2_group_desc_t *gdt;
uint32_t block_size;
uint32_t groups_count;
uint32_t inodes_per_block;
uint32_t inode_size;
uint32_t ptrs_per_block;
bool dirty;
} ext2_t;
typedef struct {
ext2_t *fs;
uint32_t ino;
} ext2_vdata_t;
int ext2_format(blkdev_t *dev, const char *label);
vnode_t *ext2_mount(blkdev_t *dev);
void ext2_unmount(ext2_t *fs);
void ext2_sync(ext2_t *fs);
#endif
+146
View File
@@ -0,0 +1,146 @@
#ifndef FAT32_H
#define FAT32_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "../drivers/blkdev.h"
#include "vfs.h"
#define FAT32_SECTOR_SIZE 512
#define FAT32_EOC 0x0FFFFFFF
#define FAT32_EOC_MIN 0x0FFFFFF8
#define FAT32_FREE_CLUSTER 0x00000000
#define FAT32_BAD_CLUSTER 0x0FFFFFF7
#define FAT32_CLUSTER_MASK 0x0FFFFFFF
#define FAT32_FSINFO_LEAD_SIG 0x41615252
#define FAT32_FSINFO_STRUCT_SIG 0x61417272
#define FAT32_FSINFO_TRAIL_SIG 0xAA550000
#define FAT_ATTR_READ_ONLY 0x01
#define FAT_ATTR_HIDDEN 0x02
#define FAT_ATTR_SYSTEM 0x04
#define FAT_ATTR_VOLUME_ID 0x08
#define FAT_ATTR_DIRECTORY 0x10
#define FAT_ATTR_ARCHIVE 0x20
#define FAT_ATTR_LONG_NAME 0x0F
typedef struct __attribute__((packed)) {
uint8_t bs_jmp[3];
uint8_t bs_oem[8];
uint16_t bpb_bytes_per_sector;
uint8_t bpb_sectors_per_cluster;
uint16_t bpb_reserved_sectors;
uint8_t bpb_num_fats;
uint16_t bpb_root_entries;
uint16_t bpb_total_sectors_16;
uint8_t bpb_media;
uint16_t bpb_fat_size_16;
uint16_t bpb_sectors_per_track;
uint16_t bpb_num_heads;
uint32_t bpb_hidden_sectors;
uint32_t bpb_total_sectors_32;
uint32_t bpb_fat_size_32;
uint16_t bpb_ext_flags;
uint16_t bpb_fs_version;
uint32_t bpb_root_cluster;
uint16_t bpb_fs_info;
uint16_t bpb_backup_boot;
uint8_t bpb_reserved[12];
uint8_t bs_drive_num;
uint8_t bs_reserved1;
uint8_t bs_boot_sig;
uint32_t bs_vol_id;
uint8_t bs_vol_label[11];
uint8_t bs_fs_type[8];
uint8_t bs_boot_code[420];
uint16_t bs_signature;
} fat32_bpb_t;
_Static_assert(sizeof(fat32_bpb_t) == 512, "fat32 bpb size");
typedef struct __attribute__((packed)) {
uint32_t lead_sig;
uint8_t reserved1[480];
uint32_t struct_sig;
uint32_t free_count;
uint32_t next_free;
uint8_t reserved2[12];
uint32_t trail_sig;
} fat32_fsinfo_t;
_Static_assert(sizeof(fat32_fsinfo_t) == 512, "fat32 fsinfo size");
typedef struct __attribute__((packed)) {
uint8_t name[11];
uint8_t attr;
uint8_t nt_reserved;
uint8_t create_time_tenth;
uint16_t create_time;
uint16_t create_date;
uint16_t access_date;
uint16_t cluster_hi;
uint16_t modify_time;
uint16_t modify_date;
uint16_t cluster_lo;
uint32_t file_size;
} fat32_dirent_t;
_Static_assert(sizeof(fat32_dirent_t) == 32, "fat32 dirent size");
typedef struct __attribute__((packed)) {
uint8_t order;
uint8_t name1[10];
uint8_t attr;
uint8_t type;
uint8_t checksum;
uint8_t name2[12];
uint16_t cluster_lo;
uint8_t name3[4];
} fat32_lfn_t;
_Static_assert(sizeof(fat32_lfn_t) == 32, "fat32 lfn size");
typedef struct {
blkdev_t *dev;
uint32_t bytes_per_sector;
uint32_t sectors_per_cluster;
uint32_t bytes_per_cluster;
uint32_t reserved_sectors;
uint32_t num_fats;
uint32_t fat_size_sectors;
uint32_t total_sectors;
uint32_t root_cluster;
uint32_t first_fat_sector;
uint32_t first_data_sector;
uint32_t total_clusters;
uint32_t fsinfo_sector;
uint32_t next_free_hint;
uint32_t free_count;
bool dirty;
bool readonly;
uint8_t *shared_buf;
} fat32_t;
typedef struct {
fat32_t *fs;
uint32_t first_cluster;
uint32_t dir_cluster;
uint32_t dir_entry_offset;
uint32_t file_size;
uint8_t attr;
uint32_t cached_cluster;
uint32_t cached_index;
bool size_dirty;
uint8_t *io_buf;
} fat32_vdata_t;
int fat32_format(blkdev_t *dev, const char *label);
vnode_t *fat32_mount(blkdev_t *dev);
void fat32_unmount(fat32_t *fs);
int fat32_sync(fat32_t *fs);
#endif
+9
View File
@@ -0,0 +1,9 @@
#ifndef INITRAMFS_H
#define INITRAMFS_H
#include <stddef.h>
#include <stdint.h>
int initramfs_mount(const void *data, size_t size);
#endif
+11
View File
@@ -0,0 +1,11 @@
#ifndef RAMFS_H
#define RAMFS_H
#include "vfs.h"
#define RAMFS_MAX_CHILDREN 64
#define RAMFS_MAX_FILE_SIZE (4 * 1024 * 1024)
vnode_t *ramfs_create_root(void);
#endif
+181
View File
@@ -0,0 +1,181 @@
#ifndef VFS_H
#define VFS_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "../syscall/errno.h"
#define VFS_MAX_PATH 512
#define VFS_MAX_NAME 256
#define VFS_MAX_MOUNTS 16
#define VFS_MAX_OPEN_FILES 512
#define O_RDONLY 0x000
#define O_WRONLY 0x001
#define O_RDWR 0x002
#define O_ACCMODE 0x003
#define O_CREAT 0x040
#define O_TRUNC 0x200
#define O_APPEND 0x400
#define O_DIRECTORY 0x10000
#define O_NONBLOCK 0x800
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
#define FD_CLOEXEC (1 << 0)
#define TASK_MAX_FDS 256
typedef enum {
VFS_NODE_FILE = 0,
VFS_NODE_DIR = 1,
VFS_NODE_CHARDEV = 2,
VFS_NODE_BLKDEV = 3,
VFS_NODE_SYMLINK = 4,
VFS_NODE_PIPE = 5,
} vnode_type_t;
typedef struct {
uint64_t st_ino;
vnode_type_t st_type;
uint32_t st_mode;
uint32_t st_uid;
uint32_t st_gid;
uint64_t st_size;
uint64_t st_blocks;
} vfs_stat_t;
typedef struct {
uint64_t d_ino;
uint8_t d_type;
char d_name[VFS_MAX_NAME];
} vfs_dirent_t;
typedef struct vnode vnode_t;
typedef struct vfs_mount vfs_mount_t;
typedef struct vnode_ops {
int64_t (*read) (vnode_t *node, void *buf, size_t len, uint64_t offset);
int64_t (*write) (vnode_t *node, const void *buf, size_t len, uint64_t offset);
int (*truncate)(vnode_t *node, uint64_t new_size);
int (*lookup) (vnode_t *dir, const char *name, vnode_t **out);
int (*readdir) (vnode_t *dir, uint64_t index, vfs_dirent_t *out);
int (*mkdir) (vnode_t *dir, const char *name, uint32_t mode);
int (*create) (vnode_t *dir, const char *name, uint32_t mode, vnode_t **out);
int (*unlink) (vnode_t *dir, const char *name);
int (*rename) (vnode_t *src_dir, const char *src_name,
vnode_t *dst_dir, const char *dst_name);
int (*stat) (vnode_t *node, vfs_stat_t *out);
void (*ref) (vnode_t *node);
void (*unref) (vnode_t *node);
int64_t (*ioctl) (vnode_t *node, uint64_t req, void *arg);
} vnode_ops_t;
struct vnode {
vnode_type_t type;
uint32_t mode;
uint32_t uid;
uint32_t gid;
uint64_t size;
uint64_t ino;
const vnode_ops_t *ops;
void *fs_data;
volatile int refcount;
vfs_mount_t *mounted;
};
struct vfs_mount {
char path[VFS_MAX_PATH];
char device[32];
char fstype[16];
vnode_t *root;
bool used;
void *fs_priv;
void (*unmount)(void *fs_priv);
void (*sync)(void *fs_priv);
};
typedef struct {
vnode_t *vnode;
uint64_t offset;
int flags;
volatile int refcount;
} vfs_file_t;
typedef struct {
vfs_file_t *file;
int fd_flags;
} fd_entry_t;
typedef struct fd_table fd_table_t;
struct fd_table {
fd_entry_t entries[TASK_MAX_FDS];
};
void vfs_init (void);
int vfs_mount (const char *path, vnode_t *fs_root);
int vfs_mount_fs(const char *path, vnode_t *fs_root,
void *fs_priv, void (*unmount_fn)(void *),
void (*sync_fn)(void *));
int vfs_umount (const char *path);
int vfs_lookup (const char *path, vnode_t **out);
int vfs_open (const char *path, int flags, uint32_t mode, vfs_file_t **out);
void vfs_close (vfs_file_t *file);
int64_t vfs_read (vfs_file_t *file, void *buf, size_t len);
int64_t vfs_write (vfs_file_t *file, const void *buf, size_t len);
int64_t vfs_seek (vfs_file_t *file, int64_t offset, int whence);
int vfs_stat (const char *path, vfs_stat_t *out);
int vfs_fstat (vfs_file_t *file, vfs_stat_t *out);
int64_t vfs_ioctl (vfs_file_t *file, uint64_t req, void *arg);
int vfs_readdir(vfs_file_t *file, vfs_dirent_t *out);
int vfs_mkdir (const char *path, uint32_t mode);
void vnode_ref (vnode_t *node);
void vnode_unref (vnode_t *node);
vfs_file_t *vfs_file_alloc(void);
void vfs_file_free (vfs_file_t *file);
fd_table_t *fd_table_create (void);
fd_table_t *fd_table_clone (const fd_table_t *src);
void fd_table_cloexec(fd_table_t *table);
void fd_table_destroy(fd_table_t *table);
int fd_alloc (fd_table_t *table, vfs_file_t *file, int min_fd);
vfs_file_t *fd_get (const fd_table_t *table, int fd);
int fd_close (fd_table_t *table, int fd);
int fd_dup2 (fd_table_t *table, int oldfd, int newfd);
int fd_set_flags(fd_table_t *table, int fd, int flags);
int fd_get_flags(const fd_table_t *table, int fd);
void vfs_sync_all(void);
int vfs_init_stdio(void *task_ptr);
typedef struct {
char path[VFS_MAX_PATH];
char device[32];
char fstype[16];
uint32_t flags;
} vfs_mount_info_t;
int vfs_set_mount_info(const char *path, const char *device, const char *fstype);
int vfs_list_mounts(vfs_mount_info_t *out, int max);
typedef struct {
uint64_t f_bsize;
uint64_t f_blocks;
uint64_t f_bfree;
uint64_t f_bavail;
uint64_t f_files;
uint64_t f_ffree;
uint32_t f_flag;
uint32_t f_namemax;
} vfs_statvfs_t;
int vfs_statvfs(const char *path, vfs_statvfs_t *out);
#endif
+78
View File
@@ -0,0 +1,78 @@
#ifndef GDT_H
#define GDT_H
#include <stdint.h>
#define GDT_CODE_SEGMENT 0x08
#define GDT_DATA_SEGMENT 0x10
#define GDT_USER_DATA_SEGMENT 0x18
#define GDT_USER_CODE_SEGMENT 0x20
#define GDT_USER_DATA_RPL3 (GDT_USER_DATA_SEGMENT | 3)
#define GDT_USER_CODE_RPL3 (GDT_USER_CODE_SEGMENT | 3)
#define GDT_STAR_SYSRET_BASE GDT_DATA_SEGMENT
#define GDT_STAR_SYSCALL_CS GDT_CODE_SEGMENT
#define TSS_SELECTOR_BASE 0x28
#define KERNEL_STACK_SIZE (4096 * 8)
#define KERNEL_STACK_PAGES (KERNEL_STACK_SIZE / 0x1000)
#define GDT_LIMIT_LOW(limit) (limit & 0xFFFF)
#define GDT_BASE_LOW(base) (base & 0xFFFF)
#define GDT_BASE_MIDDLE(base) ((base >> 16) & 0xFF)
#define GDT_FLAGS_HI_LIMIT(limit, flags) \
(((limit >> 16) & 0xF) | ((flags << 4) & 0xF0))
#define GDT_BASE_HIGH(base) ((base >> 24) & 0xFF)
#define GDT_ENTRY(base, limit, access, flags) \
{GDT_LIMIT_LOW(limit), \
GDT_BASE_LOW(base), \
GDT_BASE_MIDDLE(base), \
access, \
GDT_FLAGS_HI_LIMIT(limit, flags), \
GDT_BASE_HIGH(base)}
typedef struct {
uint32_t reserved0;
uint64_t rsp0;
uint64_t rsp1;
uint64_t rsp2;
uint64_t reserved1;
uint64_t ist[7];
uint64_t reserved2;
uint32_t iobase;
} __attribute__((packed)) tss_t;
typedef struct {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access;
uint8_t limit_high_and_flags;
uint8_t base_high;
} __attribute__((packed)) gdt_entry_t;
typedef struct {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access;
uint8_t limit_high_and_flags;
uint8_t base_high;
uint32_t base_higher;
uint32_t zero;
} __attribute__((packed)) tss_entry_t;
typedef struct {
uint16_t size;
gdt_entry_t *pointer;
} __attribute__((packed)) gdt_pointer_t;
extern gdt_pointer_t gdtr;
extern void load_tss(uint16_t sel);
void gdt_init(void);
void gdt_load(void);
#endif
+58
View File
@@ -0,0 +1,58 @@
#ifndef FB_H
#define FB_H
#include <stdint.h>
#include <stddef.h>
#include <limine.h>
#define RGB(r, g, b) (((r) << 16) | ((g) << 8) | (b))
#define COLOR_BLACK RGB( 0, 0, 0)
#define COLOR_WHITE RGB(255, 255, 255)
#define COLOR_RED RGB(255, 0, 0)
#define COLOR_GREEN RGB( 0, 255, 0)
#define COLOR_BLUE RGB( 0, 0, 255)
#define COLOR_CYAN RGB( 0, 255, 255)
#define COLOR_MAGENTA RGB(255, 0, 255)
#define COLOR_YELLOW RGB(255, 255, 0)
#define COLOR_ORANGE RGB(255, 165, 0)
#define COLOR_GRAY RGB(128, 128, 128)
#define COLOR_DARKGRAY RGB( 64, 64, 64)
#define COLOR_BROWN RGB(165, 42, 42)
struct psf_header {
uint32_t magic;
uint32_t version;
uint32_t headersize;
uint32_t flags;
uint32_t numglyph;
uint32_t bytesperglyph;
uint32_t height;
uint32_t width;
} __attribute__((packed));
extern uint8_t _binary_font_psf_start[];
extern uint8_t _binary_font_psf_end[];
static inline const uint8_t* get_font_data(void) {
return (const uint8_t*)&_binary_font_psf_start;
}
static inline size_t get_font_data_size(void) {
return _binary_font_psf_end - _binary_font_psf_start;
}
static inline const struct psf_header* get_psf_header(void) {
return (const struct psf_header*)&_binary_font_psf_start;
}
int psf_validate(void);
void fb_draw_pixel(struct limine_framebuffer *fb, uint32_t x, uint32_t y, uint32_t color);
void fb_fill_rect(struct limine_framebuffer *fb, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t color);
void fb_clear(struct limine_framebuffer *fb, uint32_t color);
void fb_draw_char(struct limine_framebuffer *fb, char c, uint32_t x, uint32_t y, uint32_t color);
void fb_draw_string(struct limine_framebuffer *fb, const char *str, uint32_t x, uint32_t y, uint32_t color);
void fb_init_backbuffer(struct limine_framebuffer *fb);
void fb_flush(struct limine_framebuffer *fb);
void fb_flush_lines(struct limine_framebuffer *fb, uint32_t y_start, uint32_t y_end);
#endif
+47
View File
@@ -0,0 +1,47 @@
#ifndef IDT_H
#define IDT_H
#include <stdint.h>
#include <stdbool.h>
#define IDT_MAX_DESCRIPTORS 256
#define IS_FLAG_SETTED(x, flag) ((x) | (flag))
#define FLAG_SET(x, flag) ((x) |= (flag))
#define FLAG_UNSET(x, flag) ((x) &= ~(flag))
typedef enum {
IDT_FLAG_GATE_TASK = 0x5,
IDT_FLAG_GATE_16BIT_INT = 0x6,
IDT_FLAG_GATE_16BIT_TRAP = 0x7,
IDT_FLAG_GATE_32BIT_INT = 0xE,
IDT_FLAG_GATE_32BIT_TRAP = 0xF,
IDT_FLAG_RING0 = (0 << 5),
IDT_FLAG_RING1 = (1 << 5),
IDT_FLAG_RING2 = (2 << 5),
IDT_FLAG_RING3 = (3 << 5),
IDT_FLAG_PRESENT = 0x80,
} IDT_FLAGS;
typedef struct {
uint16_t base_low;
uint16_t kernel_cs;
uint8_t ist;
uint8_t attributes;
uint16_t base_mid;
uint32_t base_high;
uint32_t reserved;
}__attribute__((packed)) idt_entry_t;
typedef struct {
uint16_t limit;
idt_entry_t *base;
}__attribute__((packed)) idtr_t;
extern idtr_t idtr;
void setup_interrupt_descriptor_table(uint64_t kernel_code_segment);
void idt_load(void);
#endif
+77
View File
@@ -0,0 +1,77 @@
#ifndef INTERRUPTS_H
#define INTERRUPTS_H
#include <stdint.h>
struct int_frame_t {
uint64_t ds;
uint64_t r15;
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t r11;
uint64_t r10;
uint64_t r9;
uint64_t r8;
uint64_t rbp;
uint64_t rdi;
uint64_t rsi;
uint64_t rdx;
uint64_t rcx;
uint64_t rbx;
uint64_t rax;
uint64_t interrupt;
uint64_t error;
uint64_t rip;
uint64_t cs;
uint64_t rflags;
uint64_t rsp;
uint64_t ss;
} __attribute__((packed));
typedef void (*int_handler_f)(struct int_frame_t* frame);
typedef struct {
uint64_t vector;
int_handler_f handler;
} int_desc_t;
#define __CHECK_HANDLER(fn) \
_Static_assert( \
__builtin_types_compatible_p( \
typeof(fn), void (*)(struct int_frame_t *)), \
"Invalid interrupt handler signature")
#define __CONCAT(a, b) a##b
#define __UNIQUE_NAME(base) __CONCAT(base, __COUNTER__)
#define DEFINE_ISR(_vector, _name) \
static void _name(struct int_frame_t *frame); \
static const int_desc_t __UNIQUE_NAME(__isr_desc_##_name) \
__attribute__((used, section(".isr_handlers"))) = { \
.vector = (_vector), \
.handler = _name, \
}; \
static void _name(struct int_frame_t *frame)
#define DEFINE_IRQ(_vector, _name) \
static void _name(struct int_frame_t *frame); \
static const int_desc_t __UNIQUE_NAME(__irq_desc_##_name) \
__attribute__((used, section(".irq_handlers"))) = { \
.vector = (_vector), \
.handler = _name, \
}; \
static void _name(struct int_frame_t *frame)
#define IPI_RESCHEDULE_VECTOR 0x40
#define IPI_TLB_SHOOTDOWN 0x41
void init_interrupt_system();
#endif
+31
View File
@@ -0,0 +1,31 @@
#ifndef IRQ_H
#define IRQ_H
#include <stdint.h>
#include "interrupts.h"
#define IRQ_INTERRUPTS_COUNT 224
static const char* irq_default_names[] __attribute__((unused)) = {
"IRQ0 timer",
"IRQ1 keyboard",
"IRQ2 cascade",
"IRQ3 COM2",
"IRQ4 COM1",
"IRQ5 LPT2",
"IRQ6 floppy",
"IRQ7 LPT1",
"IRQ8 RTC",
"IRQ9 ACPI",
"IRQ10 reserved",
"IRQ11 reserved",
"IRQ12 mouse",
"IRQ13 FPU",
"IRQ14 ATA1",
"IRQ15 ATA2"
};
void irq_common_handler(struct int_frame_t* regs);
void setup_defined_irq_handlers(void);
#endif
+81
View File
@@ -0,0 +1,81 @@
#ifndef ISR_H
#define ISR_H
#include "interrupts.h"
#define ISR_EXCEPTION_COUNT 32
enum {
EXCEPTION_DIVIDE_ERROR = 0,
EXCEPTION_DEBUG,
EXCEPTION_NMI,
EXCEPTION_BREAKPOINT,
EXCEPTION_OVERFLOW,
EXCEPTION_BOUND_RANGE,
EXCEPTION_INVALID_OPCODE,
EXCEPTION_DEVICE_NOT_AVAILABLE,
EXCEPTION_DOUBLE_FAULT,
EXCEPTION_COPROCESSOR_SEGMENT_OVERRUN,
EXCEPTION_INVALID_TSS,
EXCEPTION_SEGMENT_NOT_PRESENT,
EXCEPTION_STACK_SEGMENT_FAULT,
EXCEPTION_GENERAL_PROTECTION_FAULT,
EXCEPTION_PAGE_FAULT,
EXCEPTION_RESERVED15,
EXCEPTION_X87_FPU_ERROR,
EXCEPTION_ALIGNMENT_CHECK,
EXCEPTION_MACHINE_CHECK,
EXCEPTION_SIMD_FPU_EXCEPTION,
EXCEPTION_VIRTUALIZATION_EXCEPTION,
EXCEPTION_RESERVED21,
EXCEPTION_RESERVED22,
EXCEPTION_RESERVED23,
EXCEPTION_RESERVED24,
EXCEPTION_RESERVED25,
EXCEPTION_RESERVED26,
EXCEPTION_RESERVED27,
EXCEPTION_RESERVED28,
EXCEPTION_RESERVED29,
EXCEPTION_SECURITY_EXCEPTION = 30,
EXCEPTION_RESERVED31
};
static const char* exception_names[] __attribute__((unused)) = {
"Divide Error",
"Debug",
"Non-Maskable Interrupt",
"Breakpoint",
"Overflow",
"Bound Range Exceeded",
"Invalid Opcode",
"Device Not Available",
"Double Fault",
"Coprocessor Segment Overrun",
"Invalid TSS",
"Segment Not Present",
"Stack Segment Fault",
"General Protection Fault",
"Page Fault",
"Reserved",
"x87 Floating-Point Exception",
"Alignment Check",
"Machine Check",
"SIMD Floating-Point Exception",
"Virtualization Exception",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Security Exception",
"Reserved"
};
void isr_common_handler(struct int_frame_t* regs);
void setup_defined_isr_handlers(void);
#endif
+40
View File
@@ -0,0 +1,40 @@
#ifndef PORTS_H
#define PORTS_H
#include <stdint.h>
static inline uint8_t inb(uint16_t port) {
uint8_t result;
asm volatile ("inb %1, %0" : "=a"(result) : "Nd"(port));
return result;
}
static inline void outb(uint16_t port, uint8_t data) {
asm volatile ("outb %0, %1" : : "a"(data), "Nd"(port));
}
static inline uint16_t inw(uint16_t port) {
uint16_t result;
asm volatile ("inw %1, %0" : "=a"(result) : "Nd"(port));
return result;
}
static inline void outw(uint16_t port, uint16_t data) {
asm volatile ("outw %0, %1" : : "a"(data), "Nd"(port));
}
static inline uint32_t inl(uint16_t port) {
uint32_t result;
asm volatile ("inl %1, %0" : "=a"(result) : "Nd"(port));
return result;
}
static inline void outl(uint16_t port, uint32_t data) {
asm volatile ("outl %0, %1" : : "a"(data), "Nd"(port));
}
static inline void io_wait(void) {
outb(0x80, 0);
}
#endif
+51
View File
@@ -0,0 +1,51 @@
#ifndef SERIAL_H
#define SERIAL_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdarg.h>
#define COM1 0x3F8
#define COM2 0x2F8
#define COM3 0x3E8
#define COM4 0x2E8
#define SERIAL_DATA_PORT(base) (base)
#define SERIAL_FIFO_COMMAND_PORT(base) (base + 2)
#define SERIAL_LINE_COMMAND_PORT(base) (base + 3)
#define SERIAL_MODEM_COMMAND_PORT(base) (base + 4)
#define SERIAL_LINE_STATUS_PORT(base) (base + 5)
#define SERIAL_LSR_DATA_READY 0x01
#define SERIAL_LSR_OVERRUN_ERROR 0x02
#define SERIAL_LSR_PARITY_ERROR 0x04
#define SERIAL_LSR_FRAMING_ERROR 0x08
#define SERIAL_LSR_BREAK_INDICATOR 0x10
#define SERIAL_LSR_TRANSMIT_HOLDING_EMPTY 0x20
#define SERIAL_LSR_TRANSMIT_EMPTY 0x40
#define SERIAL_LSR_FIFO_ERROR 0x80
void serial_initialize(uint16_t port, uint32_t baud_rate);
int serial_received_port(uint16_t port);
char serial_read_port(uint16_t port);
int serial_is_transmit_empty_port(uint16_t port);
void serial_write_port(uint16_t port, char c);
void serial_writestring_port(uint16_t port, const char* str);
void serial_printf_port(uint16_t port, const char* format, ...);
int serial_received(void);
char serial_read(void);
int serial_is_transmit_empty(void);
void serial_write(char c);
void serial_writestring(const char* str);
void serial_writebuf(const char* buf, size_t len);
void serial_printf(const char* format, ...);
uint16_t serial_get_default_port(void);
void serial_set_default_port(uint16_t port);
void serial_force_unlock(void);
#endif
+44
View File
@@ -0,0 +1,44 @@
#ifndef PAGING_H
#define PAGING_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "vmm.h"
#include "../include/interrupts/interrupts.h"
#define PAGING_PRESENT VMM_PRESENT
#define PAGING_WRITE VMM_WRITE
#define PAGING_USER VMM_USER
#define PAGING_NOEXEC VMM_NOEXEC
#define PAGING_KERNEL (PAGING_PRESENT | PAGING_WRITE)
#define PAGING_USER_RW (PAGING_PRESENT | PAGING_WRITE | PAGING_USER)
#define PAGING_USER_RO (PAGING_PRESENT | PAGING_USER)
#define PAGING_USER_NOEXEC (PAGING_PRESENT | PAGING_USER | PAGING_NOEXEC)
#define PAGING_LARGE_PAGE_SIZE 0x200000
#define PAGING_HUGE_PAGE_SIZE 0x40000000
typedef struct {
uintptr_t virtual_start;
uintptr_t virtual_end;
uintptr_t physical_start;
uint64_t flags;
size_t page_count;
bool allocated;
} paging_region_t;
void paging_init(void);
bool paging_map_range(vmm_pagemap_t* pagemap, uintptr_t virt_start, uintptr_t phys_start, size_t page_count, uint64_t flags);
bool paging_unmap_range(vmm_pagemap_t* pagemap, uintptr_t virt_start, size_t page_count);
bool paging_change_flags(vmm_pagemap_t* pagemap, uintptr_t virt_start, size_t page_count, uint64_t new_flags);
paging_region_t* paging_create_region(vmm_pagemap_t* pagemap, uintptr_t virt_start, size_t size, uint64_t flags);
bool paging_destroy_region(vmm_pagemap_t* pagemap, paging_region_t* region);
void* paging_alloc_pages(vmm_pagemap_t* pagemap, size_t page_count, uint64_t flags, uintptr_t preferred_virt);
void paging_free_pages(vmm_pagemap_t* pagemap, void* virt_addr, size_t page_count);
bool paging_reserve_range(vmm_pagemap_t* pagemap, uintptr_t virt_start, uintptr_t virt_end);
bool paging_is_range_free(vmm_pagemap_t* pagemap, uintptr_t virt_start, uintptr_t virt_end);
void paging_print_stats(vmm_pagemap_t* pagemap);
void paging_dump_range(vmm_pagemap_t* pagemap, uintptr_t virt_start, uintptr_t virt_end);
#endif
+89
View File
@@ -0,0 +1,89 @@
#ifndef PMM_H
#define PMM_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <limine.h>
#define PAGE_SIZE 4096UL
#define PAGE_SHIFT 12
#define PMM_PAGE_ALIGN(x) (((x) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))
#define PMM_MAX_ORDER 10
#define PMM_MAX_ORDER_NR (PMM_MAX_ORDER + 1)
#define SLAB_MIN_SIZE 8
#define SLAB_MAX_SIZE 4096
#define SLAB_NUM_CACHES 10
#define PMM_FREE_MIN_PHYS 0x100000ULL
typedef struct pmm_block {
struct pmm_block *next;
struct pmm_block *prev;
int order;
} pmm_block_t;
typedef struct {
pmm_block_t head;
size_t count;
} pmm_free_list_t;
typedef struct {
uintptr_t hhdm_offset;
uintptr_t mem_start;
uintptr_t mem_end;
size_t total_pages;
size_t usable_pages;
size_t free_pages;
pmm_free_list_t orders[PMM_MAX_ORDER_NR];
} pmm_buddy_state_t;
typedef struct slab {
struct slab *next;
struct slab *prev;
void *freelist;
uint16_t obj_size;
uint16_t total;
uint16_t used;
uint16_t _pad;
} slab_t;
typedef struct {
size_t obj_size;
slab_t *partial;
slab_t *full;
size_t total_allocs;
size_t total_frees;
} slab_cache_t;
void pmm_init(struct limine_memmap_response *memmap,
struct limine_hhdm_response *hhdm);
void *pmm_alloc(size_t pages);
void *pmm_alloc_zero(size_t pages);
void *pmm_alloc_aligned(size_t pages, size_t alignment);
void pmm_free(void *addr, size_t pages);
void pmm_free_single(void *addr);
void *pmm_phys_to_virt(uintptr_t phys);
uintptr_t pmm_virt_to_phys(void *vaddr);
uint64_t pmm_get_hhdm_offset(void);
size_t pmm_get_total_pages(void);
size_t pmm_get_usable_pages(void);
size_t pmm_get_free_pages(void);
size_t pmm_get_used_pages(void);
void pmm_print_stats(void);
void slab_init(void);
void *kmalloc(size_t size);
void *kzalloc(size_t size);
void *krealloc(void *ptr, size_t new_size);
void kfree(void *ptr);
void slab_print_stats(void);
extern slab_cache_t g_caches[SLAB_NUM_CACHES];
#endif
+41
View File
@@ -0,0 +1,41 @@
#ifndef VMM_H
#define VMM_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#define VMM_PRESENT (1ULL << 0)
#define VMM_WRITE (1ULL << 1)
#define VMM_USER (1ULL << 2)
#define VMM_PWT (1ULL << 3)
#define VMM_PCD (1ULL << 4)
#define VMM_ACCESSED (1ULL << 5)
#define VMM_DIRTY (1ULL << 6)
#define VMM_PSE (1ULL << 7)
#define VMM_GLOBAL (1ULL << 8)
#define VMM_NOEXEC (1ULL << 63)
typedef uint64_t vmm_pte_t;
typedef struct {
vmm_pte_t* pml4;
} vmm_pagemap_t;
extern uintptr_t kernel_pml4_phys;
void vmm_init(void);
vmm_pagemap_t* vmm_create_pagemap(void);
void vmm_switch_pagemap(vmm_pagemap_t* map);
bool vmm_map_page(vmm_pagemap_t* map, uintptr_t virt, uintptr_t phys, uint64_t flags);
void vmm_unmap_page(vmm_pagemap_t* map, uintptr_t virt);
void vmm_unmap_page_noflush(vmm_pagemap_t* map, uintptr_t virt);
bool vmm_virt_to_phys(vmm_pagemap_t* map, uintptr_t virt, uintptr_t* phys_out);
bool vmm_get_page_flags(vmm_pagemap_t* map, uintptr_t virt, uint64_t* flags_out);
vmm_pagemap_t* vmm_get_kernel_pagemap(void);
vmm_pagemap_t* vmm_clone_pagemap(vmm_pagemap_t* src);
void vmm_free_pagemap(vmm_pagemap_t* map);
void vmm_sync_kernel_mappings(vmm_pagemap_t* map);
void vmm_test(void);
#endif
+19
View File
@@ -0,0 +1,19 @@
#ifndef PANIC_H
#define PANIC_H
#include <stdint.h>
#include "../interrupts/interrupts.h"
__attribute__((noreturn))
void kernel_panic(const char *msg);
__attribute__((noreturn))
void kernel_panic_regs(const char *msg, struct int_frame_t *regs);
#define KPANIC(msg) \
kernel_panic("[" __FILE__ ":" STRINGIFY(__LINE__) "] " msg)
#define STRINGIFY_IMPL(x) #x
#define STRINGIFY(x) STRINGIFY_IMPL(x)
#endif
+49
View File
@@ -0,0 +1,49 @@
#ifndef CAPABILITIES_H
#define CAPABILITIES_H
#include <stdint.h>
#include <stdbool.h>
#define UID_ROOT 0
#define GID_ROOT 0
#define UID_NOBODY 65534
#define GID_NOBODY 65534
#define CAP_IOPORT (1ULL << 0)
#define CAP_RAWMEM (1ULL << 1)
#define CAP_DMA (1ULL << 2)
#define CAP_IRQ (1ULL << 3)
#define CAP_KILL_ANY (1ULL << 4)
#define CAP_SET_PRIO (1ULL << 5)
#define CAP_TASK_SPAWN (1ULL << 6)
#define CAP_TASK_INFO (1ULL << 7)
#define CAP_MMAP_EXEC (1ULL << 8)
#define CAP_MMAP_PHYS (1ULL << 9)
#define CAP_FS_ROOT (1ULL << 10)
#define CAP_FS_OWNER (1ULL << 11)
#define CAP_NET_RAW (1ULL << 12)
#define CAP_NET_BIND (1ULL << 13)
#define CAP_SYSADMIN (1ULL << 14)
#define CAP_REBOOT (1ULL << 15)
#define CAP_MODULE (1ULL << 16)
#define CAP_SETUID (1ULL << 17)
#define CAP_AUDIT (1ULL << 18)
#define CAP_PTRACE (1ULL << 19)
#define CAP_DBG_SERIAL (1ULL << 20)
#define CAP_ALL (~0ULL)
#define CAP_BASIC_SET (CAP_MMAP_EXEC | CAP_TASK_SPAWN)
#define CAP_SERVICE_SET (CAP_BASIC_SET | CAP_TASK_INFO | CAP_SET_PRIO | CAP_NET_BIND)
static inline bool cap_has(uint64_t caps, uint64_t cap) {
return (caps & cap) == cap;
}
static inline uint64_t cap_drop(uint64_t caps, uint64_t cap) {
return caps & ~cap;
}
static inline uint64_t cap_initial(uint32_t uid) {
return (uid == UID_ROOT) ? CAP_ALL : CAP_BASIC_SET;
}
#endif
+183
View File
@@ -0,0 +1,183 @@
#ifndef TASK_H
#define TASK_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "capabilities.h"
#include "../memory/vmm.h"
#include "../smp/smp.h"
#ifndef __ATOMIC_RELAXED
#define __ATOMIC_RELAXED 0
#define __ATOMIC_ACQUIRE 2
#define __ATOMIC_RELEASE 3
#endif
typedef struct { volatile bool _val; } atomic_bool;
static inline void atomic_init_bool(atomic_bool *a, bool v) {
__atomic_store_n(&a->_val, v, __ATOMIC_RELAXED);
}
static inline bool atomic_load_bool_acq(const atomic_bool *a) {
return __atomic_load_n(&a->_val, __ATOMIC_ACQUIRE);
}
static inline void atomic_store_bool_rel(atomic_bool *a, bool v) {
__atomic_store_n(&a->_val, v, __ATOMIC_RELEASE);
}
static inline bool atomic_cas_bool(atomic_bool *a, bool *expected, bool desired) {
return __atomic_compare_exchange_n(
&a->_val, expected, desired,
false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
}
typedef struct fd_table fd_table_t;
typedef struct {
uint8_t data[512] __attribute__((aligned(16)));
} fpu_state_t;
typedef enum {
TASK_RUNNING = 0,
TASK_READY,
TASK_BLOCKED,
TASK_ZOMBIE,
TASK_DEAD,
} task_state_t;
typedef enum {
TASK_TYPE_KERNEL = 0,
TASK_TYPE_USER = 1,
} task_type_t;
#define MAX_PRIORITY 31
#define DEFAULT_PRIORITY 16
#define TASK_DEFAULT_TIMESLICE 10
typedef struct task {
uint64_t rsp;
uint64_t rip;
uint64_t rbp_save;
uint64_t cr3;
int priority;
bool runnable;
uint8_t is_userspace;
uint8_t _pad0[2];
uint32_t cpu_id;
char name[32];
uint32_t time_slice;
uint32_t time_slice_init;
uint8_t _pad1[4];
uint64_t total_runtime;
fpu_state_t* fpu_state;
bool fpu_used;
uint8_t _pad2[3];
uint32_t last_cpu;
uint64_t cpu_affinity;
void (*entry)(void*);
void *arg;
uintptr_t stack_base;
uint64_t user_rsp;
task_state_t state;
uint8_t _pad3[4];
struct task* next;
uint32_t pid;
uint32_t ppid;
uint32_t uid;
uint32_t gid;
uint64_t capabilities;
struct task* parent;
struct task* children;
struct task* sibling;
int exit_code;
volatile bool pending_kill;
uint8_t _pad4[3];
uintptr_t brk_start;
uintptr_t brk_current;
uintptr_t brk_max;
vmm_pagemap_t* pagemap;
uint32_t flags;
uint32_t wait_for_pid;
uint64_t wakeup_time_ns;
uint64_t user_saved_rip;
uint64_t user_saved_rbp;
uint64_t user_saved_rbx;
uint64_t user_saved_r12;
uint64_t user_saved_r13;
uint64_t user_saved_r14;
uint64_t user_saved_r15;
uint64_t user_saved_r11;
fd_table_t *fd_table;
atomic_bool on_cpu;
} task_t;
#define TASK_FLAG_TRACE (1 << 0)
#define TASK_FLAG_VFORK (1 << 1)
#define TASK_FLAG_FORK (1 << 2)
#define TASK_FLAG_STARTED (1 << 3)
#define TASK_FLAG_OWN_PAGEMAP (1 << 4)
#define TASK_FLAG_STACK_DEFERRED (1 << 5)
#define TASK_FLAG_DESTROYED (1U << 31)
_Static_assert(offsetof(task_t, rsp) == 0, "task_t: rsp");
_Static_assert(offsetof(task_t, entry) == 120, "task_t: entry — update TASK_ENTRY_OFFSET");
_Static_assert(offsetof(task_t, arg) == 128, "task_t: arg — update TASK_ARG_OFFSET");
_Static_assert(offsetof(task_t, stack_base) == 136, "task_t: stack_base");
_Static_assert(offsetof(task_t, user_rsp) == 144, "task_t: user_rsp — update TASK_USER_RSP_OFFSET");
_Static_assert(offsetof(task_t, user_saved_rip) == 272, "task_t: user_saved_rip");
_Static_assert(offsetof(task_t, user_saved_rbp) == 280, "task_t: user_saved_rbp — update TASK_USER_SAVED_RBP_OFFSET");
extern task_t* ready_queues[MAX_PRIORITY + 1];
extern task_t* current_task[MAX_CPUS];
void sched_init(void);
void sched_reschedule(void);
void sched_print_stats(void);
void task_yield(void);
task_t* task_create(const char* name, void (*entry)(void*), void* arg, int priority);
task_t* task_create_user(const char* name, uintptr_t entry, uintptr_t user_rsp, uint64_t cr3, int priority, vmm_pagemap_t* pagemap, uint32_t uid, uint32_t gid);
__attribute__((noreturn)) void task_exit(void);
void task_kill(task_t* task);
void task_destroy(task_t* task);
task_t* task_fork(task_t* parent);
task_t* task_find_by_pid(uint32_t pid);
uint32_t task_alloc_pid(void);
void task_reparent(task_t* child, task_t* new_parent);
#include "spinlock.h"
extern spinlock_t children_lock;
void task_wakeup_waiters(uint32_t pid);
void task_unblock(task_t* t);
void sched_wakeup_sleepers(uint64_t now_ns);
task_t* task_find_foreground(void);
extern volatile uint32_t g_foreground_pid;
void task_set_foreground(uint32_t pid);
extern void context_switch(task_t* old, task_t* next, task_t** current_task_slot, uint64_t new_cr3);
extern void first_task_start(task_t* task);
extern void task_trampoline(void);
extern void task_trampoline_user(void);
extern void task_trampoline_fork(void);
extern void fpu_save(fpu_state_t* state);
extern void fpu_restore(fpu_state_t* state);
#endif
+44
View File
@@ -0,0 +1,44 @@
#ifndef SPINLOCK_H
#define SPINLOCK_H
#include <stdint.h>
typedef struct {
volatile uint32_t ticket;
volatile uint32_t serving;
} spinlock_t;
#define SPINLOCK_INIT { .ticket = 0, .serving = 0 }
static inline void spinlock_acquire(spinlock_t* lock) {
uint32_t my_ticket = __atomic_fetch_add(&lock->ticket, 1, __ATOMIC_RELAXED);
while (__atomic_load_n(&lock->serving, __ATOMIC_ACQUIRE) != my_ticket)
asm volatile("pause");
}
static inline void spinlock_release(spinlock_t* lock) {
__atomic_fetch_add(&lock->serving, 1, __ATOMIC_RELEASE);
}
static inline int spinlock_try_acquire(spinlock_t* lock) {
uint32_t serving = __atomic_load_n(&lock->serving, __ATOMIC_RELAXED);
uint32_t ticket = __atomic_load_n(&lock->ticket, __ATOMIC_RELAXED);
if (serving != ticket) return 0;
uint32_t expected = serving;
return __atomic_compare_exchange_n(&lock->ticket, &expected, serving + 1,
0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
}
static inline uint64_t spinlock_acquire_irqsave(spinlock_t* lock) {
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
spinlock_acquire(lock);
return flags;
}
static inline void spinlock_release_irqrestore(spinlock_t* lock, uint64_t flags) {
spinlock_release(lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
}
#endif
+55
View File
@@ -0,0 +1,55 @@
#ifndef PERCPU_H
#define PERCPU_H
#include <stdint.h>
#include "../include/smp/smp.h"
#define PERCPU_SECTION __attribute__((section(".percpu")))
typedef struct {
uint64_t syscall_kernel_rsp;
uint64_t syscall_user_rsp;
uint32_t cpu_id;
uint32_t _pad;
void* current_task;
uint64_t some_counter;
bool need_resched;
uint8_t _pad2[7];
uint64_t user_saved_rbp;
uint64_t user_saved_rbx;
uint64_t user_saved_r12;
uint64_t user_saved_r13;
uint64_t user_saved_r14;
uint64_t user_saved_r15;
uint64_t user_saved_r11;
uint64_t user_saved_rip;
uint8_t _pad3[8];
void* deferred_free_task;
uint64_t sched_stack_top;
} __attribute__((aligned(64))) percpu_t;
_Static_assert(__builtin_offsetof(percpu_t, syscall_kernel_rsp) == 0, "percpu: kernel_rsp");
_Static_assert(__builtin_offsetof(percpu_t, syscall_user_rsp) == 8, "percpu: user_rsp");
_Static_assert(__builtin_offsetof(percpu_t, current_task) == 24, "percpu: current_task");
_Static_assert(__builtin_offsetof(percpu_t, need_resched) == 40, "percpu: need_resched");
_Static_assert(__builtin_offsetof(percpu_t, user_saved_rbp) == 48, "percpu: saved_rbp");
_Static_assert(__builtin_offsetof(percpu_t, user_saved_rbx) == 56, "percpu: saved_rbx");
_Static_assert(__builtin_offsetof(percpu_t, user_saved_r12) == 64, "percpu: saved_r12");
_Static_assert(__builtin_offsetof(percpu_t, user_saved_r13) == 72, "percpu: saved_r13");
_Static_assert(__builtin_offsetof(percpu_t, user_saved_r14) == 80, "percpu: saved_r14");
_Static_assert(__builtin_offsetof(percpu_t, user_saved_r15) == 88, "percpu: saved_r15");
_Static_assert(__builtin_offsetof(percpu_t, user_saved_r11) == 96, "percpu: saved_r11");
_Static_assert(__builtin_offsetof(percpu_t, user_saved_rip) == 104, "percpu: saved_rip");
extern percpu_t percpu;
extern percpu_t* percpu_regions[MAX_CPUS];
extern bool g_has_fsgsbase;
percpu_t* get_percpu(void);
percpu_t* get_percpu_mut(void);
void init_percpu_regions(void);
void set_percpu_base(percpu_t* base);
#define current_cpu_id() (get_percpu()->cpu_id)
#endif
+60
View File
@@ -0,0 +1,60 @@
#ifndef SMP_H
#define SMP_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <limine.h>
#define AP_STACK_SIZE 16384
#define MAX_CPUS 256
#define MAX_TLB_ADDRESSES 32
typedef struct {
volatile bool pending;
uintptr_t addresses[MAX_TLB_ADDRESSES];
size_t count;
} tlb_shootdown_t;
extern tlb_shootdown_t tlb_shootdown_queue[MAX_CPUS];
typedef enum {
CPU_UNINITIALIZED = 0,
CPU_BOOTED,
CPU_ONLINE,
CPU_OFFLINE,
CPU_FAULTED
} cpu_state_t;
typedef struct {
uint32_t lapic_id;
uint32_t processor_id;
uint32_t acpi_id;
cpu_state_t state;
bool is_bsp;
uint64_t stack_top;
uint32_t cpu_index;
uint16_t tss_selector;
} cpu_info_t;
typedef struct {
uint32_t cpu_count;
uint32_t online_count;
uint32_t bsp_lapic_id;
uint64_t lapic_base;
cpu_info_t cpus[256];
} smp_info_t;
void smp_init(struct limine_mp_response* mp_response);
void smp_boot_aps(struct limine_mp_response* mp_response);
smp_info_t* smp_get_info(void);
cpu_info_t* smp_get_current_cpu(void);
uint32_t smp_get_cpu_count(void);
uint32_t smp_get_online_count(void);
bool smp_is_bsp(void);
void smp_print_info(void);
void smp_print_info_fb(void);
void smp_wait_for_ready(void);
void ap_entry_point(struct limine_mp_info* cpu_info);
void sched_notify_ready(void);
#endif
+17
View File
@@ -0,0 +1,17 @@
#ifndef FPU_H
#define FPU_H
#include <stdint.h>
#include <stdbool.h>
void fpu_init(void);
bool fpu_detect(void);
void fpu_set_control_word(uint16_t cw);
uint16_t fpu_get_control_word(void);
void fpu_set_status_word(uint16_t sw);
uint16_t fpu_get_status_word(void);
void fpu_set_tag_word(uint16_t tw);
uint16_t fpu_get_tag_word(void);
void fpu_reset(void);
#endif
+31
View File
@@ -0,0 +1,31 @@
#ifndef SSE_H
#define SSE_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#define MXCSR_DEFAULT 0x1F80
#define MXCSR_FLUSH_TO_ZERO (1 << 15)
#define MXCSR_DENORMALS_ARE_ZERO (1 << 6)
bool sse_supported(void);
bool sse2_supported(void);
bool sse3_supported(void);
bool ssse3_supported(void);
bool sse4_1_supported(void);
bool sse4_2_supported(void);
bool avx_supported(void);
bool avx2_supported(void);
void sse_init(void);
void sse_set_mxcsr(uint32_t mxcsr);
uint32_t sse_get_mxcsr(void);
void sse_memcpy_fast(void* dest, const void* src, size_t n);
void sse_memset_fast(void* dest, int value, size_t n);
bool mmx_supported(void);
void mmx_enter(void);
void mmx_exit(void);
void print_simd_cpuid(void);
void enable_fsgsbase(void);
#endif
+66
View File
@@ -0,0 +1,66 @@
#ifndef ERRNO_H
#define ERRNO_H
#define EPERM 1
#define ENOENT 2
#define ESRCH 3
#define EINTR 4
#define EIO 5
#define ENXIO 6
#define E2BIG 7
#define ENOEXEC 8
#define EBADF 9
#define ECHILD 10
#define EAGAIN 11
#define ENOMEM 12
#define EACCES 13
#define EFAULT 14
#define EBUSY 16
#define EEXIST 17
#define EXDEV 18
#define ENODEV 19
#define ENOTDIR 20
#define EISDIR 21
#define EINVAL 22
#define ENFILE 23
#define EMFILE 24
#define ENOTTY 25
#define EFBIG 27
#define ENOSPC 28
#define ESPIPE 29
#define EROFS 30
#define EMLINK 31
#define EPIPE 32
#define ERANGE 34
#define EDEADLK 35
#define ENAMETOOLONG 36
#define ENOLCK 37
#define ENOSYS 38
#define ENOTEMPTY 39
#define ELOOP 40
#define ENOMSG 42
#define EIDRM 43
#define ENOSTR 60
#define ENODATA 61
#define ETIME 62
#define ENOSR 63
#define EREMOTE 66
#define ENOLINK 67
#define EPROTO 71
#define EMULTIHOP 72
#define EBADMSG 74
#define EOVERFLOW 75
#define EILSEQ 84
#define EUSERS 87
#define ENOTSOCK 88
#define EOPNOTSUPP 95
#define EADDRINUSE 98
#define ECONNREFUSED 111
#define ETIMEDOUT 110
#define ECERVUS_BASE 200
#define ECAPABILITY 200
#define ETASKDEAD 201
#define EBADCR3 202
#endif
+12
View File
@@ -0,0 +1,12 @@
#ifndef SYSCALL_H
#define SYSCALL_H
#include <stdint.h>
#include "syscall_nums.h"
#include "../sched/capabilities.h"
void syscall_init(void);
int64_t syscall_handler_c(uint64_t nr, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t user_rip);
#endif
+142
View File
@@ -0,0 +1,142 @@
#ifndef SYSCALL_NUMS_H
#define SYSCALL_NUMS_H
#define SYS_EXIT 0
#define SYS_EXIT_GROUP 1
#define SYS_GETPID 2
#define SYS_GETPPID 3
#define SYS_FORK 4
#define SYS_WAIT 5
#define SYS_EXECVE 14
#define SYS_YIELD 6
#define SYS_GETUID 7
#define SYS_GETGID 8
#define SYS_SETUID 9
#define SYS_SETGID 10
#define SYS_CAP_GET 11
#define SYS_CAP_DROP 12
#define SYS_TASK_INFO 13
#define SYS_READ 20
#define SYS_WRITE 21
#define SYS_OPEN 22
#define SYS_CLOSE 23
#define SYS_SEEK 24
#define SYS_STAT 25
#define SYS_FSTAT 26
#define SYS_IOCTL 27
#define SYS_DUP 28
#define SYS_DUP2 29
#define SYS_PIPE 30
#define SYS_FCNTL 31
#define SYS_READDIR 32
#define SYS_MMAP 40
#define SYS_MUNMAP 41
#define SYS_MPROTECT 42
#define SYS_BRK 43
#define SYS_CLOCK_GET 60
#define SYS_SLEEP_NS 61
#define SYS_UPTIME 62
#define SYS_MEMINFO 63
#define SYS_FUTEX_WAIT 80
#define SYS_FUTEX_WAKE 81
#define SYS_CERVUS_BASE 512
#define SYS_DBG_PRINT 512
#define SYS_DBG_DUMP 513
#define SYS_TASK_SPAWN 514
#define SYS_TASK_KILL 515
#define SYS_SHMEM_CREATE 516
#define SYS_SHMEM_MAP 517
#define SYS_SHMEM_UNMAP 518
#define SYS_IPC_SEND 519
#define SYS_IPC_RECV 520
#define SYS_IOPORT_READ 521
#define SYS_IOPORT_WRITE 522
#define SYS_SHUTDOWN 523
#define SYS_REBOOT 524
#define SYS_DISK_MOUNT 530
#define SYS_DISK_UMOUNT 531
#define SYS_DISK_FORMAT 532
#define SYS_DISK_INFO 533
#define SYS_UNLINK 534
#define SYS_RMDIR 535
#define SYS_MKDIR 536
#define SYS_RENAME 537
#define SYS_DISK_READ_RAW 540
#define SYS_DISK_WRITE_RAW 541
#define SYS_DISK_PARTITION 542
#define SYS_DISK_MKFS_FAT32 543
#define SYS_DISK_LIST_PARTS 544
#define SYS_DISK_BIOS_INSTALL 545
#define SYS_LIST_MOUNTS 546
#define SYS_STATVFS 547
#define SYSCALL_TABLE_SIZE 548
#define PROT_NONE 0x0
#define PROT_READ 0x1
#define PROT_WRITE 0x2
#define PROT_EXEC 0x4
#define MAP_PRIVATE 0x02
#define MAP_ANONYMOUS 0x20
#define MAP_FIXED 0x10
#define MAP_FAILED ((void*)-1)
#define WNOHANG 0x1
#define CLOCK_REALTIME 0
#define CLOCK_MONOTONIC 1
typedef struct {
uint32_t pid;
uint32_t ppid;
uint32_t uid;
uint32_t gid;
uint64_t capabilities;
char name[32];
uint32_t state;
uint32_t priority;
uint64_t total_runtime_ns;
} cervus_task_info_t;
typedef struct {
int64_t tv_sec;
int64_t tv_nsec;
} cervus_timespec_t;
typedef struct {
uint64_t total_bytes;
uint64_t free_bytes;
uint64_t used_bytes;
uint64_t usable_bytes;
uint64_t page_size;
} cervus_meminfo_t;
typedef struct __attribute__((packed)) {
uint8_t boot_flag;
uint8_t type;
uint32_t lba_start;
uint32_t sector_count;
} cervus_mbr_part_t;
typedef struct __attribute__((packed)) {
char disk_name[32];
char part_name[32];
uint32_t part_num;
uint8_t type;
uint8_t bootable;
uint64_t lba_start;
uint64_t sector_count;
uint64_t size_bytes;
} cervus_part_info_t;
#endif
+397
View File
@@ -0,0 +1,397 @@
#include "../../include/acpi/acpi.h"
#include "../../include/io/serial.h"
#include "../../include/io/ports.h"
#include "../../include/memory/pmm.h"
#include <string.h>
static acpi_rsdp2_t *rsdp;
static acpi_xsdt_t *xsdt;
static acpi_rsdt_t *rsdt;
static uint8_t s5_slp_typa = 0;
static uint8_t s5_slp_typb = 0;
static bool s5_parsed = false;
static inline void *phys_to_virt(uintptr_t phys) {
return (void *)(phys + pmm_get_hhdm_offset());
}
static bool checksum(void *base, size_t len) {
uint8_t sum = 0;
uint8_t *b = base;
for (size_t i = 0; i < len; i++)
sum += b[i];
return sum == 0;
}
static bool parse_s5_from_aml(const uint8_t *aml, size_t aml_len) {
if (!aml || aml_len < 40) return false;
for (size_t i = 0; i + 20 < aml_len; i++) {
if (aml[i] == '_' && aml[i+1] == 'S' &&
aml[i+2] == '5' && aml[i+3] == '_') {
size_t pos = i + 4;
for (size_t j = pos; j < pos + 4 && j < aml_len; j++) {
if (aml[j] == 0x12) {
size_t pkg = j + 1;
if (pkg >= aml_len) continue;
uint8_t lead = aml[pkg];
size_t pkg_len_bytes = 1;
if (lead & 0xC0) {
pkg_len_bytes = (lead >> 6) + 1;
}
pkg += pkg_len_bytes;
if (pkg >= aml_len) continue;
pkg++;
if (pkg >= aml_len) continue;
if (aml[pkg] == 0x0A) {
pkg++;
if (pkg >= aml_len) continue;
s5_slp_typa = aml[pkg];
pkg++;
} else if (aml[pkg] == 0x00) {
s5_slp_typa = 0;
pkg++;
} else if (aml[pkg] == 0x01) {
s5_slp_typa = 1;
pkg++;
} else {
s5_slp_typa = aml[pkg];
pkg++;
}
if (pkg >= aml_len) continue;
if (aml[pkg] == 0x0A) {
pkg++;
if (pkg >= aml_len) continue;
s5_slp_typb = aml[pkg];
} else if (aml[pkg] == 0x00) {
s5_slp_typb = 0;
} else if (aml[pkg] == 0x01) {
s5_slp_typb = 1;
} else {
s5_slp_typb = aml[pkg];
}
serial_printf("ACPI: parsed S5 from AML: SLP_TYPa=%u SLP_TYPb=%u\n",
s5_slp_typa, s5_slp_typb);
return true;
}
}
}
}
return false;
}
static void parse_s5(void) {
if (s5_parsed) return;
s5_parsed = true;
acpi_fadt_t *fadt = (acpi_fadt_t *)acpi_find_table("FACP", 0);
if (!fadt) {
serial_writestring("ACPI: no FADT, cannot parse S5\n");
return;
}
uintptr_t dsdt_phys = 0;
if (fadt->header.length >= 148 && fadt->x_dsdt)
dsdt_phys = (uintptr_t)fadt->x_dsdt;
if (!dsdt_phys)
dsdt_phys = (uintptr_t)fadt->dsdt;
if (!dsdt_phys) {
serial_writestring("ACPI: no DSDT address in FADT\n");
return;
}
acpi_sdt_header_t *dsdt = phys_to_virt(dsdt_phys);
if (memcmp(dsdt->signature, "DSDT", 4) != 0) {
serial_writestring("ACPI: invalid DSDT signature\n");
return;
}
serial_printf("ACPI: DSDT at phys=0x%llx length=%u\n",
(unsigned long long)dsdt_phys, dsdt->length);
const uint8_t *aml = (const uint8_t *)dsdt + sizeof(acpi_sdt_header_t);
size_t aml_len = dsdt->length - sizeof(acpi_sdt_header_t);
if (parse_s5_from_aml(aml, aml_len))
return;
for (uint64_t idx = 0; ; idx++) {
acpi_sdt_header_t *ssdt = acpi_find_table("SSDT", idx);
if (!ssdt) break;
const uint8_t *s_aml = (const uint8_t *)ssdt + sizeof(acpi_sdt_header_t);
size_t s_len = ssdt->length - sizeof(acpi_sdt_header_t);
if (parse_s5_from_aml(s_aml, s_len))
return;
}
serial_writestring("ACPI: S5 object not found in DSDT/SSDT, using defaults\n");
s5_slp_typa = 5;
s5_slp_typb = 0;
}
static void acpi_enable_mode(acpi_fadt_t *fadt) {
if (!fadt->smi_command_port || !fadt->acpi_enable)
return;
uint16_t pm1a_val = inw(fadt->pm1a_control_block);
if (pm1a_val & 1) {
serial_writestring("ACPI: already in ACPI mode\n");
return;
}
serial_writestring("ACPI: enabling ACPI mode...\n");
outb((uint16_t)fadt->smi_command_port, fadt->acpi_enable);
for (int i = 0; i < 3000; i++) {
pm1a_val = inw(fadt->pm1a_control_block);
if (pm1a_val & 1) {
serial_writestring("ACPI: ACPI mode enabled\n");
return;
}
for (volatile int j = 0; j < 10000; j++)
asm volatile("pause");
}
serial_writestring("ACPI: WARNING - timeout enabling ACPI mode\n");
}
void acpi_init(void) {
if (!rsdp_request.response) {
serial_writestring("ACPI: no RSDP\n");
return;
}
rsdp = (acpi_rsdp2_t *)rsdp_request.response->address;
if (!checksum(&rsdp->rsdp_v1, sizeof(acpi_rsdp_t))) {
serial_writestring("ACPI: bad RSDP checksum\n");
return;
}
if (rsdp->rsdp_v1.revision >= 2 && rsdp->xsdt_address) {
xsdt = phys_to_virt(rsdp->xsdt_address);
if (!checksum(xsdt, xsdt->header.length))
xsdt = NULL;
}
if (!xsdt && rsdp->rsdp_v1.rsdt_address) {
rsdt = phys_to_virt(rsdp->rsdp_v1.rsdt_address);
if (!checksum(rsdt, rsdt->header.length))
rsdt = NULL;
}
if (!xsdt && !rsdt)
return;
parse_s5();
}
void *acpi_find_table(const char *sig, uint64_t index) {
uint64_t count = 0;
if (xsdt) {
size_t n = (xsdt->header.length - sizeof(acpi_sdt_header_t)) / 8;
for (size_t i = 0; i < n; i++) {
acpi_sdt_header_t *h = phys_to_virt(xsdt->sdt_pointers[i]);
if (!memcmp(h->signature, sig, 4)) {
if (count++ == index)
return h;
}
}
} else if (rsdt) {
size_t n = (rsdt->header.length - sizeof(acpi_sdt_header_t)) / 4;
for (size_t i = 0; i < n; i++) {
acpi_sdt_header_t *h = phys_to_virt(rsdt->sdt_pointers[i]);
if (!memcmp(h->signature, sig, 4)) {
if (count++ == index)
return h;
}
}
}
return NULL;
}
void acpi_print_tables(void) {
serial_writestring("ACPI tables:\n");
for (uint64_t i = 0;; i++) {
acpi_sdt_header_t *h = acpi_find_table("APIC", i);
if (!h) break;
serial_writestring(" - APIC (MADT)\n");
}
for (uint64_t i = 0;; i++) {
acpi_sdt_header_t *h = acpi_find_table("HPET", i);
if (!h) break;
serial_writestring(" - HPET\n");
}
for (uint64_t i = 0;; i++) {
acpi_sdt_header_t *h = acpi_find_table("MCFG", i);
if (!h) break;
serial_writestring(" - MCFG (PCIe)\n");
}
for (uint64_t i = 0;; i++) {
acpi_sdt_header_t *h = acpi_find_table("FACP", i);
if (!h) break;
serial_writestring(" - FACP (FADT)\n");
}
}
static void acpi_write_gas(const acpi_gas_t *gas, uint64_t value) {
if (gas->address == 0) return;
switch (gas->address_space_id) {
case 0x00:
{
volatile uint8_t *mmio = phys_to_virt(gas->address);
switch (gas->register_bit_width) {
case 8: *(volatile uint8_t *)mmio = (uint8_t)value; break;
case 16: *(volatile uint16_t *)mmio = (uint16_t)value; break;
case 32: *(volatile uint32_t *)mmio = (uint32_t)value; break;
case 64: *(volatile uint64_t *)mmio = value; break;
}
break;
}
case 0x01:
{
uint16_t port = (uint16_t)gas->address;
switch (gas->register_bit_width) {
case 8: outb(port, (uint8_t)value); break;
case 16: outw(port, (uint16_t)value); break;
case 32: outl(port, (uint32_t)value); break;
}
break;
}
}
}
void acpi_shutdown(void) {
acpi_fadt_t *fadt = (acpi_fadt_t *)acpi_find_table("FACP", 0);
if (!fadt) {
serial_writestring("ACPI shutdown: FADT not found\n");
goto fallback;
}
acpi_enable_mode(fadt);
parse_s5();
serial_printf("ACPI shutdown: SLP_TYPa=%u SLP_TYPb=%u\n",
s5_slp_typa, s5_slp_typb);
serial_printf("ACPI shutdown: PM1a_CNT=0x%x PM1b_CNT=0x%x\n",
fadt->pm1a_control_block, fadt->pm1b_control_block);
asm volatile("cli");
uint16_t pm1a_value = (s5_slp_typa << 10) | (1 << 13);
outw(fadt->pm1a_control_block, pm1a_value);
if (fadt->pm1b_control_block) {
uint16_t pm1b_value = (s5_slp_typb << 10) | (1 << 13);
outw(fadt->pm1b_control_block, pm1b_value);
}
for (volatile int i = 0; i < 100000; i++)
asm volatile("pause");
if (fadt->header.length >= 244) {
if (fadt->x_pm1a_control_block.address) {
serial_writestring("ACPI shutdown: trying extended PM1a...\n");
acpi_write_gas(&fadt->x_pm1a_control_block, pm1a_value);
}
if (fadt->x_pm1b_control_block.address) {
uint16_t pm1b_value = (s5_slp_typb << 10) | (1 << 13);
acpi_write_gas(&fadt->x_pm1b_control_block, pm1b_value);
}
for (volatile int i = 0; i < 100000; i++)
asm volatile("pause");
}
fallback:
serial_writestring("ACPI shutdown: trying QEMU/Bochs port 0x604...\n");
outw(0x604, 0x2000);
for (volatile int i = 0; i < 100000; i++)
asm volatile("pause");
serial_writestring("ACPI shutdown: trying VirtualBox port 0x4004...\n");
outw(0x4004, 0x3400);
for (volatile int i = 0; i < 100000; i++)
asm volatile("pause");
serial_writestring("ACPI shutdown: trying legacy APM...\n");
outw(0xB004, 0x2000);
serial_writestring("ACPI shutdown: all methods failed, halting CPU\n");
for (;;)
asm volatile("cli; hlt");
}
void acpi_reboot(void) {
acpi_fadt_t *fadt = (acpi_fadt_t *)acpi_find_table("FACP", 0);
asm volatile("cli");
if (fadt && fadt->header.length >= 129) {
if ((fadt->flags & (1 << 10)) && fadt->reset_reg.address) {
serial_printf("ACPI reboot: using FADT reset register "
"(space=%u addr=0x%llx val=0x%x)\n",
fadt->reset_reg.address_space_id,
(unsigned long long)fadt->reset_reg.address,
fadt->reset_value);
acpi_write_gas(&fadt->reset_reg, fadt->reset_value);
for (volatile int i = 0; i < 100000; i++)
asm volatile("pause");
}
}
serial_writestring("ACPI reboot: trying 8042 keyboard controller reset...\n");
for (int i = 0; i < 10000; i++) {
if (!(inb(0x64) & 0x02))
break;
for (volatile int j = 0; j < 100; j++)
asm volatile("pause");
}
outb(0x64, 0xFE);
for (volatile int i = 0; i < 100000; i++)
asm volatile("pause");
serial_writestring("ACPI reboot: triggering triple fault...\n");
struct {
uint16_t limit;
uint64_t base;
} __attribute__((packed)) null_idt = { 0, 0 };
asm volatile(
"lidt %0\n"
"int $3\n"
:: "m"(null_idt)
);
for (;;)
asm volatile("cli; hlt");
}
+212
View File
@@ -0,0 +1,212 @@
#include "../../include/apic/apic.h"
#include "../../include/io/serial.h"
#include "../../include/io/ports.h"
#include "../../include/acpi/acpi.h"
#include "../../include/memory/pmm.h"
#include "../../include/memory/vmm.h"
#include <string.h>
#include <stddef.h>
uintptr_t lapic_base = 0;
uintptr_t ioapic_base = 0;
uintptr_t hpet_base = 0;
uint32_t hpet_period = 0;
static acpi_madt_t* madt = NULL;
static acpi_hpet_t* hpet_table = NULL;
uint64_t g_hpet_boot_counter = 0;
static inline uintptr_t phys_to_virt(uintptr_t phys) {
return phys + pmm_get_hhdm_offset();
}
static bool map_mmio_region(uintptr_t phys_base, size_t size) {
vmm_pagemap_t* kernel_pagemap = vmm_get_kernel_pagemap();
if (!kernel_pagemap) return false;
for (uintptr_t offset = 0; offset < size; offset += 0x1000) {
uintptr_t phys_addr = phys_base + offset;
uintptr_t virt_addr = phys_to_virt(phys_addr);
uintptr_t mapped_phys;
if (vmm_virt_to_phys(kernel_pagemap, virt_addr, &mapped_phys)) {
if (mapped_phys != phys_addr) return false;
continue;
}
if (!vmm_map_page(kernel_pagemap, virt_addr, phys_addr,
VMM_PRESENT | VMM_WRITE | VMM_NOEXEC)) {
return false;
}
}
return true;
}
bool hpet_init(void) {
hpet_table = (acpi_hpet_t*)acpi_find_table("HPET", 0);
if (!hpet_table) return false;
uintptr_t phys_base = hpet_table->address;
if (!map_mmio_region(phys_base, 0x1000)) return false;
hpet_base = phys_to_virt(phys_base);
volatile uint32_t* hpet_regs = (volatile uint32_t*)hpet_base;
hpet_period = hpet_regs[HPET_PERIOD / 4];
if (hpet_period == 0) return false;
uint64_t config = *(volatile uint64_t*)(hpet_base + HPET_CONFIG);
config |= HPET_ENABLE_CNF;
if (hpet_table->legacy_replacement) config |= HPET_LEGACY_CNF;
*(volatile uint64_t*)(hpet_base + HPET_CONFIG) = config;
for (volatile int _i = 0; _i < 1000; _i++) asm volatile("pause");
g_hpet_boot_counter = hpet_read_counter();
return true;
}
bool hpet_is_available(void) {
return hpet_base != 0 && hpet_period != 0;
}
uint64_t hpet_read_counter(void) {
if (!hpet_base) return 0;
if (hpet_table->counter_size) {
return *(volatile uint64_t*)(hpet_base + HPET_MAIN_COUNTER);
} else {
return *(volatile uint32_t*)(hpet_base + HPET_MAIN_COUNTER);
}
}
uint64_t hpet_get_frequency(void) {
if (!hpet_period) return 0;
return 1000000000000000ULL / hpet_period;
}
uint64_t hpet_elapsed_ns(void) {
if (!hpet_is_available()) return 0;
uint64_t delta = hpet_read_counter() - g_hpet_boot_counter;
return (delta * (uint64_t)hpet_period) / 1000000ULL;
}
void hpet_sleep_ns(uint64_t nanoseconds) {
if (!hpet_base || !hpet_period) return;
uint64_t ticks_needed = (nanoseconds * 1000000ULL) / hpet_period;
if (ticks_needed == 0) ticks_needed = 1;
uint64_t start = hpet_read_counter();
uint64_t target = start + ticks_needed;
if (target > start) {
while (hpet_read_counter() < target) asm volatile("pause");
} else {
while (hpet_read_counter() > start) asm volatile("pause");
while (hpet_read_counter() < (target - 0xFFFFFFFFFFFFFFFFULL)) asm volatile("pause");
}
}
void hpet_sleep_us(uint64_t microseconds) {
hpet_sleep_ns(microseconds * 1000ULL);
}
void hpet_sleep_ms(uint64_t milliseconds) {
hpet_sleep_ns(milliseconds * 1000000ULL);
}
static void parse_madt(void) {
madt = (acpi_madt_t*)acpi_find_table("APIC", 0);
if (!madt) return;
if (!map_mmio_region(madt->local_apic_address, 0x1000)) return;
lapic_base = phys_to_virt(madt->local_apic_address);
uint8_t* entries = madt->entries;
uint32_t length = madt->header.length;
uint8_t* end = (uint8_t*)madt + length;
while (entries < end) {
madt_entry_header_t* header = (madt_entry_header_t*)entries;
switch (header->type) {
case MADT_ENTRY_LAPIC: {
madt_lapic_entry_t* lapic_entry = (madt_lapic_entry_t*)entries;
(void)lapic_entry;
break;
}
case MADT_ENTRY_IOAPIC: {
madt_ioapic_entry_t* ioapic_entry = (madt_ioapic_entry_t*)entries;
if (!map_mmio_region(ioapic_entry->ioapic_address, 0x1000)) break;
ioapic_base = phys_to_virt(ioapic_entry->ioapic_address);
break;
}
default:
break;
}
entries += header->length;
}
}
bool apic_is_available(void) {
return madt != NULL;
}
void apic_init(void) {
parse_madt();
if (!madt) return;
if (!lapic_base) return;
hpet_init();
lapic_enable();
if (ioapic_base) {
uint32_t max_redirects = ioapic_get_max_redirects(ioapic_base);
for (uint32_t i = 0; i < max_redirects; i++) {
ioapic_mask_irq(i);
}
}
}
void apic_setup_irq(uint8_t irq, uint8_t vector, bool mask, uint32_t flags) {
if (!ioapic_base) return;
uint32_t redir_flags = IOAPIC_DELIVERY_FIXED | flags;
if (mask) redir_flags |= IOAPIC_INT_MASKED;
ioapic_redirect_irq(irq, vector, redir_flags);
}
void apic_timer_calibrate(void) {
if (!hpet_is_available()) return;
uint64_t measurement_time_ns = 10000000ULL;
uint64_t hpet_ticks_needed = (measurement_time_ns * 1000000ULL) / hpet_period;
lapic_write(LAPIC_TIMER_DCR, 0x3);
lapic_write(LAPIC_TIMER_ICR, 0xFFFFFFFF);
lapic_write(LAPIC_TIMER, LAPIC_TIMER_MASKED | 0xFF);
lapic_write(LAPIC_TIMER, 0xFF);
uint64_t hpet_start = hpet_read_counter();
uint64_t hpet_target = hpet_start + hpet_ticks_needed;
while (hpet_read_counter() < hpet_target) asm volatile("pause");
lapic_write(LAPIC_TIMER, LAPIC_TIMER_MASKED | 0xFF);
uint32_t remaining = lapic_read(LAPIC_TIMER_CCR);
if (remaining == 0 || remaining == 0xFFFFFFFF) return;
uint32_t ticks_elapsed = 0xFFFFFFFF - remaining;
uint32_t ticks_per_10ms = (uint32_t)((ticks_elapsed * 10000000ULL) / measurement_time_ns);
if (ticks_per_10ms == 0) return;
lapic_timer_init(0x20, ticks_per_10ms, true, 0x3);
}
+79
View File
@@ -0,0 +1,79 @@
#include "../../include/apic/apic.h"
#include "../../include/io/serial.h"
#include "../../include/memory/pmm.h"
#include <stddef.h>
static void ioapic_write_internal(uintptr_t base, uint32_t reg, uint32_t value) {
if (!base) {
serial_printf("IOAPIC: Attempt to write to unmapped IOAPIC (reg: 0x%x)\n", reg);
return;
}
volatile uint32_t* ioregsel = (volatile uint32_t*)base;
volatile uint32_t* iowin = (volatile uint32_t*)(base + 0x10);
*ioregsel = reg;
*iowin = value;
}
static uint32_t ioapic_read_internal(uintptr_t base, uint32_t reg) {
if (!base) {
serial_printf("IOAPIC: Attempt to read from unmapped IOAPIC (reg: 0x%x)\n", reg);
return 0;
}
volatile uint32_t* ioregsel = (volatile uint32_t*)base;
volatile uint32_t* iowin = (volatile uint32_t*)(base + 0x10);
*ioregsel = reg;
return *iowin;
}
void ioapic_write(uintptr_t base, uint32_t reg, uint32_t value) {
ioapic_write_internal(base, reg, value);
}
uint32_t ioapic_read(uintptr_t base, uint32_t reg) {
return ioapic_read_internal(base, reg);
}
uint32_t ioapic_get_max_redirects(uintptr_t base) {
uint32_t version = ioapic_read(base, IOAPIC_VERSION);
return ((version >> 16) & 0xFF) + 1;
}
void ioapic_redirect_irq(uint8_t irq, uint8_t vector, uint32_t flags) {
if (!ioapic_base) return;
uint32_t max_redirects = ioapic_get_max_redirects(ioapic_base);
if (irq >= max_redirects) {
serial_printf("IOAPIC: IRQ %u out of range (max %u)\n", irq, max_redirects);
return;
}
uint32_t low = vector | flags;
uint32_t high = 0;
uint32_t redir_reg = IOAPIC_REDIR_START + irq * 2;
ioapic_write(ioapic_base, redir_reg, low);
ioapic_write(ioapic_base, redir_reg + 1, high);
serial_printf("IOAPIC: IRQ %u redirected to vector 0x%x\n", irq, vector);
}
void ioapic_mask_irq(uint8_t irq) {
if (!ioapic_base) return;
uint32_t redir_reg = IOAPIC_REDIR_START + irq * 2;
uint32_t current = ioapic_read(ioapic_base, redir_reg);
ioapic_write(ioapic_base, redir_reg, current | IOAPIC_INT_MASKED);
}
void ioapic_unmask_irq(uint8_t irq) {
if (!ioapic_base) return;
uint32_t redir_reg = IOAPIC_REDIR_START + irq * 2;
uint32_t current = ioapic_read(ioapic_base, redir_reg);
ioapic_write(ioapic_base, redir_reg, current & ~IOAPIC_INT_MASKED);
}
+207
View File
@@ -0,0 +1,207 @@
#include "../../include/apic/apic.h"
#include "../../include/io/serial.h"
#include "../../include/io/ports.h"
#include "../../include/memory/pmm.h"
#include "../../include/smp/smp.h"
#include "../../include/interrupts/interrupts.h"
#include <stddef.h>
static inline uintptr_t phys_to_virt(uintptr_t phys) {
return phys + pmm_get_hhdm_offset();
}
void lapic_write(uint32_t reg, uint32_t value) {
if (!lapic_base) {
serial_printf("LAPIC: Attempt to write to unmapped LAPIC (reg: 0x%x)\n", reg);
return;
}
if (reg & 0x3) {
serial_printf("LAPIC: Unaligned register access: 0x%x\n", reg);
return;
}
volatile uint32_t* addr = (volatile uint32_t*)(lapic_base + reg);
*addr = value;
(void)*addr;
}
uint32_t lapic_read(uint32_t reg) {
if (!lapic_base) {
serial_printf("LAPIC: Attempt to read from unmapped LAPIC (reg: 0x%x)\n", reg);
return 0;
}
if (reg & 0x3) {
serial_printf("LAPIC: Unaligned register access: 0x%x\n", reg);
return 0;
}
volatile uint32_t* addr = (volatile uint32_t*)(lapic_base + reg);
return *addr;
}
void lapic_enable(void) {
if (!lapic_base) return;
lapic_write(LAPIC_SIVR, lapic_read(LAPIC_SIVR) | LAPIC_ENABLE | LAPIC_SPURIOUS_VECTOR);
serial_printf("LAPIC enabled, ID: 0x%x\n", lapic_get_id());
}
uint32_t lapic_get_id(void) {
return (lapic_read(LAPIC_ID) >> 24) & 0xFF;
}
void lapic_eoi(void) {
lapic_write(LAPIC_EOI, 0);
}
void lapic_timer_init(uint32_t vector, uint32_t count, bool periodic, uint8_t divisor) {
serial_printf("Initializing LAPIC timer: vector=0x%x, count=%u, periodic=%d, divisor=%u\n",
vector, count, periodic, divisor);
if (count == 0) {
serial_writestring("Warning: APIC timer count is 0, timer will not generate interrupts\n");
}
lapic_write(LAPIC_TIMER, LAPIC_TIMER_MASKED);
lapic_write(LAPIC_TIMER_DCR, divisor & 0x7);
lapic_write(LAPIC_TIMER_ICR, count);
uint32_t timer_config = vector & 0xFF;
if (periodic) {
timer_config |= LAPIC_TIMER_PERIODIC;
}
lapic_write(LAPIC_TIMER, timer_config);
uint32_t current = lapic_read(LAPIC_TIMER_CCR);
serial_printf("LAPIC timer started: current count: %u\n", current);
}
void lapic_timer_stop(void) {
lapic_write(LAPIC_TIMER, LAPIC_TIMER_MASKED);
}
uint32_t lapic_timer_get_current(void) {
return lapic_read(LAPIC_TIMER_CCR);
}
void lapic_send_ipi(uint32_t target_lapic_id, uint8_t vector)
{
uint32_t icr_low = vector | (0 << 8) | (1 << 14);
uint32_t icr_high = target_lapic_id << 24;
lapic_write(0x310, icr_high);
lapic_write(0x300, icr_low);
while (lapic_read(0x300) & (1 << 12))
asm volatile ("pause");
serial_printf("Sent IPI vector 0x%02x to LAPIC %u\n", vector, target_lapic_id);
}
void lapic_send_ipi_to_all_but_self(uint8_t vector)
{
uint32_t icr_low = vector | (0 << 8) | (1 << 14) | (3 << 18);
lapic_write(0x310, 0);
lapic_write(0x300, icr_low);
while (lapic_read(0x300) & (1 << 12))
asm volatile ("pause");
serial_printf("Broadcast IPI (all but self) vector 0x%02x sent\n", vector);
}
void lapic_send_nmi_to_all_but_self(void)
{
uint32_t icr_low = 0x02 | (0x4 << 8) | (1 << 14) | (3 << 18);
lapic_write(0x310, 0);
lapic_write(0x300, icr_low);
for (int _w = 0; _w < 10000 && (lapic_read(0x300) & (1 << 12)); _w++)
asm volatile ("pause");
serial_printf("NMI broadcast (all but self) sent\n");
}
void ipi_reschedule_all(void) {
lapic_send_ipi_to_all_but_self(IPI_RESCHEDULE_VECTOR);
}
void ipi_reschedule_cpu(uint32_t lapic_id) {
lapic_send_ipi(lapic_id, IPI_RESCHEDULE_VECTOR);
}
void ipi_reschedule_single(uint32_t target_lapic_id) {
uint32_t icr_high = target_lapic_id << 24;
uint32_t icr_low = IPI_RESCHEDULE_VECTOR | (0 << 8) | (1 << 14);
lapic_write(0x310, icr_high);
lapic_write(0x300, icr_low);
while (lapic_read(0x300) & (1 << 12))
asm volatile ("pause");
serial_printf("Reschedule IPI sent to LAPIC %u\n", target_lapic_id);
}
void ipi_tlb_shootdown_broadcast(const uintptr_t* addrs, size_t count) {
if (count > MAX_TLB_ADDRESSES) count = MAX_TLB_ADDRESSES;
uint32_t my_lapic = lapic_get_id();
smp_info_t* info = smp_get_info();
for (uint32_t i = 0; i < info->cpu_count; i++) {
uint32_t target_lapic = info->cpus[i].lapic_id;
if (target_lapic == my_lapic) continue;
tlb_shootdown_t* q = &tlb_shootdown_queue[target_lapic];
q->count = count;
for (size_t j = 0; j < count; j++)
q->addresses[j] = addrs[j];
__atomic_store_n(&q->pending, true, __ATOMIC_RELEASE);
}
asm volatile ("lock addl $0, (%%rsp)" ::: "memory", "cc");
uint32_t icr_low = IPI_TLB_SHOOTDOWN
| (0 << 8)
| (1 << 14)
| (3 << 18);
lapic_write(0x310, 0);
lapic_write(0x300, icr_low);
while (lapic_read(0x300) & (1 << 12))
asm volatile ("pause");
serial_printf("TLB shootdown broadcast sent for %zu addresses\n", count);
}
void ipi_tlb_shootdown_single(uint32_t target_lapic_id, uintptr_t addr) {
tlb_shootdown_t* q = &tlb_shootdown_queue[target_lapic_id];
q->addresses[0] = addr;
q->count = 1;
__atomic_store_n(&q->pending, true, __ATOMIC_RELEASE);
asm volatile ("lock addl $0, (%%rsp)" ::: "memory", "cc");
uint32_t icr_high = target_lapic_id << 24;
uint32_t icr_low = IPI_TLB_SHOOTDOWN | (0 << 8) | (1 << 14);
lapic_write(0x310, icr_high);
lapic_write(0x300, icr_low);
while (lapic_read(0x300) & (1 << 12))
asm volatile ("pause");
serial_printf("TLB shootdown sent to LAPIC %u for virt 0x%llx\n",
target_lapic_id, addr);
}
+371
View File
@@ -0,0 +1,371 @@
#include "../../include/drivers/ata.h"
#include "../../include/io/ports.h"
#include "../../include/io/serial.h"
#include "../../include/memory/pmm.h"
#include "../../include/sched/spinlock.h"
#include "../../include/syscall/errno.h"
#include <string.h>
static ata_drive_t g_drives[ATA_MAX_DRIVES];
static int g_drive_count = 0;
static spinlock_t g_ata_lock = SPINLOCK_INIT;
static void ata_io_wait(uint16_t ctrl) {
inb(ctrl); inb(ctrl); inb(ctrl); inb(ctrl);
}
static inline void ata_cpu_relax(void) {
asm volatile("pause" ::: "memory");
}
static void ata_soft_reset(uint16_t ctrl) {
outb(ctrl, 0x04);
ata_io_wait(ctrl);
outb(ctrl, 0x00);
ata_io_wait(ctrl);
for (int i = 0; i < 2000000; i++) {
uint8_t s = inb(ctrl);
if (!(s & ATA_SR_BSY)) return;
ata_io_wait(ctrl);
ata_cpu_relax();
}
}
static int ata_wait_ready(uint16_t io, uint16_t ctrl, int timeout_us) {
(void)io;
ata_io_wait(ctrl);
for (int i = 0; i < timeout_us; i++) {
uint8_t s = inb(ctrl + ATA_REG_ALT_STATUS);
if (!(s & ATA_SR_BSY)) {
if (s & ATA_SR_ERR) return -EIO;
if (s & ATA_SR_DF) return -EIO;
return 0;
}
ata_io_wait(ctrl);
ata_cpu_relax();
}
return -ETIMEDOUT;
}
static int ata_wait_drq(uint16_t io, uint16_t ctrl, int timeout_us) {
(void)io;
ata_io_wait(ctrl);
for (int i = 0; i < timeout_us; i++) {
uint8_t s = inb(ctrl + ATA_REG_ALT_STATUS);
if (s & ATA_SR_ERR) return -EIO;
if (s & ATA_SR_DF) return -EIO;
if (!(s & ATA_SR_BSY) && (s & ATA_SR_DRQ)) return 0;
ata_io_wait(ctrl);
ata_cpu_relax();
}
return -ETIMEDOUT;
}
static void ata_fix_string(char *dst, const uint16_t *src, int words) {
for (int i = 0; i < words; i++) {
dst[i * 2 + 0] = (char)(src[i] >> 8);
dst[i * 2 + 1] = (char)(src[i] & 0xFF);
}
dst[words * 2] = '\0';
for (int i = words * 2 - 1; i >= 0 && dst[i] == ' '; i--)
dst[i] = '\0';
}
static bool ata_identify_drive(uint16_t io, uint16_t ctrl, uint8_t drv_sel, ata_drive_t *out) {
memset(out, 0, sizeof(*out));
out->io_base = io;
out->ctrl_base = ctrl;
out->drive_select = drv_sel;
outb(io + ATA_REG_DRIVE, drv_sel);
ata_io_wait(ctrl);
outb(io + ATA_REG_SECCOUNT, 0);
outb(io + ATA_REG_LBA_LO, 0);
outb(io + ATA_REG_LBA_MID, 0);
outb(io + ATA_REG_LBA_HI, 0);
outb(io + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
ata_io_wait(ctrl);
uint8_t status = inb(io + ATA_REG_STATUS);
if (status == 0) return false;
for (int i = 0; i < 1000000; i++) {
status = inb(io + ATA_REG_STATUS);
if (!(status & ATA_SR_BSY)) break;
ata_cpu_relax();
}
if (status & ATA_SR_BSY) return false;
uint8_t lm = inb(io + ATA_REG_LBA_MID);
uint8_t lh = inb(io + ATA_REG_LBA_HI);
if (lm == 0x14 && lh == 0xEB) {
out->is_atapi = true;
return false;
}
if (lm == 0x3C && lh == 0xC3) {
}
if (lm != 0 || lh != 0) {
return false;
}
for (int i = 0; i < 1000000; i++) {
status = inb(io + ATA_REG_STATUS);
if (status & ATA_SR_ERR) return false;
if (status & ATA_SR_DRQ) break;
ata_cpu_relax();
}
if (!(status & ATA_SR_DRQ)) return false;
for (int i = 0; i < 256; i++)
out->identify[i] = inw(io + ATA_REG_DATA);
ata_fix_string(out->model, &out->identify[27], 20);
ata_fix_string(out->serial, &out->identify[10], 10);
ata_fix_string(out->firmware, &out->identify[23], 4);
if (out->identify[83] & (1 << 10)) {
out->lba48 = true;
out->sectors = (uint64_t)out->identify[100]
| ((uint64_t)out->identify[101] << 16)
| ((uint64_t)out->identify[102] << 32)
| ((uint64_t)out->identify[103] << 48);
} else {
out->lba48 = false;
out->sectors = (uint64_t)out->identify[60]
| ((uint64_t)out->identify[61] << 16);
}
out->size_bytes = out->sectors * ATA_SECTOR_SIZE;
out->present = true;
return true;
}
void ata_init(void) {
serial_writestring("[ATA] probing drives...\n");
g_drive_count = 0;
struct { uint16_t io; uint16_t ctrl; uint8_t irq; } channels[] = {
{ ATA_PRIMARY_IO, ATA_PRIMARY_CTRL, 14 },
{ ATA_SECONDARY_IO, ATA_SECONDARY_CTRL, 15 },
};
for (int ch = 0; ch < 2; ch++) {
outb(channels[ch].ctrl, 0x02);
ata_soft_reset(channels[ch].ctrl);
uint8_t drvs[] = { ATA_DRIVE_MASTER, ATA_DRIVE_SLAVE };
for (int d = 0; d < 2; d++) {
int idx = ch * 2 + d;
ata_drive_t *drv = &g_drives[idx];
if (ata_identify_drive(channels[ch].io, channels[ch].ctrl,
drvs[d], drv))
{
drv->irq = channels[ch].irq;
g_drive_count++;
serial_printf("[ATA] drive %d: '%s' %llu sectors (%llu MB) %s\n",
idx, drv->model, drv->sectors,
drv->size_bytes / (1024 * 1024),
drv->lba48 ? "LBA48" : "LBA28");
}
}
}
if (g_drive_count == 0)
serial_writestring("[ATA] no drives found\n");
else
serial_printf("[ATA] %d drive(s) detected\n", g_drive_count);
}
ata_drive_t *ata_get_drive(int index) {
if (index < 0 || index >= ATA_MAX_DRIVES) return NULL;
if (!g_drives[index].present) return NULL;
return &g_drives[index];
}
int ata_get_drive_count(void) {
return g_drive_count;
}
#define ATA_IO_TIMEOUT 20000000
#define ATA_WRITE_TIMEOUT 40000000
#define ATA_RETRY_COUNT 3
static int ata_read_sectors_once(ata_drive_t *drive, uint64_t lba,
uint32_t count, void *buffer)
{
uint16_t io = drive->io_base;
uint16_t ctrl = drive->ctrl_base;
uint16_t *buf = (uint16_t *)buffer;
int ret = 0;
outb(io + ATA_REG_DRIVE, drive->drive_select);
ata_io_wait(ctrl);
(void)inb(io + ATA_REG_STATUS);
ret = ata_wait_ready(io, ctrl, ATA_IO_TIMEOUT);
if (ret < 0) return ret;
if (drive->lba48 && (lba > 0x0FFFFFFF || count > 256)) {
outb(io + ATA_REG_DRIVE, (drive->drive_select & 0xF0) | ATA_LBA_BIT);
ata_io_wait(ctrl);
outb(io + ATA_REG_SECCOUNT, (uint8_t)(count >> 8));
outb(io + ATA_REG_LBA_LO, (uint8_t)(lba >> 24));
outb(io + ATA_REG_LBA_MID, (uint8_t)(lba >> 32));
outb(io + ATA_REG_LBA_HI, (uint8_t)(lba >> 40));
outb(io + ATA_REG_SECCOUNT, (uint8_t)(count));
outb(io + ATA_REG_LBA_LO, (uint8_t)(lba));
outb(io + ATA_REG_LBA_MID, (uint8_t)(lba >> 8));
outb(io + ATA_REG_LBA_HI, (uint8_t)(lba >> 16));
outb(io + ATA_REG_COMMAND, ATA_CMD_READ_PIO_EXT);
} else {
outb(io + ATA_REG_DRIVE,
(drive->drive_select & 0xF0) | ATA_LBA_BIT | ((lba >> 24) & 0x0F));
ata_io_wait(ctrl);
outb(io + ATA_REG_SECCOUNT, (uint8_t)count);
outb(io + ATA_REG_LBA_LO, (uint8_t)(lba));
outb(io + ATA_REG_LBA_MID, (uint8_t)(lba >> 8));
outb(io + ATA_REG_LBA_HI, (uint8_t)(lba >> 16));
outb(io + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
}
ata_io_wait(ctrl);
for (uint32_t s = 0; s < count; s++) {
ret = ata_wait_drq(io, ctrl, ATA_IO_TIMEOUT);
if (ret < 0) return ret;
for (int i = 0; i < 256; i++)
*buf++ = inw(io + ATA_REG_DATA);
ata_io_wait(ctrl);
}
return 0;
}
int ata_read_sectors(ata_drive_t *drive, uint64_t lba,
uint32_t count, void *buffer)
{
if (!drive || !drive->present || !buffer) return -EINVAL;
if (count == 0) return 0;
if (lba + count > drive->sectors) return -EINVAL;
uint64_t flags = spinlock_acquire_irqsave(&g_ata_lock);
int ret = -EIO;
for (int attempt = 0; attempt < ATA_RETRY_COUNT; attempt++) {
ret = ata_read_sectors_once(drive, lba, count, buffer);
if (ret == 0) break;
serial_printf("[ATA] read lba=%llu count=%u attempt %d failed: %d\n",
lba, count, attempt + 1, ret);
ata_soft_reset(drive->ctrl_base);
for (volatile int k = 0; k < 100000; k++) ata_cpu_relax();
}
spinlock_release_irqrestore(&g_ata_lock, flags);
return ret;
}
static int ata_write_sectors_once(ata_drive_t *drive, uint64_t lba,
uint32_t count, const void *buffer)
{
uint16_t io = drive->io_base;
uint16_t ctrl = drive->ctrl_base;
const uint16_t *buf = (const uint16_t *)buffer;
int ret = 0;
outb(io + ATA_REG_DRIVE, drive->drive_select);
ata_io_wait(ctrl);
(void)inb(io + ATA_REG_STATUS);
ret = ata_wait_ready(io, ctrl, ATA_IO_TIMEOUT);
if (ret < 0) return ret;
if (drive->lba48 && (lba > 0x0FFFFFFF || count > 256)) {
outb(io + ATA_REG_DRIVE, (drive->drive_select & 0xF0) | ATA_LBA_BIT);
ata_io_wait(ctrl);
outb(io + ATA_REG_SECCOUNT, (uint8_t)(count >> 8));
outb(io + ATA_REG_LBA_LO, (uint8_t)(lba >> 24));
outb(io + ATA_REG_LBA_MID, (uint8_t)(lba >> 32));
outb(io + ATA_REG_LBA_HI, (uint8_t)(lba >> 40));
outb(io + ATA_REG_SECCOUNT, (uint8_t)(count));
outb(io + ATA_REG_LBA_LO, (uint8_t)(lba));
outb(io + ATA_REG_LBA_MID, (uint8_t)(lba >> 8));
outb(io + ATA_REG_LBA_HI, (uint8_t)(lba >> 16));
outb(io + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO_EXT);
} else {
outb(io + ATA_REG_DRIVE,
(drive->drive_select & 0xF0) | ATA_LBA_BIT | ((lba >> 24) & 0x0F));
ata_io_wait(ctrl);
outb(io + ATA_REG_SECCOUNT, (uint8_t)count);
outb(io + ATA_REG_LBA_LO, (uint8_t)(lba));
outb(io + ATA_REG_LBA_MID, (uint8_t)(lba >> 8));
outb(io + ATA_REG_LBA_HI, (uint8_t)(lba >> 16));
outb(io + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
}
ata_io_wait(ctrl);
for (uint32_t s = 0; s < count; s++) {
ret = ata_wait_drq(io, ctrl, ATA_WRITE_TIMEOUT);
if (ret < 0) return ret;
for (int i = 0; i < 256; i++)
outw(io + ATA_REG_DATA, *buf++);
ata_io_wait(ctrl);
}
ret = ata_wait_ready(io, ctrl, ATA_IO_TIMEOUT);
if (ret < 0) return ret;
return 0;
}
int ata_write_sectors(ata_drive_t *drive, uint64_t lba,
uint32_t count, const void *buffer)
{
if (!drive || !drive->present || !buffer) return -EINVAL;
if (count == 0) return 0;
if (lba + count > drive->sectors) return -EINVAL;
uint64_t flags = spinlock_acquire_irqsave(&g_ata_lock);
int ret = -EIO;
for (int attempt = 0; attempt < ATA_RETRY_COUNT; attempt++) {
ret = ata_write_sectors_once(drive, lba, count, buffer);
if (ret == 0) break;
serial_printf("[ATA] write lba=%llu count=%u attempt %d failed: %d\n",
lba, count, attempt + 1, ret);
ata_soft_reset(drive->ctrl_base);
for (volatile int k = 0; k < 100000; k++) ata_cpu_relax();
}
spinlock_release_irqrestore(&g_ata_lock, flags);
return ret;
}
int ata_flush(ata_drive_t *drive) {
if (!drive || !drive->present) return -EINVAL;
uint64_t flags = spinlock_acquire_irqsave(&g_ata_lock);
uint16_t io = drive->io_base;
uint16_t ctrl = drive->ctrl_base;
outb(io + ATA_REG_DRIVE, drive->drive_select);
outb(io + ATA_REG_COMMAND,
drive->lba48 ? ATA_CMD_CACHE_FLUSH_EXT : ATA_CMD_CACHE_FLUSH);
int ret = ata_wait_ready(io, ctrl, ATA_IO_TIMEOUT);
spinlock_release_irqrestore(&g_ata_lock, flags);
return ret;
}
+95
View File
@@ -0,0 +1,95 @@
#include "../../include/drivers/blkdev.h"
#include "../../include/io/serial.h"
#include "../../include/memory/pmm.h"
#include "../../include/syscall/errno.h"
#include <string.h>
static blkdev_t *g_blkdevs[BLKDEV_MAX];
static int g_blkdev_count = 0;
void blkdev_init(void) {
memset(g_blkdevs, 0, sizeof(g_blkdevs));
g_blkdev_count = 0;
serial_writestring("[blkdev] initialized\n");
}
int blkdev_register(blkdev_t *dev) {
if (!dev || g_blkdev_count >= BLKDEV_MAX) return -ENOMEM;
int idx = g_blkdev_count;
g_blkdevs[idx] = dev;
g_blkdev_count++;
serial_printf("[blkdev] registered '%s' (%llu sectors, %llu MB)\n",
dev->name, dev->sector_count,
dev->size_bytes / (1024 * 1024));
return idx;
}
blkdev_t *blkdev_get_by_name(const char *name) {
for (int i = 0; i < g_blkdev_count; i++) {
if (g_blkdevs[i] && strcmp(g_blkdevs[i]->name, name) == 0)
return g_blkdevs[i];
}
return NULL;
}
blkdev_t *blkdev_get(int index) {
if (index < 0 || index >= g_blkdev_count) return NULL;
return g_blkdevs[index];
}
int blkdev_count(void) {
return g_blkdev_count;
}
int blkdev_read(blkdev_t *dev, uint64_t offset, void *buf, size_t len) {
if (!dev || !dev->ops || !dev->ops->read_sectors) return -EIO;
if (len == 0) return 0;
uint32_t sec_size = dev->sector_size ? dev->sector_size : BLKDEV_SECTOR_SIZE;
uint64_t start_lba = offset / sec_size;
uint64_t end_byte = offset + len;
uint64_t end_lba = (end_byte + sec_size - 1) / sec_size;
uint32_t nsectors = (uint32_t)(end_lba - start_lba);
uint8_t *tmp = kmalloc((size_t)nsectors * sec_size);
if (!tmp) return -ENOMEM;
int ret = dev->ops->read_sectors(dev, start_lba, nsectors, tmp);
if (ret < 0) {
kfree(tmp);
return ret;
}
uint64_t off_in_buf = offset - start_lba * sec_size;
memcpy(buf, tmp + off_in_buf, len);
kfree(tmp);
return 0;
}
int blkdev_write(blkdev_t *dev, uint64_t offset, const void *buf, size_t len) {
if (!dev || !dev->ops || !dev->ops->write_sectors) return -EIO;
if (len == 0) return 0;
uint32_t sec_size = dev->sector_size ? dev->sector_size : BLKDEV_SECTOR_SIZE;
uint64_t start_lba = offset / sec_size;
uint64_t end_byte = offset + len;
uint64_t end_lba = (end_byte + sec_size - 1) / sec_size;
uint32_t nsectors = (uint32_t)(end_lba - start_lba);
uint8_t *tmp = kmalloc((size_t)nsectors * sec_size);
if (!tmp) return -ENOMEM;
int ret = dev->ops->read_sectors(dev, start_lba, nsectors, tmp);
if (ret < 0) {
kfree(tmp);
return ret;
}
uint64_t off_in_buf = offset - start_lba * sec_size;
memcpy(tmp + off_in_buf, buf, len);
ret = dev->ops->write_sectors(dev, start_lba, nsectors, tmp);
kfree(tmp);
return ret;
}
+164
View File
@@ -0,0 +1,164 @@
#include "../../include/drivers/disk.h"
#include "../../include/drivers/ata.h"
#include "../../include/drivers/blkdev.h"
#include "../../include/drivers/partition.h"
#include "../../include/fs/ext2.h"
#include "../../include/fs/fat32.h"
#include "../../include/fs/vfs.h"
#include "../../include/io/serial.h"
#include "../../include/memory/pmm.h"
#include "../../include/syscall/errno.h"
#include <stdio.h>
#include <string.h>
static int ata_blk_read(blkdev_t *dev, uint64_t lba, uint32_t count, void *buf) {
ata_drive_t *drv = (ata_drive_t *)dev->priv;
return ata_read_sectors(drv, lba, count, buf);
}
static int ata_blk_write(blkdev_t *dev, uint64_t lba, uint32_t count, const void *buf) {
ata_drive_t *drv = (ata_drive_t *)dev->priv;
return ata_write_sectors(drv, lba, count, buf);
}
static int ata_blk_flush(blkdev_t *dev) {
ata_drive_t *drv = (ata_drive_t *)dev->priv;
return ata_flush(drv);
}
static const blkdev_ops_t ata_blkdev_ops = {
.read_sectors = ata_blk_read,
.write_sectors = ata_blk_write,
.flush = ata_blk_flush,
};
static blkdev_t g_ata_blkdevs[ATA_MAX_DRIVES];
extern void devfs_register(const char *name, vnode_t *node);
static int64_t blk_vnode_read(vnode_t *node, void *buf, size_t len, uint64_t offset) {
blkdev_t *dev = (blkdev_t *)node->fs_data;
if (!dev) return -EIO;
int r = blkdev_read(dev, offset, buf, len);
return (r < 0) ? r : (int64_t)len;
}
static int64_t blk_vnode_write(vnode_t *node, const void *buf, size_t len, uint64_t offset) {
blkdev_t *dev = (blkdev_t *)node->fs_data;
if (!dev) return -EIO;
int r = blkdev_write(dev, offset, buf, len);
return (r < 0) ? r : (int64_t)len;
}
static int blk_vnode_stat(vnode_t *node, vfs_stat_t *out) {
blkdev_t *dev = (blkdev_t *)node->fs_data;
memset(out, 0, sizeof(*out));
out->st_ino = node->ino;
out->st_type = VFS_NODE_BLKDEV;
out->st_mode = 0660;
out->st_size = dev ? dev->size_bytes : 0;
return 0;
}
static void blk_vnode_ref(vnode_t *n) { (void)n; }
static void blk_vnode_unref(vnode_t *n) { (void)n; }
static const vnode_ops_t blk_vnode_ops = {
.read = blk_vnode_read, .write = blk_vnode_write,
.stat = blk_vnode_stat, .ref = blk_vnode_ref, .unref = blk_vnode_unref,
};
static vnode_t g_blk_vnodes[ATA_MAX_DRIVES];
static uint64_t g_blk_ino_base = 200;
void disk_init(void) {
serial_writestring("[disk] initializing...\n");
blkdev_init();
ata_init();
int count = 0;
const char *names[] = { "hda", "hdb", "hdc", "hdd" };
for (int i = 0; i < ATA_MAX_DRIVES; i++) {
ata_drive_t *drv = ata_get_drive(i);
if (!drv) continue;
blkdev_t *bdev = &g_ata_blkdevs[count];
memset(bdev, 0, sizeof(*bdev));
strncpy(bdev->name, names[i], BLKDEV_NAME_MAX - 1);
bdev->present = true;
bdev->sector_count = drv->sectors;
bdev->size_bytes = drv->size_bytes;
bdev->sector_size = ATA_SECTOR_SIZE;
bdev->ops = &ata_blkdev_ops;
bdev->priv = drv;
blkdev_register(bdev);
vnode_t *vn = &g_blk_vnodes[count];
memset(vn, 0, sizeof(*vn));
vn->type = VFS_NODE_BLKDEV;
vn->mode = 0660;
vn->ino = g_blk_ino_base + (uint64_t)count;
vn->ops = &blk_vnode_ops;
vn->fs_data = bdev;
vn->size = drv->size_bytes;
vn->refcount = 1;
devfs_register(names[i], vn);
serial_printf("[disk] /dev/%s -> %s (%llu MB)\n",
names[i], drv->model, drv->size_bytes / (1024 * 1024));
printf("[disk] /dev/%s -> %s (%llu MB)\n",
names[i], drv->model, drv->size_bytes / (1024 * 1024));
partition_scan(bdev);
count++;
}
if (count == 0) serial_writestring("[disk] no disks available\n");
else { serial_printf("[disk] %d disk(s) ready\n", count); printf("[disk] %d disk(s) ready\n", count); }
}
static const char *strip_dev_prefix(const char *name) {
if (strncmp(name, "/dev/", 5) == 0) return name + 5;
return name;
}
int disk_format(const char *devname, const char *label) {
const char *raw = strip_dev_prefix(devname);
blkdev_t *dev = blkdev_get_by_name(raw);
if (!dev) return -ENODEV;
return ext2_format(dev, label ? label : raw);
}
static int detect_fs_type(blkdev_t *dev) {
uint8_t sec[512];
if (dev->ops->read_sectors(dev, 0, 1, sec) < 0) return -1;
if (sec[510] == 0x55 && sec[511] == (uint8_t)0xAA) {
if (memcmp(sec + 82, "FAT32", 5) == 0) return 1;
if (memcmp(sec + 54, "FAT", 3) == 0) return 1;
}
uint16_t magic = 0;
if (blkdev_read(dev, EXT2_SUPER_OFFSET + 56, &magic, sizeof(magic)) == 0
&& magic == EXT2_SUPER_MAGIC) return 2;
return 0;
}
int disk_mount(const char *devname, const char *path) {
const char *raw = strip_dev_prefix(devname);
blkdev_t *dev = blkdev_get_by_name(raw);
if (!dev) return -ENODEV;
int t = detect_fs_type(dev);
vnode_t *root = NULL;
if (t == 1) {
root = fat32_mount(dev);
serial_printf("[disk] mounting FAT32 %s -> %s\n", raw, path);
} else if (t == 2) {
root = ext2_mount(dev);
serial_printf("[disk] mounting ext2 %s -> %s\n", raw, path);
} else {
serial_printf("[disk] %s: no recognizable FS\n", raw);
return -EINVAL;
}
if (!root) return -EIO;
int r = vfs_mount(path, root);
if (r < 0) { vnode_unref(root); return r; }
const char *fsname = (t == 1) ? "fat32" : "ext2";
int si = vfs_set_mount_info(path, raw, fsname);
serial_printf("[disk_mount] set_mount_info path='%s' dev='%s' fs='%s' -> %d\n",
path, raw, fsname, si);
return 0;
}
int disk_umount(const char *path) {
return vfs_umount(path);
}
+204
View File
@@ -0,0 +1,204 @@
#include "../../include/drivers/partition.h"
#include "../../include/drivers/blkdev.h"
#include "../../include/fs/vfs.h"
#include "../../include/io/serial.h"
#include "../../include/syscall/errno.h"
#include "../../include/memory/pmm.h"
#include <string.h>
#include <stdio.h>
#define MAX_PARTITIONS 16
typedef struct {
blkdev_t base;
blkdev_t *parent;
uint64_t offset_sectors;
uint64_t count_sectors;
uint8_t type;
uint8_t bootable;
uint32_t partnum;
} partition_blkdev_t;
static partition_blkdev_t g_partitions[MAX_PARTITIONS];
static int g_partition_count = 0;
extern void devfs_register(const char *name, vnode_t *node);
static int64_t part_vnode_read(vnode_t *node, void *buf, size_t len, uint64_t offset) {
blkdev_t *dev = (blkdev_t *)node->fs_data;
if (!dev) return -EIO;
int r = blkdev_read(dev, offset, buf, len);
return (r < 0) ? r : (int64_t)len;
}
static int64_t part_vnode_write(vnode_t *node, const void *buf, size_t len, uint64_t offset) {
blkdev_t *dev = (blkdev_t *)node->fs_data;
if (!dev) return -EIO;
int r = blkdev_write(dev, offset, buf, len);
return (r < 0) ? r : (int64_t)len;
}
static int part_vnode_stat(vnode_t *node, vfs_stat_t *out) {
blkdev_t *dev = (blkdev_t *)node->fs_data;
memset(out, 0, sizeof(*out));
out->st_ino = node->ino;
out->st_type = VFS_NODE_BLKDEV;
out->st_mode = 0660;
out->st_size = dev ? dev->size_bytes : 0;
return 0;
}
static void part_vnode_ref(vnode_t *n) { (void)n; }
static void part_vnode_unref(vnode_t *n) { (void)n; }
static const vnode_ops_t part_vnode_ops = {
.read = part_vnode_read, .write = part_vnode_write,
.stat = part_vnode_stat, .ref = part_vnode_ref, .unref = part_vnode_unref,
};
static vnode_t g_part_vnodes[MAX_PARTITIONS];
static uint64_t g_part_ino_base = 300;
static int part_read_sectors(blkdev_t *dev, uint64_t lba, uint32_t count, void *buf) {
partition_blkdev_t *p = (partition_blkdev_t *)dev->priv;
if (!p || !p->parent) return -EIO;
if (lba + count > p->count_sectors) return -EINVAL;
return p->parent->ops->read_sectors(p->parent, p->offset_sectors + lba, count, buf);
}
static int part_write_sectors(blkdev_t *dev, uint64_t lba, uint32_t count, const void *buf) {
partition_blkdev_t *p = (partition_blkdev_t *)dev->priv;
if (!p || !p->parent) return -EIO;
if (lba + count > p->count_sectors) return -EINVAL;
return p->parent->ops->write_sectors(p->parent, p->offset_sectors + lba, count, buf);
}
static int part_flush(blkdev_t *dev) {
partition_blkdev_t *p = (partition_blkdev_t *)dev->priv;
if (!p || !p->parent) return -EIO;
return p->parent->ops->flush ? p->parent->ops->flush(p->parent) : 0;
}
static const blkdev_ops_t part_blkdev_ops = {
.read_sectors = part_read_sectors,
.write_sectors = part_write_sectors,
.flush = part_flush,
};
int partition_read_mbr(blkdev_t *disk, mbr_t *out) {
if (!disk || !out) return -EINVAL;
uint8_t sector[512];
int r = disk->ops->read_sectors(disk, 0, 1, sector);
if (r < 0) return r;
memcpy(out, sector, 512);
return 0;
}
int partition_write_mbr(blkdev_t *disk, const mbr_partition_t parts[4],
uint32_t disk_signature)
{
if (!disk || !parts) return -EINVAL;
uint8_t sector[512];
int r = disk->ops->read_sectors(disk, 0, 1, sector);
if (r < 0) return r;
mbr_t *mbr = (mbr_t *)sector;
mbr->disk_signature = disk_signature;
mbr->reserved = 0;
for (int i = 0; i < 4; i++) mbr->partitions[i] = parts[i];
mbr->signature = MBR_SIGNATURE;
return disk->ops->write_sectors(disk, 0, 1, sector);
}
static const char *part_type_name(uint8_t t) {
switch (t) {
case MBR_TYPE_EMPTY: return "empty";
case MBR_TYPE_FAT12: return "FAT12";
case MBR_TYPE_FAT16_S: return "FAT16 <32M";
case MBR_TYPE_FAT16: return "FAT16";
case MBR_TYPE_EXTENDED: return "Extended";
case MBR_TYPE_FAT32_CHS: return "FAT32 CHS";
case MBR_TYPE_FAT32_LBA: return "FAT32 LBA";
case MBR_TYPE_FAT16_LBA: return "FAT16 LBA";
case MBR_TYPE_LINUX: return "Linux";
case MBR_TYPE_ESP: return "EFI System";
default: return "unknown";
}
}
int partition_scan(blkdev_t *disk) {
if (!disk || !disk->present) return -ENODEV;
mbr_t mbr;
int r = partition_read_mbr(disk, &mbr);
if (r < 0) {
serial_printf("[part] %s: cannot read MBR: %d\n", disk->name, r);
return r;
}
if (mbr.signature != MBR_SIGNATURE) {
serial_printf("[part] %s: no MBR signature (raw disk / unpartitioned)\n",
disk->name);
return 0;
}
int found = 0;
for (int i = 0; i < 4; i++) {
mbr_partition_t *p = &mbr.partitions[i];
if (p->type == 0 || p->sector_count == 0) continue;
if (g_partition_count >= MAX_PARTITIONS) break;
partition_blkdev_t *pb = &g_partitions[g_partition_count];
memset(pb, 0, sizeof(*pb));
pb->parent = disk;
pb->offset_sectors = p->lba_start;
pb->count_sectors = p->sector_count;
pb->type = p->type;
pb->bootable = (p->boot_flag == 0x80) ? 1 : 0;
pb->partnum = (uint32_t)(i + 1);
snprintf(pb->base.name, BLKDEV_NAME_MAX, "%s%u", disk->name, pb->partnum);
pb->base.present = true;
pb->base.sector_count = pb->count_sectors;
pb->base.size_bytes = pb->count_sectors * (uint64_t)disk->sector_size;
pb->base.sector_size = disk->sector_size;
pb->base.ops = &part_blkdev_ops;
pb->base.priv = pb;
blkdev_register(&pb->base);
vnode_t *vn = &g_part_vnodes[g_partition_count];
memset(vn, 0, sizeof(*vn));
vn->type = VFS_NODE_BLKDEV;
vn->mode = 0660;
vn->ino = g_part_ino_base + (uint64_t)g_partition_count;
vn->ops = &part_vnode_ops;
vn->fs_data = &pb->base;
vn->size = pb->base.size_bytes;
vn->refcount = 1;
devfs_register(pb->base.name, vn);
serial_printf("[part] /dev/%s: type=0x%02x (%s) lba=%u sectors=%u %s\n",
pb->base.name, pb->type, part_type_name(pb->type),
p->lba_start, p->sector_count,
pb->bootable ? "[bootable]" : "");
printf("[part] /dev/%s: 0x%02x (%s) %u MB %s\n",
pb->base.name, pb->type, part_type_name(pb->type),
(unsigned)(pb->base.size_bytes / (1024 * 1024)),
pb->bootable ? "*" : "");
g_partition_count++;
found++;
}
if (found == 0) {
serial_printf("[part] %s: MBR present but no valid partitions\n", disk->name);
}
return found;
}
blkdev_t *partition_get(const char *name) {
for (int i = 0; i < g_partition_count; i++) {
if (strcmp(g_partitions[i].base.name, name) == 0) return &g_partitions[i].base;
}
return NULL;
}
+505
View File
@@ -0,0 +1,505 @@
#include "../../include/drivers/ps2.h"
#include "../../include/sched/sched.h"
#include "../../include/fs/devfs.h"
#include "../../include/drivers/timer.h"
#include "../../include/interrupts/interrupts.h"
#include "../../include/apic/apic.h"
#include "../../include/io/ports.h"
#include "../../include/io/serial.h"
#include "../../include/graphics/fb/fb.h"
#include <stdio.h>
#include <stddef.h>
#define KB_IRQ_VECTOR 0x21
#define MOUSE_IRQ_VECTOR 0x2C
#define KB_IRQ_LINE 1
#define MOUSE_IRQ_LINE 12
typedef enum { MOUSE_MODE_RELATIVE = 0, MOUSE_MODE_ABSOLUTE } mouse_mode_t;
static volatile kb_state_t kb_state;
static volatile mouse_state_t mouse_state;
static volatile mouse_mode_t mouse_mode = MOUSE_MODE_RELATIVE;
static volatile int32_t mouse_screen_w = 1024;
static volatile int32_t mouse_screen_h = 768;
static volatile uint8_t mouse_packet[4];
static volatile uint8_t mouse_packet_idx = 0;
static volatile bool mouse_has_scroll = false;
static volatile uint32_t mouse_lost_sync = 0;
static volatile kb_buf_t kb_buf;
static const char sc_lower[89] = {
0, '\x1b','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,
};
static const char sc_upper[89] = {
0, '\x1b','!', '@', '#', '$', '%', '^',
'&', '*', '(', ')', '_', '+', '\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,
};
static void ps2_wait_read(void) {
for (int i = 0; i < 100000; i++) {
if (inb(PS2_STATUS_PORT) & PS2_STATUS_OUTPUT_FULL) return;
io_wait();
}
}
static void ps2_wait_write(void) {
for (int i = 0; i < 100000; i++) {
if (!(inb(PS2_STATUS_PORT) & PS2_STATUS_INPUT_FULL)) return;
io_wait();
}
}
static void ps2_send_cmd(uint8_t cmd) { ps2_wait_write(); outb(PS2_CMD_PORT, cmd); }
static void ps2_send_data(uint8_t data) { ps2_wait_write(); outb(PS2_DATA_PORT, data); }
static uint8_t ps2_recv_data(void) { ps2_wait_read(); return inb(PS2_DATA_PORT); }
static void ps2_flush_output(void) {
for (int i = 0; i < 32; i++) {
if (!(inb(PS2_STATUS_PORT) & PS2_STATUS_OUTPUT_FULL)) break;
inb(PS2_DATA_PORT);
io_wait();
}
}
static bool ps2_mouse_wait_ack(void) {
for (int i = 0; i < 100000; i++) {
if (inb(PS2_STATUS_PORT) & PS2_STATUS_OUTPUT_FULL) {
uint8_t r = inb(PS2_DATA_PORT);
if (r == 0xFA) return true;
if (r == 0xFE || r == 0xFC) return false;
}
io_wait();
}
serial_writestring("[PS2] mouse_wait_ack: TIMEOUT\n");
return false;
}
static bool ps2_mouse_cmd(uint8_t cmd) {
ps2_send_cmd(PS2_CMD_WRITE_PORT2);
ps2_send_data(cmd);
return ps2_mouse_wait_ack();
}
static bool ps2_mouse_cmd_param(uint8_t cmd, uint8_t param) {
return ps2_mouse_cmd(cmd) && ps2_mouse_cmd(param);
}
static uint8_t ps2_mouse_read_byte(void) {
for (int i = 0; i < 100000; i++) {
if (inb(PS2_STATUS_PORT) & PS2_STATUS_OUTPUT_FULL)
return inb(PS2_DATA_PORT);
io_wait();
}
return 0xFF;
}
static uint8_t ps2_mouse_get_id(void) {
ps2_mouse_cmd(0xF2);
return ps2_mouse_read_byte();
}
static bool ps2_mouse_init_scroll(void) {
ps2_mouse_cmd_param(0xF3, 200);
ps2_mouse_cmd_param(0xF3, 100);
ps2_mouse_cmd_param(0xF3, 80);
uint8_t id = ps2_mouse_get_id();
serial_printf("[PS2] Mouse ID after IntelliMouse knock: 0x%02x\n", id);
return (id == 0x03);
}
static bool ps2_detect_absolute(void) {
uint8_t id = ps2_mouse_get_id();
serial_printf("[PS2] Mouse base device ID: 0x%02x\n", id);
if (!ps2_mouse_cmd(0xE9)) {
serial_writestring("[PS2] Status request failed\n");
return false;
}
uint8_t status = ps2_mouse_read_byte();
uint8_t resolution = ps2_mouse_read_byte();
uint8_t sample_rate = ps2_mouse_read_byte();
serial_printf("[PS2] Mouse status: flags=0x%02x res=0x%02x rate=%d\n",
status, resolution, sample_rate);
if (resolution == 0x08) {
serial_writestring("[PS2] Detected: QEMU absolute tablet mode\n");
return true;
}
ps2_mouse_cmd_param(0xF3, 200); ps2_mouse_cmd_param(0xF3, 100); ps2_mouse_cmd_param(0xF3, 80);
ps2_mouse_cmd_param(0xF3, 200); ps2_mouse_cmd_param(0xF3, 100); ps2_mouse_cmd_param(0xF3, 80);
id = ps2_mouse_get_id();
serial_printf("[PS2] Mouse ID after abs knock: 0x%02x\n", id);
if (id == 0x08) {
serial_writestring("[PS2] Detected: QEMU absolute mode (ID=0x08)\n");
return true;
}
return false;
}
static char scancode_to_char(uint8_t sc) {
if (sc >= sizeof(sc_lower) / sizeof(sc_lower[0])) return 0;
bool use_upper = kb_state.shift;
if ((sc >= 0x10 && sc <= 0x19) ||
(sc >= 0x1E && sc <= 0x26) ||
(sc >= 0x2C && sc <= 0x32))
use_upper = kb_state.caps_lock ^ kb_state.shift;
return use_upper ? sc_upper[sc] : sc_lower[sc];
}
void kb_buf_push(char c) {
uint8_t next = (kb_buf.tail + 1) % KB_BUF_SIZE;
if (next != kb_buf.head) {
kb_buf.buf[kb_buf.tail] = c;
kb_buf.tail = next;
}
}
extern uint32_t cursor_x;
extern uint32_t cursor_y;
extern uint32_t bg_color;
extern struct limine_framebuffer *global_framebuffer;
extern void get_cursor_position(uint32_t *x, uint32_t *y);
extern uint32_t get_screen_width(void);
/*static void mouse_print_status(void) {
const mouse_state_t *m = (const mouse_state_t *)&mouse_state;
const char *scroll_str = "NONE";
if (m->scroll == MOUSE_SCROLL_UP) scroll_str = "UP ";
if (m->scroll == MOUSE_SCROLL_DOWN) scroll_str = "DOWN";
serial_printf("[MOUSE] X:%5d Y:%5d L:%d M:%d R:%d Scroll:%s\n",
m->x, m->y,
(int)m->btn_left, (int)m->btn_middle, (int)m->btn_right,
scroll_str);
uint32_t saved_x, saved_y;
get_cursor_position(&saved_x, &saved_y);
if (global_framebuffer)
fb_fill_rect(global_framebuffer, 0, 0, global_framebuffer->width, 16, bg_color);
cursor_x = 0; cursor_y = 0;
printf("[MOUSE] X:%-5d Y:%-5d [L:%d M:%d R:%d] Wheel:%s",
m->x, m->y,
(int)m->btn_left, (int)m->btn_middle, (int)m->btn_right,
scroll_str);
cursor_x = saved_x;
cursor_y = saved_y;
}*/
DEFINE_IRQ(KB_IRQ_VECTOR, ps2_kb_handler)
{
(void)frame;
static bool e0_prefix = false;
uint8_t sc = inb(PS2_DATA_PORT);
bool released = (sc & PS2_KEY_RELEASE_BIT) != 0;
uint8_t key = sc & ~PS2_KEY_RELEASE_BIT;
if (sc == 0xE0) { e0_prefix = true; lapic_eoi(); return; }
if (e0_prefix) {
e0_prefix = false;
if (!released) {
switch (key) {
case 0x48: kb_buf_push('\x1b'); kb_buf_push('['); kb_buf_push('A'); break;
case 0x50: kb_buf_push('\x1b'); kb_buf_push('['); kb_buf_push('B'); break;
case 0x4D: kb_buf_push('\x1b'); kb_buf_push('['); kb_buf_push('C'); break;
case 0x4B: kb_buf_push('\x1b'); kb_buf_push('['); kb_buf_push('D'); break;
case 0x47: kb_buf_push('\x1b'); kb_buf_push('['); kb_buf_push('H'); break;
case 0x4F: kb_buf_push('\x1b'); kb_buf_push('['); kb_buf_push('F'); break;
case 0x53: kb_buf_push('\x1b'); kb_buf_push('['); kb_buf_push('3'); kb_buf_push('~'); break;
default: break;
}
}
lapic_eoi();
return;
}
if (key == SC_LSHIFT || key == SC_RSHIFT) { kb_state.shift = !released; lapic_eoi(); return; }
if (key == SC_LCTRL) { kb_state.ctrl = !released; lapic_eoi(); return; }
if (key == SC_LALT) { kb_state.alt = !released; lapic_eoi(); return; }
if (key == SC_CAPS && !released) { kb_state.caps_lock = !kb_state.caps_lock; lapic_eoi(); return; }
if (released) { lapic_eoi(); return; }
if (kb_state.ctrl) {
char base = scancode_to_char(key);
serial_printf("[PS2] ctrl+key sc=0x%02x base='%c'(0x%02x)\n", (unsigned)key, (base>=0x20&&base<0x7f)?base:'?', (unsigned)(uint8_t)base);
if (base >= 'a' && base <= 'z') {
uint8_t ctrl_char = (uint8_t)(base - 'a' + 1);
serial_printf("[PS2] ctrl char generated: 0x%02x\n", ctrl_char);
if (ctrl_char == 0x03) {
g_ctrlc_pending = 1;
lapic_eoi();
return;
}
kb_buf_push((char)ctrl_char);
lapic_eoi();
return;
}
if (base >= 'A' && base <= 'Z') {
kb_buf_push((char)(base - 'A' + 1));
lapic_eoi();
return;
}
}
char c = scancode_to_char(key);
if (c != 0)
kb_buf_push(c);
lapic_eoi();
}
DEFINE_IRQ(MOUSE_IRQ_VECTOR, ps2_mouse_handler)
{
(void)frame;
uint8_t data = inb(PS2_DATA_PORT);
uint8_t idx = mouse_packet_idx;
if (idx == 0 && !(data & 0x08)) {
mouse_lost_sync++;
lapic_eoi();
return;
}
mouse_packet[idx] = data;
mouse_packet_idx = (uint8_t)(idx + 1);
uint8_t pkt_size = mouse_has_scroll ? 4 : 3;
if (mouse_packet_idx < pkt_size) { lapic_eoi(); return; }
mouse_packet_idx = 0;
uint8_t flags = mouse_packet[0];
if ((flags & MOUSE_X_OVERFLOW) || (flags & MOUSE_Y_OVERFLOW)) { lapic_eoi(); return; }
mouse_state.btn_left = (flags & MOUSE_BTN_LEFT) != 0;
mouse_state.btn_right = (flags & MOUSE_BTN_RIGHT) != 0;
mouse_state.btn_middle = (flags & MOUSE_BTN_MIDDLE) != 0;
if (mouse_mode == MOUSE_MODE_ABSOLUTE) {
uint16_t abs_x = (uint16_t)mouse_packet[1] | ((flags & 0x10) ? 0x100 : 0);
uint16_t abs_y = (uint16_t)mouse_packet[2] | ((flags & 0x20) ? 0x100 : 0);
mouse_state.x = (int32_t)((uint32_t)abs_x * (uint32_t)mouse_screen_w / 0x1FF);
mouse_state.y = (int32_t)((uint32_t)abs_y * (uint32_t)mouse_screen_h / 0x1FF);
} else {
int32_t dx = mouse_packet[1];
int32_t dy = mouse_packet[2];
if (flags & MOUSE_X_SIGN) dx |= 0xFFFFFF00;
if (flags & MOUSE_Y_SIGN) dy |= 0xFFFFFF00;
mouse_state.x += dx;
mouse_state.y -= dy;
}
if (mouse_state.x < 0) mouse_state.x = 0;
if (mouse_state.y < 0) mouse_state.y = 0;
if (mouse_state.x >= mouse_screen_w) mouse_state.x = mouse_screen_w - 1;
if (mouse_state.y >= mouse_screen_h) mouse_state.y = mouse_screen_h - 1;
if (mouse_has_scroll) {
uint8_t z_raw = mouse_packet[3] & 0x0F;
int8_t z = (z_raw & 0x08) ? (int8_t)(z_raw | 0xF0) : (int8_t)z_raw;
if (z > 0) mouse_state.scroll = MOUSE_SCROLL_DOWN;
else if (z < 0) mouse_state.scroll = MOUSE_SCROLL_UP;
else mouse_state.scroll = MOUSE_SCROLL_NONE;
}
//mouse_print_status();
lapic_eoi();
}
bool ps2_init(void) {
serial_writestring("[PS2] Initializing PS/2 driver...\n");
if (!apic_is_available()) {
serial_writestring("[PS2] ERROR: APIC not available\n");
return false;
}
serial_writestring("[PS2] Initializing PS/2 controller...\n");
ps2_send_cmd(PS2_CMD_DISABLE_PORT1);
ps2_send_cmd(PS2_CMD_DISABLE_PORT2);
ps2_flush_output();
ps2_send_cmd(PS2_CMD_READ_CONFIG);
uint8_t cfg = ps2_recv_data();
cfg &= ~(PS2_CFG_PORT1_IRQ | PS2_CFG_PORT2_IRQ | PS2_CFG_PORT1_XLAT);
ps2_send_cmd(PS2_CMD_WRITE_CONFIG);
ps2_send_data(cfg);
ps2_send_cmd(PS2_CMD_SELF_TEST);
uint8_t result = ps2_recv_data();
if (result != 0x55) { serial_printf("[PS2] Self-test FAILED (0x%02x)\n", result); return false; }
serial_writestring("[PS2] Self-test PASSED\n");
ps2_send_cmd(PS2_CMD_WRITE_CONFIG);
ps2_send_data(cfg);
ps2_send_cmd(PS2_CMD_TEST_PORT1);
bool kb_ok = (ps2_recv_data() == 0x00);
serial_printf("[PS2] Port 1 (keyboard): %s\n", kb_ok ? "OK" : "FAIL");
ps2_send_cmd(PS2_CMD_TEST_PORT2);
bool mouse_ok = (ps2_recv_data() == 0x00);
serial_printf("[PS2] Port 2 (mouse): %s\n", mouse_ok ? "OK" : "FAIL");
if (kb_ok) ps2_send_cmd(PS2_CMD_ENABLE_PORT1);
if (mouse_ok) ps2_send_cmd(PS2_CMD_ENABLE_PORT2);
if (kb_ok) {
ps2_send_data(0xFF);
for (int i = 0; i < 200000; i++) {
if (inb(PS2_STATUS_PORT) & PS2_STATUS_OUTPUT_FULL)
if (inb(PS2_DATA_PORT) == 0xAA) break;
io_wait();
}
ps2_flush_output();
ps2_send_data(0xF0); ps2_flush_output();
ps2_send_data(0x01); ps2_flush_output();
ps2_send_data(0xF4); ps2_flush_output();
serial_writestring("[PS2] Keyboard ready (scancode set 1)\n");
}
if (mouse_ok) {
ps2_mouse_cmd(0xFF);
for (int i = 0; i < 200000; i++) {
if (inb(PS2_STATUS_PORT) & PS2_STATUS_OUTPUT_FULL) inb(PS2_DATA_PORT);
io_wait();
}
ps2_flush_output();
ps2_mouse_cmd(0xF6);
bool is_abs = ps2_detect_absolute();
if (is_abs) {
mouse_mode = MOUSE_MODE_ABSOLUTE;
mouse_has_scroll = false;
serial_writestring("[PS2] Mouse mode: ABSOLUTE (QEMU tablet)\n");
} else {
mouse_mode = MOUSE_MODE_RELATIVE;
mouse_has_scroll = ps2_mouse_init_scroll();
serial_printf("[PS2] Mouse mode: RELATIVE, scroll=%s\n",
mouse_has_scroll ? "YES" : "NO");
}
ps2_mouse_cmd_param(0xF3, 100);
ps2_mouse_cmd(0xF4);
serial_writestring("[PS2] Mouse: data reporting enabled\n");
}
ps2_send_cmd(PS2_CMD_READ_CONFIG);
cfg = ps2_recv_data();
if (kb_ok) cfg |= PS2_CFG_PORT1_IRQ;
if (mouse_ok) cfg |= PS2_CFG_PORT2_IRQ;
ps2_send_cmd(PS2_CMD_WRITE_CONFIG);
ps2_send_data(cfg);
if (kb_ok) {
apic_setup_irq(KB_IRQ_LINE, KB_IRQ_VECTOR, false, 0);
serial_printf("[PS2] Keyboard IRQ%d -> vector 0x%02x\n", KB_IRQ_LINE, KB_IRQ_VECTOR);
}
if (mouse_ok) {
apic_setup_irq(MOUSE_IRQ_LINE, MOUSE_IRQ_VECTOR, false, 0);
serial_printf("[PS2] Mouse IRQ%d -> vector 0x%02x\n", MOUSE_IRQ_LINE, MOUSE_IRQ_VECTOR);
}
kb_state = (kb_state_t){0};
kb_buf = (kb_buf_t){0};
mouse_state = (mouse_state_t){0};
mouse_packet_idx = 0;
mouse_lost_sync = 0;
if (global_framebuffer) {
mouse_screen_w = (int32_t)global_framebuffer->width;
mouse_screen_h = (int32_t)global_framebuffer->height;
mouse_state.x = mouse_screen_w / 2;
mouse_state.y = mouse_screen_h / 2;
}
serial_printf("[PS2] Screen: %dx%d, cursor start: %d,%d\n",
(int)mouse_screen_w, (int)mouse_screen_h,
(int)mouse_state.x, (int)mouse_state.y);
serial_writestring("[PS2] Driver ready.\n");
return true;
}
const kb_state_t* ps2_kb_get_state(void) {
return (const kb_state_t*)&kb_state;
}
const mouse_state_t* ps2_mouse_get_state(void) {
return (const mouse_state_t*)&mouse_state;
}
bool kb_buf_empty(void) {
return kb_buf.head == kb_buf.tail;
}
bool kb_buf_try_getc(char *out) {
if (kb_buf_empty()) return false;
*out = kb_buf.buf[kb_buf.head];
kb_buf.head = (kb_buf.head + 1) % KB_BUF_SIZE;
return true;
}
bool kb_buf_has_ctrlc(void) {
uint8_t h = kb_buf.head;
while (h != kb_buf.tail) {
if (kb_buf.buf[h] == 0x03) return true;
h = (h + 1) % KB_BUF_SIZE;
}
return false;
}
void kb_buf_consume_ctrlc(void) {
uint8_t h = kb_buf.head, t = kb_buf.tail;
uint8_t pos = h;
bool found = false;
while (pos != t) {
if (kb_buf.buf[pos] == 0x03) { found = true; break; }
pos = (pos + 1) % KB_BUF_SIZE;
}
if (!found) return;
uint8_t cur = pos;
uint8_t nxt = (cur + 1) % KB_BUF_SIZE;
while (nxt != t) {
kb_buf.buf[cur] = kb_buf.buf[nxt];
cur = nxt;
nxt = (nxt + 1) % KB_BUF_SIZE;
}
kb_buf.tail = cur;
}
char kb_buf_getc(void) {
char c;
while (!kb_buf_try_getc(&c)) {
//wait
}
return c;
}
+145
View File
@@ -0,0 +1,145 @@
#include "../include/drivers/timer.h"
#include "../include/apic/apic.h"
#include "../include/io/serial.h"
#include "../include/interrupts/interrupts.h"
#include "../include/io/ports.h"
#include "../include/sched/sched.h"
#include "../include/smp/percpu.h"
#include "../include/memory/pmm.h"
#include "../include/gdt/gdt.h"
#include <stdlib.h>
static volatile uint64_t ticks = 0;
volatile uint32_t g_ctrlc_pending = 0;
extern task_t* task_find_foreground(void);
extern void task_kill(task_t* target);
extern void sched_wakeup_sleepers(uint64_t now_ns);
DEFINE_IRQ(0x20, timer_handler)
{
ticks++;
lapic_eoi();
uint32_t cpu = lapic_get_id();
task_t* current = current_task[cpu];
percpu_t* pc = get_percpu();
if (cpu == 0 && g_ctrlc_pending) {
g_ctrlc_pending = 0;
task_t *fg = task_find_foreground();
if (fg) {
task_kill(fg);
} else {
extern void kb_buf_push(char c);
extern bool kb_buf_has_ctrlc(void);
if (!kb_buf_has_ctrlc())
kb_buf_push(0x03);
}
}
if (cpu == 0 && hpet_is_available()) {
sched_wakeup_sleepers(hpet_elapsed_ns());
}
if (!current) return;
if (current->pending_kill && pc) {
current->time_slice = 0;
pc->need_resched = true;
(void)frame;
return;
}
if (current->time_slice > 0)
current->time_slice--;
if (current->time_slice == 0 && pc)
pc->need_resched = true;
(void)frame;
}
bool timer_init(void) {
serial_writestring("Initializing timer subsystem...\n");
if (!apic_is_available()) {
serial_writestring("ERROR: APIC not available\n");
return false;
}
apic_timer_calibrate();
outb(0x21, 0xFF);
outb(0xA1, 0xFF);
serial_writestring("Timer subsystem initialized\n");
if (hpet_is_available()) {
serial_printf("HPET frequency: %llu Hz\n", hpet_get_frequency());
} else {
serial_writestring("HPET not available\n");
}
serial_writestring("Checking APIC Timer Status\n");
uint32_t timer_config = lapic_read(LAPIC_TIMER);
uint32_t initial_count = lapic_read(LAPIC_TIMER_ICR);
uint32_t current_count = lapic_read(LAPIC_TIMER_CCR);
serial_printf("APIC Timer Config: 0x%x\n", timer_config);
serial_printf(" Vector: 0x%x\n", timer_config & 0xFF);
serial_printf(" Masked: %s\n", (timer_config & LAPIC_TIMER_MASKED) ? "YES" : "NO");
serial_printf(" Periodic: %s\n", (timer_config & LAPIC_TIMER_PERIODIC) ? "YES" : "NO");
serial_printf("Initial Count: %u\n", initial_count);
serial_printf("Current Count: %u\n", current_count);
if (timer_config & LAPIC_TIMER_MASKED) {
serial_writestring("WARNING: APIC timer is masked! Unmasking...\n");
lapic_write(LAPIC_TIMER, (timer_config & ~LAPIC_TIMER_MASKED));
}
uint64_t rflags;
asm volatile("pushfq; pop %0" : "=r"(rflags));
serial_printf("RFLAGS: 0x%llx (IF=%s)\n", rflags, (rflags & 0x200) ? "ON" : "OFF");
if (!(rflags & 0x200)) {
serial_writestring("WARNING: Interrupts are DISABLED! Enabling...\n");
asm volatile("sti");
}
return true;
}
uint64_t timer_get_ticks(void) {
return ticks;
}
void timer_sleep_ms(uint64_t milliseconds) {
if (hpet_is_available()) {
hpet_sleep_ms(milliseconds);
} else {
volatile uint64_t i;
for (i = 0; i < milliseconds * 1000; i++) {
asm volatile("pause");
}
}
}
void timer_sleep_us(uint64_t microseconds) {
if (hpet_is_available()) {
hpet_sleep_us(microseconds);
} else {
volatile uint64_t i;
for (i = 0; i < microseconds; i++) {
asm volatile("pause");
}
}
}
void timer_sleep_ns(uint64_t nanoseconds) {
if (hpet_is_available()) {
hpet_sleep_ns(nanoseconds);
} else {
timer_sleep_us(nanoseconds / 1000);
}
}
+374
View File
@@ -0,0 +1,374 @@
#include "../../include/elf/elf.h"
#include "../../include/memory/vmm.h"
#include "../../include/memory/pmm.h"
#include "../../include/memory/paging.h"
#include "../../include/io/serial.h"
#include <string.h>
#include <stdlib.h>
#define ELF_PIE_BASE 0x0000000000400000ULL
#define ELF_USER_STACK_TOP 0x00007FFFFFFFE000ULL
#define ELF_DEFAULT_STACK (64 * 1024)
#define PML4_KERNEL_START 256
#define PML4_ENTRIES 512
#ifndef SHF_WRITE
#define SHF_WRITE 0x1
#endif
#ifndef SHF_ALLOC
#define SHF_ALLOC 0x2
#endif
#ifndef SHF_EXECINSTR
#define SHF_EXECINSTR 0x4
#endif
static inline uintptr_t page_align_down(uintptr_t addr) {
return addr & ~(uintptr_t)(PAGE_SIZE - 1);
}
static inline uintptr_t page_align_up(uintptr_t addr) {
return (addr + PAGE_SIZE - 1) & ~(uintptr_t)(PAGE_SIZE - 1);
}
static uint64_t phdr_flags_to_vmm(uint32_t pf) {
uint64_t flags = VMM_PRESENT | VMM_USER;
if (pf & PF_W) flags |= VMM_WRITE;
if (!(pf & PF_X)) flags |= VMM_NOEXEC;
return flags;
}
static elf_error_t elf_validate(const elf64_ehdr_t* hdr, size_t size) {
if (size < sizeof(elf64_ehdr_t)) return ELF_ERR_TOO_SMALL;
uint32_t magic;
memcpy(&magic, hdr->e_ident, 4);
if (magic != ELF_MAGIC) return ELF_ERR_BAD_MAGIC;
if (hdr->e_ident[EI_CLASS] != ELFCLASS64) return ELF_ERR_NOT_64;
if (hdr->e_ident[EI_DATA] != ELFDATA2LSB) return ELF_ERR_NOT_LE;
if (hdr->e_ident[EI_VERSION] != EV_CURRENT ||
hdr->e_version != EV_CURRENT) return ELF_ERR_BAD_VERSION;
if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) return ELF_ERR_NOT_EXEC;
if (hdr->e_machine != EM_X86_64) return ELF_ERR_WRONG_ARCH;
if (hdr->e_phnum == 0) return ELF_ERR_NO_LOAD;
uint64_t pht_end = (uint64_t)hdr->e_phoff +
(uint64_t)hdr->e_phentsize * hdr->e_phnum;
if (pht_end > size) return ELF_ERR_TOO_SMALL;
if (hdr->e_shnum && hdr->e_shoff) {
uint64_t sht_end = (uint64_t)hdr->e_shoff +
(uint64_t)hdr->e_shentsize * hdr->e_shnum;
if (sht_end > size) return ELF_ERR_TOO_SMALL;
}
return ELF_OK;
}
static bool ensure_page_mapped(vmm_pagemap_t* map, uintptr_t virt, uint64_t flags) {
uint64_t pf = 0;
if (vmm_get_page_flags(map, virt, &pf) && (pf & VMM_PRESENT)) {
return true;
}
void* page = pmm_alloc_zero(1);
if (!page) return false;
if (!vmm_map_page(map, virt, pmm_virt_to_phys(page), flags)) {
pmm_free(page, 1);
return false;
}
return true;
}
static elf_error_t load_segment(vmm_pagemap_t* map,
const uint8_t* data,
size_t file_size,
const elf64_phdr_t* phdr,
uintptr_t load_bias)
{
if (phdr->p_memsz == 0) return ELF_OK;
uintptr_t virt_start = phdr->p_vaddr + load_bias;
uintptr_t virt_end = virt_start + phdr->p_memsz;
uintptr_t page_start = page_align_down(virt_start);
uintptr_t page_end = page_align_up(virt_end);
size_t page_count = (page_end - page_start) / PAGE_SIZE;
if (phdr->p_filesz > 0 && phdr->p_offset + phdr->p_filesz > file_size) {
serial_printf("[ELF] Segment data out of file bounds\n");
return ELF_ERR_TOO_SMALL;
}
uint64_t vmm_flags = phdr_flags_to_vmm(phdr->p_flags);
for (size_t i = 0; i < page_count; i++) {
uintptr_t virt = page_start + i * PAGE_SIZE;
uint64_t cur_flags = 0;
bool already_mapped =
vmm_get_page_flags(map, virt, &cur_flags) && (cur_flags & VMM_PRESENT);
if (!already_mapped) {
void* page = pmm_alloc_zero(1);
if (!page) {
serial_printf("[ELF] pmm_alloc_zero(1) failed at page %zu for vaddr 0x%llx\n",
i, virt_start);
return ELF_ERR_NO_MEM;
}
if (!vmm_map_page(map, virt, pmm_virt_to_phys(page), vmm_flags)) {
serial_printf("[ELF] vmm_map_page failed: virt=0x%llx\n", virt);
pmm_free(page, 1);
return ELF_ERR_MAP_FAIL;
}
}
if (phdr->p_filesz > 0) {
uintptr_t pv_start = virt;
uintptr_t pv_end = pv_start + PAGE_SIZE;
uintptr_t data_end = virt_start + phdr->p_filesz;
uintptr_t cp_start = (pv_start > virt_start) ? pv_start : virt_start;
uintptr_t cp_end = (pv_end < data_end) ? pv_end : data_end;
if (cp_start < cp_end) {
uintptr_t phys = 0;
if (!vmm_virt_to_phys(map, cp_start, &phys)) {
return ELF_ERR_MAP_FAIL;
}
phys &= ~(uintptr_t)0xFFF;
size_t dst_off = cp_start - pv_start;
size_t src_off = cp_start - virt_start;
memcpy((uint8_t*)pmm_phys_to_virt(phys) + dst_off,
data + phdr->p_offset + src_off,
cp_end - cp_start);
}
}
}
serial_printf("[ELF] Segment loaded: virt=0x%llx-0x%llx flags=%s%s%s "
"phys=<per-page> pages=%zu\n",
virt_start, virt_end,
(phdr->p_flags & PF_R) ? "R" : "-",
(phdr->p_flags & PF_W) ? "W" : "-",
(phdr->p_flags & PF_X) ? "X" : "-",
page_count);
return ELF_OK;
}
static uintptr_t cover_orphan_sections(vmm_pagemap_t* map,
const elf64_ehdr_t* ehdr,
const uint8_t* bytes,
size_t file_size,
uintptr_t load_bias,
uintptr_t cur_load_end)
{
if (!ehdr->e_shnum || !ehdr->e_shoff) {
serial_printf("[ELF] no section headers; skipping orphan-section pass\n");
return cur_load_end;
}
if (ehdr->e_shentsize < sizeof(elf64_shdr_t)) {
serial_printf("[ELF] e_shentsize=%u too small; skipping orphan-section pass\n",
(unsigned)ehdr->e_shentsize);
return cur_load_end;
}
uint64_t sht_end = (uint64_t)ehdr->e_shoff +
(uint64_t)ehdr->e_shentsize * ehdr->e_shnum;
if (sht_end > file_size) {
serial_printf("[ELF] section table out of file bounds; skip\n");
return cur_load_end;
}
serial_printf("[ELF] section scan: e_shnum=%u\n", (unsigned)ehdr->e_shnum);
uintptr_t new_end = cur_load_end;
for (uint16_t i = 0; i < ehdr->e_shnum; i++) {
const elf64_shdr_t* sh = (const elf64_shdr_t*)
(bytes + ehdr->e_shoff + (uint64_t)ehdr->e_shentsize * i);
if (!(sh->sh_flags & SHF_ALLOC)) continue;
if (sh->sh_size == 0) continue;
if (sh->sh_addr == 0) continue;
uintptr_t s_start = (uintptr_t)sh->sh_addr + load_bias;
uintptr_t s_end = s_start + sh->sh_size;
uintptr_t p_start = page_align_down(s_start);
uintptr_t p_end = page_align_up(s_end);
uint64_t flags = VMM_PRESENT | VMM_USER;
if (sh->sh_flags & SHF_WRITE) flags |= VMM_WRITE;
if (!(sh->sh_flags & SHF_EXECINSTR)) flags |= VMM_NOEXEC;
size_t added_pages = 0;
for (uintptr_t p = p_start; p < p_end; p += PAGE_SIZE) {
uint64_t pf = 0;
if (vmm_get_page_flags(map, p, &pf) && (pf & VMM_PRESENT))
continue;
if (!ensure_page_mapped(map, p, flags)) {
serial_printf("[ELF] orphan-section: alloc/map failed at 0x%llx\n",
(unsigned long long)p);
return new_end;
}
added_pages++;
}
if (added_pages > 0) {
serial_printf("[ELF] ORPHAN section #%u type=%u: virt=0x%llx-0x%llx "
"added=%zu pages flags=%s%s%s\n",
(unsigned)i, (unsigned)sh->sh_type,
(unsigned long long)s_start,
(unsigned long long)s_end,
added_pages,
(sh->sh_flags & SHF_ALLOC) ? "A" : "-",
(sh->sh_flags & SHF_WRITE) ? "W" : "-",
(sh->sh_flags & SHF_EXECINSTR) ? "X" : "-");
if (sh->sh_type != SHT_NOBITS && sh->sh_size > 0) {
if (sh->sh_offset + sh->sh_size <= file_size) {
for (uintptr_t p = p_start; p < p_end; p += PAGE_SIZE) {
uintptr_t pv_end = p + PAGE_SIZE;
uintptr_t cp_start = (p > s_start) ? p : s_start;
uintptr_t cp_end = (pv_end < s_end) ? pv_end : s_end;
if (cp_start >= cp_end) continue;
uintptr_t phys = 0;
if (!vmm_virt_to_phys(map, cp_start, &phys)) continue;
phys &= ~(uintptr_t)0xFFF;
size_t dst_off = cp_start - p;
size_t src_off = cp_start - s_start;
memcpy((uint8_t*)pmm_phys_to_virt(phys) + dst_off,
bytes + sh->sh_offset + src_off,
cp_end - cp_start);
}
}
}
}
if (p_end > new_end) new_end = p_end;
}
return new_end;
}
static uintptr_t alloc_user_stack(vmm_pagemap_t* map, size_t stack_size) {
size_t page_count = page_align_up(stack_size) / PAGE_SIZE;
uintptr_t stack_bottom = ELF_USER_STACK_TOP - page_count * PAGE_SIZE;
uint64_t flags = VMM_PRESENT | VMM_WRITE | VMM_USER | VMM_NOEXEC;
for (size_t i = 0; i < page_count; i++) {
void* page = pmm_alloc_zero(1);
if (!page) {
serial_printf("[ELF] Stack alloc failed at page %zu\n", i);
for (size_t j = 0; j < i; j++)
vmm_unmap_page_noflush(map, stack_bottom + j * PAGE_SIZE);
return 0;
}
if (!vmm_map_page(map, stack_bottom + i * PAGE_SIZE,
pmm_virt_to_phys(page), flags)) {
serial_printf("[ELF] Stack map failed at page %zu\n", i);
pmm_free(page, 1);
for (size_t j = 0; j < i; j++)
vmm_unmap_page_noflush(map, stack_bottom + j * PAGE_SIZE);
return 0;
}
}
serial_printf("[ELF] Stack: virt=0x%llx-0x%llx (%zu KiB)\n",
stack_bottom, ELF_USER_STACK_TOP,
(page_count * PAGE_SIZE) / 1024);
return (ELF_USER_STACK_TOP - 8) & ~(uintptr_t)0xF;
}
elf_load_result_t elf_load(const void* data, size_t size, size_t stack_sz) {
elf_load_result_t result = {0};
if (!data) { result.error = ELF_ERR_NULL; return result; }
const elf64_ehdr_t* ehdr = (const elf64_ehdr_t*)data;
result.error = elf_validate(ehdr, size);
if (result.error != ELF_OK) {
serial_printf("[ELF] Validation failed: %s\n", elf_strerror(result.error));
return result;
}
uintptr_t load_bias = (ehdr->e_type == ET_DYN) ? ELF_PIE_BASE : 0;
if (load_bias) serial_printf("[ELF] PIE binary, bias=0x%llx\n", load_bias);
vmm_pagemap_t* map = vmm_create_pagemap();
if (!map) {
serial_printf("[ELF] vmm_create_pagemap() failed\n");
result.error = ELF_ERR_NO_MEM;
return result;
}
result.pagemap = map;
serial_printf("[ELF] Kernel PML4[256..511] inherited via vmm_create_pagemap\n");
const uint8_t* bytes = (const uint8_t*)data;
const elf64_phdr_t* phdrs = (const elf64_phdr_t*)(bytes + ehdr->e_phoff);
bool has_load = false;
uintptr_t max_vaddr = 0;
for (uint16_t i = 0; i < ehdr->e_phnum; i++) {
const elf64_phdr_t* ph = &phdrs[i];
if (ph->p_type != PT_LOAD) continue;
has_load = true;
serial_printf("[ELF] PT_LOAD[%u]: off=0x%llx vaddr=0x%llx "
"filesz=0x%llx memsz=0x%llx flags=0x%x\n",
i, ph->p_offset, ph->p_vaddr,
ph->p_filesz, ph->p_memsz, ph->p_flags);
elf_error_t err = load_segment(map, bytes, size, ph, load_bias);
if (err != ELF_OK) {
result.error = err;
return result;
}
uintptr_t seg_end = ph->p_vaddr + load_bias + ph->p_memsz;
if (seg_end > max_vaddr) max_vaddr = seg_end;
}
if (!has_load) { result.error = ELF_ERR_NO_LOAD; return result; }
uintptr_t load_end = page_align_up(max_vaddr);
load_end = cover_orphan_sections(map, ehdr, bytes, size, load_bias, load_end);
result.entry = ehdr->e_entry + load_bias;
result.load_base = load_bias;
result.load_end = load_end;
serial_printf("[ELF] Entry point: 0x%llx load_end (brk_start): 0x%llx\n",
result.entry, result.load_end);
if (stack_sz == 0) stack_sz = ELF_DEFAULT_STACK;
result.stack_size = stack_sz;
result.stack_top = alloc_user_stack(map, stack_sz);
if (result.stack_top == 0) { result.error = ELF_ERR_NO_MEM; return result; }
serial_printf("[ELF] Load complete. entry=0x%llx stack_top=0x%llx\n",
result.entry, result.stack_top);
return result;
}
void elf_unload(elf_load_result_t* result) {
if (!result) return;
if (result->pagemap) {
vmm_free_pagemap(result->pagemap);
result->pagemap = NULL;
}
serial_printf("[ELF] Process unloaded.\n");
}
const char* elf_strerror(elf_error_t err) {
switch (err) {
case ELF_OK: return "OK";
case ELF_ERR_NULL: return "NULL pointer";
case ELF_ERR_TOO_SMALL: return "file too small / data out of bounds";
case ELF_ERR_BAD_MAGIC: return "not an ELF file (bad magic)";
case ELF_ERR_NOT_64: return "not ELF64";
case ELF_ERR_NOT_LE: return "not little-endian";
case ELF_ERR_BAD_VERSION: return "unsupported ELF version";
case ELF_ERR_NOT_EXEC: return "not an executable or shared object";
case ELF_ERR_WRONG_ARCH: return "not x86_64";
case ELF_ERR_NO_LOAD: return "no PT_LOAD segments";
case ELF_ERR_MAP_FAIL: return "vmm_map_page failed";
case ELF_ERR_NO_MEM: return "out of memory";
default: return "unknown error";
}
}
Binary file not shown.
+352
View File
@@ -0,0 +1,352 @@
#include <string.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include "../../include/fs/devfs.h"
#include "../../include/fs/vfs.h"
#include "../../include/memory/pmm.h"
#include "../../include/io/serial.h"
#include "../../include/drivers/ps2.h"
#include "../../include/sched/sched.h"
#include "../../include/apic/apic.h"
#include "../../include/smp/percpu.h"
extern void draw_cursor(void);
extern void erase_cursor(void);
extern uint32_t get_screen_width(void);
extern uint32_t get_screen_height(void);
extern uint32_t get_cursor_row(void);
extern uint32_t get_cursor_col(void);
#define TIOCGWINSZ 0x5413
#define TIOCGCURSOR 0x5480
#define TCGETS 0x5401
#define TCSETS 0x5402
#define TCSETSW 0x5403
#define TCSETSF 0x5404
#define TIOCSNONBLOCK 0x5481
static int tty_nonblock = 0;
#define T_ICANON 0x0002
#define T_ECHO 0x0008
#define T_ISIG 0x0001
struct cervus_winsize {
uint16_t ws_row;
uint16_t ws_col;
uint16_t ws_xpixel;
uint16_t ws_ypixel;
};
struct cervus_cursor_pos {
uint32_t row;
uint32_t col;
};
struct cervus_termios {
uint32_t c_iflag;
uint32_t c_oflag;
uint32_t c_cflag;
uint32_t c_lflag;
uint8_t c_cc[32];
};
static struct cervus_termios g_tty_termios = {
.c_iflag = 0,
.c_oflag = 0,
.c_cflag = 0,
.c_lflag = T_ICANON | T_ECHO | T_ISIG,
.c_cc = {0},
};
static inline int tty_is_canonical(void) {
return (g_tty_termios.c_lflag & T_ICANON) != 0;
}
static inline int tty_has_isig(void) {
return (g_tty_termios.c_lflag & T_ISIG) != 0;
}
static inline task_t* devfs_cur_task(void) {
percpu_t* pc = get_percpu();
return pc ? (task_t*)pc->current_task : NULL;
}
static int64_t tty_read(vnode_t *node, void *buf, size_t len, uint64_t offset) {
(void)node; (void)offset;
if (len == 0) return 0;
char *dst = buf;
uint64_t freq = hpet_is_available() ? hpet_get_frequency() : 0;
uint64_t half_tick = freq ? freq / 2 : 0;
uint64_t next_blink = half_tick ? (hpet_read_counter() + half_tick) : 0;
int cursor_on = 1;
int canonical = tty_is_canonical();
int isig = tty_has_isig();
if (tty_nonblock && kb_buf_empty()) return -EAGAIN;
draw_cursor();
while (kb_buf_empty()) {
task_t *me = devfs_cur_task();
if (me && me->pending_kill) {
erase_cursor();
return -EINTR;
}
if (half_tick) {
uint64_t now = hpet_read_counter();
if (now >= next_blink) {
if (cursor_on) { erase_cursor(); cursor_on = 0; }
else { draw_cursor(); cursor_on = 1; }
next_blink = now + half_tick;
}
}
task_yield();
}
erase_cursor();
task_t *me = devfs_cur_task();
if (me && me->pending_kill) {
kb_buf_consume_ctrlc();
return -EINTR;
}
char first = kb_buf_getc();
if (isig && canonical && first == 0x03) {
return -EINTR;
}
dst[0] = first;
size_t got = 1;
if (!canonical) {
while (got < len) {
char c;
if (!kb_buf_try_getc(&c)) break;
dst[got++] = c;
}
return (int64_t)got;
}
while (got < len) {
char c;
if (!kb_buf_try_getc(&c)) break;
if (isig && c == 0x03) break;
dst[got++] = c;
if (c == '\n') break;
}
return (int64_t)got;
}
static int64_t tty_write(vnode_t *node, const void *buf, size_t len, uint64_t offset) {
(void)node; (void)offset;
serial_writebuf((const char *)buf, len);
const unsigned char *p = (const unsigned char *)buf;
for (size_t i = 0; i < len; i++) putchar((int)p[i]);
return (int64_t)len;
}
static int64_t tty_ioctl(vnode_t *node, uint64_t req, void *arg) {
(void)node;
if (req == TIOCGWINSZ) {
if (!arg) return -EFAULT;
struct cervus_winsize *ws = (struct cervus_winsize *)arg;
uint32_t w = get_screen_width();
uint32_t h = get_screen_height();
ws->ws_col = (uint16_t)(w / 8);
ws->ws_row = (uint16_t)(h / 16);
ws->ws_xpixel = (uint16_t)w;
ws->ws_ypixel = (uint16_t)h;
return 0;
}
if (req == TIOCGCURSOR) {
if (!arg) return -EFAULT;
struct cervus_cursor_pos *cp = (struct cervus_cursor_pos *)arg;
cp->row = get_cursor_row();
cp->col = get_cursor_col();
return 0;
}
if (req == TIOCSNONBLOCK) {
if (!arg) return -EFAULT;
tty_nonblock = *((int *)arg) ? 1 : 0;
return 0;
}
if (req == TCGETS) {
if (!arg) return -EFAULT;
memcpy(arg, &g_tty_termios, sizeof(g_tty_termios));
return 0;
}
if (req == TCSETS || req == TCSETSW || req == TCSETSF) {
if (!arg) return -EFAULT;
memcpy(&g_tty_termios, arg, sizeof(g_tty_termios));
return 0;
}
return -ENOTTY;
}
static void devfs_ref(vnode_t *node) {
(void)node;
}
static void devfs_unref(vnode_t *node) {
(void)node;
}
static int devfs_stat(vnode_t *node, vfs_stat_t *out) {
memset(out, 0, sizeof(*out));
out->st_ino = node->ino;
out->st_type = node->type;
out->st_mode = node->mode;
return 0;
}
static const vnode_ops_t tty_ops = {
.read = tty_read,
.write = tty_write,
.ioctl = tty_ioctl,
.stat = devfs_stat,
.ref = devfs_ref,
.unref = devfs_unref,
};
static int64_t null_read(vnode_t *n, void *buf, size_t len, uint64_t off) {
(void)n; (void)buf; (void)len; (void)off;
return 0;
}
static int64_t null_write(vnode_t *n, const void *buf, size_t len, uint64_t off) {
(void)n; (void)buf; (void)off;
return (int64_t)len;
}
static const vnode_ops_t null_ops = {
.read = null_read,
.write = null_write,
.stat = devfs_stat,
.ref = devfs_ref,
.unref = devfs_unref,
};
static int64_t zero_read(vnode_t *n, void *buf, size_t len, uint64_t off) {
(void)n; (void)off;
memset(buf, 0, len);
return (int64_t)len;
}
static const vnode_ops_t zero_ops = {
.read = zero_read,
.write = null_write,
.stat = devfs_stat,
.ref = devfs_ref,
.unref = devfs_unref,
};
#define DEVFS_MAX_ENTRIES 32
typedef struct {
char name[VFS_MAX_NAME];
vnode_t *node;
} devfs_entry_t;
typedef struct {
devfs_entry_t entries[DEVFS_MAX_ENTRIES];
int count;
} devfs_dir_data_t;
static devfs_dir_data_t g_devdir;
static vnode_t g_devroot;
static vnode_t g_tty_node;
static vnode_t g_null_node;
static vnode_t g_zero_node;
static uint64_t g_devfs_ino = 100;
static int devfs_dir_lookup(vnode_t *dir, const char *name, vnode_t **out) {
(void)dir;
for (int i = 0; i < g_devdir.count; i++) {
if (strcmp(g_devdir.entries[i].name, name) == 0) {
vnode_ref(g_devdir.entries[i].node);
*out = g_devdir.entries[i].node;
return 0;
}
}
return -ENOENT;
}
static int devfs_dir_readdir(vnode_t *dir, uint64_t index, vfs_dirent_t *out) {
(void)dir;
if ((int64_t)index >= g_devdir.count) return -ENOENT;
devfs_entry_t *e = &g_devdir.entries[index];
out->d_ino = e->node->ino;
out->d_type = (uint8_t)e->node->type;
strncpy(out->d_name, e->name, VFS_MAX_NAME - 1);
out->d_name[VFS_MAX_NAME - 1] = '\0';
return 0;
}
static const vnode_ops_t devfs_dir_ops = {
.lookup = devfs_dir_lookup,
.readdir = devfs_dir_readdir,
.stat = devfs_stat,
.ref = devfs_ref,
.unref = devfs_unref,
};
void devfs_register(const char *name, vnode_t *node) {
if (g_devdir.count >= DEVFS_MAX_ENTRIES) return;
devfs_entry_t *e = &g_devdir.entries[g_devdir.count++];
strncpy(e->name, name, VFS_MAX_NAME - 1);
e->name[VFS_MAX_NAME - 1] = '\0';
e->node = node;
}
vnode_t *devfs_create_root(void) {
memset(&g_devdir, 0, sizeof(g_devdir));
memset(&g_devroot, 0, sizeof(g_devroot));
memset(&g_tty_node, 0, sizeof(g_tty_node));
memset(&g_null_node, 0, sizeof(g_null_node));
memset(&g_zero_node, 0, sizeof(g_zero_node));
g_devroot.type = VFS_NODE_DIR;
g_devroot.mode = 0755;
g_devroot.ino = g_devfs_ino++;
g_devroot.ops = &devfs_dir_ops;
g_devroot.fs_data = &g_devdir;
g_devroot.refcount = 1;
g_tty_node.type = VFS_NODE_CHARDEV;
g_tty_node.mode = 0666;
g_tty_node.ino = g_devfs_ino++;
g_tty_node.ops = &tty_ops;
g_tty_node.refcount = 1;
g_null_node.type = VFS_NODE_CHARDEV;
g_null_node.mode = 0666;
g_null_node.ino = g_devfs_ino++;
g_null_node.ops = &null_ops;
g_null_node.refcount = 1;
g_zero_node.type = VFS_NODE_CHARDEV;
g_zero_node.mode = 0666;
g_zero_node.ino = g_devfs_ino++;
g_zero_node.ops = &zero_ops;
g_zero_node.refcount = 1;
devfs_register("tty", &g_tty_node);
devfs_register("null", &g_null_node);
devfs_register("zero", &g_zero_node);
serial_writestring("[devfs] /dev/tty, /dev/null, /dev/zero registered\n");
return &g_devroot;
}
+935
View File
@@ -0,0 +1,935 @@
#include "../../include/fs/ext2.h"
#include "../../include/fs/vfs.h"
#include "../../include/drivers/blkdev.h"
#include "../../include/memory/pmm.h"
#include "../../include/io/serial.h"
#include "../../include/syscall/errno.h"
#include <string.h>
#include <stdio.h>
static int block_read(ext2_t *fs, uint32_t block, void *buf) {
return blkdev_read(fs->dev, (uint64_t)block * fs->block_size, buf, fs->block_size);
}
static int block_write(ext2_t *fs, uint32_t block, const void *buf) {
return blkdev_write(fs->dev, (uint64_t)block * fs->block_size, buf, fs->block_size);
}
static int sb_flush(ext2_t *fs) {
return blkdev_write(fs->dev, EXT2_SUPER_OFFSET, &fs->sb, sizeof(fs->sb));
}
static int gdt_flush(ext2_t *fs) {
uint32_t gdt_block = (fs->block_size == 1024) ? 2 : 1;
return blkdev_write(fs->dev, (uint64_t)gdt_block * fs->block_size,
fs->gdt, fs->groups_count * sizeof(ext2_group_desc_t));
}
static int inode_read(ext2_t *fs, uint32_t ino, ext2_inode_t *out) {
if (ino == 0) return -EINVAL;
uint32_t idx = ino - 1;
uint32_t group = idx / fs->sb.s_inodes_per_group;
uint32_t local = idx % fs->sb.s_inodes_per_group;
if (group >= fs->groups_count) return -EINVAL;
uint64_t off = (uint64_t)fs->gdt[group].bg_inode_table * fs->block_size
+ (uint64_t)local * fs->inode_size;
return blkdev_read(fs->dev, off, out, sizeof(*out));
}
static int inode_write(ext2_t *fs, uint32_t ino, const ext2_inode_t *in) {
if (ino == 0) return -EINVAL;
uint32_t idx = ino - 1;
uint32_t group = idx / fs->sb.s_inodes_per_group;
uint32_t local = idx % fs->sb.s_inodes_per_group;
if (group >= fs->groups_count) return -EINVAL;
uint64_t off = (uint64_t)fs->gdt[group].bg_inode_table * fs->block_size
+ (uint64_t)local * fs->inode_size;
return blkdev_write(fs->dev, off, in, sizeof(*in));
}
static bool bmp_test(const uint8_t *bmp, uint32_t bit) {
return (bmp[bit / 8] >> (bit % 8)) & 1;
}
static void bmp_set(uint8_t *bmp, uint32_t bit) {
bmp[bit / 8] |= (uint8_t)(1 << (bit % 8));
}
static void bmp_clear(uint8_t *bmp, uint32_t bit) {
bmp[bit / 8] &= (uint8_t)~(1 << (bit % 8));
}
static int32_t alloc_inode(ext2_t *fs) {
uint8_t *bmp = kmalloc(fs->block_size);
if (!bmp) return -ENOMEM;
for (uint32_t g = 0; g < fs->groups_count; g++) {
if (fs->gdt[g].bg_free_inodes_count == 0) continue;
block_read(fs, fs->gdt[g].bg_inode_bitmap, bmp);
for (uint32_t i = 0; i < fs->sb.s_inodes_per_group; i++) {
if (!bmp_test(bmp, i)) {
bmp_set(bmp, i);
block_write(fs, fs->gdt[g].bg_inode_bitmap, bmp);
fs->gdt[g].bg_free_inodes_count--;
fs->sb.s_free_inodes_count--;
fs->dirty = true;
kfree(bmp);
return (int32_t)(g * fs->sb.s_inodes_per_group + i + 1);
}
}
}
kfree(bmp);
return -ENOSPC;
}
static void free_inode(ext2_t *fs, uint32_t ino) {
uint32_t idx = ino - 1;
uint32_t group = idx / fs->sb.s_inodes_per_group;
uint32_t local = idx % fs->sb.s_inodes_per_group;
uint8_t *bmp = kmalloc(fs->block_size);
if (!bmp) return;
block_read(fs, fs->gdt[group].bg_inode_bitmap, bmp);
bmp_clear(bmp, local);
block_write(fs, fs->gdt[group].bg_inode_bitmap, bmp);
fs->gdt[group].bg_free_inodes_count++;
fs->sb.s_free_inodes_count++;
fs->dirty = true;
kfree(bmp);
}
static int32_t alloc_block(ext2_t *fs) {
uint8_t *bmp = kmalloc(fs->block_size);
if (!bmp) return -ENOMEM;
for (uint32_t g = 0; g < fs->groups_count; g++) {
if (fs->gdt[g].bg_free_blocks_count == 0) continue;
block_read(fs, fs->gdt[g].bg_block_bitmap, bmp);
for (uint32_t i = 0; i < fs->sb.s_blocks_per_group; i++) {
uint32_t abs_block = g * fs->sb.s_blocks_per_group + i + fs->sb.s_first_data_block;
if (abs_block >= fs->sb.s_blocks_count) break;
if (!bmp_test(bmp, i)) {
bmp_set(bmp, i);
block_write(fs, fs->gdt[g].bg_block_bitmap, bmp);
fs->gdt[g].bg_free_blocks_count--;
fs->sb.s_free_blocks_count--;
fs->dirty = true;
kfree(bmp);
return (int32_t)abs_block;
}
}
}
kfree(bmp);
return -ENOSPC;
}
static void free_block(ext2_t *fs, uint32_t blk) {
if (blk < fs->sb.s_first_data_block) return;
uint32_t adj = blk - fs->sb.s_first_data_block;
uint32_t group = adj / fs->sb.s_blocks_per_group;
uint32_t local = adj % fs->sb.s_blocks_per_group;
if (group >= fs->groups_count) return;
uint8_t *bmp = kmalloc(fs->block_size);
if (!bmp) return;
block_read(fs, fs->gdt[group].bg_block_bitmap, bmp);
bmp_clear(bmp, local);
block_write(fs, fs->gdt[group].bg_block_bitmap, bmp);
fs->gdt[group].bg_free_blocks_count++;
fs->sb.s_free_blocks_count++;
fs->dirty = true;
kfree(bmp);
}
static int32_t get_block_num(ext2_t *fs, ext2_inode_t *di, uint32_t file_block) {
if (file_block < EXT2_NDIR_BLOCKS)
return (int32_t)di->i_block[file_block];
uint32_t ppb = fs->ptrs_per_block;
file_block -= EXT2_NDIR_BLOCKS;
if (file_block < ppb) {
if (di->i_block[EXT2_IND_BLOCK] == 0) return 0;
uint32_t *ind = kmalloc(fs->block_size);
if (!ind) return -ENOMEM;
block_read(fs, di->i_block[EXT2_IND_BLOCK], ind);
int32_t ret = (int32_t)ind[file_block];
kfree(ind);
return ret;
}
file_block -= ppb;
if (file_block < ppb * ppb) {
if (di->i_block[EXT2_DIND_BLOCK] == 0) return 0;
uint32_t *dind = kmalloc(fs->block_size);
if (!dind) return -ENOMEM;
block_read(fs, di->i_block[EXT2_DIND_BLOCK], dind);
uint32_t i1 = file_block / ppb;
uint32_t i2 = file_block % ppb;
if (dind[i1] == 0) { kfree(dind); return 0; }
uint32_t ib = dind[i1];
kfree(dind);
uint32_t *ind = kmalloc(fs->block_size);
if (!ind) return -ENOMEM;
block_read(fs, ib, ind);
int32_t ret = (int32_t)ind[i2];
kfree(ind);
return ret;
}
return -EFBIG;
}
static int set_block_num(ext2_t *fs, ext2_inode_t *di, uint32_t file_block, uint32_t disk_block) {
if (file_block < EXT2_NDIR_BLOCKS) {
di->i_block[file_block] = disk_block;
return 0;
}
uint32_t ppb = fs->ptrs_per_block;
file_block -= EXT2_NDIR_BLOCKS;
if (file_block < ppb) {
if (di->i_block[EXT2_IND_BLOCK] == 0) {
int32_t nb = alloc_block(fs);
if (nb < 0) return nb;
di->i_block[EXT2_IND_BLOCK] = (uint32_t)nb;
uint8_t *z = kzalloc(fs->block_size);
block_write(fs, (uint32_t)nb, z);
kfree(z);
}
uint32_t *ind = kmalloc(fs->block_size);
if (!ind) return -ENOMEM;
block_read(fs, di->i_block[EXT2_IND_BLOCK], ind);
ind[file_block] = disk_block;
block_write(fs, di->i_block[EXT2_IND_BLOCK], ind);
kfree(ind);
return 0;
}
file_block -= ppb;
if (file_block < ppb * ppb) {
if (di->i_block[EXT2_DIND_BLOCK] == 0) {
int32_t nb = alloc_block(fs);
if (nb < 0) return nb;
di->i_block[EXT2_DIND_BLOCK] = (uint32_t)nb;
uint8_t *z = kzalloc(fs->block_size);
block_write(fs, (uint32_t)nb, z);
kfree(z);
}
uint32_t *dind = kmalloc(fs->block_size);
if (!dind) return -ENOMEM;
block_read(fs, di->i_block[EXT2_DIND_BLOCK], dind);
uint32_t i1 = file_block / ppb;
uint32_t i2 = file_block % ppb;
if (dind[i1] == 0) {
int32_t nb = alloc_block(fs);
if (nb < 0) { kfree(dind); return nb; }
dind[i1] = (uint32_t)nb;
block_write(fs, di->i_block[EXT2_DIND_BLOCK], dind);
uint8_t *z = kzalloc(fs->block_size);
block_write(fs, (uint32_t)nb, z);
kfree(z);
}
uint32_t ib = dind[i1];
kfree(dind);
uint32_t *ind = kmalloc(fs->block_size);
if (!ind) return -ENOMEM;
block_read(fs, ib, ind);
ind[i2] = disk_block;
block_write(fs, ib, ind);
kfree(ind);
return 0;
}
return -EFBIG;
}
static const vnode_ops_t ext2_file_ops;
static const vnode_ops_t ext2_dir_ops;
static vnode_t *ext2_make_vnode(ext2_t *fs, uint32_t ino, ext2_inode_t *di) {
vnode_t *v = kzalloc(sizeof(vnode_t));
if (!v) return NULL;
ext2_vdata_t *vd = kzalloc(sizeof(ext2_vdata_t));
if (!vd) { kfree(v); return NULL; }
vd->fs = fs;
vd->ino = ino;
if ((di->i_mode & 0xF000) == EXT2_S_IFDIR) {
v->type = VFS_NODE_DIR;
v->ops = &ext2_dir_ops;
} else {
v->type = VFS_NODE_FILE;
v->ops = &ext2_file_ops;
}
v->mode = di->i_mode & 0x0FFF;
v->uid = di->i_uid;
v->gid = di->i_gid;
v->ino = ino;
v->size = di->i_size;
v->fs_data = vd;
v->refcount = 1;
return v;
}
static int64_t ext2_file_read(vnode_t *node, void *buf, size_t len, uint64_t offset) {
ext2_vdata_t *vd = node->fs_data;
ext2_t *fs = vd->fs;
ext2_inode_t di;
int r = inode_read(fs, vd->ino, &di);
if (r < 0) return r;
if (offset >= di.i_size) return 0;
if (offset + len > di.i_size) len = di.i_size - (size_t)offset;
if (len == 0) return 0;
uint8_t *dst = (uint8_t *)buf;
size_t done = 0;
uint8_t *bb = kmalloc(fs->block_size);
if (!bb) return -ENOMEM;
while (done < len) {
uint32_t co = (uint32_t)(offset + done);
uint32_t fb = co / fs->block_size;
uint32_t bo = co % fs->block_size;
int32_t db = get_block_num(fs, &di, fb);
if (db <= 0) {
if (db < 0) { kfree(bb); return db; }
memset(bb, 0, fs->block_size);
} else {
r = block_read(fs, (uint32_t)db, bb);
if (r < 0) { kfree(bb); return r; }
}
size_t ch = fs->block_size - bo;
if (ch > len - done) ch = len - done;
memcpy(dst + done, bb + bo, ch);
done += ch;
}
kfree(bb);
return (int64_t)done;
}
static int64_t ext2_file_write(vnode_t *node, const void *buf, size_t len, uint64_t offset) {
ext2_vdata_t *vd = node->fs_data;
ext2_t *fs = vd->fs;
ext2_inode_t di;
int r = inode_read(fs, vd->ino, &di);
if (r < 0) return r;
const uint8_t *src = (const uint8_t *)buf;
size_t done = 0;
uint8_t *bb = kmalloc(fs->block_size);
if (!bb) return -ENOMEM;
while (done < len) {
uint32_t co = (uint32_t)(offset + done);
uint32_t fb = co / fs->block_size;
uint32_t bo = co % fs->block_size;
int32_t db = get_block_num(fs, &di, fb);
if (db == 0) {
int32_t nb = alloc_block(fs);
if (nb < 0) { kfree(bb); return (done > 0) ? (int64_t)done : nb; }
set_block_num(fs, &di, fb, (uint32_t)nb);
db = nb;
memset(bb, 0, fs->block_size);
di.i_blocks += fs->block_size / 512;
} else if (db < 0) {
kfree(bb); return db;
} else {
if (bo != 0 || (len - done) < fs->block_size)
block_read(fs, (uint32_t)db, bb);
}
size_t ch = fs->block_size - bo;
if (ch > len - done) ch = len - done;
memcpy(bb + bo, src + done, ch);
block_write(fs, (uint32_t)db, bb);
done += ch;
}
uint32_t ne = (uint32_t)(offset + done);
if (ne > di.i_size) { di.i_size = ne; node->size = ne; }
inode_write(fs, vd->ino, &di);
fs->dirty = true;
kfree(bb);
return (int64_t)done;
}
static void free_all_blocks(ext2_t *fs, ext2_inode_t *di) {
for (int i = 0; i < EXT2_NDIR_BLOCKS; i++) {
if (di->i_block[i]) { free_block(fs, di->i_block[i]); di->i_block[i] = 0; }
}
if (di->i_block[EXT2_IND_BLOCK]) {
uint32_t *ind = kmalloc(fs->block_size);
if (ind) {
block_read(fs, di->i_block[EXT2_IND_BLOCK], ind);
for (uint32_t i = 0; i < fs->ptrs_per_block; i++)
if (ind[i]) free_block(fs, ind[i]);
kfree(ind);
}
free_block(fs, di->i_block[EXT2_IND_BLOCK]);
di->i_block[EXT2_IND_BLOCK] = 0;
}
if (di->i_block[EXT2_DIND_BLOCK]) {
uint32_t *dind = kmalloc(fs->block_size);
if (dind) {
block_read(fs, di->i_block[EXT2_DIND_BLOCK], dind);
for (uint32_t i = 0; i < fs->ptrs_per_block; i++) {
if (dind[i]) {
uint32_t *ind = kmalloc(fs->block_size);
if (ind) {
block_read(fs, dind[i], ind);
for (uint32_t j = 0; j < fs->ptrs_per_block; j++)
if (ind[j]) free_block(fs, ind[j]);
kfree(ind);
}
free_block(fs, dind[i]);
}
}
kfree(dind);
}
free_block(fs, di->i_block[EXT2_DIND_BLOCK]);
di->i_block[EXT2_DIND_BLOCK] = 0;
}
di->i_blocks = 0;
}
static int ext2_file_truncate(vnode_t *node, uint64_t new_size) {
ext2_vdata_t *vd = node->fs_data;
ext2_t *fs = vd->fs;
ext2_inode_t di;
int r = inode_read(fs, vd->ino, &di);
if (r < 0) return r;
if (new_size == 0) free_all_blocks(fs, &di);
di.i_size = (uint32_t)new_size;
node->size = new_size;
inode_write(fs, vd->ino, &di);
fs->dirty = true;
return 0;
}
static int ext2_stat(vnode_t *node, vfs_stat_t *out) {
memset(out, 0, sizeof(*out));
out->st_ino = node->ino;
out->st_type = node->type;
out->st_mode = node->mode;
out->st_uid = node->uid;
out->st_gid = node->gid;
out->st_size = node->size;
out->st_blocks = (node->size + 511) / 512;
return 0;
}
static void ext2_vnode_ref(vnode_t *node) { (void)node; }
static void ext2_vnode_unref(vnode_t *node) {
if (node->fs_data) kfree(node->fs_data);
kfree(node);
}
static const vnode_ops_t ext2_file_ops = {
.read = ext2_file_read,
.write = ext2_file_write,
.truncate = ext2_file_truncate,
.stat = ext2_stat,
.ref = ext2_vnode_ref,
.unref = ext2_vnode_unref,
};
static int ext2_dir_lookup(vnode_t *dir, const char *name, vnode_t **out) {
ext2_vdata_t *vd = dir->fs_data;
ext2_t *fs = vd->fs;
ext2_inode_t di;
int r = inode_read(fs, vd->ino, &di);
if (r < 0) return r;
uint8_t *bb = kmalloc(fs->block_size);
if (!bb) return -ENOMEM;
size_t nl = strlen(name);
uint32_t ds = di.i_size;
uint32_t pos = 0, fb = 0;
while (pos < ds) {
int32_t db = get_block_num(fs, &di, fb);
if (db <= 0) { fb++; pos += fs->block_size; continue; }
block_read(fs, (uint32_t)db, bb);
uint32_t off = 0;
while (off < fs->block_size && (pos + off) < ds) {
ext2_dir_entry_t *de = (ext2_dir_entry_t *)(bb + off);
if (de->rec_len == 0) break;
if (de->inode != 0 && de->name_len == nl && memcmp(de->name, name, nl) == 0) {
ext2_inode_t ci;
inode_read(fs, de->inode, &ci);
*out = ext2_make_vnode(fs, de->inode, &ci);
kfree(bb);
return *out ? 0 : -ENOMEM;
}
off += de->rec_len;
}
fb++; pos += fs->block_size;
}
kfree(bb);
return -ENOENT;
}
static int ext2_dir_readdir(vnode_t *dir, uint64_t index, vfs_dirent_t *out) {
ext2_vdata_t *vd = dir->fs_data;
ext2_t *fs = vd->fs;
ext2_inode_t di;
int r = inode_read(fs, vd->ino, &di);
if (r < 0) return r;
uint8_t *bb = kmalloc(fs->block_size);
if (!bb) return -ENOMEM;
uint32_t ds = di.i_size, pos = 0, fb = 0;
uint64_t cur = 0;
while (pos < ds) {
int32_t db = get_block_num(fs, &di, fb);
if (db <= 0) { fb++; pos += fs->block_size; continue; }
block_read(fs, (uint32_t)db, bb);
uint32_t off = 0;
while (off < fs->block_size && (pos + off) < ds) {
ext2_dir_entry_t *de = (ext2_dir_entry_t *)(bb + off);
if (de->rec_len == 0) break;
if (de->inode != 0 && de->name_len > 0) {
bool skip = (de->name_len == 1 && de->name[0] == '.') ||
(de->name_len == 2 && de->name[0] == '.' && de->name[1] == '.');
if (!skip) {
if (cur == index) {
out->d_ino = de->inode;
out->d_type = (de->file_type == EXT2_FT_DIR) ? VFS_NODE_DIR : VFS_NODE_FILE;
size_t n = de->name_len;
if (n >= VFS_MAX_NAME) n = VFS_MAX_NAME - 1;
memcpy(out->d_name, de->name, n);
out->d_name[n] = '\0';
kfree(bb);
return 0;
}
cur++;
}
}
off += de->rec_len;
}
fb++; pos += fs->block_size;
}
kfree(bb);
return -ENOENT;
}
static int ext2_dir_add_entry(ext2_t *fs, uint32_t dir_ino, uint32_t child_ino,
uint8_t file_type, const char *name) {
if (!name || !name[0]) return -EINVAL;
ext2_inode_t di;
int r = inode_read(fs, dir_ino, &di);
if (r < 0) return r;
uint8_t nl = (uint8_t)strlen(name);
uint16_t need = (uint16_t)((8 + nl + 3) & ~3);
uint8_t *bb = kmalloc(fs->block_size);
if (!bb) return -ENOMEM;
uint32_t nb = (di.i_size + fs->block_size - 1) / fs->block_size;
for (uint32_t fb = 0; fb < nb; fb++) {
int32_t db = get_block_num(fs, &di, fb);
if (db <= 0) continue;
block_read(fs, (uint32_t)db, bb);
uint32_t off = 0;
while (off < fs->block_size) {
ext2_dir_entry_t *de = (ext2_dir_entry_t *)(bb + off);
if (de->rec_len == 0) break;
if (de->inode == 0 && de->rec_len >= need) {
de->inode = child_ino;
de->name_len = nl;
de->file_type = file_type;
memcpy(de->name, name, nl);
block_write(fs, (uint32_t)db, bb);
kfree(bb);
return 0;
}
uint16_t actual = (uint16_t)((8 + de->name_len + 3) & ~3);
uint16_t slack = de->rec_len - actual;
if (slack >= need) {
de->rec_len = actual;
ext2_dir_entry_t *ne = (ext2_dir_entry_t *)(bb + off + actual);
ne->inode = child_ino;
ne->rec_len = slack;
ne->name_len = nl;
ne->file_type = file_type;
memcpy(ne->name, name, nl);
block_write(fs, (uint32_t)db, bb);
kfree(bb);
return 0;
}
off += de->rec_len;
}
}
int32_t new_blk = alloc_block(fs);
if (new_blk < 0) { kfree(bb); return (int)new_blk; }
set_block_num(fs, &di, nb, (uint32_t)new_blk);
di.i_size += fs->block_size;
di.i_blocks += fs->block_size / 512;
memset(bb, 0, fs->block_size);
ext2_dir_entry_t *de = (ext2_dir_entry_t *)bb;
de->inode = child_ino;
de->rec_len = (uint16_t)fs->block_size;
de->name_len = nl;
de->file_type = file_type;
memcpy(de->name, name, nl);
block_write(fs, (uint32_t)new_blk, bb);
inode_write(fs, dir_ino, &di);
kfree(bb);
return 0;
}
static int ext2_dir_mkdir(vnode_t *dir, const char *name, uint32_t mode) {
if (!name || !name[0]) return -EINVAL;
ext2_vdata_t *vd = dir->fs_data;
ext2_t *fs = vd->fs;
vnode_t *ex = NULL;
if (ext2_dir_lookup(dir, name, &ex) == 0) { vnode_unref(ex); return -EEXIST; }
int32_t ino = alloc_inode(fs);
if (ino < 0) return (int)ino;
ext2_inode_t ndi;
memset(&ndi, 0, sizeof(ndi));
ndi.i_mode = EXT2_S_IFDIR | (uint16_t)(mode ? mode : 0755);
ndi.i_links_count = 2;
int32_t blk = alloc_block(fs);
if (blk < 0) { free_inode(fs, (uint32_t)ino); return (int)blk; }
ndi.i_block[0] = (uint32_t)blk;
ndi.i_size = fs->block_size;
ndi.i_blocks = fs->block_size / 512;
uint8_t *bb = kzalloc(fs->block_size);
if (!bb) { free_block(fs, (uint32_t)blk); free_inode(fs, (uint32_t)ino); return -ENOMEM; }
ext2_dir_entry_t *dot = (ext2_dir_entry_t *)bb;
dot->inode = (uint32_t)ino; dot->rec_len = 12; dot->name_len = 1;
dot->file_type = EXT2_FT_DIR; dot->name[0] = '.';
ext2_dir_entry_t *dotdot = (ext2_dir_entry_t *)(bb + 12);
dotdot->inode = vd->ino; dotdot->rec_len = (uint16_t)(fs->block_size - 12);
dotdot->name_len = 2; dotdot->file_type = EXT2_FT_DIR;
dotdot->name[0] = '.'; dotdot->name[1] = '.';
block_write(fs, (uint32_t)blk, bb);
kfree(bb);
inode_write(fs, (uint32_t)ino, &ndi);
int r = ext2_dir_add_entry(fs, vd->ino, (uint32_t)ino, EXT2_FT_DIR, name);
if (r < 0) { free_block(fs, (uint32_t)blk); free_inode(fs, (uint32_t)ino); return r; }
ext2_inode_t pdi;
inode_read(fs, vd->ino, &pdi);
pdi.i_links_count++;
inode_write(fs, vd->ino, &pdi);
uint32_t grp = ((uint32_t)ino - 1) / fs->sb.s_inodes_per_group;
fs->gdt[grp].bg_used_dirs_count++;
fs->dirty = true;
dir->size++;
return 0;
}
static int ext2_dir_create(vnode_t *dir, const char *name, uint32_t mode, vnode_t **out) {
if (!name || !name[0]) return -EINVAL;
ext2_vdata_t *vd = dir->fs_data;
ext2_t *fs = vd->fs;
if (ext2_dir_lookup(dir, name, out) == 0) return 0;
int32_t ino = alloc_inode(fs);
if (ino < 0) return (int)ino;
ext2_inode_t ndi;
memset(&ndi, 0, sizeof(ndi));
ndi.i_mode = EXT2_S_IFREG | (uint16_t)(mode ? mode : 0644);
ndi.i_links_count = 1;
inode_write(fs, (uint32_t)ino, &ndi);
int r = ext2_dir_add_entry(fs, vd->ino, (uint32_t)ino, EXT2_FT_REG_FILE, name);
if (r < 0) { free_inode(fs, (uint32_t)ino); return r; }
*out = ext2_make_vnode(fs, (uint32_t)ino, &ndi);
if (!*out) return -ENOMEM;
fs->dirty = true;
dir->size++;
return 0;
}
static int ext2_dir_unlink(vnode_t *dir, const char *name) {
ext2_vdata_t *vd = dir->fs_data;
ext2_t *fs = vd->fs;
ext2_inode_t di;
int r = inode_read(fs, vd->ino, &di);
if (r < 0) return r;
uint8_t *bb = kmalloc(fs->block_size);
if (!bb) return -ENOMEM;
size_t nl = strlen(name);
uint32_t nblocks = (di.i_size + fs->block_size - 1) / fs->block_size;
for (uint32_t fb = 0; fb < nblocks; fb++) {
int32_t db = get_block_num(fs, &di, fb);
if (db <= 0) continue;
block_read(fs, (uint32_t)db, bb);
uint32_t off = 0;
ext2_dir_entry_t *prev = NULL;
while (off < fs->block_size) {
ext2_dir_entry_t *de = (ext2_dir_entry_t *)(bb + off);
if (de->rec_len == 0) break;
if (de->inode != 0 && de->name_len == nl && memcmp(de->name, name, nl) == 0) {
uint32_t cino = de->inode;
ext2_inode_t ci;
inode_read(fs, cino, &ci);
bool is_dir = ((ci.i_mode & 0xF000) == EXT2_S_IFDIR);
free_all_blocks(fs, &ci);
free_inode(fs, cino);
if (prev) prev->rec_len += de->rec_len;
else de->inode = 0;
block_write(fs, (uint32_t)db, bb);
if (is_dir) {
ext2_inode_t pdi;
inode_read(fs, vd->ino, &pdi);
if (pdi.i_links_count > 1) pdi.i_links_count--;
inode_write(fs, vd->ino, &pdi);
uint32_t grp = (cino - 1) / fs->sb.s_inodes_per_group;
if (fs->gdt[grp].bg_used_dirs_count > 0)
fs->gdt[grp].bg_used_dirs_count--;
}
dir->size--;
fs->dirty = true;
kfree(bb);
return 0;
}
prev = de;
off += de->rec_len;
}
}
kfree(bb);
return -ENOENT;
}
static int ext2_dir_rename(vnode_t *src_dir, const char *src_name,
vnode_t *dst_dir, const char *dst_name) {
ext2_vdata_t *svd = src_dir->fs_data;
ext2_t *fs = svd->fs;
vnode_t *child = NULL;
int r = ext2_dir_lookup(src_dir, src_name, &child);
if (r < 0) return r;
uint8_t ft = (child->type == VFS_NODE_DIR) ? EXT2_FT_DIR : EXT2_FT_REG_FILE;
uint32_t cino = (uint32_t)child->ino;
vnode_unref(child);
vnode_t *ex = NULL;
if (ext2_dir_lookup(dst_dir, dst_name, &ex) == 0) {
vnode_unref(ex);
ext2_dir_unlink(dst_dir, dst_name);
}
ext2_vdata_t *dvd = dst_dir->fs_data;
r = ext2_dir_add_entry(fs, dvd->ino, cino, ft, dst_name);
if (r < 0) return r;
ext2_inode_t sdi;
inode_read(fs, svd->ino, &sdi);
uint8_t *bb = kmalloc(fs->block_size);
if (!bb) return -ENOMEM;
size_t snl = strlen(src_name);
uint32_t nblocks = (sdi.i_size + fs->block_size - 1) / fs->block_size;
for (uint32_t fb = 0; fb < nblocks; fb++) {
int32_t db = get_block_num(fs, &sdi, fb);
if (db <= 0) continue;
block_read(fs, (uint32_t)db, bb);
uint32_t off = 0;
ext2_dir_entry_t *prev = NULL;
while (off < fs->block_size) {
ext2_dir_entry_t *de = (ext2_dir_entry_t *)(bb + off);
if (de->rec_len == 0) break;
if (de->inode == cino && de->name_len == snl &&
memcmp(de->name, src_name, snl) == 0) {
if (prev) prev->rec_len += de->rec_len;
else de->inode = 0;
block_write(fs, (uint32_t)db, bb);
kfree(bb);
fs->dirty = true;
return 0;
}
prev = de;
off += de->rec_len;
}
}
kfree(bb);
fs->dirty = true;
return 0;
}
static const vnode_ops_t ext2_dir_ops = {
.lookup = ext2_dir_lookup,
.readdir = ext2_dir_readdir,
.mkdir = ext2_dir_mkdir,
.create = ext2_dir_create,
.unlink = ext2_dir_unlink,
.rename = ext2_dir_rename,
.stat = ext2_stat,
.ref = ext2_vnode_ref,
.unref = ext2_vnode_unref,
};
int ext2_format(blkdev_t *dev, const char *label) {
if (!dev) return -EINVAL;
uint64_t disk_bytes = dev->size_bytes;
if (disk_bytes < 64 * 1024) return -ENOSPC;
uint32_t block_size = 1024;
uint32_t total_blocks = (uint32_t)(disk_bytes / block_size);
uint32_t blocks_per_group = block_size * 8;
uint32_t inodes_per_group = (total_blocks < 8192) ? 128 : 256;
uint32_t groups_count = (total_blocks + blocks_per_group - 1) / blocks_per_group;
if (groups_count == 0) groups_count = 1;
uint32_t total_inodes = inodes_per_group * groups_count;
uint32_t inode_size = 128;
uint32_t it_blocks_pg = (inodes_per_group * inode_size + block_size - 1) / block_size;
uint32_t first_data_block = 1;
uint8_t *zb = kzalloc(block_size);
if (!zb) return -ENOMEM;
for (uint32_t b = 0; b < total_blocks && b < 16; b++)
blkdev_write(dev, (uint64_t)b * block_size, zb, block_size);
ext2_superblock_t sb;
memset(&sb, 0, sizeof(sb));
sb.s_inodes_count = total_inodes;
sb.s_blocks_count = total_blocks;
sb.s_r_blocks_count = total_blocks / 20;
sb.s_free_blocks_count = total_blocks;
sb.s_free_inodes_count = total_inodes;
sb.s_first_data_block = first_data_block;
sb.s_log_block_size = 0;
sb.s_blocks_per_group = blocks_per_group;
sb.s_frags_per_group = blocks_per_group;
sb.s_inodes_per_group = inodes_per_group;
sb.s_magic = EXT2_SUPER_MAGIC;
sb.s_state = EXT2_VALID_FS;
sb.s_errors = 1;
sb.s_rev_level = EXT2_DYNAMIC_REV;
sb.s_first_ino = 11;
sb.s_inode_size = (uint16_t)inode_size;
sb.s_max_mnt_count = 20;
if (label) strncpy(sb.s_volume_name, label, 15);
uint32_t gdt_block = first_data_block + 1;
uint32_t gdt_blocks = (groups_count * sizeof(ext2_group_desc_t) + block_size - 1) / block_size;
ext2_group_desc_t *gdt = kzalloc(gdt_blocks * block_size);
if (!gdt) { kfree(zb); return -ENOMEM; }
uint32_t used_total = first_data_block;
printf(" ext2: ");
uint32_t last_pct_ext2 = 999;
uint32_t spinner_ext2 = 0;
for (uint32_t g = 0; g < groups_count; g++) {
uint32_t gs = g * blocks_per_group + first_data_block;
uint32_t cb = (g == 0) ? (gdt_block + gdt_blocks) : gs;
gdt[g].bg_block_bitmap = cb++;
gdt[g].bg_inode_bitmap = cb++;
gdt[g].bg_inode_table = cb;
cb += it_blocks_pg;
uint32_t meta = cb - gs;
if (g == 0) meta = cb - first_data_block;
uint32_t gb = blocks_per_group;
if (g == groups_count - 1) gb = total_blocks - gs;
gdt[g].bg_free_blocks_count = (gb > meta) ? (uint16_t)(gb - meta) : 0;
gdt[g].bg_free_inodes_count = (uint16_t)inodes_per_group;
used_total += meta;
for (uint32_t t = 0; t < it_blocks_pg; t++)
blkdev_write(dev, (uint64_t)(gdt[g].bg_inode_table + t) * block_size, zb, block_size);
uint8_t *bbmp = kzalloc(block_size);
for (uint32_t i = 0; i < meta; i++) bmp_set(bbmp, i);
if (g == 0)
for (uint32_t i = 0; i < (gdt_block + gdt_blocks - first_data_block); i++)
bmp_set(bbmp, i);
for (uint32_t i = gb; i < blocks_per_group; i++) bmp_set(bbmp, i);
blkdev_write(dev, (uint64_t)gdt[g].bg_block_bitmap * block_size, bbmp, block_size);
kfree(bbmp);
uint8_t *ibmp = kzalloc(block_size);
if (g == 0) {
for (uint32_t i = 0; i < 11; i++) { bmp_set(ibmp, i); gdt[g].bg_free_inodes_count--; }
}
for (uint32_t i = inodes_per_group; i < block_size * 8; i++) bmp_set(ibmp, i);
blkdev_write(dev, (uint64_t)gdt[g].bg_inode_bitmap * block_size, ibmp, block_size);
kfree(ibmp);
uint32_t pct = ((g + 1) * 100) / groups_count;
if (pct != last_pct_ext2) {
static const char glyphs[4] = { '|', '/', '-', '\\' };
printf("\r \r %c ext2: %u%% (group %u/%u)",
glyphs[spinner_ext2 & 3], pct, g + 1, groups_count);
spinner_ext2++;
last_pct_ext2 = pct;
}
}
printf("\r \r ext2: done\n");
sb.s_free_blocks_count = total_blocks - used_total;
sb.s_free_inodes_count = total_inodes - 11;
int32_t root_blk = -1;
{
uint8_t *bbmp = kmalloc(block_size);
blkdev_read(dev, (uint64_t)gdt[0].bg_block_bitmap * block_size, bbmp, block_size);
for (uint32_t i = 0; i < blocks_per_group; i++) {
if (!bmp_test(bbmp, i)) {
bmp_set(bbmp, i);
blkdev_write(dev, (uint64_t)gdt[0].bg_block_bitmap * block_size, bbmp, block_size);
root_blk = (int32_t)(i + first_data_block);
gdt[0].bg_free_blocks_count--;
sb.s_free_blocks_count--;
break;
}
}
kfree(bbmp);
}
if (root_blk < 0) { kfree(gdt); kfree(zb); return -ENOSPC; }
ext2_inode_t ri;
memset(&ri, 0, sizeof(ri));
ri.i_mode = EXT2_S_IFDIR | 0755;
ri.i_links_count = 2;
ri.i_block[0] = (uint32_t)root_blk;
ri.i_size = block_size;
ri.i_blocks = block_size / 512;
uint64_t roff = (uint64_t)gdt[0].bg_inode_table * block_size + (uint64_t)(EXT2_ROOT_INO - 1) * inode_size;
blkdev_write(dev, roff, &ri, sizeof(ri));
uint8_t *rdb = kzalloc(block_size);
ext2_dir_entry_t *dot = (ext2_dir_entry_t *)rdb;
dot->inode = EXT2_ROOT_INO; dot->rec_len = 12; dot->name_len = 1;
dot->file_type = EXT2_FT_DIR; dot->name[0] = '.';
ext2_dir_entry_t *dotdot = (ext2_dir_entry_t *)(rdb + 12);
dotdot->inode = EXT2_ROOT_INO; dotdot->rec_len = (uint16_t)(block_size - 12);
dotdot->name_len = 2; dotdot->file_type = EXT2_FT_DIR;
dotdot->name[0] = '.'; dotdot->name[1] = '.';
blkdev_write(dev, (uint64_t)root_blk * block_size, rdb, block_size);
kfree(rdb);
gdt[0].bg_used_dirs_count = 1;
blkdev_write(dev, EXT2_SUPER_OFFSET, &sb, sizeof(sb));
blkdev_write(dev, (uint64_t)gdt_block * block_size, gdt, gdt_blocks * block_size);
kfree(gdt);
kfree(zb);
serial_printf("[ext2] formatted: %u blocks (%u KiB), %u inodes, bs=%u, label='%s'\n",
total_blocks, total_blocks * block_size / 1024, total_inodes, block_size,
label ? label : "");
return 0;
}
vnode_t *ext2_mount(blkdev_t *dev) {
if (!dev) return NULL;
ext2_t *fs = kzalloc(sizeof(ext2_t));
if (!fs) return NULL;
fs->dev = dev;
if (blkdev_read(dev, EXT2_SUPER_OFFSET, &fs->sb, sizeof(fs->sb)) < 0) { kfree(fs); return NULL; }
if (fs->sb.s_magic != EXT2_SUPER_MAGIC) {
serial_printf("[ext2] bad magic: 0x%x (expected 0x%x)\n", fs->sb.s_magic, EXT2_SUPER_MAGIC);
kfree(fs);
return NULL;
}
fs->block_size = 1024 << fs->sb.s_log_block_size;
fs->inode_size = (fs->sb.s_rev_level >= EXT2_DYNAMIC_REV) ? fs->sb.s_inode_size : 128;
fs->groups_count = (fs->sb.s_blocks_count + fs->sb.s_blocks_per_group - 1) / fs->sb.s_blocks_per_group;
fs->inodes_per_block = fs->block_size / fs->inode_size;
fs->ptrs_per_block = fs->block_size / 4;
uint32_t gdt_block = (fs->block_size == 1024) ? 2 : 1;
uint32_t gdt_sz = fs->groups_count * sizeof(ext2_group_desc_t);
uint32_t gdt_blocks = (gdt_sz + fs->block_size - 1) / fs->block_size;
fs->gdt = kmalloc(gdt_blocks * fs->block_size);
if (!fs->gdt) { kfree(fs); return NULL; }
if (blkdev_read(dev, (uint64_t)gdt_block * fs->block_size, fs->gdt, gdt_blocks * fs->block_size) < 0) {
kfree(fs->gdt); kfree(fs); return NULL;
}
ext2_inode_t root_di;
if (inode_read(fs, EXT2_ROOT_INO, &root_di) < 0) { kfree(fs->gdt); kfree(fs); return NULL; }
vnode_t *root = ext2_make_vnode(fs, EXT2_ROOT_INO, &root_di);
if (!root) { kfree(fs->gdt); kfree(fs); return NULL; }
serial_printf("[ext2] mounted '%s': %u blocks (%u free), %u inodes (%u free), bs=%u\n",
fs->sb.s_volume_name, fs->sb.s_blocks_count, fs->sb.s_free_blocks_count,
fs->sb.s_inodes_count, fs->sb.s_free_inodes_count, fs->block_size);
return root;
}
void ext2_sync(ext2_t *fs) {
if (!fs || !fs->dirty) return;
sb_flush(fs);
gdt_flush(fs);
if (fs->dev->ops && fs->dev->ops->flush) fs->dev->ops->flush(fs->dev);
fs->dirty = false;
}
void ext2_unmount(ext2_t *fs) {
if (!fs) return;
ext2_sync(fs);
if (fs->gdt) kfree(fs->gdt);
kfree(fs);
}
int ext2_statvfs(vnode_t *root, vfs_statvfs_t *out) {
if (!root || !root->fs_data || !out) return -EINVAL;
ext2_vdata_t *vd = (ext2_vdata_t *)root->fs_data;
ext2_t *fs = vd->fs;
if (!fs) return -EINVAL;
out->f_bsize = fs->block_size;
out->f_blocks = fs->sb.s_blocks_count;
out->f_bfree = fs->sb.s_free_blocks_count;
out->f_bavail = fs->sb.s_free_blocks_count;
out->f_files = fs->sb.s_inodes_count;
out->f_ffree = fs->sb.s_free_inodes_count;
out->f_flag = 0;
out->f_namemax = 255;
return 0;
}
File diff suppressed because it is too large Load Diff
+311
View File
@@ -0,0 +1,311 @@
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include "../../include/fs/initramfs.h"
#include "../../include/fs/vfs.h"
#include "../../include/io/serial.h"
typedef struct {
char name[100];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char checksum[8];
char typeflag;
char linkname[100];
char magic[6];
char version[2];
char uname[32];
char gname[32];
char devmajor[8];
char devminor[8];
char prefix[155];
char _pad[12];
} __attribute__((packed)) ustar_header_t;
#define USTAR_BLOCK 512
static uint64_t octal_parse(const char *s, size_t n) {
uint64_t v = 0;
for (size_t i = 0; i < n && s[i] >= '0' && s[i] <= '7'; i++)
v = v * 8 + (uint64_t)(s[i] - '0');
return v;
}
static size_t align_block(size_t n) {
return (n + USTAR_BLOCK - 1) & ~(size_t)(USTAR_BLOCK - 1);
}
static void path_normalize(const char *in, char *out, size_t maxlen) {
if (maxlen == 0) return;
const char *p = in;
while (p[0] == '.' && p[1] == '/') p += 2;
while (*p == '/') p++;
if (*p == '\0' || (p[0] == '.' && p[1] == '\0')) {
out[0] = '/';
out[1] = '\0';
return;
}
char tmp[VFS_MAX_PATH];
size_t j = 0;
tmp[j++] = '/';
while (*p && j < sizeof(tmp) - 1) {
const char *start = p;
while (*p && *p != '/') p++;
size_t len = (size_t)(p - start);
if (len == 1 && start[0] == '.') {
while (*p == '/') p++;
continue;
}
if (j > 1) {
if (j < sizeof(tmp) - 1)
tmp[j++] = '/';
}
size_t avail = sizeof(tmp) - 1 - j;
size_t copy = len < avail ? len : avail;
memcpy(tmp + j, start, copy);
j += copy;
while (*p == '/') p++;
}
tmp[j] = '\0';
strncpy(out, tmp, maxlen - 1);
out[maxlen - 1] = '\0';
}
static int mkdir_p(const char *abspath) {
if (!abspath || strcmp(abspath, "/") == 0) return 0;
char buf[VFS_MAX_PATH];
strncpy(buf, abspath, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
for (char *p = buf + 1; *p; p++) {
if (*p == '/') {
*p = '\0';
int r = vfs_mkdir(buf, 0755);
if (r < 0 && r != -EEXIST) {
serial_printf("[initramfs] mkdir_p: mkdir '%s' failed: %d\n", buf, r);
*p = '/';
return r;
}
*p = '/';
}
}
int r = vfs_mkdir(buf, 0755);
if (r < 0 && r != -EEXIST) {
serial_printf("[initramfs] mkdir_p: mkdir '%s' failed: %d\n", buf, r);
return r;
}
return 0;
}
static int write_file(const char *abspath, const void *data,
size_t size, uint32_t mode)
{
serial_printf("[initramfs] write_file '%s' size=%zu\n", abspath, size);
char parent[VFS_MAX_PATH];
strncpy(parent, abspath, sizeof(parent) - 1);
parent[sizeof(parent) - 1] = '\0';
char *last_slash = NULL;
for (int i = (int)strlen(parent) - 1; i > 0; i--) {
if (parent[i] == '/') { last_slash = &parent[i]; break; }
}
if (last_slash) {
*last_slash = '\0';
if (strlen(parent) > 0) {
serial_printf("[initramfs] ensuring parent dir '%s'\n", parent);
vnode_t *check = NULL;
int lr = vfs_lookup(parent, &check);
serial_printf("[initramfs] vfs_lookup('%s') before mkdir_p = %d\n", parent, lr);
if (lr == 0) {
serial_printf("[initramfs] parent exists, type=%d\n", check->type);
vnode_unref(check);
} else {
serial_printf("[initramfs] parent not found, calling mkdir_p\n");
int mr = mkdir_p(parent);
if (mr < 0) {
serial_printf("[initramfs] mkdir_p('%s') failed: %d\n", parent, mr);
*last_slash = '/';
return mr;
}
lr = vfs_lookup(parent, &check);
serial_printf("[initramfs] vfs_lookup('%s') after mkdir_p = %d\n", parent, lr);
if (lr == 0) {
serial_printf("[initramfs] parent now exists, type=%d\n", check->type);
vnode_unref(check);
} else {
serial_printf("[initramfs] ERROR: parent still not found after mkdir_p!\n");
*last_slash = '/';
return -ENOENT;
}
}
}
*last_slash = '/';
}
vfs_file_t *file = NULL;
int ret = vfs_open(abspath, O_WRONLY | O_CREAT | O_TRUNC, mode ? mode : 0644, &file);
serial_printf("[initramfs] vfs_open('%s') = %d\n", abspath, ret);
if (ret < 0) {
serial_printf("[initramfs] open '%s' failed: %d\n", abspath, ret);
return ret;
}
if (size > 0 && data != NULL) {
int64_t w = vfs_write(file, data, size);
vfs_close(file);
if (w < 0) {
serial_printf("[initramfs] write '%s' failed: %lld\n",
abspath, (long long)w);
return (int)w;
}
serial_printf("[initramfs] wrote %lld bytes\n", (long long)w);
} else {
vfs_close(file);
}
return 0;
}
int initramfs_mount(const void *data, size_t size) {
if (!data || size < USTAR_BLOCK) return -EINVAL;
const uint8_t *ptr = data;
const uint8_t *end = ptr + size;
int files_ok = 0, dirs_ok = 0, skipped = 0, errors = 0;
serial_printf("[initramfs] parsing TAR @ %p, size=%zu\n", data, size);
{
vnode_t *root_check = NULL;
int r = vfs_lookup("/", &root_check);
serial_printf("[initramfs] pre-parse: vfs_lookup('/') = %d\n", r);
if (r == 0) {
serial_printf("[initramfs] pre-parse: root type=%d refcnt=%d\n",
root_check->type, root_check->refcount);
vnode_unref(root_check);
}
const char *test_dirs[] = { "/bin", "/etc", "/dev", "/tmp", "/proc", NULL };
for (int i = 0; test_dirs[i]; i++) {
vnode_t *n = NULL;
int rv = vfs_lookup(test_dirs[i], &n);
serial_printf("[initramfs] pre-parse: vfs_lookup('%s') = %d\n", test_dirs[i], rv);
if (rv == 0) vnode_unref(n);
}
}
while (ptr + USTAR_BLOCK <= end) {
const ustar_header_t *hdr = (const ustar_header_t *)ptr;
if (hdr->name[0] == '\0') {
ptr += USTAR_BLOCK;
if (ptr + USTAR_BLOCK <= end &&
((const ustar_header_t *)ptr)->name[0] == '\0')
break;
continue;
}
if (memcmp(hdr->magic, "ustar", 5) != 0) {
serial_writestring("[initramfs] bad magic, stopping\n");
break;
}
uint64_t file_size = octal_parse(hdr->size, sizeof(hdr->size));
uint32_t mode = (uint32_t)octal_parse(hdr->mode, sizeof(hdr->mode));
char raw[VFS_MAX_PATH];
if (hdr->prefix[0]) {
size_t pl = strnlen(hdr->prefix, sizeof(hdr->prefix));
size_t nl = strnlen(hdr->name, sizeof(hdr->name));
if (pl + nl + 2 < sizeof(raw)) {
memcpy(raw, hdr->prefix, pl);
raw[pl] = '/';
memcpy(raw + pl + 1, hdr->name, nl);
raw[pl + 1 + nl] = '\0';
} else {
strncpy(raw, hdr->name, sizeof(raw) - 1);
raw[sizeof(raw) - 1] = '\0';
}
} else {
strncpy(raw, hdr->name, sizeof(raw) - 1);
raw[sizeof(raw) - 1] = '\0';
}
char abspath[VFS_MAX_PATH];
path_normalize(raw, abspath, sizeof(abspath));
serial_printf("[initramfs] entry: raw='%s' -> abs='%s' type='%c' size=%llu\n",
raw, abspath, hdr->typeflag ? hdr->typeflag : '0',
(unsigned long long)file_size);
ptr += USTAR_BLOCK;
switch (hdr->typeflag) {
case '5':
if (strcmp(abspath, "/") != 0) {
int r = mkdir_p(abspath);
if (r == 0) {
serial_printf("[initramfs] dir %s\n", abspath);
dirs_ok++;
} else {
serial_printf("[initramfs] dir %s FAILED: %d\n", abspath, r);
errors++;
}
}
break;
case '0':
case '\0':
{
const void *filedata = (file_size > 0) ? (const void *)ptr : NULL;
int r = write_file(abspath, filedata, (size_t)file_size,
mode ? mode : 0644);
if (r == 0) {
serial_printf("[initramfs] file %s (%llu bytes)\n",
abspath, (unsigned long long)file_size);
files_ok++;
} else {
errors++;
}
break;
}
case '2':
serial_printf("[initramfs] skip symlink %s\n", abspath);
skipped++;
break;
default:
serial_printf("[initramfs] skip type='%c' %s\n",
hdr->typeflag ? hdr->typeflag : '0', abspath);
skipped++;
break;
}
ptr += align_block(file_size);
}
serial_printf("[initramfs] done: %d files, %d dirs, %d skipped, %d errors\n",
files_ok, dirs_ok, skipped, errors);
return (errors > 0 && files_ok == 0 && dirs_ok == 0) ? -EIO : 0;
}
+283
View File
@@ -0,0 +1,283 @@
#include <string.h>
#include <stddef.h>
#include <stdint.h>
#include "../../include/fs/ramfs.h"
#include "../../include/fs/vfs.h"
#include "../../include/memory/pmm.h"
#include "../../include/io/serial.h"
typedef struct {
char *name;
vnode_t *node;
} ramfs_child_t;
typedef struct {
uint8_t *data;
size_t capacity;
ramfs_child_t *children;
int child_count;
int child_cap;
uint64_t ino;
} ramfs_node_t;
static uint64_t g_next_ino = 1;
static const vnode_ops_t ramfs_file_ops;
static const vnode_ops_t ramfs_dir_ops;
static vnode_t *ramfs_alloc_vnode(vnode_type_t type, uint32_t mode) {
vnode_t *v = kzalloc(sizeof(vnode_t));
if (!v) return NULL;
ramfs_node_t *rn = kzalloc(sizeof(ramfs_node_t));
if (!rn) { kfree(v); return NULL; }
rn->ino = g_next_ino++;
v->type = type;
v->mode = mode;
v->ino = rn->ino;
v->fs_data = rn;
return v;
}
static void ramfs_ref(vnode_t *node) {
(void)node;
}
static void ramfs_unref(vnode_t *node) {
ramfs_node_t *rn = node->fs_data;
if (rn->data) kfree(rn->data);
if (rn->children) {
for (int i = 0; i < rn->child_count; i++) {
if (rn->children[i].name) kfree(rn->children[i].name);
if (rn->children[i].node) vnode_unref(rn->children[i].node);
}
kfree(rn->children);
}
kfree(rn);
kfree(node);
}
static int64_t ramfs_file_read(vnode_t *node, void *buf,
size_t len, uint64_t offset)
{
ramfs_node_t *rn = node->fs_data;
if (offset >= node->size) return 0;
size_t avail = node->size - (size_t)offset;
if (len > avail) len = avail;
if (len == 0) return 0;
memcpy(buf, rn->data + offset, len);
return (int64_t)len;
}
static int64_t ramfs_file_write(vnode_t *node, const void *buf,
size_t len, uint64_t offset)
{
ramfs_node_t *rn = node->fs_data;
size_t end = (size_t)offset + len;
if (end > RAMFS_MAX_FILE_SIZE) return -EFBIG;
if (end > rn->capacity) {
size_t newcap = (end + 4095) & ~(size_t)4095;
if (newcap > RAMFS_MAX_FILE_SIZE) newcap = RAMFS_MAX_FILE_SIZE;
uint8_t *newdata = kmalloc(newcap);
if (!newdata) return -ENOMEM;
size_t copy_size = rn->data ? node->size : 0;
if (copy_size > 0)
memcpy(newdata, rn->data, copy_size);
memset(newdata + copy_size, 0, newcap - copy_size);
if (rn->data) kfree(rn->data);
rn->data = newdata;
rn->capacity = newcap;
}
memcpy(rn->data + offset, buf, len);
if (end > node->size) node->size = end;
return (int64_t)len;
}
static int ramfs_file_truncate(vnode_t *node, uint64_t new_size) {
ramfs_node_t *rn = node->fs_data;
if (new_size == 0) {
if (rn->data) {
kfree(rn->data);
rn->data = NULL;
rn->capacity = 0;
}
node->size = 0;
} else if (new_size < node->size) {
node->size = new_size;
}
return 0;
}
static int ramfs_stat(vnode_t *node, vfs_stat_t *out) {
ramfs_node_t *rn = node->fs_data;
out->st_ino = rn->ino;
out->st_type = node->type;
out->st_mode = node->mode;
out->st_uid = node->uid;
out->st_gid = node->gid;
out->st_size = node->size;
out->st_blocks = (node->size + 511) / 512;
return 0;
}
static const vnode_ops_t ramfs_file_ops = {
.read = ramfs_file_read,
.write = ramfs_file_write,
.truncate = ramfs_file_truncate,
.stat = ramfs_stat,
.ref = ramfs_ref,
.unref = ramfs_unref,
};
static int ramfs_dir_grow(ramfs_node_t *rn) {
int newcap = rn->child_cap == 0 ? 8 : rn->child_cap * 2;
if (newcap > RAMFS_MAX_CHILDREN) return -ENOSPC;
ramfs_child_t *nb = kmalloc((size_t)newcap * sizeof(ramfs_child_t));
if (!nb) return -ENOMEM;
if (rn->children && rn->child_count > 0)
memcpy(nb, rn->children, (size_t)rn->child_count * sizeof(ramfs_child_t));
if (rn->children) kfree(rn->children);
rn->children = nb;
rn->child_cap = newcap;
return 0;
}
static int ramfs_dir_lookup(vnode_t *dir, const char *name, vnode_t **out) {
ramfs_node_t *rn = dir->fs_data;
for (int i = 0; i < rn->child_count; i++) {
if (strcmp(rn->children[i].name, name) == 0) {
vnode_ref(rn->children[i].node);
*out = rn->children[i].node;
return 0;
}
}
return -ENOENT;
}
static int ramfs_dir_readdir(vnode_t *dir, uint64_t index, vfs_dirent_t *out) {
ramfs_node_t *rn = dir->fs_data;
if ((int64_t)index >= rn->child_count) return -ENOENT;
ramfs_child_t *ch = &rn->children[index];
out->d_ino = ch->node->ino;
out->d_type = (uint8_t)ch->node->type;
strncpy(out->d_name, ch->name, VFS_MAX_NAME - 1);
out->d_name[VFS_MAX_NAME - 1] = '\0';
return 0;
}
static int ramfs_dir_add_child(vnode_t *dir, const char *name, vnode_t *child) {
ramfs_node_t *rn = dir->fs_data;
if (rn->child_count >= rn->child_cap) {
int r = ramfs_dir_grow(rn);
if (r < 0) return r;
}
char *dup = kmalloc(strlen(name) + 1);
if (!dup) return -ENOMEM;
strcpy(dup, name);
rn->children[rn->child_count].name = dup;
rn->children[rn->child_count].node = child;
rn->child_count++;
dir->size = (uint64_t)rn->child_count;
vnode_ref(child);
return 0;
}
static int ramfs_dir_mkdir(vnode_t *dir, const char *name, uint32_t mode) {
vnode_t *existing = NULL;
if (ramfs_dir_lookup(dir, name, &existing) == 0) {
vnode_unref(existing);
return -EEXIST;
}
vnode_t *child = ramfs_alloc_vnode(VFS_NODE_DIR, mode ? mode : 0755);
if (!child) return -ENOMEM;
child->ops = &ramfs_dir_ops;
child->refcount = 1;
int ret = ramfs_dir_add_child(dir, name, child);
if (ret < 0) {
kfree(child->fs_data);
kfree(child);
return ret;
}
vnode_unref(child);
return 0;
}
static int ramfs_dir_create(vnode_t *dir, const char *name, uint32_t mode, vnode_t **out) {
vnode_t *existing = NULL;
if (ramfs_dir_lookup(dir, name, &existing) == 0) {
*out = existing;
return 0;
}
vnode_t *child = ramfs_alloc_vnode(VFS_NODE_FILE, mode ? mode : 0644);
if (!child) return -ENOMEM;
child->ops = &ramfs_file_ops;
child->refcount = 1;
int ret = ramfs_dir_add_child(dir, name, child);
if (ret < 0) {
kfree(child->fs_data);
kfree(child);
return ret;
}
*out = child;
return 0;
}
static int ramfs_dir_unlink(vnode_t *dir, const char *name) {
ramfs_node_t *rn = dir->fs_data;
for (int i = 0; i < rn->child_count; i++) {
if (strcmp(rn->children[i].name, name) == 0) {
kfree(rn->children[i].name);
vnode_unref(rn->children[i].node);
for (int j = i; j < rn->child_count - 1; j++)
rn->children[j] = rn->children[j + 1];
rn->child_count--;
dir->size = (uint64_t)rn->child_count;
return 0;
}
}
return -ENOENT;
}
static const vnode_ops_t ramfs_dir_ops = {
.lookup = ramfs_dir_lookup,
.readdir = ramfs_dir_readdir,
.mkdir = ramfs_dir_mkdir,
.create = ramfs_dir_create,
.unlink = ramfs_dir_unlink,
.stat = ramfs_stat,
.ref = ramfs_ref,
.unref = ramfs_unref,
};
vnode_t *ramfs_create_root(void) {
vnode_t *root = ramfs_alloc_vnode(VFS_NODE_DIR, 0755);
if (!root) return NULL;
root->ops = &ramfs_dir_ops;
root->refcount = 1;
serial_writestring("[ramfs] root created\n");
return root;
}
+617
View File
@@ -0,0 +1,617 @@
#include "../../include/fs/vfs.h"
#include "../../include/sched/sched.h"
#include "../../include/memory/pmm.h"
#include "../../include/io/serial.h"
#include <string.h>
static vfs_mount_t g_mounts[VFS_MAX_MOUNTS];
static vfs_file_t g_open_files[VFS_MAX_OPEN_FILES];
static bool g_vfs_ready = false;
void vfs_init(void) {
memset(g_mounts, 0, sizeof(g_mounts));
memset(g_open_files, 0, sizeof(g_open_files));
g_vfs_ready = true;
serial_writestring("[VFS] initialized\n");
}
void vnode_ref(vnode_t *node) {
if (!node) return;
__atomic_fetch_add(&node->refcount, 1, __ATOMIC_RELAXED);
}
void vnode_unref(vnode_t *node) {
if (!node) return;
int old = __atomic_fetch_sub(&node->refcount, 1, __ATOMIC_ACQ_REL);
if (old <= 1) {
if (node->ops && node->ops->unref)
node->ops->unref(node);
}
}
int vfs_mount(const char *path, vnode_t *fs_root) {
if (!path || !fs_root) return -EINVAL;
for (int i = 0; i < VFS_MAX_MOUNTS; i++) {
if (g_mounts[i].used && strcmp(g_mounts[i].path, path) == 0) {
serial_printf("[VFS] mount: '%s' already mounted\n", path);
return -EBUSY;
}
}
int slot = -1;
if (strcmp(path, "/") == 0) {
slot = 0;
} else {
for (int i = 1; i < VFS_MAX_MOUNTS; i++) {
if (!g_mounts[i].used) { slot = i; break; }
}
}
if (slot < 0) return -ENOMEM;
strncpy(g_mounts[slot].path, path, VFS_MAX_PATH - 1);
g_mounts[slot].root = fs_root;
g_mounts[slot].used = true;
g_mounts[slot].fs_priv = NULL;
g_mounts[slot].unmount = NULL;
vnode_ref(fs_root);
serial_printf("[VFS] mounted '%s' at slot %d\n", path, slot);
return 0;
}
int vfs_mount_fs(const char *path, vnode_t *fs_root,
void *fs_priv, void (*unmount_fn)(void *),
void (*sync_fn)(void *)) {
int ret = vfs_mount(path, fs_root);
if (ret < 0) return ret;
for (int i = 0; i < VFS_MAX_MOUNTS; i++) {
if (g_mounts[i].used && strcmp(g_mounts[i].path, path) == 0) {
g_mounts[i].fs_priv = fs_priv;
g_mounts[i].unmount = unmount_fn;
g_mounts[i].sync = sync_fn;
break;
}
}
return 0;
}
void vfs_sync_all(void) {
for (int i = 0; i < VFS_MAX_MOUNTS; i++) {
if (g_mounts[i].used && g_mounts[i].fs_priv) {
serial_printf("[VFS] sync_all: flushing mount '%s'\n", g_mounts[i].path);
if (g_mounts[i].sync)
g_mounts[i].sync(g_mounts[i].fs_priv);
}
}
}
int vfs_umount(const char *path) {
for (int i = 0; i < VFS_MAX_MOUNTS; i++) {
if (g_mounts[i].used && strcmp(g_mounts[i].path, path) == 0) {
if (g_mounts[i].unmount && g_mounts[i].fs_priv)
g_mounts[i].unmount(g_mounts[i].fs_priv);
vnode_unref(g_mounts[i].root);
memset(&g_mounts[i], 0, sizeof(vfs_mount_t));
return 0;
}
}
return -ENOENT;
}
static vfs_mount_t *find_mount(const char *path, const char **rel_out) {
vfs_mount_t *best = NULL;
size_t best_len = 0;
for (int i = 0; i < VFS_MAX_MOUNTS; i++) {
if (!g_mounts[i].used) continue;
size_t mlen = strlen(g_mounts[i].path);
if (strncmp(path, g_mounts[i].path, mlen) == 0) {
if (path[mlen] == '/' || path[mlen] == '\0' || mlen == 1) {
if (mlen > best_len) {
best = &g_mounts[i];
best_len = mlen;
}
}
}
}
if (best && rel_out) {
const char *rel = path + best_len;
while (*rel == '/') rel++;
*rel_out = rel;
}
return best;
}
static const char *path_next_component(const char *src, char *dst, size_t maxlen) {
while (*src == '/') src++;
size_t i = 0;
while (*src && *src != '/' && i < maxlen - 1)
dst[i++] = *src++;
dst[i] = '\0';
return src;
}
int vfs_lookup(const char *path, vnode_t **out) {
if (!path || !out) return -EINVAL;
if (!g_vfs_ready) return -EIO;
if (path[0] != '/') return -EINVAL;
const char *rel;
vfs_mount_t *mnt = find_mount(path, &rel);
if (!mnt) {
serial_printf("[VFS] lookup '%s': no mount found\n", path);
return -ENOENT;
}
vnode_t *cur = mnt->root;
if (!cur) {
serial_printf("[VFS] lookup '%s': mount root is NULL!\n", path);
return -EIO;
}
vnode_ref(cur);
if (*rel == '\0') {
*out = cur;
return 0;
}
char comp[VFS_MAX_NAME];
const char *p = rel;
while (*p) {
p = path_next_component(p, comp, sizeof(comp));
if (comp[0] == '\0') continue;
if (cur->type != VFS_NODE_DIR) {
serial_printf("[VFS] lookup '%s': '%s' is not a dir (type=%d)\n",
path, comp, cur->type);
vnode_unref(cur);
return -ENOTDIR;
}
if (!cur->ops || !cur->ops->lookup) {
serial_printf("[VFS] lookup '%s': no lookup op for comp '%s'\n", path, comp);
vnode_unref(cur);
return -EIO;
}
vnode_t *next = NULL;
int ret = cur->ops->lookup(cur, comp, &next);
if (ret < 0) {
serial_printf("[VFS] lookup '%s': ops->lookup('%s') = %d\n", path, comp, ret);
vnode_unref(cur);
return ret;
}
vnode_unref(cur);
cur = next;
if (cur->mounted) {
vnode_t *mroot = cur->mounted->root;
vnode_ref(mroot);
vnode_unref(cur);
cur = mroot;
}
}
*out = cur;
return 0;
}
vfs_file_t *vfs_file_alloc(void) {
for (int i = 0; i < VFS_MAX_OPEN_FILES; i++) {
if (g_open_files[i].refcount == 0) {
memset(&g_open_files[i], 0, sizeof(vfs_file_t));
g_open_files[i].refcount = 1;
return &g_open_files[i];
}
}
return NULL;
}
void vfs_file_free(vfs_file_t *file) {
if (!file) return;
int old = __atomic_fetch_sub(&file->refcount, 1, __ATOMIC_ACQ_REL);
if (old == 1) {
vnode_unref(file->vnode);
file->vnode = NULL;
}
}
int vfs_open(const char *path, int flags, uint32_t mode, vfs_file_t **out) {
if (!path || !out) return -EINVAL;
vnode_t *node = NULL;
int ret = vfs_lookup(path, &node);
if (ret == -ENOENT && (flags & O_CREAT)) {
char dirpath[VFS_MAX_PATH];
strncpy(dirpath, path, VFS_MAX_PATH - 1);
dirpath[VFS_MAX_PATH - 1] = '\0';
char *slash = NULL;
for (int i = (int)strlen(dirpath) - 1; i >= 0; i--) {
if (dirpath[i] == '/') { slash = &dirpath[i]; break; }
}
if (!slash) return -EINVAL;
char filename[VFS_MAX_NAME];
strncpy(filename, slash + 1, VFS_MAX_NAME - 1);
filename[VFS_MAX_NAME - 1] = '\0';
if (filename[0] == '\0') return -EINVAL;
if (slash == dirpath)
dirpath[1] = '\0';
else
*slash = '\0';
serial_printf("[VFS] open O_CREAT: parent='%s' name='%s'\n", dirpath, filename);
vnode_t *dir = NULL;
ret = vfs_lookup(dirpath, &dir);
if (ret < 0) {
serial_printf("[VFS] open O_CREAT: lookup parent '%s' failed: %d\n", dirpath, ret);
return ret;
}
if (!dir->ops || !dir->ops->create) {
serial_printf("[VFS] open O_CREAT: parent has no create op\n");
vnode_unref(dir);
return -ENOSYS;
}
ret = dir->ops->create(dir, filename, mode, &node);
vnode_unref(dir);
if (ret < 0) {
serial_printf("[VFS] open O_CREAT: create '%s' failed: %d\n", filename, ret);
return ret;
}
} else if (ret < 0) {
return ret;
}
int acc = flags & O_ACCMODE;
if ((acc == O_WRONLY || acc == O_RDWR) && node->type == VFS_NODE_DIR) {
vnode_unref(node);
return -EISDIR;
}
vfs_file_t *file = vfs_file_alloc();
if (!file) {
vnode_unref(node);
return -ENFILE;
}
file->vnode = node;
file->flags = flags;
file->offset = (flags & O_APPEND) ? node->size : 0;
if ((flags & O_TRUNC) && (acc == O_WRONLY || acc == O_RDWR)) {
if (node->ops && node->ops->truncate) {
node->ops->truncate(node, 0);
} else {
node->size = 0;
}
file->offset = 0;
}
*out = file;
return 0;
}
void vfs_close(vfs_file_t *file) {
if (file) {
int acc = file->flags & O_ACCMODE;
if (acc == O_WRONLY || acc == O_RDWR) {
vfs_sync_all();
}
}
vfs_file_free(file);
}
int64_t vfs_read(vfs_file_t *file, void *buf, size_t len) {
if (!file || !file->vnode) return -EBADF;
if (len == 0) return 0;
if ((file->flags & O_ACCMODE) == O_WRONLY) return -EBADF;
if (!file->vnode->ops || !file->vnode->ops->read) return -EIO;
int64_t n = file->vnode->ops->read(file->vnode, buf, len, file->offset);
if (n > 0) file->offset += (uint64_t)n;
return n;
}
int64_t vfs_write(vfs_file_t *file, const void *buf, size_t len) {
if (!file || !file->vnode) return -EBADF;
if (len == 0) return 0;
if ((file->flags & O_ACCMODE) == O_RDONLY) return -EBADF;
if (!file->vnode->ops || !file->vnode->ops->write) return -EIO;
if (file->flags & O_APPEND) file->offset = file->vnode->size;
int64_t n = file->vnode->ops->write(file->vnode, buf, len, file->offset);
if (n > 0) file->offset += (uint64_t)n;
return n;
}
int64_t vfs_seek(vfs_file_t *file, int64_t offset, int whence) {
if (!file || !file->vnode) return -EBADF;
vnode_type_t t = file->vnode->type;
if (t == VFS_NODE_CHARDEV || t == VFS_NODE_PIPE) return -ESPIPE;
int64_t new_off;
switch (whence) {
case SEEK_SET: new_off = offset; break;
case SEEK_CUR: new_off = (int64_t)file->offset + offset; break;
case SEEK_END: new_off = (int64_t)file->vnode->size + offset; break;
default: return -EINVAL;
}
if (new_off < 0) return -EINVAL;
file->offset = (uint64_t)new_off;
return new_off;
}
int vfs_stat(const char *path, vfs_stat_t *out) {
if (!path || !out) return -EINVAL;
vnode_t *node = NULL;
int ret = vfs_lookup(path, &node);
if (ret < 0) return ret;
if (node->ops && node->ops->stat) {
ret = node->ops->stat(node, out);
} else {
memset(out, 0, sizeof(*out));
out->st_ino = node->ino;
out->st_type = node->type;
out->st_mode = node->mode;
out->st_uid = node->uid;
out->st_gid = node->gid;
out->st_size = node->size;
ret = 0;
}
vnode_unref(node);
return ret;
}
int vfs_fstat(vfs_file_t *file, vfs_stat_t *out) {
if (!file || !file->vnode || !out) return -EBADF;
vnode_t *node = file->vnode;
if (node->ops && node->ops->stat) return node->ops->stat(node, out);
memset(out, 0, sizeof(*out));
out->st_ino = node->ino;
out->st_type = node->type;
out->st_mode = node->mode;
out->st_uid = node->uid;
out->st_gid = node->gid;
out->st_size = node->size;
return 0;
}
int64_t vfs_ioctl(vfs_file_t *file, uint64_t req, void *arg) {
if (!file || !file->vnode) return -EBADF;
if (!file->vnode->ops || !file->vnode->ops->ioctl) return -ENOTTY;
return file->vnode->ops->ioctl(file->vnode, req, arg);
}
int vfs_readdir(vfs_file_t *file, vfs_dirent_t *out) {
if (!file || !file->vnode || !out) return -EBADF;
if (file->vnode->type != VFS_NODE_DIR) return -ENOTDIR;
if (!file->vnode->ops || !file->vnode->ops->readdir) return -EIO;
int ret = file->vnode->ops->readdir(file->vnode, file->offset, out);
if (ret == 0) file->offset++;
return ret;
}
int vfs_mkdir(const char *path, uint32_t mode) {
if (!path) return -EINVAL;
char dirpath[VFS_MAX_PATH];
strncpy(dirpath, path, VFS_MAX_PATH - 1);
dirpath[VFS_MAX_PATH - 1] = '\0';
char *slash = NULL;
for (int i = (int)strlen(dirpath) - 1; i >= 0; i--) {
if (dirpath[i] == '/') { slash = &dirpath[i]; break; }
}
if (!slash) return -EINVAL;
char dirname[VFS_MAX_NAME];
strncpy(dirname, slash + 1, VFS_MAX_NAME - 1);
dirname[VFS_MAX_NAME - 1] = '\0';
if (dirname[0] == '\0') return -EINVAL;
if (slash == dirpath) dirpath[1] = '\0';
else *slash = '\0';
vnode_t *dir = NULL;
int ret = vfs_lookup(dirpath, &dir);
if (ret < 0) return ret;
if (!dir->ops || !dir->ops->mkdir) { vnode_unref(dir); return -ENOSYS; }
ret = dir->ops->mkdir(dir, dirname, mode);
vnode_unref(dir);
return ret;
}
fd_table_t *fd_table_create(void) {
fd_table_t *t = kzalloc(sizeof(fd_table_t));
return t;
}
fd_table_t *fd_table_clone(const fd_table_t *src) {
if (!src) return NULL;
fd_table_t *dst = kzalloc(sizeof(fd_table_t));
if (!dst) return NULL;
for (int i = 0; i < TASK_MAX_FDS; i++) {
if (src->entries[i].file) {
dst->entries[i] = src->entries[i];
__atomic_fetch_add(&dst->entries[i].file->refcount, 1, __ATOMIC_RELAXED);
}
}
return dst;
}
void fd_table_cloexec(fd_table_t *table) {
if (!table) return;
for (int i = 0; i < TASK_MAX_FDS; i++) {
if (table->entries[i].file && (table->entries[i].fd_flags & FD_CLOEXEC)) {
vfs_file_free(table->entries[i].file);
table->entries[i].file = NULL;
table->entries[i].fd_flags = 0;
}
}
}
void fd_table_destroy(fd_table_t *table) {
if (!table) return;
for (int i = 0; i < TASK_MAX_FDS; i++) {
if (table->entries[i].file) {
vfs_file_free(table->entries[i].file);
table->entries[i].file = NULL;
}
}
kfree(table);
}
int fd_alloc(fd_table_t *table, vfs_file_t *file, int min_fd) {
if (!table || !file) return -EINVAL;
if (min_fd < 0 || min_fd >= TASK_MAX_FDS) return -EINVAL;
for (int i = min_fd; i < TASK_MAX_FDS; i++) {
if (!table->entries[i].file) {
table->entries[i].file = file;
table->entries[i].fd_flags = 0;
return i;
}
}
return -EMFILE;
}
vfs_file_t *fd_get(const fd_table_t *table, int fd) {
if (!table || fd < 0 || fd >= TASK_MAX_FDS) return NULL;
return table->entries[fd].file;
}
int fd_close(fd_table_t *table, int fd) {
if (!table || fd < 0 || fd >= TASK_MAX_FDS) return -EBADF;
vfs_file_t *file = table->entries[fd].file;
if (!file) return -EBADF;
vfs_file_free(file);
table->entries[fd].file = NULL;
table->entries[fd].fd_flags = 0;
return 0;
}
int fd_dup2(fd_table_t *table, int oldfd, int newfd) {
if (!table) return -EBADF;
if (oldfd < 0 || oldfd >= TASK_MAX_FDS) return -EBADF;
if (newfd < 0 || newfd >= TASK_MAX_FDS) return -EBADF;
if (oldfd == newfd) return newfd;
vfs_file_t *src = table->entries[oldfd].file;
if (!src) return -EBADF;
if (table->entries[newfd].file)
vfs_file_free(table->entries[newfd].file);
table->entries[newfd].file = src;
table->entries[newfd].fd_flags = 0;
__atomic_fetch_add(&src->refcount, 1, __ATOMIC_RELAXED);
return newfd;
}
int fd_set_flags(fd_table_t *table, int fd, int flags) {
if (!table || fd < 0 || fd >= TASK_MAX_FDS) return -EBADF;
if (!table->entries[fd].file) return -EBADF;
table->entries[fd].fd_flags = flags;
return 0;
}
int fd_get_flags(const fd_table_t *table, int fd) {
if (!table || fd < 0 || fd >= TASK_MAX_FDS) return -EBADF;
if (!table->entries[fd].file) return -EBADF;
return table->entries[fd].fd_flags;
}
int vfs_init_stdio(void *task_ptr) {
task_t *t = (task_t *)task_ptr;
if (!t || !t->fd_table) return -EINVAL;
vfs_file_t *in = NULL;
int ret = vfs_open("/dev/tty", O_RDONLY, 0, &in);
if (ret < 0) return ret;
fd_alloc(t->fd_table, in, 0);
vfs_file_t *out = NULL;
ret = vfs_open("/dev/tty", O_WRONLY, 0, &out);
if (ret < 0) return ret;
fd_alloc(t->fd_table, out, 1);
vfs_file_t *err = NULL;
ret = vfs_open("/dev/tty", O_WRONLY, 0, &err);
if (ret < 0) return ret;
fd_alloc(t->fd_table, err, 2);
return 0;
}
int vfs_set_mount_info(const char *path, const char *device, const char *fstype) {
for (int i = 0; i < VFS_MAX_MOUNTS; i++) {
if (g_mounts[i].used && strcmp(g_mounts[i].path, path) == 0) {
if (device) {
strncpy(g_mounts[i].device, device, sizeof(g_mounts[i].device) - 1);
g_mounts[i].device[sizeof(g_mounts[i].device) - 1] = '\0';
}
if (fstype) {
strncpy(g_mounts[i].fstype, fstype, sizeof(g_mounts[i].fstype) - 1);
g_mounts[i].fstype[sizeof(g_mounts[i].fstype) - 1] = '\0';
}
return 0;
}
}
return -ENOENT;
}
int vfs_list_mounts(vfs_mount_info_t *out, int max) {
int n = 0;
for (int i = 0; i < VFS_MAX_MOUNTS && n < max; i++) {
if (!g_mounts[i].used) continue;
strncpy(out[n].path, g_mounts[i].path, VFS_MAX_PATH - 1);
out[n].path[VFS_MAX_PATH - 1] = '\0';
strncpy(out[n].device, g_mounts[i].device, sizeof(out[n].device) - 1);
out[n].device[sizeof(out[n].device) - 1] = '\0';
strncpy(out[n].fstype, g_mounts[i].fstype, sizeof(out[n].fstype) - 1);
out[n].fstype[sizeof(out[n].fstype) - 1] = '\0';
out[n].flags = 0;
n++;
}
return n;
}
extern int ext2_statvfs(vnode_t *root, vfs_statvfs_t *out);
extern int fat32_statvfs(vnode_t *root, vfs_statvfs_t *out);
int vfs_statvfs(const char *path, vfs_statvfs_t *out) {
if (!path || !out) return -EINVAL;
memset(out, 0, sizeof(*out));
vfs_mount_t *best = NULL;
size_t best_len = 0;
for (int i = 0; i < VFS_MAX_MOUNTS; i++) {
if (!g_mounts[i].used) continue;
size_t mlen = strlen(g_mounts[i].path);
if (strncmp(path, g_mounts[i].path, mlen) == 0) {
if (path[mlen] == '/' || path[mlen] == '\0' || mlen == 1) {
if (mlen > best_len) { best = &g_mounts[i]; best_len = mlen; }
}
}
}
if (!best) return -ENOENT;
if (strcmp(best->fstype, "ext2") == 0) {
return ext2_statvfs(best->root, out);
}
if (strcmp(best->fstype, "fat32") == 0) {
return fat32_statvfs(best->root, out);
}
out->f_bsize = 4096;
out->f_namemax = 255;
return 0;
}
+41
View File
@@ -0,0 +1,41 @@
#include "../../include/gdt/gdt.h"
#include "../../include/smp/smp.h"
#include "../../include/io/serial.h"
#include <string.h>
#include <stddef.h>
gdt_pointer_t gdtr;
struct {
gdt_entry_t gdt_entries[5 + (MAX_CPUS * 2)];
} __attribute__((packed)) gdt;
tss_t *tss[MAX_CPUS] = {0};
__attribute__((aligned(16))) char kernel_stacks[MAX_CPUS][KERNEL_STACK_SIZE];
__attribute__((aligned(16))) char def_ist_stacks[MAX_CPUS][KERNEL_STACK_SIZE];
__attribute__((aligned(16))) char df_stacks[MAX_CPUS][KERNEL_STACK_SIZE];
__attribute__((aligned(16))) char nmi_stacks[MAX_CPUS][KERNEL_STACK_SIZE];
__attribute__((aligned(16))) char pf_stacks[MAX_CPUS][KERNEL_STACK_SIZE];
extern void _load_gdt(gdt_pointer_t *descriptor);
extern void _reload_segments(uint64_t cs, uint64_t ds);
void gdt_init(void) {
memset(&gdt, 0, sizeof(gdt));
gdt.gdt_entries[0] = (gdt_entry_t)GDT_ENTRY(0, 0, 0x00, 0x0);
gdt.gdt_entries[1] = (gdt_entry_t)GDT_ENTRY(0, 0xFFFFF, 0x9A, 0xA);
gdt.gdt_entries[2] = (gdt_entry_t)GDT_ENTRY(0, 0xFFFFF, 0x92, 0xC);
gdt.gdt_entries[3] = (gdt_entry_t)GDT_ENTRY(0, 0xFFFFF, 0xF2, 0x8);
gdt.gdt_entries[4] = (gdt_entry_t)GDT_ENTRY(0, 0xFFFFF, 0xFA, 0xA);
gdtr.size = (5 * sizeof(gdt_entry_t)) - 1;
gdtr.pointer = &gdt.gdt_entries[0];
serial_printf("Installing temporary GDT for BSP...\n");
gdt_load();
serial_printf("Temporary GDT installed\n");
}
void gdt_load(void) {
_load_gdt(&gdtr);
_reload_segments(GDT_CODE_SEGMENT, GDT_DATA_SEGMENT);
}
+28
View File
@@ -0,0 +1,28 @@
section .text
global _reload_segments
global _load_gdt
global load_tss
_load_gdt:
lgdt [rdi]
ret
load_tss:
ltr di
ret
_reload_segments:
push rdi
lea rax, [rel .reload_cs]
push rax
retfq
.reload_cs:
mov ax, si
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
ret
+136
View File
@@ -0,0 +1,136 @@
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <limine.h>
#include "../../../include/graphics/fb/fb.h"
#include "../../../include/io/serial.h"
uint32_t *g_backbuf = NULL;
uint32_t g_bb_pitch = 0;
static uint32_t g_bb_w = 0;
static uint32_t g_bb_h = 0;
void fb_init_backbuffer(struct limine_framebuffer *fb) {
if (!fb) return;
g_bb_w = fb->width;
g_bb_h = fb->height;
g_bb_pitch = fb->pitch / 4;
size_t sz = (size_t)g_bb_pitch * g_bb_h * sizeof(uint32_t);
g_backbuf = (uint32_t *)malloc(sz);
if (g_backbuf) {
memcpy(g_backbuf, fb->address, sz);
serial_printf("[FB] Backbuffer allocated: %ux%u (%zu KB)\n",
g_bb_w, g_bb_h, sz / 1024);
} else {
serial_printf("[FB] WARNING: backbuffer alloc failed, using direct VRAM\n");
}
}
static inline uint32_t *fb_get_buf(struct limine_framebuffer *fb) {
return g_backbuf ? g_backbuf : (uint32_t *)fb->address;
}
static inline uint32_t fb_get_pitch(struct limine_framebuffer *fb) {
return g_backbuf ? g_bb_pitch : (fb->pitch / 4);
}
void fb_flush(struct limine_framebuffer *fb) {
if (!fb || !g_backbuf) return;
memcpy(fb->address, g_backbuf, (size_t)g_bb_pitch * g_bb_h * sizeof(uint32_t));
}
void fb_flush_lines(struct limine_framebuffer *fb, uint32_t y_start, uint32_t y_end) {
if (!fb || !g_backbuf) return;
if (y_start >= g_bb_h) return;
if (y_end > g_bb_h) y_end = g_bb_h;
if (y_start >= y_end) return;
uint32_t *dst = (uint32_t *)fb->address + y_start * g_bb_pitch;
uint32_t *src = g_backbuf + y_start * g_bb_pitch;
size_t bytes = (size_t)(y_end - y_start) * g_bb_pitch * sizeof(uint32_t);
memcpy(dst, src, bytes);
}
void fb_draw_pixel(struct limine_framebuffer *fb, uint32_t x, uint32_t y, uint32_t color) {
if (x >= fb->width || y >= fb->height) return;
uint32_t *buf = fb_get_buf(fb);
uint32_t pitch = fb_get_pitch(fb);
buf[y * pitch + x] = color;
}
void fb_fill_rect(struct limine_framebuffer *fb, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t color) {
if (!fb) return;
uint32_t *buf = fb_get_buf(fb);
uint32_t pitch = fb_get_pitch(fb);
if (x >= fb->width || y >= fb->height) return;
if (x + w > fb->width) w = fb->width - x;
if (y + h > fb->height) h = fb->height - y;
for (uint32_t py = 0; py < h; py++) {
uint32_t *row = buf + (y + py) * pitch + x;
if (color == 0) {
memset(row, 0, w * sizeof(uint32_t));
} else {
for (uint32_t px = 0; px < w; px++)
row[px] = color;
}
}
}
void fb_clear(struct limine_framebuffer *fb, uint32_t color) {
if (!fb) return;
uint32_t *buf = fb_get_buf(fb);
size_t total = (size_t)fb_get_pitch(fb) * fb->height;
if (color == 0) {
memset(buf, 0, total * sizeof(uint32_t));
} else {
for (size_t i = 0; i < total; i++)
buf[i] = color;
}
}
int psf_validate(void) {
const uint8_t *raw = get_font_data();
if (raw[0] == 0x72 && raw[1] == 0xb5 &&
raw[2] == 0x4a && raw[3] == 0x86)
return 2;
if (raw[0] == 0x36 && raw[1] == 0x04)
return 1;
return 0;
}
void fb_draw_char(struct limine_framebuffer *fb, char c, uint32_t x, uint32_t y, uint32_t color) {
const uint8_t *raw = get_font_data();
uint32_t headersize = 32;
uint32_t charsiz = *(uint32_t*)(raw + 20);
uint8_t *glyphs = (uint8_t*)raw + headersize;
uint32_t glyph_index = ((uint8_t)c < 128) ? (uint8_t)c : '?';
if (glyph_index >= 512) glyph_index = '?';
uint8_t *glyph = &glyphs[glyph_index * charsiz];
uint32_t *buf = fb_get_buf(fb);
uint32_t pitch = fb_get_pitch(fb);
if (x + 8 > fb->width || y + 16 > fb->height) return;
for (uint32_t row = 0; row < 16; row++) {
uint8_t byte = glyph[row];
uint32_t *line = buf + (y + row) * pitch + x;
for (uint32_t col = 0; col < 8; col++) {
if (byte & (0x80 >> col))
line[col] = color;
}
}
}
void fb_draw_string(struct limine_framebuffer *fb, const char *str, uint32_t x, uint32_t y, uint32_t color) {
uint32_t orig_x = x;
if (!psf_validate()) return;
while (*str) {
if (*str == '\n') { x = orig_x; y += 16; }
else { fb_draw_char(fb, *str, x, y, color); x += 8; }
str++;
}
}
+79
View File
@@ -0,0 +1,79 @@
#include "../../include/interrupts/idt.h"
#include "../../include/interrupts/isr.h"
#include "../../include/interrupts/irq.h"
#include "../../include/io/serial.h"
#include <stddef.h>
#include <string.h>
extern void *interrupts_stub_table[];
__attribute__((
aligned(0x10))) static idt_entry_t idt_entries[IDT_MAX_DESCRIPTORS];
idtr_t idtr;
void idt_set_gate(uint8_t index, void *base, uint16_t selector, uint8_t flags, uint8_t ist) {
idt_entries[index].base_low = (uint64_t)base & 0xFFFF;
idt_entries[index].kernel_cs = selector;
idt_entries[index].ist = ist;
idt_entries[index].attributes = flags;
idt_entries[index].base_mid = ((uint64_t)base >> 16) & 0xFFFF;
idt_entries[index].base_high = ((uint64_t)base >> 32) & 0xFFFFFFFF;
idt_entries[index].reserved = 0;
}
void idt_gate_enable(int interrupt) {
FLAG_SET(idt_entries[interrupt].attributes, IDT_FLAG_PRESENT);
}
void idt_gate_disable(int interrupt) {
FLAG_UNSET(idt_entries[interrupt].attributes, IDT_FLAG_PRESENT);
}
bool setup_specific_vectors(uint64_t kernel_code_segment, uint64_t vector) {
if(vector == EXCEPTION_DOUBLE_FAULT) {
idt_set_gate(vector, interrupts_stub_table[vector], kernel_code_segment, 0x8E, 1);
return true;
}
if(vector == EXCEPTION_NMI) {
idt_set_gate(vector, interrupts_stub_table[vector], kernel_code_segment, 0x8E, 2);
return true;
}
if(vector == EXCEPTION_PAGE_FAULT) {
idt_set_gate(vector, interrupts_stub_table[vector], kernel_code_segment, 0x8E, 3);
return true;
}
if(vector == 0x40) {
idt_set_gate(vector, interrupts_stub_table[vector], kernel_code_segment, 0x8E, 4);
return true;
}
return false;
}
void idt_load(void) {
__asm__ volatile("lidt %0" : : "m"(idtr));
}
void setup_interrupt_descriptor_table(uint64_t kernel_code_segment) {
serial_printf("[IDT] Initializing IDT...\n");
idtr.base = (idt_entry_t *)&idt_entries[0];
idtr.limit = (uint16_t)sizeof(idt_entry_t) * IDT_MAX_DESCRIPTORS - 1;
for (uint16_t vector = 0; vector < IDT_MAX_DESCRIPTORS; vector++) {
if(setup_specific_vectors(kernel_code_segment, vector)) {
continue;
}
idt_set_gate(vector, interrupts_stub_table[vector], kernel_code_segment, 0x8E, 0);
}
idt_load();
serial_printf("[IDT] IDT initialized successfully\n");
}
+133
View File
@@ -0,0 +1,133 @@
section .text
extern base_trap
extern sched_reschedule
extern get_percpu
PERCPU_NEED_RESCHED equ 40
common_stub:
push rax
push rbx
push rcx
push rdx
push rsi
push rdi
push rbp
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
mov rax, ds
push rax
mov rax, [rsp + 19*8]
and rax, 3
jz .kernel_entry
swapgs
.kernel_entry:
mov ax, 0x10
mov ds, ax
mov es, ax
mov rdi, rsp
call base_trap
pop rax
mov ds, ax
mov es, ax
mov rax, [rsp + 18*8]
and rax, 3
jz .kernel_resched
.check_resched:
call get_percpu
test rax, rax
jz .do_swapgs
cmp byte [rax + PERCPU_NEED_RESCHED], 0
je .do_swapgs
mov byte [rax + PERCPU_NEED_RESCHED], 0
call sched_reschedule
jmp .check_resched
.do_swapgs:
swapgs
jmp .kernel_exit
.kernel_resched:
call get_percpu
test rax, rax
jz .kernel_check_cs
cmp byte [rax + PERCPU_NEED_RESCHED], 0
je .kernel_check_cs
mov byte [rax + PERCPU_NEED_RESCHED], 0
call sched_reschedule
jmp .kernel_resched
.kernel_check_cs:
mov rax, [rsp + 18*8]
and rax, 3
jz .kernel_exit
swapgs
jmp .kernel_exit
.kernel_exit:
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rbp
pop rdi
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
add rsp, 16
iretq
%macro INTERRUPT_ERR_STUB 1
interrupt_stub_%1:
push qword %1
jmp common_stub
%endmacro
%macro INTERRUPT_NO_ERR_STUB 1
interrupt_stub_%1:
push qword 0
push qword %1
jmp common_stub
%endmacro
%assign i 0
%rep 32
%if i = 8 || i = 10 || i = 11 || i = 12 || i = 13 || i = 14 || i = 17 || i = 21 || i = 29 || i = 30
INTERRUPT_ERR_STUB i
%else
INTERRUPT_NO_ERR_STUB i
%endif
%assign i i+1
%endrep
%rep 224
INTERRUPT_NO_ERR_STUB i
%assign i i+1
%endrep
section .data
global interrupts_stub_table
interrupts_stub_table:
%assign i 0
%rep 256
dq interrupt_stub_%+i
%assign i i+1
%endrep
+32
View File
@@ -0,0 +1,32 @@
#include "../../include/interrupts/interrupts.h"
#include "../../include/io/serial.h"
#include "../../include/interrupts/isr.h"
#include "../../include/interrupts/irq.h"
#include "../../include/interrupts/idt.h"
#include "../../include/gdt/gdt.h"
#include "../../include/io/ports.h"
extern const int_desc_t __start_int_handlers[];
extern const int_desc_t __stop_int_handlers[];
void init_interrupt_system(void) {
asm volatile("cli");
setup_interrupt_descriptor_table(GDT_CODE_SEGMENT);
setup_defined_isr_handlers();
setup_defined_irq_handlers();
asm volatile("sti");
}
void base_trap(void *ctx) {
struct int_frame_t *regs = (struct int_frame_t*)ctx;
if(regs->interrupt < ISR_EXCEPTION_COUNT) {
return isr_common_handler(regs);
}
return irq_common_handler(regs);
}
+82
View File
@@ -0,0 +1,82 @@
#include "../../../include/interrupts/interrupts.h"
#include "../../../include/io/serial.h"
#include "../../../include/interrupts/irq.h"
#include "../../../include/interrupts/idt.h"
#include "../../../include/io/ports.h"
#include "../../../include/smp/percpu.h"
#include "../../../include/apic/apic.h"
extern const int_desc_t __start_irq_handlers[];
extern const int_desc_t __stop_irq_handlers[];
static int_handler_f registered_irq_interrupts[IRQ_INTERRUPTS_COUNT]__attribute__((aligned(64)));
void irq_common_handler(struct int_frame_t* regs) {
uint64_t vec = regs->interrupt;
if (vec >= IRQ_INTERRUPTS_COUNT || vec < (IDT_MAX_DESCRIPTORS - IRQ_INTERRUPTS_COUNT)) {
serial_printf("IRQ vector out of range: %d\n", vec);
while (1)
{
asm volatile ("hlt");
}
}
if(registered_irq_interrupts[vec]) {
return registered_irq_interrupts[vec](regs);
}
serial_printf("IRQ interrupt handler\n");
while (1)
{
asm volatile ("hlt");
}
}
void setup_defined_irq_handlers(void) {
const int_desc_t* desc;
for (desc = __start_irq_handlers; desc < __stop_irq_handlers; desc++) {
if(desc->vector >= IRQ_INTERRUPTS_COUNT) {
serial_printf("Invalid IRQ vector number! Must be < %d\n", IRQ_INTERRUPTS_COUNT);
continue;
}
registered_irq_interrupts[desc->vector] = desc->handler;
serial_printf("Registered IRQ vector 0x%d\n", desc->vector);
}
}
extern percpu_t* percpu_regions[MAX_CPUS];
DEFINE_IRQ(IPI_RESCHEDULE_VECTOR, ipi_reschedule_handler)
{
(void)frame;
uint32_t id = lapic_get_id();
if (id < MAX_CPUS && percpu_regions[id] != NULL) {
percpu_regions[id]->need_resched = true;
}
lapic_eoi();
}
DEFINE_IRQ(IPI_TLB_SHOOTDOWN, ipi_tlb_shootdown_handler)
{
(void)frame;
uint32_t id = lapic_get_id();
tlb_shootdown_t* q = &tlb_shootdown_queue[id];
if (q->pending) {
for (size_t i = 0; i < q->count; i++) {
uintptr_t addr = q->addresses[i];
if (addr != 0) {
asm volatile ("invlpg (%0)" :: "r"(addr) : "memory");
}
}
q->pending = false;
q->count = 0;
}
lapic_eoi();
}
+271
View File
@@ -0,0 +1,271 @@
#include "../../../include/interrupts/interrupts.h"
#include "../../../include/io/serial.h"
#include "../../../include/interrupts/isr.h"
#include "../../../include/sched/sched.h"
#include "../../../include/smp/percpu.h"
#include "../../../include/apic/apic.h"
#include "../../../include/memory/vmm.h"
#include "../../../include/memory/pmm.h"
#include "../../../include/panic/panic.h"
#include <stdio.h>
extern const int_desc_t __start_isr_handlers[];
extern const int_desc_t __stop_isr_handlers[];
static int_handler_f registered_isr_interrupts[ISR_EXCEPTION_COUNT] __attribute__((aligned(64)));
extern volatile int g_panic_owner;
void registers_dump(struct int_frame_t *regs) {
uint64_t cr2 = 0;
asm volatile("mov %%cr2, %0" : "=r"(cr2));
serial_printf("\nRegisters Dump:\n");
serial_printf("\tRAX:0x%llx\n\tRBX:0x%llx\n\tRCX:0x%llx\n\tRDX:0x%llx\n",
regs->rax, regs->rbx, regs->rcx, regs->rdx);
serial_printf("\tRSI:0x%llx\n\tRDI:0x%llx\n\tRBP:0x%llx\n\tRSP:0x%llx\n",
regs->rsi, regs->rdi, regs->rbp, regs->rsp);
serial_printf("\tRIP:0x%llx\n\tRFL:0x%llx\n\tCS:0x%llx\n\tERR:0x%llx\n",
regs->rip, regs->rflags, regs->cs, regs->error);
serial_printf("\tCR2:0x%llx\n", cr2);
serial_printf("\nInt:%d (%s)\n", regs->interrupt, exception_names[regs->interrupt]);
}
static void dump_rip_bytes_safe(uint64_t rip) {
uintptr_t hhdm = (uintptr_t)pmm_phys_to_virt(0);
uint64_t vpage = rip & ~0xFFFULL;
uintptr_t off = rip & 0xFFF;
if (off > PAGE_SIZE - 8) {
serial_printf("[ISR-UD] RIP=0x%llx near page boundary, skipping byte dump\n", rip);
return;
}
uint64_t cr3_val = 0;
asm volatile("mov %%cr3, %0" : "=r"(cr3_val));
volatile uint64_t *pml4v = (volatile uint64_t*)(hhdm + (cr3_val & ~0xFFFULL));
uint64_t e4 = pml4v[(vpage >> 39) & 0x1FF];
if (!(e4 & 1)) {
serial_printf("[ISR-UD] RIP=0x%llx: PML4 not present\n", rip);
return;
}
volatile uint64_t *pdpt = (volatile uint64_t*)(hhdm + (e4 & ~0xFFFULL));
uint64_t e3 = pdpt[(vpage >> 30) & 0x1FF];
if (!(e3 & 1)) {
serial_printf("[ISR-UD] RIP=0x%llx: PDPT not present\n", rip);
return;
}
volatile uint64_t *pd = (volatile uint64_t*)(hhdm + (e3 & ~0xFFFULL));
uint64_t e2 = pd[(vpage >> 21) & 0x1FF];
if (!(e2 & 1)) {
serial_printf("[ISR-UD] RIP=0x%llx: PD not present\n", rip);
return;
}
if (e2 & (1ULL << 7)) {
uintptr_t hp_phys = (e2 & ~0x1FFFFFULL) + (vpage & 0x1FFFFFULL);
volatile uint8_t *bytes = (volatile uint8_t*)(hhdm + hp_phys + off);
serial_printf("[ISR-UD] RIP=0x%llx (huge page) bytes: "
"%02x %02x %02x %02x %02x %02x %02x %02x\n",
rip,
(unsigned)bytes[0], (unsigned)bytes[1],
(unsigned)bytes[2], (unsigned)bytes[3],
(unsigned)bytes[4], (unsigned)bytes[5],
(unsigned)bytes[6], (unsigned)bytes[7]);
return;
}
volatile uint64_t *pt = (volatile uint64_t*)(hhdm + (e2 & ~0xFFFULL));
uint64_t pte = pt[(vpage >> 12) & 0x1FF];
if (!(pte & 1)) {
serial_printf("[ISR-UD] RIP=0x%llx: PT not present (pte=0x%llx)\n", rip, pte);
return;
}
uintptr_t phys_page = pte & ~0xFFFULL;
volatile uint8_t *bytes = (volatile uint8_t*)(hhdm + phys_page + off);
serial_printf("[ISR-UD] RIP=0x%llx phys=0x%llx flags=0x%03llx bytes: "
"%02x %02x %02x %02x %02x %02x %02x %02x\n",
rip, phys_page, pte & 0xFFFULL,
(unsigned)bytes[0], (unsigned)bytes[1],
(unsigned)bytes[2], (unsigned)bytes[3],
(unsigned)bytes[4], (unsigned)bytes[5],
(unsigned)bytes[6], (unsigned)bytes[7]);
if (off >= 16) {
volatile uint8_t *prev = (volatile uint8_t*)(hhdm + phys_page + off - 16);
serial_printf("[ISR-UD] RIP-16..RIP-1: "
"%02x %02x %02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x %02x %02x %02x %02x\n",
(unsigned)prev[0], (unsigned)prev[1],
(unsigned)prev[2], (unsigned)prev[3],
(unsigned)prev[4], (unsigned)prev[5],
(unsigned)prev[6], (unsigned)prev[7],
(unsigned)prev[8], (unsigned)prev[9],
(unsigned)prev[10], (unsigned)prev[11],
(unsigned)prev[12], (unsigned)prev[13],
(unsigned)prev[14], (unsigned)prev[15]);
}
}
static void dump_user_qword_via_task(uint64_t uaddr, const char *label) {
percpu_t *pc = get_percpu();
task_t *me = pc ? (task_t *)pc->current_task : NULL;
if (!me) { uint32_t cpu = lapic_get_id(); me = current_task[cpu]; }
if (!me || !me->pagemap) {
serial_printf("[ISR-PF] %s: no task/pagemap\n", label);
return;
}
if ((uaddr & 0x7) != 0) {
serial_printf("[ISR-PF] %s addr 0x%llx not 8-aligned\n", label, uaddr);
return;
}
uint64_t flags = 0;
if (!vmm_get_page_flags(me->pagemap, uaddr, &flags) || !(flags & 1)) {
serial_printf("[ISR-PF] %s addr 0x%llx not mapped (flags=0x%llx)\n",
label, uaddr, flags);
return;
}
uintptr_t phys = 0;
if (!vmm_virt_to_phys(me->pagemap, uaddr, &phys)) {
serial_printf("[ISR-PF] %s virt_to_phys failed for 0x%llx\n", label, uaddr);
return;
}
volatile uint64_t *p = (volatile uint64_t *)pmm_phys_to_virt(phys & ~7ULL);
uint64_t v = *p;
serial_printf("[ISR-PF] %s [0x%llx] = 0x%llx\n", label, uaddr, v);
}
static __attribute__((noreturn)) void kill_current_task(int exit_code) {
percpu_t *pc = get_percpu();
task_t *me = pc ? (task_t *)pc->current_task : NULL;
if (!me) { uint32_t cpu = lapic_get_id(); me = current_task[cpu]; }
if (me) me->exit_code = exit_code;
vmm_switch_pagemap(vmm_get_kernel_pagemap());
task_exit();
}
void handle_intercpu_interrupt(struct int_frame_t *regs)
{
if (regs->interrupt == 2) {
if (__atomic_load_n(&g_panic_owner, __ATOMIC_ACQUIRE) != 0) {
for (;;) asm volatile("cli; hlt");
}
kernel_panic_regs("Non-Maskable Interrupt (hardware)", regs);
}
serial_force_unlock();
registers_dump(regs);
switch (regs->interrupt) {
case EXCEPTION_DIVIDE_ERROR:
case EXCEPTION_OVERFLOW:
case EXCEPTION_BOUND_RANGE:
case EXCEPTION_INVALID_OPCODE:
case EXCEPTION_DEVICE_NOT_AVAILABLE:
case EXCEPTION_X87_FPU_ERROR:
if ((regs->cs & 3) == 3) {
if (regs->interrupt == EXCEPTION_INVALID_OPCODE) {
dump_rip_bytes_safe(regs->rip);
serial_printf("[ISR-UD] RBP=0x%llx RSP=0x%llx\n",
regs->rbp, regs->rsp);
}
serial_printf("[ISR] %s in userspace at RIP=0x%llx — killing task\n",
exception_names[regs->interrupt], regs->rip);
kill_current_task(139);
}
kernel_panic_regs(exception_names[regs->interrupt], regs);
case EXCEPTION_DOUBLE_FAULT:
kernel_panic_regs("Double Fault", regs);
case EXCEPTION_INVALID_TSS:
case EXCEPTION_SEGMENT_NOT_PRESENT:
case EXCEPTION_STACK_SEGMENT_FAULT:
kernel_panic_regs(exception_names[regs->interrupt], regs);
case EXCEPTION_GENERAL_PROTECTION_FAULT:
if ((regs->cs & 3) == 3) {
serial_printf("[ISR] GPF in userspace at RIP=0x%llx — killing task\n",
regs->rip);
kill_current_task(139);
}
kernel_panic_regs("General Protection Fault (kernel)", regs);
case EXCEPTION_PAGE_FAULT: {
uint64_t cr2val = 0;
asm volatile("mov %%cr2, %0" : "=r"(cr2val));
if ((regs->cs & 3) == 3) {
percpu_t *pc = get_percpu();
task_t *me = pc ? (task_t *)pc->current_task : NULL;
if (!me) { uint32_t cpu = lapic_get_id(); me = current_task[cpu]; }
serial_printf("[ISR] Page Fault in userspace: RIP=0x%llx CR2=0x%llx ERR=0x%llx task='%s' pid=%u\n",
regs->rip, cr2val, regs->error,
me ? me->name : "?", me ? me->pid : 0);
dump_rip_bytes_safe(regs->rip);
dump_user_qword_via_task(regs->rsp, "[rsp] ");
dump_user_qword_via_task(regs->rsp + 8, "[rsp+8] ");
dump_user_qword_via_task(regs->rsp + 16, "[rsp+16] ");
dump_user_qword_via_task(regs->rsp + 24, "[rsp+24] ");
kill_current_task(139);
}
kernel_panic_regs("Page Fault (kernel)", regs);
}
case EXCEPTION_MACHINE_CHECK:
kernel_panic_regs("Machine Check Exception", regs);
default: {
char buf[64];
serial_printf("UNKNOWN EXCEPTION %llu\n", regs->interrupt);
const char *pfx = "Unknown Exception #";
int i = 0;
while (pfx[i] && i < 50) { buf[i] = pfx[i]; i++; }
uint64_t v = regs->interrupt;
if (v >= 100) buf[i++] = '0' + (int)(v / 100);
if (v >= 10) buf[i++] = '0' + (int)((v / 10) % 10);
buf[i++] = '0' + (int)(v % 10);
buf[i] = '\0';
kernel_panic_regs(buf, regs);
}
}
}
DEFINE_ISR(0x3, isr_breakpoint) {
(void)frame;
serial_printf("Breakpoint hit\n");
}
void isr_common_handler(struct int_frame_t *regs)
{
uint64_t vec = regs->interrupt;
if (vec >= ISR_EXCEPTION_COUNT) {
serial_printf("Invalid ISR vector %d\n", vec);
while (1) asm volatile("hlt");
}
if (registered_isr_interrupts[vec]) {
registered_isr_interrupts[vec](regs);
return;
}
handle_intercpu_interrupt(regs);
}
void setup_defined_isr_handlers(void)
{
const int_desc_t *desc;
for (desc = __start_isr_handlers; desc < __stop_isr_handlers; desc++) {
if (desc->vector >= ISR_EXCEPTION_COUNT) {
serial_printf("ISR: vector %d out of range\n", desc->vector);
continue;
}
registered_isr_interrupts[desc->vector] = desc->handler;
serial_printf("ISR: Registered vector %d\n", desc->vector);
}
}
+826
View File
@@ -0,0 +1,826 @@
#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include "../../include/io/ports.h"
#include "../../include/io/serial.h"
static volatile uint8_t serial_lock = 0;
static uint16_t default_serial_port = 0;
void serial_initialize(uint16_t port, uint32_t baud_rate) {
outb(port + 1, 0x00);
uint16_t divisor = 115200 / baud_rate;
outb(port + 3, 0x80);
outb(port + 0, divisor & 0xFF);
outb(port + 1, (divisor >> 8) & 0xFF);
outb(port + 3, 0x03);
outb(port + 2, 0xC7);
outb(port + 4, 0x0B);
outb(port + 4, 0x1E);
outb(port + 0, 0xAE);
if (inb(port + 0) != 0xAE) {
return;
}
outb(port + 4, 0x0F);
default_serial_port = port;
}
uint16_t serial_get_default_port(void) {
return default_serial_port;
}
void serial_set_default_port(uint16_t port) {
default_serial_port = port;
}
void serial_force_unlock(void) {
__sync_lock_release(&serial_lock);
asm volatile("sti");
}
int serial_received_port(uint16_t port) {
return inb(port + 5) & 1;
}
int serial_received(void) {
if (default_serial_port == 0) return 0;
return serial_received_port(default_serial_port);
}
char serial_read_port(uint16_t port) {
while (serial_received_port(port) == 0);
return inb(port);
}
char serial_read(void) {
if (default_serial_port == 0) return 0;
return serial_read_port(default_serial_port);
}
int serial_is_transmit_empty_port(uint16_t port) {
return inb(port + 5) & 0x20;
}
int serial_is_transmit_empty(void) {
if (default_serial_port == 0) return 1;
return serial_is_transmit_empty_port(default_serial_port);
}
void serial_write_port(uint16_t port, char c) {
while (serial_is_transmit_empty_port(port) == 0);
outb(port, c);
}
void serial_write(char c) {
if (default_serial_port == 0) return;
serial_write_port(default_serial_port, c);
}
void serial_writestring_port(uint16_t port, const char* str) {
while (*str) {
serial_write_port(port, *str++);
}
}
void serial_writestring(const char* str) {
if (default_serial_port == 0) return;
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
while (__sync_lock_test_and_set(&serial_lock, 1))
__asm__ volatile("pause" ::: "memory");
serial_writestring_port(default_serial_port, str);
__sync_lock_release(&serial_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
}
void serial_writebuf(const char* buf, size_t len) {
if (default_serial_port == 0 || !buf || len == 0) return;
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
while (__sync_lock_test_and_set(&serial_lock, 1))
__asm__ volatile("pause" ::: "memory");
for (size_t i = 0; i < len; i++)
serial_write_port(default_serial_port, buf[i]);
__sync_lock_release(&serial_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
}
static void reverse_string(char* str, int length) {
int start = 0;
int end = length - 1;
while (start < end) {
char temp = str[start];
str[start] = str[end];
str[end] = temp;
start++;
end--;
}
}
static void uint_to_str(uint64_t value, char* buffer, int base, bool uppercase) {
char* ptr = buffer;
if (base < 2 || base > 36) {
*ptr = '\0';
return;
}
const char* digits = uppercase ? "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" :
"0123456789abcdefghijklmnopqrstuvwxyz";
if (value == 0) {
*ptr++ = '0';
*ptr = '\0';
return;
}
while (value > 0) {
int digit = value % base;
*ptr++ = digits[digit];
value /= base;
}
*ptr = '\0';
reverse_string(buffer, ptr - buffer);
}
static void int_to_str(int64_t value, char* buffer, int base, bool uppercase) {
if (value < 0 && (base == 10 || base == 8)) {
buffer[0] = '-';
uint_to_str(-value, buffer + 1, base, uppercase);
} else {
uint_to_str(value, buffer, base, uppercase);
}
}
static uint64_t pow10_u64(int n) {
uint64_t r = 1;
while (n-- > 0) r *= 10;
return r;
}
static int double_to_string(double value, char* buffer, int precision) {
if (isnan(value)) {
strcpy(buffer, "nan");
return 3;
}
if (isinf(value)) {
if (value < 0) {
strcpy(buffer, "-inf");
return 4;
} else {
strcpy(buffer, "inf");
return 3;
}
}
if (precision < 0) precision = 6;
if (precision > 16) precision = 16;
bool negative = (value < 0);
double abs_val = negative ? -value : value;
uint64_t mult = pow10_u64(precision);
double scaled = abs_val * mult + 0.5;
uint64_t int_scaled = (uint64_t)scaled;
uint64_t int_part = int_scaled / mult;
uint64_t frac_part = int_scaled % mult;
char* ptr = buffer;
if (negative) *ptr++ = '-';
char int_buf[32];
char* p = int_buf + sizeof(int_buf) - 1;
*p = '\0';
if (int_part == 0) {
*--p = '0';
} else {
uint64_t n = int_part;
while (n > 0) {
*--p = '0' + (n % 10);
n /= 10;
}
}
size_t int_len = (int_buf + sizeof(int_buf) - 1) - p;
memcpy(ptr, p, int_len);
ptr += int_len;
if (precision > 0) {
*ptr++ = '.';
char frac_buf[32];
char* f = frac_buf + sizeof(frac_buf) - 1;
*f = '\0';
if (frac_part == 0) {
for (int i = 0; i < precision; i++) {
*--f = '0';
}
} else {
uint64_t n = frac_part;
int count = 0;
while (n > 0) {
*--f = '0' + (n % 10);
n /= 10;
count++;
}
while (count < precision) {
*--f = '0';
count++;
}
}
size_t frac_len = (frac_buf + sizeof(frac_buf) - 1) - f;
memcpy(ptr, f, frac_len);
ptr += frac_len;
}
*ptr = '\0';
return ptr - buffer;
}
static int double_to_scientific(double value, char* buffer, int precision, bool uppercase) {
if (isnan(value)) {
strcpy(buffer, "nan");
return 3;
}
if (isinf(value)) {
if (value < 0) {
strcpy(buffer, "-inf");
return 4;
} else {
strcpy(buffer, "inf");
return 3;
}
}
if (value == 0.0) {
if (precision < 0) precision = 6;
buffer[0] = '0';
buffer[1] = '.';
for (int i = 0; i < precision; i++) {
buffer[2 + i] = '0';
}
buffer[2 + precision] = uppercase ? 'E' : 'e';
buffer[3 + precision] = '+';
buffer[4 + precision] = '0';
buffer[5 + precision] = '0';
buffer[6 + precision] = '\0';
return 6 + precision;
}
if (precision < 0) precision = 6;
if (precision > 16) precision = 16;
double abs_val = value < 0 ? -value : value;
int exponent = 0;
if (abs_val >= 10.0) {
while (abs_val >= 10.0) {
abs_val /= 10.0;
exponent++;
}
} else if (abs_val < 1.0 && abs_val > 0.0) {
while (abs_val < 1.0) {
abs_val *= 10.0;
exponent--;
}
}
if (value < 0) abs_val = -abs_val;
int len = double_to_string(abs_val, buffer, precision);
char e_char = uppercase ? 'E' : 'e';
buffer[len++] = e_char;
if (exponent >= 0) {
buffer[len++] = '+';
} else {
buffer[len++] = '-';
exponent = -exponent;
}
if (exponent < 10) {
buffer[len++] = '0';
buffer[len++] = '0' + exponent;
} else {
buffer[len++] = '0' + (exponent / 10);
buffer[len++] = '0' + (exponent % 10);
}
buffer[len] = '\0';
return len;
}
static int double_to_general(double value, char* buffer, int precision, bool uppercase) {
double abs_val = value < 0 ? -value : value;
if (abs_val == 0.0) {
strcpy(buffer, "0");
return 1;
}
if (precision < 0) precision = 6;
if (precision == 0) precision = 1;
bool use_scientific = (abs_val >= 1e6 || (abs_val < 1e-4 && abs_val > 0));
if (use_scientific) {
return double_to_scientific(value, buffer, precision - 1, uppercase);
}
int digits_before = 0;
double temp = abs_val;
while (temp >= 1.0) {
temp /= 10.0;
digits_before++;
}
if (digits_before == 0) digits_before = 1;
int decimal_places = precision - digits_before;
if (decimal_places < 0) decimal_places = 0;
return double_to_string(value, buffer, decimal_places);
}
void serial_printf_port(uint16_t port, const char* format, ...) {
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
while (__sync_lock_test_and_set(&serial_lock, 1)) {
__asm__ volatile("pause" ::: "memory");
}
va_list args;
va_start(args, format);
char buffer[256];
const char* ptr = format;
while (*ptr) {
if (*ptr != '%') {
serial_write_port(port, *ptr);
ptr++;
continue;
}
const char* percent_start = ptr++;
while (*ptr == '0' || *ptr == '-' || *ptr == '+' || *ptr == ' ' || *ptr == '#') {
ptr++;
}
int width = 0;
while (*ptr >= '0' && *ptr <= '9') {
width = width * 10 + (*ptr - '0');
ptr++;
}
int precision = -1;
if (*ptr == '.') {
ptr++;
precision = 0;
while (*ptr >= '0' && *ptr <= '9') {
precision = precision * 10 + (*ptr - '0');
ptr++;
}
}
bool has_ll = false;
bool has_l = false;
bool has_size_t = false;
if (*ptr == 'z') {
ptr++;
has_size_t = true;
} else if (*ptr == 'l') {
ptr++;
if (*ptr == 'l') {
ptr++;
has_ll = true;
} else {
has_l = true;
}
} else if (*ptr == 'h') {
ptr++;
if (*ptr == 'h') ptr++;
} else if (*ptr == 'L' || *ptr == 'j' || *ptr == 't') {
ptr++;
}
switch (*ptr) {
case 'c': {
char c = (char)va_arg(args, int);
serial_write_port(port, c);
break;
}
case 's': {
const char* str = va_arg(args, const char*);
if (!str) {
str = "(null)";
}
serial_writestring_port(port, str);
break;
}
case 'd':
case 'i': {
int64_t num;
if (has_size_t) {
num = (int64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, int64_t);
} else if (has_l) {
num = (int64_t)va_arg(args, long);
} else {
num = (int64_t)va_arg(args, int);
}
int_to_str(num, buffer, 10, false);
serial_writestring_port(port, buffer);
break;
}
case 'u': {
uint64_t num;
if (has_size_t) {
num = (uint64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, uint64_t);
} else if (has_l) {
num = (uint64_t)va_arg(args, unsigned long);
} else {
num = (uint64_t)va_arg(args, unsigned int);
}
uint_to_str(num, buffer, 10, false);
serial_writestring_port(port, buffer);
break;
}
case 'x': {
uint64_t num;
if (has_size_t) {
num = (uint64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, uint64_t);
} else if (has_l) {
num = (uint64_t)va_arg(args, unsigned long);
} else {
num = (uint64_t)va_arg(args, unsigned int);
}
uint_to_str(num, buffer, 16, false);
serial_writestring_port(port, buffer);
break;
}
case 'X': {
uint64_t num;
if (has_size_t) {
num = (uint64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, uint64_t);
} else if (has_l) {
num = (uint64_t)va_arg(args, unsigned long);
} else {
num = (uint64_t)va_arg(args, unsigned int);
}
uint_to_str(num, buffer, 16, true);
serial_writestring_port(port, buffer);
break;
}
case 'o': {
uint64_t num;
if (has_size_t) {
num = (uint64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, uint64_t);
} else if (has_l) {
num = (uint64_t)va_arg(args, unsigned long);
} else {
num = (uint64_t)va_arg(args, unsigned int);
}
uint_to_str(num, buffer, 8, false);
serial_writestring_port(port, buffer);
break;
}
case 'p': {
void* ptr_val = va_arg(args, void*);
serial_writestring_port(port, "0x");
uint_to_str((uintptr_t)ptr_val, buffer, 16, false);
serial_writestring_port(port, buffer);
break;
}
case 'f':
case 'F': {
double num = va_arg(args, double);
double_to_string(num, buffer, precision);
serial_writestring_port(port, buffer);
break;
}
case 'e':
case 'E': {
double num = va_arg(args, double);
double_to_scientific(num, buffer, precision, (*ptr == 'E'));
serial_writestring_port(port, buffer);
break;
}
case 'g':
case 'G': {
double num = va_arg(args, double);
double_to_general(num, buffer, precision, (*ptr == 'G'));
serial_writestring_port(port, buffer);
break;
}
case 'a':
case 'A': {
double num = va_arg(args, double);
double_to_scientific(num, buffer, precision, (*ptr == 'A'));
buffer[0] = '0';
buffer[1] = 'x';
serial_writestring_port(port, buffer);
break;
}
case 'n': {
int* count_ptr = va_arg(args, int*);
*count_ptr = 0;
break;
}
case '%': {
serial_write_port(port, '%');
break;
}
default: {
for (const char* p = percent_start; p <= ptr; p++) {
serial_write_port(port, *p);
}
break;
}
}
if (*ptr != '\0') {
ptr++;
}
}
va_end(args);
__sync_lock_release(&serial_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
}
void serial_printf(const char* format, ...) {
if (default_serial_port == 0) return;
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
while (__sync_lock_test_and_set(&serial_lock, 1)) {
__asm__ volatile("pause" ::: "memory");
}
va_list args;
va_start(args, format);
char buffer[256];
const char* ptr = format;
while (*ptr) {
if (*ptr != '%') {
serial_write_port(default_serial_port, *ptr);
ptr++;
continue;
}
const char* percent_start = ptr++;
while (*ptr == '0' || *ptr == '-' || *ptr == '+' || *ptr == ' ' || *ptr == '#') {
ptr++;
}
int width = 0;
while (*ptr >= '0' && *ptr <= '9') {
width = width * 10 + (*ptr - '0');
ptr++;
}
int precision = -1;
if (*ptr == '.') {
ptr++;
precision = 0;
while (*ptr >= '0' && *ptr <= '9') {
precision = precision * 10 + (*ptr - '0');
ptr++;
}
}
bool has_ll = false;
bool has_l = false;
bool has_size_t = false;
if (*ptr == 'z') {
ptr++;
has_size_t = true;
} else if (*ptr == 'l') {
ptr++;
if (*ptr == 'l') {
ptr++;
has_ll = true;
} else {
has_l = true;
}
} else if (*ptr == 'h') {
ptr++;
if (*ptr == 'h') ptr++;
} else if (*ptr == 'L' || *ptr == 'j' || *ptr == 't') {
ptr++;
}
switch (*ptr) {
case 'c': {
char c = (char)va_arg(args, int);
serial_write_port(default_serial_port, c);
break;
}
case 's': {
const char* str = va_arg(args, const char*);
if (!str) {
str = "(null)";
}
serial_writestring_port(default_serial_port, str);
break;
}
case 'd':
case 'i': {
int64_t num;
if (has_size_t) {
num = (int64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, int64_t);
} else if (has_l) {
num = (int64_t)va_arg(args, long);
} else {
num = (int64_t)va_arg(args, int);
}
int_to_str(num, buffer, 10, false);
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'u': {
uint64_t num;
if (has_size_t) {
num = (uint64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, uint64_t);
} else if (has_l) {
num = (uint64_t)va_arg(args, unsigned long);
} else {
num = (uint64_t)va_arg(args, unsigned int);
}
uint_to_str(num, buffer, 10, false);
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'x': {
uint64_t num;
if (has_size_t) {
num = (uint64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, uint64_t);
} else if (has_l) {
num = (uint64_t)va_arg(args, unsigned long);
} else {
num = (uint64_t)va_arg(args, unsigned int);
}
uint_to_str(num, buffer, 16, false);
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'X': {
uint64_t num;
if (has_size_t) {
num = (uint64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, uint64_t);
} else if (has_l) {
num = (uint64_t)va_arg(args, unsigned long);
} else {
num = (uint64_t)va_arg(args, unsigned int);
}
uint_to_str(num, buffer, 16, true);
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'o': {
uint64_t num;
if (has_size_t) {
num = (uint64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, uint64_t);
} else if (has_l) {
num = (uint64_t)va_arg(args, unsigned long);
} else {
num = (uint64_t)va_arg(args, unsigned int);
}
uint_to_str(num, buffer, 8, false);
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'p': {
void* ptr_val = va_arg(args, void*);
serial_writestring_port(default_serial_port, "0x");
uint_to_str((uintptr_t)ptr_val, buffer, 16, false);
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'f':
case 'F': {
double num = va_arg(args, double);
double_to_string(num, buffer, precision);
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'e':
case 'E': {
double num = va_arg(args, double);
double_to_scientific(num, buffer, precision, (*ptr == 'E'));
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'g':
case 'G': {
double num = va_arg(args, double);
double_to_general(num, buffer, precision, (*ptr == 'G'));
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'a':
case 'A': {
double num = va_arg(args, double);
double_to_scientific(num, buffer, precision, (*ptr == 'A'));
buffer[0] = '0';
buffer[1] = 'x';
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'n': {
int* count_ptr = va_arg(args, int*);
*count_ptr = 0;
break;
}
case '%': {
serial_write_port(default_serial_port, '%');
break;
}
default: {
for (const char* p = percent_start; p <= ptr; p++) {
serial_write_port(default_serial_port, *p);
}
break;
}
}
if (*ptr != '\0') {
ptr++;
}
}
va_end(args);
__sync_lock_release(&serial_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
}
+300
View File
@@ -0,0 +1,300 @@
#include <stdint.h>
#include <string.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <limine.h>
#include "../include/graphics/fb/fb.h"
#include "../include/io/serial.h"
#include "../include/gdt/gdt.h"
#include "../include/interrupts/interrupts.h"
#include "../include/interrupts/idt.h"
#include "../include/sse/fpu.h"
#include "../include/sse/sse.h"
#include "../include/memory/pmm.h"
#include "../include/memory/vmm.h"
#include "../include/memory/paging.h"
#include "../include/acpi/acpi.h"
#include "../include/apic/apic.h"
#include "../include/io/ports.h"
#include "../include/drivers/timer.h"
#include "../include/smp/smp.h"
#include "../include/smp/percpu.h"
#include "../include/sched/sched.h"
#include "../include/elf/elf.h"
#include "../include/syscall/syscall.h"
#include "../include/drivers/ps2.h"
#include "../include/fs/vfs.h"
#include "../include/fs/ramfs.h"
#include "../include/fs/devfs.h"
#include "../include/fs/initramfs.h"
#include "../include/drivers/ata.h"
#include "../include/drivers/blkdev.h"
#include "../include/drivers/disk.h"
#include "../include/drivers/partition.h"
#include "../include/fs/ext2.h"
#include "../include/fs/fat32.h"
__attribute__((used, section(".limine_requests")))
static volatile uint64_t limine_base_revision[] = LIMINE_BASE_REVISION(4);
__attribute__((used, section(".limine_requests")))
static volatile struct limine_framebuffer_request framebuffer_request = {
.id = LIMINE_FRAMEBUFFER_REQUEST_ID,
.revision = 0
};
__attribute__((used, section(".limine_requests")))
static volatile struct limine_memmap_request memmap_request = {
.id = LIMINE_MEMMAP_REQUEST_ID,
.revision = 0
};
__attribute__((used, section(".limine_requests")))
static volatile struct limine_mp_request mp_request = {
.id = LIMINE_MP_REQUEST_ID,
.revision = 0,
.flags = 0
};
__attribute__((used, section(".limine_requests")))
static volatile struct limine_hhdm_request hhdm_request = {
.id = LIMINE_HHDM_REQUEST_ID,
.revision = 0
};
__attribute__((used, section(".limine_requests")))
volatile struct limine_rsdp_request rsdp_request = {
.id = LIMINE_RSDP_REQUEST_ID,
.revision = 0,
.response = NULL
};
__attribute__((used, section(".limine_requests")))
static volatile struct limine_module_request module_request = {
.id = LIMINE_MODULE_REQUEST_ID,
.revision = 0
};
__attribute__((used, section(".limine_requests_start")))
static volatile uint64_t limine_requests_start_marker[] = LIMINE_REQUESTS_START_MARKER;
__attribute__((used, section(".limine_requests_end")))
static volatile uint64_t limine_requests_end_marker[] = LIMINE_REQUESTS_END_MARKER;
struct limine_framebuffer *global_framebuffer = NULL;
static void hcf(void) {
for (;;) {
asm ("hlt");
}
}
static void load_elf_module(void) {
if (!module_request.response) {
serial_writestring("[ELF] No module response from Limine\n");
return;
}
if (module_request.response->module_count == 0) {
serial_writestring("[ELF] No modules provided\n");
return;
}
struct limine_file *mod = module_request.response->modules[0];
serial_printf("[ELF] Module: path='%s' size=%llu addr=%p\n", mod->path, mod->size, mod->address);
elf_load_result_t r = elf_load(mod->address, (size_t)mod->size, 0);
if (r.error != ELF_OK) {
serial_printf("[ELF] LOAD FAILED: %s\n", elf_strerror(r.error));
return;
}
serial_printf("[ELF] Load OK! entry=0x%llx stack_top=0x%llx\n", r.entry, r.stack_top);
uint64_t cr3 = (uint64_t)pmm_virt_to_phys(r.pagemap->pml4);
task_t *t = task_create_user("init", r.entry, r.stack_top, cr3, 16, r.pagemap, 0, 0);
if (!t) {
serial_writestring("[ELF] task_create_user failed\n");
elf_unload(&r);
return;
}
t->brk_start = r.load_end;
t->brk_current = r.load_end;
int sr = vfs_init_stdio(t);
if (sr < 0)
serial_printf("[VFS] vfs_init_stdio failed: %d\n", sr);
else
serial_writestring("[VFS] stdio assigned to task\n");
serial_printf("[ELF] Task 'init' created: cr3=0x%llx\n", t->cr3);
}
void ps2_task(void* arg) {
(void)arg;
asm volatile ("sti");
ps2_init();
}
static bool detect_installed_system(void) {
blkdev_t *hda2 = blkdev_get_by_name("hda2");
if (hda2) {
uint16_t magic = 0;
if (blkdev_read(hda2, EXT2_SUPER_OFFSET + 56, &magic, sizeof(magic)) == 0
&& magic == EXT2_SUPER_MAGIC) {
serial_writestring("[boot] ext2 on hda2 -> installed system\n");
return true;
}
}
blkdev_t *hda1 = blkdev_get_by_name("hda1");
if (hda1) {
uint8_t sector[512];
if (hda1->ops->read_sectors(hda1, 0, 1, sector) == 0) {
if (sector[510] == 0x55 && sector[511] == (uint8_t)0xAA
&& memcmp(sector + 82, "FAT32", 5) == 0) {
serial_writestring("[boot] FAT32 on hda1 -> looks like installed system\n");
return true;
}
}
}
blkdev_t *hda = blkdev_get_by_name("hda");
if (hda) {
uint16_t magic = 0;
if (blkdev_read(hda, EXT2_SUPER_OFFSET + 56, &magic, sizeof(magic)) == 0
&& magic == EXT2_SUPER_MAGIC) {
serial_writestring("[boot] legacy ext2-on-whole-disk detected\n");
return true;
}
}
return false;
}
void kernel_main(void) {
serial_initialize(COM1, 115200);
serial_writestring("\n=== SERIAL PORT INITIALIZED ===\n");
if (LIMINE_BASE_REVISION_SUPPORTED(limine_base_revision) == false) {
serial_writestring("ERROR: Unsupported Limine base revision\n");
hcf();
}
gdt_init();
init_interrupt_system();
serial_writestring("GDT&IDT [OK]\n");
fpu_init();
sse_init();
enable_fsgsbase();
serial_writestring("FPU/SSE/FSGSBASE [OK]\n");
if (!framebuffer_request.response ||
framebuffer_request.response->framebuffer_count < 1) {
serial_writestring("ERROR: No framebuffer available\n");
hcf();
}
if (!memmap_request.response) {
serial_writestring("ERROR: No memory map available\n");
hcf();
}
if (!hhdm_request.response) {
serial_writestring("ERROR: No HHDM available\n");
hcf();
}
global_framebuffer = framebuffer_request.response->framebuffers[0];
pmm_init(memmap_request.response, hhdm_request.response);
slab_init();
serial_writestring("PMM [OK]\n");
paging_init();
serial_writestring("Paging [OK]\n");
vmm_init();
serial_writestring("VMM [OK]\n");
vfs_init();
serial_writestring("VFS [OK]\n");
vnode_t *rootfs = ramfs_create_root();
vfs_mount("/", rootfs);
vfs_set_mount_info("/", "ramfs", "ramfs");
vnode_unref(rootfs);
serial_writestring("rootfs [OK]\n");
vfs_mkdir("/dev", 0755);
vfs_mkdir("/bin", 0755);
vfs_mkdir("/etc", 0755);
vfs_mkdir("/tmp", 0755);
vfs_mkdir("/proc", 0755);
vfs_mkdir("/mnt", 0755);
vnode_t *devroot = devfs_create_root();
vfs_mount("/dev", devroot);
vfs_set_mount_info("/dev", "devfs", "devfs");
serial_writestring("devfs [OK]\n");
acpi_init();
acpi_print_tables();
serial_writestring("ACPI [OK]\n");
apic_init();
serial_writestring("APIC [OK]\n");
clear_screen();
smp_init(mp_request.response);
serial_writestring("SMP [OK]\n");
serial_writestring("Waiting until all APs are fully ready...\n");
while (smp_get_online_count() < (smp_get_cpu_count() - 1)) {
timer_sleep_ms(100);
}
serial_writestring("All APs ready.\n");
printf("Kernel initialized successfully!\n\n");
printf("Framebuffer: %dx%d, %d bpp\n", global_framebuffer->width, global_framebuffer->height, global_framebuffer->bpp);
printf("\nMemory Information:\n");
printf("HHDM offset: 0x%llx\n", hhdm_request.response->offset);
printf("Memory map entries: %llu\n", memmap_request.response->entry_count);
pmm_print_stats();
smp_print_info_fb();
printf("\nSystem: %u CPU cores detected\n\n", smp_get_cpu_count());
syscall_init();
disk_init();
serial_writestring("Disk subsystem [OK]\n");
bool skip_initramfs = detect_installed_system();
if (skip_initramfs) {
printf("[boot] installed Cervus detected on disk -- booting from disk\n");
} else {
serial_writestring("[boot] no installed system detected, using initramfs\n");
}
if (!skip_initramfs && module_request.response &&
module_request.response->module_count >= 2) {
struct limine_file *tar = module_request.response->modules[1];
serial_printf("[initramfs] module: '%s' size=%llu\n", tar->path, tar->size);
int r = initramfs_mount(tar->address, (size_t)tar->size);
if (r == 0)
serial_writestring("[initramfs] mounted OK\n");
else
serial_printf("[initramfs] mount FAILED: %d\n", r);
} else if (!skip_initramfs) {
serial_writestring("[initramfs] no TAR module (modules[1] missing)\n");
}
timer_init();
sched_init();
sched_notify_ready();
timer_sleep_ms(10);
clear_screen();
load_elf_module();
task_create("PS/2", ps2_task, NULL, 10);
serial_writestring("Manually triggering first reschedule...\n");
sched_reschedule();
while (1) {
hcf();
}
}
+320
View File
@@ -0,0 +1,320 @@
#include "../../include/memory/paging.h"
#include "../../include/memory/pmm.h"
#include "../../include/io/serial.h"
#include <string.h>
#include <stdio.h>
#define MAX_REGIONS 256
#define MAX_RESERVED 64
#define DEFAULT_ALLOC_BASE 0xFFFFFE0000000000ULL
static paging_region_t regions[MAX_REGIONS];
static size_t region_count = 0;
static struct {
uintptr_t start;
uintptr_t end;
} reserved_ranges[MAX_RESERVED];
static size_t reserved_count = 0;
static uintptr_t next_alloc_virt = DEFAULT_ALLOC_BASE;
static inline uintptr_t align_up(uintptr_t addr, size_t alignment) {
return (addr + alignment - 1) & ~(alignment - 1);
}
static inline bool is_aligned(uintptr_t addr, size_t alignment) {
return (addr & (alignment - 1)) == 0;
}
static inline bool ranges_overlap(uintptr_t s1, uintptr_t e1, uintptr_t s2, uintptr_t e2) {
return s1 < e2 && s2 < e1;
}
void paging_init(void) {
memset(regions, 0, sizeof(regions));
memset(reserved_ranges, 0, sizeof(reserved_ranges));
region_count = 0;
reserved_count = 0;
next_alloc_virt = DEFAULT_ALLOC_BASE;
paging_reserve_range(vmm_get_kernel_pagemap(),
0xFFFFFFFF80000000ULL,
0xFFFFFFFFC0000000ULL);
serial_printf("Paging: subsystem initialized\n");
}
bool paging_reserve_range(vmm_pagemap_t* pagemap, uintptr_t virt_start, uintptr_t virt_end) {
(void)pagemap;
if (reserved_count >= MAX_RESERVED) {
serial_printf("PAGING ERROR: too many reserved ranges\n");
return false;
}
if (virt_start >= virt_end) {
serial_printf("PAGING ERROR: invalid reserved range\n");
return false;
}
for (size_t i = 0; i < reserved_count; i++) {
if (ranges_overlap(virt_start, virt_end,
reserved_ranges[i].start,
reserved_ranges[i].end)) {
serial_printf("PAGING ERROR: reserved range overlaps\n");
return false;
}
}
reserved_ranges[reserved_count].start = virt_start;
reserved_ranges[reserved_count].end = virt_end;
reserved_count++;
serial_printf("Paging: reserved range 0x%llx-0x%llx\n", virt_start, virt_end);
return true;
}
static bool is_range_reserved(uintptr_t start, uintptr_t end) {
for (size_t i = 0; i < reserved_count; i++) {
if (ranges_overlap(start, end, reserved_ranges[i].start, reserved_ranges[i].end))
return true;
}
return false;
}
bool paging_is_range_free(vmm_pagemap_t* pagemap, uintptr_t virt_start, uintptr_t virt_end) {
if (is_range_reserved(virt_start, virt_end))
return false;
for (uintptr_t virt = virt_start; virt < virt_end; virt += PAGE_SIZE) {
uintptr_t phys;
if (vmm_virt_to_phys(pagemap, virt, &phys))
return false;
}
return true;
}
bool paging_map_range(vmm_pagemap_t* pagemap, uintptr_t virt_start,
uintptr_t phys_start, size_t page_count, uint64_t flags) {
if (!pagemap || page_count == 0)
return false;
if (!is_aligned(virt_start, PAGE_SIZE) || !is_aligned(phys_start, PAGE_SIZE)) {
serial_printf("PAGING ERROR: unaligned addresses\n");
return false;
}
uintptr_t virt_end = virt_start + page_count * PAGE_SIZE;
if (is_range_reserved(virt_start, virt_end)) {
serial_printf("PAGING ERROR: range overlaps reserved area\n");
return false;
}
for (size_t i = 0; i < page_count; i++) {
uintptr_t virt = virt_start + i * PAGE_SIZE;
uintptr_t phys = phys_start + i * PAGE_SIZE;
uintptr_t existing_phys;
if (vmm_virt_to_phys(pagemap, virt, &existing_phys)) {
if (existing_phys != phys) {
serial_printf("PAGING ERROR: page at 0x%llx already mapped to 0x%llx\n",
virt, existing_phys);
return false;
}
uint64_t existing_flags;
if (vmm_get_page_flags(pagemap, virt, &existing_flags)) {
if (existing_flags != (flags & 0xFFF)) {
vmm_unmap_page(pagemap, virt);
vmm_map_page(pagemap, virt, phys, flags);
}
}
} else {
if (!vmm_map_page(pagemap, virt, phys, flags)) {
serial_printf("PAGING ERROR: failed to map 0x%llx -> 0x%llx\n", virt, phys);
for (size_t j = 0; j < i; j++)
vmm_unmap_page(pagemap, virt_start + j * PAGE_SIZE);
return false;
}
}
}
return true;
}
bool paging_unmap_range(vmm_pagemap_t* pagemap, uintptr_t virt_start, size_t page_count) {
if (!pagemap || page_count == 0)
return false;
for (size_t i = 0; i < page_count; i++)
vmm_unmap_page(pagemap, virt_start + i * PAGE_SIZE);
return true;
}
bool paging_change_flags(vmm_pagemap_t* pagemap, uintptr_t virt_start,
size_t page_count, uint64_t new_flags) {
if (!pagemap || page_count == 0)
return false;
if (!is_aligned(virt_start, PAGE_SIZE)) {
serial_printf("PAGING ERROR: unaligned address 0x%llx\n", virt_start);
return false;
}
for (size_t i = 0; i < page_count; i++) {
uintptr_t virt = virt_start + i * PAGE_SIZE;
uintptr_t phys;
if (!vmm_virt_to_phys(pagemap, virt, &phys)) {
serial_printf("PAGING ERROR: page at 0x%llx not mapped\n", virt);
return false;
}
vmm_unmap_page(pagemap, virt);
if (!vmm_map_page(pagemap, virt, phys, new_flags)) {
serial_printf("PAGING ERROR: failed to remap 0x%llx\n", virt);
return false;
}
}
return true;
}
paging_region_t* paging_create_region(vmm_pagemap_t* pagemap,
uintptr_t virt_start, size_t size,
uint64_t flags) {
if (region_count >= MAX_REGIONS) {
serial_printf("PAGING ERROR: too many regions\n");
return NULL;
}
size_t page_count = align_up(size, PAGE_SIZE) / PAGE_SIZE;
uintptr_t virt_end = virt_start + page_count * PAGE_SIZE;
if (!paging_is_range_free(pagemap, virt_start, virt_end)) {
serial_printf("PAGING ERROR: region overlaps existing mapping\n");
return NULL;
}
void* phys_mem = pmm_alloc_zero(page_count);
if (!phys_mem) {
serial_printf("PAGING ERROR: out of physical memory\n");
return NULL;
}
uintptr_t phys_start = pmm_virt_to_phys(phys_mem);
if (!paging_map_range(pagemap, virt_start, phys_start, page_count, flags)) {
pmm_free(phys_mem, page_count);
serial_printf("PAGING ERROR: failed to map region\n");
return NULL;
}
paging_region_t* region = &regions[region_count++];
region->virtual_start = virt_start;
region->virtual_end = virt_end;
region->physical_start = phys_start;
region->flags = flags;
region->page_count = page_count;
region->allocated = true;
serial_printf("Paging: created region 0x%llx-0x%llx (%zu pages)\n",
virt_start, virt_end, page_count);
return region;
}
bool paging_destroy_region(vmm_pagemap_t* pagemap, paging_region_t* region) {
if (!pagemap || !region || !region->allocated)
return false;
paging_unmap_range(pagemap, region->virtual_start, region->page_count);
void* phys_virt = pmm_phys_to_virt(region->physical_start);
pmm_free(phys_virt, region->page_count);
memset(region, 0, sizeof(paging_region_t));
return true;
}
void* paging_alloc_pages(vmm_pagemap_t* pagemap, size_t page_count,
uint64_t flags, uintptr_t preferred_virt) {
if (!pagemap || page_count == 0)
return NULL;
uintptr_t virt_start;
if (preferred_virt != 0) {
virt_start = align_up(preferred_virt, PAGE_SIZE);
if (!paging_is_range_free(pagemap, virt_start, virt_start + page_count * PAGE_SIZE))
return NULL;
} else {
virt_start = next_alloc_virt;
while (!paging_is_range_free(pagemap, virt_start, virt_start + page_count * PAGE_SIZE))
virt_start += PAGE_SIZE;
next_alloc_virt = virt_start + page_count * PAGE_SIZE;
}
void* phys_mem = pmm_alloc_zero(page_count);
if (!phys_mem)
return NULL;
uintptr_t phys_start = pmm_virt_to_phys(phys_mem);
if (!paging_map_range(pagemap, virt_start, phys_start, page_count, flags)) {
pmm_free(phys_mem, page_count);
return NULL;
}
serial_printf("Paging: allocated %zu pages at virt 0x%llx\n", page_count, virt_start);
return (void*)virt_start;
}
void paging_free_pages(vmm_pagemap_t* pagemap, void* virt_addr, size_t page_count) {
if (!pagemap || !virt_addr || page_count == 0)
return;
uintptr_t virt_start = (uintptr_t)virt_addr;
paging_unmap_range(pagemap, virt_start, page_count);
serial_printf("Paging: freed %zu pages at virt 0x%llx\n", page_count, virt_start);
}
void paging_print_stats(vmm_pagemap_t* pagemap) {
(void)pagemap;
serial_printf("\n=== Paging Statistics ===\n");
serial_printf("Regions: %zu, Reserved: %zu\n", region_count, reserved_count);
size_t active = 0, total_pages = 0, total_bytes = 0;
for (size_t i = 0; i < region_count; i++) {
if (regions[i].allocated) {
active++;
total_pages += regions[i].page_count;
total_bytes += regions[i].page_count * PAGE_SIZE;
}
}
serial_printf("Active regions: %zu\n", active);
serial_printf("Total pages: %zu (%zu KiB)\n", total_pages, total_bytes / 1024);
serial_printf("Next alloc: 0x%llx\n", next_alloc_virt);
serial_printf("\nReserved ranges:\n");
for (size_t i = 0; i < reserved_count; i++) {
serial_printf(" 0x%llx - 0x%llx\n",
reserved_ranges[i].start, reserved_ranges[i].end);
}
}
void paging_dump_range(vmm_pagemap_t* pagemap, uintptr_t virt_start, uintptr_t virt_end) {
serial_printf("\n=== Page Table Dump (0x%llx - 0x%llx) ===\n", virt_start, virt_end);
size_t mapped = 0, total = 0;
for (uintptr_t virt = virt_start; virt < virt_end; virt += PAGE_SIZE) {
total++;
uintptr_t phys;
uint64_t flags;
if (vmm_virt_to_phys(pagemap, virt, &phys) && vmm_get_page_flags(pagemap, virt, &flags)) {
mapped++;
serial_printf("0x%llx -> 0x%llx [", virt, phys);
if (flags & PAGING_PRESENT) serial_printf("P");
if (flags & PAGING_WRITE) serial_printf("W");
if (flags & PAGING_USER) serial_printf("U");
if (flags & PAGING_NOEXEC) serial_printf("NX");
serial_printf("]\n");
}
}
serial_printf("Mapped: %zu/%zu pages (%zu%%)\n", mapped, total, total ? mapped * 100 / total : 0);
}
+614
View File
@@ -0,0 +1,614 @@
#include "../../include/memory/pmm.h"
#include "../../include/io/serial.h"
#include "../../include/sched/spinlock.h"
#include <string.h>
#include <stdio.h>
static pmm_buddy_state_t g_buddy;
static spinlock_t g_pmm_lock = SPINLOCK_INIT;
static inline uintptr_t _align_up(uintptr_t v, uintptr_t a) {
return (v + a - 1) & ~(a - 1);
}
static inline int _pages_to_order(size_t pages) {
int order = 0;
size_t cap = 1;
while (cap < pages) { cap <<= 1; order++; }
return (order > PMM_MAX_ORDER) ? -1 : order;
}
static inline uintptr_t _block_phys(pmm_block_t *b) {
return (uintptr_t)b - g_buddy.hhdm_offset;
}
static inline pmm_block_t *_phys_to_block(uintptr_t phys) {
return (pmm_block_t *)(phys + g_buddy.hhdm_offset);
}
static inline void _fl_init(pmm_free_list_t *fl) {
fl->head.next = fl->head.prev = &fl->head;
fl->head.order = -1;
fl->count = 0;
}
static inline void _fl_push(pmm_free_list_t *fl, pmm_block_t *b, int order) {
b->order = order;
b->next = fl->head.next;
b->prev = &fl->head;
fl->head.next->prev = b;
fl->head.next = b;
fl->count++;
}
static inline void _fl_del(pmm_free_list_t *fl, pmm_block_t *b) {
b->prev->next = b->next;
b->next->prev = b->prev;
b->next = b->prev = NULL;
b->order = -1;
fl->count--;
}
static inline pmm_block_t *_fl_first(pmm_free_list_t *fl) {
return (fl->head.next == &fl->head) ? NULL : fl->head.next;
}
static inline int _block_ptr_valid(pmm_free_list_t *fl, pmm_block_t *b) {
uintptr_t p = (uintptr_t)b;
if (p == (uintptr_t)&fl->head) return 1;
if (p < g_buddy.hhdm_offset) return 0;
uintptr_t phys = p - g_buddy.hhdm_offset;
if (phys < g_buddy.mem_start || phys >= g_buddy.mem_end) return 0;
if (phys & 0xFFFULL) return 0;
return 1;
}
static pmm_block_t *_fl_find(pmm_free_list_t *fl, uintptr_t phys) {
size_t limit = g_buddy.total_pages + 16;
pmm_block_t *b = fl->head.next;
while (b != &fl->head) {
if (!_block_ptr_valid(fl, b)) {
serial_printf("[PMM_FREELIST_CORRUPT] bad ptr=%p in freelist walk\n", (void*)b);
return NULL;
}
if (_block_phys(b) == phys) return b;
pmm_block_t *next = b->next;
if (!_block_ptr_valid(fl, next)) {
serial_printf("[PMM_FREELIST_CORRUPT] bad next=%p at block=%p (phys=0x%llx)\n",
(void*)next, (void*)b,
(unsigned long long)_block_phys(b));
return NULL;
}
b = next;
if (limit-- == 0) return NULL;
}
return NULL;
}
static uintptr_t _buddy_alloc_order(int order) {
int found = -1;
for (int o = order; o <= PMM_MAX_ORDER; o++)
if (_fl_first(&g_buddy.orders[o])) { found = o; break; }
if (found < 0) return 0;
pmm_block_t *b = _fl_first(&g_buddy.orders[found]);
_fl_del(&g_buddy.orders[found], b);
uintptr_t phys = _block_phys(b);
while (found > order) {
found--;
uintptr_t buddy_phys = phys + ((uintptr_t)PAGE_SIZE << found);
_fl_push(&g_buddy.orders[found], _phys_to_block(buddy_phys), found);
}
g_buddy.free_pages -= (size_t)1 << order;
return phys;
}
static void _buddy_free_order(uintptr_t phys, int order) {
if (phys < PMM_FREE_MIN_PHYS) return;
while (order < PMM_MAX_ORDER) {
uintptr_t buddy_phys = phys ^ ((uintptr_t)PAGE_SIZE << order);
if (buddy_phys < PMM_FREE_MIN_PHYS) break;
if (buddy_phys < g_buddy.mem_start || buddy_phys >= g_buddy.mem_end)
break;
pmm_block_t *buddy_b = _fl_find(&g_buddy.orders[order], buddy_phys);
if (!buddy_b) break;
if (buddy_b->order != order) break;
_fl_del(&g_buddy.orders[order], buddy_b);
if (buddy_phys < phys) phys = buddy_phys;
order++;
}
_fl_push(&g_buddy.orders[order], _phys_to_block(phys), order);
g_buddy.free_pages += (size_t)1 << order;
}
static void _buddy_free_nocoalesce(uintptr_t phys) {
_fl_push(&g_buddy.orders[0], _phys_to_block(phys), 0);
g_buddy.free_pages += 1;
}
void pmm_init(struct limine_memmap_response *memmap,
struct limine_hhdm_response *hhdm) {
g_buddy.hhdm_offset = hhdm->offset;
g_buddy.free_pages = 0;
uintptr_t max_phys = 0;
size_t usable_pages = 0;
for (uint64_t i = 0; i < memmap->entry_count; i++) {
struct limine_memmap_entry *e = memmap->entries[i];
if (e->type == LIMINE_MEMMAP_USABLE) {
uintptr_t end = e->base + e->length;
if (end > max_phys) max_phys = end;
usable_pages += e->length / PAGE_SIZE;
}
}
max_phys = _align_up(max_phys, PAGE_SIZE);
g_buddy.mem_start = 0;
g_buddy.mem_end = max_phys;
g_buddy.total_pages = max_phys >> PAGE_SHIFT;
g_buddy.usable_pages = usable_pages;
for (int o = 0; o < PMM_MAX_ORDER_NR; o++) _fl_init(&g_buddy.orders[o]);
for (uint64_t i = 0; i < memmap->entry_count; i++) {
struct limine_memmap_entry *e = memmap->entries[i];
if (e->type != LIMINE_MEMMAP_USABLE) continue;
uintptr_t base = _align_up(e->base, PAGE_SIZE);
uintptr_t end = (e->base + e->length) & ~(PAGE_SIZE - 1);
if (base < PMM_FREE_MIN_PHYS) {
base = PMM_FREE_MIN_PHYS;
if (base >= end) continue;
}
while (base < end) {
size_t rem = (end - base) >> PAGE_SHIFT;
int order = PMM_MAX_ORDER;
while (order > 0) {
size_t bpages = (size_t)1 << order;
if ((base & (bpages * PAGE_SIZE - 1)) == 0 && rem >= bpages)
break;
order--;
}
_fl_push(&g_buddy.orders[order], _phys_to_block(base), order);
g_buddy.free_pages += (size_t)1 << order;
base += (uintptr_t)(PAGE_SIZE << order);
}
}
serial_printf("[PMM] buddy init: total=%zu free=%zu\n",
g_buddy.total_pages, g_buddy.free_pages);
}
static volatile uint64_t g_pmm_alloc_count = 0;
static volatile uint64_t g_pmm_free_count = 0;
void *pmm_alloc(size_t pages) {
if (!pages) return NULL;
int order = _pages_to_order(pages);
if (order < 0) return NULL;
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
spinlock_acquire(&g_pmm_lock);
uintptr_t phys = _buddy_alloc_order(order);
size_t free_after = g_buddy.free_pages;
spinlock_release(&g_pmm_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
if (phys) {
if (phys < PMM_FREE_MIN_PHYS) {
serial_printf("[PMM_BUG] alloc returned low phys=0x%llx — discarding (should not happen after pmm_init fix)\n",
(unsigned long long)phys);
phys = 0;
} else {
__atomic_fetch_add(&g_pmm_alloc_count, (size_t)1 << order, __ATOMIC_RELAXED);
}
}
if (!phys) {
serial_printf("[PMM_OOM] alloc FAILED pages=%zu order=%d free=%zu allocs=%llu frees=%llu\n",
pages, order, free_after,
(unsigned long long)g_pmm_alloc_count,
(unsigned long long)g_pmm_free_count);
}
return phys ? (void *)(phys + g_buddy.hhdm_offset) : NULL;
}
void *pmm_alloc_zero(size_t pages) {
if (!pages) return NULL;
int order = _pages_to_order(pages);
if (order < 0) return NULL;
void *p = pmm_alloc(pages);
if (p) memset(p, 0, pages * PAGE_SIZE);
return p;
}
void *pmm_alloc_aligned(size_t pages, size_t alignment) {
if (!pages) return NULL;
if (alignment < PAGE_SIZE) alignment = PAGE_SIZE;
if (alignment & (alignment - 1)) {
size_t a = 1;
while (a < alignment) a <<= 1;
alignment = a;
}
size_t align_pages = alignment / PAGE_SIZE;
size_t req = pages > align_pages ? pages : align_pages;
int order = _pages_to_order(req);
if (order < 0) return NULL;
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
spinlock_acquire(&g_pmm_lock);
uintptr_t phys = _buddy_alloc_order(order);
if (!phys) { spinlock_release(&g_pmm_lock); asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc"); return NULL; }
if (phys & (alignment - 1)) {
_buddy_free_order(phys, order);
spinlock_release(&g_pmm_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
return NULL;
}
spinlock_release(&g_pmm_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
return (void *)(phys + g_buddy.hhdm_offset);
}
void pmm_free(void *addr, size_t pages) {
if (!addr || !pages) return;
uintptr_t phys = (uintptr_t)addr - g_buddy.hhdm_offset;
if (phys < g_buddy.mem_start || phys >= g_buddy.mem_end) {
serial_printf("[PMM_FREE_BUG] out-of-range addr=%p phys=0x%llx pages=%zu\n",
addr, (unsigned long long)phys, pages);
return;
}
if (phys < 0x100000ULL) {
serial_printf("[PMM_FREE_BUG] low-phys addr=%p phys=0x%llx pages=%zu\n",
addr, (unsigned long long)phys, pages);
return;
}
int order = _pages_to_order(pages);
if (order < 0) { serial_printf("[PMM_FREE] bad order pages=%zu\n", pages); return; }
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
spinlock_acquire(&g_pmm_lock);
pmm_block_t *existing = _fl_find(&g_buddy.orders[order], phys);
if (existing) {
serial_printf("[PMM_DOUBLE_FREE] addr=%p phys=0x%llx pages=%zu order=%d ALREADY FREE!\n",
addr, (unsigned long long)phys, pages, order);
spinlock_release(&g_pmm_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
return;
}
size_t free_before = g_buddy.free_pages;
memset(addr, 0, 64);
_buddy_free_order(phys, order);
size_t free_after = g_buddy.free_pages;
__atomic_fetch_add(&g_pmm_free_count, (size_t)1 << order, __ATOMIC_RELAXED);
if (free_after <= free_before) {
serial_printf("[PMM_FREE_BUG] free_pages didn't grow: before=%zu after=%zu phys=0x%llx\n",
free_before, free_after, (unsigned long long)phys);
}
spinlock_release(&g_pmm_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
}
void pmm_free_single(void *addr) {
if (!addr) return;
uintptr_t phys = (uintptr_t)addr - g_buddy.hhdm_offset;
if (phys < g_buddy.mem_start || phys >= g_buddy.mem_end) return;
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
spinlock_acquire(&g_pmm_lock);
_buddy_free_nocoalesce(phys);
spinlock_release(&g_pmm_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
}
void *pmm_phys_to_virt(uintptr_t phys) { return (void *)(phys + g_buddy.hhdm_offset); }
uintptr_t pmm_virt_to_phys(void *vaddr) { return (uintptr_t)vaddr - g_buddy.hhdm_offset; }
uint64_t pmm_get_hhdm_offset(void) { return g_buddy.hhdm_offset; }
size_t pmm_get_total_pages(void) { return g_buddy.total_pages; }
size_t pmm_get_usable_pages(void) { return g_buddy.usable_pages; }
size_t pmm_get_free_pages(void) { return g_buddy.free_pages; }
size_t pmm_get_used_pages(void) { return g_buddy.usable_pages > g_buddy.free_pages
? g_buddy.usable_pages - g_buddy.free_pages : 0; }
static void _print_size(size_t bytes, const char *label) {
uint64_t v = (uint64_t)bytes;
const char *unit;
uint64_t whole, frac;
if (v >= 1024ULL * 1024 * 1024) {
whole = v / (1024ULL * 1024 * 1024);
frac = (v % (1024ULL * 1024 * 1024)) * 100 / (1024ULL * 1024 * 1024);
unit = "GiB";
} else if (v >= 1024ULL * 1024) {
whole = v / (1024ULL * 1024);
frac = (v % (1024ULL * 1024)) * 100 / (1024ULL * 1024);
unit = "MiB";
} else if (v >= 1024ULL) {
whole = v / 1024;
frac = (v % 1024) * 100 / 1024;
unit = "KiB";
} else {
whole = v; frac = 0; unit = "B";
}
printf(" %-16s %llu.%02llu %s\n", label,
(unsigned long long)whole, (unsigned long long)frac, unit);
serial_printf(" %-16s %llu.%02llu %s\n", label,
(unsigned long long)whole, (unsigned long long)frac, unit);
}
void pmm_print_stats(void) {
size_t usable_bytes = g_buddy.usable_pages * PAGE_SIZE;
size_t free_bytes = g_buddy.free_pages * PAGE_SIZE;
size_t total_bytes = g_buddy.total_pages * PAGE_SIZE;
size_t used_bytes = usable_bytes > free_bytes ? usable_bytes - free_bytes : 0;
size_t reserved = total_bytes > usable_bytes ? total_bytes - usable_bytes : 0;
serial_printf("\n=== Physical Memory ===\n");
printf("\n=== Physical Memory ===\n");
_print_size(usable_bytes, "Usable RAM:");
_print_size(free_bytes, "Free RAM:");
_print_size(used_bytes, "Used (kernel):");
_print_size(reserved, "Reserved/MMIO:");
serial_printf(" %-16s %u bytes\n", "Page size:", (unsigned)PAGE_SIZE);
printf(" %-16s %u bytes\n", "Page size:", (unsigned)PAGE_SIZE);
serial_printf("=======================\n");
printf("=======================\n");
serial_printf("[PMM] buddy free-list:\n");
for (int o = 0; o < PMM_MAX_ORDER_NR; o++) {
if (g_buddy.orders[o].count)
serial_printf(" order %2d (%4zu KiB): %zu blocks\n",
o, (PAGE_SIZE << o) / 1024,
g_buddy.orders[o].count);
}
}
#define SLAB_PAGE_ALLOC(n) pmm_alloc_zero(n)
#define SLAB_PAGE_FREE(p, n) pmm_free(p, n)
static const size_t g_size_classes[SLAB_NUM_CACHES] = {
8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096
};
slab_cache_t g_caches[SLAB_NUM_CACHES];
static inline uintptr_t _slab_obj_start(slab_t *s) {
return _align_up((uintptr_t)s + sizeof(slab_t), 8);
}
static inline size_t _slab_pages(size_t obj_size) {
uintptr_t hdr_end = _align_up(sizeof(slab_t), 8);
if (obj_size <= PAGE_SIZE - hdr_end)
return 1;
if (obj_size <= PAGE_SIZE)
return 2;
return 0;
}
static inline uint16_t _slab_capacity(size_t obj_size) {
size_t pages = _slab_pages(obj_size);
if (pages == 0) return 0;
uintptr_t obj_start = _align_up(sizeof(slab_t), 8);
size_t avail = pages * PAGE_SIZE - obj_start;
uint16_t cap = (uint16_t)(avail / obj_size);
return cap > 0 ? cap : 0;
}
static void _slab_list_push(slab_t **head, slab_t *s) {
s->next = *head; s->prev = NULL;
if (*head) (*head)->prev = s;
*head = s;
}
static void _slab_list_remove(slab_t **head, slab_t *s) {
if (s->prev) s->prev->next = s->next;
else *head = s->next;
if (s->next) s->next->prev = s->prev;
s->next = s->prev = NULL;
}
static slab_t *_slab_new(slab_cache_t *cache) {
uint16_t cap = _slab_capacity(cache->obj_size);
if (!cap) return NULL;
size_t pages = _slab_pages(cache->obj_size);
slab_t *s = (slab_t *)SLAB_PAGE_ALLOC(pages);
if (!s) return NULL;
s->obj_size = (uint16_t)cache->obj_size;
s->total = cap;
s->used = 0;
s->next = s->prev = NULL;
uintptr_t start = _slab_obj_start(s);
s->freelist = (void *)start;
for (uint16_t i = 0; i < s->total; i++) {
void **slot = (void **)(start + (uintptr_t)i * cache->obj_size);
*slot = (i + 1 < s->total)
? (void *)(start + (uintptr_t)(i + 1) * cache->obj_size)
: NULL;
}
return s;
}
#define LARGE_ALLOC_MAGIC 0xDEADBEEFCAFEBABEULL
typedef struct {
uint64_t magic;
uint64_t pages;
} large_hdr_t;
static inline bool _is_large_alloc(void *ptr) {
large_hdr_t *hdr = (large_hdr_t *)ptr - 1;
return hdr->magic == LARGE_ALLOC_MAGIC;
}
static slab_cache_t *_cache_for(size_t size) {
for (int i = 0; i < SLAB_NUM_CACHES; i++)
if (g_caches[i].obj_size >= size) return &g_caches[i];
return NULL;
}
void slab_init(void) {
for (int i = 0; i < SLAB_NUM_CACHES; i++) {
g_caches[i].obj_size = g_size_classes[i];
g_caches[i].partial = NULL;
g_caches[i].full = NULL;
g_caches[i].total_allocs = 0;
g_caches[i].total_frees = 0;
}
serial_printf("[PMM] slab init: %d caches, sizes 8..4096 bytes\n",
SLAB_NUM_CACHES);
}
static spinlock_t g_slab_lock = SPINLOCK_INIT;
void *kmalloc(size_t size) {
if (!size) return NULL;
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
spinlock_acquire(&g_slab_lock);
void *result;
if (size > SLAB_MAX_SIZE) {
size_t pages = (size + sizeof(large_hdr_t) + PAGE_SIZE - 1) / PAGE_SIZE;
spinlock_release(&g_slab_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
large_hdr_t *hdr = (large_hdr_t *)SLAB_PAGE_ALLOC(pages);
if (!hdr) return NULL;
hdr->magic = LARGE_ALLOC_MAGIC;
hdr->pages = (uint64_t)pages;
return (void *)(hdr + 1);
}
slab_cache_t *cache = _cache_for(size);
if (!cache) { spinlock_release(&g_slab_lock); asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc"); return NULL; }
if (!cache->partial) {
spinlock_release(&g_slab_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
slab_t *s = _slab_new(cache);
if (!s) return NULL;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
spinlock_acquire(&g_slab_lock);
_slab_list_push(&cache->partial, s);
}
slab_t *s = cache->partial;
void *obj = s->freelist;
s->freelist = *(void **)obj;
s->used++;
cache->total_allocs++;
if (s->used == s->total) {
_slab_list_remove(&cache->partial, s);
_slab_list_push(&cache->full, s);
}
result = obj;
spinlock_release(&g_slab_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
return result;
}
void *kzalloc(size_t size) {
void *p = kmalloc(size);
if (p) memset(p, 0, size);
return p;
}
void kfree(void *ptr) {
if (!ptr) return;
if (_is_large_alloc(ptr)) {
large_hdr_t *hdr = (large_hdr_t *)ptr - 1;
size_t pages = (size_t)hdr->pages;
hdr->magic = 0;
SLAB_PAGE_FREE(hdr, pages);
return;
}
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
spinlock_acquire(&g_slab_lock);
slab_t *s = (slab_t *)((uintptr_t)ptr & ~((uintptr_t)PAGE_SIZE - 1));
slab_cache_t *cache = NULL;
for (int i = 0; i < SLAB_NUM_CACHES; i++) {
if (g_caches[i].obj_size == s->obj_size) {
cache = &g_caches[i];
break;
}
}
if (!cache) { spinlock_release(&g_slab_lock); asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc"); return; }
bool was_full = (s->used == s->total);
*(void **)ptr = s->freelist;
s->freelist = ptr;
s->used--;
cache->total_frees++;
if (was_full) {
_slab_list_remove(&cache->full, s);
_slab_list_push(&cache->partial, s);
}
if (s->used == 0) {
_slab_list_remove(&cache->partial, s);
size_t pages = _slab_pages(s->obj_size);
spinlock_release(&g_slab_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
SLAB_PAGE_FREE(s, pages > 0 ? pages : 1);
return;
}
spinlock_release(&g_slab_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
}
void *krealloc(void *ptr, size_t new_size) {
if (!ptr) return kmalloc(new_size);
if (!new_size) { kfree(ptr); return NULL; }
size_t old_size;
if (_is_large_alloc(ptr)) {
large_hdr_t *hdr = (large_hdr_t *)ptr - 1;
old_size = (size_t)hdr->pages * PAGE_SIZE - sizeof(large_hdr_t);
} else {
slab_t *s = (slab_t *)((uintptr_t)ptr & ~((uintptr_t)PAGE_SIZE - 1));
old_size = s->obj_size;
}
void *np = kmalloc(new_size);
if (!np) return NULL;
memcpy(np, ptr, old_size < new_size ? old_size : new_size);
kfree(ptr);
return np;
}
void slab_print_stats(void) {
serial_printf("[PMM] slab stats:\n");
for (int i = 0; i < SLAB_NUM_CACHES; i++) {
slab_cache_t *c = &g_caches[i];
size_t np = 0, nf = 0;
for (slab_t *s = c->partial; s && np < 10000; s = s->next) np++;
for (slab_t *s = c->full; s && nf < 10000; s = s->next) nf++;
serial_printf(" [%4zu B] partial=%zu full=%zu allocs=%zu frees=%zu\n",
c->obj_size, np, nf, c->total_allocs, c->total_frees);
}
}
+402
View File
@@ -0,0 +1,402 @@
#include "../../include/memory/vmm.h"
#include "../../include/memory/pmm.h"
#include "../../include/smp/smp.h"
#include "../../include/apic/apic.h"
#include "../../include/io/serial.h"
#include <stdio.h>
#include <string.h>
#define KERNEL_TEST_BASE 0xFFFF800000100000ULL
#define PTE_PHYS_MASK 0x000FFFFFFFFFF000ULL
#define MASK 0x1FF
static vmm_pagemap_t kernel_pagemap;
static inline void invlpg(void* addr) {
asm volatile ("invlpg (%0)" :: "r"(addr) : "memory");
}
static vmm_pte_t* alloc_table(void) {
void* page = pmm_alloc_zero(1);
if (!page) {
serial_printf("[VMM] alloc_table: OUT OF MEMORY (pmm_alloc_zero(1) returned NULL)!\n");
serial_printf("[VMM] free_pages=%zu\n", (size_t)pmm_get_free_pages());
for (;;) asm volatile ("hlt");
}
return (vmm_pte_t*)page;
}
static vmm_pte_t* get_table(vmm_pte_t* parent, size_t index, uint64_t flags) {
if (!(parent[index] & VMM_PRESENT)) {
vmm_pte_t* table = alloc_table();
uintptr_t table_phys = pmm_virt_to_phys(table);
parent[index] = table_phys
| VMM_PRESENT
| VMM_WRITE
| (flags & VMM_USER);
} else if (flags & VMM_USER) {
parent[index] |= VMM_USER;
}
return (vmm_pte_t*)pmm_phys_to_virt(parent[index] & PTE_PHYS_MASK);
}
bool vmm_map_page(vmm_pagemap_t* map, uintptr_t virt, uintptr_t phys, uint64_t flags) {
if (!map || !map->pml4) {
serial_printf("[VMM_BUG] vmm_map_page: map=%p pml4=%p virt=0x%llx\n",
(void*)map, map ? (void*)map->pml4 : NULL,
(unsigned long long)virt);
return false;
}
size_t pml4_i = (virt >> 39) & MASK;
size_t pdpt_i = (virt >> 30) & MASK;
size_t pd_i = (virt >> 21) & MASK;
size_t pt_i = (virt >> 12) & MASK;
vmm_pte_t* pdpt = get_table(map->pml4, pml4_i, flags);
vmm_pte_t* pd = get_table(pdpt, pdpt_i, flags);
vmm_pte_t* pt = get_table(pd, pd_i, flags);
pt[pt_i] = (phys & PTE_PHYS_MASK) | (flags | VMM_PRESENT);
asm volatile ("lock addl $0, (%%rsp)" ::: "memory", "cc");
invlpg((void*)virt);
asm volatile ("lock addl $0, (%%rsp)" ::: "memory", "cc");
return true;
}
void vmm_unmap_page_noflush(vmm_pagemap_t* map, uintptr_t virt) {
size_t pml4_i = (virt >> 39) & MASK;
size_t pdpt_i = (virt >> 30) & MASK;
size_t pd_i = (virt >> 21) & MASK;
size_t pt_i = (virt >> 12) & MASK;
if (!(map->pml4[pml4_i] & VMM_PRESENT)) return;
vmm_pte_t* pdpt = (vmm_pte_t*)pmm_phys_to_virt(map->pml4[pml4_i] & PTE_PHYS_MASK);
if (!(pdpt[pdpt_i] & VMM_PRESENT)) return;
vmm_pte_t* pd = (vmm_pte_t*)pmm_phys_to_virt(pdpt[pdpt_i] & PTE_PHYS_MASK);
if (!(pd[pd_i] & VMM_PRESENT)) return;
vmm_pte_t* pt = (vmm_pte_t*)pmm_phys_to_virt(pd[pd_i] & PTE_PHYS_MASK);
pt[pt_i] = 0;
}
void vmm_unmap_page(vmm_pagemap_t* map, uintptr_t virt) {
size_t pml4_i = (virt >> 39) & MASK;
size_t pdpt_i = (virt >> 30) & MASK;
size_t pd_i = (virt >> 21) & MASK;
size_t pt_i = (virt >> 12) & MASK;
if (!(map->pml4[pml4_i] & VMM_PRESENT)) return;
vmm_pte_t* pdpt = (vmm_pte_t*)pmm_phys_to_virt(map->pml4[pml4_i] & PTE_PHYS_MASK);
if (!(pdpt[pdpt_i] & VMM_PRESENT)) return;
vmm_pte_t* pd = (vmm_pte_t*)pmm_phys_to_virt(pdpt[pdpt_i] & PTE_PHYS_MASK);
if (!(pd[pd_i] & VMM_PRESENT)) return;
vmm_pte_t* pt = (vmm_pte_t*)pmm_phys_to_virt(pd[pd_i] & PTE_PHYS_MASK);
if (!(pt[pt_i] & VMM_PRESENT)) return;
uintptr_t phys = pt[pt_i] & PTE_PHYS_MASK;
pt[pt_i] = 0;
asm volatile ("lock addl $0, (%%rsp)" ::: "memory", "cc");
invlpg((void*)virt);
if (smp_get_cpu_count() > 1 && (virt >= 0xffff800000000000ULL)) {
ipi_tlb_shootdown_broadcast(&virt, 1);
}
if (phys >= PMM_FREE_MIN_PHYS) {
pmm_free(pmm_phys_to_virt(phys), 1);
}
}
vmm_pagemap_t* vmm_create_pagemap(void) {
vmm_pagemap_t* map = pmm_alloc_zero(1);
if (!map) {
serial_printf("[VMM] vmm_create_pagemap: pmm_alloc_zero for map failed\n");
return NULL;
}
map->pml4 = alloc_table();
if (!map->pml4) {
serial_printf("[VMM] vmm_create_pagemap: alloc_table for pml4 failed\n");
pmm_free(map, 1);
return NULL;
}
for (size_t i = 256; i < 512; i++) {
map->pml4[i] = kernel_pagemap.pml4[i];
}
return map;
}
void vmm_switch_pagemap(vmm_pagemap_t* map) {
uintptr_t phys = pmm_virt_to_phys(map->pml4);
asm volatile ("mov %0, %%cr3" :: "r"(phys) : "memory");
}
bool vmm_virt_to_phys(vmm_pagemap_t* map, uintptr_t virt, uintptr_t* phys_out) {
if (!map || !phys_out) {
serial_printf("VMM_VIRT_TO_PHYS ERROR: null parameters\n");
return false;
}
size_t pml4_i = (virt >> 39) & MASK;
size_t pdpt_i = (virt >> 30) & MASK;
size_t pd_i = (virt >> 21) & MASK;
size_t pt_i = (virt >> 12) & MASK;
if (!(map->pml4[pml4_i] & VMM_PRESENT)) return false;
vmm_pte_t* pdpt = (vmm_pte_t*)pmm_phys_to_virt(map->pml4[pml4_i] & PTE_PHYS_MASK);
if (!(pdpt[pdpt_i] & VMM_PRESENT)) return false;
vmm_pte_t* pd = (vmm_pte_t*)pmm_phys_to_virt(pdpt[pdpt_i] & PTE_PHYS_MASK);
if (!(pd[pd_i] & VMM_PRESENT)) return false;
vmm_pte_t* pt = (vmm_pte_t*)pmm_phys_to_virt(pd[pd_i] & PTE_PHYS_MASK);
if (!(pt[pt_i] & VMM_PRESENT)) return false;
*phys_out = (pt[pt_i] & PTE_PHYS_MASK) | (virt & 0xFFF);
return true;
}
bool vmm_get_page_flags(vmm_pagemap_t* map, uintptr_t virt, uint64_t* flags_out) {
if (!map || !flags_out) return false;
size_t pml4_i = (virt >> 39) & MASK;
size_t pdpt_i = (virt >> 30) & MASK;
size_t pd_i = (virt >> 21) & MASK;
size_t pt_i = (virt >> 12) & MASK;
if (!(map->pml4[pml4_i] & VMM_PRESENT)) return false;
vmm_pte_t* pdpt = (vmm_pte_t*)pmm_phys_to_virt(map->pml4[pml4_i] & PTE_PHYS_MASK);
if (!(pdpt[pdpt_i] & VMM_PRESENT)) return false;
vmm_pte_t* pd = (vmm_pte_t*)pmm_phys_to_virt(pdpt[pdpt_i] & PTE_PHYS_MASK);
if (!(pd[pd_i] & VMM_PRESENT)) return false;
vmm_pte_t* pt = (vmm_pte_t*)pmm_phys_to_virt(pd[pd_i] & PTE_PHYS_MASK);
if (!(pt[pt_i] & VMM_PRESENT)) return false;
*flags_out = pt[pt_i] & (0xFFF | (1ULL << 63));
return true;
}
vmm_pagemap_t* vmm_clone_pagemap(vmm_pagemap_t* src) {
if (!src) return NULL;
vmm_pagemap_t* dst = pmm_alloc_zero(1);
if (!dst) return NULL;
dst->pml4 = alloc_table();
for (size_t i = 256; i < 512; i++)
dst->pml4[i] = kernel_pagemap.pml4[i];
for (size_t pml4_i = 0; pml4_i < 256; pml4_i++) {
if (!(src->pml4[pml4_i] & VMM_PRESENT)) continue;
vmm_pte_t* src_pdpt = (vmm_pte_t*)pmm_phys_to_virt(src->pml4[pml4_i] & PTE_PHYS_MASK);
vmm_pte_t* dst_pdpt = alloc_table();
dst->pml4[pml4_i] = pmm_virt_to_phys(dst_pdpt) | (src->pml4[pml4_i] & 0xFFF);
for (size_t pdpt_i = 0; pdpt_i < 512; pdpt_i++) {
if (!(src_pdpt[pdpt_i] & VMM_PRESENT)) continue;
vmm_pte_t* src_pd = (vmm_pte_t*)pmm_phys_to_virt(src_pdpt[pdpt_i] & PTE_PHYS_MASK);
vmm_pte_t* dst_pd = alloc_table();
dst_pdpt[pdpt_i] = pmm_virt_to_phys(dst_pd) | (src_pdpt[pdpt_i] & 0xFFF);
for (size_t pd_i = 0; pd_i < 512; pd_i++) {
if (!(src_pd[pd_i] & VMM_PRESENT)) continue;
if (src_pd[pd_i] & VMM_PSE) {
void* new_hp = pmm_alloc(512);
if (!new_hp) continue;
void* old_hp = pmm_phys_to_virt(src_pd[pd_i] & PTE_PHYS_MASK & ~0x1FFFFFULL);
memcpy(new_hp, old_hp, 512 * 0x1000);
uint64_t hp_flags = src_pd[pd_i] & ~(PTE_PHYS_MASK & ~0x1FFFFFULL);
dst_pd[pd_i] = (pmm_virt_to_phys(new_hp) & (PTE_PHYS_MASK & ~0x1FFFFFULL))
| hp_flags;
continue;
}
vmm_pte_t* src_pt = (vmm_pte_t*)pmm_phys_to_virt(src_pd[pd_i] & PTE_PHYS_MASK);
vmm_pte_t* dst_pt = alloc_table();
dst_pd[pd_i] = pmm_virt_to_phys(dst_pt) | (src_pd[pd_i] & 0xFFF);
for (size_t pt_i = 0; pt_i < 512; pt_i++) {
if (!(src_pt[pt_i] & VMM_PRESENT)) continue;
uintptr_t src_phys = src_pt[pt_i] & PTE_PHYS_MASK;
if (src_phys < PMM_FREE_MIN_PHYS) continue;
void* new_page = pmm_alloc_zero(1);
if (!new_page) continue;
void* old_page = pmm_phys_to_virt(src_phys);
memcpy(new_page, old_page, 0x1000);
dst_pt[pt_i] = pmm_virt_to_phys(new_page)
| (src_pt[pt_i] & (0xFFF | (1ULL << 63)));
}
}
}
}
return dst;
}
void vmm_free_pagemap(vmm_pagemap_t* map)
{
if (!map || !map->pml4) return;
for (size_t pml4_i = 0; pml4_i < 256; pml4_i++) {
if (!(map->pml4[pml4_i] & VMM_PRESENT)) continue;
vmm_pte_t* pdpt = (vmm_pte_t*)pmm_phys_to_virt(map->pml4[pml4_i] & PTE_PHYS_MASK);
for (size_t pdpt_i = 0; pdpt_i < 512; pdpt_i++) {
if (!(pdpt[pdpt_i] & VMM_PRESENT)) continue;
vmm_pte_t* pd = (vmm_pte_t*)pmm_phys_to_virt(pdpt[pdpt_i] & PTE_PHYS_MASK);
for (size_t pd_i = 0; pd_i < 512; pd_i++) {
if (!(pd[pd_i] & VMM_PRESENT)) continue;
if (pd[pd_i] & VMM_PSE) {
uintptr_t hp_phys = pd[pd_i] & PTE_PHYS_MASK & ~0x1FFFFFULL;
if (hp_phys >= PMM_FREE_MIN_PHYS)
pmm_free(pmm_phys_to_virt(hp_phys), 512);
continue;
}
vmm_pte_t* pt = (vmm_pte_t*)pmm_phys_to_virt(pd[pd_i] & PTE_PHYS_MASK);
for (size_t pt_i = 0; pt_i < 512; pt_i++) {
if (!(pt[pt_i] & VMM_PRESENT)) continue;
uintptr_t phys = pt[pt_i] & PTE_PHYS_MASK;
if (phys >= PMM_FREE_MIN_PHYS)
pmm_free(pmm_phys_to_virt(phys), 1);
}
uintptr_t pt_phys = pd[pd_i] & PTE_PHYS_MASK;
if (pt_phys >= PMM_FREE_MIN_PHYS)
pmm_free(pt, 1);
}
uintptr_t pd_phys = pdpt[pdpt_i] & PTE_PHYS_MASK;
if (pd_phys >= PMM_FREE_MIN_PHYS)
pmm_free(pd, 1);
}
uintptr_t pdpt_phys = map->pml4[pml4_i] & PTE_PHYS_MASK;
if (pdpt_phys >= PMM_FREE_MIN_PHYS)
pmm_free(pdpt, 1);
}
pmm_free(map->pml4, 1);
pmm_free(map, 1);
}
uintptr_t kernel_pml4_phys;
void vmm_init(void) {
uintptr_t cr3;
asm volatile ("mov %%cr3, %0" : "=r"(cr3));
kernel_pagemap.pml4 = (vmm_pte_t*)pmm_phys_to_virt(cr3);
kernel_pml4_phys = cr3;
serial_printf("VMM: kernel pagemap initialized\n");
serial_printf("VMM: kernel PML4 phys = 0x%llx\n", kernel_pml4_phys);
}
vmm_pagemap_t* vmm_get_kernel_pagemap(void) {
return &kernel_pagemap;
}
void vmm_sync_kernel_mappings(vmm_pagemap_t* map) {
if (!map) return;
for (size_t i = 256; i < 512; i++) {
map->pml4[i] = kernel_pagemap.pml4[i];
}
}
void vmm_test(void) {
serial_printf("\n--- VMM EXTENDED 64-BIT TEST ---\n");
void* phys1 = pmm_alloc_zero(1);
void* phys2 = pmm_alloc_zero(1);
uintptr_t paddr1 = pmm_virt_to_phys(phys1);
uintptr_t paddr2 = pmm_virt_to_phys(phys2);
uintptr_t vaddr1 = KERNEL_TEST_BASE;
uintptr_t vaddr2 = KERNEL_TEST_BASE + 0x1000;
vmm_map_page(&kernel_pagemap, vaddr1, paddr1, VMM_WRITE);
vmm_map_page(&kernel_pagemap, vaddr2, paddr2, VMM_WRITE);
uint64_t* ptr1 = (uint64_t*)vaddr1;
uint64_t* ptr2 = (uint64_t*)vaddr2;
*ptr1 = 0xDEADBEEFCAFEBABE;
*ptr2 = 0xFEEDFACE12345678;
vmm_pagemap_t* new_map = vmm_create_pagemap();
void* phys3 = pmm_alloc_zero(1);
uintptr_t paddr3 = pmm_virt_to_phys(phys3);
uintptr_t vaddr3 = KERNEL_TEST_BASE + 0x2000;
vmm_map_page(new_map, vaddr3, paddr3, VMM_WRITE);
uint64_t* ptr3 = (uint64_t*)vaddr3;
*ptr3 = 0xBAADF00DBAADF00D;
vmm_switch_pagemap(new_map);
serial_printf("Value (new): 0x%llx\n", *ptr3);
vmm_switch_pagemap(&kernel_pagemap);
serial_printf("Value (kernel): 0x%llx\n", *ptr1);
serial_printf("--- VMM TEST DONE ---\n");
serial_printf("\n--- VMM TRANSLATION TEST ---\n");
void* phys_page = pmm_alloc_zero(1);
uintptr_t paddr = pmm_virt_to_phys(phys_page);
uintptr_t vaddr = KERNEL_TEST_BASE + 0x3000;
vmm_map_page(&kernel_pagemap, vaddr, paddr, VMM_WRITE);
uintptr_t translated_phys;
if (vmm_virt_to_phys(&kernel_pagemap, vaddr, &translated_phys)) {
serial_printf("Virt 0x%llx -> Phys 0x%llx\n", vaddr, translated_phys);
serial_printf("Original phys: 0x%llx\n", paddr);
serial_printf("Match: %s\n", translated_phys == paddr ? "YES" : "NO");
} else {
serial_printf("Translation failed!\n");
}
uint64_t flags;
if (vmm_get_page_flags(&kernel_pagemap, vaddr, &flags)) {
serial_printf("Page flags: 0x%llx\n", flags);
serial_printf("Present: %s\n", (flags & VMM_PRESENT) ? "YES" : "NO");
serial_printf("Writable: %s\n", (flags & VMM_WRITE) ? "YES" : "NO");
}
uint64_t hhdm = pmm_get_hhdm_offset();
serial_printf("HHDM offset: 0x%llx\n", hhdm);
serial_printf("\n--- Memory statistics after tests ---\n");
size_t free_before = pmm_get_free_pages();
serial_printf("\n--- Memory free test ---\n");
void* test_alloc = pmm_alloc_aligned(2, 4096);
if (test_alloc) {
size_t free_after_alloc = pmm_get_free_pages();
serial_printf("Allocated 2 pages. Free pages: %zu -> %zu\n",
free_before, free_after_alloc);
pmm_free(test_alloc, 2);
size_t free_after_free = pmm_get_free_pages();
serial_printf("Freed 2 pages. Free pages: %zu -> %zu\n",
free_after_alloc, free_after_free);
if (free_after_free == free_before) {
serial_printf("Memory free test PASSED\n");
} else {
serial_printf("Memory free test FAILED (possible leak)\n");
}
}
serial_printf("--- VMM TRANSLATION TEST DONE ---\n");
}
+211
View File
@@ -0,0 +1,211 @@
#include "../../include/panic/panic.h"
#include "../../include/io/serial.h"
#include "../../include/graphics/fb/fb.h"
#include "../../include/apic/apic.h"
#include "../../include/smp/smp.h"
#include "../../include/smp/percpu.h"
#include "../../include/sched/sched.h"
#include <stddef.h>
#include <stdio.h>
extern struct limine_framebuffer *global_framebuffer;
#define COL_BG 0x000080
#define COL_WHITE 0xFFFFFF
#define COL_YELLOW 0xFFFF00
#define COL_RED 0xFF4444
#define COL_CYAN 0x00FFFF
#define COL_GRAY 0xAAAAAA
static uint32_t fb_x = 0, fb_y = 0;
static struct limine_framebuffer *g_fb = NULL;
static void fb_nl(void) {
fb_x = 0;
fb_y += 18;
}
static void fb_puts_col(const char *s, uint32_t col) {
if (!g_fb) return;
while (*s) {
if (*s == '\n') { fb_nl(); s++; continue; }
fb_draw_char(g_fb, *s, fb_x, fb_y, col);
fb_x += 9;
if (fb_x + 9 > (uint32_t)g_fb->width) fb_nl();
s++;
}
}
static void fb_puts(const char *s) { fb_puts_col(s, COL_WHITE); }
static void fb_puthex(uint64_t v) {
static const char h[] = "0123456789ABCDEF";
char full[19] = "0x";
for (int i = 0; i < 16; i++)
full[2 + i] = h[(v >> (60 - i * 4)) & 0xF];
full[18] = '\0';
fb_puts_col(full, COL_CYAN);
}
static void fb_putdec(uint64_t v) {
char buf[22]; int i = 20; buf[21] = '\0';
if (v == 0) { fb_puts_col("0", COL_CYAN); return; }
while (v && i >= 0) { buf[i--] = '0' + (v % 10); v /= 10; }
fb_puts_col(buf + i + 1, COL_CYAN);
}
volatile int g_panic_owner = 0;
static int panic_try_own(void) {
int expected = 0;
return __atomic_compare_exchange_n(&g_panic_owner, &expected, 1,
0, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED);
}
static void halt_other_cpus(void) {
asm volatile("cli");
lapic_send_nmi_to_all_but_self();
for (volatile int i = 0; i < 200000; i++)
asm volatile("pause");
}
static void draw_panic_screen(const char *msg, struct int_frame_t *regs) {
g_fb = global_framebuffer;
if (!g_fb) return;
uint32_t W = (uint32_t)g_fb->width;
uint32_t H = (uint32_t)g_fb->height;
fb_fill_rect(g_fb, 0, 0, W, H, COL_BG);
fb_fill_rect(g_fb, 0, 0, W, 4, COL_WHITE);
fb_fill_rect(g_fb, 0, H - 4, W, 4, COL_WHITE);
fb_x = 16; fb_y = 16;
fb_puts_col("*** KERNEL PANIC ***\n", COL_YELLOW);
fb_nl();
fb_puts_col("Cervus OS has encountered a fatal error and cannot continue.\n", COL_WHITE);
fb_nl();
fb_puts_col("Reason: ", COL_GRAY);
fb_puts_col(msg, COL_YELLOW);
fb_nl(); fb_nl();
if (regs) {
percpu_t *pc = get_percpu();
task_t *t = pc ? (task_t *)pc->current_task : NULL;
uint32_t cpu = lapic_get_id();
if (!t) t = current_task[cpu];
fb_puts_col("CPU: ", COL_GRAY); fb_putdec(cpu); fb_puts("\n");
if (t) {
fb_puts_col("Task: ", COL_GRAY);
fb_puts_col(t->name, COL_CYAN);
fb_puts(" PID: "); fb_putdec(t->pid);
fb_puts("\n");
}
fb_nl();
fb_puts_col(" Register Dump \n", COL_YELLOW);
fb_puts_col("RIP: ", COL_GRAY); fb_puthex(regs->rip);
fb_puts(" CS: "); fb_puthex(regs->cs); fb_puts("\n");
fb_puts_col("RSP: ", COL_GRAY); fb_puthex(regs->rsp);
fb_puts(" RFL: "); fb_puthex(regs->rflags); fb_puts("\n");
fb_puts_col("RAX: ", COL_GRAY); fb_puthex(regs->rax);
fb_puts(" RBX: "); fb_puthex(regs->rbx); fb_puts("\n");
fb_puts_col("RCX: ", COL_GRAY); fb_puthex(regs->rcx);
fb_puts(" RDX: "); fb_puthex(regs->rdx); fb_puts("\n");
fb_puts_col("RSI: ", COL_GRAY); fb_puthex(regs->rsi);
fb_puts(" RDI: "); fb_puthex(regs->rdi); fb_puts("\n");
fb_puts_col("RBP: ", COL_GRAY); fb_puthex(regs->rbp); fb_puts("\n");
fb_puts_col("R8: ", COL_GRAY); fb_puthex(regs->r8);
fb_puts(" R9: "); fb_puthex(regs->r9); fb_puts("\n");
fb_puts_col("R10: ", COL_GRAY); fb_puthex(regs->r10);
fb_puts(" R11: "); fb_puthex(regs->r11); fb_puts("\n");
fb_puts_col("R12: ", COL_GRAY); fb_puthex(regs->r12);
fb_puts(" R13: "); fb_puthex(regs->r13); fb_puts("\n");
fb_puts_col("R14: ", COL_GRAY); fb_puthex(regs->r14);
fb_puts(" R15: "); fb_puthex(regs->r15); fb_puts("\n");
fb_nl();
uint64_t cr2 = 0;
asm volatile("mov %%cr2, %0" : "=r"(cr2));
fb_puts_col("CR2: ", COL_GRAY); fb_puthex(cr2); fb_puts("\n");
fb_puts_col("ERR: ", COL_GRAY); fb_puthex(regs->error);
fb_puts(" INT: "); fb_putdec(regs->interrupt); fb_puts("\n");
}
fb_nl();
fb_puts_col("System halted. Check serial output for details.\n", COL_GRAY);
}
static void serial_panic_dump(const char *msg, struct int_frame_t *regs) {
serial_printf("\n");
serial_printf(" KERNEL PANIC \n");
serial_printf("Reason: %s\n", msg);
uint32_t cpu = lapic_get_id();
serial_printf("CPU: %u\n", cpu);
percpu_t *pc = get_percpu();
task_t *t = pc ? (task_t *)pc->current_task : NULL;
if (!t) t = current_task[cpu];
if (t) {
serial_printf("Task: %s PID=%u PPID=%u\n",
t->name, t->pid, t->ppid);
}
if (regs) {
uint64_t cr2 = 0;
asm volatile("mov %%cr2, %0" : "=r"(cr2));
serial_printf("\nRegisters:\n");
serial_printf(" RIP=0x%016llx CS=0x%llx\n", regs->rip, regs->cs);
serial_printf(" RSP=0x%016llx SS=0x%llx\n", regs->rsp, regs->ss);
serial_printf(" RFLAGS=0x%016llx\n", regs->rflags);
serial_printf(" RAX=0x%016llx RBX=0x%016llx\n", regs->rax, regs->rbx);
serial_printf(" RCX=0x%016llx RDX=0x%016llx\n", regs->rcx, regs->rdx);
serial_printf(" RSI=0x%016llx RDI=0x%016llx\n", regs->rsi, regs->rdi);
serial_printf(" RBP=0x%016llx\n", regs->rbp);
serial_printf(" R8 =0x%016llx R9 =0x%016llx\n", regs->r8, regs->r9);
serial_printf(" R10=0x%016llx R11=0x%016llx\n", regs->r10, regs->r11);
serial_printf(" R12=0x%016llx R13=0x%016llx\n", regs->r12, regs->r13);
serial_printf(" R14=0x%016llx R15=0x%016llx\n", regs->r14, regs->r15);
serial_printf(" CR2=0x%016llx\n", cr2);
serial_printf(" ERR=0x%016llx INT=%llu\n", regs->error, regs->interrupt);
}
serial_printf("========================================\n");
serial_printf("System halted.\n\n");
}
__attribute__((noreturn))
void kernel_panic(const char *msg) {
kernel_panic_regs(msg, NULL);
}
__attribute__((noreturn))
void kernel_panic_regs(const char *msg, struct int_frame_t *regs) {
asm volatile("cli");
if (!panic_try_own()) {
for (;;) asm volatile("cli; hlt");
}
serial_force_unlock();
halt_other_cpus();
serial_panic_dump(msg, regs);
draw_panic_screen(msg, regs);
for (;;) asm volatile("cli; hlt");
}
+669
View File
@@ -0,0 +1,669 @@
#include "../include/sched/capabilities.h"
#include "../include/sched/sched.h"
#include "../include/sched/spinlock.h"
#include "../include/memory/pmm.h"
#include "../include/memory/vmm.h"
#include "../include/io/serial.h"
#include "../include/smp/smp.h"
#include "../include/smp/percpu.h"
#include "../include/apic/apic.h"
#include "../include/gdt/gdt.h"
#include "../include/fs/vfs.h"
#include "../include/panic/panic.h"
#include <string.h>
#include <stdlib.h>
#define KERNEL_STACK_PAGES (KERNEL_STACK_SIZE / 0x1000)
#define MAX_PIDS 4096
task_t* ready_queues[MAX_PRIORITY + 1] = {0};
task_t* current_task[MAX_CPUS] = {0};
static task_t idle_tasks[MAX_CPUS];
static task_t bootstrap_tasks[MAX_CPUS];
static volatile uint64_t reschedule_calls = 0;
static spinlock_t ready_queue_lock = SPINLOCK_INIT;
static task_t* pid_table[MAX_PIDS] = {0};
static uint32_t next_pid = 1;
static spinlock_t pid_lock = SPINLOCK_INIT;
spinlock_t children_lock = SPINLOCK_INIT;
extern tss_t* tss[MAX_CPUS];
static inline void fix_gs_base(percpu_t* pc) {
uint64_t val = (uint64_t)pc;
asm volatile("wrmsr"
:: "c"(0xC0000101U),
"a"((uint32_t)val),
"d"((uint32_t)(val >> 32)));
}
uint32_t task_alloc_pid(void) {
uint64_t _irqf;
_irqf = spinlock_acquire_irqsave(&pid_lock);
uint32_t found = 0;
for (uint32_t attempt = 0; attempt < MAX_PIDS - 1; attempt++) {
uint32_t i = next_pid;
if (i == 0 || i >= MAX_PIDS) {
next_pid = 1;
i = 1;
}
next_pid = (next_pid + 1 >= MAX_PIDS) ? 1 : next_pid + 1;
if (!pid_table[i]) { found = i; break; }
}
if (!found) {
serial_printf("[PID] FATAL: pid table exhausted (MAX_PIDS=%u)!\n", MAX_PIDS);
}
spinlock_release_irqrestore(&pid_lock, _irqf);
return found;
}
task_t* task_find_by_pid(uint32_t pid) {
if (pid == 0 || pid >= MAX_PIDS) return NULL;
return pid_table[pid];
}
static void pid_register(task_t* t) {
if (t->pid && t->pid < MAX_PIDS) pid_table[t->pid] = t;
}
static void pid_unregister(task_t* t) {
if (t->pid && t->pid < MAX_PIDS) pid_table[t->pid] = NULL;
}
static void idle_loop(void* arg);
#define STACK_CANARY_VALUE 0xDEADC0DEDEADC0DEULL
static uint64_t alloc_and_init_stack(task_t* t) {
uintptr_t stack_virt = (uintptr_t)pmm_alloc_zero(KERNEL_STACK_PAGES);
if (!stack_virt) return 0;
t->stack_base = stack_virt;
uint64_t* canary_page = (uint64_t*)stack_virt;
for (size_t i = 0; i < PAGE_SIZE / sizeof(uint64_t); i++)
canary_page[i] = STACK_CANARY_VALUE;
uintptr_t stack_top = (stack_virt + KERNEL_STACK_SIZE) & ~0xFULL;
uint64_t* sp = (uint64_t*)stack_top;
if (t->flags & TASK_FLAG_FORK) {
extern void task_trampoline_fork(void);
*--sp = (uint64_t)task_trampoline_fork;
} else if (t->is_userspace) {
extern void task_trampoline_user(void);
*--sp = (uint64_t)task_trampoline_user;
} else {
extern void task_trampoline(void);
*--sp = (uint64_t)task_trampoline;
}
*--sp = (uint64_t)t;
*--sp = 0;
*--sp = 0;
*--sp = 0;
*--sp = 0;
*--sp = 0;
return (uint64_t)sp;
}
static void enqueue_global(task_t* t) {
uint64_t f = spinlock_acquire_irqsave(&ready_queue_lock);
t->next = ready_queues[t->priority];
ready_queues[t->priority] = t;
spinlock_release_irqrestore(&ready_queue_lock, f);
}
void __attribute__((used)) ctx_rsp_corruption_detected(task_t* old, uint64_t saved_rsp) {
serial_printf("[CTX-CORRUPT] pid=%u rsp=0x%llx saved but INVALID (stack=0x%llx..0x%llx)!\n",
old ? old->pid : 0,
saved_rsp,
old ? old->stack_base : 0,
old ? (old->stack_base + KERNEL_STACK_SIZE) : 0);
kernel_panic("context_switch: saved invalid RSP into task->rsp");
}
static void process_deferred_free(void) {
percpu_t* pc = get_percpu();
if (!pc) return;
task_t* dead = (task_t*)pc->deferred_free_task;
if (!dead) return;
pc->deferred_free_task = NULL;
task_destroy(dead);
}
void sched_init(void) {
memset(pid_table, 0, sizeof(pid_table));
memset(bootstrap_tasks, 0, sizeof(bootstrap_tasks));
next_pid = 1;
for (uint32_t i = 0; i < smp_get_cpu_count(); i++) {
task_t* idle = &idle_tasks[i];
memset(idle, 0, sizeof(task_t));
idle->priority = 0;
idle->runnable = true;
idle->state = TASK_READY;
idle->cpu_id = i;
idle->last_cpu = i;
idle->time_slice = 1;
idle->time_slice_init = 1;
idle->entry = idle_loop;
idle->arg = NULL;
idle->is_userspace = TASK_TYPE_KERNEL;
idle->pid = 0;
idle->uid = UID_ROOT;
idle->gid = GID_ROOT;
idle->capabilities = CAP_ALL;
atomic_init_bool(&idle->on_cpu, false);
idle->name[0]='i'; idle->name[1]='d';
idle->name[2]='l'; idle->name[3]='e';
idle->rsp = alloc_and_init_stack(idle);
if (!idle->rsp) {
kernel_panic("SCHED: failed to allocate idle stack");
}
current_task[i] = NULL;
}
serial_writestring("Scheduler initialized (PREEMPTIVE SMP MODE)\n");
}
task_t* task_create(const char* name, void (*entry)(void*), void* arg, int priority) {
task_t* t = calloc(1, sizeof(task_t));
if (!t) return NULL;
t->pid = task_alloc_pid();
if (!t->pid) { free(t); return NULL; }
t->ppid = 0;
t->uid = UID_ROOT;
t->gid = GID_ROOT;
t->capabilities = CAP_ALL;
t->entry = entry;
t->arg = arg;
t->priority = priority > MAX_PRIORITY ? MAX_PRIORITY : priority;
t->runnable = true;
t->state = TASK_READY;
t->cpu_id = (uint32_t)-1;
t->time_slice = TASK_DEFAULT_TIMESLICE;
t->time_slice_init = TASK_DEFAULT_TIMESLICE;
t->rip = (uint64_t)entry;
t->is_userspace = TASK_TYPE_KERNEL;
atomic_init_bool(&t->on_cpu, false);
strncpy(t->name, name, sizeof(t->name) - 1);
t->rsp = alloc_and_init_stack(t);
if (!t->rsp) { free(t); return NULL; }
t->fpu_state = (fpu_state_t*)pmm_alloc_zero(1);
pid_register(t);
enqueue_global(t);
serial_printf("[SCHED] task_create: '%s' pid=%u prio=%d\n", t->name, t->pid, t->priority);
return t;
}
task_t* task_create_user(const char* name, uintptr_t entry, uintptr_t user_rsp, uint64_t cr3, int priority, vmm_pagemap_t* pagemap, uint32_t uid, uint32_t gid) {
task_t* t = calloc(1, sizeof(task_t));
if (!t) return NULL;
t->pid = task_alloc_pid();
if (!t->pid) { free(t); return NULL; }
t->ppid = 0;
t->uid = uid;
t->gid = gid;
t->capabilities = cap_initial(uid);
t->entry = (void (*)(void*))entry;
t->arg = NULL;
t->priority = priority > MAX_PRIORITY ? MAX_PRIORITY : priority;
t->runnable = true;
t->state = TASK_READY;
t->cpu_id = (uint32_t)-1;
t->time_slice = TASK_DEFAULT_TIMESLICE;
t->time_slice_init = TASK_DEFAULT_TIMESLICE;
t->rip = entry;
t->is_userspace = TASK_TYPE_USER;
t->user_rsp = user_rsp;
t->cr3 = cr3;
t->pagemap = pagemap;
t->brk_start = 0;
t->brk_current = 0;
t->brk_max = 0x0000700000000000ULL;
atomic_init_bool(&t->on_cpu, false);
strncpy(t->name, name, sizeof(t->name) - 1);
t->rsp = alloc_and_init_stack(t);
if (!t->rsp) { free(t); return NULL; }
t->fpu_state = (fpu_state_t*)pmm_alloc_zero(1);
t->fd_table = fd_table_create();
if (t->fd_table) {
int stdio_ret = vfs_init_stdio(t);
if (stdio_ret < 0)
serial_printf("[SCHED] task_create_user: vfs_init_stdio failed: %d\n",
stdio_ret);
}
t->flags |= TASK_FLAG_OWN_PAGEMAP;
pid_register(t);
enqueue_global(t);
serial_printf("[SCHED] task_create_user: '%s' pid=%u uid=%u entry=0x%llx user_rsp=0x%llx caps=0x%llx\n",
t->name, t->pid, t->uid, entry, user_rsp, t->capabilities);
return t;
}
task_t* task_fork(task_t* parent) {
if (!parent) return NULL;
task_t* child = calloc(1, sizeof(task_t));
if (!child) return NULL;
child->pid = task_alloc_pid();
if (!child->pid) { free(child); return NULL; }
child->ppid = parent->pid;
child->priority = parent->priority;
child->is_userspace = parent->is_userspace;
strncpy(child->name, parent->name, sizeof(child->name)-1);
child->uid = parent->uid;
child->gid = parent->gid;
child->capabilities = parent->capabilities;
child->time_slice = parent->time_slice_init;
child->time_slice_init = parent->time_slice_init;
child->pagemap = vmm_clone_pagemap(parent->pagemap);
if (!child->pagemap) { free(child); return NULL; }
child->cr3 = (uint64_t)pmm_virt_to_phys(child->pagemap->pml4);
child->brk_start = parent->brk_start;
child->brk_current = parent->brk_current;
child->brk_max = parent->brk_max;
child->user_rsp = parent->user_rsp;
serial_printf("[FORK-DBG2] parent pid=%u user_rsp=0x%llx user_saved_rip=0x%llx\n",
parent->pid, parent->user_rsp, parent->user_saved_rip);
child->user_saved_rip = parent->user_saved_rip;
child->user_saved_rbp = parent->user_saved_rbp;
child->user_saved_rbx = parent->user_saved_rbx;
child->user_saved_r12 = parent->user_saved_r12;
child->user_saved_r13 = parent->user_saved_r13;
child->user_saved_r14 = parent->user_saved_r14;
child->user_saved_r15 = parent->user_saved_r15;
child->user_saved_r11 = parent->user_saved_r11 | (1ULL << 9);
child->flags |= TASK_FLAG_FORK;
atomic_init_bool(&child->on_cpu, false);
child->rsp = alloc_and_init_stack(child);
if (!child->rsp) {
vmm_free_pagemap(child->pagemap);
free(child);
return NULL;
}
vmm_sync_kernel_mappings(child->pagemap);
serial_printf("[FORK-DBG] child pid=%u stack_base=0x%llx rsp=0x%llx\n",
child->pid, child->stack_base, child->rsp);
child->fpu_state = (fpu_state_t*)pmm_alloc_zero(1);
if (child->fpu_state && parent->fpu_state) {
memcpy(child->fpu_state, parent->fpu_state, sizeof(fpu_state_t));
child->fpu_used = parent->fpu_used;
}
if (parent->fd_table)
child->fd_table = fd_table_clone(parent->fd_table);
child->state = TASK_READY;
child->runnable = true;
child->parent = parent;
{
uint64_t _cf = spinlock_acquire_irqsave(&children_lock);
child->sibling = parent->children;
parent->children = child;
spinlock_release_irqrestore(&children_lock, _cf);
}
pid_register(child);
serial_printf("[FORK-CHK] child=%p rsp=0x%llx stk=0x%llx rip=0x%llx\n",
(void*)child, child->rsp, child->stack_base, child->user_saved_rip);
serial_printf("[SCHED] fork: parent='%s' pid=%u -> child pid=%u rip=0x%llx\n",
parent->name, parent->pid, child->pid, child->user_saved_rip);
enqueue_global(child);
return child;
}
void task_destroy(task_t* task) {
if (!task) return;
uint32_t old_flags = __atomic_fetch_or(&task->flags, TASK_FLAG_DESTROYED, __ATOMIC_ACQ_REL);
if (old_flags & TASK_FLAG_DESTROYED) return;
serial_printf("[DESTROY] pid=%u flags=0x%x on_cpu=%d\n",
task->pid, task->flags, (int)atomic_load_bool_acq(&task->on_cpu));
pid_unregister(task);
if (task->fpu_state) {
pmm_free(task->fpu_state, 1);
task->fpu_state = NULL;
}
if (task->stack_base) {
pmm_free((void*)task->stack_base, KERNEL_STACK_PAGES);
task->stack_base = 0;
}
if (task->pagemap && (task->flags & (TASK_FLAG_FORK | TASK_FLAG_OWN_PAGEMAP))) {
vmm_free_pagemap(task->pagemap);
task->pagemap = NULL;
}
if (task->fd_table) {
fd_table_destroy(task->fd_table);
task->fd_table = NULL;
}
free(task);
}
void task_reparent(task_t* child, task_t* new_parent) {
if (!child || !new_parent) return;
child->parent = new_parent;
child->ppid = new_parent->pid;
child->sibling = new_parent->children;
new_parent->children = child;
}
void task_wakeup_waiters(uint32_t pid) {
task_t* to_wake[64];
int wake_count = 0;
uint64_t _irqf = spinlock_acquire_irqsave(&pid_lock);
for (uint32_t i = 1; i < MAX_PIDS && wake_count < 64; i++) {
task_t* t = pid_table[i];
if (!t) continue;
if (t->state != TASK_BLOCKED) continue;
if (t->wait_for_pid != pid && t->wait_for_pid != (uint32_t)-1) continue;
serial_printf("[SCHED] wakeup_waiters: waking pid=%u (waited for pid=%u)\n", t->pid, pid);
t->wait_for_pid = 0;
t->runnable = true;
t->state = TASK_READY;
to_wake[wake_count++] = t;
}
spinlock_release_irqrestore(&pid_lock, _irqf);
for (int i = 0; i < wake_count; i++)
enqueue_global(to_wake[i]);
}
__attribute__((noreturn)) void task_exit(void)
{
asm volatile("cli");
uint32_t cpu = lapic_get_id();
percpu_t* pc = get_percpu();
task_t* me = pc ? (task_t*)pc->current_task : current_task[cpu];
if (!me) kernel_panic("task_exit: no current task");
serial_printf("[EXIT] task_exit called cpu=%u me=%p pid=%u\n", cpu, (void*)me, me->pid);
task_t* init = task_find_by_pid(1);
if (init && init != me) {
uint64_t _cf = spinlock_acquire_irqsave(&children_lock);
task_t* child = me->children;
me->children = NULL;
while (child) {
task_t* sib = child->sibling;
task_reparent(child, init);
child = sib;
}
spinlock_release_irqrestore(&children_lock, _cf);
}
vmm_switch_pagemap(vmm_get_kernel_pagemap());
me->runnable = false;
me->state = TASK_ZOMBIE;
me->cr3 = 0;
task_wakeup_waiters(me->pid);
sched_reschedule();
kernel_panic("task_exit: returned from sched_reschedule (should never happen)");
}
void task_kill(task_t* target) {
if (!target) return;
if (target->pid == 0) return;
if (target->is_userspace == TASK_TYPE_KERNEL) return;
if (target->state == TASK_ZOMBIE || target->state == TASK_DEAD) return;
if (target->pending_kill) return;
serial_printf("[KILL] task_kill pid=%u state=%d cpu=%u\n",
target->pid, (int)target->state, lapic_get_id());
target->exit_code = 130;
target->pending_kill = true;
if (target->state == TASK_BLOCKED) {
target->wakeup_time_ns = 0;
task_unblock(target);
}
if (!(target->flags & TASK_FLAG_STARTED)) {
return;
}
for (uint32_t cpu = 0; cpu < smp_get_cpu_count(); cpu++) {
if (current_task[cpu] == target) {
extern uint32_t smp_get_lapic_id_for_cpu(uint32_t);
ipi_reschedule_cpu(smp_get_lapic_id_for_cpu(cpu));
}
}
}
volatile uint32_t g_foreground_pid = 0;
void task_set_foreground(uint32_t pid) {
g_foreground_pid = pid;
}
task_t* task_find_foreground(void) {
uint32_t fpid = g_foreground_pid;
if (fpid == 0) return NULL;
task_t *t = task_find_by_pid(fpid);
if (!t || t->state == TASK_ZOMBIE || t->state == TASK_DEAD) {
g_foreground_pid = 0;
return NULL;
}
return t;
}
static task_t* sched_pick_next(uint32_t cpu) {
uint64_t _irqf;
_irqf = spinlock_acquire_irqsave(&ready_queue_lock);
task_t* found = NULL;
for (int p = MAX_PRIORITY; p >= 0 && !found; p--) {
task_t** head = &ready_queues[p];
task_t* t = *head;
while (t) {
if (t->runnable && t->state != TASK_ZOMBIE && t->state != TASK_DEAD) {
bool expected = false;
if (atomic_cas_bool(&t->on_cpu, &expected, true)) {
*head = t->next;
t->next = NULL;
found = t;
break;
}
} else {
if (t->state == TASK_ZOMBIE || t->state == TASK_DEAD || !t->runnable) {
*head = t->next;
t->next = NULL;
t = *head;
continue;
}
}
head = &t->next;
t = *head;
}
}
spinlock_release_irqrestore(&ready_queue_lock, _irqf);
return found ? found : &idle_tasks[cpu];
}
void sched_reschedule(void) {
asm volatile("cli");
process_deferred_free();
reschedule_calls++;
uint32_t cpu = lapic_get_id();
task_t* old = current_task[cpu];
task_t* next = sched_pick_next(cpu);
if (!next) { asm volatile("sti"); return; }
if (old == next) {
if (old == &idle_tasks[cpu]) {
next = sched_pick_next(cpu);
if (next == &idle_tasks[cpu]) {
asm volatile("sti");
return;
}
} else {
old->time_slice = old->time_slice_init;
asm volatile("sti");
return;
}
}
if (old && old->fpu_state && old->state != TASK_ZOMBIE && old->state != TASK_DEAD) {
fpu_save(old->fpu_state);
old->fpu_used = true;
}
if (old && old != &idle_tasks[cpu]) {
if (old->state == TASK_ZOMBIE) {
percpu_t* pc = get_percpu();
if (pc) pc->deferred_free_task = old;
} else if (old->runnable && old->state != TASK_DEAD) {
old->time_slice = old->time_slice_init;
old->last_cpu = cpu;
old->state = TASK_READY;
enqueue_global(old);
}
}
uint64_t switch_cr3 = 0;
if (next->cr3 && (!old || old->cr3 != next->cr3)) {
if (next->pagemap)
vmm_sync_kernel_mappings(next->pagemap);
asm volatile("lock addl $0, (%%rsp)" ::: "memory", "cc");
switch_cr3 = next->cr3;
} else if (!next->cr3) {
vmm_pagemap_t* kpm = vmm_get_kernel_pagemap();
if (kpm && kpm->pml4) {
uint64_t kphys = (uint64_t)pmm_virt_to_phys(kpm->pml4);
if (!old || old->cr3 != kphys)
switch_cr3 = kphys;
}
}
if (tss[cpu]) {
if (next->is_userspace && next->stack_base == 0) {
kernel_panic("SCHED: userspace task has stack_base=0");
}
tss[cpu]->rsp0 = next->stack_base + KERNEL_STACK_SIZE;
percpu_t* pc = get_percpu();
if (pc) {
pc->syscall_kernel_rsp = tss[cpu]->rsp0;
if (next->is_userspace) {
pc->syscall_user_rsp = next->user_rsp;
}
}
}
next->cpu_id = cpu;
next->state = TASK_RUNNING;
if (next->fpu_state) fpu_restore(next->fpu_state);
if (!(next->flags & TASK_FLAG_STARTED)) {
next->flags |= TASK_FLAG_STARTED;
current_task[cpu] = next;
if (next->cr3) {
serial_printf("[SCHED] CR3 switch cpu=%u old=0x%llx -> new=0x%llx (pid %u->%u)\n",
cpu, old ? old->cr3 : 0ULL, switch_cr3,
old ? old->pid : 0, next->pid);
asm volatile("mov %0, %%cr3" :: "r"(next->cr3) : "memory");
switch_cr3 = 0;
} else {
asm volatile("mov %%cr3, %%rax; mov %%rax, %%cr3" ::: "rax", "memory");
}
if (next->is_userspace) {
serial_printf("[SCHED] CPU %u: first start '%s' pid=%u entry=0x%llx user_rsp=0x%llx\n",
cpu, next->name, next->pid,
(uint64_t)next->entry, next->user_rsp);
}
}
if (old) context_switch(old, next, &current_task[cpu], switch_cr3);
else context_switch(&bootstrap_tasks[cpu], next, &current_task[cpu], switch_cr3);
process_deferred_free();
asm volatile("sti");
}
void task_yield(void) {
sched_reschedule();
}
void sched_print_stats(void) {
uint64_t _irqf;
serial_printf("[SCHED] reschedule_calls=%llu\n", reschedule_calls);
_irqf = spinlock_acquire_irqsave(&ready_queue_lock);
for (int p = MAX_PRIORITY; p >= 0; p--) {
int n = 0;
for (task_t* t = ready_queues[p]; t; t = t->next) n++;
if (n) serial_printf(" prio %d: %d tasks\n", p, n);
}
spinlock_release_irqrestore(&ready_queue_lock, _irqf);
}
void task_unblock(task_t* t) {
if (!t) return;
t->runnable = true;
t->state = TASK_READY;
enqueue_global(t);
}
void sched_wakeup_sleepers(uint64_t now_ns) {
task_t* to_wake[64];
int wake_count = 0;
uint64_t _irqf = spinlock_acquire_irqsave(&pid_lock);
for (uint32_t i = 1; i < MAX_PIDS && wake_count < 64; i++) {
task_t *t = pid_table[i];
if (!t) continue;
if (t->state != TASK_BLOCKED) continue;
if (t->wakeup_time_ns == 0) continue;
if (now_ns >= t->wakeup_time_ns) {
t->wakeup_time_ns = 0;
t->runnable = true;
t->state = TASK_READY;
to_wake[wake_count++] = t;
}
}
spinlock_release_irqrestore(&pid_lock, _irqf);
for (int i = 0; i < wake_count; i++) {
enqueue_global(to_wake[i]);
}
}
static void idle_loop(void* arg) {
(void)arg;
uint32_t cpu = lapic_get_id();
serial_printf("[IDLE] CPU %u entering idle loop\n", cpu);
while (1) {
process_deferred_free();
asm volatile("sti; hlt");
}
}
+157
View File
@@ -0,0 +1,157 @@
section .text
extern task_exit
global context_switch
global first_task_start
global fpu_save
global fpu_restore
global task_trampoline
global task_trampoline_user
global task_trampoline_fork
TASK_ENTRY_OFFSET equ 120
TASK_ARG_OFFSET equ 128
TASK_USER_RSP_OFFSET equ 144
TASK_CR3_OFFSET equ 24
TASK_USER_SAVED_RIP_OFFSET equ 272
TASK_USER_SAVED_RBP_OFFSET equ 280
TASK_USER_SAVED_RBX_OFFSET equ 288
TASK_USER_SAVED_R12_OFFSET equ 296
TASK_USER_SAVED_R13_OFFSET equ 304
TASK_USER_SAVED_R14_OFFSET equ 312
TASK_USER_SAVED_R15_OFFSET equ 320
TASK_USER_SAVED_R11_OFFSET equ 328
PERCPU_CURRENT_TASK equ 24
TASK_ON_CPU_OFFSET equ 0x158
context_switch:
push rbp
push rbx
push r12
push r13
push r14
push r15
mov [rdi], rsp
mov [rdx], rsi
mov [gs:PERCPU_CURRENT_TASK], rsi
mov rsp, [rsi]
test rcx, rcx
jz .skip_cr3
mov cr3, rcx
.skip_cr3:
lock add dword [rsp], 0
mov byte [rdi + TASK_ON_CPU_OFFSET], 0
pop r15
pop r14
pop r13
pop r12
pop rbx
pop rbp
ret
first_task_start:
mov [gs:PERCPU_CURRENT_TASK], rdi
mov rsi, [rdi + TASK_CR3_OFFSET]
mov rsp, [rdi]
test rsi, rsi
jz .skip_cr3_fts
mov cr3, rsi
.skip_cr3_fts:
pop r15
pop r14
pop r13
pop r12
pop rbx
pop rbp
ret
fpu_save:
fxsave [rdi]
ret
fpu_restore:
fxrstor [rdi]
ret
task_trampoline:
mov rdi, [rbp + TASK_ARG_OFFSET]
mov rax, [rbp + TASK_ENTRY_OFFSET]
xor rbp, rbp
sti
call rax
call task_exit
.hang:
cli
hlt
jmp .hang
task_trampoline_user:
mov rax, [rbp + TASK_ENTRY_OFFSET]
mov rcx, [rbp + TASK_USER_RSP_OFFSET]
xor rbp, rbp
push qword 0x1B
push rcx
pushfq
pop rdx
or rdx, (1 << 9)
and rdx, ~(3 << 12)
push rdx
push qword 0x23
push rax
xor rax, rax
xor rbx, rbx
xor rcx, rcx
xor rdx, rdx
xor rdi, rdi
xor rsi, rsi
xor r8, r8
xor r9, r9
xor r10, r10
xor r11, r11
xor r12, r12
xor r13, r13
xor r14, r14
xor r15, r15
swapgs
iretq
task_trampoline_fork:
cli
mov rax, [rbp + TASK_USER_SAVED_RIP_OFFSET]
mov rcx, [rbp + TASK_USER_RSP_OFFSET]
mov r11, [rbp + TASK_USER_SAVED_R11_OFFSET]
mov rbx, [rbp + TASK_USER_SAVED_RBX_OFFSET]
mov r12, [rbp + TASK_USER_SAVED_R12_OFFSET]
mov r13, [rbp + TASK_USER_SAVED_R13_OFFSET]
mov r14, [rbp + TASK_USER_SAVED_R14_OFFSET]
mov r15, [rbp + TASK_USER_SAVED_R15_OFFSET]
mov rbp, [rbp + TASK_USER_SAVED_RBP_OFFSET]
or r11, (1 << 9)
push qword 0x1B
push rcx
push r11
push qword 0x23
push rax
xor rax, rax
swapgs
iretq
+88
View File
@@ -0,0 +1,88 @@
#include "../../include/smp/percpu.h"
#include "../../include/smp/smp.h"
#include "../../include/memory/pmm.h"
#include "../../include/io/serial.h"
#include <string.h>
#include <stdlib.h>
extern uintptr_t __percpu_start;
extern uintptr_t __percpu_end;
PERCPU_SECTION percpu_t percpu = {0};
PERCPU_SECTION int dummy_percpu = 0xDEADBEEF;
percpu_t* percpu_regions[MAX_CPUS] = {0};
bool g_has_fsgsbase = false;
#define MSR_GS_BASE 0xC0000101
#define MSR_KERNEL_GS_BASE 0xC0000102
static inline uint64_t rdmsr_local(uint32_t msr) {
uint32_t lo, hi;
asm volatile("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr));
return ((uint64_t)hi << 32) | lo;
}
static inline void wrmsr_local(uint32_t msr, uint64_t val) {
asm volatile("wrmsr" :: "c"(msr), "a"((uint32_t)val), "d"((uint32_t)(val >> 32)));
}
static bool detect_fsgsbase(void) {
uint32_t eax = 7, ebx, ecx = 0, edx;
asm volatile("cpuid"
: "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
: "0"(eax), "2"(ecx));
return (ebx & (1u << 0)) != 0;
}
void init_percpu_regions(void) {
g_has_fsgsbase = detect_fsgsbase();
serial_printf("[PerCPU] FSGSBASE: %s\n", g_has_fsgsbase ? "YES" : "NO (using MSR fallback)");
size_t percpu_size = &__percpu_end - &__percpu_start;
serial_printf("PerCPU size: %zu bytes\n", percpu_size);
smp_info_t* info = smp_get_info();
for (uint32_t i = 0; i < info->cpu_count; i++) {
void* region = malloc(percpu_size);
if (!region) {
serial_printf("PerCPU: Alloc failed for CPU %u\n", i);
continue;
}
memset(region, 0, percpu_size);
memcpy(region, &__percpu_start, percpu_size);
percpu_regions[i] = (percpu_t*)region;
percpu_regions[i]->cpu_id = info->cpus[i].lapic_id;
percpu_regions[i]->syscall_kernel_rsp = 0;
percpu_regions[i]->syscall_user_rsp = 0;
serial_printf("PerCPU region for CPU %u at 0x%llx\n", i, (uint64_t)region);
}
}
percpu_t* get_percpu(void) {
uint64_t gs_base;
if (g_has_fsgsbase) {
asm volatile("rdgsbase %0" : "=r"(gs_base));
} else {
gs_base = rdmsr_local(MSR_GS_BASE);
}
if (gs_base == 0) return NULL;
return (percpu_t*)gs_base;
}
percpu_t* get_percpu_mut(void) {
return get_percpu();
}
void set_percpu_base(percpu_t* base) {
uint64_t val = (uint64_t)base;
if (g_has_fsgsbase) {
asm volatile("wrgsbase %0" :: "r"(val) : "memory");
} else {
wrmsr_local(MSR_GS_BASE, val);
}
wrmsr_local(MSR_KERNEL_GS_BASE, val);
}
+327
View File
@@ -0,0 +1,327 @@
#include "../../include/smp/smp.h"
#include "../../include/smp/percpu.h"
#include "../../include/acpi/acpi.h"
#include "../../include/apic/apic.h"
#include "../../include/io/serial.h"
#include "../../include/memory/pmm.h"
#include "../../include/memory/vmm.h"
#include "../../include/gdt/gdt.h"
#include "../../include/interrupts/idt.h"
#include "../../include/sse/fpu.h"
#include "../../include/sse/sse.h"
#include "../../include/sched/sched.h"
#include "../include/syscall/syscall.h"
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
extern tss_t *tss[MAX_CPUS];
extern struct {
gdt_entry_t gdt_entries[5 + (MAX_CPUS * 2)];
} __attribute__((packed)) gdt;
static smp_info_t smp_info = {0};
static volatile uint32_t ap_online_count = 0;
tlb_shootdown_t tlb_shootdown_queue[MAX_CPUS] = {0};
volatile uint32_t sched_ready_flag = 0;
void sched_notify_ready(void) {
__sync_synchronize();
sched_ready_flag = 1;
__sync_synchronize();
serial_writestring("[SCHED] Scheduler ready, notifying all APs\n");
}
__attribute__((used))
void ap_entry_init(struct limine_mp_info* cpu_info) {
(void)cpu_info;
asm volatile ("cli");
lapic_write(0xF0, 0);
gdt_load();
idt_load();
uint32_t lapic_id = lapic_get_id();
smp_info_t* info = smp_get_info();
uint32_t my_index = 0;
for (uint32_t i = 0; i < info->cpu_count; i++) {
if (info->cpus[i].lapic_id == lapic_id && !info->cpus[i].is_bsp) {
my_index = i;
info->cpus[i].state = CPU_ONLINE;
break;
}
}
load_tss(info->cpus[my_index].tss_selector);
serial_printf("TSS Loaded (selector 0x%x)\n", info->cpus[my_index].tss_selector);
fpu_init();
sse_init();
enable_fsgsbase();
lapic_enable();
apic_timer_calibrate();
serial_printf("[SMP] AP %u LAPIC timer started\n", lapic_id);
cpu_info_t* cpu = smp_get_current_cpu();
percpu_t* region = percpu_regions[cpu->cpu_index];
set_percpu_base(region);
serial_printf("PerCPU base set for AP %u: 0x%llx\n",
lapic_id, (uint64_t)region);
syscall_init();
__sync_fetch_and_add(&ap_online_count, 1);
lapic_eoi();
serial_printf("[SMP] AP (LAPIC ID %u) initialized and online!\n", lapic_id);
while (!sched_ready_flag)
asm volatile ("pause");
serial_printf("[SMP] AP %u entering scheduler loop\n", lapic_id);
asm volatile ("sti");
sched_reschedule();
while (1)
asm volatile ("hlt");
}
void ap_entry_point(struct limine_mp_info* cpu_info) {
uint64_t stack_top;
asm volatile (
"mov 24(%%rdi), %0"
: "=r"(stack_top)
: "D"(cpu_info)
);
asm volatile (
"mov %0, %%rsp\n"
"cli\n"
"jmp ap_entry_init\n"
:
: "r"(stack_top), "D"(cpu_info)
: "memory"
);
}
static uint64_t smp_allocate_stack(uint32_t cpu_index, size_t stack_size) {
size_t pages = (stack_size + PAGE_SIZE - 1) / PAGE_SIZE;
void* stack_pages = pmm_alloc(pages);
if (!stack_pages) {
serial_printf("[SMP WARNING] Failed to allocate stack for CPU %u\n", cpu_index);
return 0;
}
uint64_t stack_virt = (uint64_t)stack_pages + stack_size;
serial_printf("[SMP] Allocated stack for CPU %u at 0x%llx (size %zu)\n",
cpu_index, stack_virt, stack_size);
return stack_virt;
}
void smp_boot_aps(struct limine_mp_response* mp_response) {
if (!mp_response) {
serial_writestring("[SMP ERROR] MP response is NULL\n");
return;
}
serial_writestring("\n[SMP] Booting Application Processors \n");
smp_info_t* info = smp_get_info();
uint32_t bsp_lapic_id = info->bsp_lapic_id;
uint32_t ap_count = 0;
for (uint64_t i = 0; i < mp_response->cpu_count; i++) {
struct limine_mp_info* cpu = mp_response->cpus[i];
if (cpu->lapic_id == bsp_lapic_id) continue;
uint64_t stack_top = smp_allocate_stack(i, AP_STACK_SIZE);
if (stack_top == 0) {
serial_printf("[SMP] Skipping CPU %u (stack alloc failed)\n", i);
info->cpus[i].state = CPU_FAULTED;
continue;
}
info->cpus[i].stack_top = stack_top;
info->cpus[i].state = CPU_BOOTED;
cpu->extra_argument = stack_top;
cpu->goto_address = (void*)ap_entry_point;
serial_printf("[SMP] Configured AP %lu (LAPIC ID %u) to boot at 0x%llx\n",
i, cpu->lapic_id, (uint64_t)ap_entry_point);
ap_count++;
}
__sync_synchronize();
if (ap_count > 0) {
serial_printf("[SMP] Waiting for %u AP(s) to initialize...\n", ap_count);
uint64_t timeout = 10000000;
while (ap_online_count < ap_count && timeout--)
asm volatile ("pause");
uint32_t online = ap_online_count;
if (online == ap_count)
serial_printf("[SMP SUCCESS] All %u AP(s) online!\n", ap_count);
else
serial_printf("[SMP WARNING] Only %u/%u AP(s) online (timeout)\n",
online, ap_count);
info->online_count = 1 + online;
} else {
serial_writestring("[SMP] No APs to boot\n");
}
serial_writestring("[SMP] AP Boot Sequence Complete \n\n");
}
static void smp_init_limine(struct limine_mp_response* response) {
if (!response) { serial_writestring("[SMP] Limine MP response is NULL\n"); return; }
serial_printf("[SMP] Initializing via Limine MP (CPU count: %u)\n",
response->cpu_count);
smp_info.cpu_count = response->cpu_count;
smp_info.online_count = 1;
acpi_madt_t* madt = (acpi_madt_t*)acpi_find_table("APIC", 0);
if (madt) {
smp_info.lapic_base = madt->local_apic_address;
serial_printf("[SMP] LAPIC base from ACPI: 0x%x\n", smp_info.lapic_base);
} else {
smp_info.lapic_base = 0xFEE00000;
}
for (uint64_t i = 0; i < response->cpu_count; i++) {
struct limine_mp_info* cpu = response->cpus[i];
smp_info.cpus[i].lapic_id = cpu->lapic_id;
smp_info.cpus[i].processor_id = cpu->lapic_id;
smp_info.cpus[i].acpi_id = 0;
smp_info.cpus[i].state = CPU_UNINITIALIZED;
smp_info.cpus[i].is_bsp = (cpu->lapic_id == response->bsp_lapic_id);
smp_info.cpus[i].cpu_index = i;
if (smp_info.cpus[i].is_bsp) {
smp_info.bsp_lapic_id = cpu->lapic_id;
smp_info.cpus[i].state = CPU_ONLINE;
serial_printf("[SMP] BSP detected - APIC ID: %u\n", cpu->lapic_id);
}
serial_printf("[SMP] CPU[%lu] - APIC ID: %u, Processor ID: %u, BSP: %s\n",
i, cpu->lapic_id, cpu->lapic_id,
smp_info.cpus[i].is_bsp ? "YES" : "NO");
}
}
void smp_init(struct limine_mp_response* mp_response) {
serial_writestring("\n[SMP] Initialization\n");
smp_init_limine(mp_response);
uint32_t bsp_index = 0;
for (uint32_t i = 0; i < smp_info.cpu_count; i++)
if (smp_info.cpus[i].is_bsp) { bsp_index = i; break; }
for (uint32_t i = 0; i < smp_info.cpu_count; i++) {
tss[i] = (tss_t *)calloc(1, sizeof(tss_t));
if (!tss[i]) { serial_printf("[SMP ERROR] FAILED to allocate TSS for CPU %u\n", i); continue; }
tss[i]->rsp0 = smp_allocate_stack(i, KERNEL_STACK_SIZE);
tss[i]->ist[0] = smp_allocate_stack(i, KERNEL_STACK_SIZE);
tss[i]->ist[1] = smp_allocate_stack(i, KERNEL_STACK_SIZE);
tss[i]->ist[2] = smp_allocate_stack(i, KERNEL_STACK_SIZE);
tss[i]->ist[3] = smp_allocate_stack(i, KERNEL_STACK_SIZE);
tss[i]->iobase = sizeof(tss_t);
serial_printf("TSS[%u] base: 0x%llx\n", i, (uint64_t)tss[i]);
tss_entry_t *entry = (tss_entry_t *)&gdt.gdt_entries[5 + (i * 2)];
entry->limit_low = sizeof(tss_t) - 1;
uint64_t addr = (uint64_t)tss[i];
entry->base_low = addr & 0xffff;
entry->base_middle = (addr >> 16) & 0xff;
entry->access = 0x89;
entry->limit_high_and_flags = 0;
entry->base_high = (addr >> 24) & 0xff;
entry->base_higher = addr >> 32;
entry->zero = 0;
smp_info.cpus[i].tss_selector = TSS_SELECTOR_BASE + (i * 0x10);
}
gdtr.size = (5 + (smp_info.cpu_count * 2)) * sizeof(gdt_entry_t) - 1;
serial_printf("Reloading extended GDT on BSP...\n");
gdt_load();
load_tss(smp_info.cpus[bsp_index].tss_selector);
serial_printf("BSP TSS loaded (selector 0x%x)\n",
smp_info.cpus[bsp_index].tss_selector);
uint32_t current_lapic_id = lapic_get_id();
serial_printf("[SMP] Current LAPIC ID: %u\n", current_lapic_id);
for (uint32_t i = 0; i < smp_info.cpu_count; i++) {
if (smp_info.cpus[i].lapic_id == current_lapic_id) {
smp_info.cpus[i].is_bsp = true;
smp_info.cpus[i].state = CPU_ONLINE;
smp_info.bsp_lapic_id = current_lapic_id;
break;
}
}
smp_print_info();
init_percpu_regions();
smp_boot_aps(mp_response);
set_percpu_base(percpu_regions[bsp_index]);
serial_printf("PerCPU base set for BSP %u: 0x%llx\n",
smp_info.bsp_lapic_id, (uint64_t)percpu_regions[bsp_index]);
serial_writestring("[SMP] Initialization Complete \n\n");
}
smp_info_t* smp_get_info(void) { return &smp_info; }
uint32_t smp_get_cpu_count(void) { return smp_info.cpu_count; }
uint32_t smp_get_online_count(void){ return smp_info.online_count; }
bool smp_is_bsp(void) { return lapic_get_id() == smp_info.bsp_lapic_id; }
cpu_info_t* smp_get_current_cpu(void) {
uint32_t id = lapic_get_id();
for (uint32_t i = 0; i < smp_info.cpu_count; i++)
if (smp_info.cpus[i].lapic_id == id) return &smp_info.cpus[i];
for (uint32_t i = 0; i < smp_info.cpu_count; i++)
if (smp_info.cpus[i].is_bsp) return &smp_info.cpus[i];
return NULL;
}
void smp_wait_for_ready(void) {
serial_writestring("Waiting until all APs are fully ready...\n");
while (smp_get_online_count() < smp_get_cpu_count())
asm volatile ("pause");
serial_writestring("All APs ready.\n");
}
uint32_t smp_get_lapic_id_for_cpu(uint32_t cpu_index) {
smp_info_t* info = smp_get_info();
if (cpu_index >= info->cpu_count) return 0xFFFFFFFF;
return info->cpus[cpu_index].lapic_id;
}
void smp_print_info(void) {
serial_printf("\n[SMP] CPU Information \n");
serial_printf("Total CPUs: %u\n", smp_info.cpu_count);
serial_printf("Online CPUs: %u\n", smp_info.online_count);
serial_printf("BSP APIC ID: %u\n", smp_info.bsp_lapic_id);
serial_printf("LAPIC Base: 0x%llx\n",smp_info.lapic_base);
const char* states[] = {"UNINITIALIZED","BOOTED","ONLINE","OFFLINE","FAULTED"};
for (uint32_t i = 0; i < smp_info.cpu_count; i++)
serial_printf("CPU[%u]: APIC ID: %u, Processor ID: %u, ACPI ID: %u, State: %s, BSP: %s\n",
i, smp_info.cpus[i].lapic_id, smp_info.cpus[i].processor_id,
smp_info.cpus[i].acpi_id, states[smp_info.cpus[i].state],
smp_info.cpus[i].is_bsp ? "YES" : "NO");
serial_printf("[SMP] End CPU Information \n");
}
void smp_print_info_fb(void) {
printf("[SMP] CPU Information \n");
printf("Total CPUs: %u\n", smp_info.cpu_count);
printf("Online CPUs: %u\n", smp_info.online_count);
printf("BSP APIC ID: %u\n", smp_info.bsp_lapic_id);
const char* states[] = {"UNINITIALIZED","BOOTED","ONLINE","OFFLINE","FAULTED"};
for (uint32_t i = 0; i < smp_info.cpu_count; i++)
printf("CPU[%u]: APIC ID: %u, State: %s, BSP: %s\n",
i, smp_info.cpus[i].lapic_id, states[smp_info.cpus[i].state],
smp_info.cpus[i].is_bsp ? "YES" : "NO");
}
+96
View File
@@ -0,0 +1,96 @@
#include "../../include/sse/fpu.h"
#include "../../include/io/serial.h"
#define COM1 0x3F8
void fpu_init(void) {
serial_writestring("[FPU] Initializing x87 FPU...\n");
if (!fpu_detect()) {
serial_writestring("[FPU] No FPU detected!\n");
return;
}
uint64_t cr0;
asm volatile("mov %%cr0, %0" : "=r"(cr0));
cr0 &= ~(1 << 2);
cr0 |= (1 << 1);
cr0 &= ~(1 << 3);
cr0 &= ~(1 << 5);
asm volatile("mov %0, %%cr0" : : "r"(cr0));
asm volatile("finit");
fpu_set_control_word(0x037F);
uint64_t cr4;
asm volatile("mov %%cr4, %0" : "=r"(cr4));
cr4 |= (1ULL << 9);
cr4 |= (1ULL << 10);
asm volatile("mov %0, %%cr4" : : "r"(cr4));
uint32_t mxcsr = 0x1F80;
asm volatile("ldmxcsr %0" : : "m"(mxcsr));
serial_writestring("[FPU] SSE/SSE2 enabled (CR4.OSFXSR|OSXMMEXCPT)\n");
serial_writestring("[FPU] FPU initialized successfully\n");
}
bool fpu_detect(void) {
uint32_t edx;
asm volatile(
"mov $1, %%eax\n"
"cpuid\n"
"mov %%edx, %0\n"
: "=r"(edx)
:
: "eax", "ebx", "ecx", "edx"
);
return (edx & (1 << 0)) != 0;
}
void fpu_reset(void) {
asm volatile("finit");
}
void fpu_set_control_word(uint16_t cw) {
asm volatile("fldcw %0" : : "m"(cw));
}
uint16_t fpu_get_control_word(void) {
uint16_t cw;
asm volatile("fnstcw %0" : "=m"(cw));
return cw;
}
void fpu_set_status_word(uint16_t sw) {
(void)sw;
}
uint16_t fpu_get_status_word(void) {
uint16_t sw;
asm volatile("fnstsw %0" : "=m"(sw));
return sw;
}
void fpu_set_tag_word(uint16_t tw) {
(void)tw;
}
uint16_t fpu_get_tag_word(void) {
struct {
uint16_t control_word;
uint16_t status_word;
uint16_t tag_word;
uint16_t fpu_ip;
uint16_t fpu_cs;
uint16_t fpu_opcode;
uint16_t fpu_dp;
uint16_t fpu_ds;
} __attribute__((packed)) fpu_env;
asm volatile("fstenv %0" : "=m"(fpu_env));
return fpu_env.tag_word;
}
+238
View File
@@ -0,0 +1,238 @@
#include "../../include/sse/sse.h"
#include "../../include/io/serial.h"
#include "../../include/apic/apic.h"
#include <string.h>
#include <stdio.h>
static uint32_t cpuid_edx = 0;
static uint32_t cpuid_ecx = 0;
static uint32_t cpuid_ebx_ext = 0;
static bool features_cached = false;
static void cpuid_cache_features(void) {
if (features_cached) return;
uint32_t eax, ebx, ecx, edx;
asm volatile(
"mov $1, %%eax\n"
"cpuid\n"
: "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
:
: "memory"
);
cpuid_edx = edx;
cpuid_ecx = ecx;
uint32_t max_leaf;
asm volatile("cpuid" : "=a"(max_leaf) : "0"(0) : "ebx", "ecx", "edx");
if (max_leaf >= 7) {
asm volatile(
"mov $7, %%eax\n"
"xor %%ecx, %%ecx\n"
"cpuid\n"
"mov %%ebx, %0\n"
: "=r"(cpuid_ebx_ext)
:
: "eax", "ecx", "edx"
);
}
features_cached = true;
}
bool sse_supported(void) { cpuid_cache_features(); return (cpuid_edx & (1 << 25)) != 0; }
bool sse2_supported(void) { cpuid_cache_features(); return (cpuid_edx & (1 << 26)) != 0; }
bool sse3_supported(void) { cpuid_cache_features(); return (cpuid_ecx & (1 << 0)) != 0; }
bool ssse3_supported(void) { cpuid_cache_features(); return (cpuid_ecx & (1 << 9)) != 0; }
bool sse4_1_supported(void){ cpuid_cache_features(); return (cpuid_ecx & (1 << 19)) != 0; }
bool sse4_2_supported(void){ cpuid_cache_features(); return (cpuid_ecx & (1 << 20)) != 0; }
bool avx_supported(void) { cpuid_cache_features(); return (cpuid_ecx & (1 << 28)) != 0; }
bool avx2_supported(void) { cpuid_cache_features(); return (cpuid_ebx_ext & (1 << 5)) != 0; }
bool mmx_supported(void) { cpuid_cache_features(); return (cpuid_edx & (1 << 23)) != 0; }
void sse_init(void) {
serial_writestring("[SSE] Initializing SSE/AVX...\n");
cpuid_cache_features();
if (!sse_supported()) {
serial_writestring("[SSE] SSE not supported — skipping SSE/AVX init\n");
return;
}
uint64_t cr4;
asm volatile("mov %%cr4, %0" : "=r"(cr4));
cr4 |= (1 << 9);
cr4 |= (1 << 10);
if (avx_supported()) {
uint32_t ecx;
asm volatile(
"mov $1, %%eax\n"
"cpuid\n"
"mov %%ecx, %0\n"
: "=r"(ecx)
:
: "eax", "ebx", "edx"
);
if (ecx & (1 << 27)) {
serial_writestring("[SSE] AVX supported, enabling...\n");
cr4 |= (1 << 18);
uint64_t xcr0;
asm volatile("xgetbv" : "=a"(xcr0) : "c"(0) : "edx");
xcr0 |= (1 << 1) | (1 << 2);
asm volatile("xsetbv" : : "c"(0), "a"(xcr0), "d"(xcr0 >> 32));
}
}
asm volatile("mov %0, %%cr4" : : "r"(cr4));
sse_set_mxcsr(MXCSR_DEFAULT);
serial_writestring("[SSE] SSE initialized successfully\n");
if (sse_supported()) serial_writestring("[SSE] SSE: YES\n");
if (sse2_supported()) serial_writestring("[SSE] SSE2: YES\n");
if (sse3_supported()) serial_writestring("[SSE] SSE3: YES\n");
if (sse4_1_supported())serial_writestring("[SSE] SSE4.1: YES\n");
if (sse4_2_supported())serial_writestring("[SSE] SSE4.2: YES\n");
if (avx_supported()) serial_writestring("[SSE] AVX: YES\n");
if (mmx_supported()) serial_writestring("[SSE] MMX: YES\n");
}
void sse_set_mxcsr(uint32_t mxcsr) {
if (!sse_supported()) return;
asm volatile("ldmxcsr %0" : : "m"(mxcsr));
}
uint32_t sse_get_mxcsr(void) {
if (!sse_supported()) return 0;
uint32_t mxcsr;
asm volatile("stmxcsr %0" : "=m"(mxcsr));
return mxcsr;
}
void mmx_enter(void) {
if (!mmx_supported()) return;
}
void mmx_exit(void) {
if (!mmx_supported()) return;
asm volatile("emms");
}
void sse_memcpy_fast(void *dest, const void *src, size_t n) {
if (!sse_supported() || n < 64) {
memcpy(dest, src, n);
return;
}
uint8_t *d = (uint8_t *)dest;
const uint8_t *s = (const uint8_t *)src;
size_t align_offset = (16 - ((uintptr_t)d & 0xF)) & 0xF;
if (align_offset > n) align_offset = n;
for (size_t i = 0; i < align_offset; i++) d[i] = s[i];
d += align_offset; s += align_offset; n -= align_offset;
size_t i;
for (i = 0; i + 64 <= n; i += 64) {
asm volatile(
"movdqu (%0), %%xmm0\n"
"movdqu 16(%0), %%xmm1\n"
"movdqu 32(%0), %%xmm2\n"
"movdqu 48(%0), %%xmm3\n"
"movdqu %%xmm0, (%1)\n"
"movdqu %%xmm1, 16(%1)\n"
"movdqu %%xmm2, 32(%1)\n"
"movdqu %%xmm3, 48(%1)\n"
:
: "r"(s + i), "r"(d + i)
: "xmm0", "xmm1", "xmm2", "xmm3", "memory"
);
}
for (; i < n; i++) d[i] = s[i];
}
void sse_memset_fast(void *dest, int value, size_t n) {
if (!sse_supported() || n < 64) {
memset(dest, value, n);
return;
}
uint8_t *d = (uint8_t *)dest;
uint8_t v = (uint8_t)value;
uint64_t pattern64 =
(uint64_t)v << 56 | (uint64_t)v << 48 | (uint64_t)v << 40 | (uint64_t)v << 32 |
(uint64_t)v << 24 | (uint64_t)v << 16 | (uint64_t)v << 8 | (uint64_t)v;
size_t align_offset = (16 - ((uintptr_t)d & 0xF)) & 0xF;
if (align_offset > n) align_offset = n;
for (size_t i = 0; i < align_offset; i++) d[i] = v;
d += align_offset; n -= align_offset;
asm volatile(
"movq %0, %%xmm0\n"
"punpcklqdq %%xmm0, %%xmm0\n"
: : "r"(pattern64) : "xmm0"
);
size_t i;
for (i = 0; i + 64 <= n; i += 64) {
asm volatile(
"movdqu %%xmm0, (%0)\n"
"movdqu %%xmm0, 16(%0)\n"
"movdqu %%xmm0, 32(%0)\n"
"movdqu %%xmm0, 48(%0)\n"
:
: "r"(d + i)
: "memory"
);
}
for (; i < n; i++) d[i] = v;
}
void print_simd_cpuid(void) {
printf("\n=== CPUID/SIMD INFORMATION ===\n");
uint32_t eax, ebx, ecx, edx;
char vendor[13] = {0};
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(0));
*(uint32_t *)vendor = ebx;
*(uint32_t *)(vendor + 4) = edx;
*(uint32_t *)(vendor + 8) = ecx;
printf("CPU Vendor: %s\n", vendor);
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1));
printf("Processor Signature: 0x%08x\n", eax);
printf("Stepping: %d Model: %d Family: %d\n",
eax & 0xF, (eax >> 4) & 0xF, (eax >> 8) & 0xF);
printf("\nSIMD Extensions:\n");
printf("MMX: %s\n", (edx & (1 << 23)) ? "YES" : "NO");
printf("SSE: %s\n", (edx & (1 << 25)) ? "YES" : "NO");
printf("SSE2: %s\n", (edx & (1 << 26)) ? "YES" : "NO");
printf("SSE3: %s\n", (ecx & (1 << 0)) ? "YES" : "NO");
printf("SSSE3: %s\n", (ecx & (1 << 9)) ? "YES" : "NO");
printf("SSE4.1: %s\n", (ecx & (1 << 19)) ? "YES" : "NO");
printf("SSE4.2: %s\n", (ecx & (1 << 20)) ? "YES" : "NO");
printf("AVX: %s\n", (ecx & (1 << 28)) ? "YES" : "NO");
printf("=== END OF CPUID/SIMD INFO ===\n\n");
}
void enable_fsgsbase(void) {
uint32_t eax = 7, ebx, ecx = 0, edx;
asm volatile("cpuid"
: "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
: "0"(eax), "2"(ecx));
if (ebx & (1 << 0)) {
uint64_t cr4;
asm volatile("mov %%cr4, %0" : "=r"(cr4));
cr4 |= (1ULL << 16);
asm volatile("mov %0, %%cr4" :: "r"(cr4));
serial_printf("[FSGSBASE] Enabled on CPU %u\n", lapic_get_id());
} else {
serial_writestring("[FSGSBASE] Not supported — using MSR fallback\n");
}
}
+431
View File
@@ -0,0 +1,431 @@
#include "../../include/syscall/syscall.h"
#include "../../include/syscall/syscall_nums.h"
#include "../../include/syscall/errno.h"
#include "../../include/drivers/disk.h"
#include "../../include/drivers/blkdev.h"
#include "../../include/drivers/ata.h"
#include "../../include/drivers/partition.h"
#include "../../include/sched/sched.h"
#include "../../include/sched/capabilities.h"
#include "../../include/smp/percpu.h"
#include "../../include/io/serial.h"
#include "../../include/memory/pmm.h"
#include "../../include/fs/vfs.h"
#include "../../include/fs/ext2.h"
#include "../../include/fs/fat32.h"
#include <string.h>
#include <stdlib.h>
static inline task_t* disk_cur_task(void) {
percpu_t* pc = get_percpu();
return pc ? (task_t*)pc->current_task : NULL;
}
static int disk_strncpy_from_user(char *dst, const char *src, size_t max) {
uintptr_t addr = (uintptr_t)src;
if (addr < 0x1000ULL || addr >= 0x0000800000000000ULL) return -EFAULT;
for (size_t i = 0; i < max - 1; i++) {
dst[i] = src[i];
if (!dst[i]) return (int)i;
}
dst[max-1] = '\0';
return (int)(max-1);
}
static int user_ptr_ok(uint64_t p) {
return p >= 0x1000ULL && p < 0x0000800000000000ULL;
}
int64_t sys_disk_mount(uint64_t devname_ptr, uint64_t path_ptr, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6) {
(void)a3; (void)a4; (void)a5; (void)a6;
task_t *t = disk_cur_task();
if (!t) return -ESRCH;
if (t->uid != 0 && !(t->capabilities & (1ULL << 1))) return -EPERM;
char devname[64], path[256];
if (disk_strncpy_from_user(devname, (const char *)devname_ptr, sizeof(devname)) < 0) return -EFAULT;
if (disk_strncpy_from_user(path, (const char *)path_ptr, sizeof(path)) < 0) return -EFAULT;
serial_printf("[SYSCALL] disk_mount('%s', '%s') by pid=%u\n", devname, path, t->pid);
return disk_mount(devname, path);
}
int64_t sys_disk_umount(uint64_t path_ptr, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6) {
(void)a2; (void)a3; (void)a4; (void)a5; (void)a6;
task_t *t = disk_cur_task();
if (!t) return -ESRCH;
if (t->uid != 0 && !(t->capabilities & (1ULL << 1))) return -EPERM;
char path[256];
if (disk_strncpy_from_user(path, (const char *)path_ptr, sizeof(path)) < 0) return -EFAULT;
return disk_umount(path);
}
int64_t sys_disk_format(uint64_t devname_ptr, uint64_t label_ptr,
uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6)
{
(void)a3; (void)a4; (void)a5; (void)a6;
task_t *t = disk_cur_task();
if (!t) return -ESRCH;
if (t->uid != 0 && !(t->capabilities & (1ULL << 1))) return -EPERM;
char devname[64], label[64];
if (disk_strncpy_from_user(devname, (const char *)devname_ptr, sizeof(devname)) < 0) return -EFAULT;
if (label_ptr) {
if (disk_strncpy_from_user(label, (const char *)label_ptr, sizeof(label)) < 0) return -EFAULT;
} else {
strncpy(label, devname, sizeof(label) - 1);
}
return disk_format(devname, label);
}
int64_t sys_disk_info(uint64_t index, uint64_t buf_ptr, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6) {
(void)a3; (void)a4; (void)a5; (void)a6;
if (!buf_ptr) return -EINVAL;
blkdev_t *dev = blkdev_get((int)index);
if (!dev || !dev->present) return -ENODEV;
struct {
char name[32];
uint64_t sectors;
uint64_t size_bytes;
char model[41];
uint8_t present;
uint8_t _pad[6];
} info;
memset(&info, 0, sizeof(info));
strncpy(info.name, dev->name, 31);
info.sectors = dev->sector_count;
info.size_bytes = dev->size_bytes;
info.present = 1;
ata_drive_t *ata = (ata_drive_t *)dev->priv;
if (ata) strncpy(info.model, ata->model, 40);
if (!user_ptr_ok(buf_ptr)) return -EFAULT;
memcpy((void *)buf_ptr, &info, sizeof(info));
return 0;
}
int64_t sys_disk_read_raw(uint64_t devname_ptr, uint64_t lba, uint64_t count,
uint64_t buf_ptr, uint64_t a5, uint64_t a6)
{
(void)a5; (void)a6;
task_t *t = disk_cur_task();
if (!t) return -ESRCH;
if (t->uid != 0 && !(t->capabilities & (1ULL << 1))) return -EPERM;
char devname[64];
if (disk_strncpy_from_user(devname, (const char *)devname_ptr, sizeof(devname)) < 0) return -EFAULT;
if (!user_ptr_ok(buf_ptr)) return -EFAULT;
if (count == 0 || count > 256) return -EINVAL;
const char *name = devname;
if (strncmp(name, "/dev/", 5) == 0) name += 5;
blkdev_t *dev = blkdev_get_by_name(name);
if (!dev) return -ENODEV;
if (lba + count > dev->sector_count) return -EINVAL;
int r = dev->ops->read_sectors(dev, lba, (uint32_t)count, (void *)buf_ptr);
if (r < 0) return r;
return (int64_t)(count * dev->sector_size);
}
int64_t sys_disk_write_raw(uint64_t devname_ptr, uint64_t lba, uint64_t count,
uint64_t buf_ptr, uint64_t a5, uint64_t a6)
{
(void)a5; (void)a6;
task_t *t = disk_cur_task();
if (!t) return -ESRCH;
if (t->uid != 0 && !(t->capabilities & (1ULL << 1))) return -EPERM;
char devname[64];
if (disk_strncpy_from_user(devname, (const char *)devname_ptr, sizeof(devname)) < 0) return -EFAULT;
if (!user_ptr_ok(buf_ptr)) return -EFAULT;
if (count == 0 || count > 256) return -EINVAL;
const char *name = devname;
if (strncmp(name, "/dev/", 5) == 0) name += 5;
blkdev_t *dev = blkdev_get_by_name(name);
if (!dev) return -ENODEV;
if (lba + count > dev->sector_count) return -EINVAL;
int r = dev->ops->write_sectors(dev, lba, (uint32_t)count, (const void *)buf_ptr);
if (r < 0) return r;
if (dev->ops->flush) dev->ops->flush(dev);
return (int64_t)(count * dev->sector_size);
}
int64_t sys_disk_partition(uint64_t devname_ptr, uint64_t specs_ptr, uint64_t nparts,
uint64_t a4, uint64_t a5, uint64_t a6)
{
(void)a4; (void)a5; (void)a6;
task_t *t = disk_cur_task();
if (!t) return -ESRCH;
if (t->uid != 0 && !(t->capabilities & (1ULL << 1))) return -EPERM;
char devname[64];
if (disk_strncpy_from_user(devname, (const char *)devname_ptr, sizeof(devname)) < 0) return -EFAULT;
if (!user_ptr_ok(specs_ptr)) return -EFAULT;
if (nparts == 0 || nparts > 4) return -EINVAL;
const char *name = devname;
if (strncmp(name, "/dev/", 5) == 0) name += 5;
blkdev_t *dev = blkdev_get_by_name(name);
if (!dev) return -ENODEV;
cervus_mbr_part_t specs[4];
memset(specs, 0, sizeof(specs));
memcpy(specs, (const void *)specs_ptr, sizeof(cervus_mbr_part_t) * nparts);
mbr_partition_t parts[4];
memset(parts, 0, sizeof(parts));
for (uint64_t i = 0; i < nparts; i++) {
parts[i].boot_flag = specs[i].boot_flag ? 0x80 : 0x00;
parts[i].type = specs[i].type;
parts[i].lba_start = specs[i].lba_start;
parts[i].sector_count = specs[i].sector_count;
parts[i].chs_start[0] = 0xFE;
parts[i].chs_start[1] = 0xFF;
parts[i].chs_start[2] = 0xFF;
parts[i].chs_end[0] = 0xFE;
parts[i].chs_end[1] = 0xFF;
parts[i].chs_end[2] = 0xFF;
}
uint32_t sig = 0xCE705CE7;
int r = partition_write_mbr(dev, parts, sig);
if (r < 0) return r;
if (dev->ops->flush) dev->ops->flush(dev);
partition_scan(dev);
return 0;
}
int64_t sys_disk_mkfs_fat32(uint64_t devname_ptr, uint64_t label_ptr,
uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6)
{
(void)a3; (void)a4; (void)a5; (void)a6;
task_t *t = disk_cur_task();
if (!t) return -ESRCH;
if (t->uid != 0 && !(t->capabilities & (1ULL << 1))) return -EPERM;
char devname[64], label[16];
if (disk_strncpy_from_user(devname, (const char *)devname_ptr, sizeof(devname)) < 0) return -EFAULT;
if (label_ptr) {
if (disk_strncpy_from_user(label, (const char *)label_ptr, sizeof(label)) < 0) return -EFAULT;
} else {
strncpy(label, "CERVUS", sizeof(label) - 1);
label[sizeof(label) - 1] = '\0';
}
const char *name = devname;
if (strncmp(name, "/dev/", 5) == 0) name += 5;
blkdev_t *dev = blkdev_get_by_name(name);
if (!dev) return -ENODEV;
return fat32_format(dev, label);
}
int64_t sys_disk_bios_install(uint64_t a1, uint64_t a2, uint64_t a3,
uint64_t a4, uint64_t a5, uint64_t a6) {
(void)a4; (void)a5; (void)a6;
const char *disk_name = (const char *)a1;
const void *sys_data = (const void *)a2;
uint32_t sys_size = (uint32_t)a3;
if (!disk_name || !sys_data || sys_size < 512) return -EINVAL;
blkdev_t *dev = blkdev_get_by_name(disk_name);
if (!dev) return -ENOENT;
if (dev->sector_size != 512) return -EINVAL;
uint8_t sector0[512];
int r = dev->ops->read_sectors(dev, 0, 1, sector0);
if (r < 0) return r;
uint8_t saved_timestamp[6];
uint8_t saved_parttable[70];
memcpy(saved_timestamp, sector0 + 218, 6);
memcpy(saved_parttable, sector0 + 440, 70);
const uint8_t *src = (const uint8_t *)sys_data;
memcpy(sector0, src, 512);
memcpy(sector0 + 218, saved_timestamp, 6);
memcpy(sector0 + 440, saved_parttable, 70);
uint64_t stage2_loc = 512;
memcpy(sector0 + 0x1A4, &stage2_loc, 8);
uint32_t stage2_bytes = sys_size - 512;
uint32_t stage2_sectors = (stage2_bytes + 511) / 512;
if (1 + stage2_sectors >= 2048) return -ENOSPC;
uint8_t sector_buf[512];
for (uint32_t i = 0; i < stage2_sectors; i++) {
uint32_t off = i * 512;
uint32_t take = (stage2_bytes - off >= 512) ? 512 : (stage2_bytes - off);
memset(sector_buf, 0, 512);
memcpy(sector_buf, src + 512 + off, take);
r = dev->ops->write_sectors(dev, 1 + i, 1, sector_buf);
if (r < 0) return r;
}
r = dev->ops->write_sectors(dev, 0, 1, sector0);
if (r < 0) return r;
if (dev->ops->flush) dev->ops->flush(dev);
serial_printf("[bios-install] deployed: stage1=512B at LBA 0, stage2=%uB at LBA 1..%u\n",
stage2_bytes, stage2_sectors);
return 0;
}
int64_t sys_disk_list_parts(uint64_t out_ptr, uint64_t max,
uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6)
{
(void)a3; (void)a4; (void)a5; (void)a6;
if (!user_ptr_ok(out_ptr)) return -EFAULT;
if (max == 0) return -EINVAL;
cervus_part_info_t *out = (cervus_part_info_t *)out_ptr;
int total = blkdev_count();
uint64_t written = 0;
for (int i = 0; i < total && written < max; i++) {
blkdev_t *d = blkdev_get(i);
if (!d || !d->present) continue;
size_t nlen = strlen(d->name);
bool is_part = false;
if (nlen >= 2) {
for (size_t k = 0; k < nlen; k++) {
if (d->name[k] >= '0' && d->name[k] <= '9') { is_part = true; break; }
}
}
cervus_part_info_t info;
memset(&info, 0, sizeof(info));
strncpy(info.part_name, d->name, sizeof(info.part_name) - 1);
if (is_part) {
char base[32]; int bi = 0;
for (size_t k = 0; k < nlen && bi < 31; k++) {
if (d->name[k] >= '0' && d->name[k] <= '9') break;
base[bi++] = d->name[k];
}
base[bi] = '\0';
strncpy(info.disk_name, base, sizeof(info.disk_name) - 1);
info.part_num = (uint32_t)atoi(d->name + bi);
} else {
strncpy(info.disk_name, d->name, sizeof(info.disk_name) - 1);
info.part_num = 0;
}
info.size_bytes = d->size_bytes;
info.sector_count = d->sector_count;
info.lba_start = 0;
info.type = 0;
info.bootable = 0;
memcpy(&out[written], &info, sizeof(info));
written++;
}
return (int64_t)written;
}
int64_t sys_unlink(uint64_t path_ptr, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6) {
(void)a2;(void)a3;(void)a4;(void)a5;(void)a6;
char path[256];
if (disk_strncpy_from_user(path, (const char *)path_ptr, sizeof(path)) < 0) return -EFAULT;
char dirpath[256]; strncpy(dirpath, path, 255);
char *slash = NULL;
for (int i = (int)strlen(dirpath)-1; i >= 0; i--) { if (dirpath[i]=='/') { slash=&dirpath[i]; break; } }
if (!slash) return -EINVAL;
char name[256]; strncpy(name, slash+1, 255);
if (slash==dirpath) dirpath[1]='\0'; else *slash='\0';
vnode_t *dir = NULL;
int r = vfs_lookup(dirpath, &dir);
if (r<0) return r;
if (!dir->ops || !dir->ops->unlink) { vnode_unref(dir); return -ENOSYS; }
r = dir->ops->unlink(dir, name);
vnode_unref(dir);
if (r == 0) vfs_sync_all();
return r;
}
int64_t sys_rmdir(uint64_t p, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6) {
return sys_unlink(p,a2,a3,a4,a5,a6);
}
int64_t sys_mkdir(uint64_t path_ptr, uint64_t mode, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6) {
(void)a3;(void)a4;(void)a5;(void)a6;
char path[256];
if (disk_strncpy_from_user(path, (const char *)path_ptr, sizeof(path)) < 0) return -EFAULT;
int r = vfs_mkdir(path, (uint32_t)mode);
if (r == 0) vfs_sync_all();
return r;
}
int64_t sys_rename(uint64_t old_ptr, uint64_t new_ptr, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6) {
(void)a3;(void)a4;(void)a5;(void)a6;
char oldp[256], newp[256];
if (disk_strncpy_from_user(oldp, (const char *)old_ptr, 256) < 0) return -EFAULT;
if (disk_strncpy_from_user(newp, (const char *)new_ptr, 256) < 0) return -EFAULT;
vnode_t *src_node = NULL;
int r = vfs_lookup(oldp, &src_node);
if (r < 0) return r;
vnode_t *dst_node = NULL;
if (vfs_lookup(newp, &dst_node) == 0) {
if (dst_node->type == VFS_NODE_DIR) {
const char *base = oldp;
for (const char *p = oldp; *p; p++) if (*p == '/') base = p + 1;
size_t dlen = strlen(newp);
if (dlen + 1 + strlen(base) < 255) {
if (newp[dlen-1] != '/') { newp[dlen] = '/'; newp[dlen+1] = '\0'; dlen++; }
strncat(newp, base, 254 - dlen);
}
}
vnode_unref(dst_node);
}
vfs_file_t *src_f = NULL, *dst_f = NULL;
r = vfs_open(oldp, O_RDONLY, 0, &src_f);
if (r < 0) { vnode_unref(src_node); return r; }
r = vfs_open(newp, O_WRONLY | O_CREAT | O_TRUNC, src_node->mode, &dst_f);
if (r < 0) { vfs_close(src_f); vnode_unref(src_node); return r; }
char buf[512]; int64_t n;
while ((n = vfs_read(src_f, buf, sizeof(buf))) > 0) vfs_write(dst_f, buf, (size_t)n);
vfs_close(src_f);
vfs_close(dst_f);
vnode_unref(src_node);
char dirp[256]; strncpy(dirp, oldp, 255); dirp[255] = '\0';
char *sl = NULL;
for (int i = (int)strlen(dirp)-1; i >= 0; i--) if (dirp[i] == '/') { sl = &dirp[i]; break; }
if (sl) {
char nm[256]; strncpy(nm, sl+1, 255);
if (sl == dirp) dirp[1] = '\0'; else *sl = '\0';
vnode_t *dir = NULL;
if (vfs_lookup(dirp, &dir) == 0) {
if (dir->ops && dir->ops->unlink) dir->ops->unlink(dir, nm);
vnode_unref(dir);
}
}
vfs_sync_all();
return 0;
}
int64_t sys_list_mounts(uint64_t a1, uint64_t a2, uint64_t a3,
uint64_t a4, uint64_t a5, uint64_t a6) {
(void)a3; (void)a4; (void)a5; (void)a6;
vfs_mount_info_t *out = (vfs_mount_info_t *)a1;
int max = (int)a2;
if (!out || max <= 0) return -EINVAL;
return vfs_list_mounts(out, max);
}
int64_t sys_statvfs(uint64_t a1, uint64_t a2, uint64_t a3,
uint64_t a4, uint64_t a5, uint64_t a6) {
(void)a3; (void)a4; (void)a5; (void)a6;
const char *path = (const char *)a1;
vfs_statvfs_t *out = (vfs_statvfs_t *)a2;
if (!path || !out) return -EINVAL;
return vfs_statvfs(path, out);
}
File diff suppressed because it is too large Load Diff
+145
View File
@@ -0,0 +1,145 @@
section .text
global syscall_entry
extern syscall_handler_c
extern sched_reschedule
PERCPU_KERNEL_RSP equ 0
PERCPU_USER_RSP equ 8
PERCPU_SAVED_RBP equ 48
PERCPU_SAVED_RBX equ 56
PERCPU_SAVED_R12 equ 64
PERCPU_SAVED_R13 equ 72
PERCPU_SAVED_R14 equ 80
PERCPU_SAVED_R15 equ 88
PERCPU_SAVED_R11 equ 96
PERCPU_SAVED_RIP equ 104
PERCPU_NEED_RESCHED equ 40
PERCPU_CURRENT_TASK equ 24
TASK_USER_RSP equ 144
TASK_USER_SAVED_RIP equ 272
TASK_USER_SAVED_RBP equ 280
TASK_USER_SAVED_RBX equ 288
TASK_USER_SAVED_R12 equ 296
TASK_USER_SAVED_R13 equ 304
TASK_USER_SAVED_R14 equ 312
TASK_USER_SAVED_R15 equ 320
TASK_USER_SAVED_R11 equ 328
syscall_entry:
swapgs
mov [gs:PERCPU_USER_RSP], rsp
mov [gs:PERCPU_SAVED_RBP], rbp
mov [gs:PERCPU_SAVED_RBX], rbx
mov [gs:PERCPU_SAVED_R12], r12
mov [gs:PERCPU_SAVED_R13], r13
mov [gs:PERCPU_SAVED_R14], r14
mov [gs:PERCPU_SAVED_R15], r15
mov [gs:PERCPU_SAVED_R11], r11
mov [gs:PERCPU_SAVED_RIP], rcx
mov rsp, [gs:PERCPU_KERNEL_RSP]
push r11
push rcx
push rbx
push r12
push r13
push r14
push r15
push rbp
push rcx
mov rcx, rdx
mov r9, r8
mov r8, r10
mov rdx, rsi
mov rsi, rdi
mov rdi, rax
call syscall_handler_c
add rsp, 8
pop rbp
pop r15
pop r14
pop r13
pop r12
pop rbx
pop rcx
pop r11
.check_resched:
cmp byte [gs:PERCPU_NEED_RESCHED], 0
je .no_resched
mov byte [gs:PERCPU_NEED_RESCHED], 0
push rax
call sched_reschedule
pop rax
jmp .check_resched
.no_resched:
cli
mov r10, rax
mov rax, [gs:PERCPU_CURRENT_TASK]
mov rcx, [rax + TASK_USER_SAVED_RIP]
test rcx, rcx
jnz .rip_ok
push rax
push rcx
extern serial_printf
mov rdi, rax
mov rsi, [rax + 168]
mov esi, esi
mov rdx, [rax + 272]
mov rcx, [rax + 144]
lea rdi, [rel .fmt_zero_rip]
call serial_printf
lea rdi, [rel .msg_zero_rip]
extern kernel_panic
call kernel_panic
.fmt_zero_rip: db "[NO_RESCHED-BUG] task=0x%llx pid=%u user_saved_rip=0x%llx user_rsp=0x%llx", 10, 0
.msg_zero_rip: db "sysret: user_saved_rip=0 — would fault at NULL", 0
.rip_ok:
mov rsp, [rax + TASK_USER_RSP]
mov rcx, [rax + TASK_USER_SAVED_RIP]
mov r11, [rax + TASK_USER_SAVED_R11]
mov rbp, [rax + TASK_USER_SAVED_RBP]
mov rbx, [rax + TASK_USER_SAVED_RBX]
mov r12, [rax + TASK_USER_SAVED_R12]
mov r13, [rax + TASK_USER_SAVED_R13]
mov r14, [rax + TASK_USER_SAVED_R14]
mov r15, [rax + TASK_USER_SAVED_R15]
mov rax, r10
and r11, 0x00000000003C0FFF
or r11, 0x0000000000000202
mov r9, rcx
shr r9, 47
jnz .sysret_bad_rip
mov r9, rsp
shr r9, 47
jnz .sysret_bad_rsp
swapgs
o64 sysret
.sysret_bad_rip:
extern sysret_bad_rip_panic
sti
mov rdi, rcx
mov rsi, rax
call sysret_bad_rip_panic
cli
hlt
.sysret_bad_rsp:
extern sysret_bad_rsp_panic
sti
mov rdi, rsp
mov rsi, rcx
call sysret_bad_rsp_panic
cli
hlt