- Add user-accessible page mapping (32-36 MiB) in boot.S with proper permission bits - Implement ELF64 loader and process management in new process.c module - Add syscall infrastructure including int 0x80 handler in process-entry.S - Create userspace programs (sh, ls, cd) with dedicated linker script - Integrate filesystem path type checking and directory listing functions - Reserve memory regions for user processes and update kernel memory layout - Add console input/output support for userspace applications - Update kernel main loop to start user process after initialization - Modify Makefile to build and embed userspace ELF images as kernel objects - Document new userspace capabilities and limitations in README.mdmaster
parent
515132b6b8
commit
0cfa577db1
21 changed files with 870 additions and 26 deletions
@ -0,0 +1,64 @@ |
|||||||
|
.section .text |
||||||
|
.code64 |
||||||
|
|
||||||
|
.global typephp_os_syscall_entry
|
||||||
|
.type typephp_os_syscall_entry, @function
|
||||||
|
.extern typephp_os_syscall_dispatch
|
||||||
|
typephp_os_syscall_entry: |
||||||
|
# Save every general-purpose register. The saved RAX slot is overwritten |
||||||
|
# with the syscall result before the frame is restored. |
||||||
|
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_syscall_dispatch |
||||||
|
mov %rax, (%rsp) |
||||||
|
|
||||||
|
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 |
||||||
|
iretq |
||||||
|
.size typephp_os_syscall_entry, . - typephp_os_syscall_entry |
||||||
|
|
||||||
|
.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;
|
||||||
|
# int 0x80 is synchronous and the first process manager has no scheduler. |
||||||
|
mov $0x1b, %ax |
||||||
|
mov %ax, %ds |
||||||
|
mov %ax, %es |
||||||
|
pushq $0x1b |
||||||
|
push %rsi |
||||||
|
pushq $0x2 |
||||||
|
pushq $0x23 |
||||||
|
push %rdi |
||||||
|
iretq |
||||||
|
.size typephp_os_enter_user, . - typephp_os_enter_user |
||||||
|
|
||||||
|
.section .note.GNU-stack, "", @progbits
|
||||||
@ -0,0 +1,425 @@ |
|||||||
|
/*
|
||||||
|
+----------------------------------------------------------------------+ |
||||||
|
| TypePHP OS | |
||||||
|
+----------------------------------------------------------------------+ |
||||||
|
| Single foreground process, ELF64 loader and int 0x80 syscall core. | |
||||||
|
| SPDX-License-Identifier: BSD-3-Clause | |
||||||
|
+----------------------------------------------------------------------+ |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "typephp_os_abi.h" |
||||||
|
|
||||||
|
#include <stddef.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
enum { |
||||||
|
USER_BEGIN = 32u * 1024u * 1024u, |
||||||
|
USER_END = 36u * 1024u * 1024u, |
||||||
|
USER_STACK = USER_END - 16u, |
||||||
|
USER_CODE_SELECTOR = 0x23, |
||||||
|
USER_DATA_SELECTOR = 0x1b, |
||||||
|
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 { |
||||||
|
ELF_PT_LOAD = 1, |
||||||
|
ELF_ET_EXEC = 2, |
||||||
|
ELF_MACHINE_X86_64 = 62, |
||||||
|
}; |
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) { |
||||||
|
unsigned char ident[16]; |
||||||
|
uint16_t type; |
||||||
|
uint16_t machine; |
||||||
|
uint32_t version; |
||||||
|
uint64_t entry; |
||||||
|
uint64_t phoff; |
||||||
|
uint64_t shoff; |
||||||
|
uint32_t flags; |
||||||
|
uint16_t ehsize; |
||||||
|
uint16_t phentsize; |
||||||
|
uint16_t phnum; |
||||||
|
uint16_t shentsize; |
||||||
|
uint16_t shnum; |
||||||
|
uint16_t shstrndx; |
||||||
|
} elf64_header; |
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) { |
||||||
|
uint32_t type; |
||||||
|
uint32_t flags; |
||||||
|
uint64_t offset; |
||||||
|
uint64_t vaddr; |
||||||
|
uint64_t paddr; |
||||||
|
uint64_t filesz; |
||||||
|
uint64_t memsz; |
||||||
|
uint64_t align; |
||||||
|
} elf64_program_header; |
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) { |
||||||
|
uint16_t limit; |
||||||
|
uint64_t base; |
||||||
|
} descriptor_pointer; |
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) { |
||||||
|
uint16_t offset_low; |
||||||
|
uint16_t selector; |
||||||
|
uint8_t ist; |
||||||
|
uint8_t attributes; |
||||||
|
uint16_t offset_middle; |
||||||
|
uint32_t offset_high; |
||||||
|
uint32_t reserved; |
||||||
|
} idt_gate; |
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) { |
||||||
|
uint32_t reserved0; |
||||||
|
uint64_t rsp0; |
||||||
|
uint64_t rsp1; |
||||||
|
uint64_t rsp2; |
||||||
|
uint64_t reserved1; |
||||||
|
uint64_t ist[7]; |
||||||
|
uint64_t reserved2; |
||||||
|
uint16_t reserved3; |
||||||
|
uint16_t iomap_base; |
||||||
|
} task_state_segment; |
||||||
|
|
||||||
|
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 rip; |
||||||
|
uint64_t cs; |
||||||
|
uint64_t rflags; |
||||||
|
uint64_t rsp; |
||||||
|
uint64_t ss; |
||||||
|
} syscall_frame; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
uint64_t pid; |
||||||
|
int running; |
||||||
|
char cwd[16]; |
||||||
|
syscall_frame parent_frame; |
||||||
|
int parent_waiting; |
||||||
|
} process_state; |
||||||
|
|
||||||
|
extern const unsigned char typephp_user_sh_elf_start[]; |
||||||
|
extern const unsigned char typephp_user_sh_elf_end[]; |
||||||
|
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 void typephp_os_syscall_entry(void); |
||||||
|
extern void typephp_os_enter_user(uint64_t entry, uint64_t stack); |
||||||
|
|
||||||
|
static uint64_t gdt[7] __attribute__((aligned(16))); |
||||||
|
static idt_gate idt[256] __attribute__((aligned(16))); |
||||||
|
static task_state_segment tss; |
||||||
|
static unsigned char syscall_stack[64u * 1024u] __attribute__((aligned(16))); |
||||||
|
static process_state foreground_process = {.pid = 1, .cwd = "/"}; |
||||||
|
|
||||||
|
static void panic(const char *message) |
||||||
|
{ |
||||||
|
typephp_os_write("Process panic: ", sizeof("Process panic: ") - 1); |
||||||
|
typephp_os_panic(message); |
||||||
|
} |
||||||
|
|
||||||
|
static int range_inside(uint64_t address, uint64_t size, uint64_t begin, uint64_t end) |
||||||
|
{ |
||||||
|
return address >= begin && size <= end - begin && address <= end - size; |
||||||
|
} |
||||||
|
|
||||||
|
static int user_buffer(const void *pointer, size_t size) |
||||||
|
{ |
||||||
|
return range_inside((uint64_t) (uintptr_t) pointer, size, USER_BEGIN, USER_END); |
||||||
|
} |
||||||
|
|
||||||
|
static size_t bounded_user_string(const char *string, size_t maximum) |
||||||
|
{ |
||||||
|
size_t length = 0; |
||||||
|
if (!user_buffer(string, 1)) { |
||||||
|
return (size_t) -1; |
||||||
|
} |
||||||
|
while (length < maximum && user_buffer(string + length, 1) && string[length] != '\0') { |
||||||
|
++length; |
||||||
|
} |
||||||
|
return length < maximum && user_buffer(string + length, 1) ? length : (size_t) -1; |
||||||
|
} |
||||||
|
|
||||||
|
static void install_tss_descriptor(uint64_t base, uint32_t limit) |
||||||
|
{ |
||||||
|
gdt[5] = ((uint64_t) (limit & 0xffffu)) |
||||||
|
| ((base & 0xffffffu) << 16u) |
||||||
|
| (UINT64_C(0x89) << 40u) |
||||||
|
| ((uint64_t) ((limit >> 16u) & 0x0fu) << 48u) |
||||||
|
| (((base >> 24u) & 0xffu) << 56u); |
||||||
|
gdt[6] = base >> 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); |
||||||
|
gdt[2] = UINT64_C(0x00cf92000000ffff); |
||||||
|
gdt[3] = UINT64_C(0x00cff2000000ffff); |
||||||
|
gdt[4] = UINT64_C(0x00affa000000ffff); |
||||||
|
memset(&tss, 0, sizeof(tss)); |
||||||
|
tss.rsp0 = (uint64_t) (uintptr_t) (syscall_stack + sizeof(syscall_stack)); |
||||||
|
tss.iomap_base = sizeof(tss); |
||||||
|
install_tss_descriptor((uint64_t) (uintptr_t) &tss, sizeof(tss) - 1u); |
||||||
|
|
||||||
|
pointer.limit = sizeof(gdt) - 1u; |
||||||
|
pointer.base = (uint64_t) (uintptr_t) gdt; |
||||||
|
__asm__ volatile("lgdt %0" : : "m"(pointer) : "memory"); |
||||||
|
__asm__ volatile( |
||||||
|
"mov %[data], %%ax\n" |
||||||
|
"mov %%ax, %%ds\n" |
||||||
|
"mov %%ax, %%es\n" |
||||||
|
"mov %%ax, %%ss\n" |
||||||
|
"ltr %[tss]\n" |
||||||
|
: |
||||||
|
: [data] "i"(0x10), [tss] "r"((uint16_t) TSS_SELECTOR) |
||||||
|
: "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; |
||||||
|
pointer.limit = sizeof(idt) - 1u; |
||||||
|
pointer.base = (uint64_t) (uintptr_t) idt; |
||||||
|
__asm__ volatile("lidt %0" : : "m"(pointer) : "memory"); |
||||||
|
} |
||||||
|
|
||||||
|
static uint64_t load_user_elf(const unsigned char *image, const unsigned char *image_end) |
||||||
|
{ |
||||||
|
const size_t image_size = (size_t) (image_end - image); |
||||||
|
const elf64_header *header; |
||||||
|
|
||||||
|
if (image_size < sizeof(elf64_header)) { |
||||||
|
panic("truncated user ELF\n"); |
||||||
|
} |
||||||
|
header = (const elf64_header *) image; |
||||||
|
if (header->ident[0] != 0x7f || header->ident[1] != 'E' |
||||||
|
|| header->ident[2] != 'L' || header->ident[3] != 'F' |
||||||
|
|| header->ident[4] != 2 || header->ident[5] != 1 |
||||||
|
|| header->type != ELF_ET_EXEC || header->machine != ELF_MACHINE_X86_64 |
||||||
|
|| header->phentsize != sizeof(elf64_program_header)) { |
||||||
|
panic("invalid user ELF64 header\n"); |
||||||
|
} |
||||||
|
if (!range_inside(header->phoff, |
||||||
|
(uint64_t) header->phnum * sizeof(elf64_program_header), 0, image_size)) { |
||||||
|
panic("invalid user ELF program table\n"); |
||||||
|
} |
||||||
|
|
||||||
|
for (uint16_t index = 0; index < header->phnum; ++index) { |
||||||
|
const elf64_program_header *segment = (const elf64_program_header *) |
||||||
|
(image + header->phoff + (uint64_t) index * sizeof(*segment)); |
||||||
|
if (segment->type != ELF_PT_LOAD) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (segment->filesz > segment->memsz |
||||||
|
|| !range_inside(segment->offset, segment->filesz, 0, image_size) |
||||||
|
|| !range_inside(segment->vaddr, segment->memsz, USER_BEGIN, USER_END)) { |
||||||
|
panic("invalid user ELF load segment\n"); |
||||||
|
} |
||||||
|
memcpy((void *) (uintptr_t) segment->vaddr, image + segment->offset, segment->filesz); |
||||||
|
memset((void *) (uintptr_t) (segment->vaddr + segment->filesz), |
||||||
|
0, segment->memsz - segment->filesz); |
||||||
|
} |
||||||
|
if (!range_inside(header->entry, 1, USER_BEGIN, USER_END)) { |
||||||
|
panic("invalid user ELF entry\n"); |
||||||
|
} |
||||||
|
return header->entry; |
||||||
|
} |
||||||
|
|
||||||
|
static int command_image( |
||||||
|
const char *name, |
||||||
|
const unsigned char **image, |
||||||
|
const unsigned char **image_end) |
||||||
|
{ |
||||||
|
if (strcmp(name, "ls") == 0) { |
||||||
|
*image = typephp_user_ls_elf_start; |
||||||
|
*image_end = typephp_user_ls_elf_end; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
if (strcmp(name, "cd") == 0) { |
||||||
|
*image = typephp_user_cd_elf_start; |
||||||
|
*image_end = typephp_user_cd_elf_end; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static long syscall_getcwd(char *buffer, size_t size) |
||||||
|
{ |
||||||
|
size_t length = strlen(foreground_process.cwd) + 1u; |
||||||
|
if (size < length || !user_buffer(buffer, size)) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
memcpy(buffer, foreground_process.cwd, length); |
||||||
|
return (long) length; |
||||||
|
} |
||||||
|
|
||||||
|
static long syscall_chdir(const char *path) |
||||||
|
{ |
||||||
|
char normalized[16]; |
||||||
|
size_t length = bounded_user_string(path, sizeof(normalized)); |
||||||
|
if (length == (size_t) -1 || length == 0) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
if ((length == 1 && path[0] == '/') |
||||||
|
|| (length == 2 && path[0] == '.' && path[1] == '.')) { |
||||||
|
normalized[0] = '/'; |
||||||
|
normalized[1] = '\0'; |
||||||
|
} else if (length == 1 && path[0] == '.') { |
||||||
|
return 0; |
||||||
|
} else { |
||||||
|
size_t source = path[0] == '/' ? 1u : 0u; |
||||||
|
size_t name_length = length - source; |
||||||
|
if (name_length == 0 || name_length > 12u) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
normalized[0] = '/'; |
||||||
|
memcpy(normalized + 1, path + source, name_length); |
||||||
|
normalized[name_length + 1u] = '\0'; |
||||||
|
} |
||||||
|
if (typephp_os_fs_path_type(normalized) != 2) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
memcpy(foreground_process.cwd, normalized, strlen(normalized) + 1u); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static long syscall_readdir(const char *path, char *buffer, size_t capacity) |
||||||
|
{ |
||||||
|
char resolved[16]; |
||||||
|
const char *directory = foreground_process.cwd; |
||||||
|
if (path != 0) { |
||||||
|
size_t length = bounded_user_string(path, sizeof(resolved)); |
||||||
|
if (length == (size_t) -1) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
if (length != 0) { |
||||||
|
memcpy(resolved, path, length + 1u); |
||||||
|
directory = resolved; |
||||||
|
} |
||||||
|
} |
||||||
|
if (!user_buffer(buffer, capacity)) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
return typephp_os_fs_list(directory, buffer, capacity); |
||||||
|
} |
||||||
|
|
||||||
|
static long syscall_exec(syscall_frame *frame, const char *name, const char *argument) |
||||||
|
{ |
||||||
|
const unsigned char *image; |
||||||
|
const unsigned char *image_end; |
||||||
|
char command[8]; |
||||||
|
size_t name_length; |
||||||
|
|
||||||
|
if (foreground_process.parent_waiting) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
name_length = bounded_user_string(name, sizeof(command)); |
||||||
|
if (name_length == (size_t) -1 || name_length == 0) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
memcpy(command, name, name_length + 1u); |
||||||
|
if (!command_image(command, &image, &image_end)) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
if (argument != 0 && bounded_user_string(argument, 64) == (size_t) -1) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
memcpy(&foreground_process.parent_frame, frame, sizeof(*frame)); |
||||||
|
foreground_process.parent_waiting = 1; |
||||||
|
foreground_process.pid = 2; |
||||||
|
frame->rip = load_user_elf(image, image_end); |
||||||
|
frame->rsp = 35u * 1024u * 1024u - 16u; |
||||||
|
frame->rdi = (uint64_t) (uintptr_t) argument; |
||||||
|
frame->rsi = 0; |
||||||
|
frame->rdx = 0; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static long syscall_exit(syscall_frame *frame, long status) |
||||||
|
{ |
||||||
|
if (!foreground_process.parent_waiting) { |
||||||
|
foreground_process.running = 0; |
||||||
|
typephp_os_panic("shell process exited\n"); |
||||||
|
} |
||||||
|
memcpy(frame, &foreground_process.parent_frame, sizeof(*frame)); |
||||||
|
foreground_process.parent_waiting = 0; |
||||||
|
foreground_process.pid = 1; |
||||||
|
return status; |
||||||
|
} |
||||||
|
|
||||||
|
long typephp_os_syscall_dispatch(syscall_frame *frame) |
||||||
|
{ |
||||||
|
switch (frame->rax) { |
||||||
|
case 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: |
||||||
|
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: |
||||||
|
return syscall_exec(frame, (const char *) frame->rdi, (const char *) frame->rsi); |
||||||
|
case SYS_GETCWD: |
||||||
|
return syscall_getcwd((char *) frame->rdi, frame->rsi); |
||||||
|
case SYS_CHDIR: |
||||||
|
return syscall_chdir((const char *) frame->rdi); |
||||||
|
case SYS_READDIR: |
||||||
|
return syscall_readdir((const char *) frame->rdi, |
||||||
|
(char *) frame->rsi, frame->rdx); |
||||||
|
case SYS_EXIT: |
||||||
|
return syscall_exit(frame, (long) frame->rdi); |
||||||
|
default: |
||||||
|
return -1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void typephp_os_process_start(void) |
||||||
|
{ |
||||||
|
uint64_t entry; |
||||||
|
memset((void *) (uintptr_t) USER_BEGIN, 0, USER_END - USER_BEGIN); |
||||||
|
entry = load_user_elf(typephp_user_sh_elf_start, typephp_user_sh_elf_end); |
||||||
|
install_descriptor_tables(); |
||||||
|
foreground_process.running = 1; |
||||||
|
typephp_os_write("Process 1: sh.elf (Ring 3)\n", |
||||||
|
sizeof("Process 1: sh.elf (Ring 3)\n") - 1); |
||||||
|
typephp_os_enter_user(entry, USER_STACK); |
||||||
|
panic("Ring-3 entry returned\n"); |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
#include "syscall.h" |
||||||
|
|
||||||
|
void _start(const char *argument) |
||||||
|
{ |
||||||
|
const char *path = argument != 0 && argument[0] != '\0' ? argument : "/"; |
||||||
|
if (typephp_syscall(TYPEPHP_SYS_CHDIR, (long) path, 0, 0) != 0) { |
||||||
|
typephp_write("cd: no such directory\n"); |
||||||
|
typephp_exit(1); |
||||||
|
} |
||||||
|
typephp_exit(0); |
||||||
|
} |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
OUTPUT_FORMAT(elf64-x86-64) |
||||||
|
OUTPUT_ARCH(i386:x86-64) |
||||||
|
ENTRY(_start) |
||||||
|
|
||||||
|
SECTIONS |
||||||
|
{ |
||||||
|
. = USER_BASE + SIZEOF_HEADERS; |
||||||
|
|
||||||
|
.text : { *(.text .text.*) } |
||||||
|
.rodata : { *(.rodata .rodata.*) } |
||||||
|
.data : { *(.data .data.*) } |
||||||
|
.bss (NOLOAD) : { *(COMMON) *(.bss .bss.*) } |
||||||
|
|
||||||
|
/DISCARD/ : { *(.comment) *(.eh_frame*) *(.note*) } |
||||||
|
} |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
#include "syscall.h" |
||||||
|
|
||||||
|
void _start(const char *argument) |
||||||
|
{ |
||||||
|
char entries[512]; |
||||||
|
const char *path = argument != 0 && argument[0] != '\0' ? argument : 0; |
||||||
|
long length = typephp_syscall( |
||||||
|
TYPEPHP_SYS_READDIR, (long) path, (long) entries, sizeof(entries)); |
||||||
|
if (length < 0) { |
||||||
|
typephp_write("ls: cannot read directory\n"); |
||||||
|
typephp_exit(1); |
||||||
|
} |
||||||
|
typephp_write_bytes(entries, (size_t) length); |
||||||
|
typephp_exit(0); |
||||||
|
} |
||||||
@ -0,0 +1,92 @@ |
|||||||
|
#include "syscall.h" |
||||||
|
|
||||||
|
static int string_equal(const char *left, const char *right) |
||||||
|
{ |
||||||
|
while (*left != '\0' && *left == *right) { |
||||||
|
++left; |
||||||
|
++right; |
||||||
|
} |
||||||
|
return *left == *right; |
||||||
|
} |
||||||
|
|
||||||
|
static size_t read_line(char *buffer, size_t capacity) |
||||||
|
{ |
||||||
|
size_t length = 0; |
||||||
|
while (capacity > 1) { |
||||||
|
char character = 0; |
||||||
|
if (typephp_syscall(TYPEPHP_SYS_READ, 0, (long) &character, 1) != 1) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (character == '\r' || character == '\n') { |
||||||
|
typephp_write("\n"); |
||||||
|
break; |
||||||
|
} |
||||||
|
if (character == 8 || character == 127) { |
||||||
|
if (length != 0) { |
||||||
|
--length; |
||||||
|
typephp_write("\b \b"); |
||||||
|
} |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (character >= 32 && character < 127) { |
||||||
|
buffer[length++] = character; |
||||||
|
--capacity; |
||||||
|
typephp_write_bytes(&character, 1); |
||||||
|
} |
||||||
|
} |
||||||
|
buffer[length] = '\0'; |
||||||
|
return length; |
||||||
|
} |
||||||
|
|
||||||
|
static void show_prompt(void) |
||||||
|
{ |
||||||
|
char cwd[16]; |
||||||
|
typephp_write("typephp-os:"); |
||||||
|
if (typephp_syscall(TYPEPHP_SYS_GETCWD, (long) cwd, sizeof(cwd), 0) > 0) { |
||||||
|
typephp_write(cwd); |
||||||
|
} else { |
||||||
|
typephp_write("?"); |
||||||
|
} |
||||||
|
typephp_write("$ "); |
||||||
|
} |
||||||
|
|
||||||
|
void _start(void) |
||||||
|
{ |
||||||
|
char line[80]; |
||||||
|
unsigned short code_selector; |
||||||
|
__asm__ volatile("mov %%cs, %0" : "=r"(code_selector)); |
||||||
|
if ((code_selector & 3u) != 3u) { |
||||||
|
typephp_write("sh: Ring-3 transition failed\n"); |
||||||
|
typephp_exit(1); |
||||||
|
} |
||||||
|
typephp_write("TypePHP-OS user shell\n"); |
||||||
|
typephp_write("Ring 3 confirmed\n"); |
||||||
|
typephp_write("Commands: ls, cd <directory>\n"); |
||||||
|
for (;;) { |
||||||
|
char *argument; |
||||||
|
show_prompt(); |
||||||
|
if (read_line(line, sizeof(line)) == 0) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
argument = line; |
||||||
|
while (*argument != '\0' && *argument != ' ') { |
||||||
|
++argument; |
||||||
|
} |
||||||
|
if (*argument != '\0') { |
||||||
|
*argument++ = '\0'; |
||||||
|
while (*argument == ' ') { |
||||||
|
++argument; |
||||||
|
} |
||||||
|
} |
||||||
|
if (string_equal(line, "ls") || string_equal(line, "cd")) { |
||||||
|
if (typephp_syscall(TYPEPHP_SYS_EXEC, |
||||||
|
(long) line, (long) argument, 0) < 0) { |
||||||
|
typephp_write("sh: unable to execute command\n"); |
||||||
|
} |
||||||
|
} else { |
||||||
|
typephp_write("sh: command not found: "); |
||||||
|
typephp_write(line); |
||||||
|
typephp_write("\n"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,57 @@ |
|||||||
|
#ifndef TYPEPHP_OS_USER_SYSCALL_H |
||||||
|
#define TYPEPHP_OS_USER_SYSCALL_H |
||||||
|
|
||||||
|
typedef unsigned long size_t; |
||||||
|
|
||||||
|
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, |
||||||
|
}; |
||||||
|
|
||||||
|
static inline long typephp_syscall( |
||||||
|
long number, long first, long second, long third) |
||||||
|
{ |
||||||
|
register long rax __asm__("rax") = number; |
||||||
|
register long rdi __asm__("rdi") = first; |
||||||
|
register long rsi __asm__("rsi") = second; |
||||||
|
register long rdx __asm__("rdx") = third; |
||||||
|
__asm__ volatile("int $0x80" |
||||||
|
: "+a"(rax) |
||||||
|
: "D"(rdi), "S"(rsi), "d"(rdx) |
||||||
|
: "rcx", "r11", "memory"); |
||||||
|
return rax; |
||||||
|
} |
||||||
|
|
||||||
|
static inline size_t typephp_strlen(const char *value) |
||||||
|
{ |
||||||
|
size_t length = 0; |
||||||
|
while (value[length] != '\0') { |
||||||
|
++length; |
||||||
|
} |
||||||
|
return length; |
||||||
|
} |
||||||
|
|
||||||
|
static inline void typephp_write_bytes(const char *value, size_t length) |
||||||
|
{ |
||||||
|
(void) typephp_syscall(TYPEPHP_SYS_WRITE, 1, (long) value, (long) length); |
||||||
|
} |
||||||
|
|
||||||
|
static inline void typephp_write(const char *value) |
||||||
|
{ |
||||||
|
typephp_write_bytes(value, typephp_strlen(value)); |
||||||
|
} |
||||||
|
|
||||||
|
static inline void typephp_exit(long status) |
||||||
|
{ |
||||||
|
(void) typephp_syscall(TYPEPHP_SYS_EXIT, status, 0, 0); |
||||||
|
for (;;) { |
||||||
|
__asm__ volatile("pause"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
||||||
Loading…
Reference in new issue