Added global timer (PIT-backed now) and nanosleep() syscall
This commit is contained in:
@@ -15,6 +15,8 @@ ifdef KERNEL_TEST_MODE
|
||||
CFLAGS+=-DKERNEL_TEST_MODE
|
||||
endif
|
||||
|
||||
CFLAGS+=-D__KERNEL__
|
||||
|
||||
# DIRS+=$(O)/sys \
|
||||
# $(O)/sys/vfs \
|
||||
# $(O)/sys/vfs/ext2 \
|
||||
|
||||
+18
-16
@@ -1,19 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#define SYSCALL_NR_READ 0
|
||||
#define SYSCALL_NR_WRITE 1
|
||||
#define SYSCALL_NR_OPEN 2
|
||||
#define SYSCALL_NR_CLOSE 3
|
||||
#define SYSCALL_NR_STAT 4
|
||||
#define SYSCALL_NR_GETCWD 79
|
||||
#define SYSCALL_NR_CHDIR 80
|
||||
#define SYSCALL_NR_READDIR 89
|
||||
#define SYSCALL_NR_READ 0
|
||||
#define SYSCALL_NR_WRITE 1
|
||||
#define SYSCALL_NR_OPEN 2
|
||||
#define SYSCALL_NR_CLOSE 3
|
||||
#define SYSCALL_NR_STAT 4
|
||||
#define SYSCALL_NR_GETCWD 79
|
||||
#define SYSCALL_NR_CHDIR 80
|
||||
#define SYSCALL_NR_READDIR 89
|
||||
|
||||
#define SYSCALL_NR_BRK 12
|
||||
#define SYSCALL_NR_GETPID 39
|
||||
#define SYSCALL_NRX_SIGNAL 48
|
||||
#define SYSCALL_NR_FORK 57
|
||||
#define SYSCALL_NR_EXECVE 59
|
||||
#define SYSCALL_NR_EXIT 60
|
||||
#define SYSCALL_NR_KILL 62
|
||||
#define SYSCALL_NRX_SIGRET 119
|
||||
#define SYSCALL_NR_BRK 12
|
||||
#define SYSCALL_NR_NANOSLEEP 35
|
||||
#define SYSCALL_NR_GETPID 39
|
||||
#define SYSCALL_NRX_SIGNAL 48
|
||||
#define SYSCALL_NR_FORK 57
|
||||
#define SYSCALL_NR_EXECVE 59
|
||||
#define SYSCALL_NR_EXIT 60
|
||||
#define SYSCALL_NR_KILL 62
|
||||
#define SYSCALL_NR_GETTIMEOFDAY 96
|
||||
#define SYSCALL_NRX_SIGRET 119
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#define THREAD_KERNEL (1 << 31)
|
||||
#define THREAD_CTX_ONLY (1 << 30)
|
||||
#define THREAD_WAITING (1 << 3)
|
||||
#define THREAD_STOPPED (1 << 2)
|
||||
#define THREAD_SIGRET (1 << 29)
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
struct timeval {
|
||||
uint64_t tv_sec;
|
||||
uint32_t tv_usec;
|
||||
};
|
||||
|
||||
struct timezone {
|
||||
int tz_minuteswest;
|
||||
int tz_dsttime;
|
||||
};
|
||||
|
||||
struct timespec {
|
||||
uint64_t tv_sec;
|
||||
long tv_nsec;
|
||||
};
|
||||
|
||||
#if defined(__KERNEL__)
|
||||
extern uint64_t system_time;
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
struct thread;
|
||||
|
||||
struct wait_block {
|
||||
enum {
|
||||
WAIT_SLEEP,
|
||||
} type;
|
||||
union {
|
||||
uint64_t sleep_deadline;
|
||||
} data;
|
||||
|
||||
struct wait_block *next;
|
||||
};
|
||||
|
||||
void thread_nanosleep(struct thread *thr, uint64_t nano);
|
||||
// TODO: interrupt threads
|
||||
@@ -122,6 +122,14 @@ void amd64_acpi_init(void) {
|
||||
}
|
||||
|
||||
acpi_fadt = (struct acpi_fadt *) hdr;
|
||||
} else if (!strncmp((const char *) hdr, "HPET", 4)) {
|
||||
kdebug("Found HPET = %p\n", entry_addr);
|
||||
if (checksum(hdr, hdr->length) != 0) {
|
||||
kdebug("Entry is invalid\n");
|
||||
while (1);
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+32
-46
@@ -5,70 +5,56 @@
|
||||
#include "sys/amd64/hw/irq.h"
|
||||
#include "sys/amd64/hw/io.h"
|
||||
#include "sys/spin.h"
|
||||
#include "sys/time.h"
|
||||
#include "sys/debug.h"
|
||||
#include "sys/amd64/cpu.h"
|
||||
#include "sys/assert.h"
|
||||
|
||||
#define TIMER_PIT 1
|
||||
|
||||
#define PIT_FREQ_BASE 1193182
|
||||
// Gives ~1kHz, ~1ms resolution
|
||||
#define PIT_DIV 1193
|
||||
#define PIT_CH0 0x40
|
||||
#define PIT_CMD 0x43
|
||||
|
||||
// 10kHz calibration frequency = 100us period
|
||||
#define PIT_CALIB_PERIOD 100
|
||||
// Period of LAPIC timer in micros
|
||||
#define TIMER_PERIOD 1000
|
||||
uint64_t system_time = 0;
|
||||
|
||||
#define PIT_CALIB_FREQ (1000000 / (PIT_CALIB_PERIOD))
|
||||
#define PIT_CALIB_TICKS ((TIMER_PERIOD) / (PIT_CALIB_PERIOD))
|
||||
static uint32_t timer_tick(void *arg) {
|
||||
switch ((uint64_t) arg) {
|
||||
case TIMER_PIT:
|
||||
// Each tick is approx. 1ms, so add 1ms to system time
|
||||
system_time += 1000000;
|
||||
break;
|
||||
}
|
||||
return IRQ_UNHANDLED;
|
||||
}
|
||||
|
||||
static spin_t timer_lock = 0;
|
||||
extern void amd64_irq2_counting(void);
|
||||
void amd64_global_timer_init(void) {
|
||||
// Initialize global timer
|
||||
// Setup PIT
|
||||
outb(PIT_CMD, (3 << 4 /* Write lo/hi */) | (2 << 1 /* Rate generator */));
|
||||
outb(PIT_CH0, PIT_DIV & 0xFF);
|
||||
outb(PIT_CH0, PIT_DIV >> 8);
|
||||
|
||||
irq_add_handler(2, timer_tick, (void *) TIMER_PIT);
|
||||
amd64_ioapic_unmask(2);
|
||||
}
|
||||
|
||||
void amd64_timer_init(void) {
|
||||
spin_lock(&timer_lock);
|
||||
kdebug("cpu%d: initializing timer\n", get_cpu()->processor_id);
|
||||
uint8_t lapic = LAPIC(LAPIC_REG_ID) >> 24;
|
||||
|
||||
uint32_t low = amd64_ioapic_read(0x14);
|
||||
uint32_t high = amd64_ioapic_read(0x15);
|
||||
amd64_ioapic_map_gsi(2, lapic, 32);
|
||||
amd64_ioapic_unmask(2);
|
||||
amd64_idt_set(32, (uintptr_t) amd64_irq2_counting, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
|
||||
|
||||
get_cpu()->ticks = 0;
|
||||
|
||||
uint16_t pit_div = PIT_FREQ_BASE / PIT_CALIB_FREQ;
|
||||
|
||||
// Configure PIT
|
||||
outb(PIT_CMD, (0x3 << 4) | (1 << 2));
|
||||
outb(PIT_CH0, pit_div & 0xFF);
|
||||
outb(PIT_CH0, pit_div >> 8);
|
||||
|
||||
// Divider = 3
|
||||
LAPIC(LAPIC_REG_TMRDIV) = 0x3;
|
||||
// InitCnt = -1
|
||||
LAPIC(LAPIC_REG_TMRINITCNT) = 0xFFFFFFFF;
|
||||
// Mask Local APIC timer
|
||||
LAPIC(LAPIC_REG_LVTT) = (1 << 16);
|
||||
|
||||
asm volatile ("sti");
|
||||
while (get_cpu()->ticks < PIT_CALIB_TICKS) {
|
||||
asm volatile ("hlt");
|
||||
if (get_cpu()->processor_id == 0) {
|
||||
amd64_global_timer_init();
|
||||
}
|
||||
asm volatile ("cli");
|
||||
|
||||
// Ticks per TIMER_PERIOD
|
||||
uint32_t ticks = 0xFFFFFFFF - LAPIC(LAPIC_REG_TMRCURRCNT);
|
||||
// Initialize CPU-local timer
|
||||
kdebug("cpu%d: initializing timer\n", get_cpu()->processor_id);
|
||||
|
||||
// No need to calibrate CPU-local timers - precision timer is global
|
||||
// LAPIC Timer is only used to trigger task switches
|
||||
LAPIC(LAPIC_REG_TMRDIV) = 0x3;
|
||||
LAPIC(LAPIC_REG_LVTT) = 32 | (1 << 17);
|
||||
LAPIC(LAPIC_REG_TMRINITCNT) = ticks;
|
||||
LAPIC(LAPIC_REG_TMRINITCNT) = 1000;
|
||||
LAPIC(LAPIC_REG_TMRCURRCNT) = 0;
|
||||
|
||||
get_cpu()->ticks = 0;
|
||||
|
||||
amd64_ioapic_mask(2);
|
||||
amd64_idt_set(32, (uintptr_t) amd64_irq0, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
|
||||
|
||||
spin_release(&timer_lock);
|
||||
}
|
||||
|
||||
+41
-1
@@ -12,6 +12,7 @@
|
||||
#include "sys/tty.h"
|
||||
#include "sys/amd64/mm/phys.h"
|
||||
#include "sys/amd64/mm/map.h"
|
||||
#include "sys/time.h"
|
||||
|
||||
static ssize_t sys_read(int fd, void *buf, size_t lim);
|
||||
static ssize_t sys_write(int fd, const void *buf, size_t lim);
|
||||
@@ -25,6 +26,8 @@ static int sys_stat(const char *filename, struct stat *st);
|
||||
static void sys_exit(int status);
|
||||
static int sys_kill(int pid, int signum);
|
||||
static int sys_brk(void *ptr);
|
||||
static int sys_nanosleep(const struct timespec *req, struct timespec *rem);
|
||||
static int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
|
||||
|
||||
extern int sys_execve(const char *filename, const char *const argv[], const char *const envp[]);
|
||||
|
||||
@@ -62,6 +65,8 @@ intptr_t amd64_syscall(uintptr_t rdi, uintptr_t rsi, uintptr_t rdx, uintptr_t rc
|
||||
return 0;
|
||||
case SYSCALL_NR_BRK:
|
||||
return sys_brk((void *) rdi);
|
||||
case SYSCALL_NR_NANOSLEEP:
|
||||
return sys_nanosleep((const struct timespec *) rdi, (struct timespec *) rsi);
|
||||
case SYSCALL_NR_GETPID:
|
||||
{
|
||||
struct thread *t = get_cpu()->thread;
|
||||
@@ -73,6 +78,8 @@ intptr_t amd64_syscall(uintptr_t rdi, uintptr_t rsi, uintptr_t rdx, uintptr_t rc
|
||||
amd64_syscall_yield_stopped();
|
||||
case SYSCALL_NR_KILL:
|
||||
return sys_kill((int) rdi, (int) rsi);
|
||||
case SYSCALL_NR_GETTIMEOFDAY:
|
||||
return sys_gettimeofday((struct timeval *) rdi, (struct timezone *) rsi);
|
||||
case SYSCALL_NRX_SIGRET:
|
||||
sys_sigret();
|
||||
return 0;
|
||||
@@ -312,7 +319,40 @@ static int sys_kill(int pid, int signum) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern void amd64_syscall_iretq(uintptr_t ctx);
|
||||
static int sys_nanosleep(const struct timespec *req, struct timespec *rem) {
|
||||
struct thread *cur_thread = get_cpu()->thread;
|
||||
_assert(cur_thread);
|
||||
|
||||
uint64_t time_now = system_time;
|
||||
uint64_t deadline = time_now + req->tv_sec * 1000000000ULL + req->tv_nsec;
|
||||
|
||||
// Make sure scheduler won't select a waiting thread
|
||||
cur_thread->flags |= THREAD_WAITING;
|
||||
|
||||
while (1) {
|
||||
// TODO: interruptible sleep
|
||||
if (system_time >= deadline) {
|
||||
break;
|
||||
}
|
||||
asm volatile ("sti; hlt; cli");
|
||||
}
|
||||
|
||||
cur_thread->flags &= ~THREAD_WAITING;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_gettimeofday(struct timeval *tv, struct timezone *tz) {
|
||||
tz->tz_dsttime = 0;
|
||||
tz->tz_minuteswest = 0;
|
||||
|
||||
// System time is in nanos
|
||||
// TODO: use RTC-provided time for full system time
|
||||
tv->tv_usec = (system_time / 1000) % 1000000;
|
||||
tv->tv_sec = system_time / 1000000000ULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sys_sigret(void) {
|
||||
struct thread *thr = get_cpu()->thread;
|
||||
|
||||
+26
@@ -36,6 +36,7 @@ static int b_cd(const char *path);
|
||||
static int b_pwd(const char *_);
|
||||
static int b_cat(const char *path);
|
||||
static int b_curs(const char *arg);
|
||||
static int b_sleep(const char *arg);
|
||||
|
||||
static struct builtin builtins[] = {
|
||||
{
|
||||
@@ -63,6 +64,11 @@ static struct builtin builtins[] = {
|
||||
"Cursor demo",
|
||||
b_curs,
|
||||
},
|
||||
{
|
||||
"sleep",
|
||||
"Sleep N seconds",
|
||||
b_sleep
|
||||
},
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
@@ -182,6 +188,26 @@ static int b_curs(const char *arg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int b_sleep(const char *arg) {
|
||||
if (!arg) {
|
||||
return -1;
|
||||
}
|
||||
int seconds = 0;
|
||||
while (*arg) {
|
||||
seconds *= 10;
|
||||
seconds += *arg - '0';
|
||||
++arg;
|
||||
}
|
||||
|
||||
struct timespec ts = { seconds, 0 };
|
||||
if ((seconds = nanosleep(&ts, NULL))) {
|
||||
perror("nanosleep()");
|
||||
return seconds;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void prompt(void) {
|
||||
printf("\033[36mygg\033[0m > ");
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
ssize_t read(int fd, void *buf, size_t count);
|
||||
ssize_t write(int fd, const void *buf, size_t count);
|
||||
@@ -19,5 +20,6 @@ __attribute__((noreturn)) void __kernel_sigret(void);
|
||||
int getpid(void);
|
||||
int chdir(const char *filename);
|
||||
char *getcwd(char *buf, size_t size);
|
||||
int nanosleep(const struct timespec *req, struct timespec *rem);
|
||||
|
||||
__attribute__((noreturn)) void exit(int code);
|
||||
|
||||
@@ -125,6 +125,10 @@ char *getcwd(char *buf, size_t lim) {
|
||||
}
|
||||
}
|
||||
|
||||
int nanosleep(const struct timespec *req, struct timespec *rem) {
|
||||
return SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NR_NANOSLEEP, req, rem));
|
||||
}
|
||||
|
||||
// Although sbrk() is implemented in userspace, I guess it should also be here
|
||||
void *sbrk(intptr_t inc) {
|
||||
if (inc == 0) {
|
||||
|
||||
Reference in New Issue
Block a user