feat(kernel): add date and pwd user commands with syscall improvements

- Add date command implementation that displays current time in UTC format
- Add pwd command to print current working directory
- Update Makefile to build date and pwd as user programs with shared ELF loading
- Refactor syscall handling to use shared header definitions
- Remove KernelGreeting class from kernel startup sequence
- Update README and ROADMAP documentation for new user commands
- Add test assertions for date and pwd command output in test suite
- Introduce reusable command address space management in process.c
master
韩天峰 4 weeks ago
parent 0cfa577db1
commit 66184e775c
  1. 40
      examples/typephp-os/Makefile
  2. 29
      examples/typephp-os/README.md
  3. 5
      examples/typephp-os/ROADMAP.md
  4. 18
      examples/typephp-os/freestanding/abi/typephp_os_syscall.h
  5. 194
      examples/typephp-os/freestanding/process.c
  6. 3
      examples/typephp-os/project.yml
  7. 10
      examples/typephp-os/src/kernel.php
  8. 6
      examples/typephp-os/tools/test-qemu.sh
  9. 82
      examples/typephp-os/user/date.c
  10. 14
      examples/typephp-os/user/pwd.c
  11. 6
      examples/typephp-os/user/sh.c
  12. 12
      examples/typephp-os/user/syscall.h

@ -6,15 +6,16 @@ PAYLOAD_BIN := $(BUILD)/kernel64.bin
PAYLOAD_OBJECT := $(OBJECT_DIR)/kernel64-payload.o PAYLOAD_OBJECT := $(OBJECT_DIR)/kernel64-payload.o
KERNEL := $(BUILD)/typephp-os.elf KERNEL := $(BUILD)/typephp-os.elf
DISK := $(BUILD)/typephp-os.img DISK := $(BUILD)/typephp-os.img
USER_COMMANDS := ls cd date pwd fault
USER_PROGRAMS := sh $(USER_COMMANDS)
USER_SH_ELF := $(BUILD)/sh.elf USER_SH_ELF := $(BUILD)/sh.elf
USER_LS_ELF := $(BUILD)/ls.elf USER_COMMAND_ELFS := $(addprefix $(BUILD)/,$(addsuffix .elf,$(USER_COMMANDS)))
USER_CD_ELF := $(BUILD)/cd.elf USER_IMAGES := $(addprefix $(OBJECT_DIR)/,$(addsuffix -image.o,$(USER_PROGRAMS)))
USER_IMAGES := $(OBJECT_DIR)/sh-image.o $(OBJECT_DIR)/ls-image.o $(OBJECT_DIR)/cd-image.o
BOOTFLAGS := -m32 -O2 -ffreestanding -fno-pic -fno-pie \ BOOTFLAGS := -m32 -O2 -ffreestanding -fno-pic -fno-pie \
-fno-stack-protector -Wall -Wextra -fno-stack-protector -Wall -Wextra
USER_CFLAGS := -m64 -O2 -ffreestanding -fno-builtin -fno-pic -fno-pie \ USER_CFLAGS := -m64 -O2 -ffreestanding -fno-builtin -fno-pic -fno-pie \
-fno-stack-protector -mno-red-zone -Wall -Wextra -Iuser -fno-stack-protector -mno-red-zone -Wall -Wextra -Iuser -Ifreestanding/abi
USER_LDFLAGS := -m elf_x86_64 -nostdlib -static -T user/linker.ld USER_LDFLAGS := -m elf_x86_64 -nostdlib -static -T user/linker.ld
.PHONY: all payload objects disk run test clean .PHONY: all payload objects disk run test clean
@ -43,41 +44,20 @@ $(OBJECT_DIR):
$(BOOTSTRAP_OBJECT): freestanding/boot.S | $(OBJECT_DIR) $(BOOTSTRAP_OBJECT): freestanding/boot.S | $(OBJECT_DIR)
gcc $(BOOTFLAGS) -c $< -o $@ gcc $(BOOTFLAGS) -c $< -o $@
$(OBJECT_DIR)/sh.o: user/sh.c user/syscall.h | $(OBJECT_DIR) $(OBJECT_DIR)/%.o: user/%.c user/syscall.h freestanding/abi/typephp_os_syscall.h | $(OBJECT_DIR)
gcc $(USER_CFLAGS) -c $< -o $@
$(OBJECT_DIR)/ls.o: user/ls.c user/syscall.h | $(OBJECT_DIR)
gcc $(USER_CFLAGS) -c $< -o $@
$(OBJECT_DIR)/cd.o: user/cd.c user/syscall.h | $(OBJECT_DIR)
gcc $(USER_CFLAGS) -c $< -o $@ gcc $(USER_CFLAGS) -c $< -o $@
$(USER_SH_ELF): $(OBJECT_DIR)/sh.o user/linker.ld | $(BUILD) $(USER_SH_ELF): $(OBJECT_DIR)/sh.o user/linker.ld | $(BUILD)
ld $(USER_LDFLAGS) --defsym USER_BASE=0x2000000 -o $@ $< ld $(USER_LDFLAGS) --defsym USER_BASE=0x2000000 -o $@ $<
$(USER_LS_ELF): $(OBJECT_DIR)/ls.o user/linker.ld | $(BUILD) $(USER_COMMAND_ELFS): $(BUILD)/%.elf: $(OBJECT_DIR)/%.o user/linker.ld | $(BUILD)
ld $(USER_LDFLAGS) --defsym USER_BASE=0x2100000 -o $@ $< ld $(USER_LDFLAGS) --defsym USER_BASE=0x2100000 -o $@ $<
$(USER_CD_ELF): $(OBJECT_DIR)/cd.o user/linker.ld | $(BUILD) $(OBJECT_DIR)/%-image.o: $(BUILD)/%.elf | $(OBJECT_DIR)
ld $(USER_LDFLAGS) --defsym USER_BASE=0x2200000 -o $@ $<
$(OBJECT_DIR)/sh-image.o: $(USER_SH_ELF) | $(OBJECT_DIR)
objcopy -I binary -O elf64-x86-64 -B i386:x86-64 \
--rename-section .data=.rodata.user_elf,alloc,load,readonly,data,contents \
--redefine-sym _binary_build_sh_elf_start=typephp_user_sh_elf_start \
--redefine-sym _binary_build_sh_elf_end=typephp_user_sh_elf_end $< $@
$(OBJECT_DIR)/ls-image.o: $(USER_LS_ELF) | $(OBJECT_DIR)
objcopy -I binary -O elf64-x86-64 -B i386:x86-64 \
--rename-section .data=.rodata.user_elf,alloc,load,readonly,data,contents \
--redefine-sym _binary_build_ls_elf_start=typephp_user_ls_elf_start \
--redefine-sym _binary_build_ls_elf_end=typephp_user_ls_elf_end $< $@
$(OBJECT_DIR)/cd-image.o: $(USER_CD_ELF) | $(OBJECT_DIR)
objcopy -I binary -O elf64-x86-64 -B i386:x86-64 \ objcopy -I binary -O elf64-x86-64 -B i386:x86-64 \
--rename-section .data=.rodata.user_elf,alloc,load,readonly,data,contents \ --rename-section .data=.rodata.user_elf,alloc,load,readonly,data,contents \
--redefine-sym _binary_build_cd_elf_start=typephp_user_cd_elf_start \ --redefine-sym _binary_build_$*_elf_start=typephp_user_$*_elf_start \
--redefine-sym _binary_build_cd_elf_end=typephp_user_cd_elf_end $< $@ --redefine-sym _binary_build_$*_elf_end=typephp_user_$*_elf_end $< $@
$(KERNEL): payload $(KERNEL): payload
objcopy -O binary $(PAYLOAD_ELF) $(PAYLOAD_BIN) objcopy -O binary $(PAYLOAD_ELF) $(PAYLOAD_BIN)

