diff --git a/etc/make/conf.mk b/etc/make/conf.mk index 7a08f87..07f8d67 100644 --- a/etc/make/conf.mk +++ b/etc/make/conf.mk @@ -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 \ diff --git a/include/sys/syscall.h b/include/sys/syscall.h index 24dea34..96f64fb 100644 --- a/include/sys/syscall.h +++ b/include/sys/syscall.h @@ -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 diff --git a/include/sys/thread.h b/include/sys/thread.h index d576ebd..e0b62d3 100644 --- a/include/sys/thread.h +++ b/include/sys/thread.h @@ -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) diff --git a/include/sys/time.h b/include/sys/time.h new file mode 100644 index 0000000..0649dc0 --- /dev/null +++ b/include/sys/time.h @@ -0,0 +1,21 @@ +#pragma once +#include + +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 diff --git a/include/sys/wait.h b/include/sys/wait.h new file mode 100644 index 0000000..7b0a69a --- /dev/null +++ b/include/sys/wait.h @@ -0,0 +1,18 @@ +#pragma once +#include + +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 diff --git a/sys/amd64/hw/acpi.c b/sys/amd64/hw/acpi.c index 15e8001..e07f441 100644 --- a/sys/amd64/hw/acpi.c +++ b/sys/amd64/hw/acpi.c @@ -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); + } + + // ... } } diff --git a/sys/amd64/hw/timer.c b/sys/amd64/hw/timer.c index ff04307..e6785cf 100644 --- a/sys/amd64/hw/timer.c +++ b/sys/amd64/hw/timer.c @@ -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); } diff --git a/sys/amd64/syscall.c b/sys/amd64/syscall.c index 5530b01..db5bad4 100644 --- a/sys/amd64/syscall.c +++ b/sys/amd64/syscall.c @@ -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; diff --git a/usr/init.c b/usr/init.c index 84d77f5..d48d74a 100644 --- a/usr/init.c +++ b/usr/init.c @@ -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 > "); } diff --git a/usr/libc/include/bits/syscall.h b/usr/libc/include/bits/syscall.h index 49c21a7..67e435c 100644 --- a/usr/libc/include/bits/syscall.h +++ b/usr/libc/include/bits/syscall.h @@ -3,6 +3,7 @@ #include #include #include +#include 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); diff --git a/usr/libc/syscall.c b/usr/libc/syscall.c index ced7c6f..b905961 100644 --- a/usr/libc/syscall.c +++ b/usr/libc/syscall.c @@ -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) {