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:
Gregory Kenneth Bowne 2024-03-30 00:49:36 -07:00
parent ad70cfd836
commit 528fb00d49
22 changed files with 650 additions and 189 deletions

BIN
.vscode/browse.vc.db vendored

Binary file not shown.

Binary file not shown.

View File

@ -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 ;

View 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

View File

@ -1,68 +1,169 @@
#include "pci.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
// PCI bus controller base address
#define PCI_BASE_ADDRESS 0x0000
// PCI bus controller data port
#define PCI_DATA_PORT 0x00
// PCI bus controller command port
#define PCI_COMMAND_PORT 0x01
// Initialize the PCI bus
void pci_init()
{
// Enable PCI bus master
pci_write(PCI_COMMAND_PORT, 0x04);
}
// 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++) {
uint16_t vendor_id = pci_read(bus, device, function, 0x00);
if (vendor_id != 0xFFFF) {
uint16_t device_id = pci_read(bus, device, function, 0x02);
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
}
}
}
}
}
// Read from a PCI device
uint8_t pci_read(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;
}
// Write to a PCI device
void pci_write(uint16_t port, uint8_t value)
{
// Write the specified value to the specified port
__asm__ volatile("outb %0, %1" : : "a"(value), "dN"(port));
}
#include "pci.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
// PCI bus controller base address
#define PCI_BASE_ADDRESS 0x0000
// PCI bus controller data port
#define PCI_DATA_PORT 0x00
// PCI bus controller command port
#define PCI_COMMAND_PORT 0x01
// Initialize the PCI bus
void pci_init()
{
// Enable PCI bus master
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++)
{
uint16_t vendor_id = pci_read(bus, device, function, 0x00);
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);
// ... (rest of your code for handling BARs, etc.)
}
}
}
}
}
// Read from a PCI device
uint8_t pci_read(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;
}
// Write to a PCI device
void pci_write(uint16_t port, uint8_t value)
{
// Write the specified value to the specified port
__asm__ volatile("outb %0, %1" : : "a"(value), "dN"(port));
}

View File

@ -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,38 +62,43 @@ 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
uint8_t read_vga_register(uint16_t register_address)
{
uint8_t value = inb(register_address); // Read from the specified port
return value;
uint8_t value = inb(register_address); // Read from the specified port
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)

View File

@ -3,14 +3,22 @@
#include <stdint.h>
typedef struct {
uint16_t width;
uint16_t height;
uint8_t depth;
// Add other relevant settings like color palette, etc.
#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);

View File

@ -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
}

View File

@ -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
View 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
View 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 */

View File

@ -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

View File

@ -1,18 +1,19 @@
#include "./types.h"
void gdt_set_entry(gdt_entry_t *entry, uint32_t base, uint32_t limit,
uint16_t flags)
{
entry->base = base;
entry->limit = limit;
entry->flags = flags;
}
void idt_set_entry(idt_entry_t *entry, uint32_t base, uint16_t selector,
uint16_t flags)
{
entry->base_low = base & 0xFFFF;
entry->base_high = (base >> 16) & 0xFFFF;
entry->selector = selector;
entry->flags = flags;
}
#include "./types.h"
#include <stdint.h>
void gdt_set_entry(gdt_entry_t *entry, uint32_t base, uint32_t limit,
uint16_t flags)
{
entry->base = base;
entry->limit = limit;
entry->flags = flags;
}
void idt_set_entry(idt_entry_t *entry, uint32_t base, uint16_t selector,
uint16_t flags)
{
entry->base_low = base & 0xFFFF;
entry->base_high = (base >> 16) & 0xFFFF;
entry->selector = selector;
entry->flags = flags;
}

View File

@ -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
JNE skip_eoi
MOV AL, 0x20
OUT 0x20, AL
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

View File

@ -1,4 +1,5 @@
#include "isr.h"
#include <stdio.h>
void isr_handler(struct isr_regs regs) {
switch(regs.int_no) {

View File

@ -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
{

View File

@ -1,43 +1,108 @@
#include "kmalloc.h"
static void *kernel_heap_start; // Start of the kernel heap
static void *kernel_heap_end; // End 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) {
// Kernel heap not initialized, cannot allocate
return NULL;
}
void mark_as_free_kernel(void *ptr)
{
// Cast the pointer to a memory_block structure
struct memory_block *block = (struct memory_block *)ptr;
// Align the size to the word size for efficiency
size += sizeof(size_t) - 1;
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) {
// Mark the block as used
mark_as_used_kernel(block, size);
return block;
}
// No suitable block found, out of memory
return NULL;
// Add the block to the beginning of the free block list
block->next = free_blocks;
free_blocks = block;
}
void kfree(void *ptr) {
if (ptr == NULL) {
return;
}
void init_kernel_heap(void *start, void *end)
{
kernel_heap_start = start;
kernel_heap_end = end;
// Mark the block as free
// This is a placeholder for the actual algorithm
mark_as_free_kernel(ptr);
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;
}
// Align the size to the word size for efficiency
size += sizeof(size_t) - 1;
size &= ~(sizeof(size_t) - 1);
// Search for a free block of sufficient size
void *block = find_free_kernel_block(size);
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;
}
// No suitable block found, out of memory
return NULL;
}
void kfree(void *ptr)
{
if (ptr == NULL)
{
return;
}
// Mark the block as free
// This is a placeholder for the actual algorithm
mark_as_free_kernel(ptr);
}

View File

@ -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_ */

View File

@ -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)

View File

@ -6,4 +6,6 @@
void *malloc(size_t size);
void free(void *ptr);
void *find_free_block(size_t size);
#endif // MALLOC_H

View File

@ -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)
}
}

View File

@ -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 */