15 Commits

Author SHA1 Message Date
xamidev 0274e00b55 DOOM target + launcher code 2026-05-11 10:43:18 +02:00
xamidev deebd5d432 fix syscalls YET AGAIN 2026-05-10 21:49:17 +02:00
xamidev 5dc6b1124d fix syscalls 2026-05-10 21:44:54 +02:00
xamidev 3bbdc998cd PureDOOM + WAD 2026-05-10 21:35:35 +02:00
xamidev 18ab2c7628 syscalls needed for doom (tell/eof/draw_fb) + minor fixes, compiler shut up etc 2026-05-10 21:25:41 +02:00
xamidev 22f20d47ad Line discipline for carriage return + ps/kill kshell commands 2026-05-10 19:37:32 +02:00
xamidev 1142699c48 Alloc extra pages for raw binary 2026-05-10 19:36:01 +02:00
xamidev 01911bdd32 atoi 2026-05-10 19:34:35 +02:00
xamidev d65a736012 freestanding headers 2026-05-10 19:33:29 +02:00
xamidev 5e0bd98874 Merge pull request 'should be right?' (#20) from sys_fix into main
Reviewed-on: #20
2026-05-08 12:59:05 +02:00
xamidev 8c5911bef9 Update README.md 2026-05-08 12:58:08 +02:00
xamidev eb8a03facd Load raw C binary + docs 2026-05-08 12:38:16 +02:00
xamidev 9a1a0e428a tar_list 2026-05-06 14:04:28 +02:00
xamidev c061da4d81 sys_read/open/close 2026-05-06 13:29:35 +02:00
xamidev 63e9a761a3 should be right? 2026-05-06 11:26:33 +02:00
34 changed files with 49718 additions and 239 deletions
+6 -5
View File
@@ -1,3 +1,5 @@
USER_PROGRAMS := pedicel.raw apex.raw doom.raw
USER_FILES := wow.txt doom1.wad
BUILDDIR := build BUILDDIR := build
ELFFILE := pepperk ELFFILE := pepperk
@@ -21,10 +23,6 @@ CC_FLAGS=-Wall -Wextra -std=gnu99 -nostdlib -ffreestanding -fstack-protector -fn
LD := x86_64-elf-ld LD := x86_64-elf-ld
$(ELFFILE): $(BUILDDIR) $(OBJFILES) $(ELFFILE): $(BUILDDIR) $(OBJFILES)
nasm -f bin user/hello.S -o $(BUILDDIR)/hello
nasm -f bin user/pedicel.S -o $(BUILDDIR)/pedicel
tar cvf $(BUILDDIR)/initfs.tar -C $(BUILDDIR) hello pedicel
nasm -f elf64 src/arch/x86/idt.S -o $(BUILDDIR)/idt_stub.o nasm -f elf64 src/arch/x86/idt.S -o $(BUILDDIR)/idt_stub.o
$(LD) -o $(ELFFILE) -T linker.ld $(OBJFILES) $(BUILDDIR)/idt_stub.o $(LD) -o $(ELFFILE) -T linker.ld $(OBJFILES) $(BUILDDIR)/idt_stub.o
# Get the symbols for debugging # Get the symbols for debugging
@@ -45,7 +43,10 @@ limine/limine:
git clone https://github.com/limine-bootloader/limine.git --branch=v9.x-binary --depth=1 git clone https://github.com/limine-bootloader/limine.git --branch=v9.x-binary --depth=1
$(MAKE) -C limine $(MAKE) -C limine
initfs: .PHONY: user
user:
$(MAKE) -C user
tar cvf $(BUILDDIR)/initfs.tar -C $(BUILDDIR) $(USER_PROGRAMS) -C ../user $(USER_FILES)
build-iso: limine/limine $(ELFFILE) build-iso: limine/limine $(ELFFILE)
rm -rf iso_root rm -rf iso_root
+6 -27
View File
@@ -1,5 +1,5 @@
# <img width="40" height="40" alt="red-pepper" src="https://i.ibb.co/mrHH6d1m/pixil-frame-0-4.png" /> pepperOS: "will never be done" # <img width="40" height="40" alt="red-pepper" src="https://i.ibb.co/mrHH6d1m/pixil-frame-0-4.png" /> pepperOS: "will never be done"
<a href="https://ibb.co/KxbfxmFB"><img src="https://i.ibb.co/GQn8QFcG/pepper.png" alt="pepper" border="0"></a>
## Description ## Description
PepperOS is a 64-bit freely-licensed monolithic kernel for x86 processors, with round-robin preemptive scheduling and 4-level paging. See the [manual](docs/MANUAL.md) for more. PepperOS is a 64-bit freely-licensed monolithic kernel for x86 processors, with round-robin preemptive scheduling and 4-level paging. See the [manual](docs/MANUAL.md) for more.
@@ -23,8 +23,9 @@ CC := gcc
LD := ld LD := ld
``` ```
Then, to compile the kernel and make an ISO image file, run: `make build-iso` Then, to compile the kernel and make an ISO image file, run: `make`.
To run it with QEMU, do: `make run` To build the user programs and the initial filesystem, do `make user`.
To run it with QEMU, do: `make run`.
## Trying the kernel on real hardware ## Trying the kernel on real hardware
@@ -48,31 +49,9 @@ These features can be activated by setting them to "true" at the end of the make
make UBSAN=true make UBSAN=true
``` ```
## TODO ## Writing software for PepperOS
The basics that I'm targeting are: If you want to write software for PepperOS, take a look at the [Software Developer's guide](docs/SOFTWARE.md).
### Basic utility of what we call a "kernel"
- Implement tasks, and task switching + context switching and spinlock acquire/release
- Load an executable
- Filesystem (TAR for read-only initfs, then maybe read-write using FAT12/16/32 or easier fs) w/ VFS layer
- Getting to userspace (ring 3 switching, syscall interface)
- Porting musl libc or equivalent
### Scalability/maintenance/expansion features
- Documentation
- SOME error handling in functions
- Unit tests
- Good error codes (like Linux kernel: ENOMEM, ENOENT, ...)
### Optional features
In the future, maybe?
- SMP support (Limine provides functionality to make this easier)
- Parsing the ACPI tables and using them for something
- Replacing the PIT timer with APIC
## Thanks ## Thanks
+1 -5
View File
@@ -41,8 +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. You will find it in [SYSCALLS.md](SYSCALLS.md).
Name | Number (%rax) | arg0 (%rdi) | arg1 (%rsi) | arg2 (%rdx) |
|---|---|---|---|---|
| sys_write | 1 | unsigned int fd | const char* buf | size_t count | |
| sys_exit | 60 | int error_code | | | |
+72
View File
@@ -0,0 +1,72 @@
# Writing software for PepperOS
## Why would you want to do that?
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).
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
PepperOS is able to run programs written in x86 assembly, and C programs.
### x86 Assembly
Start your assembly file with the `bits 64` instruction, to emit 64-bit code.
You can add sections `.text`, `.data`, `.bss` as you need.
The three things to take in consideration here are:
- PepperOS does not use the `syscall` instruction, instead it uses the old-fashioned `int 0x80` to trigger a system call.
- The entry point should be labelled as `_start`.
- At the end of the file, there should be an exit system call followed by a loop, like so:
```nasm
.end:
mov rax, 0x3C
mov rdi, 0x0
int 0x80
.loop:
jmp .loop
```
For an example, look at the file [pedicel.S](../user/pedicel.S).
### C program
You will find relevant headers in the `libc` directory. They contain system call wrappers, utility functions, and more. See what's implemented there and what's not.
To invoke a system call you can use the functions defined in `libc/syscall.h`.
## 2. Add the Makefile rule and variable
Now that your code is complete, add a Makefile rule to `user/Makefile` with your program name. You can just copy-paste the rule that applies to you (either from an Assembly source or C source) and change the name of the files (.raw, .elf, etc...) in the rule.
For clarity, raw binaries have the `.raw` extension, and ELF ones have `.elf`.
You also now have to add the name of the executable to the `USER_PROGRAMS` variable at the top of the global Makefile.
Finally, do `make user` to compile your program.
## 3. Run your program
You can now boot up PepperOS, in a VM or on real hardware, and use the kernel's shell to `list` files in the filesystem (to see if your executable was properly added), and then, run it with the `load` command. Congratulations, you made a program for a random hobby OS!
## 4. (Optional) debugging
Use GDB with the `make debug` rule!
For your information, user programs are loaded at `0x400000`. Can be good to know to set breakpoints.
## 5. (Optional) contribute!
If you like what you've done and you think it could be nice to add it to PepperOS, send it to me by e-mail: `xamidev (at) riseup (dot) net`. It may or may not be added in a future release... who knows?
+8
View File
@@ -2,6 +2,14 @@
This document describes the coding style for the Pepper kernel. It is used as a guideline across all source files. This document describes the coding style for the Pepper kernel. It is used as a guideline across all source files.
## Setting up a language server (optional)
Before you do anything you might want to setup a language server with your editor. This will save you lots of time correcting errors and stuff. I use `clangd`, and generate my `compile_commands.json` like so:
```
bear -- make
```
## Indentation ## Indentation
Indentations should be 4 characters long. Indentations should be 4 characters long.
+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 | | | |
+3
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
@@ -66,4 +68,5 @@
/* fs */ /* fs */
#define FDT_MAX 8 // Maximum amount of file descriptors per process #define FDT_MAX 8 // Maximum amount of file descriptors per process
#endif #endif
+8 -1
View File
@@ -11,7 +11,14 @@
int initfs_init(struct limine_file* tar_file); int initfs_init(struct limine_file* tar_file);
int tar_lookup(unsigned char* archive, char* filename, char** out); int tar_lookup(unsigned char* archive, char* filename, char** out);
int tar_read(char* filename, char** buf);
int tar_exists(const char* filename); int tar_exists(const char* filename);
int tar_read(char* filename, char* out, int count, int offset);
void tar_list();
enum Seek {
SEEK_SET,
SEEK_CUR,
SEEK_END
};
#endif #endif
+3 -1
View File
@@ -9,12 +9,14 @@
#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
ENOENT, // No entry ENOENT, // No entry
EBADFD, // Bad file descriptor EBADFD, // Bad file descriptor
EMFILE // Too many open files EMFILE, // Too many open files
EINVAL // Invalid argument
}; };
#define CLEAR_INTERRUPTS __asm__ volatile("cli") #define CLEAR_INTERRUPTS __asm__ volatile("cli")
+1
View File
@@ -50,6 +50,7 @@ void process_exit(void);
void process_display_list(struct process* processes_list); void process_display_list(struct process* processes_list);
void process_create_user_raw(char* file, int size, char* name); void process_create_user_raw(char* file, int size, char* name);
#endif #endif
+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
-152
View File
@@ -1,152 +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);
if (sz == 0) {
return -ENOENT;
} else {
return sz;
}
}
return -EBADFD;
}
// TODO: Should have a return value: number of bytes written on success, -1 on error (errno set)
void sys_write(unsigned int fd, const char* buf, size_t count)
{
switch (fd) {
case 1: //stdout
for (size_t i=0; i<count; i++) {
internal_putc(buf[i], NULL);
}
break;
case 2: //stderr
for (size_t i=0; i<count; i++) {
internal_putc(buf[i], NULL);
}
break;
}
}
void sys_exit(int error_code)
{
current_process->status = DEAD;
DEBUG("exiting process PID=%u name=%s", current_process->pid, current_process->name);
}
/*
* 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)
{
DEBUG("Syscall %lx with (arg0=%lx arg1=%lx)", regs->rax, regs->rdi, regs->rsi);
switch (regs->rax)
{
case 0: //sys_read
regs->rax = sys_read(regs->rdi, (char*)regs->rsi, regs->rdx);
break;
case 1: //sys_write
sys_write(regs->rdi, (char*)regs->rsi, regs->rdx);
break;
case 2:
regs->rax = sys_open((const char*)regs->rdi, regs->rsi);
break;
case 3:
regs->rax = sys_close(regs->rdi);
break;
case 60: //sys_exit
sys_exit(regs->rdi);
break;
default: // bad syscall
regs->rax = 0xbad515ca11;
break;
}
return regs;
}
+42 -7
View File
@@ -42,7 +42,7 @@ int tar_oct2bin(unsigned char* str, int size)
* *
* Return: * Return:
* $filesize - size of the file, if found * $filesize - size of the file, if found
* $0 - file not found * $-ENOENT - file not found
*/ */
int tar_lookup(unsigned char* archive, char* filename, char** out) int tar_lookup(unsigned char* archive, char* filename, char** out)
{ {
@@ -56,21 +56,55 @@ int tar_lookup(unsigned char* archive, char* filename, char** out)
} }
ptr += (((filesize + 511) / 512) + 1) * 512; ptr += (((filesize + 511) / 512) + 1) * 512;
} }
return 0; return -ENOENT;
} }
/* /*
* tar_read - read a file in the TAR file * tar_list - list all files present in archive
*/
void tar_list()
{
printf("++ Contents of initial filesystem ++\r\n\r\n");
unsigned char *ptr = archive_start_addr;
while (!memcmp(ptr + 257, "ustar", 5)) {
int filesize = tar_oct2bin(ptr + 0x7c, 11);
char* filename = (char*)ptr;
printf("file: %s\r\n", filename);
ptr += (((filesize + 511) / 512) + 1) * 512;
}
}
/*
* tar_read - read a file in the TAR archive
* @filename: file to read (absolute path) * @filename: file to read (absolute path)
* @out: out buffer (if file is found) * @out: where to store file data if found
* @count: amount of bytes to read
* @offset: read from byte offset (0 for none)
* *
* Return: * Return:
* $filesize - size of the file, if found * $filesize - size of the file, if found
* $0 - file not found * $-ENOENT - file not found
*/ */
int tar_read(char* filename, char** buf) int tar_read(char* filename, char* out, int count, int offset)
{ {
return tar_lookup(archive_start_addr, filename, buf); char* file_data;
int filesize = tar_lookup(archive_start_addr, filename, &file_data);
if (filesize <= 0) {
return filesize;
}
if (offset >= filesize) {
return -EINVAL;
}
int remaining = filesize - offset;
int to_read = remaining < count ? remaining : count;
memcpy(out, file_data + offset, to_read);
return to_read;
} }
/* /*
@@ -95,6 +129,7 @@ int tar_exists(const char* filename)
return -ENOENT; return -ENOENT;
} }
/* /*
* initfs_init - initialize the TAR initial filesystem * initfs_init - initialize the TAR initial filesystem
* @tar_file: pointer to the Limine-loaded archive * @tar_file: pointer to the Limine-loaded archive
+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);
} }
} }
+76 -11
View File
@@ -4,6 +4,7 @@
* @license GPL-3.0-only * @license GPL-3.0-only
*/ */
#include "fs/initfs.h"
#include <io/term/term.h> #include <io/term/term.h>
#include <config.h> #include <config.h>
#include <io/kbd/ps2.h> #include <io/kbd/ps2.h>
@@ -12,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()
@@ -22,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)
@@ -34,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 (;;) {
@@ -42,17 +91,18 @@ void pedicel_main(void* arg)
keyboard_getline(input_buf, PEDICEL_INPUT_SIZE); keyboard_getline(input_buf, PEDICEL_INPUT_SIZE);
if (strncmp(input_buf, "help", 4) == 0) { if (strncmp(input_buf, "help", 4) == 0) {
printf("\r\nYou are currently running the test kernel shell. This is not\r\n" printf("++ shell builtins ++\r\n\r\n"
"a fully-fledged shell like you'd find in a complete operating system,\r\n" "\tclear - clear the screen\n"
"but rather a toy to play around in the meantime.\r\n\r\n" "\tpanic - trigger a test panic\n"
"clear - clear the screen\r\n" "\tsyscall - trigger int 0x80\n"
"panic - trigger a test panic\r\n" "\tpf - trigger a page fault\n"
"syscall - trigger int 0x80\r\n" "\tnow - get current date\n"
"pf - trigger a page fault\r\n" "\tsmash - smash the stack\n"
"now - get current date\r\n" "\tmem - get used heap info\n"
"smash - smash the stack\r\n" "\tload - load an user executable\n"
"mem - get used heap info\r\n" "\tlist - list initfs.tar contents\n"
"load - load an user executable\r\n"); "\tps - list running processes\n"
"\tkill - kill a running process by PID\n");
continue; continue;
} }
@@ -102,6 +152,21 @@ void pedicel_main(void* arg)
continue; continue;
} }
if (strncmp(input_buf, "list", 4) == 0) {
tar_list();
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);
} }
+1 -2
View File
@@ -236,7 +236,6 @@ extern struct tss tss;
*/ */
void process_create_user_raw(char* file, int size, char* name) void process_create_user_raw(char* file, int size, char* name)
{ {
// Need to refactor this mess
CLEAR_INTERRUPTS; CLEAR_INTERRUPTS;
struct process* proc = (struct process*)kmalloc(sizeof(struct process)); struct process* proc = (struct process*)kmalloc(sizeof(struct process));
struct cpu_status* ctx = (struct cpu_status*)kmalloc(sizeof(struct cpu_status)); struct cpu_status* ctx = (struct cpu_status*)kmalloc(sizeof(struct cpu_status));
@@ -255,7 +254,7 @@ void process_create_user_raw(char* file, int size, char* name)
proc->context->iret_cs = USER_CODE_SEGMENT | 3; proc->context->iret_cs = USER_CODE_SEGMENT | 3;
proc->context->iret_flags = 0x202; // Interrupt Flag set proc->context->iret_flags = 0x202; // Interrupt Flag set
/* Set basic entries for the process's File Descriptor Table */ /* Set basic entries for the process's File Descriptor Table */
proc->fdt[0].fd = 0; proc->fdt[0].fd = 0;
proc->fdt[0].open = true; proc->fdt[0].open = true;
proc->fdt[0].cursor = 0; proc->fdt[0].cursor = 0;
+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;
}
+28
View File
@@ -0,0 +1,28 @@
CC := x86_64-elf-gcc
CC_FLAGS := -ffreestanding -nostdlib -fno-pic -mno-red-zone -Ilibc
DOOM_CC_FLAGS := $(CC_FLAGS) -std=gnu11
LD := x86_64-elf-ld
BUILDDIR := ../build
LIBDIR := libc
all: pedicel apex doom
.PHONY: pedicel
pedicel:
nasm -f bin pedicel.S -o $(BUILDDIR)/pedicel.raw
.PHONY: apex
apex:
$(CC) $(CC_FLAGS) -c apex.c -o $(BUILDDIR)/apex.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
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
+7
View File
@@ -0,0 +1,7 @@
#include <syscall.h>
int main() {
const char* msg = "hi from C userland\r\n";
write(1, msg, 21);
return 42;
}
+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.
-21
View File
@@ -1,21 +0,0 @@
bits 64
section .data
hi db "hi from userland :) we did it man", 0x0A, 0x0d, 0
section .text
hello:
mov rax, 0x1 ;sys_write
mov rdi, 0x1 ;stdout
lea rsi, [rel hi] ;char* buf
mov rdx, 35 ;count
int 0x80
.end:
mov rax, 0x3C ;sys_exit
mov rdi, 0x0 ;error_code
int 0x80
.loop:
jmp .loop
+18
View File
@@ -0,0 +1,18 @@
bits 64
global _start
extern main
section .text
; Begin the program with main() function
_start:
call main
; Exit the program by exit() syscall
.exit:
mov rdi, rax ; put the value of "return X;" (rax) as arg1 (error_code)
mov rax, 60 ; sys_exit
int 0x80
.loop:
jmp .loop
+22
View File
@@ -0,0 +1,22 @@
ENTRY(_start)
SECTIONS
{
. = 0x400000;
.text : {
*(.text*)
}
.rodata : {
*(.rodata*)
}
.data : {
*(.data*)
}
.bss : {
*(.bss*)
}
}
+88
View File
@@ -0,0 +1,88 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief System call wrappers for userspace
* @license GPL-3.0-only
*/
#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) {
long ret;
__asm__ volatile (
"int $0x80"
: "=a"(ret)
: "a"(n), "D"(a), "S"(b), "d"(c) // a = rax, D = rdi, S = rsi, d = rdx
: "memory"
);
return ret;
}
// 4-args syscall
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) {
__asm__ volatile (
"int $0x80"
:
: "a"(60), "D"(code)
: "memory"
);
for (;;);
}
+28 -2
View File
@@ -1,7 +1,8 @@
bits 64 bits 64
section .data section .data
hello db 0x0A, 0x0D, "User program 2 speaking", 0x0A, 0x0D, 0 hello db 0x0A, 0x0D, "TEST PROGRAM...", 0x0A, 0x0D, 0
filename db "wow.txt", 0
section .text section .text
@@ -9,7 +10,29 @@ _start:
mov rax, 0x1 ;sys_write mov rax, 0x1 ;sys_write
mov rdi, 0x1 ;stdout mov rdi, 0x1 ;stdout
lea rsi, [rel hello] lea rsi, [rel hello]
mov rdx, 27 ;count mov rdx, 19 ;count
int 0x80
; Open a file
mov rax, 0x2 ;sys_open
lea rdi, [rel filename] ;filename
mov rsi, 0x0 ;flags
int 0x80
mov rdi, rax ;fd
mov rax, 0x0 ;sys_read
lea rsi, [rel buf] ;buf
mov rdx, 33 ;count
int 0x80
mov rax, 0x1 ;sys_write
mov rdi, 0x1 ;stdout
lea rsi, [rel buf] ;buf
mov rdx, 33 ;count
int 0x80
mov rax, 0x3 ;sys_close
mov rdi, 0x3 ;fd
int 0x80 int 0x80
; when we are ready to have an os specific toolchain, ; when we are ready to have an os specific toolchain,
@@ -23,3 +46,6 @@ _start:
.loop: .loop:
jmp .loop jmp .loop
section .bss
buf resb 10
+1
View File
@@ -0,0 +1 @@
hi from a file opened in usermode