push
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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.
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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 = ®ions[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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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, ¤t_task[cpu], switch_cr3);
|
||||
else context_switch(&bootstrap_tasks[cpu], next, ¤t_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");
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
Reference in New Issue
Block a user