TIME!!!
This commit is contained in:
parent
70bd58e5ec
commit
75c2759e5a
16
Makefile
16
Makefile
@ -1,25 +1,25 @@
|
||||
.PHONY: clean run run-iso build_dir
|
||||
.PHONY: clean run run-iso build-dir clean-build
|
||||
|
||||
TARGET_DIR = target/x86-unknown-bare_metal/release
|
||||
|
||||
build/mxrox.iso: build/kernel.elf build_dir
|
||||
build/mxrox.iso: build/kernel.elf build-dir
|
||||
cp 'grub.cfg' build/iso/boot/grub
|
||||
cp '$<' build/iso/boot
|
||||
grub-mkrescue -o '$@' build/iso
|
||||
|
||||
build/boot.o: boot.s build_dir
|
||||
build/boot.o: boot.s build-dir
|
||||
nasm -f elf32 '$<' -o '$@'
|
||||
|
||||
build/kernel.elf: linker.ld build/boot.o build/libkernel.a
|
||||
i686-elf-gcc -nostdlib -o $@ -T $^
|
||||
|
||||
build/libkernel.a: build_dir
|
||||
build/libkernel.a: build-dir
|
||||
rm -rf $(TARGET_DIR)
|
||||
rustup override set nightly
|
||||
cargo build --release
|
||||
cp $(TARGET_DIR)/libmxrox.a build/libkernel.a
|
||||
|
||||
build_dir:
|
||||
build-dir:
|
||||
mkdir -p build
|
||||
mkdir -p build/iso
|
||||
mkdir -p build/iso/boot
|
||||
@ -27,11 +27,13 @@ build_dir:
|
||||
|
||||
build: build/mxrox.iso
|
||||
|
||||
clean:
|
||||
cargo clean
|
||||
clean-build:
|
||||
rm -rf build
|
||||
mkdir build
|
||||
|
||||
clean: clean-build
|
||||
cargo clean
|
||||
|
||||
run-kernel: build/kernel.elf
|
||||
qemu-system-i386 -kernel '$<' -m 512M
|
||||
|
||||
|
@ -38,6 +38,7 @@ Internet resources where I found most information about OS dev
|
||||
- https://os.phil-opp.com/
|
||||
- https://wiki.osdev.org/Interrupts_Tutorial
|
||||
- https://wiki.osdev.org/Interrupt_Vector_Table
|
||||
- https://wiki.osdev.org/Inline_Assembly/Examples#I/O_access
|
||||
|
||||
### Contributing
|
||||
|
||||
|
54
boot.s
54
boot.s
@ -16,6 +16,60 @@ align 4
|
||||
dd MBFLAGS
|
||||
dd CHECKSUM
|
||||
|
||||
; %macro isr_err_stub 1
|
||||
; isr_stub_%+%1:
|
||||
; call exception_handler
|
||||
; iret
|
||||
; %endmacro
|
||||
|
||||
; %macro isr_no_err_stub 1
|
||||
; isr_stub_%+%1:
|
||||
; call exception_handler
|
||||
; iret
|
||||
; %endmacro
|
||||
|
||||
; extern exception_handler
|
||||
; isr_no_err_stub 0
|
||||
; isr_no_err_stub 1
|
||||
; isr_no_err_stub 2
|
||||
; isr_no_err_stub 3
|
||||
; isr_no_err_stub 4
|
||||
; isr_no_err_stub 5
|
||||
; isr_no_err_stub 6
|
||||
; isr_no_err_stub 7
|
||||
; isr_err_stub 8
|
||||
; isr_no_err_stub 9
|
||||
; isr_err_stub 10
|
||||
; isr_err_stub 11
|
||||
; isr_err_stub 12
|
||||
; isr_err_stub 13
|
||||
; isr_err_stub 14
|
||||
; isr_no_err_stub 15
|
||||
; isr_no_err_stub 16
|
||||
; isr_err_stub 17
|
||||
; isr_no_err_stub 18
|
||||
; isr_no_err_stub 19
|
||||
; isr_no_err_stub 20
|
||||
; isr_no_err_stub 21
|
||||
; isr_no_err_stub 22
|
||||
; isr_no_err_stub 23
|
||||
; isr_no_err_stub 24
|
||||
; isr_no_err_stub 25
|
||||
; isr_no_err_stub 26
|
||||
; isr_no_err_stub 27
|
||||
; isr_no_err_stub 28
|
||||
; isr_no_err_stub 29
|
||||
; isr_err_stub 30
|
||||
; isr_no_err_stub 31
|
||||
|
||||
; global isr_stub_table
|
||||
; isr_stub_table:
|
||||
; %assign i 0
|
||||
; %rep 32
|
||||
; dd isr_stub_%+i ; use DQ instead if targeting 64-bit
|
||||
; %assign i i+1
|
||||
; %endrep
|
||||
|
||||
; The multiboot standard does not define the value of the stack pointer register
|
||||
; (esp) and it is up to the kernel to provide a stack. This allocates room for a
|
||||
; small stack by creating a symbol at the bottom of it, then allocating 16384
|
||||
|
@ -4,6 +4,8 @@ use core::mem;
|
||||
use core::cell::UnsafeCell;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use super::terminal::log_info;
|
||||
|
||||
struct FreeListNode {
|
||||
size: usize,
|
||||
next: *mut FreeListNode,
|
||||
@ -122,4 +124,6 @@ pub fn init_heap(heap_start: usize, heap_size: usize) {
|
||||
unsafe {
|
||||
ALLOCATOR.init(heap_start, heap_size);
|
||||
}
|
||||
|
||||
log_info(&format!("Heap initialized on {heap_start} with size {heap_size}"));
|
||||
}
|
||||
|
205
src/kernel/interrupt.rs
Normal file
205
src/kernel/interrupt.rs
Normal file
@ -0,0 +1,205 @@
|
||||
use core::ptr::write_volatile;
|
||||
use core::arch::asm;
|
||||
|
||||
use crate::kernel::util::{cli, sti};
|
||||
|
||||
use super::show_error;
|
||||
use super::terminal::{log_error, log_info};
|
||||
use super::util::{inb, io_wait, outb};
|
||||
|
||||
const APIC_BASE: u32 = 0xFEE00000;
|
||||
const APIC_EOI: u32 = APIC_BASE + 0x0B0; // End Of Interrupt
|
||||
const APIC_SVR: u32 = APIC_BASE + 0x0F0; // Spurious Interrupt Vector Register
|
||||
|
||||
const IA32_APIC_BASE_MSR: u32 = 0x1B;
|
||||
const APIC_BASE_ENABLE: u64 = 1 << 11;
|
||||
|
||||
const PIC1_COMMAND: u16 = 0x0020;
|
||||
const PIC1_DATA: u16 = 0x0021;
|
||||
const PIC2_COMMAND: u16 = 0x00A0;
|
||||
const PIC2_DATA: u16 = 0x00A1;
|
||||
|
||||
const ICW1_ICW4: u8 = 0x01; /* Indicates that ICW4 will be present */
|
||||
const ICW1_SINGLE: u8 = 0x02; /* Single (cascade) mode */
|
||||
const ICW1_INTERVAL4: u8 = 0x04; /* Call address interval 4 (8) */
|
||||
const ICW1_LEVEL: u8 = 0x08; /* Level triggered (edge) mode */
|
||||
const ICW1_INIT: u8 = 0x10; /* Initialization - required! */
|
||||
|
||||
const ICW4_8086: u8 = 0x01; /* 8086/88 (MCS-80/85) mode */
|
||||
const ICW4_AUTO: u8 = 0x02; /* Auto (normal) EOI */
|
||||
const ICW4_BUF_SLAVE: u8 = 0x08; /* Buffered mode/slave */
|
||||
const ICW4_BUF_MASTER: u8 = 0x0C; /* Buffered mode/master */
|
||||
const ICW4_SFNM: u8 = 0x10; /* Special fully nested (not) */
|
||||
|
||||
const PIC_EOI: u8 = 0x20; /* End-of-interrupt command code */
|
||||
|
||||
const APIC_ENABLED: bool = false;
|
||||
|
||||
#[repr(C, packed)]
|
||||
struct IDTDescriptor {
|
||||
limit: u16,
|
||||
base: u32,
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Copy, Clone)]
|
||||
struct IDTEntry {
|
||||
offset_low: u16,
|
||||
selector: u16,
|
||||
ist: u8,
|
||||
type_attr: u8,
|
||||
offset_mid: u16,
|
||||
}
|
||||
|
||||
const IDT_MAX_DESCRIPTORS: usize = 256;
|
||||
static mut IDT: [IDTEntry; IDT_MAX_DESCRIPTORS] = [IDTEntry {
|
||||
offset_low: 0,
|
||||
selector: 0,
|
||||
ist: 0,
|
||||
type_attr: 0,
|
||||
offset_mid: 0,
|
||||
}; IDT_MAX_DESCRIPTORS];
|
||||
|
||||
// static mut VECTORS: [bool; 32] = [false; 32];
|
||||
static mut IDTR: IDTDescriptor = IDTDescriptor { limit: 0, base: 0 };
|
||||
|
||||
// extern "C" {
|
||||
// static ist_stub_table: [u32; 32];
|
||||
// }
|
||||
|
||||
pub unsafe fn idt_set_descriptor(vector: u8, handler: u32, type_attr: u8) {
|
||||
IDT[vector as usize] = IDTEntry {
|
||||
offset_low: handler as u16,
|
||||
selector: 0x08, // Kernel code segment
|
||||
ist: type_attr,
|
||||
type_attr,
|
||||
offset_mid: (handler >> 16) as u16
|
||||
};
|
||||
}
|
||||
|
||||
pub unsafe fn load_idt() {
|
||||
IDTR.base = &IDT as *const _ as u32;
|
||||
IDTR.limit = (size_of::<IDTEntry>() * IDT_MAX_DESCRIPTORS - 1) as u16;
|
||||
|
||||
// for vector in 0..32 {
|
||||
// idt_set_descriptor(vector, ist_stub_table[vector as usize], 0x8E);
|
||||
// VECTORS[vector as usize] = true;
|
||||
// }
|
||||
|
||||
asm!("lidt [{}]", in(reg) &IDTR);
|
||||
asm!("sti");
|
||||
}
|
||||
|
||||
unsafe fn is_apic_available() -> bool {
|
||||
if !APIC_ENABLED {
|
||||
return false;
|
||||
}
|
||||
|
||||
let edx: u32;
|
||||
|
||||
asm!(
|
||||
"cpuid",
|
||||
inout("eax") 1 => _,
|
||||
lateout("edx") edx
|
||||
);
|
||||
|
||||
(edx & (1 << 9)) != 0
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn exception_handler() {
|
||||
log_error("Unknown error");
|
||||
asm!("cli; hlt");
|
||||
loop {}
|
||||
}
|
||||
|
||||
unsafe fn send_apic_eoi() {
|
||||
write_volatile(APIC_EOI as *mut u32, 0);
|
||||
}
|
||||
|
||||
unsafe fn send_pic_eoi(irq: usize) {
|
||||
if irq >= 8 {
|
||||
write_volatile(PIC2_COMMAND as *mut u8, PIC_EOI);
|
||||
}
|
||||
write_volatile(PIC1_COMMAND as *mut u8, PIC_EOI);
|
||||
}
|
||||
|
||||
pub unsafe fn init_pic() {
|
||||
let offset1 = 0x20;
|
||||
let offset2 = 0x28;
|
||||
|
||||
let a1 = inb(PIC1_DATA); // save masks
|
||||
let a2 = inb(PIC2_DATA);
|
||||
|
||||
outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); // starts the initialization sequence (in cascade mode)
|
||||
io_wait();
|
||||
outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4);
|
||||
io_wait();
|
||||
outb(PIC1_DATA, offset1); // ICW2: Master PIC vector offset
|
||||
io_wait();
|
||||
outb(PIC2_DATA, offset2); // ICW2: Slave PIC vector offset
|
||||
io_wait();
|
||||
outb(PIC1_DATA, 4); // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100)
|
||||
io_wait();
|
||||
outb(PIC2_DATA, 2); // ICW3: tell Slave PIC its cascade identity (0000 0010)
|
||||
io_wait();
|
||||
|
||||
outb(PIC1_DATA, ICW4_8086); // ICW4: have the PICs use 8086 mode (and not 8080 mode)
|
||||
io_wait();
|
||||
outb(PIC2_DATA, ICW4_8086);
|
||||
io_wait();
|
||||
|
||||
outb(PIC1_DATA, a1); // restore saved masks.
|
||||
outb(PIC2_DATA, a2);
|
||||
}
|
||||
|
||||
pub unsafe fn disable_pic() {
|
||||
outb(PIC1_DATA, 0xff);
|
||||
outb(PIC2_DATA, 0xff);
|
||||
}
|
||||
|
||||
pub fn send_eoi(irq: usize) {
|
||||
unsafe {
|
||||
if is_apic_available() {
|
||||
send_apic_eoi()
|
||||
} else {
|
||||
send_pic_eoi(irq)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn read_msr(msr: u32) -> u64 {
|
||||
let low: u32;
|
||||
let high: u32;
|
||||
asm!("rdmsr", in("ecx") msr, out("eax") low, out("edx") high);
|
||||
((high as u64) << 32) | (low as u64)
|
||||
}
|
||||
|
||||
unsafe fn write_msr(msr: u32, value: u64) {
|
||||
let low = value as u32;
|
||||
let high = (value >> 32) as u32;
|
||||
asm!("wrmsr", in("ecx") msr, in("eax") low, in("edx") high);
|
||||
}
|
||||
|
||||
pub unsafe fn init_apic() {
|
||||
let apic_base = read_msr(IA32_APIC_BASE_MSR) | APIC_BASE_ENABLE;
|
||||
write_msr(IA32_APIC_BASE_MSR, apic_base);
|
||||
let svr_value: u32 = 0x1FF;
|
||||
write_volatile(APIC_SVR as *mut u32, svr_value);
|
||||
write_volatile(APIC_EOI as *mut u32, 0);
|
||||
}
|
||||
|
||||
pub fn init_interrupts() {
|
||||
unsafe {
|
||||
init_pic();
|
||||
log_info("PIC initialized");
|
||||
|
||||
if is_apic_available() {
|
||||
disable_pic();
|
||||
log_info("Disable PIC");
|
||||
init_apic();
|
||||
log_info("APIC initialized");
|
||||
}
|
||||
|
||||
load_idt();
|
||||
}
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
use core::ptr::write_volatile;
|
||||
use core::arch::asm;
|
||||
|
||||
const APIC_BASE: u32 = 0xFEE00000;
|
||||
|
||||
const APIC_EOI: u32 = APIC_BASE + 0x0B0;
|
||||
const APIC_SVR: u32 = APIC_BASE + 0x0F0;
|
||||
|
||||
const PIC1_CMD: u16 = 0x20;
|
||||
const PIC1_DATA: u16 = 0x21;
|
||||
const PIC2_CMD: u16 = 0xA0;
|
||||
const PIC2_DATA: u16 = 0xA1;
|
||||
|
||||
const ICW1_INIT: u8 = 0x10;
|
||||
const ICW1_ICW4: u8 = 0x01;
|
||||
const ICW4_8086: u8 = 0x01;
|
||||
|
||||
const PIC_EOI: u8 = 0x20; /* End-of-interrupt command code */
|
||||
|
||||
const APIC_ENABLED: bool = false;
|
||||
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct IdtEntry {
|
||||
offset_low: u16,
|
||||
selector: u16,
|
||||
ist: u8,
|
||||
type_attr: u8,
|
||||
offset_middle: u16
|
||||
}
|
||||
|
||||
pub const IDT_SIZE: usize = 256;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Idt {
|
||||
entries: [IdtEntry; IDT_SIZE],
|
||||
}
|
||||
|
||||
static mut IDT: Idt = Idt {
|
||||
entries: [IdtEntry {
|
||||
offset_low: 0,
|
||||
selector: 0,
|
||||
ist: 0,
|
||||
type_attr: 0,
|
||||
offset_middle: 0,
|
||||
}; IDT_SIZE],
|
||||
};
|
||||
|
||||
unsafe fn is_apic_available() -> bool {
|
||||
if !APIC_ENABLED {
|
||||
return false;
|
||||
}
|
||||
|
||||
let edx: u32;
|
||||
|
||||
asm!(
|
||||
"cpuid",
|
||||
inout("eax") 1 => _,
|
||||
lateout("edx") edx
|
||||
);
|
||||
|
||||
(edx & (1 << 9)) != 0
|
||||
}
|
||||
|
||||
unsafe fn init_idt() {
|
||||
asm!("lidt [{}]", "sti", in(reg) &IDT as *const _);
|
||||
}
|
||||
|
||||
unsafe fn init_apic() {
|
||||
write_volatile(APIC_SVR as *mut u32, 0x100 | 0x1);
|
||||
}
|
||||
|
||||
unsafe fn send_apic_eoi() {
|
||||
write_volatile(APIC_EOI as *mut u32, 0);
|
||||
}
|
||||
|
||||
unsafe fn send_pic_eoi(irq: usize) {
|
||||
if irq >= 8 { write_volatile(PIC2_CMD as *mut u8, PIC_EOI); }
|
||||
write_volatile(PIC1_CMD as *mut u8, PIC_EOI);
|
||||
}
|
||||
|
||||
unsafe fn init_pic() {
|
||||
write_volatile(PIC1_CMD as *mut u8, ICW1_INIT | ICW1_ICW4);
|
||||
write_volatile(PIC2_CMD as *mut u8, ICW1_INIT | ICW1_ICW4);
|
||||
|
||||
write_volatile(PIC1_DATA as *mut u8, 0x20);
|
||||
write_volatile(PIC2_DATA as *mut u8, 0x28);
|
||||
|
||||
write_volatile(PIC1_DATA as *mut u8, 4);
|
||||
write_volatile(PIC2_DATA as *mut u8, 2);
|
||||
|
||||
write_volatile(PIC1_DATA as *mut u8, ICW4_8086);
|
||||
write_volatile(PIC2_DATA as *mut u8, ICW4_8086);
|
||||
|
||||
write_volatile(PIC1_DATA as *mut u8, 0xFB);
|
||||
write_volatile(PIC2_DATA as *mut u8, 0xFF);
|
||||
}
|
||||
|
||||
pub fn send_eoi(irq: usize) {
|
||||
unsafe {
|
||||
if is_apic_available() {
|
||||
send_apic_eoi()
|
||||
} else {
|
||||
send_pic_eoi(irq)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// all interrupt vectors you can find here: https://wiki.osdev.org/Interrupt_Vector_Table
|
||||
pub fn register_idt(handler: u32, int_vec: u8) {
|
||||
unsafe {
|
||||
let entry = &mut IDT.entries[int_vec as usize];
|
||||
|
||||
entry.offset_low = handler as u16;
|
||||
entry.selector = 0x08;
|
||||
entry.ist = 0;
|
||||
entry.type_attr = 0x8E;
|
||||
entry.offset_middle = (handler >> 16) as u16;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_interrupts() {
|
||||
unsafe {
|
||||
init_idt();
|
||||
if is_apic_available() {
|
||||
init_apic();
|
||||
} else {
|
||||
init_pic();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +1,35 @@
|
||||
use irq::init_interrupts;
|
||||
use interrupt::init_interrupts;
|
||||
use heap::init_heap;
|
||||
use pit::init_pit;
|
||||
use time::init_pit;
|
||||
use ps2::init_ps2;
|
||||
use terminal::{log_info, println};
|
||||
use vga::{
|
||||
fill_with_color,
|
||||
put_string,
|
||||
put_string_by_index,
|
||||
VGA_COLOR_BLACK,
|
||||
VGA_COLOR_LIGHT_MAGENTA,
|
||||
VGA_COLOR_RED
|
||||
};
|
||||
|
||||
mod vga;
|
||||
mod ps2;
|
||||
mod irq;
|
||||
mod pit;
|
||||
mod interrupt;
|
||||
mod time;
|
||||
mod heap;
|
||||
mod util;
|
||||
mod terminal;
|
||||
|
||||
pub fn show_error(message: &str) {
|
||||
fill_with_color(VGA_COLOR_BLACK);
|
||||
put_string_by_index(0, message, VGA_COLOR_BLACK, VGA_COLOR_RED);
|
||||
}
|
||||
|
||||
pub fn init_kernel() {
|
||||
init_heap(16400, 16384);
|
||||
init_pit();
|
||||
init_ps2();
|
||||
init_interrupts();
|
||||
|
||||
fill_with_color(VGA_COLOR_BLACK);
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub fn init_kernel() {
|
||||
init_heap(0x200000, 1048576);
|
||||
init_interrupts();
|
||||
init_pit();
|
||||
// init_ps2();
|
||||
|
||||
loop {}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
use core::{ptr::write_volatile, sync::atomic::{AtomicUsize, Ordering}};
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use spin::RwLock;
|
||||
use core::sync::atomic::AtomicBool;
|
||||
|
||||
use super::irq::{register_idt, send_eoi};
|
||||
|
||||
const DIVISOR: u16 = ((1193182u32 + 500u32) / 1000) as u16;
|
||||
|
||||
static TIMERS: RwLock<Vec<Arc<AtomicUsize>>> = RwLock::new(Vec::new());
|
||||
|
||||
extern "C" fn pit_handler() {
|
||||
for t in TIMERS.read().iter() {
|
||||
t.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
send_eoi(0);
|
||||
}
|
||||
|
||||
pub fn init_pit() {
|
||||
unsafe {
|
||||
write_volatile(0x43 as *mut u8, 0x36);
|
||||
write_volatile(0x40 as *mut u8, (DIVISOR & 0xFF) as u8);
|
||||
write_volatile(0x40 as *mut u8, ((DIVISOR >> 8) & 0xFF) as u8);
|
||||
}
|
||||
register_idt(pit_handler as u32, 0x08);
|
||||
}
|
||||
|
||||
pub fn sleep(millis: usize) {
|
||||
let atomic = Arc::new(AtomicUsize::new(0));
|
||||
|
||||
TIMERS.write().push(atomic.clone());
|
||||
|
||||
while atomic.load(Ordering::SeqCst) < millis {
|
||||
core::hint::spin_loop(); // Уменьшаем нагрузку на CPU
|
||||
}
|
||||
|
||||
TIMERS.write().retain(|t| !Arc::ptr_eq(t, &atomic));
|
||||
}
|
@ -1,10 +1,8 @@
|
||||
use core::ptr::{read_volatile, write_volatile};
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use crate::kernel::irq::register_idt;
|
||||
use crate::kernel::interrupt::idt_set_descriptor;
|
||||
|
||||
use super::{irq::send_eoi, pit::sleep};
|
||||
use super::{interrupt::send_eoi, terminal::log_info, time::sleep, util::*};
|
||||
|
||||
const DATA_PORT: u16 = 0x60;
|
||||
const STATUS_PORT: u16 = 0x64;
|
||||
@ -16,23 +14,24 @@ static mut DEVICE1_TYPE: Vec<u8> = Vec::new();
|
||||
static mut DEVICE2_TYPE: Vec<u8> = Vec::new();
|
||||
|
||||
unsafe fn send_data(data: u8) {
|
||||
write_volatile(DATA_PORT as *mut u8, data)
|
||||
outb(DATA_PORT, data)
|
||||
}
|
||||
|
||||
unsafe fn read_data() -> u8 {
|
||||
read_volatile(DATA_PORT as *mut u8)
|
||||
inb(DATA_PORT)
|
||||
}
|
||||
|
||||
unsafe fn send_command(command: u8) {
|
||||
write_volatile(STATUS_PORT as *mut u8, command)
|
||||
outb(STATUS_PORT, command)
|
||||
}
|
||||
|
||||
unsafe fn read_status() -> u8 {
|
||||
read_volatile(STATUS_PORT as *mut u8)
|
||||
inb(STATUS_PORT)
|
||||
}
|
||||
|
||||
unsafe fn on_device_1_irq() {
|
||||
let data = read_data();
|
||||
log_info(&format!("[PS/2] device 1 data: 0x{:X}", data));
|
||||
send_eoi(DEVICE1_IRQ);
|
||||
}
|
||||
|
||||
@ -86,28 +85,43 @@ unsafe fn send_identify() -> Vec<u8> {
|
||||
}
|
||||
|
||||
unsafe fn is_ps2_2_device_enabled() -> bool {
|
||||
true
|
||||
false
|
||||
}
|
||||
|
||||
pub fn init_ps2() {
|
||||
unsafe {
|
||||
send_command(0xAD); // disable device 1
|
||||
|
||||
log_info("[PS/2] Disabled device 1");
|
||||
|
||||
if is_ps2_2_device_enabled() {
|
||||
DEVICE2_TYPE = send_identify();
|
||||
// DEVICE2_TYPE = send_identify();
|
||||
log_info("[PS/2] Inspected device 2 identify");
|
||||
send_command(0xA7); // disable device 2
|
||||
log_info("[PS/2] Disabled device 2");
|
||||
}
|
||||
|
||||
read_data(); // flush device data
|
||||
send_command(0x60); // set CCB command
|
||||
send_command(0x000000u8); // clear CCB
|
||||
|
||||
log_info("[PS/2] Flush device data and clear CCB");
|
||||
|
||||
send_command(0xAE); // enable device 1
|
||||
log_info("[PS/2] Enable device 1");
|
||||
// DEVICE1_TYPE = send_identify();
|
||||
log_info("[PS/2] Inspected device 1 identify");
|
||||
|
||||
if is_ps2_2_device_enabled() {
|
||||
DEVICE1_TYPE = send_identify();
|
||||
send_command(0xA8); // enable device 2
|
||||
}
|
||||
log_info("[PS/2] Disable device 2");
|
||||
}
|
||||
|
||||
register_idt(on_device_1_irq as u32, 0x09); // bind handler for device 1
|
||||
register_idt(on_device_2_irq as u32, 0x74); // bind handler for device 2
|
||||
idt_set_descriptor(0x21, on_device_1_irq as u32, 0x09); // bind handler for device 1
|
||||
log_info("[PS/2] Bind handler for device 1");
|
||||
if is_ps2_2_device_enabled() {
|
||||
idt_set_descriptor(0x8B, on_device_2_irq as u32, 0x74); // bind handler for device 2
|
||||
log_info("[PS/2] Bind handler for device 2");
|
||||
}
|
||||
}
|
||||
}
|
48
src/kernel/terminal.rs
Normal file
48
src/kernel/terminal.rs
Normal file
@ -0,0 +1,48 @@
|
||||
use alloc::{string::{String, ToString}, vec::Vec};
|
||||
use spin::RwLock;
|
||||
|
||||
use super::vga::{put_string, VGA_COLOR_BLACK, VGA_COLOR_WHITE, VGA_HEIGHT, VGA_WIDTH};
|
||||
use super::time::get_time_millis;
|
||||
|
||||
static TEXT: RwLock<String> = RwLock::new(String::new());
|
||||
|
||||
pub fn update() {
|
||||
let text = TEXT.read().to_string();
|
||||
let mut lines: Vec<String> = Vec::new();
|
||||
|
||||
for line in text.as_str().lines() {
|
||||
let chars: Vec<char> = line.chars().collect();
|
||||
lines.append(&mut chars.chunks(VGA_WIDTH)
|
||||
.map(|chunk| chunk.iter().collect())
|
||||
.collect::<Vec<String>>())
|
||||
}
|
||||
|
||||
while lines.len() > VGA_HEIGHT {
|
||||
lines.remove(0);
|
||||
}
|
||||
|
||||
for y in 0..VGA_HEIGHT {
|
||||
if let Some(line) = lines.get(y) {
|
||||
put_string(0, y, &(line.to_string()+&" ".repeat(VGA_WIDTH-line.len())), VGA_COLOR_BLACK, VGA_COLOR_WHITE);
|
||||
} else {
|
||||
put_string(0, y, &" ".repeat(VGA_WIDTH), VGA_COLOR_BLACK, VGA_COLOR_BLACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(text: &str) {
|
||||
TEXT.write().push_str(text);
|
||||
update();
|
||||
}
|
||||
|
||||
pub fn println(text: &str) {
|
||||
print(&format!("{text}\n"))
|
||||
}
|
||||
|
||||
pub fn log_info(text: &str) {
|
||||
print(&format!("[{}] [INFO] {text}\n", get_time_millis()))
|
||||
}
|
||||
|
||||
pub fn log_error(text: &str) {
|
||||
print(&format!("[{}] [FATAL] {text}\n", get_time_millis()))
|
||||
}
|
65
src/kernel/time.rs
Normal file
65
src/kernel/time.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use core::{ptr::write_volatile, sync::atomic::{AtomicUsize, Ordering}};
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use spin::RwLock;
|
||||
|
||||
use super::{interrupt::{idt_set_descriptor, load_idt, send_eoi}, terminal::log_info, util::{cli, io_wait, outb, sti}};
|
||||
|
||||
const DIVISOR: u16 = ((1193182u32 + 500u32) / 100) as u16;
|
||||
|
||||
static TIMERS: RwLock<Vec<Arc<AtomicUsize>>> = RwLock::new(Vec::new());
|
||||
static TIME_MILLIS: AtomicUsize = AtomicUsize::new(0);
|
||||
static TIME: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
extern "C" fn pit_handler() {
|
||||
for t in TIMERS.read().iter() {
|
||||
t.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
if TIME_MILLIS.fetch_add(1, Ordering::SeqCst) >= 999 {
|
||||
TIME_MILLIS.store(0, Ordering::SeqCst);
|
||||
TIME.fetch_add(1, Ordering::SeqCst);
|
||||
log_info("proshlo second");
|
||||
}
|
||||
|
||||
send_eoi(0);
|
||||
}
|
||||
|
||||
pub fn get_time_seconds() -> usize {
|
||||
TIME.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
pub fn get_time_millis() -> usize {
|
||||
TIME.load(Ordering::SeqCst) * 1000 + TIME_MILLIS.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
pub fn init_pit() {
|
||||
log_info("PIT initialization");
|
||||
|
||||
unsafe {
|
||||
outb(0x43, 0x36);
|
||||
cli();
|
||||
outb(0x40, (DIVISOR & 0xFF) as u8);
|
||||
outb(0x40, ((DIVISOR & 0xFF) >> 8) as u8);
|
||||
|
||||
log_info("PIT initialized");
|
||||
log_info(&format!("{}", pit_handler as u32));
|
||||
|
||||
idt_set_descriptor(0x20, pit_handler as u32, 0x8E);
|
||||
log_info("PIT registered");
|
||||
load_idt();
|
||||
|
||||
log_info("PIT loaded idt");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sleep(millis: usize) {
|
||||
let atomic = Arc::new(AtomicUsize::new(0));
|
||||
|
||||
TIMERS.write().push(atomic.clone());
|
||||
|
||||
while atomic.load(Ordering::SeqCst) < millis {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
TIMERS.write().retain(|t| !Arc::ptr_eq(t, &atomic));
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
use core::arch::asm;
|
||||
|
||||
pub unsafe fn outb(port: u16, value: u8) {
|
||||
asm!("out dx, al", in("dx") port, in("al") value, options(nostack, preserves_flags));
|
||||
}
|
||||
|
||||
pub unsafe fn inb(port: u16) -> u8 {
|
||||
let result: u8;
|
||||
asm!("inb %dx, %al", in("dx") port, out("al") result, options(nostack, preserves_flags, att_syntax));
|
||||
result
|
||||
}
|
||||
|
||||
pub unsafe fn nop() {
|
||||
asm!("nop", options(nostack, preserves_flags));
|
||||
}
|
||||
|
||||
pub unsafe fn io_wait() {
|
||||
outb(0x80, 0);
|
||||
}
|
||||
|
||||
pub unsafe fn sti() {
|
||||
asm!("sti");
|
||||
}
|
||||
|
||||
pub unsafe fn cli() {
|
||||
asm!("cli");
|
||||
}
|
Loading…
Reference in New Issue
Block a user