Load raw C binary + docs
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
USER_PROGRAMS := pedicel.raw apex.raw
|
||||
USER_FILES := wow.txt
|
||||
|
||||
BUILDDIR := build
|
||||
ELFFILE := pepperk
|
||||
@@ -43,9 +45,8 @@ limine/limine:
|
||||
|
||||
.PHONY: user
|
||||
user:
|
||||
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 -C ../user wow.txt
|
||||
$(MAKE) -C user
|
||||
tar cvf $(BUILDDIR)/initfs.tar -C $(BUILDDIR) $(USER_PROGRAMS) -C ../user $(USER_FILES)
|
||||
|
||||
build-iso: limine/limine $(ELFFILE)
|
||||
rm -rf iso_root
|
||||
|
||||
@@ -23,8 +23,9 @@ CC := gcc
|
||||
LD := ld
|
||||
```
|
||||
|
||||
Then, to compile the kernel and make an ISO image file, run: `make build-iso`
|
||||
To run it with QEMU, do: `make run`
|
||||
Then, to compile the kernel and make an ISO image file, run: `make`.
|
||||
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
|
||||
|
||||
@@ -48,31 +49,9 @@ These features can be activated by setting them to "true" at the end of the make
|
||||
make UBSAN=true
|
||||
```
|
||||
|
||||
## TODO
|
||||
## Writing software for PepperOS
|
||||
|
||||
The basics that I'm targeting are:
|
||||
|
||||
### 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
|
||||
If you want to write software for PepperOS, take a look at the [Software Developer's guide](docs/SOFTWARE.md).
|
||||
|
||||
## Thanks
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# 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.
|
||||
|
||||
## 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?
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
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
|
||||
|
||||
Indentations should be 4 characters long.
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
CC := x86_64-elf-gcc
|
||||
CC_FLAGS := -ffreestanding -nostdlib -fno-pic -mno-red-zone -Ilibc
|
||||
|
||||
LD := x86_64-elf-ld
|
||||
|
||||
BUILDDIR := ../build
|
||||
LIBDIR := libc
|
||||
|
||||
all: pedicel apex
|
||||
|
||||
pedicel:
|
||||
nasm -f bin pedicel.S -o $(BUILDDIR)/pedicel.raw
|
||||
|
||||
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
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <syscall.h>
|
||||
|
||||
int main() {
|
||||
const char* msg = "hi from C userland\r\n";
|
||||
write(1, msg, 21);
|
||||
return 42;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,22 @@
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x400000;
|
||||
|
||||
.text : {
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
.rodata : {
|
||||
*(.rodata*)
|
||||
}
|
||||
|
||||
.data : {
|
||||
*(.data*)
|
||||
}
|
||||
|
||||
.bss : {
|
||||
*(.bss*)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
// 3 because 3 arguments to the call, get it??
|
||||
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)
|
||||
: "memory"
|
||||
);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void write(int fd, const char* buf, long len) {
|
||||
syscall3(1, fd, (long)buf, len);
|
||||
}
|
||||
|
||||
static inline void exit(int code) {
|
||||
__asm__ volatile (
|
||||
"int $0x80"
|
||||
:
|
||||
: "a"(60), "D"(code)
|
||||
: "memory"
|
||||
);
|
||||
|
||||
for (;;);
|
||||
}
|
||||
Reference in New Issue
Block a user