- 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