@ -6,8 +6,9 @@ PHP, libc, or libstdc++. TypePHP implements the startup self-check, a custom
Zend class, the prime-number demo, and filesystem services. Small C and Zend class, the prime-number demo, and filesystem services. Small C and
assembly layers provide the machine bootstrap, current kernel services, and assembly layers provide the machine bootstrap, current kernel services, and
the temporary freestanding userspace programs. the temporary freestanding userspace programs.
The first userspace programs (`sh`, `ls`, and `cd`) are deliberately written The first userspace programs (`sh`, `ls`, `cd`, `pwd`, and `date`) are
in freestanding C until tpc can target this small userspace ABI. deliberately written in freestanding C until tpc can target this small
userspace ABI.
The architectural rules and staged plan are maintained in The architectural rules and staged plan are maintained in
[ROADMAP.md](ROADMAP.md). [ROADMAP.md](ROADMAP.md).
@ -62,8 +63,8 @@ The build produces these useful files:
tpc; tpc;
- `build/typephp-os.elf`: the final Multiboot kernel accepted by QEMU; - `build/typephp-os.elf`: the final Multiboot kernel accepted by QEMU;
- `build/typephp-os.img`: a persistent 32 MiB FAT16 disk image. - `build/typephp-os.img`: a persistent 32 MiB FAT16 disk image.
- `build/sh.elf`, `build/ls.elf`, and `build/cd.elf`: independent ELF64 Ring-3 - `build/sh.elf`, `build/ls.elf`, `build/cd.elf`, `build/pwd.elf`, and
programs built without libc. `build/date.elf`: independent ELF64 Ring-3 programs built without libc.
Run the automated serial-output smoke test with: Run the automated serial-output smoke test with:
@ -73,7 +74,7 @@ make test
The Makefile compiles the 32-bit Multiboot bootstrap externally because its The Makefile compiles the 32-bit Multiboot bootstrap externally because its
`-m32` ABI cannot participate in the 64-bit payload link. It also builds the `-m32` ABI cannot participate in the 64-bit payload link. It also builds the
three deliberately small freestanding C userspace ELF files. All ordinary five deliberately small freestanding C userspace ELF files. All ordinary
64-bit `.c`, `.cc`, and `.S` files remain in `project.yml` and use tpc's generic 64-bit `.c`, `.cc`, and `.S` files remain in `project.yml` and use tpc's generic
`c-flags`, `cxx-flags`, and `asm-flags`. Generic same-ABI prebuilt objects can `c-flags`, `cxx-flags`, and `asm-flags`. Generic same-ABI prebuilt objects can
be supplied with `objects`; this is how read-only copies of the user ELF files be supplied with `objects`; this is how read-only copies of the user ELF files
@ -139,12 +140,15 @@ After the TypePHP self-check, the kernel validates and loads `sh.elf`, installs
a 64-bit TSS and an IDT gate, and enters Ring 3 with `iretq`. The user pages at a 64-bit TSS and an IDT gate, and enters Ring 3 with `iretq`. The user pages at
32-36 MiB are marked user-accessible while kernel pages remain supervisor-only. 32-36 MiB are marked user-accessible while kernel pages remain supervisor-only.
An `int 0x80` boundary currently provides synchronous `read`, `write`, `exec`, An `int 0x80` boundary currently provides synchronous `read`, `write`, `exec`,
`exit`, `getcwd`, `chdir`, and directory-list operations. Standard input and `exit`, `getcwd`, `chdir`, `time`, and directory-list operations. Standard
output are backed by QEMU's COM1 serial console. 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 shell synchronously executes the separate `ls.elf` and `cd.elf` images.
Only one user context runs at a time: while a command is active, the kernel The shell synchronously executes separate `ls.elf`, `cd.elf`, `pwd.elf`, and
keeps the shell register frame and restores it when the command calls `exit`. `date.elf` images. Short-lived commands reuse one ELF load address; only the
resident shell has a separate image and stack. Only one user context runs at a
time: while a command is active, the kernel keeps the shell register frame and
restores it when the command calls `exit`.
Because this is a deliberately single-task model, the working directory is a Because this is a deliberately single-task model, the working directory is a
session-global property; a successful `cd` therefore remains visible after session-global property; a successful `cd` therefore remains visible after
control returns to the shell. control returns to the shell.
@ -153,7 +157,10 @@ Available commands are:
```text ```text
ls ls
date
pwd
cd DOCS cd DOCS
pwd
ls ls
cd .. cd ..
``` ```

