diff --git a/examples/typephp-os/README.md b/examples/typephp-os/README.md index 26c2f37c..bc7e8871 100644 --- a/examples/typephp-os/README.md +++ b/examples/typephp-os/README.md @@ -224,13 +224,16 @@ segment. The native x86-64 `SYSCALL` boundary provides synchronous `read`, `write`, `close`, `lseek`, `openat`, `exit`/`exit_group`, `getcwd`, `chdir`, `mkdir`, `rmdir`, `unlink`, file stat/access/persistence/truncation families, fixed identity -queries, `time`, `gettimeofday`, `clock_gettime`, `clock_getres`, `brk`, +queries, `time`, `gettimeofday`, `clock_gettime`, `clock_getres`, `nanosleep`, `brk`, anonymous private `mmap`, `mprotect`, `munmap`, `getdents64`, the initial `fcntl` flag operations, fixed-console `ioctl(TIOCGWINSZ)`, and private spawn and same-directory rename operations. The former private directory-list syscall has been removed; `ls` and PHP Nano use the standard directory ABI. Standard input and output are backed by QEMU's COM1 serial console. Syscall numbers are shared by the kernel and userspace through `typephp_os_syscall.h`. +The 8259 PIC routes a 100 Hz PIT timer on IRQ0 and COM1 input on IRQ4. Console +reads and sleeps block with `HLT` until an interrupt arrives, so the resident +shell no longer consumes a host CPU core while it waits at the prompt. Entry immediately switches from the untrusted user RSP to a dedicated kernel stack. Return uses `iretq`, allowing synchronous spawn/exit to replace the complete saved user context. diff --git a/examples/typephp-os/ROADMAP.md b/examples/typephp-os/ROADMAP.md index eb59011e..27b87dd2 100644 --- a/examples/typephp-os/ROADMAP.md +++ b/examples/typephp-os/ROADMAP.md @@ -98,14 +98,15 @@ behavior. does not yet replace the working userspace commands. Single-task identity calls report PID/TID 1 for the resident shell, 2 for its synchronous foreground command, and root UID/GID. Linux-compatible - wall/monotonic clock structures are exposed at the current one-second RTC - resolution. The `systest` ELF exercises this complete syscall slice. + wall-clock time uses the RTC while monotonic time and blocking sleeps use a + 100 Hz PIT. The `systest` ELF exercises this complete syscall slice. Syscall evolution follows the glibc migration rules in `user/README.md`. 7. **Native Class memory.** Exercise Wren GC through Zend MM and verify tracing of PHPX fields under sustained allocation. -8. **Kernel services.** Interrupt-driven timer and keyboard, higher-level VM - region management, Zend-chunk reclamation, and a capability-oriented native - API. +8. **Kernel services.** The legacy PIC, a 100 Hz PIT clock, interrupt-driven + COM1 input, blocking console reads, and blocking `nanosleep` are implemented. + Higher-level VM region management, Zend-chunk reclamation, and a + capability-oriented native API remain future work. 9. **Packaging and CI.** Automate the two-stage ELF32/ELF64 build and QEMU boot smoke test in GitHub Actions. diff --git a/examples/typephp-os/kernel/core/abi/typephp_os_abi.h b/examples/typephp-os/kernel/core/abi/typephp_os_abi.h index 4e3737c6..821209d8 100644 --- a/examples/typephp-os/kernel/core/abi/typephp_os_abi.h +++ b/examples/typephp-os/kernel/core/abi/typephp_os_abi.h @@ -43,6 +43,8 @@ void typephp_os_process_start(void); long typephp_os_fs_path_type(const char *path); long typephp_os_fs_getdents64(int fd, void *buffer, size_t capacity); long typephp_os_console_read(void *buffer, size_t size); +void typephp_os_console_enable_interrupts(void); +void typephp_os_console_interrupt(void); #ifdef __cplusplus } diff --git a/examples/typephp-os/kernel/core/abi/typephp_os_syscall.h b/examples/typephp-os/kernel/core/abi/typephp_os_syscall.h index 91e3f512..56665f42 100644 --- a/examples/typephp-os/kernel/core/abi/typephp_os_syscall.h +++ b/examples/typephp-os/kernel/core/abi/typephp_os_syscall.h @@ -18,6 +18,7 @@ enum typephp_os_syscall_number { TYPEPHP_SYS_BRK = 12, TYPEPHP_SYS_IOCTL = 16, TYPEPHP_SYS_ACCESS = 21, + TYPEPHP_SYS_NANOSLEEP = 35, TYPEPHP_SYS_GETPID = 39, TYPEPHP_SYS_EXIT = 60, TYPEPHP_SYS_UNAME = 63, diff --git a/examples/typephp-os/kernel/core/kernel.c b/examples/typephp-os/kernel/core/kernel.c index 805609ca..8366e87d 100644 --- a/examples/typephp-os/kernel/core/kernel.c +++ b/examples/typephp-os/kernel/core/kernel.c @@ -8,11 +8,15 @@ enum { VGA_WIDTH = 80, VGA_HEIGHT = 25, COM1 = 0x3f8, + SERIAL_BUFFER_SIZE = 8192, }; static volatile uint16_t *const vga = (volatile uint16_t *) 0xb8000; static uint32_t row; static uint32_t column; +static volatile uint32_t serial_read_head; +static volatile uint32_t serial_read_tail; +static volatile uint8_t serial_read_buffer[SERIAL_BUFFER_SIZE]; static inline void outb(uint16_t port, uint8_t value) { @@ -37,6 +41,25 @@ static void serial_init(void) outb(COM1 + 4, 0x0b); } +void typephp_os_console_enable_interrupts(void) +{ + /* Received-data-available interrupt. MCR.OUT2 was enabled by serial_init + * and routes the UART interrupt to the legacy PIC's IRQ4 input. */ + outb(COM1 + 1, 0x01); +} + +void typephp_os_console_interrupt(void) +{ + while ((inb(COM1 + 5) & 0x01u) != 0) { + const uint8_t value = inb(COM1); + const uint32_t next = (serial_read_head + 1u) % SERIAL_BUFFER_SIZE; + if (next != serial_read_tail) { + serial_read_buffer[serial_read_head] = value; + serial_read_head = next; + } + } +} + static void serial_put(uint8_t value) { while ((inb(COM1 + 5) & 0x20u) == 0) { @@ -50,14 +73,18 @@ long typephp_os_console_read(void *buffer, unsigned long size) if (output == 0 || size == 0) { return 0; } - /* COM1 is the standard input device for the headless QEMU target. Keep - * this synchronous: the process model intentionally has one foreground - * task and no scheduler yet. */ - while ((inb(COM1 + 5) & 0x01u) == 0) { - __asm__ volatile("pause"); + /* The system remains single-task, but a blocking read can still halt the + * CPU until IRQ4 supplies input. STI followed immediately by HLT avoids + * the empty-buffer/check-to-sleep lost-wakeup race on x86. */ + for (;;) { + __asm__ volatile("cli" : : : "memory"); + if (serial_read_tail != serial_read_head) { + output[0] = serial_read_buffer[serial_read_tail]; + serial_read_tail = (serial_read_tail + 1u) % SERIAL_BUFFER_SIZE; + return 1; + } + __asm__ volatile("sti; hlt; cli" : : : "memory"); } - output[0] = inb(COM1); - return 1; } static void vga_scroll(void) diff --git a/examples/typephp-os/kernel/core/process-entry.S b/examples/typephp-os/kernel/core/process-entry.S index 9f1abd46..5003844a 100644 --- a/examples/typephp-os/kernel/core/process-entry.S +++ b/examples/typephp-os/kernel/core/process-entry.S @@ -89,6 +89,60 @@ TYPEPHP_EXCEPTION_WITH_ERROR 12 TYPEPHP_EXCEPTION_WITH_ERROR 13 TYPEPHP_EXCEPTION_WITH_ERROR 14 +.macro TYPEPHP_IRQ number vector +.global typephp_os_irq_\number +.type typephp_os_irq_\number, @function +typephp_os_irq_\number: + pushq $0 + pushq $\vector + jmp typephp_os_irq_common +.size typephp_os_irq_\number, . - typephp_os_irq_\number +.endm + +TYPEPHP_IRQ 0 32 +TYPEPHP_IRQ 4 36 + +.type typephp_os_irq_common, @function +.extern typephp_os_irq_dispatch +typephp_os_irq_common: + push %rbx + push %rcx + push %rdx + push %rsi + push %rdi + push %rbp + push %r8 + push %r9 + push %r10 + push %r11 + push %r12 + push %r13 + push %r14 + push %r15 + push %rax + + mov %rsp, %rdi + call typephp_os_irq_dispatch + + pop %rax + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %r11 + pop %r10 + pop %r9 + pop %r8 + pop %rbp + pop %rdi + pop %rsi + pop %rdx + pop %rcx + pop %rbx + add $16, %rsp + iretq +.size typephp_os_irq_common, . - typephp_os_irq_common + .type typephp_os_exception_common, @function .extern typephp_os_exception_dispatch typephp_os_exception_common: @@ -133,14 +187,15 @@ typephp_os_exception_common: .global typephp_os_enter_user .type typephp_os_enter_user, @function typephp_os_enter_user: - # RDI = ELF entry, RSI = top of the user stack. Interrupts remain masked; - # SYSCALL is synchronous and the first process manager has no scheduler. + # RDI = ELF entry, RSI = top of the user stack. Hardware interrupts stay + # enabled in Ring 3 so timer and serial input can arrive while a command is + # running. IA32_FMASK masks them again on every SYSCALL entry. mov $0x1b, %ax mov %ax, %ds mov %ax, %es pushq $0x1b push %rsi - pushq $0x2 + pushq $0x202 pushq $0x23 push %rdi iretq diff --git a/examples/typephp-os/kernel/core/process.c b/examples/typephp-os/kernel/core/process.c index 0cf9ec84..8361cf73 100644 --- a/examples/typephp-os/kernel/core/process.c +++ b/examples/typephp-os/kernel/core/process.c @@ -35,6 +35,14 @@ enum { USER_DATA_SELECTOR = 0x1b, KERNEL_CODE_SELECTOR = 0x08, TSS_SELECTOR = 0x28, + PIC_MASTER_COMMAND = 0x20, + PIC_MASTER_DATA = 0x21, + PIC_SLAVE_COMMAND = 0xa0, + PIC_SLAVE_DATA = 0xa1, + PIC_END_OF_INTERRUPT = 0x20, + PIT_CHANNEL_0 = 0x40, + PIT_COMMAND = 0x43, + TIMER_FREQUENCY = 100, MAX_USER_ARGUMENTS = 8, USER_PATH_MAX = 128, UTSNAME_LENGTH = 65, @@ -216,6 +224,8 @@ extern void typephp_os_exception_11(void); extern void typephp_os_exception_12(void); extern void typephp_os_exception_13(void); extern void typephp_os_exception_14(void); +extern void typephp_os_irq_0(void); +extern void typephp_os_irq_4(void); extern int rename(const char *old_path, const char *new_path); extern uint64_t physical_page_available(void); extern uint64_t physical_page_total(void); @@ -229,6 +239,7 @@ static task_state_segment tss; unsigned char typephp_os_syscall_stack[64u * 1024u] __attribute__((aligned(16))); uint64_t typephp_os_syscall_user_rsp; static process_state foreground_process = {.pid = 1, .cwd = "/"}; +static volatile uint64_t timer_ticks; enum { IA32_EFER = 0xc0000080u, @@ -245,6 +256,57 @@ static uint64_t read_msr(uint32_t index) return ((uint64_t) high << 32u) | low; } +static void outb(uint16_t port, uint8_t value) +{ + __asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port)); +} + +static void io_wait(void) +{ + outb(0x80, 0); +} + +static void install_interrupt_controller(void) +{ + const uint16_t divisor = (uint16_t) (1193182u / TIMER_FREQUENCY); + + /* Remap the legacy PIC away from the CPU exception vectors. */ + outb(PIC_MASTER_COMMAND, 0x11); + io_wait(); + outb(PIC_SLAVE_COMMAND, 0x11); + io_wait(); + outb(PIC_MASTER_DATA, 0x20); + io_wait(); + outb(PIC_SLAVE_DATA, 0x28); + io_wait(); + outb(PIC_MASTER_DATA, 0x04); + io_wait(); + outb(PIC_SLAVE_DATA, 0x02); + io_wait(); + outb(PIC_MASTER_DATA, 0x01); + io_wait(); + outb(PIC_SLAVE_DATA, 0x01); + io_wait(); + + /* A 100 Hz periodic PIT is sufficient for the single-task sleep ABI. */ + outb(PIT_COMMAND, 0x36); + outb(PIT_CHANNEL_0, (uint8_t) divisor); + outb(PIT_CHANNEL_0, (uint8_t) (divisor >> 8u)); + typephp_os_console_enable_interrupts(); + + /* IRQ0 (PIT) and IRQ4 (COM1) only. The slave PIC stays fully masked. */ + outb(PIC_SLAVE_DATA, 0xff); + outb(PIC_MASTER_DATA, (uint8_t) ~(UINT8_C(1) | UINT8_C(1) << 4u)); +} + +static void pic_end_of_interrupt(unsigned int irq) +{ + if (irq >= 8u) { + outb(PIC_SLAVE_COMMAND, PIC_END_OF_INTERRUPT); + } + outb(PIC_MASTER_COMMAND, PIC_END_OF_INTERRUPT); +} + static void write_msr(uint32_t index, uint64_t value) { __asm__ volatile("wrmsr" : : "c"(index), "a"((uint32_t) value), @@ -348,6 +410,8 @@ static void install_descriptor_tables(void) install_idt_gate(12, typephp_os_exception_12, 0x8e); install_idt_gate(13, typephp_os_exception_13, 0x8e); install_idt_gate(14, typephp_os_exception_14, 0x8e); + install_idt_gate(32, typephp_os_irq_0, 0x8e); + install_idt_gate(36, typephp_os_irq_4, 0x8e); pointer.limit = sizeof(idt) - 1u; pointer.base = (uint64_t) (uintptr_t) idt; __asm__ volatile("lidt %0" : : "m"(pointer) : "memory"); @@ -362,6 +426,7 @@ static void install_descriptor_tables(void) write_msr(IA32_LSTAR, (uint64_t) (uintptr_t) typephp_os_syscall_entry); write_msr(IA32_FMASK, UINT64_C(0x700)); /* TF, IF and DF */ write_msr(IA32_EFER, read_msr(IA32_EFER) | UINT64_C(1)); + install_interrupt_controller(); } static int read_exact(int fd, void *buffer, size_t size) @@ -790,6 +855,7 @@ static long syscall_gettimeofday(user_timeval *result) static long syscall_clock(int clock_id, user_timespec *result, int resolution) { + uint64_t ticks; if (clock_id != 0 && clock_id != 1) { return -EINVAL; } @@ -799,8 +865,66 @@ static long syscall_clock(int clock_id, user_timespec *result, int resolution) if (!user_buffer(result, sizeof(*result))) { return -EFAULT; } - result->seconds = resolution ? 1 : typephp_os_time_seconds(); - result->nanoseconds = 0; + if (resolution) { + result->seconds = clock_id == 0 ? 1 : 0; + result->nanoseconds = clock_id == 0 + ? 0 + : 1000000000L / TIMER_FREQUENCY; + return 0; + } + if (clock_id == 0) { + result->seconds = typephp_os_time_seconds(); + result->nanoseconds = 0; + return 0; + } + ticks = timer_ticks; + result->seconds = (long) (ticks / TIMER_FREQUENCY); + result->nanoseconds = (long) (ticks % TIMER_FREQUENCY) + * (1000000000L / TIMER_FREQUENCY); + return 0; +} + +static long syscall_nanosleep( + const user_timespec *requested, user_timespec *remaining) +{ + uint64_t seconds; + uint64_t subsecond_ticks; + uint64_t duration_ticks; + uint64_t deadline; + long nanoseconds; + + if (!user_buffer(requested, sizeof(*requested))) { + return -EFAULT; + } + if (remaining != 0 && !user_buffer(remaining, sizeof(*remaining))) { + return -EFAULT; + } + if (requested->seconds < 0 || requested->nanoseconds < 0 + || requested->nanoseconds >= 1000000000L) { + return -EINVAL; + } + seconds = (uint64_t) requested->seconds; + nanoseconds = requested->nanoseconds; + subsecond_ticks = ((uint64_t) nanoseconds + + (1000000000u / TIMER_FREQUENCY) - 1u) + / (1000000000u / TIMER_FREQUENCY); + if (seconds > (UINT64_MAX - subsecond_ticks) / TIMER_FREQUENCY) { + return -EINVAL; + } + duration_ticks = seconds * TIMER_FREQUENCY + subsecond_ticks; + if (duration_ticks > INT64_MAX) { + return -EINVAL; + } + deadline = timer_ticks + duration_ticks; + while ((int64_t) (timer_ticks - deadline) < 0) { + /* The current single foreground process sleeps inside its syscall. + * IRQ0 advances timer_ticks and wakes HLT; no scheduler is required. */ + __asm__ volatile("sti; hlt; cli" : : : "memory"); + } + if (remaining != 0) { + remaining->seconds = 0; + remaining->nanoseconds = 0; + } return 0; } @@ -1199,6 +1323,22 @@ void typephp_os_exception_dispatch(exception_frame *frame) restore_shell_after_fault(frame); } +void typephp_os_irq_dispatch(exception_frame *frame) +{ + switch (frame->vector) { + case 32: + ++timer_ticks; + pic_end_of_interrupt(0); + return; + case 36: + typephp_os_console_interrupt(); + pic_end_of_interrupt(4); + return; + default: + panic("unexpected hardware interrupt\n"); + } +} + long typephp_os_syscall_dispatch(syscall_frame *frame) { switch (frame->rax) { @@ -1255,6 +1395,9 @@ long typephp_os_syscall_dispatch(syscall_frame *frame) case TYPEPHP_SYS_ACCESS: return syscall_access_path(AT_FDCWD, (const char *) frame->rdi, (int) frame->rsi, 0); + case TYPEPHP_SYS_NANOSLEEP: + return syscall_nanosleep((const user_timespec *) frame->rdi, + (user_timespec *) frame->rsi); case TYPEPHP_SYS_GETPID: case TYPEPHP_SYS_GETTID: return (long) foreground_process.pid; diff --git a/examples/typephp-os/user/README.md b/examples/typephp-os/user/README.md index 3192d72f..94cbfba1 100644 --- a/examples/typephp-os/user/README.md +++ b/examples/typephp-os/user/README.md @@ -46,7 +46,7 @@ logic, while C/C++ is limited to the syscall or PHPX boundary. The current shared userspace runtime provides `syscall`, `read`, `write`, `openat`, `open`, `close`, `lseek`, `getcwd`, `chdir`, `mkdir`, `rmdir`, `unlink`, `rename`, `stat`, `lstat`, `fstat`, `access`, `fsync`, `fdatasync`, `truncate`, -`ftruncate`, `time`, `gettimeofday`, `clock_gettime`, `clock_getres`, `uname`, +`ftruncate`, `time`, `gettimeofday`, `clock_gettime`, `clock_getres`, `nanosleep`, `sleep`, `uname`, `getpid`, `getppid`, `gettid`, the root UID/GID queries, `brk`, `sbrk`, `mmap`, `mprotect`, `munmap`, `opendir`, `fdopendir`, `readdir`, `rewinddir`, `closedir`, `dirfd`, the `F_GETFD`/`F_SETFD`/`F_GETFL`/`F_SETFL` subset of @@ -85,7 +85,8 @@ The kernel additionally accepts Linux x86-64 `newfstatat`, `faccessat`, metadata uses the Linux x86-64 144-byte `struct stat` layout. FAT16 currently has no owners, ACLs, symlinks, executable file bit, or sub-second timestamps: UID/GID are always root, ordinary files are `0666`, -directories are `0777`, and clock resolution is one second. +directories are `0777`. Wall-clock values currently retain one-second RTC +resolution; the monotonic clock and sleeping use the 100 Hz PIT. `rename()` currently accepts only source and destination paths with the same parent directory and does not replace an existing entry. Its transport uses a diff --git a/examples/typephp-os/user/cmd/systest.c b/examples/typephp-os/user/cmd/systest.c index b5e1d7de..7f7ac154 100644 --- a/examples/typephp-os/user/cmd/systest.c +++ b/examples/typephp-os/user/cmd/systest.c @@ -39,7 +39,9 @@ int main(int argc, char **argv) struct timeval wall; struct timespec realtime; struct timespec monotonic; + struct timespec after_sleep; struct timespec resolution; + const struct timespec sleep_duration = {0, 30000000}; struct winsize window; DIR *directory; struct dirent *entry; @@ -113,10 +115,18 @@ int main(int argc, char **argv) || clock_gettime(CLOCK_REALTIME, &realtime) != 0 || clock_gettime(CLOCK_MONOTONIC, &monotonic) != 0 || clock_getres(CLOCK_MONOTONIC, &resolution) != 0 - || realtime.tv_sec <= 0 || monotonic.tv_sec <= 0 - || resolution.tv_sec != 1 || resolution.tv_nsec != 0) { + || realtime.tv_sec <= 0 || monotonic.tv_nsec < 0 + || monotonic.tv_nsec >= 1000000000L + || resolution.tv_sec != 0 || resolution.tv_nsec != 10000000L) { return fail("clock ABI"); } + if (nanosleep(&sleep_duration, NULL) != 0 + || clock_gettime(CLOCK_MONOTONIC, &after_sleep) != 0 + || after_sleep.tv_sec < monotonic.tv_sec + || (after_sleep.tv_sec == monotonic.tv_sec + && after_sleep.tv_nsec - monotonic.tv_nsec < 20000000L)) { + return fail("interruptible nanosleep"); + } (void) write(STDOUT_FILENO, "basic syscalls: OK\n", sizeof("basic syscalls: OK\n") - 1); diff --git a/examples/typephp-os/user/include/sys/syscall.h b/examples/typephp-os/user/include/sys/syscall.h index 8da9b579..01f2ae06 100644 --- a/examples/typephp-os/user/include/sys/syscall.h +++ b/examples/typephp-os/user/include/sys/syscall.h @@ -16,6 +16,7 @@ #define SYS_brk TYPEPHP_SYS_BRK #define SYS_ioctl TYPEPHP_SYS_IOCTL #define SYS_access TYPEPHP_SYS_ACCESS +#define SYS_nanosleep TYPEPHP_SYS_NANOSLEEP #define SYS_getpid TYPEPHP_SYS_GETPID #define SYS_exit TYPEPHP_SYS_EXIT #define SYS_uname TYPEPHP_SYS_UNAME diff --git a/examples/typephp-os/user/include/time.h b/examples/typephp-os/user/include/time.h index 13282d30..f50643cc 100644 --- a/examples/typephp-os/user/include/time.h +++ b/examples/typephp-os/user/include/time.h @@ -14,5 +14,6 @@ struct timespec { time_t time(time_t *result); int clock_gettime(clockid_t clock_id, struct timespec *value); int clock_getres(clockid_t clock_id, struct timespec *value); +int nanosleep(const struct timespec *duration, struct timespec *remaining); #endif diff --git a/examples/typephp-os/user/include/unistd.h b/examples/typephp-os/user/include/unistd.h index b7d64c6a..4908ff12 100644 --- a/examples/typephp-os/user/include/unistd.h +++ b/examples/typephp-os/user/include/unistd.h @@ -30,6 +30,7 @@ uid_t getuid(void); uid_t geteuid(void); gid_t getgid(void); gid_t getegid(void); +unsigned int sleep(unsigned int seconds); int brk(void *address); void *sbrk(intptr_t increment); void _exit(int status) __attribute__((noreturn)); diff --git a/examples/typephp-os/user/runtime/host.cc b/examples/typephp-os/user/runtime/host.cc index 1ff1cfdb..d3ffd455 100644 --- a/examples/typephp-os/user/runtime/host.cc +++ b/examples/typephp-os/user/runtime/host.cc @@ -4,6 +4,7 @@ #include #include #include +#include #include extern "C" int typephp_nano_project_main(); @@ -54,17 +55,23 @@ extern "C" void php_nano_host_system_time( extern "C" std::uint64_t php_nano_host_monotonic_nanoseconds() { - return static_cast(syscall(SYS_time, nullptr)) - * UINT64_C(1000000000); + timespec value{}; + if (clock_gettime(CLOCK_MONOTONIC, &value) != 0) { + typephp_os_panic("unable to read the monotonic clock"); + } + return static_cast(value.tv_sec) * UINT64_C(1000000000) + + static_cast(value.tv_nsec); } extern "C" void php_nano_host_sleep( std::uint64_t seconds, std::uint32_t nanoseconds) { - const auto now = static_cast(syscall(SYS_time, nullptr)); - const auto deadline = now + seconds + (nanoseconds != 0 ? 1u : 0u); - while (static_cast(syscall(SYS_time, nullptr)) < deadline) { - __asm__ volatile("pause"); + const timespec duration{ + static_cast(seconds), + static_cast(nanoseconds), + }; + if (nanosleep(&duration, nullptr) != 0) { + typephp_os_panic("unable to sleep"); } } diff --git a/examples/typephp-os/user/runtime/posix.c b/examples/typephp-os/user/runtime/posix.c index dc1236fa..28602ae5 100644 --- a/examples/typephp-os/user/runtime/posix.c +++ b/examples/typephp-os/user/runtime/posix.c @@ -142,18 +142,16 @@ int clock_getres(clockid_t clock_id, struct timespec *value) unsigned int sleep(unsigned int seconds) { - const time_t deadline = time(NULL) + seconds; - while (time(NULL) < deadline) { - __asm__ volatile("pause"); - } - return 0; + struct timespec duration = {(time_t) seconds, 0}; + struct timespec remaining = {0, 0}; + return nanosleep(&duration, &remaining) == 0 + ? 0 + : (unsigned int) remaining.tv_sec + (remaining.tv_nsec != 0); } int nanosleep(const struct timespec *duration, struct timespec *remaining) { - (void) remaining; - sleep((unsigned int) duration->tv_sec + (duration->tv_nsec != 0)); - return 0; + return (int) syscall(TYPEPHP_SYS_NANOSLEEP, duration, remaining); } int uname(struct utsname *value)