feature: x86_64 timer and sleep
This commit is contained in:
@@ -94,7 +94,9 @@ extern "C" fn __x86_64_bsp_main(mb_checksum: u32, mb_info_ptr: u32) -> ! {
|
||||
virt_base: virt,
|
||||
});
|
||||
unsafe {
|
||||
x86_64::COM1.init_irqs();
|
||||
x86_64::COM1.init_irqs().unwrap();
|
||||
x86_64::local_timer().init_irqs().unwrap();
|
||||
x86_64::local_timer().enable().unwrap();
|
||||
}
|
||||
font::init();
|
||||
debug::set_display(&x86_64::DISPLAY);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::arch::platform::ForkFrame;
|
||||
use crate::mem::{
|
||||
self,
|
||||
phys::{self, PageUsage},
|
||||
};
|
||||
use crate::arch::platform::ForkFrame;
|
||||
use core::mem::size_of;
|
||||
use core::arch::global_asm;
|
||||
use core::mem::size_of;
|
||||
|
||||
struct Stack {
|
||||
bp: usize,
|
||||
@@ -66,7 +66,7 @@ impl Context {
|
||||
Self {
|
||||
k_sp: stack.sp,
|
||||
stack_base: stack.bp,
|
||||
stack_page_count: 8
|
||||
stack_page_count: 8,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,13 @@ impl Context {
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: may clobber an already active context
|
||||
pub unsafe fn setup_signal_entry(&mut self, entry: usize, arg: usize, cr3: usize, ustack: usize) {
|
||||
pub unsafe fn setup_signal_entry(
|
||||
&mut self,
|
||||
entry: usize,
|
||||
arg: usize,
|
||||
cr3: usize,
|
||||
ustack: usize,
|
||||
) {
|
||||
let cr3 = cr3 & 0xFFFFFFFF;
|
||||
let mut stack = Stack::from_base_size(self.stack_base, self.stack_page_count);
|
||||
let stack_top = stack.sp;
|
||||
@@ -98,24 +104,24 @@ impl Context {
|
||||
stack.push(frame.saved_rip);
|
||||
stack.push(frame.saved_rsp);
|
||||
|
||||
stack.push(frame.x[6]); // rax
|
||||
stack.push(frame.x[5]); // r9
|
||||
stack.push(frame.x[4]); // r8
|
||||
stack.push(frame.x[3]); // r10
|
||||
stack.push(frame.x[2]); // rdx
|
||||
stack.push(frame.x[1]); // rsi
|
||||
stack.push(frame.x[0]); // rdi
|
||||
stack.push(frame.x[6]); // rax
|
||||
stack.push(frame.x[5]); // r9
|
||||
stack.push(frame.x[4]); // r8
|
||||
stack.push(frame.x[3]); // r10
|
||||
stack.push(frame.x[2]); // rdx
|
||||
stack.push(frame.x[1]); // rsi
|
||||
stack.push(frame.x[0]); // rdi
|
||||
|
||||
// Setup common
|
||||
stack.push(__x86_64_ctx_enter_from_fork as usize); // return address
|
||||
stack.push(stack_top); // gs_base
|
||||
stack.push(__x86_64_ctx_enter_from_fork as usize); // return address
|
||||
stack.push(stack_top); // gs_base
|
||||
stack.push(cr3);
|
||||
stack.push(frame.x[9]); // r15
|
||||
stack.push(frame.x[10]); // r14
|
||||
stack.push(frame.x[11]); // r13
|
||||
stack.push(frame.x[12]); // r12
|
||||
stack.push(frame.x[7]); // rbx
|
||||
stack.push(frame.x[8]); // rbp
|
||||
stack.push(frame.x[9]); // r15
|
||||
stack.push(frame.x[10]); // r14
|
||||
stack.push(frame.x[11]); // r13
|
||||
stack.push(frame.x[12]); // r12
|
||||
stack.push(frame.x[7]); // rbx
|
||||
stack.push(frame.x[8]); // rbp
|
||||
|
||||
Self {
|
||||
k_sp: stack.sp,
|
||||
@@ -159,20 +165,20 @@ impl Stack {
|
||||
pub unsafe fn from_base_size(bp: usize, page_count: usize) -> Stack {
|
||||
Stack {
|
||||
bp,
|
||||
sp: bp + page_count * mem::PAGE_SIZE
|
||||
sp: bp + page_count * mem::PAGE_SIZE,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_common(&mut self, entry: usize, cr3: usize, tss_rsp0: usize) {
|
||||
self.push(entry); // return address
|
||||
self.push(tss_rsp0); // gs_base
|
||||
self.push(entry); // return address
|
||||
self.push(tss_rsp0); // gs_base
|
||||
self.push(cr3);
|
||||
self.push(0); // r15
|
||||
self.push(0); // r14
|
||||
self.push(0); // r13
|
||||
self.push(0); // r12
|
||||
self.push(0); // rbx
|
||||
self.push(0); // rbp
|
||||
self.push(0); // r15
|
||||
self.push(0); // r14
|
||||
self.push(0); // r13
|
||||
self.push(0); // r12
|
||||
self.push(0); // rbx
|
||||
self.push(0); // rbp
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: usize) {
|
||||
|
||||
@@ -70,11 +70,7 @@ impl IntController for I8259 {
|
||||
irq: Self::IrqNumber,
|
||||
handler: &'static (dyn IntSource + Sync),
|
||||
) -> Result<(), Errno> {
|
||||
if irq.0 == 0 {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
let index = (irq.0 - 1) as usize;
|
||||
let index = irq.0 as usize;
|
||||
let mut lock = self.table.lock();
|
||||
if lock[index].is_some() {
|
||||
return Err(Errno::AlreadyExists);
|
||||
@@ -98,7 +94,6 @@ impl IntController for I8259 {
|
||||
|
||||
fn handle_pending_irqs<'irq_context>(&'irq_context self, ic: &IrqContext<'irq_context>) {
|
||||
let irq_number = ic.token();
|
||||
assert!(irq_number > 0);
|
||||
|
||||
// Clear irq
|
||||
if irq_number > 8 {
|
||||
@@ -108,7 +103,7 @@ impl IntController for I8259 {
|
||||
|
||||
{
|
||||
let table = self.table.lock();
|
||||
match table[irq_number - 1] {
|
||||
match table[irq_number] {
|
||||
None => panic!("No handler registered for irq{}", irq_number),
|
||||
Some(handler) => {
|
||||
drop(table);
|
||||
|
||||
@@ -47,51 +47,52 @@ __x86_64_irq_\no:
|
||||
|
||||
.section .text
|
||||
|
||||
__x86_64_irq_0:
|
||||
cli
|
||||
|
||||
push %rax
|
||||
push %rcx
|
||||
push %rdx
|
||||
push %rbx
|
||||
push %rbp
|
||||
push %rsi
|
||||
push %rdi
|
||||
|
||||
push %r8
|
||||
push %r9
|
||||
push %r10
|
||||
push %r11
|
||||
push %r12
|
||||
push %r13
|
||||
push %r14
|
||||
push %r15
|
||||
|
||||
mov $0x20, %al
|
||||
mov $0x20, %dx
|
||||
outb %al, %dx
|
||||
|
||||
call sched_yield
|
||||
|
||||
pop %r15
|
||||
pop %r14
|
||||
pop %r13
|
||||
pop %r12
|
||||
pop %r11
|
||||
pop %r10
|
||||
pop %r9
|
||||
pop %r8
|
||||
|
||||
pop %rdi
|
||||
pop %rsi
|
||||
pop %rbp
|
||||
pop %rbx
|
||||
pop %rdx
|
||||
pop %rcx
|
||||
pop %rax
|
||||
|
||||
iretq
|
||||
// __x86_64_irq_0:
|
||||
// cli
|
||||
//
|
||||
// push %rax
|
||||
// push %rcx
|
||||
// push %rdx
|
||||
// push %rbx
|
||||
// push %rbp
|
||||
// push %rsi
|
||||
// push %rdi
|
||||
//
|
||||
// push %r8
|
||||
// push %r9
|
||||
// push %r10
|
||||
// push %r11
|
||||
// push %r12
|
||||
// push %r13
|
||||
// push %r14
|
||||
// push %r15
|
||||
//
|
||||
// mov $0x20, %al
|
||||
// mov $0x20, %dx
|
||||
// outb %al, %dx
|
||||
//
|
||||
// call sched_yield
|
||||
//
|
||||
// pop %r15
|
||||
// pop %r14
|
||||
// pop %r13
|
||||
// pop %r12
|
||||
// pop %r11
|
||||
// pop %r10
|
||||
// pop %r9
|
||||
// pop %r8
|
||||
//
|
||||
// pop %rdi
|
||||
// pop %rsi
|
||||
// pop %rbp
|
||||
// pop %rbx
|
||||
// pop %rdx
|
||||
// pop %rcx
|
||||
// pop %rax
|
||||
//
|
||||
// iretq
|
||||
|
||||
irq_entry 0
|
||||
irq_entry 1
|
||||
irq_entry 2
|
||||
irq_entry 3
|
||||
|
||||
@@ -6,6 +6,8 @@ use uart::Uart;
|
||||
mod intc;
|
||||
use intc::I8259;
|
||||
pub use intc::IrqNumber;
|
||||
mod timer;
|
||||
use timer::Timer;
|
||||
|
||||
mod io;
|
||||
pub(self) use io::PortIo;
|
||||
@@ -55,6 +57,11 @@ pub fn console() -> &'static impl SerialDevice {
|
||||
&COM1
|
||||
}
|
||||
|
||||
pub fn local_timer() -> &'static Timer {
|
||||
&TIMER
|
||||
}
|
||||
|
||||
static COM1: Uart = unsafe { Uart::new(0x3F8, IrqNumber::new(4)) };
|
||||
pub(self) static INTC: I8259 = I8259::new();
|
||||
pub(self) static DISPLAY: StaticFramebuffer = StaticFramebuffer::uninit();
|
||||
pub(self) static TIMER: Timer = Timer::new(IrqNumber::new(0));
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
//! i.... timer implementation
|
||||
|
||||
use crate::arch::machine::{self, IrqNumber, PortIo};
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource},
|
||||
pseudo,
|
||||
timer::TimestampSource,
|
||||
Device,
|
||||
};
|
||||
use crate::proc;
|
||||
use core::time::Duration;
|
||||
use libsys::error::Errno;
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
// 1.1931816666 MHz base freq
|
||||
|
||||
/// Generic timer struct
|
||||
pub struct Timer {
|
||||
data0: PortIo<u8>,
|
||||
cmd: PortIo<u8>,
|
||||
counter: AtomicU64,
|
||||
irq: IrqNumber
|
||||
}
|
||||
|
||||
impl Device for Timer {
|
||||
fn name(&self) -> &'static str {
|
||||
"Intel Timer"
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
const DIV: u16 = (1193182u32 / 1000) as u16;
|
||||
|
||||
self.data0.write((DIV & 0xFF) as u8);
|
||||
self.data0.write((DIV >> 8) as u8);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TimestampSource for Timer {
|
||||
fn timestamp(&self) -> Result<Duration, Errno> {
|
||||
Ok(Duration::from_millis(self.counter.load(Ordering::Relaxed)))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntSource for Timer {
|
||||
fn handle_irq(&self) -> Result<(), Errno> {
|
||||
self.counter.fetch_add(1, Ordering::Relaxed);
|
||||
proc::wait::tick();
|
||||
proc::switch();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_irqs(&'static self) -> Result<(), Errno> {
|
||||
machine::INTC.register_handler(self.irq, self)?;
|
||||
machine::INTC.enable_irq(self.irq)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
/// Constructs a new instance of ARM Generic Timer
|
||||
pub const fn new(irq: IrqNumber) -> Self {
|
||||
Self {
|
||||
data0: unsafe { PortIo::new(0x40) },
|
||||
cmd: unsafe { PortIo::new(0x43) },
|
||||
counter: AtomicU64::new(0),
|
||||
irq
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
-22
@@ -39,12 +39,35 @@ pub static WAIT_SELECT: Wait = Wait::new("select");
|
||||
|
||||
/// Checks for any timed out wait channels and interrupts them
|
||||
pub fn tick() {
|
||||
todo!();
|
||||
let time = machine::local_timer().timestamp().unwrap();
|
||||
let mut list = TICK_LIST.lock();
|
||||
let mut cursor = list.cursor_front_mut();
|
||||
|
||||
while let Some(item) = cursor.current() {
|
||||
if time > item.deadline {
|
||||
let tid = item.tid;
|
||||
cursor.remove_current();
|
||||
SCHED.enqueue(tid);
|
||||
} else {
|
||||
cursor.move_next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Suspends current process for given duration
|
||||
pub fn sleep(timeout: Duration, remaining: &mut Duration) -> Result<(), Errno> {
|
||||
todo!()
|
||||
// Dummy wait descriptor which will never receive notifications
|
||||
static SLEEP_NOTIFY: Wait = Wait::new("sleep");
|
||||
let deadline = machine::local_timer().timestamp()? + timeout;
|
||||
match SLEEP_NOTIFY.wait(Some(deadline)) {
|
||||
Err(Errno::Interrupt) => {
|
||||
*remaining = deadline - machine::local_timer().timestamp()?;
|
||||
Err(Errno::Interrupt)
|
||||
}
|
||||
Err(Errno::TimedOut) => Ok(()),
|
||||
Ok(_) => panic!("Impossible result"),
|
||||
res => res,
|
||||
}
|
||||
}
|
||||
|
||||
/// Suspends current process until some file descriptor
|
||||
@@ -146,12 +169,12 @@ impl Wait {
|
||||
queue_lock.push_back(thread.id());
|
||||
thread.setup_wait(self);
|
||||
|
||||
// if let Some(deadline) = deadline {
|
||||
// TICK_LIST.lock().push_back(Timeout {
|
||||
// tid: thread.id(),
|
||||
// deadline,
|
||||
// });
|
||||
// }
|
||||
if let Some(deadline) = deadline {
|
||||
TICK_LIST.lock().push_back(Timeout {
|
||||
tid: thread.id(),
|
||||
deadline,
|
||||
});
|
||||
}
|
||||
|
||||
loop {
|
||||
match thread.wait_status() {
|
||||
@@ -168,22 +191,22 @@ impl Wait {
|
||||
thread.enter_wait();
|
||||
queue_lock = self.queue.lock();
|
||||
|
||||
// if let Some(deadline) = deadline {
|
||||
// if machine::local_timer().timestamp()? > deadline {
|
||||
// let mut cursor = queue_lock.cursor_front_mut();
|
||||
if let Some(deadline) = deadline {
|
||||
if machine::local_timer().timestamp()? > deadline {
|
||||
let mut cursor = queue_lock.cursor_front_mut();
|
||||
|
||||
// while let Some(&mut item) = cursor.current() {
|
||||
// if thread.id() == item {
|
||||
// cursor.remove_current();
|
||||
// break;
|
||||
// } else {
|
||||
// cursor.move_next();
|
||||
// }
|
||||
// }
|
||||
while let Some(&mut item) = cursor.current() {
|
||||
if thread.id() == item {
|
||||
cursor.remove_current();
|
||||
break;
|
||||
} else {
|
||||
cursor.move_next();
|
||||
}
|
||||
}
|
||||
|
||||
// return Err(Errno::TimedOut);
|
||||
// }
|
||||
// }
|
||||
return Err(Errno::TimedOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +122,15 @@ pub fn option_struct_mut<'a, T>(base: usize) -> Result<Option<&'a mut T>, Errno>
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks given argument and interprets it as a `Option<&'a mut [T]>`
|
||||
pub fn option_struct_buf_mut<'a, T>(base: usize, count: usize) -> Result<Option<&'a mut [T]>, Errno> {
|
||||
if base == 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
struct_buf_mut(base, count).map(Some)
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates that the argument pointer is accessible for requested operation
|
||||
/// for current process
|
||||
pub fn validate_ptr(base: usize, len: usize, write: bool) -> Result<(), Errno> {
|
||||
|
||||
@@ -314,13 +314,14 @@ fn _syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
SystemCall::GetPid => Ok(u32::from(Process::current().id()) as usize),
|
||||
SystemCall::GetTid => Ok(u32::from(Thread::current().id()) as usize),
|
||||
SystemCall::Sleep => {
|
||||
let rem_buf = arg::option_buf_ref(args[1], size_of::<u64>() * 2)?;
|
||||
let rem_buf = arg::option_struct_buf_mut::<u64>(args[1], size_of::<u64>() * 2)?;
|
||||
let mut rem = Duration::new(0, 0);
|
||||
let res = wait::sleep(Duration::from_nanos(args[0] as u64), &mut rem);
|
||||
if res == Err(Errno::Interrupt) {
|
||||
warnln!("Sleep interrupted, {:?} remaining", rem);
|
||||
if rem_buf.is_some() {
|
||||
todo!()
|
||||
if let Some(rem_buf) = rem_buf {
|
||||
rem_buf[0] = rem.as_secs();
|
||||
rem_buf[1] = rem.subsec_nanos() as u64;
|
||||
}
|
||||
}
|
||||
res.map(|_| 0)
|
||||
|
||||
+35
-6
@@ -9,8 +9,8 @@ use alloc::{borrow::ToOwned, vec::Vec};
|
||||
use libusr::io::{self, Read};
|
||||
use libusr::signal::{self, SignalHandler};
|
||||
use libusr::sys::{
|
||||
sys_chdir, sys_execve, sys_exit, sys_faccessat, sys_fork, sys_getpgid, sys_setpgid,
|
||||
sys_waitpid, AccessMode, Errno, ExitCode, FileDescriptor, Signal,
|
||||
sys_chdir, sys_ex_nanosleep, sys_execve, sys_exit, sys_faccessat, sys_fork, sys_getpgid,
|
||||
sys_setpgid, sys_waitpid, AccessMode, Errno, ExitCode, FileDescriptor, Signal,
|
||||
};
|
||||
|
||||
struct Builtin {
|
||||
@@ -30,10 +30,39 @@ fn cmd_cd(args: &[&str]) -> ExitCode {
|
||||
}
|
||||
}
|
||||
|
||||
static BUILTINS: [Builtin; 1] = [Builtin {
|
||||
name: "cd",
|
||||
func: cmd_cd,
|
||||
}];
|
||||
fn cmd_sleep(args: &[&str]) -> ExitCode {
|
||||
if args.len() == 2 {
|
||||
match args[1].parse::<u32>() {
|
||||
Err(e) => {
|
||||
eprintln!("{}: {:?}", args[1], e);
|
||||
ExitCode::from(-1)
|
||||
}
|
||||
Ok(count) => {
|
||||
let mut rem = [0; 2];
|
||||
if let Err(err) = sys_ex_nanosleep((count as u64) * 1000000000, &mut rem) {
|
||||
eprintln!("Sleep failed (rem. {:?}): {:?}", rem, err);
|
||||
ExitCode::from(-1)
|
||||
} else {
|
||||
ExitCode::from(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eprintln!("Usage: sleep SECS");
|
||||
ExitCode::from(-1)
|
||||
}
|
||||
}
|
||||
|
||||
static BUILTINS: [Builtin; 2] = [
|
||||
Builtin {
|
||||
name: "cd",
|
||||
func: cmd_cd,
|
||||
},
|
||||
Builtin {
|
||||
name: "sleep",
|
||||
func: cmd_sleep,
|
||||
},
|
||||
];
|
||||
|
||||
fn readline<'a, F: Read>(f: &mut F, bytes: &'a mut [u8]) -> Result<Option<&'a str>, io::Error> {
|
||||
let size = f.read(bytes)?;
|
||||
|
||||
Reference in New Issue
Block a user