@ -43,8 +43,9 @@ behavior.
nested directories and long filenames are next. nested directories and long filenames are next.
6. **Single-task userspace — first slice complete.** ELF64 validation/loading, 6. **Single-task userspace — first slice complete.** ELF64 validation/loading,
supervisor/user page separation, GDT/TSS, Ring-3 entry, synchronous supervisor/user page separation, GDT/TSS, Ring-3 entry, synchronous
`int 0x80` system calls, COM1 standard I/O, saved parent context, and `int 0x80` system calls, COM1 standard I/O, saved parent context, shared
independent freestanding C `sh`, `ls`, and `cd` programs. syscall definitions, reusable command address space, and independent
freestanding C `sh`, `ls`, `cd`, `pwd`, and `date` programs.
7. **Native Class memory.** Exercise Wren GC through Zend MM and verify tracing 7. **Native Class memory.** Exercise Wren GC through Zend MM and verify tracing
of PHPX fields under sustained allocation. of PHPX fields under sustained allocation.
8. **Kernel services.** Interrupt-driven timer, keyboard, physical-page 8. **Kernel services.** Interrupt-driven timer, keyboard, physical-page

@ -0,0 +1,18 @@
#ifndef TYPEPHP_OS_SYSCALL_H
#define TYPEPHP_OS_SYSCALL_H
/* Keep familiar Linux x86_64 numbers where the operation has a close match.
* READDIR is currently a compact TypePHP-OS directory-list operation rather
* than Linux getdents64's binary record ABI. */
enum typephp_os_syscall_number {
TYPEPHP_SYS_READ = 0,
TYPEPHP_SYS_WRITE = 1,
TYPEPHP_SYS_EXEC = 59,
TYPEPHP_SYS_EXIT = 60,
TYPEPHP_SYS_GETCWD = 79,
TYPEPHP_SYS_CHDIR = 80,
TYPEPHP_SYS_TIME = 201,
TYPEPHP_SYS_READDIR = 217,
};
#endif

