Merge pull request 'Doom port' (#21) from doomed into main

Reviewed-on: #21
This commit was merged in pull request #21.
This commit is contained in:
2026-05-11 10:58:13 +02:00
23 changed files with 49468 additions and 190 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
USER_PROGRAMS := pedicel.raw apex.raw USER_PROGRAMS := pedicel.raw apex.raw doom.raw
USER_FILES := wow.txt USER_FILES := wow.txt doom1.wad
BUILDDIR := build BUILDDIR := build
ELFFILE := pepperk ELFFILE := pepperk
+1 -9
View File
@@ -41,12 +41,4 @@ The recommended hardware to run PepperOS is the following:
## III. Syscall table ## III. Syscall table
The syscall interface in the Pepper kernel uses the System V ABI convention for argument order. The syscall interface in the Pepper kernel uses the System V ABI convention for argument order.
It vaguely mimics Unix-like systems. It vaguely mimics Unix-like systems. You will find it in [SYSCALLS.md](SYSCALLS.md).
Name | Number (%rax) | arg0 (%rdi) | arg1 (%rsi) | arg2 (%rdx) |
|---|---|---|---|---|
| sys_read | 0 | unsigned int fd | char* buf | size_t count |
| sys_write | 1 | unsigned int fd | const char* buf | size_t count |
| sys_open | 2 | const char* filename | int flags | |
| sys_close | 3 | unsigned int fd | | |
| sys_exit | 60 | int error_code | | |
+15
View File
@@ -6,6 +6,21 @@ Honestly I have no idea. Maybe you have too much free time.
Keep in mind that the Pepper kernel is a personal project and it's full of bugs, inconsistencies, weird ways of doing things (and I don't care because it's my toy). Keep in mind that the Pepper kernel is a personal project and it's full of bugs, inconsistencies, weird ways of doing things (and I don't care because it's my toy).
Now if you still want to write something for this OS, thank you. Follow along. Now if you still want to write something for this OS, thank you. Follow along.
## Headers available in userspace
Of course, all of the freestanding headers are available:
- `<float.h>`: macros for floating-point types
- `<limits.h>`: macros for integer types
- `<iso646.h>`: macros for bitwise and logical operators
- `<stdarg.h>`: variadic function support
- `<stddef.h>`: definitions for `size_t`, `ptrdiff_t`, and others
- `<stdbool.h>`: definitions for boolean types
- `<stdint.h>`: definitions for `int_t` and `uint_t` types
Also available is the `<syscall.h>` header that gives access to low-level system call interface, notably the `syscallX` function family, X being the amount of arguments to use.
(TODO: put the other headers here once libc is more complete)
## 1. Write the source code ## 1. Write the source code
PepperOS is able to run programs written in x86 assembly, and C programs. PepperOS is able to run programs written in x86 assembly, and C programs.
+15
View File
@@ -0,0 +1,15 @@
# Pepper kernel system call table
The following table contains all of the system calls supported by PepperOS, as well as their arguments. The explanation for what every system call does is available as a comment above each function in corresponding files in the `src/syscall` folder.
Name | Number (%rax) | arg0 (%rdi) | arg1 (%rsi) | arg2 (%rdx) | arg3 (%r10) |
|---|---|---|---|---|---|
| sys_read | 0 | unsigned int fd | char* buf | size_t count | |
| sys_write | 1 | unsigned int fd | const char* buf | size_t count | |
| sys_open | 2 | const char* filename | int flags | | |
| sys_close | 3 | unsigned int fd | | | |
| sys_lseek | 8 | unsigned int fd | int offset | int whence | |
| sys_tell | 9 | unsigned int fd | | | |
| sys_eof | 10 | unsigned int fd | | | |
| sys_draw | 11 | const uint8_t* src | int width | int height | int channels |
| sys_exit | 60 | int error_code | | | |
+2
View File
@@ -44,6 +44,8 @@
#define USER_STACK_TOP 0x80000000 #define USER_STACK_TOP 0x80000000
#define USER_STACK_PAGES 16 // 16*4096 = 64kb #define USER_STACK_PAGES 16 // 16*4096 = 64kb
#define USER_CODE_START 0x400000 // like linux #define USER_CODE_START 0x400000 // like linux
#define USER_RAW_EXTRA_PAGES 8192 // Extra writable pages after raw image for .bss/heap
// TODO: throw this away and make an ELF loader instead bruh
/* paging */ /* paging */
#define PAGING_MAX_PHYS 0x200000000 #define PAGING_MAX_PHYS 0x200000000
+6
View File
@@ -15,4 +15,10 @@ int tar_exists(const char* filename);
int tar_read(char* filename, char* out, int count, int offset); int tar_read(char* filename, char* out, int count, int offset);
void tar_list(); void tar_list();
enum Seek {
SEEK_SET,
SEEK_CUR,
SEEK_END
};
#endif #endif
+1
View File
@@ -9,6 +9,7 @@
#include "limine.h" #include "limine.h"
// Not in POSIX order.
enum ErrorCodes { enum ErrorCodes {
ENOMEM, // No memory ENOMEM, // No memory
EIO, // Input/output error EIO, // Input/output error
+1
View File
@@ -14,5 +14,6 @@ char *strcat(char *dest, const char *src);
void strncpy(char* dst, const char* src, size_t n); void strncpy(char* dst, const char* src, size_t n);
int strncmp(const char* s1, const char* s2, size_t n); int strncmp(const char* s1, const char* s2, size_t n);
size_t strlen(const char* str); size_t strlen(const char* str);
int atoi(const char* str);
#endif #endif
-160
View File
@@ -1,160 +0,0 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief System call handling
* @license GPL-3.0-only
*/
#include "config.h"
#include "sched/scheduler.h"
#include <arch/x86.h>
#include <kernel.h>
#include <stddef.h>
#include <io/term/term.h>
#include <sched/process.h>
#include <io/kbd/ps2.h>
#include <fs/initfs.h>
#include <string/string.h>
extern struct process* current_process;
// Return fd on success, -errno on error
int sys_open(const char* filename, int flags)
{
if (tar_exists(filename) < 0) {
return -ENOENT; // file doesn't exist..
}
// file exists here!
if (current_process->next_free_fd >= FDT_MAX) {
return -EMFILE;
}
int fd = current_process->next_free_fd++;
current_process->fdt[fd].fd = fd;
current_process->fdt[fd].open = true;
current_process->fdt[fd].cursor = 0;
strncpy(current_process->fdt[fd].filename, filename, PROCESS_NAME_MAX - 1);
return fd;
}
// Return 0 on success, -EBADFD if invalid FD
int sys_close(int fd)
{
if (fd < 0 || fd >= FDT_MAX) {
return -EBADFD;
}
if (!current_process->fdt[fd].open) {
return -EBADFD; // FD not opened in the first place
}
current_process->fdt[fd].open = false;
current_process->fdt[fd].filename[0] = '\0';
current_process->fdt[fd].cursor = 0;
return 0;
}
// Should return the number of bytes read
int sys_read(unsigned int fd, char* buf, size_t count)
{
size_t i;
switch (fd) {
case 0: //read from stdin (keyboard)
for (i=0; i<count; i++) {
buf[i] = keyboard_getchar();
}
return i;
case 1: // from stdout
case 2: // from stderr
return -EBADFD;
default: // from an open file?
if (current_process->fdt[fd].open == false) {
return -EBADFD; // File descriptor wasn't open
}
// Here fd refers to a valid opened file..
int sz = tar_read(current_process->fdt[fd].filename, buf, count,
current_process->fdt[fd].cursor);
if (sz == 0) {
return -ENOENT;
} else {
current_process->fdt[fd].cursor += sz;
return sz;
}
}
return -EBADFD;
}
// TODO: Should have a return value: number of bytes written on success, -1 on error (errno set)
int sys_write(unsigned int fd, const char* buf, size_t count)
{
switch (fd) {
case 1: //stdout
case 2: //stderr
for (size_t i=0; i<count; i++) {
internal_putc(buf[i], NULL);
}
return count;
default:
return -EBADFD;
}
}
int sys_exit(int error_code)
{
current_process->status = DEAD;
DEBUG("(pid=%u, name=%s)", current_process->pid, current_process->name);
return error_code;
}
/*
* syscall_handler - System call dispatcher
* @regs: CPU state
*
* This function is called from the interrupt dispatcher,
* when an interrupt 0x80 is emitted from userland.
*
* It switches control to the syscall number provided
* in %rax.
*
* We try to follow the System V convention here:
* - syscall number in %rax
* - args in %rdi, %rsi, %rdx, %r10, %r8, %r9
* - return value (if any) in %rax
*
* Return:
* <regs> - CPU state after system call
*/
struct cpu_status* syscall_handler(struct cpu_status* regs)
{
switch (regs->rax)
{
case 0:
DEBUG("sys_read(fd=%u, buf=%p, count=%u)", regs->rdi, regs->rsi, regs->rdx);
regs->rax = sys_read(regs->rdi, (char*)regs->rsi, regs->rdx);
break;
case 1:
DEBUG("sys_write(fd=%u, buf=%p, count=%u)", regs->rdi, regs->rsi, regs->rdx);
regs->rax = sys_write(regs->rdi, (char*)regs->rsi, regs->rdx);
break;
case 2:
DEBUG("sys_open(filename=%s, flags=%u)", regs->rdi, regs->rsi);
regs->rax = sys_open((const char*)regs->rdi, regs->rsi);
break;
case 3:
DEBUG("sys_close(fd=%u)", regs->rdi);
regs->rax = sys_close(regs->rdi);
break;
case 60:
DEBUG("sys_exit(error_code=%d)", regs->rdi);
regs->rax = sys_exit(regs->rdi);
break;
default:
DEBUG("Bad syscall! (rax=%p, rdi=%p, rsi=%p, rdx=%p)",
regs->rax, regs->rdi, regs->rsi, regs->rdx);
regs->rax = 0xbad515ca11;
break;
}
DEBUG("returned rax=%p (%u)", regs->rax, regs->rax);
return regs;
}
+16
View File
@@ -50,9 +50,17 @@ void internal_putc(int c, void *_)
if (init.terminal) { if (init.terminal) {
if (panic_count == 0) { if (panic_count == 0) {
spinlock_acquire(&term_lock); spinlock_acquire(&term_lock);
if (ch == '\n') {
char cr = '\r';
flanterm_write(ft_ctx, &cr, 1);
}
flanterm_write(ft_ctx, &ch, 1); flanterm_write(ft_ctx, &ch, 1);
spinlock_release(&term_lock); spinlock_release(&term_lock);
} else { } else {
if (ch == '\n') {
char cr = '\r';
flanterm_write(ft_ctx, &cr, 1);
}
flanterm_write(ft_ctx, &ch, 1); flanterm_write(ft_ctx, &ch, 1);
} }
} }
@@ -82,9 +90,17 @@ void debug_putc(int c, void *_)
if (init.terminal && (!init.all || panic_count > 0)) { if (init.terminal && (!init.all || panic_count > 0)) {
if (panic_count == 0) { if (panic_count == 0) {
spinlock_acquire(&term_lock); spinlock_acquire(&term_lock);
if (ch == '\n') {
char cr = '\r';
flanterm_write(ft_ctx, &cr, 1);
}
flanterm_write(ft_ctx, &ch, 1); flanterm_write(ft_ctx, &ch, 1);
spinlock_release(&term_lock); spinlock_release(&term_lock);
} else { } else {
if (ch == '\n') {
char cr = '\r';
flanterm_write(ft_ctx, &cr, 1);
}
flanterm_write(ft_ctx, &ch, 1); flanterm_write(ft_ctx, &ch, 1);
} }
} }
+69 -9
View File
@@ -13,6 +13,7 @@
#include <kernel.h> #include <kernel.h>
#include <time/date.h> #include <time/date.h>
#include <mem/kheap.h> #include <mem/kheap.h>
#include <sched/process.h>
__attribute__((noinline)) __attribute__((noinline))
void smash_it() void smash_it()
@@ -23,6 +24,52 @@ void smash_it()
} }
} }
extern struct process* processes_list;
void ps()
{
printf("pid\tname\tstatus\n");
struct process* curr = processes_list;
while (curr != NULL) {
char* status;
switch (curr->status) {
case READY: status = "READY"; break;
case RUNNING: status = "RUNNING"; break;
case DEAD: status = "DEAD"; break;
default: status = "N/A"; break;
}
printf("%u\t%s\t%s\n", curr->pid, curr->name, status);
if (curr->next != NULL) {
curr = curr->next;
} else {
break;
}
}
}
void kill()
{
char input_buf[11] = {0};
printf("pid> ");
keyboard_getline(input_buf, 10);
int pid = atoi(input_buf);
struct process* curr = processes_list;
while (curr != NULL) {
if (curr->pid == (size_t)pid) {
curr->status = DEAD; // equivalent of SIGKILL (no clean termination like SIGTERM)
printf("killed %d\n", pid);
break;
}
if (curr->next != NULL) {
curr = curr->next;
} else {
printf("couldn't find process with PID %d\n", pid);
break;
}
}
}
/* /*
* pedicel_main - Kernel shell main function * pedicel_main - Kernel shell main function
* @arg: argument (optional) * @arg: argument (optional)
@@ -35,6 +82,7 @@ void smash_it()
*/ */
void pedicel_main(void* arg) void pedicel_main(void* arg)
{ {
(void)arg;
printf("Welcome to the kernel shell!\r\nType 'help' for a list of commands.\r\n"); printf("Welcome to the kernel shell!\r\nType 'help' for a list of commands.\r\n");
for (;;) { for (;;) {
@@ -44,15 +92,17 @@ void pedicel_main(void* arg)
if (strncmp(input_buf, "help", 4) == 0) { if (strncmp(input_buf, "help", 4) == 0) {
printf("++ shell builtins ++\r\n\r\n" printf("++ shell builtins ++\r\n\r\n"
"\tclear - clear the screen\r\n" "\tclear - clear the screen\n"
"\tpanic - trigger a test panic\r\n" "\tpanic - trigger a test panic\n"
"\tsyscall - trigger int 0x80\r\n" "\tsyscall - trigger int 0x80\n"
"\tpf - trigger a page fault\r\n" "\tpf - trigger a page fault\n"
"\tnow - get current date\r\n" "\tnow - get current date\n"
"\tsmash - smash the stack\r\n" "\tsmash - smash the stack\n"
"\tmem - get used heap info\r\n" "\tmem - get used heap info\n"
"\tload - load an user executable\r\n" "\tload - load an user executable\n"
"\tlist - list initfs.tar contents\r\n"); "\tlist - list initfs.tar contents\n"
"\tps - list running processes\n"
"\tkill - kill a running process by PID\n");
continue; continue;
} }
@@ -107,6 +157,16 @@ void pedicel_main(void* arg)
continue; continue;
} }
if (strncmp(input_buf, "ps", 2) == 0) {
ps();
continue;
}
if (strncmp(input_buf, "kill", 4) == 0) {
kill();
continue;
}
printf("%s: command not found\r\n", input_buf); printf("%s: command not found\r\n", input_buf);
} }
} }
+1
View File
@@ -73,6 +73,7 @@ struct process* idle_proc;
void idle_main(void* arg) void idle_main(void* arg)
{ {
(void)arg;
for (;;) { for (;;) {
asm("hlt"); asm("hlt");
} }
+5 -1
View File
@@ -18,6 +18,7 @@ compared to the PMM which allocs/frees 4kb frames ("physical pages").
#include <mem/paging.h> #include <mem/paging.h>
#include <stddef.h> #include <stddef.h>
#include <mem/pmm.h> #include <mem/pmm.h>
#include <mem/utils.h>
#include <kernel.h> #include <kernel.h>
extern uint64_t *kernel_pml4; extern uint64_t *kernel_pml4;
@@ -116,6 +117,8 @@ void* vmm_map(uint64_t* pml4, uint64_t virt, uint64_t flags)
panic(NULL, "VMM/PMM out of memory!"); panic(NULL, "VMM/PMM out of memory!");
} }
memset(PHYS_TO_VIRT(phys), 0, PAGE_SIZE);
paging_map_page(pml4, virt, phys, flags | PTE_PRESENT); paging_map_page(pml4, virt, phys, flags | PTE_PRESENT);
return (void*)virt; return (void*)virt;
} }
@@ -255,8 +258,9 @@ uintptr_t vmm_alloc_user_code(uint64_t* pml4, void* code_addr, uint64_t code_siz
// Round code_size up to next page boundary // Round code_size up to next page boundary
uint64_t code_size_aligned = (code_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); uint64_t code_size_aligned = (code_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
uint64_t mapped_size = code_size_aligned + ((uint64_t)USER_RAW_EXTRA_PAGES * PAGE_SIZE);
for (uint64_t i=code_start; i<code_start+code_size_aligned; i+=PAGE_SIZE) { for (uint64_t i=code_start; i<code_start+mapped_size; i+=PAGE_SIZE) {
vmm_map(pml4, i, PTE_PRESENT | PTE_WRITABLE | PTE_USER); vmm_map(pml4, i, PTE_PRESENT | PTE_WRITABLE | PTE_USER);
} }
+27 -2
View File
@@ -71,7 +71,6 @@ void strncpy(char* dst, const char* src, size_t n)
while(i++ != n && (*dst++ = *src++)); while(i++ != n && (*dst++ = *src++));
} }
/* /*
* strncmp - compare two strings up to n characters * strncmp - compare two strings up to n characters
* @s1: first string * @s1: first string
@@ -100,10 +99,36 @@ int strncmp(const char* s1, const char* s2, size_t n)
} }
} }
// BSD implementation /*
* strlen - get length of a string (BSD implementation)
* @str: NULL-terminated string
*
* Return:
* %len - length of string
*/
size_t strlen(const char* str) size_t strlen(const char* str)
{ {
const char* s; const char* s;
for (s = str; *s; ++s); for (s = str; *s; ++s);
return (s - str); return (s - str);
} }
/*
* atoi - ascii to integer (PureDOOM implementation)
* @str: ASCII string to convert
*
* Return:
* %i - integer represented in @str
*/
int atoi(const char* str)
{
int i = 0;
int c;
while ((c = *str++) != 0) {
i *= 10;
i += c - '0';
}
return i;
}
+252
View File
@@ -0,0 +1,252 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief File-related system calls
* @license GPL-3.0-only
*/
#include <stddef.h>
#include <kernel.h>
#include <config.h>
#include <sched/process.h>
#include <string/string.h>
#include <fs/initfs.h>
#include <io/kbd/ps2.h>
extern struct process* current_process;
/*
* normalize_path - remove leading slashes from a path
* @path: path to normalize
*
* Return:
* %path - normalized path
* %NULL - if bad argument
*/
static const char* normalize_path(const char* path)
{
if (!path) return NULL;
while (*path == '/') {
path++;
}
return path;
}
/*
* sys_open - Open a file
* @filename: Absolute path to file
* @flags: (Not implemented)
*
* This system call opens a file in read-only mode,
* because of the TAR filesystem.
*
* Return:
* %fd - file descriptor refering to file
* On error, a negative number representing the error code is returned.
*/
int sys_open(const char* filename, int flags)
{
// TODO: support flags (right now everything is read only, O_RDONLY)
(void)flags; // gcc shut up
const char* path = normalize_path(filename);
if (tar_exists(path) < 0) {
return -ENOENT; // file doesn't exist..
}
// file exists here!
if (current_process->next_free_fd >= FDT_MAX) {
return -EMFILE;
}
int fd = current_process->next_free_fd++;
current_process->fdt[fd].fd = fd;
current_process->fdt[fd].open = true;
current_process->fdt[fd].cursor = 0;
strncpy(current_process->fdt[fd].filename, path, PROCESS_NAME_MAX - 1);
return fd;
}
/*
* sys_close - Close a file
* @fd: file descriptor
*
* This system call closes the file referred to by
* the file descriptor @fd.
*
* Return:
* %0 - file closed
* %-EBADFD - bad file descriptor
*/
int sys_close(unsigned int fd)
{
if (fd >= FDT_MAX) {
return -EBADFD;
}
if (!current_process->fdt[fd].open) {
return -EBADFD; // FD not opened in the first place
}
current_process->fdt[fd].open = false;
current_process->fdt[fd].filename[0] = '\0';
current_process->fdt[fd].cursor = 0;
return 0;
}
/*
* sys_lseek - reposition file cursor
* @fd: file descriptor referring to file
* @offset: byte amount to be used according to @whence
* @whence: resposition directive
*
* This system call repositions the cursor for @fd by @offset,
* from somewhere in the file, depending on the value of @whence:
* SEEK_SET (0) -> from the beginning
* SEEK_CUR (1) -> from the actual value of the cursor
* SEEK_END (2) -> from the end of the file
*
* Return:
* %new_cursor - the new cursor value
* On error, a negative value corresponding to an error code is returned.
*/
int sys_lseek(unsigned int fd, int offset, int whence)
{
if (fd >= FDT_MAX) {
return -EBADFD;
}
if (!current_process->fdt[fd].open) {
return -EBADFD;
}
int filesize = tar_exists(current_process->fdt[fd].filename);
if (filesize < 0) {
return -ENOENT;
}
int cursor = current_process->fdt[fd].cursor;
int base;
switch (whence) {
case SEEK_SET: base = 0; break;
case SEEK_CUR: base = cursor; break;
case SEEK_END: base = filesize; break;
default: return -EINVAL;
}
int new_cursor = base + offset;
if (new_cursor < 0) new_cursor = 0;
if (new_cursor > filesize) new_cursor = filesize;
current_process->fdt[fd].cursor = new_cursor;
return new_cursor;
}
/*
* sys_tell - Get cursor position for a file
* @fd: file descriptor to use
*
* Return:
* %cursor - cursor position of the file descriptor
* On error, a negative value with the corresponding error code is set.
*/
int sys_tell(unsigned int fd)
{
if (fd >= FDT_MAX) {
return -EBADFD;
}
if (!current_process->fdt[fd].open) {
return -EBADFD;
}
return current_process->fdt[fd].cursor;
}
/*
* sys_eof - Are we at the end of the file yet?
* @fd: file descriptor to use
*
* This function determines if the cursor for the
* file descriptor @fd is at or past the end of
* the file it refers to.
*
* Return:
* %>0 - we are at or past EOF
* %0 - not yet (still have bytes to read)
*/
int sys_eof(unsigned int fd)
{
if (fd >= FDT_MAX) {
return -EBADFD;
}
if (!current_process->fdt[fd].open) {
return -EBADFD;
}
int filesize = tar_exists(current_process->fdt[fd].filename);
if (filesize < 0) {
return -ENOENT;
}
return current_process->fdt[fd].cursor >= (uint64_t)filesize;
}
/*
* sys_read - read from an open file
* @fd: file descriptor to use
* @buf: out buffer for read data
* @count: amount of bytes to read
*
* Return:
* %sz - amount of bytes read (on success)
* On error, a negative value with an error code is returned.
*/
int sys_read(unsigned int fd, char* buf, size_t count)
{
size_t i;
switch (fd) {
case 0: //read from stdin (keyboard)
for (i=0; i<count; i++) {
buf[i] = keyboard_getchar();
}
return i;
case 1: // from stdout
case 2: // from stderr
return -EBADFD;
default: // from an open file?
if (current_process->fdt[fd].open == false) {
return -EBADFD; // File descriptor wasn't open
}
// Here fd refers to a valid opened file..
int sz = tar_read(current_process->fdt[fd].filename, buf, count,
current_process->fdt[fd].cursor);
if (sz == 0) {
return -ENOENT;
} else {
current_process->fdt[fd].cursor += sz;
return sz;
}
}
return -EBADFD;
}
/*
* sys_write - write to a file
* @fd: file descriptor to write to
* @buf: buffer of bytes to write
* @count: number of bytes to write
*
* Return:
* %count - number of bytes written
* On error, a negative value with corresponding error code is returned.
*/
int sys_write(unsigned int fd, const char* buf, size_t count)
{
switch (fd) {
case 1: //stdout
case 2: //stderr
for (size_t i=0; i<count; i++) {
internal_putc(buf[i], NULL);
}
return count;
default:
return -EBADFD;
}
}
+28
View File
@@ -0,0 +1,28 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Process-wide system calls
* @license GPL-3.0-only
*/
#include <kernel.h>
#include <sched/process.h>
extern struct process* current_process;
/*
* sys_exit - Terminate the current process
* @error_code: error code to return
*
* This system call exits the running process. Be aware that
* it does nothing except that. All resources are supposed to
* be cleaned up before calling this.
*
* Return:
* %error_code - the error code to return
*/
int sys_exit(int error_code)
{
current_process->status = DEAD;
DEBUG("(pid=%u, name=%s)", current_process->pid, current_process->name);
return error_code;
}
+105
View File
@@ -0,0 +1,105 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Screen/framebuffer system calls
* @license GPL-3.0-only
*/
// This is not part of POSIX or anything btw
#include <stdint.h>
#include <kernel.h>
extern struct boot_context boot_ctx;
/*
* pack_rbga_to_fb - Convert rgb values to rgba pixel for framebuffer
* @r: red value
* @g: green value
* @b: blue value
*
* This function uses the mask sizes and shifts of the Limine
* framebuffer to convert raw rgb values into usable pixels.
*
* Return:
* <pixel> - usable pixel
*/
static uint32_t pack_rgba_to_fb(uint8_t r, uint8_t g, uint8_t b)
{
uint32_t pixel = 0;
if (boot_ctx.fb->red_mask_size) {
pixel |= ((uint32_t)(r >> (8 - boot_ctx.fb->red_mask_size)) << boot_ctx.fb->red_mask_shift);
}
if (boot_ctx.fb->green_mask_size) {
pixel |= ((uint32_t)(g >> (8 - boot_ctx.fb->green_mask_size)) << boot_ctx.fb->green_mask_shift);
}
if (boot_ctx.fb->blue_mask_size) {
pixel |= ((uint32_t)(b >> (8 - boot_ctx.fb->blue_mask_size)) << boot_ctx.fb->blue_mask_shift);
}
return pixel;
}
/*
* sys_draw - Draw a framebuffer subset
* @src: Source frame
* @width: width of the frame
* @height: height of the frame
* @channels: how many channels (RGB, RGBA)
*
* This system call draws the frame @src, having some
* @width and @height, in the top right of the Limine
* framebuffer. (Used for DOOM)
*
* Return:
* %0 - on success
* On error, a negative value with an error code is returned.
*/
int sys_draw(const uint8_t* src, int width, int height, int channels)
{
if (!boot_ctx.fb || !src) {
return -EINVAL;
}
if (channels != 4 || width <= 0 || height <= 0) {
return -EINVAL;
}
if (boot_ctx.fb->bpp < 24) {
return -EIO;
}
uint32_t* dst = (uint32_t*)boot_ctx.fb->address;
uint64_t dst_w = boot_ctx.fb->width;
uint64_t dst_h = boot_ctx.fb->height;
uint64_t dst_pitch_px = boot_ctx.fb->pitch / 4;
uint64_t scale = 2;
uint64_t scaled_w = (uint64_t)width * scale;
uint64_t scaled_h = (uint64_t)height * scale;
if (scaled_w > dst_w || scaled_h > dst_h) {
return -EINVAL;
}
uint64_t dst_x = dst_w - scaled_w;
uint64_t dst_y = 0;
for (uint64_t y = 0; y < (uint64_t)height; ++y) {
const uint8_t* src_row = src + y * (uint64_t)width * 4ULL;
uint32_t* dst_row0 = dst + (dst_y + y * scale) * dst_pitch_px + dst_x;
uint32_t* dst_row1 = dst_row0 + dst_pitch_px;
for (uint64_t x = 0; x < (uint64_t)width; ++x) {
const uint8_t* p = src_row + x * 4ULL;
uint32_t pixel = pack_rgba_to_fb(p[0], p[1], p[2]);
uint64_t dx = x * scale;
// Write 2x2 block. This is because source DOOM frame (320x200)
// is quite small.
dst_row0[dx + 0] = pixel;
dst_row0[dx + 1] = pixel;
dst_row1[dx + 0] = pixel;
dst_row1[dx + 1] = pixel;
}
}
return 0;
}
+98
View File
@@ -0,0 +1,98 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief System call handling
* @license GPL-3.0-only
*/
#include "config.h"
#include "sched/scheduler.h"
#include <arch/x86.h>
#include <kernel.h>
#include <stddef.h>
#include <io/term/term.h>
#include <sched/process.h>
#include <io/kbd/ps2.h>
#include <fs/initfs.h>
#include <string/string.h>
#include <mem/utils.h>
extern struct process* current_process;
int sys_read(unsigned int fd, char* buf, size_t count);
int sys_write(unsigned int fd, const char* buf, size_t count);
int sys_open(const char* filename, int flags);
int sys_close(unsigned int fd);
int sys_lseek(unsigned int fd, int offset, int whence);
int sys_tell(unsigned int fd); // needed by doom, therefore TOP PRIORITY
int sys_eof(unsigned int fd); // same
int sys_draw(const uint8_t* src, int width, int height, int channels);
int sys_exit(int error_code);
/*
* syscall_handler - System call dispatcher
* @regs: CPU state
*
* This function is called from the interrupt dispatcher,
* when an interrupt 0x80 is emitted from userland.
*
* It switches control to the syscall number provided
* in %rax.
*
* We try to follow the System V convention here:
* - syscall number in %rax
* - args in %rdi, %rsi, %rdx, %r10, %r8, %r9
* - return value (if any) in %rax
*
* Return:
* <regs> - CPU state after system call
*/
struct cpu_status* syscall_handler(struct cpu_status* regs)
{
switch (regs->rax)
{
case 0:
DEBUG("sys_read(fd=%u, buf=%p, count=%u)", regs->rdi, regs->rsi, regs->rdx);
regs->rax = sys_read(regs->rdi, (char*)regs->rsi, regs->rdx);
break;
case 1:
DEBUG("sys_write(fd=%u, buf=%p, count=%u)", regs->rdi, regs->rsi, regs->rdx);
regs->rax = sys_write(regs->rdi, (char*)regs->rsi, regs->rdx);
break;
case 2:
DEBUG("sys_open(filename=%s, flags=%u)", regs->rdi, regs->rsi);
regs->rax = sys_open((const char*)regs->rdi, regs->rsi);
break;
case 3:
DEBUG("sys_close(fd=%u)", regs->rdi);
regs->rax = sys_close(regs->rdi);
break;
case 8:
DEBUG("sys_lseek(fd=%u, off=%d, whence=%u)", regs->rdi, regs->rsi, regs->rdx);
regs->rax = sys_lseek(regs->rdi, regs->rsi, regs->rdx);
break;
case 9:
DEBUG("sys_tell(fd=%u)", regs->rdi);
regs->rax = sys_tell(regs->rdi);
break;
case 10:
DEBUG("sys_eof(fd=%u)", regs->rdi);
regs->rax = sys_eof(regs->rdi);
break;
case 11: // No DEBUG() here because it makes significant overhead
regs->rax = sys_draw((const uint8_t*)regs->rdi, regs->rsi, regs->rdx, regs->r10);
break;
case 60:
DEBUG("sys_exit(error_code=%d)", regs->rdi);
regs->rax = sys_exit(regs->rdi);
break;
default:
DEBUG("Bad syscall! (rax=%p, rdi=%p, rsi=%p, rdx=%p)",
regs->rax, regs->rdi, regs->rsi, regs->rdx);
regs->rax = 0xbad515ca11;
break;
}
//DEBUG("returned rax=%p (%u)", regs->rax, regs->rax);
return regs;
}
+11 -1
View File
@@ -1,18 +1,28 @@
CC := x86_64-elf-gcc CC := x86_64-elf-gcc
CC_FLAGS := -ffreestanding -nostdlib -fno-pic -mno-red-zone -Ilibc CC_FLAGS := -ffreestanding -nostdlib -fno-pic -mno-red-zone -Ilibc
DOOM_CC_FLAGS := $(CC_FLAGS) -std=gnu11
LD := x86_64-elf-ld LD := x86_64-elf-ld
BUILDDIR := ../build BUILDDIR := ../build
LIBDIR := libc LIBDIR := libc
all: pedicel apex all: pedicel apex doom
.PHONY: pedicel
pedicel: pedicel:
nasm -f bin pedicel.S -o $(BUILDDIR)/pedicel.raw nasm -f bin pedicel.S -o $(BUILDDIR)/pedicel.raw
.PHONY: apex
apex: apex:
$(CC) $(CC_FLAGS) -c apex.c -o $(BUILDDIR)/apex.o $(CC) $(CC_FLAGS) -c apex.c -o $(BUILDDIR)/apex.o
nasm -f elf64 $(LIBDIR)/crt0.S -o $(BUILDDIR)/crt0.o nasm -f elf64 $(LIBDIR)/crt0.S -o $(BUILDDIR)/crt0.o
$(LD) -T $(LIBDIR)/linker.ld $(BUILDDIR)/crt0.o $(BUILDDIR)/apex.o -o $(BUILDDIR)/apex.elf $(LD) -T $(LIBDIR)/linker.ld $(BUILDDIR)/crt0.o $(BUILDDIR)/apex.o -o $(BUILDDIR)/apex.elf
objcopy -O binary $(BUILDDIR)/apex.elf $(BUILDDIR)/apex.raw objcopy -O binary $(BUILDDIR)/apex.elf $(BUILDDIR)/apex.raw
.PHONY: doom
doom:
$(CC) $(DOOM_CC_FLAGS) -c doom.c -o $(BUILDDIR)/doom.o
nasm -f elf64 $(LIBDIR)/crt0.S -o $(BUILDDIR)/crt0.o
$(LD) -T $(LIBDIR)/linker.ld $(BUILDDIR)/crt0.o $(BUILDDIR)/doom.o -o $(BUILDDIR)/doom.elf
objcopy -O binary $(BUILDDIR)/doom.elf $(BUILDDIR)/doom.raw
+48602
View File
File diff suppressed because it is too large Load Diff
+147
View File
@@ -0,0 +1,147 @@
#define DOOM_IMPLEMENTATION
#include "PureDOOM.h"
#include <syscall.h>
#include <stdint.h>
//We use a separate heap because malloc is not yet available in userspace.
#define DOOM_HEAP_START ((unsigned char*)0x00500000)
#define DOOM_HEAP_SIZE (24 * 1024 * 1024) //24mb
static unsigned char* doom_heap_curr = DOOM_HEAP_START;
static unsigned char* doom_heap_end = DOOM_HEAP_START + DOOM_HEAP_SIZE;
// The following functions are wrappers for system calls made for
// compatibility with puredoom's function signatures
static int doom_cstr_len(const char* str)
{
int len = 0;
while (str && str[len]) len++;
return len;
}
static void doom_print_cb(const char* str)
{
int len = doom_cstr_len(str);
if (len > 0) {
write(1, str, len);
}
}
static void* doom_open_cb(const char* filename, const char* mode)
{
(void)mode; // open doesn't support flags/mode (yet)
int fd = open(filename, 0);
if (fd < 0) return 0;
return (void*)(intptr_t)(fd);
}
static void doom_close_cb(void* handle)
{
int fd = (int)(intptr_t)handle;
if (fd >= 0) {
close(fd);
}
}
static int doom_read_cb(void* handle, void* buf, int count)
{
int fd = (int)(intptr_t)handle;
if (fd < 0) return -1;
return read(fd, (char*)buf, count);
}
static int doom_write_cb(void* handle, const void* buf, int count)
{
int fd = (int)(intptr_t)handle;
if (fd < 0) return -1;
return (int)write(fd, (const char*)buf, count);
}
static int doom_seek_cb(void* handle, int offset, doom_seek_t origin)
{
int fd = (int)(intptr_t)handle;
if (fd < 0) return -1;
return seek(fd, offset, (int)origin);
}
static int doom_tell_cb(void* handle)
{
int fd = (int)(intptr_t)handle;
if (fd < 0) return -1;
return tell(fd);
}
static int doom_eof_cb(void* handle)
{
int fd = (int)(intptr_t)handle;
if (fd < 0) return 1;
return eof(fd);
}
// Bump allocator (bump ptr until we're out of heap memory)
static void* doom_malloc_cb(int size)
{
if (size <= 0) return 0;
uintptr_t curr = (uintptr_t)doom_heap_curr;
// 16-byte align allocated blocks
curr = (curr + 15ULL) & ~15ULL;
if (curr + (uintptr_t)size > (uintptr_t)doom_heap_end) {
return 0;
}
doom_heap_curr = (unsigned char*)(curr + (uintptr_t)size);
return (void*)curr;
}
// No free
static void doom_free_cb(void* ptr)
{
(void)ptr;
}
static void doom_exit_cb(int code)
{
exit(code);
}
// To get path for the WAD file
static char* doom_getenv_cb(const char* var)
{
static char home[] = "/";
static char waddir[] = "/";
if (!var) return 0;
if (doom_strcmp(var, "HOME") == 0) return home;
if (doom_strcmp(var, "DOOMWADDIR") == 0) return waddir;
return 0;
}
int main()
{
char* argv[2] = {"doom", 0};
// Override default implementations
doom_set_print(doom_print_cb);
doom_set_malloc(doom_malloc_cb, doom_free_cb);
doom_set_file_io(doom_open_cb,
doom_close_cb,
doom_read_cb,
doom_write_cb,
doom_seek_cb,
doom_tell_cb,
doom_eof_cb);
doom_set_exit(doom_exit_cb);
doom_set_getenv(doom_getenv_cb);
doom_init(1, argv, 0);
while (true)
{
doom_force_update();
const uint8_t* framebuffer = doom_get_framebuffer(4);
draw(framebuffer, 320, 200, 4);
}
}
BIN
View File
Binary file not shown.
+63 -5
View File
@@ -1,21 +1,79 @@
#pragma once /*
* @author xamidev <xamidev@riseup.net>
* @brief System call wrappers for userspace
* @license GPL-3.0-only
*/
// 3 because 3 arguments to the call, get it?? #pragma once
// TODO: replace all ifndef/define/endif by pragma once..
#include <stddef.h>
#include <stdint.h>
// 3-args syscall
static inline long syscall3(long n, long a, long b, long c) { static inline long syscall3(long n, long a, long b, long c) {
long ret; long ret;
__asm__ volatile ( __asm__ volatile (
"int $0x80" "int $0x80"
: "=a"(ret) : "=a"(ret)
: "a"(n), "D"(a), "S"(b), "d"(c) : "a"(n), "D"(a), "S"(b), "d"(c) // a = rax, D = rdi, S = rsi, d = rdx
: "memory" : "memory"
); );
return ret; return ret;
} }
static inline void write(int fd, const char* buf, long len) { // 4-args syscall
syscall3(1, fd, (long)buf, len); static inline long syscall4(long n, long a, long b, long c, long d) {
long ret;
register long r10 __asm__("r10") = d;
__asm__ volatile (
"int $0x80"
: "=a"(ret)
: "a"(n), "D"(a), "S"(b), "d"(c), "r"(r10)
: "memory"
);
return ret;
}
// Single-arg syscall
static inline long syscall1(long n, long a) {
return syscall3(n, a, 0, 0);
}
static inline int write(int fd, const char* buf, long len) {
return (int)syscall3(1, fd, (long)buf, len);
}
static inline int read(int fd, char* buf, long len) {
return (int)syscall3(0, fd, (long)buf, len);
}
static inline int open(const char* path, int flags) {
return (int)syscall3(2, (long)path, flags, 0);
}
static inline int close(unsigned int fd) {
return (int)syscall3(3, fd, 0, 0);
}
static inline int seek(int fd, int offset, int whence) {
return (int)syscall3(8, fd, offset, whence);
}
static inline int tell(unsigned int fd) {
return (int)syscall1(9, fd);
}
static inline int eof(unsigned int fd) {
return (int)syscall1(10, fd);
}
static inline int draw(const unsigned char* buf, int width, int height, int channels) {
return (int)syscall4(11, (long)buf, width, height, channels);
} }
static inline void exit(int code) { static inline void exit(int code) {