diff --git a/examples/typephp-os/Makefile b/examples/typephp-os/Makefile index 451bb621..47ef2009 100644 --- a/examples/typephp-os/Makefile +++ b/examples/typephp-os/Makefile @@ -6,15 +6,16 @@ PAYLOAD_BIN := $(BUILD)/kernel64.bin PAYLOAD_OBJECT := $(OBJECT_DIR)/kernel64-payload.o KERNEL := $(BUILD)/typephp-os.elf DISK := $(BUILD)/typephp-os.img +USER_COMMANDS := ls cd date pwd fault +USER_PROGRAMS := sh $(USER_COMMANDS) USER_SH_ELF := $(BUILD)/sh.elf -USER_LS_ELF := $(BUILD)/ls.elf -USER_CD_ELF := $(BUILD)/cd.elf -USER_IMAGES := $(OBJECT_DIR)/sh-image.o $(OBJECT_DIR)/ls-image.o $(OBJECT_DIR)/cd-image.o +USER_COMMAND_ELFS := $(addprefix $(BUILD)/,$(addsuffix .elf,$(USER_COMMANDS))) +USER_IMAGES := $(addprefix $(OBJECT_DIR)/,$(addsuffix -image.o,$(USER_PROGRAMS))) BOOTFLAGS := -m32 -O2 -ffreestanding -fno-pic -fno-pie \ -fno-stack-protector -Wall -Wextra 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 .PHONY: all payload objects disk run test clean @@ -43,41 +44,20 @@ $(OBJECT_DIR): $(BOOTSTRAP_OBJECT): freestanding/boot.S | $(OBJECT_DIR) gcc $(BOOTFLAGS) -c $< -o $@ -$(OBJECT_DIR)/sh.o: user/sh.c user/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) +$(OBJECT_DIR)/%.o: user/%.c user/syscall.h freestanding/abi/typephp_os_syscall.h | $(OBJECT_DIR) gcc $(USER_CFLAGS) -c $< -o $@ $(USER_SH_ELF): $(OBJECT_DIR)/sh.o user/linker.ld | $(BUILD) 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 $@ $< -$(USER_CD_ELF): $(OBJECT_DIR)/cd.o user/linker.ld | $(BUILD) - 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) +$(OBJECT_DIR)/%-image.o: $(BUILD)/%.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_cd_elf_start=typephp_user_cd_elf_start \ - --redefine-sym _binary_build_cd_elf_end=typephp_user_cd_elf_end $< $@ + --redefine-sym _binary_build_$*_elf_start=typephp_user_$*_elf_start \ + --redefine-sym _binary_build_$*_elf_end=typephp_user_$*_elf_end $< $@ $(KERNEL): payload objcopy -O binary $(PAYLOAD_ELF) $(PAYLOAD_BIN) diff --git a/examples/typephp-os/README.md b/examples/typephp-os/README.md index a763c468..dcd2ff59 100644 --- a/examples/typephp-os/README.md +++ b/examples/typephp-os/README.md @@ -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 assembly layers provide the machine bootstrap, current kernel services, and the temporary freestanding userspace programs. -The first userspace programs (`sh`, `ls`, and `cd`) are deliberately written -in freestanding C until tpc can target this small userspace ABI. +The first userspace programs (`sh`, `ls`, `cd`, `pwd`, and `date`) are +deliberately written in freestanding C until tpc can target this small +userspace ABI. The architectural rules and staged plan are maintained in [ROADMAP.md](ROADMAP.md). @@ -62,8 +63,8 @@ The build produces these useful files: tpc; - `build/typephp-os.elf`: the final Multiboot kernel accepted by QEMU; - `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 - programs built without libc. +- `build/sh.elf`, `build/ls.elf`, `build/cd.elf`, `build/pwd.elf`, and + `build/date.elf`: independent ELF64 Ring-3 programs built without libc. 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 `-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 `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 @@ -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 32-36 MiB are marked user-accessible while kernel pages remain supervisor-only. An `int 0x80` boundary currently provides synchronous `read`, `write`, `exec`, -`exit`, `getcwd`, `chdir`, and directory-list operations. Standard input and -output are backed by QEMU's COM1 serial console. - -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 -keeps the shell register frame and restores it when the command calls `exit`. +`exit`, `getcwd`, `chdir`, `time`, and directory-list operations. 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 shell synchronously executes separate `ls.elf`, `cd.elf`, `pwd.elf`, and +`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 session-global property; a successful `cd` therefore remains visible after control returns to the shell. @@ -153,7 +157,10 @@ Available commands are: ```text ls +date +pwd cd DOCS +pwd ls cd .. ``` diff --git a/examples/typephp-os/ROADMAP.md b/examples/typephp-os/ROADMAP.md index bdc2b356..2effd324 100644 --- a/examples/typephp-os/ROADMAP.md +++ b/examples/typephp-os/ROADMAP.md @@ -43,8 +43,9 @@ behavior. nested directories and long filenames are next. 6. **Single-task userspace — first slice complete.** ELF64 validation/loading, supervisor/user page separation, GDT/TSS, Ring-3 entry, synchronous - `int 0x80` system calls, COM1 standard I/O, saved parent context, and - independent freestanding C `sh`, `ls`, and `cd` programs. + `int 0x80` system calls, COM1 standard I/O, saved parent context, shared + 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 of PHPX fields under sustained allocation. 8. **Kernel services.** Interrupt-driven timer, keyboard, physical-page diff --git a/examples/typephp-os/freestanding/abi/typephp_os_syscall.h b/examples/typephp-os/freestanding/abi/typephp_os_syscall.h new file mode 100644 index 00000000..1264b7d9 --- /dev/null +++ b/examples/typephp-os/freestanding/abi/typephp_os_syscall.h @@ -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 diff --git a/examples/typephp-os/freestanding/process.c b/examples/typephp-os/freestanding/process.c index 8a12ac9f..4022843e 100644 --- a/examples/typephp-os/freestanding/process.c +++ b/examples/typephp-os/freestanding/process.c @@ -8,6 +8,7 @@ */ #include "typephp_os_abi.h" +#include "typephp_os_syscall.h" #include #include @@ -22,13 +23,6 @@ enum { KERNEL_CODE_SELECTOR = 0x08, TSS_SELECTOR = 0x28, 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 { @@ -115,6 +109,31 @@ typedef struct { uint64_t ss; } 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 { uint64_t pid; 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_cd_elf_start[]; 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_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 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; } +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) { descriptor_pointer pointer; - uintptr_t handler = (uintptr_t) typephp_os_syscall_entry; memset(gdt, 0, sizeof(gdt)); gdt[1] = UINT64_C(0x00af9a000000ffff); @@ -205,11 +249,16 @@ static void install_descriptor_tables(void) : "rax", "memory"); memset(idt, 0, sizeof(idt)); - idt[IDT_SYSCALL].offset_low = handler & 0xffffu; - idt[IDT_SYSCALL].selector = KERNEL_CODE_SELECTOR; - idt[IDT_SYSCALL].attributes = 0xee; /* present, DPL 3, interrupt gate */ - idt[IDT_SYSCALL].offset_middle = (handler >> 16u) & 0xffffu; - idt[IDT_SYSCALL].offset_high = handler >> 32u; + install_idt_gate(0, typephp_os_exception_0, 0x8e); + install_idt_gate(3, typephp_os_exception_3, 0xee); + install_idt_gate(5, typephp_os_exception_5, 0x8e); + install_idt_gate(6, typephp_os_exception_6, 0x8e); + 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.base = (uint64_t) (uintptr_t) idt; __asm__ volatile("lidt %0" : : "m"(pointer) : "memory"); @@ -272,6 +321,21 @@ static int command_image( *image_end = typephp_user_cd_elf_end; 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; } @@ -380,31 +444,121 @@ static long syscall_exit(syscall_frame *frame, long 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) { switch (frame->rax) { - case SYS_READ: + case TYPEPHP_SYS_READ: if (frame->rdi != 0 || !user_buffer((void *) frame->rsi, frame->rdx)) { return -1; } return typephp_os_console_read((void *) frame->rsi, frame->rdx); - case SYS_WRITE: + case TYPEPHP_SYS_WRITE: if ((frame->rdi != 1 && frame->rdi != 2) || !user_buffer((void *) frame->rsi, frame->rdx)) { return -1; } typephp_os_write((const char *) frame->rsi, 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); - case SYS_GETCWD: + case TYPEPHP_SYS_GETCWD: return syscall_getcwd((char *) frame->rdi, frame->rsi); - case SYS_CHDIR: + case TYPEPHP_SYS_CHDIR: 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, (char *) frame->rsi, frame->rdx); - case SYS_EXIT: + case TYPEPHP_SYS_EXIT: return syscall_exit(frame, (long) frame->rdi); default: return -1; diff --git a/examples/typephp-os/project.yml b/examples/typephp-os/project.yml index 95f44c61..8c89533f 100644 --- a/examples/typephp-os/project.yml +++ b/examples/typephp-os/project.yml @@ -20,6 +20,9 @@ objects: - build/objects/sh-image.o - build/objects/ls-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 output: build/kernel64.elf cxx-std: c++17 diff --git a/examples/typephp-os/src/kernel.php b/examples/typephp-os/src/kernel.php index edf075f8..c7d18ab5 100644 --- a/examples/typephp-os/src/kernel.php +++ b/examples/typephp-os/src/kernel.php @@ -1,13 +1,5 @@ render(date('Y-m-d H:i:s')), "\n"; kernel_process_start(); } diff --git a/examples/typephp-os/tools/test-qemu.sh b/examples/typephp-os/tools/test-qemu.sh index 11643582..0c0d9d54 100755 --- a/examples/typephp-os/tools/test-qemu.sh +++ b/examples/typephp-os/tools/test-qemu.sh @@ -16,7 +16,7 @@ if timeout 8 qemu-system-x86_64 \ -no-shutdown \ -device isa-debug-exit,iobase=0xf4,iosize=0x04 \ >"${log}" 2>&1 \ - <<< $'\nls\ncd DOCS\nls\ncd ..\nls\n'; then + <<< $'\ndate\npwd\nls\ncd DOCS\npwd\nls\ncd ..\nls\n'; then status=0 else 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 "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 -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 "TypePHP-OS user shell" "${log}" grep -q "Ring 3 confirmed" "${log}" +grep -q "Commands: ls, cd , 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:/DOCS$ ' "${log}" +grep -q '^/DOCS' "${log}" grep -q '^HELLO.TXT' "${log}" cat "${log}" diff --git a/examples/typephp-os/user/date.c b/examples/typephp-os/user/date.c new file mode 100644 index 00000000..695dbed2 --- /dev/null +++ b/examples/typephp-os/user/date.c @@ -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); +} diff --git a/examples/typephp-os/user/pwd.c b/examples/typephp-os/user/pwd.c new file mode 100644 index 00000000..6ef4115a --- /dev/null +++ b/examples/typephp-os/user/pwd.c @@ -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); +} diff --git a/examples/typephp-os/user/sh.c b/examples/typephp-os/user/sh.c index 0760ecb7..db7166be 100644 --- a/examples/typephp-os/user/sh.c +++ b/examples/typephp-os/user/sh.c @@ -61,7 +61,7 @@ void _start(void) } typephp_write("TypePHP-OS user shell\n"); typephp_write("Ring 3 confirmed\n"); - typephp_write("Commands: ls, cd \n"); + typephp_write("Commands: ls, cd , pwd, date, fault\n"); for (;;) { char *argument; show_prompt(); @@ -78,7 +78,9 @@ void _start(void) ++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, (long) line, (long) argument, 0) < 0) { typephp_write("sh: unable to execute command\n"); diff --git a/examples/typephp-os/user/syscall.h b/examples/typephp-os/user/syscall.h index 90d60a27..66e862e8 100644 --- a/examples/typephp-os/user/syscall.h +++ b/examples/typephp-os/user/syscall.h @@ -1,17 +1,9 @@ #ifndef TYPEPHP_OS_USER_SYSCALL_H #define TYPEPHP_OS_USER_SYSCALL_H -typedef unsigned long size_t; +#include -enum { - 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, -}; +typedef unsigned long size_t; static inline long typephp_syscall( long number, long first, long second, long third)