feat(typephp-os): add nano user mode implementation with updated memory layout

- Add crt0.S assembly startup code for user mode entry point
- Create hello.php example application with basic system information output
- Add hello.yml build configuration for nano user mode executable
- Implement host.cc with system call interfaces and runtime initialization
- Add linker script for proper user mode memory layout
- Update Makefile with new user base addresses for shell and commands
- Implement posix.c with POSIX compatibility layer using system calls
- Update process.c with new user memory range constants and stack sizes
- Add syscall.S assembly wrapper for Linux system call interface
- Modify vm.c with expanded user address space mapping and cleanup
- Update vmfault.c with correct shell memory address reference
master
韩天峰 4 weeks ago
parent d1085f68f8
commit ce4bcdda4f
  1. 4
      examples/typephp-os/Makefile
  2. 14
      examples/typephp-os/kernel/core/process.c
  3. 42
      examples/typephp-os/kernel/core/vm.c
  4. 23
      examples/typephp-os/user/nano/crt0.S
  5. 11
      examples/typephp-os/user/nano/hello.php
  6. 64
      examples/typephp-os/user/nano/hello.yml
  7. 84
      examples/typephp-os/user/nano/host.cc
  8. 28
      examples/typephp-os/user/nano/linker.ld
  9. 237
      examples/typephp-os/user/nano/posix.c
  10. 30
      examples/typephp-os/user/nano/syscall.S
  11. 2
      examples/typephp-os/user/vmfault.c

@ -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)

@ -20,15 +20,15 @@
#include <unistd.h>
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,

@ -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);

@ -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

@ -0,0 +1,11 @@
<?php
function main(int $argc, array $argv): void
{
echo "Hello World!";
var_dump(PHP_VERSION);
var_dump($argc);
var_dump($argv);
var_dump(date('Y-m-d H:i:s', time()));
var_dump(php_uname());
}

@ -0,0 +1,64 @@
name: typephp_os_user_hello
mode: bin
sources:
- hello.php
- host.cc
- posix.c
- ../../kernel/core/abi/libc.c
- ../../kernel/core/abi/cxx.cpp
- ../../kernel/core/abi/unimplemented.S
- ../../thirdparty/openlibm/src
objects:
- ../../build/objects/nano-crt0.o
- ../../build/objects/nano-syscall.o
build-dir: ../../build/generated-user-hello
output: ../../build/tnhello.elf
cxx-std: c++17
include-paths:
- ../../kernel/core/abi
- ../../thirdparty/openlibm/include
- ../../thirdparty/openlibm/src
- ../../thirdparty/openlibm/amd64
defines:
- TYPEPHP_NO_MAIN=1
- PHPX_NO_EXCEPTION=1
- PHPX_NO_RTTI=1
- PHP_NANO_NO_LIBC=1
- HAVE_SYS_UTSNAME_H=1
- __NO_CTYPE=1
- ZEND_MM_ERROR=0
- ZEND_MM_CUSTOM=0
- ASSEMBLER=1
- __BSD_VISIBLE=1
cxx-flags:
- -ffreestanding
- -fno-builtin
- -fno-stack-protector
- -fno-pic
- -fno-pie
- -m64
- -mno-red-zone
- -mcmodel=small
- -fno-rtti
- -fno-threadsafe-statics
c-flags:
- -ffreestanding
- -fno-builtin
- -fno-stack-protector
- -fno-pic
- -fno-pie
- -m64
- -mno-red-zone
- -mcmodel=small
asm-flags:
- -m64
- -mno-red-zone
- -mcmodel=small
ld-flags:
- -nostdlib
- -no-pie
- -Wl,-m,elf_x86_64
- -Wl,--build-id=none
- -Wl,-T,examples/typephp-os/user/nano/linker.ld
- -Wl,--defsym,USER_BASE=0x41000000
- -lgcc

@ -0,0 +1,84 @@
#include <php_nano_extension.h>
#include <typephp_os_abi.h>
#include <cstdint>
#include <cstring>
#include <sys/syscall.h>
#include <unistd.h>
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<std::int64_t>(syscall(SYS_time, nullptr));
*microseconds = 0;
}
extern "C" std::uint64_t php_nano_host_monotonic_nanoseconds()
{
return static_cast<std::uint64_t>(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<std::uint64_t>(syscall(SYS_time, nullptr));
const auto deadline = now + seconds + (nanoseconds != 0 ? 1u : 0u);
while (static_cast<std::uint64_t>(syscall(SYS_time, nullptr)) < deadline) {
__asm__ volatile("pause");
}
}
int main(int argc, char **argv)
{
const auto begin = static_cast<std::uintptr_t>(syscall(SYS_brk, 0));
const auto end = begin + arena_size;
if (begin == static_cast<std::uintptr_t>(-1)
|| end < begin
|| static_cast<std::uintptr_t>(syscall(SYS_brk, end)) != end) {
typephp_os_panic("unable to allocate the Nano runtime arena");
}
typephp_os_memory_init(reinterpret_cast<void *>(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;
}

@ -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*) }
}

@ -0,0 +1,237 @@
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdint.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/utsname.h>
#include <time.h>
#include <unistd.h>
#include <typephp_os_syscall.h>
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");
}

@ -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

@ -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;
}

Loading…
Cancel
Save