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
|
BUILDDIR := build
|
||||||
ELFFILE := pepperk
|
ELFFILE := pepperk
|
||||||
@@ -43,9 +45,8 @@ limine/limine:
|
|||||||
|
|
||||||
.PHONY: user
|
.PHONY: user
|
||||||
user:
|
user:
|
||||||
nasm -f bin user/hello.S -o $(BUILDDIR)/hello
|
$(MAKE) -C user
|
||||||
nasm -f bin user/pedicel.S -o $(BUILDDIR)/pedicel
|
tar cvf $(BUILDDIR)/initfs.tar -C $(BUILDDIR) $(USER_PROGRAMS) -C ../user $(USER_FILES)
|
||||||
tar cvf $(BUILDDIR)/initfs.tar -C $(BUILDDIR) hello pedicel -C ../user wow.txt
|
|
||||||
|
|
||||||
build-iso: limine/limine $(ELFFILE)
|
build-iso: limine/limine $(ELFFILE)
|
||||||
rm -rf iso_root
|
rm -rf iso_root
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
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.
|
||||||
|
|||||||
@@ -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