@ -8,6 +8,7 @@
*/ */
#include "typephp_os_abi.h" #include "typephp_os_abi.h"
#include "typephp_os_syscall.h"
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
@ -22,13 +23,6 @@ enum {
KERNEL_CODE_SELECTOR = 0x08, KERNEL_CODE_SELECTOR = 0x08,
TSS_SELECTOR = 0x28, TSS_SELECTOR = 0x28,
IDT_SYSCALL = 0x80, IDT_SYSCALL = 0x80,
SYS_READ = 0,
SYS_WRITE = 1,
SYS_EXEC = 59,
SYS_EXIT = 60,
SYS_GETCWD = 79,
SYS_CHDIR = 80,
SYS_READDIR = 217,
}; };
enum { enum {
@ -115,6 +109,31 @@ typedef struct {
uint64_t ss; uint64_t ss;
} syscall_frame; } syscall_frame;
typedef struct {
uint64_t rax;
uint64_t r15;
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t r11;
uint64_t r10;
uint64_t r9;
uint64_t r8;
uint64_t rbp;
uint64_t rdi;
uint64_t rsi;
uint64_t rdx;
uint64_t rcx;
uint64_t rbx;
uint64_t vector;
uint64_t error;
uint64_t rip;
uint64_t cs;
uint64_t rflags;
uint64_t rsp;
uint64_t ss;
} exception_frame;
typedef struct { typedef struct {
uint64_t pid; uint64_t pid;
int running; int running;
@ -129,8 +148,24 @@ extern const unsigned char typephp_user_ls_elf_start[];
extern const unsigned char typephp_user_ls_elf_end[]; extern const unsigned char typephp_user_ls_elf_end[];
extern const unsigned char typephp_user_cd_elf_start[]; extern const unsigned char typephp_user_cd_elf_start[];
extern const unsigned char typephp_user_cd_elf_end[]; extern const unsigned char typephp_user_cd_elf_end[];
extern const unsigned char typephp_user_date_elf_start[];
extern const unsigned char typephp_user_date_elf_end[];
extern const unsigned char typephp_user_pwd_elf_start[];
extern const unsigned char typephp_user_pwd_elf_end[];
extern const unsigned char typephp_user_fault_elf_start[];
extern const unsigned char typephp_user_fault_elf_end[];
extern void typephp_os_syscall_entry(void); extern void typephp_os_syscall_entry(void);
extern void typephp_os_enter_user(uint64_t entry, uint64_t stack); extern void typephp_os_enter_user(uint64_t entry, uint64_t stack);
extern long typephp_os_time_seconds(void);
extern void typephp_os_exception_0(void);
extern void typephp_os_exception_3(void);
extern void typephp_os_exception_5(void);
extern void typephp_os_exception_6(void);
extern void typephp_os_exception_10(void);
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);
static uint64_t gdt[7] __attribute__((aligned(16))); static uint64_t gdt[7] __attribute__((aligned(16)));
static idt_gate idt[256] __attribute__((aligned(16))); static idt_gate idt[256] __attribute__((aligned(16)));
@ -176,10 +211,19 @@ static void install_tss_descriptor(uint64_t base, uint32_t limit)
gdt[6] = base >> 32u; gdt[6] = base >> 32u;
} }
static void install_idt_gate(unsigned int vector, void (*entry)(void), uint8_t attributes)
{
uintptr_t handler = (uintptr_t) entry;
idt[vector].offset_low = handler & 0xffffu;
idt[vector].selector = KERNEL_CODE_SELECTOR;
idt[vector].attributes = attributes;
idt[vector].offset_middle = (handler >> 16u) & 0xffffu;
idt[vector].offset_high = handler >> 32u;
}
static void install_descriptor_tables(void) static void install_descriptor_tables(void)
{ {
descriptor_pointer pointer; descriptor_pointer pointer;
uintptr_t handler = (uintptr_t) typephp_os_syscall_entry;
memset(gdt, 0, sizeof(gdt)); memset(gdt, 0, sizeof(gdt));
gdt[1] = UINT64_C(0x00af9a000000ffff); gdt[1] = UINT64_C(0x00af9a000000ffff);
@ -205,11 +249,16 @@ static void install_descriptor_tables(void)
: "rax", "memory"); : "rax", "memory");
memset(idt, 0, sizeof(idt)); memset(idt, 0, sizeof(idt));
idt[IDT_SYSCALL].offset_low = handler & 0xffffu; install_idt_gate(0, typephp_os_exception_0, 0x8e);
idt[IDT_SYSCALL].selector = KERNEL_CODE_SELECTOR; install_idt_gate(3, typephp_os_exception_3, 0xee);
idt[IDT_SYSCALL].attributes = 0xee; /* present, DPL 3, interrupt gate */ install_idt_gate(5, typephp_os_exception_5, 0x8e);
idt[IDT_SYSCALL].offset_middle = (handler >> 16u) & 0xffffu; install_idt_gate(6, typephp_os_exception_6, 0x8e);
idt[IDT_SYSCALL].offset_high = handler >> 32u; install_idt_gate(10, typephp_os_exception_10, 0x8e);
install_idt_gate(11, typephp_os_exception_11, 0x8e);
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(IDT_SYSCALL, typephp_os_syscall_entry, 0xee);
pointer.limit = sizeof(idt) - 1u; pointer.limit = sizeof(idt) - 1u;
pointer.base = (uint64_t) (uintptr_t) idt; pointer.base = (uint64_t) (uintptr_t) idt;
__asm__ volatile("lidt %0" : : "m"(pointer) : "memory"); __asm__ volatile("lidt %0" : : "m"(pointer) : "memory");
@ -272,6 +321,21 @@ static int command_image(
*image_end = typephp_user_cd_elf_end; *image_end = typephp_user_cd_elf_end;
return 1; return 1;
} }
if (strcmp(name, "date") == 0) {
*image = typephp_user_date_elf_start;
*image_end = typephp_user_date_elf_end;
return 1;
}
if (strcmp(name, "pwd") == 0) {
*image = typephp_user_pwd_elf_start;
*image_end = typephp_user_pwd_elf_end;
return 1;
}
if (strcmp(name, "fault") == 0) {
*image = typephp_user_fault_elf_start;
*image_end = typephp_user_fault_elf_end;
return 1;
}
return 0; return 0;
} }
@ -380,31 +444,121 @@ static long syscall_exit(syscall_frame *frame, long status)
return status; return status;
} }
static void write_unsigned(uint64_t value)
{
char digits[20];
size_t count = 0;
do {
digits[count++] = (char) ('0' + value % 10u);
value /= 10u;
} while (value != 0);
while (count != 0) {
--count;
typephp_os_write(&digits[count], 1);
}
}
static void write_hex(uint64_t value)
{
static const char digits[] = "0123456789abcdef";
char output[18];
int index;
output[0] = '0';
output[1] = 'x';
for (index = 0; index < 16; ++index) {
output[index + 2] = digits[(value >> ((15 - index) * 4)) & 0x0fu];
}
typephp_os_write(output, sizeof(output));
}
static const char *exception_name(uint64_t vector)
{
switch (vector) {
case 0: return "divide error";
case 3: return "breakpoint";
case 5: return "bounds";
case 6: return "invalid opcode";
case 10: return "invalid TSS";
case 11: return "segment not present";
case 12: return "stack fault";
case 13: return "general protection";
case 14: return "page fault";
default: return "unknown";
}
}
static void restore_shell_after_fault(exception_frame *frame)
{
syscall_frame *parent = &foreground_process.parent_frame;
/* The general-register prefixes of both frame formats are identical. */
memcpy(frame, parent, offsetof(syscall_frame, rip));
frame->rax = (uint64_t) -1;
frame->rip = parent->rip;
frame->cs = parent->cs;
frame->rflags = parent->rflags;
frame->rsp = parent->rsp;
frame->ss = parent->ss;
foreground_process.parent_waiting = 0;
foreground_process.pid = 1;
}
void typephp_os_exception_dispatch(exception_frame *frame)
{
uint64_t fault_address = 0;
if (frame->vector == 14) {
__asm__ volatile("mov %%cr2, %0" : "=r"(fault_address));
}
typephp_os_write("User process ", sizeof("User process ") - 1);
write_unsigned(foreground_process.pid);
typephp_os_write(" fault: ", sizeof(" fault: ") - 1);
typephp_os_write(exception_name(frame->vector), strlen(exception_name(frame->vector)));
typephp_os_write(" (#", sizeof(" (#") - 1);
write_unsigned(frame->vector);
typephp_os_write(") at ", sizeof(") at ") - 1);
write_hex(frame->rip);
if (frame->vector == 14) {
typephp_os_write(", address ", sizeof(", address ") - 1);
write_hex(fault_address);
}
typephp_os_write("\n", 1);
if ((frame->cs & 3u) != 3u) {
panic("kernel-mode exception\n");
}
if (!foreground_process.parent_waiting) {
panic("resident shell faulted\n");
}
restore_shell_after_fault(frame);
}
long typephp_os_syscall_dispatch(syscall_frame *frame) long typephp_os_syscall_dispatch(syscall_frame *frame)
{ {
switch (frame->rax) { switch (frame->rax) {
case SYS_READ: case TYPEPHP_SYS_READ:
if (frame->rdi != 0 || !user_buffer((void *) frame->rsi, frame->rdx)) { if (frame->rdi != 0 || !user_buffer((void *) frame->rsi, frame->rdx)) {
return -1; return -1;
} }
return typephp_os_console_read((void *) frame->rsi, frame->rdx); return typephp_os_console_read((void *) frame->rsi, frame->rdx);
case SYS_WRITE: case TYPEPHP_SYS_WRITE:
if ((frame->rdi != 1 && frame->rdi != 2) if ((frame->rdi != 1 && frame->rdi != 2)
|| !user_buffer((void *) frame->rsi, frame->rdx)) { || !user_buffer((void *) frame->rsi, frame->rdx)) {
return -1; return -1;
} }
typephp_os_write((const char *) frame->rsi, frame->rdx); typephp_os_write((const char *) frame->rsi, frame->rdx);
return (long) frame->rdx; return (long) frame->rdx;
case SYS_EXEC: case TYPEPHP_SYS_EXEC:
return syscall_exec(frame, (const char *) frame->rdi, (const char *) frame->rsi); return syscall_exec(frame, (const char *) frame->rdi, (const char *) frame->rsi);
case SYS_GETCWD: case TYPEPHP_SYS_GETCWD:
return syscall_getcwd((char *) frame->rdi, frame->rsi); return syscall_getcwd((char *) frame->rdi, frame->rsi);
case SYS_CHDIR: case TYPEPHP_SYS_CHDIR:
return syscall_chdir((const char *) frame->rdi); return syscall_chdir((const char *) frame->rdi);
case SYS_READDIR: case TYPEPHP_SYS_TIME:
return typephp_os_time_seconds();
case TYPEPHP_SYS_READDIR:
return syscall_readdir((const char *) frame->rdi, return syscall_readdir((const char *) frame->rdi,
(char *) frame->rsi, frame->rdx); (char *) frame->rsi, frame->rdx);
case SYS_EXIT: case TYPEPHP_SYS_EXIT:
return syscall_exit(frame, (long) frame->rdi); return syscall_exit(frame, (long) frame->rdi);
default: default:
return -1; return -1;

@ -20,6 +20,9 @@ objects:
- build/objects/sh-image.o - build/objects/sh-image.o
- build/objects/ls-image.o - build/objects/ls-image.o
- build/objects/cd-image.o - build/objects/cd-image.o
- build/objects/date-image.o
- build/objects/pwd-image.o
- build/objects/fault-image.o
build-dir: build/generated build-dir: build/generated
output: build/kernel64.elf output: build/kernel64.elf
cxx-std: c++17 cxx-std: c++17

@ -1,13 +1,5 @@
<?php <?php
final class KernelGreeting
{
public function render(string $now): string
{
return $now . ' Hello TypePHP-OS!';
}
}
function writeLine(string $text, int $color): void function writeLine(string $text, int $color): void
{ {
kernel_write($text, $color); kernel_write($text, $color);
@ -176,7 +168,5 @@ function main(): void
runMathSelfCheck(); runMathSelfCheck();
runFilesystemSelfCheck(); runFilesystemSelfCheck();
runPrimeDemo(100); runPrimeDemo(100);
$greeting = new KernelGreeting();
echo $greeting->render(date('Y-m-d H:i:s')), "\n";
kernel_process_start(); kernel_process_start();
} }

@ -16,7 +16,7 @@ if timeout 8 qemu-system-x86_64 \
-no-shutdown \ -no-shutdown \
-device isa-debug-exit,iobase=0xf4,iosize=0x04 \ -device isa-debug-exit,iobase=0xf4,iosize=0x04 \
>"${log}" 2>&1 \ >"${log}" 2>&1 \
<<< $'\nls\ncd DOCS\nls\ncd ..\nls\n'; then <<< $'\ndate\npwd\nls\ncd DOCS\npwd\nls\ncd ..\nls\n'; then
status=0 status=0
else else
status=$? status=$?
@ -42,11 +42,13 @@ grep -q "PHP directory scan: ., .., DATA, DOCS, HELLO.TXT, STREAM.TXT" "${log}"
grep -q "Calculate primes: 0-100" "${log}" grep -q "Calculate primes: 0-100" "${log}"
grep -q "Prime count: 25" "${log}" grep -q "Prime count: 25" "${log}"
grep -q "Prime list: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97" "${log}" grep -q "Prime list: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97" "${log}"
grep -Eq '^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} Hello TypePHP-OS![[:space:]]*$' "${log}"
grep -q "Process 1: sh.elf (Ring 3)" "${log}" grep -q "Process 1: sh.elf (Ring 3)" "${log}"
grep -q "TypePHP-OS user shell" "${log}" grep -q "TypePHP-OS user shell" "${log}"
grep -q "Ring 3 confirmed" "${log}" grep -q "Ring 3 confirmed" "${log}"
grep -q "Commands: ls, cd <directory>, pwd, date" "${log}"
grep -Eq '^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} UTC' "${log}"
grep -Fq 'typephp-os:/$ ' "${log}" grep -Fq 'typephp-os:/$ ' "${log}"
grep -Fq 'typephp-os:/DOCS$ ' "${log}" grep -Fq 'typephp-os:/DOCS$ ' "${log}"
grep -q '^/DOCS' "${log}"
grep -q '^HELLO.TXT' "${log}" grep -q '^HELLO.TXT' "${log}"
cat "${log}" cat "${log}"

@ -0,0 +1,82 @@
#include "syscall.h"
static int leap_year(long year)
{
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}
static void append_two_digits(char *output, size_t *offset, long value)
{
output[(*offset)++] = (char) ('0' + (value / 10) % 10);
output[(*offset)++] = (char) ('0' + value % 10);
}
static void append_four_digits(char *output, size_t *offset, long value)
{
output[(*offset)++] = (char) ('0' + (value / 1000) % 10);
output[(*offset)++] = (char) ('0' + (value / 100) % 10);
output[(*offset)++] = (char) ('0' + (value / 10) % 10);
output[(*offset)++] = (char) ('0' + value % 10);
}
void _start(const char *argument)
{
static const int month_days[] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
};
char output[25];
size_t offset = 0;
long seconds = typephp_syscall(TYPEPHP_SYS_TIME, 0, 0, 0);
long days;
long day_seconds;
long year = 1970;
long month = 0;
int days_in_year;
int days_in_month;
(void) argument;
if (seconds < 0) {
typephp_write("date: clock unavailable\n");
typephp_exit(1);
}
days = seconds / 86400;
day_seconds = seconds % 86400;
for (;;) {
days_in_year = leap_year(year) ? 366 : 365;
if (days < days_in_year) {
break;
}
days -= days_in_year;
++year;
}
for (;;) {
days_in_month = month_days[month];
if (month == 1 && leap_year(year)) {
++days_in_month;
}
if (days < days_in_month) {
break;
}
days -= days_in_month;
++month;
}
append_four_digits(output, &offset, year);
output[offset++] = '-';
append_two_digits(output, &offset, month + 1);
output[offset++] = '-';
append_two_digits(output, &offset, days + 1);
output[offset++] = ' ';
append_two_digits(output, &offset, day_seconds / 3600);
output[offset++] = ':';
append_two_digits(output, &offset, (day_seconds / 60) % 60);
output[offset++] = ':';
append_two_digits(output, &offset, day_seconds % 60);
output[offset++] = ' ';
output[offset++] = 'U';
output[offset++] = 'T';
output[offset++] = 'C';
output[offset++] = '\n';
typephp_write_bytes(output, offset);
typephp_exit(0);
}

@ -0,0 +1,14 @@
#include "syscall.h"
void _start(const char *argument)
{
char cwd[16];
(void) argument;
if (typephp_syscall(TYPEPHP_SYS_GETCWD, (long) cwd, sizeof(cwd), 0) < 0) {
typephp_write("pwd: working directory unavailable\n");
typephp_exit(1);
}
typephp_write(cwd);
typephp_write("\n");
typephp_exit(0);
}

@ -61,7 +61,7 @@ void _start(void)
} }
typephp_write("TypePHP-OS user shell\n"); typephp_write("TypePHP-OS user shell\n");
typephp_write("Ring 3 confirmed\n"); typephp_write("Ring 3 confirmed\n");
typephp_write("Commands: ls, cd <directory>\n"); typephp_write("Commands: ls, cd <directory>, pwd, date, fault\n");
for (;;) { for (;;) {
char *argument; char *argument;
show_prompt(); show_prompt();
@ -78,7 +78,9 @@ void _start(void)
++argument; ++argument;
} }
} }
if (string_equal(line, "ls") || string_equal(line, "cd")) { if (string_equal(line, "ls") || string_equal(line, "cd")
|| string_equal(line, "pwd") || string_equal(line, "date")
|| string_equal(line, "fault")) {
if (typephp_syscall(TYPEPHP_SYS_EXEC, if (typephp_syscall(TYPEPHP_SYS_EXEC,
(long) line, (long) argument, 0) < 0) { (long) line, (long) argument, 0) < 0) {
typephp_write("sh: unable to execute command\n"); typephp_write("sh: unable to execute command\n");

@ -1,17 +1,9 @@
#ifndef TYPEPHP_OS_USER_SYSCALL_H #ifndef TYPEPHP_OS_USER_SYSCALL_H
#define TYPEPHP_OS_USER_SYSCALL_H #define TYPEPHP_OS_USER_SYSCALL_H
typedef unsigned long size_t; #include <typephp_os_syscall.h>
enum { typedef unsigned long size_t;
TYPEPHP_SYS_READ = 0,
TYPEPHP_SYS_WRITE = 1,
TYPEPHP_SYS_EXEC = 59,
TYPEPHP_SYS_EXIT = 60,
TYPEPHP_SYS_GETCWD = 79,
TYPEPHP_SYS_CHDIR = 80,
TYPEPHP_SYS_READDIR = 217,
};
static inline long typephp_syscall( static inline long typephp_syscall(
long number, long first, long second, long third) long number, long first, long second, long third)

Loading…
Cancel
Save