diff --git a/examples/typephp-os/Makefile b/examples/typephp-os/Makefile index 96547df5..6bbcd71d 100644 --- a/examples/typephp-os/Makefile +++ b/examples/typephp-os/Makefile @@ -72,10 +72,10 @@ $(OBJECT_DIR)/user-syscall.o: user/syscall.S kernel/core/abi/typephp_os_syscall. gcc $(USER_CFLAGS) -c $< -o $@ $(USER_SH_ELF): $(OBJECT_DIR)/sh.o $(USER_CRT0_OBJECT) $(USER_LIBC_OBJECTS) user/linker.ld | $(BUILD) - ld $(USER_LDFLAGS) --defsym USER_BASE=0x2000000 -o $@ $(filter %.o,$^) + ld $(USER_LDFLAGS) --defsym USER_BASE=0x40000000 -o $@ $(filter %.o,$^) $(USER_COMMAND_ELFS): $(BUILD)/%.elf: $(OBJECT_DIR)/%.o $(USER_CRT0_OBJECT) $(USER_LIBC_OBJECTS) user/linker.ld | $(BUILD) - ld $(USER_LDFLAGS) --defsym USER_BASE=0x2100000 -o $@ $(filter %.o,$^) + ld $(USER_LDFLAGS) --defsym USER_BASE=0x41000000 -o $@ $(filter %.o,$^) $(KERNEL): payload objcopy -O binary $(PAYLOAD_ELF) $(PAYLOAD_BIN) diff --git a/examples/typephp-os/kernel/core/process.c b/examples/typephp-os/kernel/core/process.c index 2230137b..3e1a07a1 100644 --- a/examples/typephp-os/kernel/core/process.c +++ b/examples/typephp-os/kernel/core/process.c @@ -20,15 +20,15 @@ #include enum { - USER_BEGIN = 32u * 1024u * 1024u, - USER_COMMAND_BEGIN = 33u * 1024u * 1024u, + USER_BEGIN = 0x40000000u, + USER_COMMAND_BEGIN = 0x41000000u, USER_SHELL_END = USER_COMMAND_BEGIN, - USER_COMMAND_END = 34u * 1024u * 1024u, - USER_END = 36u * 1024u * 1024u, + USER_COMMAND_END = 0x48000000u, + USER_END = 0x50000000u, USER_STACK = USER_END - 16u, - USER_COMMAND_STACK = 35u * 1024u * 1024u - 16u, - USER_STACK_SIZE = 64u * 1024u, - USER_HEAP_LIMIT = 34u * 1024u * 1024u, + USER_COMMAND_STACK = USER_END - 16u, + USER_STACK_SIZE = 1024u * 1024u, + USER_HEAP_LIMIT = 0x4c000000u, USER_MMAP_BEGIN = USER_HEAP_LIMIT, USER_CODE_SELECTOR = 0x23, USER_DATA_SELECTOR = 0x1b, diff --git a/examples/typephp-os/kernel/core/vm.c b/examples/typephp-os/kernel/core/vm.c index 0a056162..8f3ce8cf 100644 --- a/examples/typephp-os/kernel/core/vm.c +++ b/examples/typephp-os/kernel/core/vm.c @@ -18,12 +18,14 @@ enum { PAGE_USER = 4, PAGE_HUGE = 128, PAGE_OWNED = 512, - USER_FIRST_PDE = 16, - USER_LAST_PDE = 17, + USER_FIRST_PDE = 0, + USER_LAST_PDE = 127, }; #define PAGE_NX (UINT64_C(1) << 63) #define PAGE_ADDRESS UINT64_C(0x000ffffffffff000) +#define USER_ADDRESS_BEGIN UINT64_C(0x40000000) +#define USER_ADDRESS_END UINT64_C(0x50000000) extern uint64_t physical_page_allocate(void); extern void physical_page_free(uint64_t page); @@ -56,19 +58,22 @@ static uint64_t *page_directory(uint64_t address_space) return 0; } uint64_t *pdp = page_table(pml4[0]); - if ((pdp[0] & PAGE_PRESENT) == 0) { + if ((pdp[1] & PAGE_PRESENT) == 0) { return 0; } - return page_table(pdp[0]); + return page_table(pdp[1]); } uint64_t typephp_vm_create(void) { const uint64_t pml4_page = physical_page_allocate(); const uint64_t pdp_page = physical_page_allocate(); - const uint64_t pd_page = physical_page_allocate(); - if (pml4_page == 0 || pdp_page == 0 || pd_page == 0) { - if (pd_page != 0) physical_page_free(pd_page); + const uint64_t kernel_pd_page = physical_page_allocate(); + const uint64_t user_pd_page = physical_page_allocate(); + if (pml4_page == 0 || pdp_page == 0 + || kernel_pd_page == 0 || user_pd_page == 0) { + if (user_pd_page != 0) physical_page_free(user_pd_page); + if (kernel_pd_page != 0) physical_page_free(kernel_pd_page); if (pdp_page != 0) physical_page_free(pdp_page); if (pml4_page != 0) physical_page_free(pml4_page); return 0; @@ -76,14 +81,14 @@ uint64_t typephp_vm_create(void) uint64_t *pml4 = page_table(pml4_page); uint64_t *pdp = page_table(pdp_page); - uint64_t *pd = page_table(pd_page); + uint64_t *kernel_pd = page_table(kernel_pd_page); pml4[0] = pdp_page | PAGE_PRESENT | PAGE_WRITE | PAGE_USER; - pdp[0] = pd_page | PAGE_PRESENT | PAGE_WRITE | PAGE_USER; + pdp[0] = kernel_pd_page | PAGE_PRESENT | PAGE_WRITE; + pdp[1] = user_pd_page | PAGE_PRESENT | PAGE_WRITE | PAGE_USER; for (uint64_t index = 0; index < 512; ++index) { - pd[index] = index * UINT64_C(0x200000) | PAGE_PRESENT | PAGE_WRITE | PAGE_HUGE; + kernel_pd[index] = index * UINT64_C(0x200000) + | PAGE_PRESENT | PAGE_WRITE | PAGE_HUGE; } - pd[USER_FIRST_PDE] = 0; - pd[USER_LAST_PDE] = 0; return pml4_page; } @@ -106,7 +111,7 @@ int typephp_vm_map_user( uint64_t end; uint64_t *pd = page_directory(address_space); if (pd == 0 || !align_range(address, size, &begin, &end) - || begin < UINT64_C(0x2000000) || end > UINT64_C(0x2400000)) { + || begin < USER_ADDRESS_BEGIN || end > USER_ADDRESS_END) { return 0; } @@ -156,7 +161,7 @@ int typephp_vm_user_range_free( uint64_t end; uint64_t *pd = page_directory(address_space); if (pd == 0 || !align_range(address, size, &begin, &end) - || begin < UINT64_C(0x2000000) || end > UINT64_C(0x2400000)) { + || begin < USER_ADDRESS_BEGIN || end > USER_ADDRESS_END) { return 0; } for (uint64_t cursor = begin; cursor < end; cursor += PAGE_SIZE) { @@ -180,7 +185,7 @@ int typephp_vm_unmap_user( uint64_t end; uint64_t *pd = page_directory(address_space); if (pd == 0 || !align_range(address, size, &begin, &end) - || begin < UINT64_C(0x2000000) || end > UINT64_C(0x2400000)) { + || begin < USER_ADDRESS_BEGIN || end > USER_ADDRESS_END) { return 0; } for (uint64_t cursor = begin; cursor < end; cursor += PAGE_SIZE) { @@ -208,7 +213,7 @@ int typephp_vm_protect_user( uint64_t end; uint64_t *pd = page_directory(address_space); if (pd == 0 || !align_range(address, size, &begin, &end) - || begin < UINT64_C(0x2000000) || end > UINT64_C(0x2400000)) { + || begin < USER_ADDRESS_BEGIN || end > USER_ADDRESS_END) { return 0; } for (uint64_t cursor = begin; cursor < end; cursor += PAGE_SIZE) { @@ -244,7 +249,7 @@ int typephp_vm_user_range( return 1; } if (pd == 0 || !align_range(address, size, &begin, &end) - || begin < UINT64_C(0x2000000) || end > UINT64_C(0x2400000)) { + || begin < USER_ADDRESS_BEGIN || end > USER_ADDRESS_END) { return 0; } for (uint64_t cursor = begin; cursor < end; cursor += PAGE_SIZE) { @@ -267,7 +272,7 @@ void typephp_vm_destroy(uint64_t address_space) { uint64_t *pml4 = page_table(address_space); uint64_t *pdp = page_table(pml4[0]); - uint64_t *pd = page_table(pdp[0]); + uint64_t *pd = page_table(pdp[1]); for (uint64_t pd_index = USER_FIRST_PDE; pd_index <= USER_LAST_PDE; ++pd_index) { if ((pd[pd_index] & PAGE_PRESENT) == 0 || (pd[pd_index] & PAGE_HUGE) != 0) { continue; @@ -280,6 +285,7 @@ void typephp_vm_destroy(uint64_t address_space) } physical_page_free(pd[pd_index] & PAGE_ADDRESS); } + physical_page_free(pdp[1] & PAGE_ADDRESS); physical_page_free(pdp[0] & PAGE_ADDRESS); physical_page_free(pml4[0] & PAGE_ADDRESS); physical_page_free(address_space); diff --git a/examples/typephp-os/user/nano/crt0.S b/examples/typephp-os/user/nano/crt0.S new file mode 100644 index 00000000..09da672b --- /dev/null +++ b/examples/typephp-os/user/nano/crt0.S @@ -0,0 +1,23 @@ +.section .text.start, "ax" +.code64 + +.global _start +.type _start, @function +.extern main +_start: + # Linux-compatible process entry stack: + # argc, argv[], NULL, envp[], NULL, auxv[] + xor %ebp, %ebp + mov (%rsp), %rdi + lea 8(%rsp), %rsi + lea 16(%rsp,%rdi,8), %rdx + and $-16, %rsp + call main + + mov %eax, %edi + mov $60, %eax + int $0x80 + ud2 +.size _start, . - _start + +.section .note.GNU-stack, "", @progbits diff --git a/examples/typephp-os/user/nano/hello.php b/examples/typephp-os/user/nano/hello.php new file mode 100644 index 00000000..98c38b53 --- /dev/null +++ b/examples/typephp-os/user/nano/hello.php @@ -0,0 +1,11 @@ + +#include + +#include +#include +#include +#include + +extern "C" int typephp_nano_project_main(); + +namespace { + +constexpr std::uintptr_t arena_size = 24u * 1024u * 1024u; + +[[noreturn]] void raw_exit(int status) +{ + (void) syscall(SYS_exit, status); + for (;;) { + __asm__ volatile("pause"); + } +} + +} // namespace + +extern "C" void typephp_os_write(const char *data, size_t size) +{ + (void) syscall(SYS_write, STDOUT_FILENO, data, size); +} + +extern "C" void typephp_os_panic(const char *message) +{ + static constexpr char prefix[] = "TypePHP user panic: "; + typephp_os_write(prefix, sizeof(prefix) - 1); + typephp_os_write(message, std::strlen(message)); + typephp_os_write("\n", 1); + raw_exit(127); +} + +extern "C" void phpx_no_exception_abort(const char *message) +{ + typephp_os_panic(message); +} + +extern "C" void php_nano_host_system_time( + std::int64_t *seconds, std::int32_t *microseconds) +{ + *seconds = static_cast(syscall(SYS_time, nullptr)); + *microseconds = 0; +} + +extern "C" std::uint64_t php_nano_host_monotonic_nanoseconds() +{ + return static_cast(syscall(SYS_time, nullptr)) + * UINT64_C(1000000000); +} + +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"); + } +} + +int main(int argc, char **argv) +{ + const auto begin = static_cast(syscall(SYS_brk, 0)); + const auto end = begin + arena_size; + if (begin == static_cast(-1) + || end < begin + || static_cast(syscall(SYS_brk, end)) != end) { + typephp_os_panic("unable to allocate the Nano runtime arena"); + } + typephp_os_memory_init(reinterpret_cast(begin), arena_size); + php_nano_set_cli_arguments(argc, argv); + if (php_nano_startup_composer_extensions() != SUCCESS) { + typephp_os_panic("unable to start PHP Nano extensions"); + } + const int status = typephp_nano_project_main(); + php_nano_shutdown_composer_extensions(); + return status; +} diff --git a/examples/typephp-os/user/nano/linker.ld b/examples/typephp-os/user/nano/linker.ld new file mode 100644 index 00000000..95020799 --- /dev/null +++ b/examples/typephp-os/user/nano/linker.ld @@ -0,0 +1,28 @@ +OUTPUT_FORMAT(elf64-x86-64) +OUTPUT_ARCH(i386:x86-64) +ENTRY(_start) + +PHDRS +{ + text PT_LOAD FILEHDR PHDRS FLAGS(5); + data PT_LOAD FLAGS(6); + stack PT_GNU_STACK FLAGS(6); +} + +SECTIONS +{ + . = USER_BASE + SIZEOF_HEADERS; + + .text : + { + KEEP(*(.text.start)) + *(.text .text.*) + } :text + .rodata : { *(.rodata .rodata.*) } :text + + . = ALIGN(4K); + .data : { *(.data .data.*) } :data + .bss (NOLOAD) : { *(COMMON) *(.bss .bss.*) } :data + + /DISCARD/ : { *(.comment) *(.eh_frame*) *(.note*) } +} diff --git a/examples/typephp-os/user/nano/posix.c b/examples/typephp-os/user/nano/posix.c new file mode 100644 index 00000000..2a260cb7 --- /dev/null +++ b/examples/typephp-os/user/nano/posix.c @@ -0,0 +1,237 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static __attribute__((noreturn)) void unsupported(const char *name) +{ + static const char prefix[] = "unsupported TypePHP-OS user ABI: "; + (void) syscall(TYPEPHP_SYS_WRITE, STDERR_FILENO, prefix, sizeof(prefix) - 1); + (void) syscall(TYPEPHP_SYS_WRITE, STDERR_FILENO, name, strlen(name)); + (void) syscall(TYPEPHP_SYS_WRITE, STDERR_FILENO, "\n", 1); + (void) syscall(TYPEPHP_SYS_EXIT, 126); + for (;;) { + __asm__ volatile("pause"); + } +} + +ssize_t read(int fd, void *buffer, size_t count) +{ + return (ssize_t) syscall(TYPEPHP_SYS_READ, fd, buffer, count); +} + +ssize_t write(int fd, const void *buffer, size_t count) +{ + return (ssize_t) syscall(TYPEPHP_SYS_WRITE, fd, buffer, count); +} + +int close(int fd) +{ + return (int) syscall(TYPEPHP_SYS_CLOSE, fd); +} + +off_t lseek(int fd, off_t offset, int whence) +{ + return (off_t) syscall(TYPEPHP_SYS_LSEEK, fd, offset, whence); +} + +int openat(int directory_fd, const char *path, int flags, ...) +{ + return (int) syscall(TYPEPHP_SYS_OPENAT, directory_fd, path, flags, 0); +} + +int open(const char *path, int flags, ...) +{ + return openat(AT_FDCWD, path, flags, 0); +} + +int chdir(const char *path) +{ + return (int) syscall(TYPEPHP_SYS_CHDIR, path); +} + +int mkdir(const char *path, mode_t mode) +{ + return (int) syscall(TYPEPHP_SYS_MKDIR, path, mode); +} + +int unlink(const char *path) +{ + return (int) syscall(TYPEPHP_SYS_UNLINK, path); +} + +int rmdir(const char *path) +{ + return (int) syscall(TYPEPHP_SYS_RMDIR, path); +} + +int brk(void *address) +{ + return (void *) syscall(TYPEPHP_SYS_BRK, address) == address ? 0 : -1; +} + +void *sbrk(intptr_t increment) +{ + const intptr_t current = syscall(TYPEPHP_SYS_BRK, 0); + const intptr_t requested = current + increment; + if ((increment > 0 && requested < current) + || (increment < 0 && requested > current) + || (increment != 0 && syscall(TYPEPHP_SYS_BRK, requested) != requested)) { + errno = ENOMEM; + return (void *) -1; + } + return (void *) current; +} + +void *mmap(void *address, size_t length, int protection, int flags, int fd, off_t offset) +{ + return (void *) syscall(TYPEPHP_SYS_MMAP, + address, length, protection, flags, fd, offset); +} + +int mprotect(void *address, size_t length, int protection) +{ + return (int) syscall(TYPEPHP_SYS_MPROTECT, address, length, protection); +} + +int munmap(void *address, size_t length) +{ + return (int) syscall(TYPEPHP_SYS_MUNMAP, address, length); +} + +time_t time(time_t *result) +{ + return (time_t) syscall(TYPEPHP_SYS_TIME, result); +} + +int gettimeofday(struct timeval *value, void *timezone) +{ + (void) timezone; + value->tv_sec = time(NULL); + value->tv_usec = 0; + return 0; +} + +int clock_gettime(clockid_t clock_id, struct timespec *value) +{ + (void) clock_id; + value->tv_sec = time(NULL); + value->tv_nsec = 0; + return 0; +} + +unsigned int sleep(unsigned int seconds) +{ + const time_t deadline = time(NULL) + seconds; + while (time(NULL) < deadline) { + __asm__ volatile("pause"); + } + return 0; +} + +int nanosleep(const struct timespec *duration, struct timespec *remaining) +{ + (void) remaining; + sleep((unsigned int) duration->tv_sec + (duration->tv_nsec != 0)); + return 0; +} + +int uname(struct utsname *value) +{ + if (value == NULL) { + errno = EFAULT; + return -1; + } + memset(value, 0, sizeof(*value)); + memcpy(value->sysname, "TypePHP-OS", sizeof("TypePHP-OS")); + memcpy(value->nodename, "typephp-os", sizeof("typephp-os")); + memcpy(value->release, "0.1", sizeof("0.1")); + memcpy(value->version, "TypePHP Nano user mode", sizeof("TypePHP Nano user mode")); + memcpy(value->machine, "x86_64", sizeof("x86_64")); + return 0; +} + +int isatty(int fd) +{ + return fd >= STDIN_FILENO && fd <= STDERR_FILENO; +} + +int rename(const char *old_path, const char *new_path) +{ + return (int) syscall(TYPEPHP_SYS_RENAME, old_path, new_path); +} + +int access(const char *path, int mode) +{ + (void) path; + (void) mode; + unsupported("access"); +} + +int stat(const char *path, struct stat *value) +{ + (void) path; + (void) value; + unsupported("stat"); +} + +int lstat(const char *path, struct stat *value) +{ + (void) path; + (void) value; + unsupported("lstat"); +} + +int fstat(int fd, struct stat *value) +{ + (void) fd; + (void) value; + unsupported("fstat"); +} + +DIR *opendir(const char *path) +{ + (void) path; + unsupported("opendir"); +} + +struct dirent *readdir(DIR *directory) +{ + (void) directory; + unsupported("readdir"); +} + +int closedir(DIR *directory) +{ + (void) directory; + unsupported("closedir"); +} + +void rewinddir(DIR *directory) +{ + (void) directory; + unsupported("rewinddir"); +} + +int fsync(int fd) +{ + (void) fd; + unsupported("fsync"); +} + +int ftruncate(int fd, off_t length) +{ + (void) fd; + (void) length; + unsupported("ftruncate"); +} diff --git a/examples/typephp-os/user/nano/syscall.S b/examples/typephp-os/user/nano/syscall.S new file mode 100644 index 00000000..bd224581 --- /dev/null +++ b/examples/typephp-os/user/nano/syscall.S @@ -0,0 +1,30 @@ +.section .text +.code64 + +.global syscall +.type syscall, @function +.extern __errno_location +syscall: + # Translate the System V variadic-call ABI to Linux x86-64 syscall + # registers. TypePHP-OS currently enters the kernel through int 0x80. + mov %rdi, %rax + mov %rsi, %rdi + mov %rdx, %rsi + mov %rcx, %rdx + mov %r8, %r10 + mov %r9, %r8 + mov 8(%rsp), %r9 + int $0x80 + cmp $-4095, %rax + jb 1f + neg %eax + push %rax + call __errno_location + pop %rdx + mov %edx, (%rax) + mov $-1, %rax +1: + ret +.size syscall, . - syscall + +.section .note.GNU-stack, "", @progbits diff --git a/examples/typephp-os/user/vmfault.c b/examples/typephp-os/user/vmfault.c index 0abbd5fd..f1b1225c 100644 --- a/examples/typephp-os/user/vmfault.c +++ b/examples/typephp-os/user/vmfault.c @@ -4,6 +4,6 @@ int main(void) { /* The resident shell is linked at 32 MiB. A command has a distinct CR3, * so this address must be unmapped even though the shell remains alive. */ - volatile const uint8_t *shell_memory = (const uint8_t *) UINT64_C(0x2000000); + volatile const uint8_t *shell_memory = (const uint8_t *) UINT64_C(0x40000000); return *shell_memory; }