mirror of
https://github.com/gbowne1/ClassicOS.git
synced 2024-11-21 13:56:53 -08:00
fixed and added a lot more stuff like timer and acpi and ahci stub code and a kernel loader as well as worked on pci enumeration
This commit is contained in:
parent
ad70cfd836
commit
528fb00d49
BIN
.vscode/browse.vc.db
vendored
BIN
.vscode/browse.vc.db
vendored
Binary file not shown.
BIN
.vscode/browse.vc.db-shm
vendored
BIN
.vscode/browse.vc.db-shm
vendored
Binary file not shown.
@ -40,16 +40,58 @@ start:
|
||||
call printstr
|
||||
|
||||
; Load the kernel into memory
|
||||
; ...
|
||||
%include "kernel_loader.asm"
|
||||
|
||||
; Set up GDT, IDT, IVT
|
||||
; ...
|
||||
; Set up IVT (GTD and IDT not used in 16 bit real mode)
|
||||
; Define interrupt handlers (replace with your actual handler code)
|
||||
div_by_zero_handler:
|
||||
cli
|
||||
hlt
|
||||
|
||||
timer_handler:
|
||||
; Your timer interrupt handler code
|
||||
hlt
|
||||
ret
|
||||
|
||||
; IVT entries (offset 0 for all handlers since segment 0)
|
||||
times 0x100 dw 0x0000 ; Initialize unused entries
|
||||
dw offset div_by_zero_handler ; Interrupt 0 (Division by Zero)
|
||||
times 0x7 dw 0x0000 ; Skip unused entries
|
||||
dw offset timer_handler ; Interrupt 0x8 (Timer)
|
||||
|
||||
; Switch to protected mode
|
||||
; ...
|
||||
; Enable Protected Mode
|
||||
switch_to_protected_mode:
|
||||
cli ; Disable interrupts
|
||||
mov eax, cr0 ; Get current CR0 value
|
||||
or eax, 0x01 ; Set PE bit (Protected Mode Enable)
|
||||
mov cr0, eax ; Write modified CR0 value back
|
||||
|
||||
; Set up stack and start executing kernel's code
|
||||
; ...
|
||||
; Define GDT structure (replace with your actual GDT definition)
|
||||
gdt_start: ; Beginning of GDT
|
||||
times 5 dd 0 ; Null descriptor entries (optional)
|
||||
gdt_code: ; Code segment descriptor
|
||||
dw 0xffff ; Segment size (limit)
|
||||
dw 0x0000 ; Segment base address (low)
|
||||
db 0x0 ; Segment base address (high)
|
||||
db 10011010b ; Access rights (present, readable, conforming, executable)
|
||||
db 11001111b ; Access rights (long mode, 4-granularity, size)
|
||||
gdt_end: ; End of GDT
|
||||
gdt_descriptor:
|
||||
equ gdt_end - gdt_start ; Size of GDT
|
||||
dw gdt_descriptor ; Offset of GDT
|
||||
dd gdt_start ; Base address of GDT
|
||||
|
||||
; Load the GDT
|
||||
lgdt [gdt_descriptor] ; Load GDT descriptor into GDTR
|
||||
|
||||
; Set up Stack (replace with your actual stack segment and address)
|
||||
mov ss, data_segment ; Set stack segment selector
|
||||
mov esp, 0x100000 ; Set stack pointer (top of stack)
|
||||
|
||||
; Start Kernel Execution
|
||||
jmp farptr kernel_entry ; Jump to kernel entry point (replace with actual address)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; printstr routine, prints the string pointed by si using int 0x10 ;
|
||||
|
28
src/boot/kernel_loader.asm
Normal file
28
src/boot/kernel_loader.asm
Normal file
@ -0,0 +1,28 @@
|
||||
; Define constants (adjust as needed)
|
||||
boot_drive equ 0x00 ; Drive to load kernel from (usually 0 for primary)
|
||||
kernel_sector equ 1 ; Sector containing the kernel (adjust for your kernel's location)
|
||||
kernel_segments equ 4 ; Number of sectors to load (adjust for your kernel size)
|
||||
kernel_load_address equ 0x1000 ; Memory address to load the kernel
|
||||
|
||||
; ... (Function prototypes and int_13h implementation copied from previous response)
|
||||
; Function prototypes for readability
|
||||
; (These functions are not strictly necessary in NASM, but improve code organization)
|
||||
; Prototype for BIOS disk read interrupt (INT 13h)
|
||||
void int_13h(unsigned int ah, unsigned int al, unsigned int dx, unsigned int ch, unsigned int cl, unsigned int bx);
|
||||
|
||||
; Function prototype for error handling or printing a message
|
||||
void error_handler(const char *message);
|
||||
; Main kernel loading code
|
||||
mov bx, kernel_load_address ; Set load address
|
||||
|
||||
; Loop to load kernel sectors
|
||||
mov cx, 0 ; Initialize counter
|
||||
loop_load:
|
||||
int_13h(0x02, kernel_segments, boot_drive * 256 + kernel_sector, ch, cl, bx) ; Read sectors
|
||||
add bx, 512 * kernel_segments ; Update load address
|
||||
inc cx ; Increment counter
|
||||
cmp cx, kernel_segments ; Check if all sectors loaded
|
||||
jne loop_load ; Jump back if not finished
|
||||
|
||||
; Success - kernel is now loaded into memory
|
||||
ret ; Return to the main bootloader code
|
@ -20,28 +20,129 @@ void pci_init()
|
||||
pci_write(PCI_COMMAND_PORT, 0x04);
|
||||
}
|
||||
|
||||
// Read from a PCI device
|
||||
uint8_t pci_read_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset)
|
||||
{
|
||||
uint16_t port = PCI_BASE_ADDRESS | ((uint16_t)bus << 8) | ((uint16_t)device << 3) | ((uint16_t)function << 0x0B) | ((uint16_t)offset & 0xFC);
|
||||
uint8_t value;
|
||||
|
||||
// Read from the specified port
|
||||
__asm__ volatile("inb %1, %0" : "=a"(value) : "dN"(port));
|
||||
|
||||
return value;
|
||||
}
|
||||
// Function to write a value to a PCI configuration register
|
||||
void pci_write_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint8_t value)
|
||||
{
|
||||
// ... (your existing implementation for setting up the port address)
|
||||
|
||||
// Write the specified value to the specified offset
|
||||
__asm__ volatile("outb %0, %1" : : "a"(value), "dN"(port));
|
||||
}
|
||||
|
||||
// Set the PCI Latency Timer for a device
|
||||
void pci_set_latency(uint8_t bus, uint8_t device, uint8_t function, uint8_t value)
|
||||
{
|
||||
pci_write_config(bus, device, function, 0x0D, value);
|
||||
}
|
||||
|
||||
uint16_t pci_read_config_word(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset)
|
||||
{
|
||||
uint16_t value = 0;
|
||||
value |= pci_read_config(bus, device, function, offset);
|
||||
value |= (uint16_t)pci_read_config(bus, device, function, offset + 1) << 8;
|
||||
return value;
|
||||
}
|
||||
|
||||
// Function to read a 32-bit value from PCI configuration space
|
||||
uint32_t pci_read_config_dword(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset)
|
||||
{
|
||||
uint32_t value = 0;
|
||||
value |= pci_read_config(bus, device, function, offset);
|
||||
value |= (uint32_t)pci_read_config(bus, device, function, offset + 1) << 8;
|
||||
value |= (uint32_t)pci_read_config(bus, device, function, offset + 2) << 16;
|
||||
value |= (uint32_t)pci_read_config(bus, device, function, offset + 3) << 24;
|
||||
return value;
|
||||
}
|
||||
|
||||
// Structure to hold information about a BAR
|
||||
typedef struct
|
||||
{
|
||||
uint8_t type; // Memory (0) or I/O (1)
|
||||
uint32_t base; // Base address
|
||||
uint32_t size; // Size of the region (in bytes)
|
||||
} bar_t;
|
||||
|
||||
// Function to decode a BAR register
|
||||
bar_t pci_decode_bar(uint32_t bar_value)
|
||||
{
|
||||
bar_t bar;
|
||||
bar.type = (bar_value & 1) ? 1 : 0; // Check bit 0 for type
|
||||
|
||||
// Extract address and size information (logic might need adjustment)
|
||||
bar.base = bar_value & ~((1 << 0) | (1 << 1)); // Mask out type and prefetchable bits
|
||||
|
||||
// Size calculation needs further processing based on Memory Space Enable bit (bit 3)
|
||||
uint32_t size_bits = bar_value & ~(bar.base | (1 << 0) | (1 << 1) | (1 << 3));
|
||||
if (size_bits == 0)
|
||||
{ // Handle case where size is 0
|
||||
bar.size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Calculate size based on the number of leading 1s in size_bits
|
||||
int num_leading_ones = 0;
|
||||
while (size_bits && (size_bits & 0x80000000))
|
||||
{
|
||||
num_leading_ones++;
|
||||
size_bits <<= 1;
|
||||
}
|
||||
bar.size = 1 << num_leading_ones;
|
||||
}
|
||||
|
||||
return bar;
|
||||
}
|
||||
// Detect and configure PCI devices
|
||||
void pci_detect_devices()
|
||||
{
|
||||
// Scan all PCI buses and devices
|
||||
for (int bus = 0; bus < 256; bus++) {
|
||||
for (int device = 0; device < 32; device++) {
|
||||
for (int function = 0; function < 8; function++) {
|
||||
for (int bus = 0; bus < 256; bus++)
|
||||
{
|
||||
for (int device = 0; device < 32; device++)
|
||||
{
|
||||
for (int function = 0; function < 8; function++)
|
||||
{
|
||||
uint16_t vendor_id = pci_read(bus, device, function, 0x00);
|
||||
if (vendor_id != 0xFFFF) {
|
||||
if (vendor_id != 0xFFFF)
|
||||
{
|
||||
uint16_t device_id = pci_read(bus, device, function, 0x02);
|
||||
|
||||
// Lookup vendor and device ID in a database (e.g., PCI.ids)
|
||||
const char *device_name = lookup_device_name(vendor_id, device_id);
|
||||
|
||||
if (device_name != NULL)
|
||||
{
|
||||
// Device identified!
|
||||
printk("Found PCI device: %s (vendor: %04x, device: %04x)\n",
|
||||
device_name, vendor_id, device_id);
|
||||
|
||||
// ... (Optional) Further configuration based on device type
|
||||
}
|
||||
else
|
||||
{
|
||||
// Device not found in database, handle unknown device
|
||||
printk("Found unknown PCI device (vendor: %04x, device: %04x)\n",
|
||||
vendor_id, device_id);
|
||||
}
|
||||
|
||||
// Continue reading other registers, handling BARs, etc.
|
||||
uint8_t class_code = pci_read(bus, device, function, 0x0B);
|
||||
uint8_t subclass_code = pci_read(bus, device, function, 0x0A);
|
||||
uint8_t prog_if = pci_read(bus, device, function, 0x09);
|
||||
uint8_t header_type = pci_read(bus, device, function, 0x0E);
|
||||
uint8_t irq_line = pci_read(bus, device, function, 0x3C);
|
||||
uint32_t bar0 = pci_read(bus, device, function, 0x10);
|
||||
uint32_t bar1 = pci_read(bus, device, function, 0x14);
|
||||
uint32_t bar2 = pci_read(bus, device, function, 0x18);
|
||||
uint32_t bar3 = pci_read(bus, device, function, 0x1C);
|
||||
uint32_t bar4 = pci_read(bus, device, function, 0x20);
|
||||
uint32_t bar5 = pci_read(bus, device, function, 0x24);
|
||||
// Add any necessary device detection and configuration code here
|
||||
|
||||
// ... (rest of your code for handling BARs, etc.)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,30 +7,31 @@
|
||||
#include <unistd.h>
|
||||
|
||||
uint8_t *map_memory(uint32_t base_address, uint32_t size);
|
||||
|
||||
#define VGA_MEMORY_ADDRESS 0xA0000
|
||||
#define VGA_MEMORY_SIZE 0x60000
|
||||
#define VGA_MEMORY_BASE_ADDRESS 0xA0000
|
||||
|
||||
#define COLOR_GREEN 0x00FF00
|
||||
|
||||
uint8_t *vga_memory = (uint8_t *)VGA_MEMORY_ADDRESS;
|
||||
static uint16_t vga_width = VGA_WIDTH;
|
||||
static uint16_t vga_height = VGA_HEIGHT;
|
||||
static uint8_t vga_depth = VGA_DEPTH;
|
||||
uint8_t *framebuffer = (uint8_t *)vga_memory;
|
||||
uint8_t *map_memory(uint32_t base_address, uint32_t size);
|
||||
uint8_t read_vga_register(uint16_t register_address);
|
||||
int inb(uint16_t port);
|
||||
void outb(uint16_t port, uint8_t data);
|
||||
uint16_t width;
|
||||
uint32_t get_vga_memory_address();
|
||||
|
||||
// Set a specific pixel color (assuming RGB format)
|
||||
int x = 10;
|
||||
int y = 20;
|
||||
int color = 0x00FF00; // Green
|
||||
|
||||
framebuffer[y * width + x * 3] = (color & 0x00FF) >> 8; // Green value
|
||||
framebuffer[y * width + x * 3 + 1] = color & 0x00FF; // Blue value
|
||||
framebuffer[y * width + x * 3 + 2] = color >> 8; // Red value
|
||||
void some_function()
|
||||
{
|
||||
// ... other code ...
|
||||
uint8_t misc_output_value = read_vga_register(0x3C2); // Initialize here
|
||||
// ... use misc_output_value ...
|
||||
}
|
||||
|
||||
uint8_t misc_output_value = read_vga_register(0x3C2);
|
||||
|
||||
// Check bit 1 to determine text mode (0) or graphics mode (1)
|
||||
if (misc_output_value & 0x02)
|
||||
{
|
||||
@ -53,7 +54,7 @@ uint32_t get_vga_memory_address()
|
||||
void vga_init(void)
|
||||
{
|
||||
// Initialize VGA driver here
|
||||
vga_memory = (uint8_t *)map_memory(VGA_MEMORY_BASE_ADDRESS, VGA_MEMORY_SIZE);
|
||||
vga_memory = (uint8_t *)map_memory(VGA_MEMORY_ADDRESS, VGA_MEMORY_SIZE);
|
||||
|
||||
if (vga_memory == NULL)
|
||||
{
|
||||
@ -61,7 +62,7 @@ void vga_init(void)
|
||||
fprintf(stderr, "Failed to map VGA Memory\n");
|
||||
}
|
||||
|
||||
framebuffer = vga_memory;
|
||||
framebuffer = (uint8_t *)vga_memory;
|
||||
}
|
||||
|
||||
// Replace with actual functions to read VGA registers based on your system
|
||||
@ -71,28 +72,33 @@ uint8_t read_vga_register(uint16_t register_address)
|
||||
return value;
|
||||
}
|
||||
|
||||
int inb(uint16_t port) {
|
||||
// Implement port access logic (replace with system-specific function)
|
||||
int inb(uint16_t port)
|
||||
{
|
||||
uint8_t value;
|
||||
__asm volatile("inb %1, %0" : "=a"(value) : "d"(port));
|
||||
return value;
|
||||
}
|
||||
|
||||
void outb(uint16_t port, uint8_t data) {
|
||||
|
||||
void outb(uint16_t port, uint8_t data)
|
||||
{
|
||||
__asm volatile("outb %0, %1" : : "a"(data), "d"(port));
|
||||
}
|
||||
|
||||
void vga_set_resolution(uint16_t width, uint16_t height, uint8_t depth)
|
||||
{
|
||||
// Set VGA resolution here
|
||||
// Example (replace with actual register values and writes based on your system):
|
||||
|
||||
// Sequencer unlock
|
||||
outb(0x3C4, 0x01);
|
||||
outb(0x3C5, 0x01);
|
||||
|
||||
// Set specific CRTC registers for desired resolution and refresh rate
|
||||
// ... (write to specific VGA registers using outb)
|
||||
outb(0x3D4, 0x01); // Horizontal total
|
||||
outb(0x3D5, 0x5F); // ...
|
||||
// ... (set other CRTC registers)
|
||||
|
||||
// Graphics controller registers for memory access
|
||||
// ... (write to specific VGA registers using outb)
|
||||
outb(0x3CE, 0x05); // Graphics mode
|
||||
outb(0x3CF, 0x03);
|
||||
}
|
||||
|
||||
void vga_draw_pixel(uint16_t x, uint16_t y, uint8_t color)
|
||||
@ -100,6 +106,9 @@ void vga_draw_pixel(uint16_t x, uint16_t y, uint8_t color)
|
||||
// Draw a pixel on the screen at (x, y) with the given color
|
||||
uint32_t offset = y * vga_width + x;
|
||||
vga_memory[offset] = color;
|
||||
framebuffer[y * width + x * 3] = (color & 0x00FF) >> 8; // Green value
|
||||
framebuffer[y * width + x * 3 + 1] = color & 0x00FF; // Blue value
|
||||
framebuffer[y * width + x * 3 + 2] = color >> 8; // Red value
|
||||
}
|
||||
|
||||
void vga_clear_screen(uint8_t color)
|
||||
|
@ -3,14 +3,22 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
#define VGA_WIDTH 320 // Replace with the desired width
|
||||
#define VGA_HEIGHT 200 // Replace with the desired height
|
||||
#define VGA_DEPTH 8 // Replace with the desired color depth (e.g., 8 for 256 colors)
|
||||
#define VGA_MEMORY_ADDRESS 0xA0000 // Move to header file
|
||||
#define VGA_MEMORY_SIZE 0x60000 // Move to header file
|
||||
#define COLOR_GREEN 0x00FF00
|
||||
#define VGA_MAP_MEM_ERROR -1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint8_t depth;
|
||||
// Add other relevant settings like color palette, etc.
|
||||
} vga_display_settings_t;
|
||||
|
||||
void vga_init();
|
||||
void vga_set_resolution(uint16_t width, uint16_t height, uint8_t depth);
|
||||
void vga_draw_pixel(uint16_t x, uint16_t y, uint8_t color);
|
||||
void vga_clear_screen(uint8_t color);
|
||||
|
@ -0,0 +1,33 @@
|
||||
#include "acpi.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Function prototypes (implementations below)
|
||||
acpi_fadt_t *acpi_find_fadt();
|
||||
int acpi_validate_rsdt(acpi_fadt_t *fadt);
|
||||
|
||||
// ... (Add other ACPI functions for table parsing, resource access, etc.)
|
||||
|
||||
// Function to locate the FADT table
|
||||
acpi_fadt_t *acpi_find_fadt() {
|
||||
// Read the RSDP (Root System Description Pointer) signature and checksum
|
||||
// at the ACPI base address.
|
||||
acpi_fadt_t *rsdp = (acpi_fadt_t *)ACPI_BASE;
|
||||
if (rsdp->signature != *(uint32_t *)"RSDT" && rsdp->signature != *(uint32_t *)"XSDT") {
|
||||
return NULL; // Not a valid ACPI signature
|
||||
}
|
||||
if (rsdp->checksum != 0) {
|
||||
return NULL; // Invalid checksum
|
||||
}
|
||||
|
||||
// Check ACPI revision and use appropriate table address (RSDT or XSDT)
|
||||
return (acpi_fadt_t *)(rsdp->revision >= 2 ? rsdp->xsdt_addr : rsdp->rsdp_addr);
|
||||
}
|
||||
|
||||
// Function to validate the RSDT (or XSDT) table
|
||||
int acpi_validate_rsdt(acpi_fadt_t *fadt) {
|
||||
// Check table length and perform basic integrity checks
|
||||
// ...
|
||||
|
||||
return 0; // Assuming success, replace with actual validation logic
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
#ifndef ACPI_H
|
||||
#define ACPI_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// ACPI base address (replace with actual value)
|
||||
#define ACPI_BASE 0xE0000000
|
||||
|
||||
// ACPI Fixed ACPI Description Table (FADT) structure
|
||||
typedef struct
|
||||
{
|
||||
uint32_t signature; // Signature ("RSDT" or "XSDT")
|
||||
uint32_t length; // Length of the table
|
||||
uint8_t revision; // ACPI revision
|
||||
uint8_t checksum; // Checksum (should be zero)
|
||||
uint32_t oem_id[6]; // OEM ID string
|
||||
uint8_t oem_revision[8]; // OEM revision string
|
||||
uint32_t rsdp_addr; // Physical address of RSDT (for ACPI 1.0)
|
||||
uint32_t xsdt_addr; // Physical address of XSDT (for ACPI 2.0+)
|
||||
// ... other FADT fields (refer to ACPI specification)
|
||||
} __attribute__((packed)) acpi_fadt_t;
|
||||
|
||||
// ... (Add other ACPI table structure definitions as needed)
|
||||
|
||||
#endif /* ACPI_H */
|
36
src/kernel/ahci.c
Normal file
36
src/kernel/ahci.c
Normal file
@ -0,0 +1,36 @@
|
||||
#include "ahci.h"
|
||||
#include <stdint.h>
|
||||
|
||||
// Function prototypes (implementations below)
|
||||
uint32_t ahci_read_reg(uint32_t offset);
|
||||
void ahci_write_reg(uint32_t offset, uint32_t value);
|
||||
|
||||
// Function to send an AHCI command
|
||||
int ahci_send_command(uint32_t cmd_slot, ahci_cmd_t *cmd);
|
||||
|
||||
// ... (Add other AHCI functions for initialization, status checks, etc.)
|
||||
|
||||
// Helper function to read a 32-bit register
|
||||
uint32_t ahci_read_reg(uint32_t offset)
|
||||
{
|
||||
return *(volatile uint32_t *)(HBA_PORT_BASE + offset);
|
||||
}
|
||||
|
||||
// Helper function to write a 32-bit register
|
||||
void ahci_write_reg(uint32_t offset, uint32_t value)
|
||||
{
|
||||
*(volatile uint32_t *)(HBA_PORT_BASE + offset) = value;
|
||||
}
|
||||
|
||||
// Function to send an AHCI command
|
||||
int ahci_send_command(uint32_t cmd_slot, ahci_cmd_t *cmd)
|
||||
{
|
||||
// Implement logic to write the command list entry to appropriate registers
|
||||
// ...
|
||||
|
||||
// Wait for command completion (check relevant AHCI registers)
|
||||
// ...
|
||||
|
||||
// Return status based on completion and error checking
|
||||
// ...
|
||||
}
|
28
src/kernel/ahci.h
Normal file
28
src/kernel/ahci.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef AHCI_H
|
||||
#define AHCI_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Base address of AHCI registers (replace with actual value)
|
||||
#define HBA_PORT_BASE 0x10000000
|
||||
|
||||
// AHCI register offsets
|
||||
#define HBA_CAP 0x00 // Host Capabilities
|
||||
#define HBA_PI 0x0C // Port Interrupt
|
||||
#define HBA_ISR 0x20 // Interrupt Status
|
||||
#define HBA_IMR 0x30 // Interrupt Mask
|
||||
#define HBA_CMD 0x34 // Command List Head
|
||||
#define HBA_CMD_SLOT_0 0 // First command slot
|
||||
|
||||
// AHCI command list structure
|
||||
typedef struct
|
||||
{
|
||||
uint32_t cmd_sts; // FIS (Frame in flight)
|
||||
uint32_t prdt_addr; // Physical address of PRDT (SATA FIS)
|
||||
uint32_t prdt_length; // Length of PRDT in bytes
|
||||
uint32_t reserved[4]; // Reserved (set to 0)
|
||||
} __attribute__((packed)) ahci_cmd_t;
|
||||
|
||||
// ... (Add other AHCI register definitions and structures as needed)
|
||||
|
||||
#endif /* AHCI_H */
|
@ -1,7 +1,12 @@
|
||||
global LoadIDT
|
||||
global LoadIDT ; Declare function as global
|
||||
|
||||
_offset idt db 0
|
||||
|
||||
section .text ; Code section
|
||||
|
||||
%include "idt.h"
|
||||
|
||||
section .text
|
||||
LoadIDT:
|
||||
mov eax, [offset idt] ; Get the IDT address
|
||||
LIDT [eax + 8*0x33] ; Load only the timer interrupt entry (offset 8*0x33)
|
||||
RET
|
||||
mov eax, [esp + 4] ; Get IDT address (ensure offset idt is defined)
|
||||
LIDT [eax] ; Load timer interrupt entry (check for correct brackets)
|
||||
RET ; Return from function
|
@ -1,4 +1,5 @@
|
||||
#include "./types.h"
|
||||
#include <stdint.h>
|
||||
|
||||
void gdt_set_entry(gdt_entry_t *entry, uint32_t base, uint32_t limit,
|
||||
uint16_t flags)
|
||||
|
@ -1,7 +1,13 @@
|
||||
section .text
|
||||
global isr_handler
|
||||
global _isr_stub
|
||||
%include "isr.h"
|
||||
|
||||
section .data
|
||||
ISR_HAS_ERROR_CODE equ 1
|
||||
ISR_IS_HARDWARE_INTERRUPT equ 1
|
||||
|
||||
section .text
|
||||
global isr_stub
|
||||
|
||||
global _isr_stub
|
||||
_isr_stub:
|
||||
; Save context
|
||||
PUSHAD ; Preserve all general-purpose registers
|
||||
@ -10,41 +16,36 @@ _isr_stub:
|
||||
CLI
|
||||
|
||||
; Align stack to 16-byte boundary for performance (if needed)
|
||||
; AND ESP, 0xFFFFFFF0
|
||||
AND ESP, 0xFFFFFFF0
|
||||
|
||||
; Check if an error code is present (typically in EAX)
|
||||
; This is just a placeholder, actual implementation depends on your IDT setup
|
||||
CMP BYTE [ISR_HAS_ERROR_CODE], 1
|
||||
JNE no_error_code
|
||||
PUSH DWORD [ESP + 32] ; Push error code
|
||||
JMP done_pushing_error_code
|
||||
|
||||
no_error_code:
|
||||
PUSH DWORD 0 ; Push a dummy error code
|
||||
done_pushing_error_code:
|
||||
|
||||
; Push interrupt number onto the stack
|
||||
MOV EAX, [ESP + 36] ; Move interrupt number to EAX (accounting for the dummy/error code)
|
||||
PUSH EAX ; Interrupt number
|
||||
|
||||
; Call isr_handler
|
||||
PUSH DWORD 0
|
||||
MOV EAX, [ESP + 36]
|
||||
PUSH EAX
|
||||
; Call the C ISR handler
|
||||
CALL isr_handler
|
||||
|
||||
; Send EOI to PIC only if it's a hardware interrupt
|
||||
; This is just a placeholder, actual implementation depends on your IDT setup
|
||||
CMP BYTE [ISR_IS_HARDWARE_INTERRUPT], 1
|
||||
MOV AL, BYTE [ISR_IS_HARDWARE_INTERRUPT]
|
||||
CMP AL, 1
|
||||
|
||||
JNE skip_eoi
|
||||
MOV AL, 0x20
|
||||
OUT 0x20, AL
|
||||
|
||||
skip_eoi:
|
||||
|
||||
POPAD
|
||||
; Enable interrupts
|
||||
STI
|
||||
|
||||
; Clean up the stack
|
||||
ADD ESP, 8 ; Remove error code and interrupt number
|
||||
|
||||
; Restore context
|
||||
POPAD ; Restore all general-purpose registers
|
||||
ADD ESP, 16 ; Remove error code and interrupt number
|
||||
|
||||
; Return from interrupt
|
||||
IRETD
|
@ -1,4 +1,5 @@
|
||||
#include "isr.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void isr_handler(struct isr_regs regs) {
|
||||
switch(regs.int_no) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define ISR_H
|
||||
|
||||
#include "../include/types.h"
|
||||
#include <stdio.h>
|
||||
|
||||
enum ISR_Vector
|
||||
{
|
||||
@ -33,6 +34,7 @@ struct isr_regs
|
||||
esp_at_signal; // Pushed by the processor automatically
|
||||
};
|
||||
|
||||
void isr_handler(struct isr_regs regs);
|
||||
// Structure for storing register values during an ISR
|
||||
struct idt_regs
|
||||
{
|
||||
|
@ -2,15 +2,72 @@
|
||||
|
||||
static void *kernel_heap_start; // Start of the kernel heap
|
||||
static void *kernel_heap_end; // End of the kernel heap
|
||||
static struct memory_block *free_blocks = NULL;
|
||||
|
||||
void init_kernel_heap(void *start, void *end) {
|
||||
kernel_heap_start = start;
|
||||
kernel_heap_end = end;
|
||||
// Initialize the kernel heap here
|
||||
struct memory_block
|
||||
{
|
||||
size_t size;
|
||||
struct memory_block *next;
|
||||
};
|
||||
|
||||
void mark_as_used_kernel(void *ptr, size_t size)
|
||||
{
|
||||
// Since we're using a linked list, there's no specific action needed
|
||||
// to mark the block itself as used (it's already removed from the list).
|
||||
// However, you might want to perform additional bookkeeping for debugging
|
||||
// or memory analysis purposes.
|
||||
|
||||
// Example: Update some metadata associated with the allocated block
|
||||
// ((struct memory_block *)ptr)->used = true; // Assuming a 'used' flag
|
||||
|
||||
// This function can be potentially empty if you don't require specific
|
||||
// actions for marking a block as used in the kernel.
|
||||
}
|
||||
|
||||
void *kmalloc(size_t size) {
|
||||
if (kernel_heap_start == NULL || kernel_heap_end == NULL) {
|
||||
void mark_as_free_kernel(void *ptr)
|
||||
{
|
||||
// Cast the pointer to a memory_block structure
|
||||
struct memory_block *block = (struct memory_block *)ptr;
|
||||
|
||||
// Add the block to the beginning of the free block list
|
||||
block->next = free_blocks;
|
||||
free_blocks = block;
|
||||
}
|
||||
|
||||
void init_kernel_heap(void *start, void *end)
|
||||
{
|
||||
kernel_heap_start = start;
|
||||
kernel_heap_end = end;
|
||||
|
||||
free_blocks = (struct memory_block *)start;
|
||||
free_blocks->size = (size_t)(end - start);
|
||||
free_blocks->next = NULL;
|
||||
|
||||
// You'll need to replace this placeholder with logic to split the
|
||||
// initial block into smaller ones based on your requirements.
|
||||
}
|
||||
|
||||
void *find_free_kernel_block(size_t size)
|
||||
{
|
||||
// First-fit algorithm (can be optimized with different strategies)
|
||||
struct memory_block *current = free_blocks;
|
||||
while (current != NULL)
|
||||
{
|
||||
if (current->size >= size)
|
||||
{
|
||||
// Found a suitable block
|
||||
return current;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
// No suitable block found
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *kmalloc(size_t size)
|
||||
{
|
||||
if (kernel_heap_start == NULL || kernel_heap_end == NULL)
|
||||
{
|
||||
// Kernel heap not initialized, cannot allocate
|
||||
return NULL;
|
||||
}
|
||||
@ -20,11 +77,17 @@ void *kmalloc(size_t size) {
|
||||
size &= ~(sizeof(size_t) - 1);
|
||||
|
||||
// Search for a free block of sufficient size
|
||||
// This is a placeholder for the actual algorithm
|
||||
void *block = find_free_kernel_block(size);
|
||||
if (block != NULL) {
|
||||
if (block != NULL)
|
||||
{
|
||||
// Mark the block as used
|
||||
mark_as_used_kernel(block, size);
|
||||
|
||||
// This is a placeholder for splitting the block if necessary
|
||||
// You'll need to implement logic to potentially split the block
|
||||
// into a used part of size 'size' and a remaining free block
|
||||
// if the block size is significantly larger than 'size'.
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
@ -32,8 +95,10 @@ void *kmalloc(size_t size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void kfree(void *ptr) {
|
||||
if (ptr == NULL) {
|
||||
void kfree(void *ptr)
|
||||
{
|
||||
if (ptr == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,12 @@
|
||||
#ifndef KMALLOC_H
|
||||
#define KMALLOC_H
|
||||
#ifndef KMALLOC_H_ // Corrected guard macro
|
||||
#define KMALLOC_H_
|
||||
|
||||
#include <stddef.h> // For size_t
|
||||
|
||||
void *kmalloc(size_t size);
|
||||
void kfree(void *ptr);
|
||||
|
||||
#endif // KMALLOC_H
|
||||
void mark_as_used_kernel(void *ptr, size_t size);
|
||||
void mark_as_free_kernel(void *ptr);
|
||||
|
||||
#endif /* KMALLOC_H_ */
|
||||
|
@ -10,6 +10,20 @@ void init_heap(void *start, void *end)
|
||||
// Initialize the heap here
|
||||
}
|
||||
|
||||
void *find_free_block(size_t size) {
|
||||
// Implementation is similar to find_free_kernel_block
|
||||
struct memory_block *current = free_blocks;
|
||||
while (current != NULL) {
|
||||
if (current->size >= size) {
|
||||
return current;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
// No suitable block found
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *malloc(size_t size)
|
||||
{
|
||||
if (heap_start == NULL || heap_end == NULL)
|
||||
|
@ -6,4 +6,6 @@
|
||||
void *malloc(size_t size);
|
||||
void free(void *ptr);
|
||||
|
||||
void *find_free_block(size_t size);
|
||||
|
||||
#endif // MALLOC_H
|
||||
|
@ -1,3 +1,26 @@
|
||||
/*
|
||||
timer functions go here
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include "timer.h"
|
||||
|
||||
// Function to send a byte to an I/O port
|
||||
void outb(uint16_t port, uint8_t data) {
|
||||
__asm__ volatile ("outb %b, %w" : : "a" (data), "d" (port));
|
||||
}
|
||||
|
||||
// Function to initialize the timer with a desired frequency
|
||||
void init_timer(uint32_t frequency) {
|
||||
// Calculate the divisor based on the desired frequency
|
||||
uint16_t divisor = 1193180 / frequency;
|
||||
|
||||
// Send the mode and divisor byte to the timer control port
|
||||
outb(IOPORT_TIMER_CONTROL, TIMER_CTRL_SELECT_0 | TIMER_MODE_3 | TIMER_CTRL_BINARY);
|
||||
outb(IOPORT_TIMER_0, (uint8_t) (divisor & 0xFF));
|
||||
outb(IOPORT_TIMER_0, (uint8_t) ((divisor >> 8) & 0xFF));
|
||||
}
|
||||
|
||||
// Function to wait for a specified number of microseconds
|
||||
void wait_us(uint32_t microseconds) {
|
||||
uint32_t count = microseconds * 10; // Adjust for desired resolution (1 count = 100ns)
|
||||
while (count--) {
|
||||
// Implement a short delay loop here (e.g., empty loop or reading the PIT)
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,37 @@
|
||||
/*
|
||||
timer functions go here
|
||||
*/
|
||||
Here's a breakdown of how to create timer.c and timer.h compatible with the 8253 for x86 32-bit protected mode on 386/486 processors:
|
||||
|
||||
1. timer.h:
|
||||
|
||||
This header file will contain function prototypes, constants, and any data structures you might need for interacting with the 8253 timer. Here's an example:
|
||||
C
|
||||
|
||||
#ifndef TIMER_H
|
||||
#define TIMER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// I/O port addresses for the 8253
|
||||
#define IOPORT_TIMER_CONTROL 0x43
|
||||
#define IOPORT_TIMER_0 0x40
|
||||
|
||||
// Timer modes (refer to 8253 documentation for details)
|
||||
#define TIMER_MODE_0 0x00
|
||||
#define TIMER_MODE_1 0x40
|
||||
#define TIMER_MODE_2 0x80
|
||||
#define TIMER_MODE_3 0xC0
|
||||
|
||||
// Timer control bits (refer to 8253 documentation for details)
|
||||
#define TIMER_CTRL_SELECT_0 0x01
|
||||
#define TIMER_CTRL_SELECT_1 0x02
|
||||
#define TIMER_CTRL_SELECT_2 0x04
|
||||
#define TIMER_CTRL_MODE 0x30
|
||||
#define TIMER_CTRL_BINARY 0x00
|
||||
#define TIMER_CTRL_BCD 0x40
|
||||
#define TIMER_CTRL_ONE_SHOT 0x80
|
||||
#define TIMER_CTRL_CONTINOUS 0x00
|
||||
|
||||
// Function prototypes
|
||||
void init_timer(uint32_t frequency);
|
||||
void wait_us(uint32_t microseconds);
|
||||
|
||||
#endif /* TIMER_H */
|
||||
|
Loading…
Reference in New Issue
Block a user