Added global timer (PIT-backed now) and nanosleep() syscall

This commit is contained in:
Mark
2019-12-31 13:01:47 +02:00
parent 4b9d6e3303
commit 6a03d92882
11 changed files with 173 additions and 63 deletions
+2
View File
@@ -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
View File
@@ -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
+1
View File
@@ -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)
+21
View File
@@ -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
+18
View File
@@ -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
+8
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 > ");
}
+2
View File
@@ -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);
+4
View File
@@ -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) {