Compare commits

...

87 Commits

Author SHA1 Message Date
38e26a9c12 Implement argument_ptr () syscall for handling process arguments
All checks were successful
Build documentation / build-and-deploy (push) Successful in 37s
2026-01-30 14:05:47 +01:00
124aa12f5b Redesign scheduling points
All checks were successful
Build documentation / build-and-deploy (push) Successful in 35s
2026-01-30 02:36:27 +01:00
d2f5c032d9 Fix TLS alignment issues, works on BOCHS now too!
All checks were successful
Build documentation / build-and-deploy (push) Successful in 40s
2026-01-29 18:18:24 +01:00
73e42588fb Fix BOCHS clock
All checks were successful
Build documentation / build-and-deploy (push) Successful in 41s
2026-01-29 15:04:06 +01:00
e78bfb9984 Move suspension q code into proc/suspension_q.c
All checks were successful
Build documentation / build-and-deploy (push) Successful in 24s
2026-01-29 01:52:18 +01:00
d2a88b3641 Move suspension q's cleanup to proc/suspension_q.c 2026-01-29 01:43:01 +01:00
fdda2e2df8 Unlock mutexes on process death 2026-01-29 01:38:44 +01:00
388418a718 Nice wrappers around process management
All checks were successful
Build documentation / build-and-deploy (push) Successful in 34s
2026-01-29 00:08:54 +01:00
1c64d608bd Rename make/libc.mk -> make/libmsl.mk
All checks were successful
Build documentation / build-and-deploy (push) Successful in 44s
2026-01-28 23:57:28 +01:00
3d23187acf Implement userspace TLS, remove RW Locks 2026-01-28 23:52:48 +01:00
a3b62ebd3d Clean up AMD64 memory management code, remove dependency on pd.lock 2026-01-27 19:03:03 +01:00
8bda300f6a Fix sys_clone () wrong argument bug
All checks were successful
Build documentation / build-and-deploy (push) Successful in 26s
2026-01-27 18:05:02 +01:00
cf51600c6a Cleanup syscalls
All checks were successful
Build documentation / build-and-deploy (push) Successful in 34s
2026-01-27 17:34:43 +01:00
b388b30b24 Redesign userspace memory management
All checks were successful
Build documentation / build-and-deploy (push) Successful in 44s
2026-01-27 17:04:08 +01:00
600886a7ee Organize resources into process groups 2026-01-27 14:18:05 +01:00
67b66f2b39 Implement proper mutex cleanup
All checks were successful
Build documentation / build-and-deploy (push) Successful in 23s
2026-01-25 23:10:12 +01:00
18f791222e Remove dead process from it's suspension queues 2026-01-25 22:39:29 +01:00
5e16bb647c Multiple process suspension queues 2026-01-25 22:10:04 +01:00
a68373e4ee Dynamically assign cpu upon mutex unlock
All checks were successful
Build documentation / build-and-deploy (push) Successful in 35s
2026-01-25 20:39:51 +01:00
8650010992 Fix user CPU context saving
All checks were successful
Build documentation / build-and-deploy (push) Successful in 31s
2026-01-25 17:39:34 +01:00
95f590fb3b multi-cpu scheduling WIP 2026-01-25 15:54:00 +01:00
7bb3b77ede Disable kernel preemption, fix requesting rescheduling
All checks were successful
Build documentation / build-and-deploy (push) Successful in 29s
2026-01-22 19:32:15 +01:00
c26fd3cb2b Fix scheduler locking hierarchy 2026-01-22 15:59:29 +01:00
fea0999726 Fix scheduler starvation, use lists for scheduling
All checks were successful
Build documentation / build-and-deploy (push) Successful in 33s
2026-01-22 11:54:52 +01:00
7eceecf6e3 Add mutex syscalls 2026-01-20 22:18:43 +01:00
fff51321bc Redesign syscalls
All checks were successful
Build documentation / build-and-deploy (push) Successful in 40s
2026-01-20 20:46:34 +01:00
a29233f853 Rename proc_spawn_thread to proc_clone 2026-01-19 22:01:44 +01:00
38a43b59b0 Resolve strange IRQ issues which cause the scheduler to behave weirdly (IRQ mapping)
All checks were successful
Build documentation / build-and-deploy (push) Successful in 52s
2026-01-19 01:51:34 +01:00
ddafc4eb19 Rewrite resource subsystem 2026-01-18 20:50:45 +01:00
4f7077d458 Move mutex and mem create/cleanup functions into mutex.c and mem.c respectively
All checks were successful
Build documentation / build-and-deploy (push) Successful in 33s
2026-01-16 22:13:17 +01:00
9a7dbf0594 Properly implement liballoc_free () 2026-01-16 22:09:16 +01:00
ab8093cc6c CI install pymdown-extensions from pip
All checks were successful
Build documentation / build-and-deploy (push) Successful in 26s
2026-01-16 20:28:26 +01:00
ddbb66b5e4 Docs processes overview 2026-01-16 20:26:23 +01:00
11a1eb52aa Move status codes into a separate header
All checks were successful
Build documentation / build-and-deploy (push) Successful in 36s
2026-01-16 19:07:32 +01:00
a054257336 Port liballoc to userspace 2026-01-16 18:50:40 +01:00
9fc8521e63 sys_proc_mutex_unlock () automatically reschedule at the end
All checks were successful
Build documentation / build-and-deploy (push) Successful in 29s
2026-01-16 00:28:46 +01:00
711da8aeab Implement proc_spawn_thread syscall, fix proc_resume and proc_suspend
All checks were successful
Build documentation / build-and-deploy (push) Successful in 35s
2026-01-16 00:26:37 +01:00
ebd9f0cac6 Let the user application decide upon the resource ID (RID)
All checks were successful
Build documentation / build-and-deploy (push) Successful in 22s
2026-01-14 23:19:39 +01:00
7cd5623d36 Use reference counting to track filetime of process PD
All checks were successful
Build documentation / build-and-deploy (push) Successful in 26s
2026-01-14 23:11:06 +01:00
270ff507d4 Implement lock IRQ nesting via stack variables/contexts
All checks were successful
Build documentation / build-and-deploy (push) Successful in 21s
2026-01-14 22:11:56 +01:00
55166f9d5f syscall doesn't need RPL 3 bits on kernel code
All checks were successful
Build documentation / build-and-deploy (push) Successful in 24s
2026-01-14 21:21:20 +01:00
e5cc3a64d3 Fix syscall return value - preserve RAX register
All checks were successful
Build documentation / build-and-deploy (push) Successful in 39s
2026-01-14 20:58:00 +01:00
2ab308d678 Drop m_ prefix from libmsl 2026-01-14 20:56:09 +01:00
d1d772cb42 Fix user apps randomly crashing (APIC, GDT layout, syscall entry)
All checks were successful
Build documentation / build-and-deploy (push) Successful in 23s
2026-01-14 19:51:18 +01:00
0d8f9e565f Fix missing CPU_REQUEST_SCHED IDT entry 2026-01-11 12:07:17 +01:00
f80a26e5eb Load kernel CR3 2026-01-11 03:45:32 +01:00
5bf10c1218 Extra compiler flags for AMD64
All checks were successful
Build documentation / build-and-deploy (push) Successful in 49s
2026-01-11 03:42:15 +01:00
41a458b925 Implement Mutexes and supporting syscalls, cleanup/optimize scheduler
All checks were successful
Build documentation / build-and-deploy (push) Successful in 39s
2026-01-10 00:12:42 +01:00
6a474c21a0 Use RW spin locks
All checks were successful
Build documentation / build-and-deploy (push) Successful in 39s
2026-01-09 19:53:08 +01:00
a5283283f6 Hold proc->lock while killing the process 2026-01-09 00:00:18 +01:00
79768d94e6 Preserve syscall return value in RAX
All checks were successful
Build documentation / build-and-deploy (push) Successful in 49s
2026-01-08 23:06:32 +01:00
0555ddd041 Clean up IOAPIC and LAPIC implementations
All checks were successful
Build documentation / build-and-deploy (push) Successful in 33s
2026-01-08 22:05:11 +01:00
ebb026b807 proc_cleanup_resources () drop instead of immediate removal
All checks were successful
Build documentation / build-and-deploy (push) Successful in 30s
2026-01-07 23:09:13 +01:00
d7b734306f Introduce concept of Process Resources (PR_MEM), implement necessary syscalls
All checks were successful
Build documentation / build-and-deploy (push) Successful in 42s
2026-01-07 22:47:30 +01:00
28aef30f77 Implement proc_map () and proc_unmap () syscalls
All checks were successful
Build documentation / build-and-deploy (push) Successful in 21s
2026-01-06 23:32:11 +01:00
9f107a1a5e Implement proc_unmap () 2026-01-06 17:47:21 +01:00
e50f8940a9 Redesign linked list
All checks were successful
Build documentation / build-and-deploy (push) Successful in 49s
2026-01-06 16:38:42 +01:00
d09e4d97ad Fix missing headers, generate compile db with bear
All checks were successful
Build documentation / build-and-deploy (push) Successful in 31s
2026-01-06 03:08:13 +01:00
7915986902 Remove Doxygen-style comments, change formatting to wrap comments
All checks were successful
Build documentation / build-and-deploy (push) Successful in 28s
2026-01-06 02:04:32 +01:00
902682ac11 Remove doxygen infra
All checks were successful
Build documentation / build-and-deploy (push) Successful in 31s
2026-01-06 01:41:07 +01:00
7747e5e0aa Docs update theme
All checks were successful
Build documentation / build-and-deploy (push) Successful in 47s
2026-01-06 01:37:51 +01:00
a8423fe657 Better proc_kill () and process cleanup
All checks were successful
Build documentation / build-and-deploy (push) Successful in 27s
2026-01-06 01:19:11 +01:00
6538fd8023 Generate new PIDs for processes 2026-01-05 20:24:26 +01:00
fcd5658a80 Use red-black trees to store process run queue and process list
All checks were successful
Build documentation / build-and-deploy (push) Successful in 43s
2026-01-05 18:30:58 +01:00
b1579e4ac1 Implement automatic paging table deallocation 2026-01-04 21:26:11 +01:00
bba36ef057 Remove sign warning in comparison
All checks were successful
Build documentation / build-and-deploy (push) Successful in 22s
2026-01-04 01:45:56 +01:00
b5353cb600 Auxilary scripts for formatting all components
All checks were successful
Build documentation / build-and-deploy (push) Successful in 44s
2026-01-04 01:44:02 +01:00
e077d322f4 Rewrite init app in C, introduce MSL (MOP3 System Library)
All checks were successful
Build documentation / build-and-deploy (push) Successful in 35s
2026-01-04 01:11:31 +01:00
2c954a9ca9 Fix return syscall result
All checks were successful
Build documentation / build-and-deploy (push) Successful in 32s
2026-01-03 15:06:36 +01:00
cf04e3db18 proc_quit () and proc_test () syscalls
All checks were successful
Build documentation / build-and-deploy (push) Successful in 43s
2026-01-03 12:21:56 +01:00
124a7f7215 Docs add kernel build instructions
All checks were successful
Build documentation / build-and-deploy (push) Successful in 39s
2026-01-03 02:19:40 +01:00
e52268cd8e First Hello world syscall
All checks were successful
Build documentation / build-and-deploy (push) Successful in 26s
2026-01-03 02:04:09 +01:00
1341dc00d9 make -B format_kernel
All checks were successful
Build documentation / build-and-deploy (push) Successful in 32s
2026-01-01 20:17:29 +01:00
99bab4ceee Use generic spin () instead of amd64_spin () 2026-01-01 20:16:40 +01:00
121fb3b33c Move platform-specific code for process loading/init for AMD64 to amd64/
All checks were successful
Build documentation / build-and-deploy (push) Successful in 49s
2026-01-01 20:08:37 +01:00
5e6bdcc52d Handle swapgs in interrupts and scheduling
All checks were successful
Build documentation / build-and-deploy (push) Successful in 29s
2026-01-01 18:42:53 +01:00
3bcbdb5ec4 Fix proc_kill () race, improve scheduler locking
All checks were successful
Build documentation / build-and-deploy (push) Successful in 1m45s
2026-01-01 16:59:04 +01:00
7f53ede2ab CI docs use $REMOTE_IP
All checks were successful
Build documentation / build-and-deploy (push) Successful in 38s
2025-12-31 22:50:59 +01:00
f1e34b78cd CI docs chmod 777 on site build dir
All checks were successful
Build documentation / build-and-deploy (push) Successful in 39s
2025-12-31 22:40:28 +01:00
97ad0b338c Fix CI docs build, install rsync
All checks were successful
Build documentation / build-and-deploy (push) Successful in 43s
2025-12-31 21:25:33 +01:00
74c782d653 mkdir docs/kernel/doxygen
Some checks failed
Build documentation / build-and-deploy (push) Failing after 30s
2025-12-31 21:21:02 +01:00
949f9c5293 Add docs gitea workflow
Some checks failed
Build documentation / build-and-deploy (push) Failing after 1m11s
2025-12-31 20:57:09 +01:00
a6c3f4cf87 Move kernel doxygen stuff to kernel/ 2025-12-30 17:04:05 +01:00
34f1e0ba30 Document amd64 platform-specific code 2025-12-30 16:50:15 +01:00
4f4f5c3d2f Move doxygen-awesome-darkmode-toggle.js to doxytheme/ 2025-12-30 01:52:45 +01:00
d861ab56c4 Remove pre-SMP TSS code 2025-12-30 01:50:47 +01:00
b279774bd6 Generated docs using doxygen and mkdocs 2025-12-30 01:47:29 +01:00
154 changed files with 3980 additions and 1149 deletions

View File

@@ -50,7 +50,7 @@ AlignOperands: false
SortIncludes: true SortIncludes: true
# Comments # Comments
ReflowComments: false ReflowComments: true
CommentPragmas: '^ IWYU pragma:' CommentPragmas: '^ IWYU pragma:'
# Misc # Misc

2
.gdbinit Normal file
View File

@@ -0,0 +1,2 @@
file kernel/build/kernel.elf
target remote :1234

View File

@@ -0,0 +1,43 @@
name: Build documentation
on:
push:
branches:
- master
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Install software
run: |
sudo apt-get update
sudo apt-get install -y doxygen make rsync
- name: Set up python3
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install mkdocs
run: |
pip install --upgrade pip
pip install mkdocs mkdocs-material pymdown-extensions
- name: Build
run: make docs
- name: Deploy
env:
SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
REMOTE_IP: ${{ vars.DEPLOY_REMOTE_IP }}
run: |
mkdir -p ~/.ssh
echo "$SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$REMOTE_IP" >> ~/.ssh/known_hosts
chmod -R 777 site
rsync -az --delete site/ webuser@"$REMOTE_IP":/home/webuser/mop/

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@ mop3.iso
bochs-log.txt bochs-log.txt
bochs-com1.txt bochs-com1.txt
mop3dist.tar mop3dist.tar
site/

View File

@@ -1,5 +1,7 @@
platform ?= amd64 platform ?= amd64
include apps.mk include make/apps.mk
include kernel.mk include make/kernel.mk
include dist.mk include make/dist.mk
include make/docs.mk
include make/libmsl.mk

View File

@@ -4,7 +4,8 @@ cflags += --target=x86_64-pc-none-elf \
-mno-avx \ -mno-avx \
-mno-mmx \ -mno-mmx \
-mno-80387 \ -mno-80387 \
-mno-red-zone -mno-red-zone \
-mcmodel=large
ldflags += --target=x86_64-pc-none-elf \ ldflags += --target=x86_64-pc-none-elf \
-Wl,-zmax-page-size=0x1000 -Wl,-zmax-page-size=0x1000

View File

@@ -6,34 +6,61 @@ PHDRS {
text PT_LOAD; text PT_LOAD;
rodata PT_LOAD; rodata PT_LOAD;
data PT_LOAD; data PT_LOAD;
bss PT_LOAD;
tls PT_TLS;
} }
SECTIONS { SECTIONS {
. = 0x0000000050000000; . = 0x0000500000000000;
.text : { .text : {
*(.text .text.*) *(.text .text.*)
*(.ltext .ltext.*)
} :text } :text
. = ALIGN(CONSTANT(MAXPAGESIZE)); . = ALIGN(0x1000);
.rodata : { .rodata : {
*(.rodata .rodata.*) *(.rodata .rodata.*)
} :rodata } :rodata
.note.gnu.build-id : { . = ALIGN(0x1000);
*(.note.gnu.build-id)
} :rodata
. = ALIGN(CONSTANT(MAXPAGESIZE));
.data : { .data : {
*(.data .data.*) *(.data .data.*)
*(.ldata .ldata.*)
} :data } :data
. = ALIGN(0x1000);
__bss_start = .;
.bss : { .bss : {
*(.bss .bss.*) *(.bss .bss.*)
} :data *(.lbss .lbss.*)
} :bss
__bss_end = .;
. = ALIGN(0x1000);
__tdata_start = .;
.tdata : {
*(.tdata .tdata.*)
} :tls
__tdata_end = .;
__tbss_start = .;
.tbss : {
*(.tbss .tbss.*)
} :tls
__tbss_end = .;
__tls_size = __tbss_end - __tdata_start;
/DISCARD/ : { /DISCARD/ : {
*(.eh_frame*) *(.eh_frame*)

View File

@@ -1,4 +1,4 @@
cpu: model=p4_prescott_celeron_336 cpu: model=p4_prescott_celeron_336, ips=200000000
memory: guest=4096 host=2048 memory: guest=4096 host=2048
@@ -9,6 +9,7 @@ ata0: enabled=1
ata0-master: type=cdrom, path=mop3.iso, status=inserted ata0-master: type=cdrom, path=mop3.iso, status=inserted
com1: enabled=1, mode=file, dev=bochs-com1.txt com1: enabled=1, mode=file, dev=bochs-com1.txt
pci: enabled=1, chipset=i440fx pci: enabled=1, chipset=i440fx
clock: sync=realtime, time0=local
boot: cdrom boot: cdrom

View File

@@ -8,6 +8,7 @@ else
make -B all_kernel make -B all_kernel
fi fi
make -B all_libmsl
make -B all_apps make -B all_apps
make -B all_dist make -B all_dist
./aux/limine_iso_amd64.sh ./aux/limine_iso_amd64.sh

7
aux/format.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/sh
set -x
make -B format_kernel
make -B format_libmsl
make -B format_apps

5
aux/qemu_amd64_debug.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/sh
set -x
qemu-system-x86_64 -M q35 -m 4G -serial stdio -cdrom mop3.iso -smp 4 -s -S $@

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

44
docs/building_kernel.md Normal file
View File

@@ -0,0 +1,44 @@
# Building the MOP3 kernel
This article describes, how to build the kernel, how the build system works and prerequisites.
## Preprequistes
- POSIX host system (tested on Linux, may break on other systems)
- Git
- GNU make
- LLVM toolchain/Clang C compiler
- Xorriso
## Build steps
cd into root of MOP3 source tree.
Build the kernel:
```
make -B all_kernel buildtype=<debug|release>
```
Build essential system applications:
```
make -B all_apps
```
Prepare the ramdisk:
```
make -B all_dist
```
Build ISO image:
```
./aux/limine_iso_amd64.sh
```
Now you have an ISO image, which can be run my QEMU or you can burn it onto a CD.
For the convenience of the developer, there's a magic "do all" script located in `aux`:
```
./aux/devel.sh # optionally "./aux/devel.sh debug" for debugging
```
It does all the previous steps, just packed into a single script.

4
docs/index.md Normal file
View File

@@ -0,0 +1,4 @@
# MOP3 operating system documentation
MOP3 is a hobby OS project of mine ;).

View File

@@ -0,0 +1,30 @@
# Overview of processes in MOP3
## What is a process?
A process is a structure defined to represent an internal state of a user application's environment. This includes
the necessary stacks, code, data and other resources. A process (usually) has it's own address, but in certain
circumstances may share it with another process.
## Only processes vs. processes-threads model
### Overview
MOP3 doesn't have a process-thread separation. Ususally in operating systems you'd have a "process", which consists
of multiple worker threads. For eg. a single-threaded application is a process, which consists of one worker. In MOP3
we do things a little differently. We only have processes, but some processes may work within the same pool of (generally speaking)
"resources", such as a shared address space, shared memory allocations, mutexes and so on. An application then consists of
not threads, but processes, which are loosely tied together via shared data.
#### Processes-threads model diagram
![Processes-threads model](assets/images/processes-threads.png)
#### Only processes model diagram
![Only processes model](assets/images/only-processes.png)
## Scheduling
MOP3 uses a round-robin based scheduler. For now priorities are left unimplemented, ie. every processes has
equal priority, but this may change in the future.
A good explaination of round-robin scheduling can be found on the OSDev wiki: [the article](https://wiki.osdev.org/Scheduling_Algorithms#Round_Robin)

View File

@@ -6,10 +6,16 @@ cflags += -nostdinc \
-pedantic \ -pedantic \
-Wall \ -Wall \
-Wextra \ -Wextra \
-mcmodel=kernel -ffunction-sections \
-fdata-sections
cflags += -isystem ../include
ldflags += -ffreestanding \ ldflags += -ffreestanding \
-nostdlib \ -nostdlib \
-fno-builtin \ -fno-builtin \
-fuse-ld=lld \ -fuse-ld=lld \
-static -static \
-Wl,--gc-sections \
-Wl,--strip-all \
-flto

13
include/m/status.h Normal file
View File

@@ -0,0 +1,13 @@
#ifndef _M_STATUS_H
#define _M_STATUS_H
#define ST_OK 0
#define ST_SYSCALL_NOT_FOUND 1
#define ST_UNALIGNED 2
#define ST_OOM_ERROR 3
#define ST_NOT_FOUND 4
#define ST_BAD_ADDRESS_SPACE 5
#define ST_PERMISSION_ERROR 6
#define ST_BAD_RESOURCE 7
#endif // _M_STATUS_H

16
include/m/syscall_defs.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef _M_SYSCALL_DEFS_H
#define _M_SYSCALL_DEFS_H
#define SYS_QUIT 1
#define SYS_TEST 2
#define SYS_MAP 3
#define SYS_UNMAP 4
#define SYS_CLONE 5
#define SYS_SCHED 6
#define SYS_MUTEX_CREATE 7
#define SYS_MUTEX_DELETE 8
#define SYS_MUTEX_LOCK 9
#define SYS_MUTEX_UNLOCK 10
#define SYS_ARGUMENT_PTR 11
#endif // _M_SYSCALL_DEFS_H

View File

@@ -1 +1 @@
include ../user.mk include ../make/user.mk

View File

@@ -1,5 +0,0 @@
.global _start
_start:
pushq $123
addq $8, %rsp
jmp _start

46
init/init.c Normal file
View File

@@ -0,0 +1,46 @@
#include <limits.h>
#include <proc/local.h>
#include <proc/proc.h>
#include <stddef.h>
#include <stdint.h>
#include <string/string.h>
#define MUTEX 2000
LOCAL volatile char letter = 'c';
void app_proc (void) {
char arg_letter = (char)(uintptr_t)argument_ptr ();
letter = arg_letter;
for (;;) {
mutex_lock (MUTEX);
for (int i = 0; i < 3; i++)
test (letter);
mutex_unlock (MUTEX);
}
process_quit ();
}
void app_main (void) {
mutex_create (MUTEX);
letter = 'a';
process_spawn (&app_proc, (void*)'a');
process_spawn (&app_proc, (void*)'b');
process_spawn (&app_proc, (void*)'c');
for (;;) {
mutex_lock (MUTEX);
for (int i = 0; i < 3; i++)
test (letter);
mutex_unlock (MUTEX);
}
}

View File

@@ -1,3 +1,3 @@
S += init.S c += init.c
o += init.o o += init.o

2
kernel/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*.json
.cache

View File

@@ -4,8 +4,10 @@
#include <amd64/msr.h> #include <amd64/msr.h>
#include <libk/std.h> #include <libk/std.h>
#include <limine/requests.h> #include <limine/requests.h>
#include <sync/spin_lock.h>
#include <sys/debug.h> #include <sys/debug.h>
#include <sys/mm.h> #include <sys/mm.h>
#include <sys/spin.h>
#include <sys/time.h> #include <sys/time.h>
#include <uacpi/acpi.h> #include <uacpi/acpi.h>
#include <uacpi/status.h> #include <uacpi/status.h>
@@ -15,56 +17,94 @@
#define IOAPICS_MAX 24 #define IOAPICS_MAX 24
#define INTERRUPT_SRC_OVERRIDES_MAX 24 #define INTERRUPT_SRC_OVERRIDES_MAX 24
#define LAPIC_ID 0x20 /* ID */ /* ID of Local APIC */
#define LAPIC_EOI 0xB0 /* End of interrupt */ #define LAPIC_ID 0x20
#define LAPIC_SIVR 0xF0 /* Spurious interrupt vector register */ /* End of interrupt register */
#define LAPIC_ICR 0x300 /* Interrupt command register */ #define LAPIC_EOI 0xB0
#define LAPIC_LVTTR 0x320 /* LVT timer register */ /* Spurious interrupt vector register */
#define LAPIC_TIMICT 0x380 /* Initial count register */ #define LAPIC_SIVR 0xF0
#define LAPIC_TIMCCT 0x390 /* Current count register */ /* Interrupt command register */
#define LAPIC_DCR 0x3E0 /* Divide config register */ #define LAPIC_ICR 0x300
/* LVT timer register */
#define LAPIC_LVTTR 0x320
/* Timer initial count register */
#define LAPIC_TIMICT 0x380
/* Timer current count register */
#define LAPIC_TIMCCT 0x390
/* Divide config register */
#define LAPIC_DCR 0x3E0
static struct acpi_madt_ioapic apics[IOAPICS_MAX]; #define DIVIDER_VALUE 0x0B
struct ioapic {
struct acpi_madt_ioapic table_data;
spin_lock_t lock;
uintptr_t mmio_base;
};
/* Table of IOAPICS */
static struct ioapic ioapics[IOAPICS_MAX];
/* Table of interrupt source overrides */
/* clang-format off */ /* clang-format off */
static struct acpi_madt_interrupt_source_override intr_src_overrides[INTERRUPT_SRC_OVERRIDES_MAX]; static struct acpi_madt_interrupt_source_override intr_src_overrides[INTERRUPT_SRC_OVERRIDES_MAX];
/* clang-format on */ /* clang-format on */
/* Count of actual IOAPIC entries */
static size_t ioapic_entries = 0; static size_t ioapic_entries = 0;
/* Count of actual interrupt source overrides */
static size_t intr_src_override_entries = 0; static size_t intr_src_override_entries = 0;
static uintptr_t lapic_mmio_base = 0;
extern void amd64_spin (void); static spin_lock_t lapic_calibration_lock = SPIN_LOCK_INIT;
static uint32_t amd64_ioapic_read (uintptr_t vaddr, uint32_t reg) { /* Read IOAPIC */
*(volatile uint32_t*)vaddr = reg; static uint32_t amd64_ioapic_read (struct ioapic* ioapic, uint32_t reg) {
return *(volatile uint32_t*)(vaddr + 0x10); spin_lock_ctx_t ctxioar;
spin_lock (&ioapic->lock, &ctxioar);
*(volatile uint32_t*)ioapic->mmio_base = reg;
uint32_t ret = *(volatile uint32_t*)(ioapic->mmio_base + 0x10);
spin_unlock (&ioapic->lock, &ctxioar);
return ret;
} }
static void amd64_ioapic_write (uintptr_t vaddr, uint32_t reg, uint32_t value) { /* Write IOAPIC */
*(volatile uint32_t*)vaddr = reg; static void amd64_ioapic_write (struct ioapic* ioapic, uint32_t reg, uint32_t value) {
*(volatile uint32_t*)(vaddr + 0x10) = value; spin_lock_ctx_t ctxioaw;
spin_lock (&ioapic->lock, &ctxioaw);
*(volatile uint32_t*)ioapic->mmio_base = reg;
*(volatile uint32_t*)(ioapic->mmio_base + 0x10) = value;
spin_unlock (&ioapic->lock, &ctxioaw);
} }
static struct acpi_madt_ioapic* amd64_ioapic_find (uint8_t irq) { /* Find an IOAPIC corresposting to provided IRQ */
struct acpi_madt_ioapic* apic = NULL; static struct ioapic* amd64_ioapic_find (uint32_t irq) {
struct limine_hhdm_response* hhdm = limine_hhdm_request.response; struct ioapic* ioapic = NULL;
for (size_t i = 0; i < ioapic_entries; i++) { for (size_t i = 0; i < ioapic_entries; i++) {
apic = &apics[i]; ioapic = &ioapics[i];
uint32_t version = amd64_ioapic_read ((uintptr_t)hhdm->offset + (uintptr_t)apic->address, 1); uint32_t version = amd64_ioapic_read (ioapic, 1);
uint32_t max = ((version >> 16) & 0xFF); uint32_t max = ((version >> 16) & 0xFF);
if ((irq >= apic->gsi_base) && (irq <= (apic->gsi_base + max))) if ((irq >= ioapic->table_data.gsi_base) && (irq <= (ioapic->table_data.gsi_base + max)))
return apic; return ioapic;
} }
return NULL; return NULL;
} }
void amd64_ioapic_route_irq (uint8_t vec, uint8_t irq, uint64_t flags, uint64_t lapic_id) { /*
struct acpi_madt_ioapic* apic = NULL; * Route IRQ to an IDT entry of a given Local APIC.
*
* vec - Interrupt vector number, which will be delivered to the CPU.
* irq -Legacy IRQ number to be routed. Can be changed by an interrupt source override
* into a different GSI.
* flags - IOAPIC redirection flags.
* lapic_id - Local APIC that will receive the interrupt.
*/
void amd64_ioapic_route_irq (uint32_t vec, uint32_t irq, uint64_t flags, uint64_t lapic_id) {
struct ioapic* ioapic = NULL;
struct acpi_madt_interrupt_source_override* override; struct acpi_madt_interrupt_source_override* override;
bool found_override = false; bool found_override = false;
struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
for (size_t i = 0; i < intr_src_override_entries; i++) { for (size_t i = 0; i < intr_src_override_entries; i++) {
override = &intr_src_overrides[i]; override = &intr_src_overrides[i];
@@ -77,62 +117,26 @@ void amd64_ioapic_route_irq (uint8_t vec, uint8_t irq, uint64_t flags, uint64_t
uint64_t calc_flags = (lapic_id << 56) | (flags) | (vec & 0xFF); uint64_t calc_flags = (lapic_id << 56) | (flags) | (vec & 0xFF);
if (found_override) { if (found_override) {
uint8_t polarity = ((override->flags & 0x03) == 0x03) ? 1 : 0; uint32_t polarity = ((override->flags & 0x03) == 0x03) ? 1 : 0;
uint8_t mode = (((override->flags >> 2) & 0x03) == 0x03) ? 1 : 0; uint32_t mode = (((override->flags >> 2) & 0x03) == 0x03) ? 1 : 0;
calc_flags |= (uint64_t)mode << 15; calc_flags |= (uint64_t)mode << 15;
calc_flags |= (uint64_t)polarity << 13; calc_flags |= (uint64_t)polarity << 13;
calc_flags |= flags;
} else {
calc_flags |= flags;
} }
apic = amd64_ioapic_find (irq); uint32_t gsi = found_override ? override->gsi : irq;
if (apic == NULL) ioapic = amd64_ioapic_find (gsi);
if (ioapic == NULL)
return; return;
uint32_t irq_reg = ((irq - apic->gsi_base) * 2) + 0x10; uint32_t irq_reg = ((gsi - ioapic->table_data.gsi_base) * 2) + 0x10;
amd64_ioapic_write ((uintptr_t)hhdm->offset + (uintptr_t)apic->address, irq_reg, amd64_ioapic_write (ioapic, irq_reg + 1, (uint32_t)(calc_flags >> 32));
(uint32_t)calc_flags); amd64_ioapic_write (ioapic, irq_reg, (uint32_t)calc_flags);
amd64_ioapic_write ((uintptr_t)hhdm->offset + (uintptr_t)apic->address, irq_reg + 1,
(uint32_t)(calc_flags >> 32));
}
void amd64_ioapic_mask (uint8_t irq) {
struct acpi_madt_ioapic* apic;
struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
apic = amd64_ioapic_find (irq);
if (apic == NULL)
return;
uint32_t irq_reg = ((irq - apic->gsi_base) * 2) + 0x10;
uint32_t value = amd64_ioapic_read ((uintptr_t)hhdm->offset + (uintptr_t)apic->address, irq_reg);
amd64_ioapic_write ((uintptr_t)hhdm->offset + (uintptr_t)apic->address, irq_reg,
value | (1 << 16));
}
void amd64_ioapic_unmask (uint8_t irq) {
struct acpi_madt_ioapic* apic;
struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
apic = amd64_ioapic_find (irq);
if (apic == NULL)
return;
uint32_t irq_reg = ((irq - apic->gsi_base) * 2) + 0x10;
uint32_t value = amd64_ioapic_read ((uintptr_t)hhdm->offset + (uintptr_t)apic->address, irq_reg);
amd64_ioapic_write ((uintptr_t)hhdm->offset + (uintptr_t)apic->address, irq_reg,
value & ~(1 << 16));
} }
/* Find and initialize the IOAPIC */
void amd64_ioapic_init (void) { void amd64_ioapic_init (void) {
struct limine_hhdm_response* hhdm = limine_hhdm_request.response; struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
@@ -140,7 +144,7 @@ void amd64_ioapic_init (void) {
uacpi_status status = uacpi_table_find_by_signature (ACPI_MADT_SIGNATURE, &apic_table); uacpi_status status = uacpi_table_find_by_signature (ACPI_MADT_SIGNATURE, &apic_table);
if (status != UACPI_STATUS_OK) { if (status != UACPI_STATUS_OK) {
DEBUG ("Could not find MADT table!\n"); DEBUG ("Could not find MADT table!\n");
amd64_spin (); spin ();
} }
struct acpi_madt* apic = (struct acpi_madt*)apic_table.virt_addr; struct acpi_madt* apic = (struct acpi_madt*)apic_table.virt_addr;
@@ -153,11 +157,15 @@ void amd64_ioapic_init (void) {
switch (current->type) { switch (current->type) {
case ACPI_MADT_ENTRY_TYPE_IOAPIC: { case ACPI_MADT_ENTRY_TYPE_IOAPIC: {
struct acpi_madt_ioapic* ioapic = (struct acpi_madt_ioapic*)current; struct acpi_madt_ioapic* ioapic_table_data = (struct acpi_madt_ioapic*)current;
mm_map_kernel_page ((uintptr_t)ioapic->address, mm_map_kernel_page ((uintptr_t)ioapic_table_data->address,
(uintptr_t)hhdm->offset + (uintptr_t)ioapic->address, (uintptr_t)hhdm->offset + (uintptr_t)ioapic_table_data->address,
MM_PG_PRESENT | MM_PG_RW | MM_PD_RELOAD); MM_PG_PRESENT | MM_PG_RW);
apics[ioapic_entries++] = *ioapic; ioapics[ioapic_entries++] = (struct ioapic){
.lock = SPIN_LOCK_INIT,
.table_data = *ioapic_table_data,
.mmio_base = ((uintptr_t)hhdm->offset + (uintptr_t)ioapic_table_data->address),
};
} break; } break;
case ACPI_MADT_ENTRY_TYPE_INTERRUPT_SOURCE_OVERRIDE: { case ACPI_MADT_ENTRY_TYPE_INTERRUPT_SOURCE_OVERRIDE: {
struct acpi_madt_interrupt_source_override* override = struct acpi_madt_interrupt_source_override* override =
@@ -170,65 +178,94 @@ void amd64_ioapic_init (void) {
} }
} }
static uintptr_t amd64_lapic_base (void) { return lapic_mmio_base; } /* Get MMIO base of Local APIC */
static uintptr_t amd64_lapic_base (void) { return thiscpu->lapic_mmio_base; }
/* Write Local APIC */
static void amd64_lapic_write (uint32_t reg, uint32_t value) { static void amd64_lapic_write (uint32_t reg, uint32_t value) {
*(volatile uint32_t*)(amd64_lapic_base () + reg) = value; *(volatile uint32_t*)(amd64_lapic_base () + reg) = value;
} }
/* Read Local APIC */
static uint32_t amd64_lapic_read (uint32_t reg) { static uint32_t amd64_lapic_read (uint32_t reg) {
return *(volatile uint32_t*)(amd64_lapic_base () + reg); return *(volatile uint32_t*)(amd64_lapic_base () + reg);
} }
/* Get ID of Local APIC */
uint32_t amd64_lapic_id (void) { return amd64_lapic_read (LAPIC_ID) >> 24; } uint32_t amd64_lapic_id (void) { return amd64_lapic_read (LAPIC_ID) >> 24; }
/* Send End of interrupt command to Local APIC */
void amd64_lapic_eoi (void) { amd64_lapic_write (LAPIC_EOI, 0); } void amd64_lapic_eoi (void) { amd64_lapic_write (LAPIC_EOI, 0); }
void amd64_lapic_tick (uint32_t tick) { amd64_lapic_write (LAPIC_TIMICT, tick); } /*
* Calibrate Local APIC to send interrupts in a set interval.
*
* us - Period length in microseconds
*/
static uint32_t amd64_lapic_calibrate (uint32_t us) { static uint32_t amd64_lapic_calibrate (uint32_t us) {
amd64_lapic_write (LAPIC_DCR, 0x03); spin_lock_ctx_t ctxlacb;
spin_lock (&lapic_calibration_lock, &ctxlacb);
amd64_lapic_write (LAPIC_DCR, DIVIDER_VALUE);
amd64_lapic_write (LAPIC_LVTTR, SCHED_PREEMPT_TIMER | (1 << 16)); amd64_lapic_write (LAPIC_LVTTR, SCHED_PREEMPT_TIMER | (1 << 16));
amd64_lapic_write (LAPIC_TIMICT, 0xFFFFFFFF); amd64_lapic_write (LAPIC_TIMICT, 0xFFFFFFFF);
sleep_micro (us); sleep_micro (us);
amd64_lapic_write (LAPIC_LVTTR, SCHED_PREEMPT_TIMER | (0 << 16));
uint32_t ticks = 0xFFFFFFFF - amd64_lapic_read (LAPIC_TIMCCT); uint32_t ticks = 0xFFFFFFFF - amd64_lapic_read (LAPIC_TIMCCT);
DEBUG ("timer ticks = %u\n", ticks);
spin_unlock (&lapic_calibration_lock, &ctxlacb);
return ticks; return ticks;
} }
/*
* Starts a Local APIC, configures LVT timer to send interrupts at SCHED_PREEMPT_TIMER.
*
* ticks - Initial tick count
*/
static void amd64_lapic_start (uint32_t ticks) { static void amd64_lapic_start (uint32_t ticks) {
amd64_lapic_write (LAPIC_DCR, 0x03); amd64_lapic_write (LAPIC_DCR, DIVIDER_VALUE);
amd64_lapic_write (LAPIC_LVTTR, SCHED_PREEMPT_TIMER | (1 << 17));
amd64_lapic_write (LAPIC_TIMICT, ticks); amd64_lapic_write (LAPIC_TIMICT, ticks);
amd64_lapic_write (LAPIC_LVTTR, SCHED_PREEMPT_TIMER | (1 << 17));
} }
uint64_t amd64_lapic_init (uint32_t us) { /*
* Initialize Local APIC, configure to send timer interrupts at a given period. See
* amd64_lapic_calibrate and amd64_lapic_start.
*/
void amd64_lapic_init (uint32_t us) {
struct limine_hhdm_response* hhdm = limine_hhdm_request.response; struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
amd64_wrmsr (MSR_APIC_BASE, amd64_rdmsr (MSR_APIC_BASE) | (1 << 11)); amd64_wrmsr (MSR_APIC_BASE, amd64_rdmsr (MSR_APIC_BASE) | (1 << 11));
uintptr_t lapic_paddr = amd64_rdmsr (MSR_APIC_BASE) & 0xFFFFF000; uintptr_t lapic_paddr = amd64_rdmsr (MSR_APIC_BASE) & 0xFFFFF000;
lapic_mmio_base = lapic_paddr + (uintptr_t)hhdm->offset; thiscpu->lapic_mmio_base = lapic_paddr + (uintptr_t)hhdm->offset;
mm_map_kernel_page (lapic_paddr, lapic_mmio_base, mm_map_kernel_page (lapic_paddr, thiscpu->lapic_mmio_base, MM_PG_PRESENT | MM_PG_RW);
MM_PG_PRESENT | MM_PG_RW | MM_PD_LOCK | MM_PD_RELOAD);
amd64_lapic_write (LAPIC_SIVR, 0xFF | (1 << 8)); amd64_lapic_write (LAPIC_SIVR, 0xFF | (1 << 8));
uint32_t ticks = amd64_lapic_calibrate (us); thiscpu->lapic_ticks = amd64_lapic_calibrate (us);
amd64_lapic_start (thiscpu->lapic_ticks);
amd64_lapic_start (ticks);
return ticks;
} }
void amd64_lapic_ipi (uint8_t lapic_id, uint8_t vec) { /*
* Send an IPI to a given Local APIC. This till invoke an IDT stub located at vec.
*
* lapic_id - Target Local APIC
* vec - Interrupt vector/IDT stub, which will be invoked by the IPI.
*/
void amd64_lapic_ipi (uint32_t lapic_id, uint32_t vec) {
/* wait for previous IPI to finish */
while (amd64_lapic_read (LAPIC_ICR) & (1 << 12)) {
__asm__ volatile ("pause");
}
amd64_lapic_write (LAPIC_ICR + 0x10, (lapic_id << 24)); amd64_lapic_write (LAPIC_ICR + 0x10, (lapic_id << 24));
amd64_lapic_write (LAPIC_ICR, vec); amd64_lapic_write (LAPIC_ICR, vec | (1 << 14));
} }

View File

@@ -3,15 +3,12 @@
#include <libk/std.h> #include <libk/std.h>
void amd64_ioapic_route_irq (uint8_t vec, uint8_t irq, uint64_t flags, uint64_t lapic_id); void amd64_ioapic_route_irq (uint32_t vec, uint32_t irq, uint64_t flags, uint64_t lapic_id);
void amd64_ioapic_mask (uint8_t irq);
void amd64_ioapic_unmask (uint8_t irq);
void amd64_ioapic_init (void); void amd64_ioapic_init (void);
uint32_t amd64_lapic_id (void); uint32_t amd64_lapic_id (void);
void amd64_lapic_tick (uint32_t tick);
void amd64_lapic_eoi (void); void amd64_lapic_eoi (void);
void amd64_lapic_ipi (uint8_t lapic_id, uint8_t vec); void amd64_lapic_ipi (uint32_t lapic_id, uint32_t vec);
uint64_t amd64_lapic_init (uint32_t us); void amd64_lapic_init (uint32_t us);
#endif // _KERNEL_AMD64_APIC_H #endif // _KERNEL_AMD64_APIC_H

View File

@@ -3,10 +3,13 @@
#include <amd64/hpet.h> #include <amd64/hpet.h>
#include <amd64/init.h> #include <amd64/init.h>
#include <amd64/intr_defs.h> #include <amd64/intr_defs.h>
#include <amd64/msr-index.h>
#include <amd64/msr.h>
#include <aux/compiler.h> #include <aux/compiler.h>
#include <irq/irq.h> #include <irq/irq.h>
#include <libk/std.h> #include <libk/std.h>
#include <limine/limine.h> #include <limine/limine.h>
#include <limine/requests.h>
#include <mm/liballoc.h> #include <mm/liballoc.h>
#include <mm/pmm.h> #include <mm/pmm.h>
#include <proc/proc.h> #include <proc/proc.h>
@@ -14,6 +17,7 @@
#include <sys/debug.h> #include <sys/debug.h>
#include <sys/mm.h> #include <sys/mm.h>
#include <sys/smp.h> #include <sys/smp.h>
#include <sys/syscall.h>
#include <sys/time.h> #include <sys/time.h>
#include <uacpi/uacpi.h> #include <uacpi/uacpi.h>
@@ -21,11 +25,17 @@
ALIGNED (16) static uint8_t uacpi_memory_buffer[UACPI_MEMORY_BUFFER_MAX]; ALIGNED (16) static uint8_t uacpi_memory_buffer[UACPI_MEMORY_BUFFER_MAX];
/*
* The kernel starts booting here. This is the entry point after Limine hands control. We set up all
* the necessary platform-dependent subsystems/drivers and jump into the init app.
*/
void bootmain (void) { void bootmain (void) {
struct cpu* bsp_cpu = cpu_make (); struct limine_mp_response* mp = limine_mp_request.response;
cpu_assign (bsp_cpu->id);
struct cpu* bsp_cpu = cpu_make (mp->bsp_lapic_id);
amd64_init (bsp_cpu, false); amd64_init (bsp_cpu, false);
syscall_init ();
amd64_debug_init (); amd64_debug_init ();
pmm_init (); pmm_init ();
mm_init (); mm_init ();
@@ -39,14 +49,6 @@ void bootmain (void) {
smp_init (); smp_init ();
/* busy wait for cpus to come online */
for (volatile int i = 0; i < INT_MAX; i++)
;
mm_init2 ();
__asm__ volatile ("sti");
proc_init (); proc_init ();
for (;;) for (;;)

View File

@@ -6,22 +6,40 @@
#include <sync/spin_lock.h> #include <sync/spin_lock.h>
#include <sys/debug.h> #include <sys/debug.h>
#define PORT_COM1 0x03F8 /* Port for printing to serial */
/* TODO: Make this configurable */
#define PORT_COM1 0x03F8
/* debugprintf buffer size */
#define BUFFER_SIZE 1024 #define BUFFER_SIZE 1024
/*
* Lock, which ensures that prints to the serial port are atomic (ie. one debugprintf is atomic in
* itself).
*/
static spin_lock_t serial_lock = SPIN_LOCK_INIT;
spin_lock_t serial_lock = SPIN_LOCK_INIT; static bool debug_init = false;
/* Block until TX buffer is empty */
static bool amd64_debug_serial_tx_empty (void) { static bool amd64_debug_serial_tx_empty (void) {
return (bool)(amd64_io_inb (PORT_COM1 + 5) & 0x20); return (bool)(amd64_io_inb (PORT_COM1 + 5) & 0x20);
} }
/* Write a single character to serial */
static void amd64_debug_serial_write (char x) { static void amd64_debug_serial_write (char x) {
while (!amd64_debug_serial_tx_empty ()) while (!amd64_debug_serial_tx_empty ())
; ;
amd64_io_outb (PORT_COM1, (uint8_t)x); amd64_io_outb (PORT_COM1, (uint8_t)x);
} }
/*
* Formatted printing to serial. serial_lock ensures that all prints are atomic.
*/
void debugprintf (const char* fmt, ...) { void debugprintf (const char* fmt, ...) {
spin_lock_ctx_t ctxdbgp;
if (!debug_init)
return;
char buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
memset (buffer, 0, sizeof (buffer)); memset (buffer, 0, sizeof (buffer));
@@ -34,16 +52,17 @@ void debugprintf (const char* fmt, ...) {
const char* p = buffer; const char* p = buffer;
spin_lock (&serial_lock); spin_lock (&serial_lock, &ctxdbgp);
while (*p) { while (*p) {
amd64_debug_serial_write (*p); amd64_debug_serial_write (*p);
p++; p++;
} }
spin_unlock (&serial_lock); spin_unlock (&serial_lock, &ctxdbgp);
} }
/* Initialize serial */
void amd64_debug_init (void) { void amd64_debug_init (void) {
amd64_io_outb (PORT_COM1 + 1, 0x00); amd64_io_outb (PORT_COM1 + 1, 0x00);
amd64_io_outb (PORT_COM1 + 3, 0x80); amd64_io_outb (PORT_COM1 + 3, 0x80);
@@ -52,4 +71,6 @@ void amd64_debug_init (void) {
amd64_io_outb (PORT_COM1 + 3, 0x03); amd64_io_outb (PORT_COM1 + 3, 0x03);
amd64_io_outb (PORT_COM1 + 2, 0xC7); amd64_io_outb (PORT_COM1 + 2, 0xC7);
amd64_io_outb (PORT_COM1 + 4, 0x0B); amd64_io_outb (PORT_COM1 + 4, 0x0B);
debug_init = true;
} }

View File

@@ -4,7 +4,8 @@ cflags += --target=x86_64-pc-none-elf \
-mno-avx \ -mno-avx \
-mno-mmx \ -mno-mmx \
-mno-80387 \ -mno-80387 \
-mno-red-zone -mno-red-zone \
-fno-omit-frame-pointer
ldflags += --target=x86_64-pc-none-elf \ ldflags += --target=x86_64-pc-none-elf \
-Wl,-zmax-page-size=0x1000 -Wl,-zmax-page-size=0x1000

View File

@@ -5,8 +5,21 @@
#include <libk/std.h> #include <libk/std.h>
#include <proc/proc.h> #include <proc/proc.h>
#define GDT_KCODE 0x08
#define GDT_KDATA 0x10
#define GDT_UDATA 0x18
#define GDT_UCODE 0x20
#define GDT_TSS 0x28
/* Size of kernel stack */
#define KSTACK_SIZE (32 * 1024) #define KSTACK_SIZE (32 * 1024)
/*
* 64-bit GDT structure. For more info see:
* - https://wiki.osdev.org/Global_Descriptor_Table
* - https://wiki.osdev.org/GDT_Tutorial
*/
struct gdt_entry { struct gdt_entry {
uint16_t limitlow; uint16_t limitlow;
uint16_t baselow; uint16_t baselow;
@@ -16,11 +29,13 @@ struct gdt_entry {
uint8_t basehigh; uint8_t basehigh;
} PACKED; } PACKED;
/* Struct that gets loaded into GDTR */
struct gdt_ptr { struct gdt_ptr {
uint16_t limit; uint16_t limit;
uint64_t base; uint64_t base;
} PACKED; } PACKED;
/* New, extended GDT (we need to extend Limine's GDT) */
struct gdt_extended { struct gdt_extended {
struct gdt_entry old[5]; struct gdt_entry old[5];
struct gdt_entry tsslow; struct gdt_entry tsslow;

View File

@@ -4,95 +4,139 @@
#include <sync/spin_lock.h> #include <sync/spin_lock.h>
#include <sys/debug.h> #include <sys/debug.h>
#include <sys/mm.h> #include <sys/mm.h>
#include <sys/spin.h>
#include <uacpi/acpi.h> #include <uacpi/acpi.h>
#include <uacpi/status.h> #include <uacpi/status.h>
#include <uacpi/tables.h> #include <uacpi/tables.h>
#include <uacpi/uacpi.h> #include <uacpi/uacpi.h>
#define HPET_MCVR 0xF0 /* Main Counter Value Register */ /*
#define HPET_GCR 0x10 /* General Configuration Register */ * HPET (High Precision Event Timer) driver code. See more at https://wiki.osdev.org/HPET
#define HPET_GCIDR 0x00 /* General Capabilities and ID Register */ */
/* HPET Main Counter Value Register */
#define HPET_MCVR 0xF0
/* HPET General Configuration Register */
#define HPET_GCR 0x10
/* HPET General Capabilities and ID Register */
#define HPET_GCIDR 0x00
/* Set whether we sould use 32-bit or 64-bit reads/writes */
static bool hpet_32bits = 1; static bool hpet_32bits = 1;
/* Physical address for HPET MMIO */
static uintptr_t hpet_paddr; static uintptr_t hpet_paddr;
static uint64_t hpet_clock_nano; /* HPET period in femtoseconds */
static uint64_t hpet_period_fs;
/* Lock, which protects concurrent access. See amd64/smp.c */
static spin_lock_t hpet_lock = SPIN_LOCK_INIT; static spin_lock_t hpet_lock = SPIN_LOCK_INIT;
extern void amd64_spin (void); /* Read a HPET register. Assumes caller holds hpet_lock */
static uint64_t amd64_hpet_read64 (uint32_t reg) {
/* These functions assume hpet_lock is held by the caller! */
static uint64_t amd64_hpet_read (uint32_t reg) {
struct limine_hhdm_response* hhdm = limine_hhdm_request.response; struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
uintptr_t hpet_vaddr = hpet_paddr + (uintptr_t)hhdm->offset; uintptr_t hpet_vaddr = hpet_paddr + (uintptr_t)hhdm->offset;
return (hpet_32bits ? *(volatile uint32_t*)(hpet_vaddr + reg) return *(volatile uint64_t*)(hpet_vaddr + reg);
: *(volatile uint64_t*)(hpet_vaddr + reg));
} }
static void amd64_hpet_write (uint32_t reg, uint64_t value) { static uint32_t amd64_hpet_read32 (uint32_t reg) {
struct limine_hhdm_response* hhdm = limine_hhdm_request.response; struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
uintptr_t hpet_vaddr = hpet_paddr + (uintptr_t)hhdm->offset; uintptr_t hpet_vaddr = hpet_paddr + (uintptr_t)hhdm->offset;
if (hpet_32bits) return *(volatile uint32_t*)(hpet_vaddr + reg);
*(volatile uint32_t*)(hpet_vaddr + reg) = (value & 0xFFFFFFFF);
else
*(volatile uint64_t*)(hpet_vaddr + reg) = value;
} }
static uint64_t amd64_hpet_timestamp (void) { return amd64_hpet_read (HPET_MCVR); } /* Write a HPET register. Assumes caller holds hpet_lock */
static void amd64_hpet_write64 (uint32_t reg, uint64_t value) {
uint64_t amd64_hpet_current_nano (bool lock) { struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
if (lock) uintptr_t hpet_vaddr = hpet_paddr + (uintptr_t)hhdm->offset;
spin_lock (&hpet_lock); *(volatile uint64_t*)(hpet_vaddr + reg) = value;
uint64_t t = amd64_hpet_timestamp () * hpet_clock_nano;
if (lock)
spin_unlock (&hpet_lock);
return t;
} }
static void amd64_hpet_write32 (uint32_t reg, uint32_t value) {
struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
uintptr_t hpet_vaddr = hpet_paddr + (uintptr_t)hhdm->offset;
*(volatile uint32_t*)(hpet_vaddr + reg) = value;
}
/* Read current value of HPET_MCVR register. */
static uint64_t amd64_hpet_read_counter (void) {
uint64_t value;
spin_lock_ctx_t ctxhrc;
spin_lock (&hpet_lock, &ctxhrc);
if (!hpet_32bits)
value = amd64_hpet_read64 (HPET_MCVR);
else {
uint32_t hi1, lo, hi2;
do {
hi1 = amd64_hpet_read32 (HPET_MCVR + 4);
lo = amd64_hpet_read32 (HPET_MCVR + 0);
hi2 = amd64_hpet_read32 (HPET_MCVR + 4);
} while (hi1 != hi2);
value = ((uint64_t)hi1 << 32) | lo;
}
spin_unlock (&hpet_lock, &ctxhrc);
return value;
}
static void amd64_hpet_write_counter (uint64_t value) {
spin_lock_ctx_t ctxhwc;
spin_lock (&hpet_lock, &ctxhwc);
if (!hpet_32bits)
amd64_hpet_write64 (HPET_MCVR, value);
else {
amd64_hpet_write32 (HPET_MCVR, (uint32_t)value);
amd64_hpet_write32 (HPET_MCVR + 4, (uint32_t)(value >> 32));
}
spin_unlock (&hpet_lock, &ctxhwc);
}
/* Sleep for a given amount of microseconds. This time can last longer due to \ref hpet_lock being
* held. */
void amd64_hpet_sleep_micro (uint64_t us) { void amd64_hpet_sleep_micro (uint64_t us) {
spin_lock (&hpet_lock); if (hpet_period_fs == 0)
return;
uint64_t ticks_to_wait = (us * 1000ULL) / (hpet_period_fs / 1000000ULL);
uint64_t start = amd64_hpet_read_counter ();
for (;;) {
uint64_t now = amd64_hpet_read_counter ();
if ((now - start) >= ticks_to_wait)
break;
uint64_t start = amd64_hpet_timestamp ();
uint64_t conv = us * 1000;
while (((amd64_hpet_timestamp () - start) * hpet_clock_nano) < conv)
__asm__ volatile ("pause" ::: "memory"); __asm__ volatile ("pause" ::: "memory");
}
spin_unlock (&hpet_lock);
} }
/* Initialize HPET */
void amd64_hpet_init (void) { void amd64_hpet_init (void) {
struct uacpi_table hpet_table; struct uacpi_table hpet_table;
uacpi_status status = uacpi_table_find_by_signature (ACPI_HPET_SIGNATURE, &hpet_table); uacpi_status status = uacpi_table_find_by_signature (ACPI_HPET_SIGNATURE, &hpet_table);
if (status != UACPI_STATUS_OK) { if (status != UACPI_STATUS_OK) {
DEBUG ("Could not find HPET table!\n"); DEBUG ("Could not find HPET table!\n");
amd64_spin (); spin ();
} }
struct acpi_hpet* hpet = (struct acpi_hpet*)hpet_table.virt_addr; struct acpi_hpet* hpet = (struct acpi_hpet*)hpet_table.virt_addr;
hpet_paddr = (uintptr_t)hpet->address.address; hpet_paddr = (uintptr_t)hpet->address.address;
struct limine_hhdm_response* hhdm = limine_hhdm_request.response; struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
mm_map_kernel_page (hpet_paddr, (uintptr_t)hhdm->offset + hpet_paddr, mm_map_kernel_page (hpet_paddr, (uintptr_t)hhdm->offset + hpet_paddr, MM_PG_PRESENT | MM_PG_RW);
MM_PG_PRESENT | MM_PG_RW | MM_PD_RELOAD);
hpet_32bits = (amd64_hpet_read (HPET_GCIDR) & (1 << 13)) ? 0 : 1; uint64_t caps = amd64_hpet_read64 (HPET_GCIDR);
hpet_32bits = (caps & (1 << 13)) ? 0 : 1;
/* reset */ hpet_period_fs = (uint32_t)(caps >> 32);
amd64_hpet_write (HPET_GCR, 0);
amd64_hpet_write (HPET_MCVR, 0);
amd64_hpet_write (HPET_GCR, 1);
uint64_t gcidr = amd64_hpet_read (HPET_GCIDR); amd64_hpet_write64 (HPET_GCR, 0);
if (hpet_32bits) { amd64_hpet_write_counter (0);
uint32_t low = (uint32_t)gcidr; amd64_hpet_write64 (HPET_GCR, 1);
uint32_t high = (uint32_t)amd64_hpet_read (HPET_GCIDR + 4);
gcidr = (((uint64_t)high << 32) | low);
}
uint64_t period_fs = (gcidr >> 32);
hpet_clock_nano = period_fs / 1000000;
} }

View File

@@ -3,7 +3,6 @@
#include <libk/std.h> #include <libk/std.h>
uint64_t amd64_hpet_current_nano (bool lock);
void amd64_hpet_sleep_micro (uint64_t us); void amd64_hpet_sleep_micro (uint64_t us);
void amd64_hpet_init (void); void amd64_hpet_init (void);

View File

@@ -1,3 +1,4 @@
#include <amd64/gdt.h>
#include <amd64/init.h> #include <amd64/init.h>
#include <amd64/intr.h> #include <amd64/intr.h>
#include <amd64/smp.h> #include <amd64/smp.h>
@@ -5,15 +6,10 @@
#include <libk/std.h> #include <libk/std.h>
#include <libk/string.h> #include <libk/string.h>
#define GDT_KCODE 0x08
#define GDT_KDATA 0x10
#define GDT_UCODE 0x18
#define GDT_UDATA 0x20
#define GDT_TSS 0x28
#define TSS 0x80 #define TSS 0x80
#define TSS_PRESENT 0x89 #define TSS_PRESENT 0x89
/* Set a GDT entry */
static void amd64_gdt_set (volatile struct gdt_entry* ent, uint32_t base, uint32_t limit, static void amd64_gdt_set (volatile struct gdt_entry* ent, uint32_t base, uint32_t limit,
uint8_t acc, uint8_t gran) { uint8_t acc, uint8_t gran) {
ent->baselow = (base & 0xFFFF); ent->baselow = (base & 0xFFFF);
@@ -24,6 +20,7 @@ static void amd64_gdt_set (volatile struct gdt_entry* ent, uint32_t base, uint32
ent->access = acc; ent->access = acc;
} }
/* Initialize GDT and TSS structures for a given CPU */
static void amd64_gdt_init (struct cpu* cpu) { static void amd64_gdt_init (struct cpu* cpu) {
volatile struct tss* tss = &cpu->tss; volatile struct tss* tss = &cpu->tss;
volatile struct gdt_extended* gdt = &cpu->gdt; volatile struct gdt_extended* gdt = &cpu->gdt;
@@ -42,8 +39,8 @@ static void amd64_gdt_init (struct cpu* cpu) {
amd64_gdt_set (&gdt->old[0], 0, 0, 0, 0); amd64_gdt_set (&gdt->old[0], 0, 0, 0, 0);
amd64_gdt_set (&gdt->old[1], 0, 0xFFFFF, 0x9A, 0xA0); amd64_gdt_set (&gdt->old[1], 0, 0xFFFFF, 0x9A, 0xA0);
amd64_gdt_set (&gdt->old[2], 0, 0xFFFFF, 0x92, 0xC0); amd64_gdt_set (&gdt->old[2], 0, 0xFFFFF, 0x92, 0xC0);
amd64_gdt_set (&gdt->old[3], 0, 0xFFFFF, 0xFA, 0xA0); amd64_gdt_set (&gdt->old[3], 0, 0xFFFFF, 0xF2, 0xC0);
amd64_gdt_set (&gdt->old[4], 0, 0xFFFFF, 0xF2, 0xC0); amd64_gdt_set (&gdt->old[4], 0, 0xFFFFF, 0xFA, 0xA0);
amd64_gdt_set (&gdt->tsslow, (tssbase & 0xFFFFFFFF), tsslimit, TSS_PRESENT | TSS, 0); amd64_gdt_set (&gdt->tsslow, (tssbase & 0xFFFFFFFF), tsslimit, TSS_PRESENT | TSS, 0);
uint32_t tssbasehigh = (tssbase >> 32); uint32_t tssbasehigh = (tssbase >> 32);
@@ -54,11 +51,13 @@ static void amd64_gdt_init (struct cpu* cpu) {
gdt->tsshigh.access = 0; gdt->tsshigh.access = 0;
gdt->tsshigh.gran = 0; gdt->tsshigh.gran = 0;
/* Load GDTR */
struct gdt_ptr gdtr; struct gdt_ptr gdtr;
gdtr.limit = sizeof (*gdt) - 1; gdtr.limit = sizeof (*gdt) - 1;
gdtr.base = (uint64_t)gdt; gdtr.base = (uint64_t)gdt;
__asm__ volatile ("lgdt %0" ::"m"(gdtr) : "memory"); __asm__ volatile ("lgdt %0" ::"m"(gdtr) : "memory");
/* Reload CS */
__asm__ volatile ("pushq %[kcode]\n" __asm__ volatile ("pushq %[kcode]\n"
"lea 1f(%%rip), %%rax\n" "lea 1f(%%rip), %%rax\n"
"pushq %%rax\n" "pushq %%rax\n"
@@ -75,6 +74,12 @@ static void amd64_gdt_init (struct cpu* cpu) {
__asm__ volatile ("ltr %0" ::"r"((uint16_t)GDT_TSS)); __asm__ volatile ("ltr %0" ::"r"((uint16_t)GDT_TSS));
} }
/*
* Initialize essentials (GDT, TSS, IDT) for a given CPU
*
* load_idt - Tell whether the IDT needs to be loaded. It only has to be loaded once on
* the BSP
*/
void amd64_init (struct cpu* cpu, bool load_idt) { void amd64_init (struct cpu* cpu, bool load_idt) {
amd64_gdt_init (cpu); amd64_gdt_init (cpu);
if (load_idt) if (load_idt)

View File

@@ -1,4 +1,5 @@
#include <amd64/apic.h> #include <amd64/apic.h>
#include <amd64/gdt.h>
#include <amd64/intr.h> #include <amd64/intr.h>
#include <amd64/intr_defs.h> #include <amd64/intr_defs.h>
#include <amd64/io.h> #include <amd64/io.h>
@@ -6,9 +7,12 @@
#include <irq/irq.h> #include <irq/irq.h>
#include <libk/std.h> #include <libk/std.h>
#include <libk/string.h> #include <libk/string.h>
#include <m/syscall_defs.h>
#include <sys/debug.h> #include <sys/debug.h>
#include <sys/irq.h> #include <sys/irq.h>
#include <sys/smp.h> #include <sys/smp.h>
#include <sys/spin.h>
#include <syscall/syscall.h>
/* 8259 PIC defs. */ /* 8259 PIC defs. */
#define PIC1 0x20 #define PIC1 0x20
@@ -37,6 +41,7 @@
#define IDT_ENTRIES_MAX 256 #define IDT_ENTRIES_MAX 256
/* 64-bit <IDT entry structure: https://wiki.osdev.org/Interrupt_Descriptor_Table */
struct idt_entry { struct idt_entry {
uint16_t intrlow; uint16_t intrlow;
uint16_t kernel_cs; uint16_t kernel_cs;
@@ -55,8 +60,6 @@ struct idt {
ALIGNED (16) static volatile struct idt_entry idt_entries[IDT_ENTRIES_MAX]; ALIGNED (16) static volatile struct idt_entry idt_entries[IDT_ENTRIES_MAX];
static volatile struct idt idt; static volatile struct idt idt;
extern void amd64_spin (void);
/* Remaps and disables old 8259 PIC, since we'll be using APIC. */ /* Remaps and disables old 8259 PIC, since we'll be using APIC. */
static void amd64_init_pic (void) { static void amd64_init_pic (void) {
#define IO_OP(fn, ...) \ #define IO_OP(fn, ...) \
@@ -82,10 +85,11 @@ static void amd64_init_pic (void) {
#undef IO_OP #undef IO_OP
} }
/* Set IDT entry */
static void amd64_idt_set (volatile struct idt_entry* ent, uint64_t handler, uint8_t flags, static void amd64_idt_set (volatile struct idt_entry* ent, uint64_t handler, uint8_t flags,
uint8_t ist) { uint8_t ist) {
ent->intrlow = (handler & 0xFFFF); ent->intrlow = (handler & 0xFFFF);
ent->kernel_cs = 0x08; // GDT_KCODE (init.c) ent->kernel_cs = GDT_KCODE;
ent->ist = ist; ent->ist = ist;
ent->attrs = flags; ent->attrs = flags;
ent->intrmid = ((handler >> 16) & 0xFFFF); ent->intrmid = ((handler >> 16) & 0xFFFF);
@@ -93,8 +97,10 @@ static void amd64_idt_set (volatile struct idt_entry* ent, uint64_t handler, uin
ent->resv = 0; ent->resv = 0;
} }
/* Load the IDT */
void amd64_load_idt (void) { __asm__ volatile ("lidt %0" ::"m"(idt)); } void amd64_load_idt (void) { __asm__ volatile ("lidt %0" ::"m"(idt)); }
/* Initialize IDT entries */
static void amd64_idt_init (void) { static void amd64_idt_init (void) {
memset ((void*)idt_entries, 0, sizeof (idt_entries)); memset ((void*)idt_entries, 0, sizeof (idt_entries));
@@ -117,6 +123,8 @@ static void amd64_idt_init (void) {
IDT_ENTRY (SCHED_PREEMPT_TIMER, 1); IDT_ENTRY (SCHED_PREEMPT_TIMER, 1);
IDT_ENTRY (TLB_SHOOTDOWN, 1); IDT_ENTRY (TLB_SHOOTDOWN, 1);
IDT_ENTRY (CPU_REQUEST_SCHED, 1);
IDT_ENTRY (CPU_SPURIOUS, 1);
/* clang-format on */ /* clang-format on */
#undef IDT_ENTRY #undef IDT_ENTRY
@@ -126,6 +134,7 @@ static void amd64_idt_init (void) {
amd64_load_idt (); amd64_load_idt ();
} }
/* Handle CPU exception and dump registers. If incoming CS has CPL3, kill the process. */
static void amd64_intr_exception (struct saved_regs* regs) { static void amd64_intr_exception (struct saved_regs* regs) {
DEBUG ("cpu exception %lu (%lu)\n", regs->trap, regs->error); DEBUG ("cpu exception %lu (%lu)\n", regs->trap, regs->error);
@@ -147,16 +156,30 @@ static void amd64_intr_exception (struct saved_regs* regs) {
regs->error, regs->rip, regs->cs, regs->rflags, regs->rsp, regs->ss, cr2, cr3, regs->error, regs->rip, regs->cs, regs->rflags, regs->rsp, regs->ss, cr2, cr3,
regs->rbx); regs->rbx);
if (regs->cs == (0x18 | 0x03)) { if (regs->cs == (GDT_UCODE | 0x03)) {
proc_kill (thiscpu->proc_current); proc_kill (thiscpu->proc_current);
} else { } else {
amd64_spin (); spin ();
} }
} }
/* Handle incoming interrupt, dispatch IRQ handlers. */
void amd64_intr_handler (void* stack_ptr) { void amd64_intr_handler (void* stack_ptr) {
spin_lock_ctx_t ctxcpu, ctxpr;
amd64_load_kernel_cr3 ();
struct saved_regs* regs = stack_ptr; struct saved_regs* regs = stack_ptr;
spin_lock (&thiscpu->lock, &ctxcpu);
struct proc* proc_current = thiscpu->proc_current;
spin_lock (&proc_current->lock, &ctxpr);
memcpy (&proc_current->pdata.regs, regs, sizeof (struct saved_regs));
spin_unlock (&proc_current->lock, &ctxpr);
spin_unlock (&thiscpu->lock, &ctxcpu);
if (regs->trap <= 31) { if (regs->trap <= 31) {
amd64_intr_exception (regs); amd64_intr_exception (regs);
} else { } else {
@@ -165,17 +188,12 @@ void amd64_intr_handler (void* stack_ptr) {
struct irq* irq = irq_find (regs->trap); struct irq* irq = irq_find (regs->trap);
if (irq != NULL) { if (irq != NULL) {
if (!(irq->flags & IRQ_INTERRUPT_SAFE))
__asm__ volatile ("sti");
irq->func (irq->arg, stack_ptr); irq->func (irq->arg, stack_ptr);
if (!(irq->flags & IRQ_INTERRUPT_SAFE))
__asm__ volatile ("cli");
} }
} }
} }
/* Initialize interrupts */
void amd64_intr_init (void) { void amd64_intr_init (void) {
amd64_init_pic (); amd64_init_pic ();
amd64_idt_init (); amd64_idt_init ();
@@ -183,42 +201,21 @@ void amd64_intr_init (void) {
/* Aux. */ /* Aux. */
/* Save RFLAGS of the current CPU */
static uint64_t amd64_irq_save_flags (void) { static uint64_t amd64_irq_save_flags (void) {
uint64_t rflags; uint64_t rflags;
__asm__ volatile ("pushfq; cli; popq %0" : "=r"(rflags)::"memory", "cc"); __asm__ volatile ("pushfq; cli; popq %0" : "=r"(rflags)::"memory", "cc");
return rflags; return rflags;
} }
/* Restore interrupts (IF bit) from RFLAGS */
static void amd64_irq_restore_flags (uint64_t rflags) { static void amd64_irq_restore_flags (uint64_t rflags) {
if (rflags & (1ULL << 9)) if (rflags & (1ULL << 9))
__asm__ volatile ("sti"); __asm__ volatile ("sti");
} }
void irq_save (void) { /* Save current interrupt state */
/* before smp init. */ void irq_save (spin_lock_ctx_t* ctx) { *ctx = amd64_irq_save_flags (); }
if (thiscpu == NULL)
return;
int prev = atomic_fetch_add_explicit (&thiscpu->irq_ctx.nesting, 1, memory_order_acq_rel); /* Restore interrupt state */
if (prev == 0) void irq_restore (spin_lock_ctx_t* ctx) { amd64_irq_restore_flags (*ctx); }
thiscpu->irq_ctx.rflags = amd64_irq_save_flags ();
}
void irq_restore (void) {
/* before smp init. */
if (thiscpu == NULL)
return;
int prev = atomic_fetch_sub_explicit (&thiscpu->irq_ctx.nesting, 1, memory_order_acq_rel);
if (prev == 1)
amd64_irq_restore_flags (thiscpu->irq_ctx.rflags);
}
uint8_t amd64_resolve_irq (uint8_t irq) {
static const uint8_t mappings[] = {
[SCHED_PREEMPT_TIMER] = 0,
[TLB_SHOOTDOWN] = 1,
};
return mappings[irq];
}

View File

@@ -32,7 +32,6 @@ struct saved_regs {
} PACKED; } PACKED;
void amd64_load_idt (void); void amd64_load_idt (void);
uint8_t amd64_resolve_irq (uint8_t irq);
void amd64_intr_init (void); void amd64_intr_init (void);
#endif // _KERNEL_AMD64_INTR_H #endif // _KERNEL_AMD64_INTR_H

View File

@@ -1,7 +1,12 @@
#ifndef _KERNEL_AMD64_INTR_DEFS_H #ifndef _KERNEL_AMD64_INTR_DEFS_H
#define _KERNEL_AMD64_INTR_DEFS_H #define _KERNEL_AMD64_INTR_DEFS_H
/* Definitions for custom, nonstandard IDT entries. They have to be remapped by amd64_resolve_irq
* into legacy IRQs. */
#define SCHED_PREEMPT_TIMER 80 #define SCHED_PREEMPT_TIMER 80
#define TLB_SHOOTDOWN 81 #define TLB_SHOOTDOWN 81
#define CPU_REQUEST_SCHED 82
#define CPU_SPURIOUS 255
#endif // _KERNEL_AMD64_INTR_DEFS_H #endif // _KERNEL_AMD64_INTR_DEFS_H

View File

@@ -1,4 +1,5 @@
#include <amd64/intr_defs.h> #include <amd64/intr_defs.h>
#include <amd64/regsasm.h>
.extern amd64_intr_handler .extern amd64_intr_handler
@@ -6,59 +7,41 @@
pushq $z; pushq $z;
#define no_err(z) \ #define no_err(z) \
pushq $0; \ pushq $0; \
pushq $z; pushq $z;
#define push_regs \ #define make_intr_stub(x, n) \
pushq %rax; \ .global amd64_intr ## n; \
pushq %rcx; \ amd64_intr ## n:; \
pushq %rdx; \ x(n); \
pushq %rsi; \ cli; \
pushq %rdi; \ ; \
pushq %rbp; \ push_regs; \
pushq %rbx; \ ; \
pushq %r8; \ movw $0x10, %ax; \
pushq %r9; \ movw %ax, %ds; \
pushq %r10; \ movw %ax, %es; \
pushq %r11; \ ; \
pushq %r12; \ cld; \
pushq %r13; \ ; \
pushq %r14; \ movq %rsp, %rdi; \
pushq %r15; ; \
movq %cr3, %rax; pushq %rax; \
#define pop_regs \ ; \
popq %r15; \ movq %rsp, %rbp; \
popq %r14; \ ; \
popq %r13; \ subq $8, %rsp; \
popq %r12; \ andq $-16, %rsp; \
popq %r11; \ ; \
popq %r10; \ callq amd64_intr_handler; \
popq %r9; \ ; \
popq %r8; \ movq %rbp, %rsp; \
popq %rbx; \ ; \
popq %rbp; \ popq %rax; movq %rax, %cr3; \
popq %rdi; \ ; \
popq %rsi; \ pop_regs; \
popq %rdx; \ addq $16, %rsp; \
popq %rcx; \ ; \
popq %rax;
#define make_intr_stub(x, n) \
.global amd64_intr ## n; \
amd64_intr ## n:; \
x(n); \
cli; \
push_regs; \
cld; \
movq %rsp, %rdi; \
movq %rsp, %rax; \
subq $8, %rsp; \
andq $~0xF, %rsp; \
movq %rax, (%rsp); \
callq amd64_intr_handler; \
movq (%rsp), %rsp; \
pop_regs; \
addq $16, %rsp; \
iretq; iretq;
@@ -113,3 +96,5 @@ make_intr_stub(no_err, 47)
make_intr_stub(no_err, SCHED_PREEMPT_TIMER) make_intr_stub(no_err, SCHED_PREEMPT_TIMER)
make_intr_stub(no_err, TLB_SHOOTDOWN) make_intr_stub(no_err, TLB_SHOOTDOWN)
make_intr_stub(no_err, CPU_REQUEST_SCHED)
make_intr_stub(no_err, CPU_SPURIOUS)

View File

@@ -1,42 +1,51 @@
#include <amd64/io.h> #include <amd64/io.h>
#include <libk/std.h> #include <libk/std.h>
/// Perform outb instruction (send 8-bit int)
void amd64_io_outb (uint16_t port, uint8_t v) { void amd64_io_outb (uint16_t port, uint8_t v) {
__asm__ volatile ("outb %1, %0" ::"dN"(port), "a"(v)); __asm__ volatile ("outb %1, %0" ::"dN"(port), "a"(v));
} }
/// Perform outw instruction (send 16-bit int)
void amd64_io_outw (uint16_t port, uint16_t v) { void amd64_io_outw (uint16_t port, uint16_t v) {
__asm__ volatile ("outw %%ax, %%dx" ::"a"(v), "d"(port)); __asm__ volatile ("outw %%ax, %%dx" ::"a"(v), "d"(port));
} }
/// Perform outl instruction (send 32-bit int)
void amd64_io_outl (uint16_t port, uint32_t v) { void amd64_io_outl (uint16_t port, uint32_t v) {
__asm__ volatile ("outl %%eax, %%dx" ::"d"(port), "a"(v)); __asm__ volatile ("outl %%eax, %%dx" ::"d"(port), "a"(v));
} }
/// Perform outsw instruction (send a string)
void amd64_io_outsw (uint16_t port, const void* addr, int cnt) { void amd64_io_outsw (uint16_t port, const void* addr, int cnt) {
__asm__ volatile ("cld; rep outsw" : "+S"(addr), "+c"(cnt) : "d"(port) : "memory", "cc"); __asm__ volatile ("cld; rep outsw" : "+S"(addr), "+c"(cnt) : "d"(port) : "memory", "cc");
} }
/// Perform inb instruction (receive 8-bit int)
uint8_t amd64_io_inb (uint16_t port) { uint8_t amd64_io_inb (uint16_t port) {
uint8_t r; uint8_t r;
__asm__ volatile ("inb %1, %0" : "=a"(r) : "dN"(port)); __asm__ volatile ("inb %1, %0" : "=a"(r) : "dN"(port));
return r; return r;
} }
/// Perform inw instruction (receive 16-bit int)
uint16_t amd64_io_inw (uint16_t port) { uint16_t amd64_io_inw (uint16_t port) {
uint16_t r; uint16_t r;
__asm__ volatile ("inw %%dx, %%ax" : "=a"(r) : "d"(port)); __asm__ volatile ("inw %%dx, %%ax" : "=a"(r) : "d"(port));
return r; return r;
} }
/// Perform inl instruction (receive 32-bit int)
uint32_t amd64_io_inl (uint16_t port) { uint32_t amd64_io_inl (uint16_t port) {
uint32_t r; uint32_t r;
__asm__ volatile ("inl %%dx, %%eax" : "=a"(r) : "d"(port)); __asm__ volatile ("inl %%dx, %%eax" : "=a"(r) : "d"(port));
return r; return r;
} }
/// Perform insw instruction (receive a string)
void amd64_io_insw (uint16_t port, void* addr, int cnt) { void amd64_io_insw (uint16_t port, void* addr, int cnt) {
__asm__ volatile ("cld; rep insw" : "+D"(addr), "+c"(cnt) : "d"(port) : "memory", "cc"); __asm__ volatile ("cld; rep insw" : "+D"(addr), "+c"(cnt) : "d"(port) : "memory", "cc");
} }
/// output a byte on port 0x80, which does a small IO delay
void amd64_io_wait (void) { amd64_io_outb (0x80, 0); } void amd64_io_wait (void) { amd64_io_outb (0x80, 0); }

View File

@@ -14,25 +14,40 @@
#define AMD64_PG_PRESENT (1 << 0) #define AMD64_PG_PRESENT (1 << 0)
#define AMD64_PG_RW (1 << 1) #define AMD64_PG_RW (1 << 1)
#define AMD64_PG_USER (1 << 2) #define AMD64_PG_USER (1 << 2)
#define AMD64_PG_HUGE (1 << 7)
/* Auxilary struct for page directory walking */
struct pg_index { struct pg_index {
uint16_t pml4, pml3, pml2, pml1; uint16_t pml4, pml3, pml2, pml1;
} PACKED; } PACKED;
static struct pd kernel_pd = {.lock = SPIN_LOCK_INIT}; /* Kernel page directory */
/* This is needed to sync between map/unmap operations and TLB shootdown. */ static struct pd kernel_pd;
static spin_lock_t mm_lock = SPIN_LOCK_INIT; static spin_lock_t kernel_pd_lock;
void mm_kernel_lock (spin_lock_ctx_t* ctx) { spin_lock (&kernel_pd_lock, ctx); }
void mm_kernel_unlock (spin_lock_ctx_t* ctx) { spin_lock (&kernel_pd_lock, ctx); }
/* Get current value of CR3 register */
static uintptr_t amd64_current_cr3 (void) { static uintptr_t amd64_current_cr3 (void) {
uintptr_t cr3; uintptr_t cr3;
__asm__ volatile ("movq %%cr3, %0" : "=r"(cr3)::"memory"); __asm__ volatile ("movq %%cr3, %0" : "=r"(cr3)::"memory");
return cr3; return cr3;
} }
/* Load kernel CR3 as current CR3 */
void amd64_load_kernel_cr3 (void) { void amd64_load_kernel_cr3 (void) {
__asm__ volatile ("movq %0, %%cr3" ::"r"(kernel_pd.cr3_paddr) : "memory"); uintptr_t cr3 = amd64_current_cr3 ();
if (cr3 != kernel_pd.cr3_paddr) {
__asm__ volatile ("movq %0, %%cr3" ::"r"(kernel_pd.cr3_paddr) : "memory");
}
} }
struct pd* mm_get_kernel_pd (void) { return &kernel_pd; }
/* Extract PML info from virtual address */
static struct pg_index amd64_mm_page_index (uint64_t vaddr) { static struct pg_index amd64_mm_page_index (uint64_t vaddr) {
struct pg_index ret; struct pg_index ret;
@@ -44,15 +59,19 @@ static struct pg_index amd64_mm_page_index (uint64_t vaddr) {
return ret; return ret;
} }
/* Walk paging tables and allocate necessary structures along the way */
static uint64_t* amd64_mm_next_table (uint64_t* table, uint64_t entry_idx, bool alloc) { static uint64_t* amd64_mm_next_table (uint64_t* table, uint64_t entry_idx, bool alloc) {
uint64_t entry = table[entry_idx]; uint64_t entry = table[entry_idx];
physaddr_t paddr; physaddr_t paddr;
struct limine_hhdm_response* hhdm = limine_hhdm_request.response; struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
if (entry & AMD64_PG_PRESENT) if (entry & AMD64_PG_PRESENT) {
if (entry & AMD64_PG_HUGE)
return NULL;
paddr = entry & ~0xFFFULL; paddr = entry & ~0xFFFULL;
else { } else {
if (!alloc) if (!alloc)
return NULL; return NULL;
@@ -68,6 +87,15 @@ static uint64_t* amd64_mm_next_table (uint64_t* table, uint64_t entry_idx, bool
return (uint64_t*)((uintptr_t)hhdm->offset + (uintptr_t)paddr); return (uint64_t*)((uintptr_t)hhdm->offset + (uintptr_t)paddr);
} }
static bool amd64_mm_is_table_empty (uint64_t* table) {
for (size_t i = 0; i < 512; i++) {
if (table[i] & AMD64_PG_PRESENT)
return false;
}
return true;
}
/* Convert generic memory management subsystem flags into AMD64-specific flags */
static uint64_t amd64_mm_resolve_flags (uint32_t generic) { static uint64_t amd64_mm_resolve_flags (uint32_t generic) {
uint64_t flags = 0; uint64_t flags = 0;
@@ -78,19 +106,15 @@ static uint64_t amd64_mm_resolve_flags (uint32_t generic) {
return flags; return flags;
} }
/* Reload the current CR3 value ON A LOCAL CPU */
static void amd64_reload_cr3 (void) { static void amd64_reload_cr3 (void) {
uint64_t cr3; uint64_t cr3;
__asm__ volatile ("movq %%cr3, %0; movq %0, %%cr3" : "=r"(cr3)::"memory"); __asm__ volatile ("movq %%cr3, %0; movq %0, %%cr3" : "=r"(cr3)::"memory");
} }
/* Map physical address to virtual address with flags. TLB needs to be flushed afterwards. */
void mm_map_page (struct pd* pd, uintptr_t paddr, uintptr_t vaddr, uint32_t flags) { void mm_map_page (struct pd* pd, uintptr_t paddr, uintptr_t vaddr, uint32_t flags) {
spin_lock (&mm_lock);
struct limine_hhdm_response* hhdm = limine_hhdm_request.response; struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
bool do_reload = false;
if (flags & MM_PD_LOCK)
spin_lock (&pd->lock);
uint64_t amd64_flags = amd64_mm_resolve_flags (flags); uint64_t amd64_flags = amd64_mm_resolve_flags (flags);
@@ -99,82 +123,77 @@ void mm_map_page (struct pd* pd, uintptr_t paddr, uintptr_t vaddr, uint32_t flag
uint64_t* pml3 = amd64_mm_next_table (pml4, pg_index.pml4, true); uint64_t* pml3 = amd64_mm_next_table (pml4, pg_index.pml4, true);
if (pml3 == NULL) if (pml3 == NULL)
goto done; return;
uint64_t* pml2 = amd64_mm_next_table (pml3, pg_index.pml3, true); uint64_t* pml2 = amd64_mm_next_table (pml3, pg_index.pml3, true);
if (pml2 == NULL) if (pml2 == NULL)
goto done; return;
uint64_t* pml1 = amd64_mm_next_table (pml2, pg_index.pml2, true); uint64_t* pml1 = amd64_mm_next_table (pml2, pg_index.pml2, true);
if (pml1 == NULL) if (pml1 == NULL)
goto done; return;
uint64_t* pte = &pml1[pg_index.pml1]; uint64_t* pte = &pml1[pg_index.pml1];
*pte = ((paddr & ~0xFFFULL) | (amd64_flags & 0x7ULL)); *pte = ((paddr & ~0xFFFULL) | (amd64_flags & 0x7ULL));
do_reload = true;
done:
if (do_reload && (flags & MM_PD_RELOAD))
amd64_reload_cr3 ();
if (flags & MM_PD_LOCK)
spin_unlock (&pd->lock);
spin_unlock (&mm_lock);
} }
/* Map a page into kernel page directory */
void mm_map_kernel_page (uintptr_t paddr, uintptr_t vaddr, uint32_t flags) { void mm_map_kernel_page (uintptr_t paddr, uintptr_t vaddr, uint32_t flags) {
mm_map_page (&kernel_pd, paddr, vaddr, flags); mm_map_page (&kernel_pd, paddr, vaddr, flags);
amd64_reload_cr3 ();
} }
void mm_unmap_page (struct pd* pd, uintptr_t vaddr, uint32_t flags) { /* Unmap a virtual address. TLB needs to be flushed afterwards */
spin_lock (&mm_lock); void mm_unmap_page (struct pd* pd, uintptr_t vaddr) {
struct limine_hhdm_response* hhdm = limine_hhdm_request.response; struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
bool do_reload = false;
if (flags & MM_PD_LOCK)
spin_lock (&pd->lock);
uint64_t* pml4 = (uint64_t*)(pd->cr3_paddr + (uintptr_t)hhdm->offset); uint64_t* pml4 = (uint64_t*)(pd->cr3_paddr + (uintptr_t)hhdm->offset);
struct pg_index pg_index = amd64_mm_page_index (vaddr); struct pg_index pg_index = amd64_mm_page_index (vaddr);
uint64_t* pml3 = amd64_mm_next_table (pml4, pg_index.pml4, false); uint64_t* pml3 = amd64_mm_next_table (pml4, pg_index.pml4, false);
if (pml3 == NULL) if (pml3 == NULL)
goto done; return;
uint64_t* pml2 = amd64_mm_next_table (pml3, pg_index.pml3, false); uint64_t* pml2 = amd64_mm_next_table (pml3, pg_index.pml3, false);
if (pml2 == NULL) if (pml2 == NULL)
goto done; return;
uint64_t* pml1 = amd64_mm_next_table (pml2, pg_index.pml2, false); uint64_t* pml1 = amd64_mm_next_table (pml2, pg_index.pml2, false);
if (pml1 == NULL) if (pml1 == NULL)
goto done; return;
uint64_t* pte = &pml1[pg_index.pml1]; uint64_t* pte = &pml1[pg_index.pml1];
*pte &= ~AMD64_PG_PRESENT; if ((*pte) & AMD64_PG_PRESENT)
do_reload = true; *pte = 0;
done: if (amd64_mm_is_table_empty (pml1)) {
if (do_reload && (flags & MM_PD_RELOAD)) uintptr_t pml1_phys = pml2[pg_index.pml2] & ~0xFFFULL;
amd64_reload_cr3 (); pmm_free (pml1_phys, 1);
pml2[pg_index.pml2] = 0;
if (flags & MM_PD_LOCK) if (amd64_mm_is_table_empty (pml2)) {
spin_unlock (&pd->lock); uintptr_t pml2_phys = pml3[pg_index.pml3] & ~0xFFFULL;
pmm_free (pml2_phys, 1);
pml3[pg_index.pml3] = 0;
spin_unlock (&mm_lock); if (amd64_mm_is_table_empty (pml3)) {
uintptr_t pml3_phys = pml4[pg_index.pml4] & ~0xFFFULL;
pmm_free (pml3_phys, 1);
pml4[pg_index.pml4] = 0;
}
}
}
} }
void mm_unmap_kernel_page (uintptr_t vaddr, uint32_t flags) { /* Unmap a page from kernel page directory */
mm_unmap_page (&kernel_pd, vaddr, flags); void mm_unmap_kernel_page (uintptr_t vaddr) {
mm_unmap_page (&kernel_pd, vaddr);
amd64_reload_cr3 ();
} }
void mm_lock_kernel (void) { spin_lock (&kernel_pd.lock); } /* Allocate a userspace-ready page directory */
void mm_unlock_kernel (void) { spin_unlock (&kernel_pd.lock); }
uintptr_t mm_alloc_user_pd_phys (void) { uintptr_t mm_alloc_user_pd_phys (void) {
struct limine_hhdm_response* hhdm = limine_hhdm_request.response; struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
@@ -192,27 +211,111 @@ uintptr_t mm_alloc_user_pd_phys (void) {
return cr3; return cr3;
} }
void mm_reload (void) { bool mm_validate (struct pd* pd, uintptr_t vaddr) {
spin_lock (&mm_lock); struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
bool ret = false;
struct limine_mp_response* mp = limine_mp_request.response; uint64_t* pml4 = (uint64_t*)(pd->cr3_paddr + (uintptr_t)hhdm->offset);
struct pg_index pg_index = amd64_mm_page_index (vaddr);
for (size_t i = 0; i < mp->cpu_count; i++) { uint64_t* pml3 = amd64_mm_next_table (pml4, pg_index.pml4, false);
amd64_lapic_ipi (mp->cpus[i]->lapic_id, TLB_SHOOTDOWN); if (pml3 == NULL)
goto done;
uint64_t* pml2 = amd64_mm_next_table (pml3, pg_index.pml3, false);
if (pml2 == NULL)
goto done;
uint64_t* pml1 = amd64_mm_next_table (pml2, pg_index.pml2, false);
if (pml1 == NULL)
goto done;
uint64_t pte = pml1[pg_index.pml1];
ret = (pte & AMD64_PG_PRESENT) != 0;
done:
return ret;
}
bool mm_validate_buffer (struct pd* pd, uintptr_t vaddr, size_t size) {
bool ok = true;
for (size_t i = 0; i < size; i++) {
ok = mm_validate (pd, vaddr + i);
if (!ok)
goto done;
} }
spin_unlock (&mm_lock); done:
return ok;
} }
static void amd64_tlb_shootdown_irq (void* arg, void* regs) { uintptr_t mm_p2v (struct pd* pd, uintptr_t paddr) {
(void)arg, (void)regs; struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
uintptr_t ret = 0;
amd64_reload_cr3 (); uint64_t* pml4 = (uint64_t*)(pd->cr3_paddr + (uintptr_t)hhdm->offset);
DEBUG ("cpu %u TLB shootdown\n", thiscpu->id);
for (size_t i4 = 0; i4 < 512; i4++) {
if (!(pml4[i4] & AMD64_PG_PRESENT))
continue;
uint64_t* pml3 = (uint64_t*)((uintptr_t)hhdm->offset + (pml4[i4] & ~0xFFFULL));
for (size_t i3 = 0; i3 < 512; i3++) {
if (!(pml3[i3] & AMD64_PG_PRESENT))
continue;
uint64_t* pml2 = (uint64_t*)((uintptr_t)hhdm->offset + (pml3[i3] & ~0xFFFULL));
for (size_t i2 = 0; i2 < 512; i2++) {
if (!(pml2[i2] & AMD64_PG_PRESENT))
continue;
uint64_t* pml1 = (uint64_t*)((uintptr_t)hhdm->offset + (pml2[i2] & ~0xFFFULL));
for (size_t i1 = 0; i1 < 512; i1++) {
if ((pml1[i1] & AMD64_PG_PRESENT) && ((pml1[i1] & ~0xFFFULL) == (paddr & ~0xFFFULL))) {
struct pg_index idx = {i4, i3, i2, i1};
ret = (((uint64_t)idx.pml4 << 39) | ((uint64_t)idx.pml3 << 30) |
((uint64_t)idx.pml2 << 21) | ((uint64_t)idx.pml1 << 12) | (paddr & 0xFFFULL));
goto done;
}
}
}
}
}
done:
return ret;
} }
void mm_init2 (void) { uintptr_t mm_v2p (struct pd* pd, uintptr_t vaddr) {
irq_attach (&amd64_tlb_shootdown_irq, NULL, TLB_SHOOTDOWN, IRQ_INTERRUPT_SAFE); struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
uintptr_t ret = 0;
uint64_t* pml4 = (uint64_t*)(pd->cr3_paddr + (uintptr_t)hhdm->offset);
struct pg_index pg_index = amd64_mm_page_index (vaddr);
uint64_t* pml3 = amd64_mm_next_table (pml4, pg_index.pml4, false);
if (pml3 == NULL)
goto done;
uint64_t* pml2 = amd64_mm_next_table (pml3, pg_index.pml3, false);
if (pml2 == NULL)
goto done;
uint64_t* pml1 = amd64_mm_next_table (pml2, pg_index.pml2, false);
if (pml1 == NULL)
goto done;
uint64_t pte = pml1[pg_index.pml1];
if (!(pte & AMD64_PG_PRESENT))
goto done;
ret = ((pte & ~0xFFFULL) | (vaddr & 0xFFFULL));
done:
return ret;
} }
/* Initialize essentials for the AMD64 memory management subsystem */
void mm_init (void) { kernel_pd.cr3_paddr = amd64_current_cr3 (); } void mm_init (void) { kernel_pd.cr3_paddr = amd64_current_cr3 (); }

View File

@@ -7,11 +7,9 @@
#define PAGE_SIZE 4096 #define PAGE_SIZE 4096
struct pd { struct pd {
spin_lock_t lock;
uintptr_t cr3_paddr; uintptr_t cr3_paddr;
}; };
void amd64_load_kernel_cr3 (void); void amd64_load_kernel_cr3 (void);
void mm_init2 (void);
#endif // _KERNEL_AMD64_MM_H #endif // _KERNEL_AMD64_MM_H

View File

@@ -28,15 +28,15 @@ Usage-Guide:
License-Text: License-Text:
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
Version 2, June 1991 Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc. Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed. of this license document, but changing it is not allowed.
Preamble Preamble
The licenses for most software are designed to take away your The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public freedom to share and change it. By contrast, the GNU General Public
@@ -85,8 +85,8 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and The precise terms and conditions for copying, distribution and
modification follow. modification follow.
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains 0. This License applies to any program or other work which contains
@@ -140,7 +140,7 @@ above, provided that you also meet all of these conditions:
License. (Exception: if the Program itself is interactive but License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on does not normally print such an announcement, your work based on
the Program is not required to print an announcement.) the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program, identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in and can be reasonably considered independent and separate works in
@@ -198,7 +198,7 @@ access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not distribution of the source code, even though third parties are not
compelled to copy the source along with the object code. compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program 4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is otherwise to copy, modify, sublicense or distribute the Program is
@@ -255,7 +255,7 @@ impose that choice.
This section is intended to make thoroughly clear what is believed to This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License. be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in 8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License original copyright holder who places the Program under this License
@@ -285,7 +285,7 @@ make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally. of promoting the sharing and reuse of software generally.
NO WARRANTY NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
@@ -307,9 +307,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES. POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it possible use to the public, the best way to achieve this is to make it

View File

@@ -1,12 +1,14 @@
#include <amd64/msr.h> #include <amd64/msr.h>
#include <libk/std.h> #include <libk/std.h>
/// Read a model-specific register
uint64_t amd64_rdmsr (uint32_t msr) { uint64_t amd64_rdmsr (uint32_t msr) {
uint32_t low, high; uint32_t low, high;
__asm__ volatile ("rdmsr" : "=a"(low), "=d"(high) : "c"(msr)); __asm__ volatile ("rdmsr" : "=a"(low), "=d"(high) : "c"(msr));
return ((uint64_t)high << 32 | (uint64_t)low); return ((uint64_t)high << 32 | (uint64_t)low);
} }
/// Write a model-specific register
void amd64_wrmsr (uint32_t msr, uint64_t value) { void amd64_wrmsr (uint32_t msr, uint64_t value) {
uint32_t low = (uint32_t)(value & 0xFFFFFFFF); uint32_t low = (uint32_t)(value & 0xFFFFFFFF);
uint32_t high = (uint32_t)(value >> 32); uint32_t high = (uint32_t)(value >> 32);

View File

@@ -3,9 +3,6 @@
#include <libk/std.h> #include <libk/std.h>
#define MSR_FS_BASE 0xC0000100
#define MSR_GS_BASE 0xC0000101
uint64_t amd64_rdmsr (uint32_t msr); uint64_t amd64_rdmsr (uint32_t msr);
void amd64_wrmsr (uint32_t msr, uint64_t value); void amd64_wrmsr (uint32_t msr, uint64_t value);

138
kernel/amd64/proc.c Normal file
View File

@@ -0,0 +1,138 @@
#include <amd64/gdt.h>
#include <amd64/proc.h>
#include <aux/elf.h>
#include <libk/align.h>
#include <libk/list.h>
#include <libk/rbtree.h>
#include <libk/std.h>
#include <libk/string.h>
#include <limine/requests.h>
#include <mm/liballoc.h>
#include <mm/pmm.h>
#include <proc/mutex.h>
#include <proc/proc.h>
#include <proc/procgroup.h>
#include <proc/resource.h>
#include <sync/spin_lock.h>
#include <sys/debug.h>
#include <sys/proc.h>
static atomic_int pids = 0;
struct proc* proc_from_elf (uint8_t* elf_contents) {
struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
struct proc* proc = malloc (sizeof (*proc));
if (proc == NULL)
return NULL;
memset (proc, 0, sizeof (*proc));
proc->lock = SPIN_LOCK_INIT;
atomic_store (&proc->state, PROC_READY);
proc->pid = atomic_fetch_add (&pids, 1);
proc->procgroup = procgroup_create ();
if (proc->procgroup == NULL) {
free (proc);
return NULL;
}
procgroup_attach (proc->procgroup, proc);
uintptr_t kstack_paddr = pmm_alloc (KSTACK_SIZE / PAGE_SIZE);
proc->pdata.kernel_stack = kstack_paddr + (uintptr_t)hhdm->offset + KSTACK_SIZE;
procgroup_map (proc->procgroup, PROC_USTACK_TOP - USTACK_SIZE, USTACK_SIZE / PAGE_SIZE,
MM_PG_USER | MM_PG_PRESENT | MM_PG_RW, NULL);
proc->flags |= PROC_USTK_PREALLOC;
struct elf_aux aux = proc_load_segments (proc, elf_contents);
proc->pdata.regs.ss = GDT_UDATA | 0x03;
proc->pdata.regs.rsp = (uint64_t)PROC_USTACK_TOP;
proc->pdata.regs.rflags = 0x202;
proc->pdata.regs.cs = GDT_UCODE | 0x03;
proc->pdata.regs.rip = aux.entry;
return proc;
}
struct proc* proc_clone (struct proc* proto, uintptr_t vstack_top, uintptr_t entry,
uintptr_t argument_ptr) {
struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
spin_lock_ctx_t ctxprt;
struct proc* proc = malloc (sizeof (*proc));
if (proc == NULL)
return NULL;
memset (proc, 0, sizeof (*proc));
proc->lock = SPIN_LOCK_INIT;
atomic_store (&proc->state, PROC_READY);
proc->pid = atomic_fetch_add (&pids, 1);
spin_lock (&proto->lock, &ctxprt);
proc->procgroup = proto->procgroup;
procgroup_attach (proc->procgroup, proc);
spin_unlock (&proto->lock, &ctxprt);
uintptr_t kstack_paddr = pmm_alloc (KSTACK_SIZE / PAGE_SIZE);
proc->pdata.kernel_stack = kstack_paddr + (uintptr_t)hhdm->offset + KSTACK_SIZE;
proc->pdata.regs.ss = GDT_UDATA | 0x03;
proc->pdata.regs.rsp = (uint64_t)vstack_top;
proc->pdata.regs.rflags = 0x202;
proc->pdata.regs.cs = GDT_UCODE | 0x03;
proc->pdata.regs.rip = (uint64_t)entry;
proc->uvaddr_argument = argument_ptr;
proc_init_tls (proc);
return proc;
}
void proc_cleanup (struct proc* proc) {
proc_sqs_cleanup (proc);
proc_mutexes_cleanup (proc);
pmm_free (proc->pdata.kernel_stack, KSTACK_SIZE / PAGE_SIZE);
procgroup_unmap (proc->procgroup, proc->pdata.tls_vaddr, proc->procgroup->tls.tls_tmpl_pages);
procgroup_detach (proc->procgroup, proc);
/* clean the process */
free (proc);
}
void proc_init_tls (struct proc* proc) {
struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
if (proc->procgroup->tls.tls_tmpl == NULL)
return;
size_t tls_size = proc->procgroup->tls.tls_tmpl_size;
size_t pages = proc->procgroup->tls.tls_tmpl_pages;
uintptr_t tls_paddr;
uint32_t flags = MM_PG_USER | MM_PG_PRESENT | MM_PG_RW;
uintptr_t tls_vaddr = procgroup_map (proc->procgroup, 0, pages, flags, &tls_paddr);
uintptr_t k_tls_addr = (uintptr_t)hhdm->offset + tls_paddr;
memset ((void*)k_tls_addr, 0, pages * PAGE_SIZE);
memcpy ((void*)k_tls_addr, (void*)proc->procgroup->tls.tls_tmpl, tls_size);
uintptr_t ktcb = k_tls_addr + tls_size;
uintptr_t utcb = tls_vaddr + tls_size;
*(uintptr_t*)ktcb = utcb;
proc->pdata.fs_base = utcb;
proc->pdata.tls_vaddr = tls_vaddr;
}

View File

@@ -4,14 +4,19 @@
#include <amd64/intr.h> #include <amd64/intr.h>
#include <libk/std.h> #include <libk/std.h>
/* Top of userspace process' stack */
#define PROC_USTACK_TOP 0x00007FFFFFFFF000ULL #define PROC_USTACK_TOP 0x00007FFFFFFFF000ULL
#define USTACK_SIZE (256 * PAGE_SIZE) /* Size of userspace process' stack */
#define USTACK_SIZE (256 * PAGE_SIZE)
/* proc_map () base address */
#define PROC_MAP_BASE 0x0000700000000000
/* Platform-dependent process data */
struct proc_platformdata { struct proc_platformdata {
struct saved_regs regs; struct saved_regs regs;
uintptr_t syscall_stack; uintptr_t kernel_stack;
uintptr_t user_stack; uint64_t fs_base;
uint64_t fsbase; uintptr_t tls_vaddr;
}; };
#endif // _KERNEL_AMD64_PROC_H #endif // _KERNEL_AMD64_PROC_H

13
kernel/amd64/procgroup.h Normal file
View File

@@ -0,0 +1,13 @@
#ifndef _KERNEL_AMD64_PROCGRPUP_H
#define _KERNEL_AMD64_PROCGRPUP_H
#include <libk/std.h>
struct procgroup_tls {
uint8_t* tls_tmpl;
size_t tls_tmpl_size;
size_t tls_tmpl_total_size;
size_t tls_tmpl_pages;
};
#endif // _KERNEL_AMD64_PROCGRPUP_H

55
kernel/amd64/regsasm.h Normal file
View File

@@ -0,0 +1,55 @@
#ifndef _KERNEL_AMD64_REGSASM_H
#define _KERNEL_AMD64_REGSASM_H
#define push_regs \
pushq % rax; \
pushq % rcx; \
pushq % rdx; \
pushq % rsi; \
pushq % rdi; \
pushq % rbp; \
pushq % rbx; \
pushq % r8; \
pushq % r9; \
pushq % r10; \
pushq % r11; \
pushq % r12; \
pushq % r13; \
pushq % r14; \
pushq % r15;
#define pop_regs \
popq % r15; \
popq % r14; \
popq % r13; \
popq % r12; \
popq % r11; \
popq % r10; \
popq % r9; \
popq % r8; \
popq % rbx; \
popq % rbp; \
popq % rdi; \
popq % rsi; \
popq % rdx; \
popq % rcx; \
popq % rax;
#define pop_regs_skip_rax \
popq % r15; \
popq % r14; \
popq % r13; \
popq % r12; \
popq % r11; \
popq % r10; \
popq % r9; \
popq % r8; \
popq % rbx; \
popq % rbp; \
popq % rdi; \
popq % rsi; \
popq % rdx; \
popq % rcx; \
addq $8, % rsp
#endif // _KERNEL_AMD64_REGSASM_H

View File

@@ -1,24 +1,9 @@
#define pop_regs \ #include <amd64/regsasm.h>
popq %r15; \
popq %r14; \
popq %r13; \
popq %r12; \
popq %r11; \
popq %r10; \
popq %r9; \
popq %r8; \
popq %rbx; \
popq %rbp; \
popq %rdi; \
popq %rsi; \
popq %rdx; \
popq %rcx; \
popq %rax;
.global amd64_do_sched .global amd64_do_sched
amd64_do_sched: amd64_do_sched:
movq %rsi, %cr3 movq %rsi, %cr3
movq %rdi, %rsp movq %rdi, %rsp
pop_regs pop_regs
add $16, %rsp addq $16, %rsp
iretq iretq

View File

@@ -1,6 +1,7 @@
#ifndef _KERNEL_AMD64_SCHED_H #ifndef _KERNEL_AMD64_SCHED_H
#define _KERNEL_AMD64_SCHED_H #define _KERNEL_AMD64_SCHED_H
/// Perform process context switch
void amd64_do_sched (void* regs, void* cr3); void amd64_do_sched (void* regs, void* cr3);
#endif // _KERNEL_AMD64_SCHED_H #endif // _KERNEL_AMD64_SCHED_H

View File

@@ -1,4 +1,23 @@
#include <amd64/msr-index.h>
#include <amd64/msr.h>
#include <amd64/sched.h> #include <amd64/sched.h>
#include <libk/std.h>
#include <proc/proc.h>
#include <sync/spin_lock.h>
#include <sys/mm.h> #include <sys/mm.h>
#include <sys/smp.h>
void do_sched (void* regs, struct pd* pd) { amd64_do_sched (regs, (void*)pd->cr3_paddr); } void do_sched (struct proc* proc, spin_lock_t* cpu_lock, spin_lock_ctx_t* ctxcpu) {
spin_lock_ctx_t ctxpr;
spin_lock (&proc->lock, &ctxpr);
thiscpu->tss.rsp0 = proc->pdata.kernel_stack;
thiscpu->syscall_kernel_stack = proc->pdata.kernel_stack;
amd64_wrmsr (MSR_FS_BASE, proc->pdata.fs_base);
spin_unlock (&proc->lock, &ctxpr);
spin_unlock (cpu_lock, ctxcpu);
amd64_do_sched ((void*)&proc->pdata.regs, (void*)proc->procgroup->pd.cr3_paddr);
}

View File

@@ -1,5 +1,6 @@
#include <amd64/apic.h> #include <amd64/apic.h>
#include <amd64/init.h> #include <amd64/init.h>
#include <amd64/intr_defs.h>
#include <amd64/mm.h> #include <amd64/mm.h>
#include <amd64/msr-index.h> #include <amd64/msr-index.h>
#include <amd64/msr.h> #include <amd64/msr.h>
@@ -7,67 +8,107 @@
#include <libk/string.h> #include <libk/string.h>
#include <limine/requests.h> #include <limine/requests.h>
#include <mm/liballoc.h> #include <mm/liballoc.h>
#include <proc/proc.h>
#include <sync/spin_lock.h> #include <sync/spin_lock.h>
#include <sys/debug.h> #include <sys/debug.h>
#include <sys/sched.h>
#include <sys/smp.h> #include <sys/smp.h>
#include <sys/syscall.h>
static uint32_t cpu_counter = 0; /// Cpu ID counter
static spin_lock_t cpu_counter_lock = SPIN_LOCK_INIT; static atomic_uint cpu_counter = 0;
/// The CPUs
static struct cpu cpus[CPUS_MAX]; static struct cpu cpus[CPUS_MAX];
struct cpu* cpu_make (void) { static atomic_int cpu_init_count;
spin_lock (&cpu_counter_lock);
int id = cpu_counter++; /// Allocate a CPU structure
spin_unlock (&cpu_counter_lock); struct cpu* cpu_make (uint64_t lapic_id) {
int id = atomic_fetch_add (&cpu_counter, 1);
struct cpu* cpu = &cpus[id]; struct cpu* cpu = &cpus[id];
memset (cpu, 0, sizeof (*cpu)); memset (cpu, 0, sizeof (*cpu));
cpu->lock = SPIN_LOCK_INIT; cpu->lock = SPIN_LOCK_INIT;
cpu->id = id; cpu->id = id;
cpu->lapic_id = lapic_id;
return cpu_get (id); amd64_wrmsr (MSR_GS_BASE, (uint64_t)cpu);
return cpu;
} }
struct cpu* cpu_get (uint32_t id) { struct cpu* cpu_get (void) {
if (id >= CPUS_MAX) struct cpu* ptr = (struct cpu*)amd64_rdmsr (MSR_GS_BASE);
return NULL; return ptr;
return &cpus[id];
} }
uint32_t cpu_id (void) { return (uint32_t)amd64_rdmsr (MSR_GS_BASE); } void cpu_request_sched (struct cpu* cpu) {
if (cpu == thiscpu) {
proc_sched ();
return;
}
void cpu_assign (uint32_t id) { amd64_wrmsr (MSR_GS_BASE, (uint64_t)id); } amd64_lapic_ipi (cpu->lapic_id, CPU_REQUEST_SCHED);
}
struct cpu* cpu_find_lightest (void) {
struct cpu* cpu = &cpus[0];
int load = atomic_load (&cpu->proc_run_q_count);
for (unsigned int i = 1; i < cpu_counter; i++) {
struct cpu* new_cpu = &cpus[i];
int new_load = atomic_load (&new_cpu->proc_run_q_count);
if (new_load < load) {
load = new_load;
cpu = new_cpu;
}
}
return cpu;
}
/// Bootstrap code for non-BSP CPUs
static void amd64_smp_bootstrap (struct limine_mp_info* mp_info) { static void amd64_smp_bootstrap (struct limine_mp_info* mp_info) {
amd64_load_kernel_cr3 (); amd64_load_kernel_cr3 ();
struct cpu* cpu = cpu_make (); struct cpu* cpu = cpu_make (mp_info->lapic_id);
cpu_assign (cpu->id);
amd64_init (cpu, true); /* gdt + idt */ amd64_init (cpu, true); /* gdt + idt */
syscall_init ();
thiscpu->lapic_ticks = amd64_lapic_init (2500); amd64_lapic_init (1000);
amd64_lapic_tick (thiscpu->lapic_ticks);
DEBUG ("CPU %u is online!\n", thiscpu->id); DEBUG ("CPU %u is online!\n", thiscpu->id);
__asm__ volatile ("sti"); atomic_fetch_sub (&cpu_init_count, 1);
for (;;) struct proc* spin_proc = proc_spawn_rd ("spin.exe");
; proc_register (spin_proc, thiscpu);
spin_lock_ctx_t ctxcpu;
spin_lock (&spin_proc->cpu->lock, &ctxcpu);
do_sched (spin_proc, &spin_proc->cpu->lock, &ctxcpu);
} }
/// Initialize SMP subsystem for AMD64. Start AP CPUs
void smp_init (void) { void smp_init (void) {
thiscpu->lapic_ticks = amd64_lapic_init (2500); amd64_lapic_init (1000);
struct limine_mp_response* mp = limine_mp_request.response; struct limine_mp_response* mp = limine_mp_request.response;
cpu_init_count = mp->cpu_count - 1; /* Don't include BSP */
for (size_t i = 0; i < mp->cpu_count; i++) { for (size_t i = 0; i < mp->cpu_count; i++) {
if (mp->cpus[i]->lapic_id != thiscpu->id) { if (mp->cpus[i]->lapic_id != thiscpu->lapic_id) {
DEBUG ("Trying CPU %u\n", mp->cpus[i]->lapic_id); DEBUG ("Trying CPU %u\n", mp->cpus[i]->lapic_id);
mp->cpus[i]->goto_address = &amd64_smp_bootstrap; mp->cpus[i]->goto_address = &amd64_smp_bootstrap;
} }
} }
while (atomic_load (&cpu_init_count) > 0)
;
DEBUG ("All CPUs are online\n");
} }

View File

@@ -2,41 +2,43 @@
#define _KERNEL_AMD64_SMP_H #define _KERNEL_AMD64_SMP_H
#include <amd64/gdt.h> #include <amd64/gdt.h>
#include <amd64/intr.h>
#include <amd64/tss.h> #include <amd64/tss.h>
#include <aux/compiler.h> #include <aux/compiler.h>
#include <libk/rbtree.h>
#include <libk/std.h> #include <libk/std.h>
#include <proc/proc.h> #include <proc/proc.h>
#include <sync/spin_lock.h>
#define CPUS_MAX 32 #define CPUS_MAX 32
struct cpu { struct cpu {
uint64_t lapic_ticks; /* for syscall instruction */
uint32_t id; uintptr_t syscall_user_stack;
uintptr_t syscall_kernel_stack;
struct {
uint64_t rflags;
atomic_int nesting;
} irq_ctx;
uint8_t user_stack[USTACK_SIZE] ALIGNED (16);
volatile uint8_t kernel_stack[KSTACK_SIZE] ALIGNED (16); volatile uint8_t kernel_stack[KSTACK_SIZE] ALIGNED (16);
volatile uint8_t except_stack[KSTACK_SIZE] ALIGNED (16); volatile uint8_t except_stack[KSTACK_SIZE] ALIGNED (16);
volatile uint8_t irq_stack[KSTACK_SIZE] ALIGNED (16); volatile uint8_t irq_stack[KSTACK_SIZE] ALIGNED (16);
volatile struct gdt_extended gdt ALIGNED (16); volatile struct gdt_extended gdt ALIGNED (16);
volatile struct tss tss; volatile struct tss tss;
uintptr_t lapic_mmio_base;
uint64_t lapic_ticks;
uint64_t lapic_id;
uint32_t id;
spin_lock_t lock; spin_lock_t lock;
struct proc* proc_run_q; struct list_node_link* proc_run_q;
struct proc* proc_current; struct proc* proc_current;
atomic_int proc_run_q_count;
}; };
struct cpu* cpu_make (void); struct cpu* cpu_make (uint64_t lapic_id);
struct cpu* cpu_get (uint32_t id); struct cpu* cpu_get (void);
void cpu_assign (uint32_t id); void cpu_request_sched (struct cpu* cpu);
uint32_t cpu_id (void); struct cpu* cpu_find_lightest (void);
#define thiscpu (cpu_get (cpu_id ())) #define thiscpu (cpu_get ())
#endif // _KERNEL_AMD64_SMP_H #endif // _KERNEL_AMD64_SMP_H

View File

@@ -1,3 +1,4 @@
.global amd64_spin .global amd64_spin
amd64_spin: amd64_spin:
hlt
jmp amd64_spin jmp amd64_spin

View File

@@ -1,3 +1,4 @@
#include <sys/spin_lock.h> #include <sys/spin_lock.h>
/// Relax the spinlock using AMD64 pause instruction
void spin_lock_relax (void) { __asm__ volatile ("pause"); } void spin_lock_relax (void) { __asm__ volatile ("pause"); }

View File

@@ -1,6 +1,5 @@
c += amd64/bootmain.c \ c += amd64/bootmain.c \
amd64/init.c \ amd64/init.c \
amd64/tss.c \
amd64/io.c \ amd64/io.c \
amd64/debug.c \ amd64/debug.c \
amd64/spin_lock.c \ amd64/spin_lock.c \
@@ -11,15 +10,17 @@ c += amd64/bootmain.c \
amd64/mm.c \ amd64/mm.c \
amd64/time.c \ amd64/time.c \
amd64/smp.c \ amd64/smp.c \
amd64/sched1.c amd64/sched1.c \
amd64/proc.c \
amd64/syscall.c
S += amd64/intr_stub.S \ S += amd64/intr_stub.S \
amd64/spin.S \ amd64/spin.S \
amd64/sched.S amd64/sched.S \
amd64/syscallentry.S
o += amd64/bootmain.o \ o += amd64/bootmain.o \
amd64/init.o \ amd64/init.o \
amd64/tss.o \
amd64/io.o \ amd64/io.o \
amd64/debug.o \ amd64/debug.o \
amd64/spin_lock.o \ amd64/spin_lock.o \
@@ -33,4 +34,7 @@ o += amd64/bootmain.o \
amd64/time.o \ amd64/time.o \
amd64/smp.o \ amd64/smp.o \
amd64/sched.o \ amd64/sched.o \
amd64/sched1.o amd64/sched1.o \
amd64/proc.o \
amd64/syscall.o \
amd64/syscallentry.o

46
kernel/amd64/syscall.c Normal file
View File

@@ -0,0 +1,46 @@
#include <amd64/gdt.h>
#include <amd64/intr.h>
#include <amd64/mm.h>
#include <amd64/msr-index.h>
#include <amd64/msr.h>
#include <libk/string.h>
#include <m/status.h>
#include <m/syscall_defs.h>
#include <proc/proc.h>
#include <sys/debug.h>
#include <sys/smp.h>
#include <syscall/syscall.h>
extern void amd64_syscall_entry (void);
uintptr_t amd64_syscall_dispatch (void* stack_ptr) {
spin_lock_ctx_t ctxcpu, ctxpr;
amd64_load_kernel_cr3 ();
struct saved_regs* regs = stack_ptr;
spin_lock (&thiscpu->lock, &ctxcpu);
struct proc* caller = thiscpu->proc_current;
spin_lock (&caller->lock, &ctxpr);
memcpy (&caller->pdata.regs, regs, sizeof (struct saved_regs));
spin_unlock (&caller->lock, &ctxpr);
spin_unlock (&thiscpu->lock, &ctxcpu);
int syscall_num = regs->rax;
syscall_handler_func_t func = syscall_find_handler (syscall_num);
if (func == NULL) {
return -ST_SYSCALL_NOT_FOUND;
}
return func (caller, regs, regs->rdi, regs->rsi, regs->rdx, regs->r10, regs->r8, regs->r9);
}
void syscall_init (void) {
amd64_wrmsr (MSR_STAR, ((uint64_t)GDT_KCODE << 32) | ((uint64_t)(GDT_KDATA | 0x03) << 48));
amd64_wrmsr (MSR_LSTAR, (uint64_t)&amd64_syscall_entry);
amd64_wrmsr (MSR_SYSCALL_MASK, (1ULL << 9));
amd64_wrmsr (MSR_EFER, amd64_rdmsr (MSR_EFER) | EFER_SCE);
}

View File

@@ -0,0 +1,49 @@
#include <amd64/regsasm.h>
.extern amd64_syscall_dispatch
.global amd64_syscall_entry
amd64_syscall_entry:
cli
movq %rsp, %gs:0
movq %gs:8, %rsp
pushq $0x1b
pushq %gs:0
pushq %r11
pushq $0x23
pushq %rcx
pushq $0
pushq $0
push_regs
movw $0x10, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
cld
movq %rsp, %rdi
movq %cr3, %rax; pushq %rax
movq %rsp, %rbp
subq $8, %rsp
andq $-16, %rsp
callq amd64_syscall_dispatch
movq %rbp, %rsp
popq %rbx; movq %rbx, %cr3
pop_regs_skip_rax
addq $56, %rsp
movq %gs:0, %rsp
sysretq

View File

@@ -2,4 +2,5 @@
#include <libk/std.h> #include <libk/std.h>
#include <sys/time.h> #include <sys/time.h>
/// Sleep for given amount of microseconds
void sleep_micro (size_t us) { amd64_hpet_sleep_micro (us); } void sleep_micro (size_t us) { amd64_hpet_sleep_micro (us); }

View File

@@ -1,7 +0,0 @@
#include <amd64/tss.h>
#include <aux/compiler.h>
#include <libk/std.h>
ALIGNED (16) static volatile struct tss tss;
volatile struct tss* amd64_get_tss (void) { return &tss; }

View File

@@ -4,6 +4,7 @@
#include <aux/compiler.h> #include <aux/compiler.h>
#include <libk/std.h> #include <libk/std.h>
/// 64-bit TSS structure: https://wiki.osdev.org/Task_State_Segment
struct tss { struct tss {
uint32_t resv0; uint32_t resv0;
uint64_t rsp0; uint64_t rsp0;
@@ -16,6 +17,4 @@ struct tss {
uint16_t iopb_off; uint16_t iopb_off;
} PACKED; } PACKED;
volatile struct tss* amd64_get_tss (void);
#endif // _KERNEL_AMD64_TSS_H #endif // _KERNEL_AMD64_TSS_H

View File

@@ -4,5 +4,6 @@
#define PACKED __attribute__ ((packed)) #define PACKED __attribute__ ((packed))
#define ALIGNED(N) __attribute__ ((aligned ((N)))) #define ALIGNED(N) __attribute__ ((aligned ((N))))
#define SECTION(name) __attribute__ ((section (name))) #define SECTION(name) __attribute__ ((section (name)))
#define UNUSED __attribute__ ((unused))
#endif // _KERNEL_AUX_COMPILER_H #endif // _KERNEL_AUX_COMPILER_H

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ cflags += -nostdinc \
-Wextra \ -Wextra \
-mcmodel=kernel -mcmodel=kernel
cflags += -isystem . -isystem c_headers/include cflags += -isystem . -isystem ../include
cflags += -DPRINTF_INCLUDE_CONFIG_H=1 \ cflags += -DPRINTF_INCLUDE_CONFIG_H=1 \
-D_ALLOC_SKIP_DEFINE -D_ALLOC_SKIP_DEFINE

View File

@@ -3,18 +3,20 @@
#include <libk/std.h> #include <libk/std.h>
#include <mm/liballoc.h> #include <mm/liballoc.h>
#include <sync/spin_lock.h> #include <sync/spin_lock.h>
#include <sys/debug.h>
#if defined(__x86_64__) #if defined(__x86_64__)
#include <amd64/apic.h> #include <amd64/apic.h>
#include <amd64/intr.h> #include <amd64/intr.h>
#endif #endif
/* TODO: figure out a generic way to work with IRQs */ struct irq* irq_table[0x100];
static struct irq* irqs = NULL; static spin_lock_t irqs_lock = SPIN_LOCK_INIT;
static spin_lock_t irqs_lock;
bool irq_attach (void (*func) (void*, void*), void* arg, uint32_t irq_num) {
spin_lock_ctx_t ctxiqa;
bool irq_attach (void (*func) (void*, void*), void* arg, uint32_t irq_num, uint32_t flags) {
struct irq* irq = malloc (sizeof (*irq)); struct irq* irq = malloc (sizeof (*irq));
if (irq == NULL) { if (irq == NULL) {
return false; return false;
@@ -23,44 +25,22 @@ bool irq_attach (void (*func) (void*, void*), void* arg, uint32_t irq_num, uint3
irq->func = func; irq->func = func;
irq->arg = arg; irq->arg = arg;
irq->irq_num = irq_num; irq->irq_num = irq_num;
irq->flags = flags;
spin_lock (&irqs_lock); spin_lock (&irqs_lock, &ctxiqa);
linklist_append (struct irq*, irqs, irq); irq_table[irq_num] = irq;
spin_unlock (&irqs_lock); spin_unlock (&irqs_lock, &ctxiqa);
#if defined(__x86_64__)
uint8_t resolution = amd64_resolve_irq (irq_num);
amd64_ioapic_route_irq (irq_num, resolution, 0, amd64_lapic_id ());
#endif
return true; return true;
} }
void irq_detach (void (*func) (void*, void*)) {
spin_lock (&irqs_lock);
struct irq *irq, *irq_tmp;
linklist_foreach (irqs, irq, irq_tmp) {
if ((uintptr_t)irq->func == (uintptr_t)func)
linklist_remove (struct irq*, irqs, irq);
}
spin_unlock (&irqs_lock);
}
struct irq* irq_find (uint32_t irq_num) { struct irq* irq_find (uint32_t irq_num) {
spin_lock (&irqs_lock); spin_lock_ctx_t ctxiqa;
struct irq *irq, *irq_tmp; spin_lock (&irqs_lock, &ctxiqa);
linklist_foreach (irqs, irq, irq_tmp) {
if (irq->irq_num == irq_num) {
spin_unlock (&irqs_lock);
return irq;
}
}
spin_unlock (&irqs_lock); struct irq* irq = irq_table[irq_num];
return NULL; spin_unlock (&irqs_lock, &ctxiqa);
return irq;
} }

View File

@@ -1,23 +1,20 @@
#ifndef _KERNEL_IRQ_IRQ_H #ifndef _KERNEL_IRQ_IRQ_H
#define _KERNEL_IRQ_IRQ_H #define _KERNEL_IRQ_IRQ_H
#include <libk/list.h>
#include <libk/std.h> #include <libk/std.h>
#define IRQ_INTERRUPT_SAFE (1 << 0)
typedef void (*irq_func_t) (void* arg, void* regs); typedef void (*irq_func_t) (void* arg, void* regs);
struct irq { struct irq {
struct irq* next; struct list_node_link irqs_link;
irq_func_t func; irq_func_t func;
void* arg; void* arg;
uint32_t irq_num; uint32_t irq_num;
uint32_t flags;
}; };
bool irq_attach (irq_func_t, void* arg, uint32_t irq_num, uint32_t flags); bool irq_attach (irq_func_t, void* arg, uint32_t irq_num);
void irq_detach (irq_func_t func);
struct irq* irq_find (uint32_t irq_num); struct irq* irq_find (uint32_t irq_num);
#endif // _KERNEL_IRQ_IRQ_H #endif // _KERNEL_IRQ_IRQ_H

15
kernel/libk/assert.h Normal file
View File

@@ -0,0 +1,15 @@
#ifndef _KERNEL_LIBK_ASSERT_H
#define _KERNEL_LIBK_ASSERT_H
#include <sys/spin.h>
#define assert(x) \
do { \
if (!(x)) { \
DEBUG ("%s ssertion failed\n", #x); \
spin (); \
__builtin_unreachable (); \
} \
} while (0)
#endif // _KERNEL_LIBK_ASSERT_H

View File

@@ -1,12 +1,19 @@
#ifndef _KERNEL_LIBK_LIST_H #ifndef _KERNEL_LIBK_LIST_H
#define _KERNEL_LIBK_LIST_H #define _KERNEL_LIBK_LIST_H
#define dlinklist_append(type, head, new) \ struct list_node_link {
struct list_node_link* next;
struct list_node_link* prev;
};
#define list_entry(ptr, type, member) ((type*)((char*)(ptr) - offsetof (type, member)))
#define list_append(head, new) \
do { \ do { \
if ((new) != NULL) { \ if ((new) != NULL) { \
(new)->next = NULL; \ (new)->next = NULL; \
if ((head) != NULL) { \ if ((head) != NULL) { \
type __tmp = (head); \ struct list_node_link* __tmp = (head); \
while (__tmp->next != NULL) { \ while (__tmp->next != NULL) { \
__tmp = __tmp->next; \ __tmp = __tmp->next; \
} \ } \
@@ -19,7 +26,7 @@
} \ } \
} while (0) } while (0)
#define dlinklist_prepend(head, new) \ #define list_prepend(head, new) \
do { \ do { \
if ((new) != NULL) { \ if ((new) != NULL) { \
(new)->prev = NULL; \ (new)->prev = NULL; \
@@ -31,7 +38,7 @@
} \ } \
} while (0) } while (0)
#define dlinklist_remove(head, ele) \ #define list_remove(head, ele) \
do { \ do { \
if ((ele) != NULL) { \ if ((ele) != NULL) { \
if ((ele)->prev != NULL) { \ if ((ele)->prev != NULL) { \
@@ -47,10 +54,10 @@
} \ } \
} while (0) } while (0)
#define dlinklist_find(type, head, out, propname, propvalue) \ #define list_find(head, out, propname, propvalue) \
do { \ do { \
(out) = NULL; \ (out) = NULL; \
type __tmp = (head); \ struct list_node_link* __tmp = (head); \
while (__tmp) { \ while (__tmp) { \
if (__tmp->propname == (propvalue)) { \ if (__tmp->propname == (propvalue)) { \
(out) = __tmp; \ (out) = __tmp; \
@@ -60,23 +67,23 @@
} \ } \
} while (0) } while (0)
#define dlinklist_foreach(head, var, tmp) \ #define list_foreach(head, var, tmp) \
for (var = (head), tmp = (var ? var->next : NULL); var != NULL; \ for (var = (head), tmp = (var ? var->next : NULL); var != NULL; \
var = tmp, tmp = (var ? var->next : NULL)) var = tmp, tmp = (var ? var->next : NULL))
#define dlinklist_foreach_index(head, var, tmp, idx) \ #define list_foreach_index(head, var, tmp, idx) \
for ((idx) = 0, var = (head), tmp = (var ? var->next : NULL); var != NULL; \ for ((idx) = 0, var = (head), tmp = (var ? var->next : NULL); var != NULL; \
var = tmp, tmp = (var ? var->next : NULL), (idx)++) var = tmp, tmp = (var ? var->next : NULL), (idx)++)
#define dlinklist_foreach_index_limit(head, var, tmp, idx, max) \ #define list_foreach_index_limit(head, var, tmp, idx, max) \
for ((idx) = 0, var = (head), tmp = (var ? var->next : NULL); var != NULL && (idx) < (max); \ for ((idx) = 0, var = (head), tmp = (var ? var->next : NULL); var != NULL && (idx) < (max); \
var = tmp, tmp = (var ? var->next : NULL), (idx)++) var = tmp, tmp = (var ? var->next : NULL), (idx)++)
#define dlinklist_back(type, head, out) \ #define list_back(head, out) \
do { \ do { \
(out) = NULL; \ (out) = NULL; \
if ((head) != NULL) { \ if ((head) != NULL) { \
type __tmp = (head); \ struct list_node_link* __tmp = (head); \
while (__tmp->next != NULL) { \ while (__tmp->next != NULL) { \
__tmp = __tmp->next; \ __tmp = __tmp->next; \
} \ } \
@@ -84,11 +91,11 @@
} \ } \
} while (0) } while (0)
#define dlinklist_front(type, head, out) \ #define list_front(head, out) \
do { \ do { \
(out) = NULL; \ (out) = NULL; \
if ((head) != NULL) { \ if ((head) != NULL) { \
type __tmp = (head); \ struct list_node_link* __tmp = (head); \
while (__tmp->prev != NULL) { \ while (__tmp->prev != NULL) { \
__tmp = __tmp->prev; \ __tmp = __tmp->prev; \
} \ } \
@@ -96,7 +103,7 @@
} \ } \
} while (0) } while (0)
#define dlinklist_insert_after(head, pos, new) \ #define list_insert_after(head, pos, new) \
do { \ do { \
if ((pos) != NULL && (new) != NULL) { \ if ((pos) != NULL && (new) != NULL) { \
(new)->prev = (pos); \ (new)->prev = (pos); \
@@ -112,7 +119,7 @@
} \ } \
} while (0) } while (0)
#define dlinklist_insert_before(head, pos, new) \ #define list_insert_before(head, pos, new) \
do { \ do { \
if ((pos) != NULL && (new) != NULL) { \ if ((pos) != NULL && (new) != NULL) { \
(new)->next = (pos); \ (new)->next = (pos); \
@@ -130,11 +137,11 @@
} \ } \
} while (0) } while (0)
#define dlinklist_index_of(type, head, ele, out_idx) \ #define list_index_of(head, ele, out_idx) \
do { \ do { \
(out_idx) = -1; \ (out_idx) = -1; \
int __idx = 0; \ int __idx = 0; \
type __tmp = (head); \ struct list_node_link* __tmp = (head); \
while (__tmp != NULL) { \ while (__tmp != NULL) { \
if (__tmp == (ele)) { \ if (__tmp == (ele)) { \
(out_idx) = __idx; \ (out_idx) = __idx; \
@@ -145,11 +152,11 @@
} \ } \
} while (0) } while (0)
#define dlinklist_index_of_prop(type, head, propname, propvalue, out_idx) \ #define list_index_of_prop(head, propname, propvalue, out_idx) \
do { \ do { \
(out_idx) = -1; \ (out_idx) = -1; \
int __idx = 0; \ int __idx = 0; \
type __tmp = (head); \ struct list_node_link* __tmp = (head); \
while (__tmp != NULL) { \ while (__tmp != NULL) { \
if (__tmp->propname == (propvalue)) { \ if (__tmp->propname == (propvalue)) { \
(out_idx) = __idx; \ (out_idx) = __idx; \
@@ -160,109 +167,4 @@
} \ } \
} while (0) } while (0)
#define linklist_index_of(type, head, ele, out_idx) \
do { \
(out_idx) = -1; \
int __idx = 0; \
type __tmp = (head); \
while (__tmp != NULL) { \
if (__tmp == (ele)) { \
(out_idx) = __idx; \
break; \
} \
__tmp = __tmp->next; \
__idx++; \
} \
} while (0)
#define linklist_index_of_prop(type, head, propname, propvalue, out_idx) \
do { \
(out_idx) = -1; \
int __idx = 0; \
type __tmp = (head); \
while (__tmp != NULL) { \
if (__tmp->propname == (propvalue)) { \
(out_idx) = __idx; \
break; \
} \
__tmp = __tmp->next; \
__idx++; \
} \
} while (0)
#define linklist_append(type, head, new) \
do { \
if ((new) != NULL) { \
if ((head) != NULL) { \
type __tmp; \
(new)->next = NULL; \
__tmp = (head); \
while (__tmp->next != NULL) { \
__tmp = __tmp->next; \
} \
__tmp->next = (new); \
} else { \
(new)->next = NULL; \
(head) = (new); \
} \
} \
} while (0)
#define linklist_remove(type, head, ele) \
do { \
if ((head) != NULL && (ele) != NULL) { \
type __cur = (head); \
type __prev = NULL; \
while (__cur != NULL && __cur != (ele)) { \
__prev = __cur; \
__cur = __cur->next; \
} \
if (__cur == (ele)) { \
if (__prev != NULL) { \
__prev->next = __cur->next; \
} else { \
(head) = __cur->next; \
} \
(ele)->next = NULL; \
} \
} \
} while (0)
#define linklist_find(type, head, out, propname, propvalue) \
do { \
(out) = NULL; \
type __tmp = (head); \
while (__tmp) { \
if (__tmp->propname == (propvalue)) { \
(out) = __tmp; \
break; \
} \
__tmp = __tmp->next; \
} \
} while (0)
#define linklist_foreach(head, var, tmp) \
for (var = (head), tmp = (var ? var->next : NULL); var != NULL; \
var = tmp, tmp = (var ? var->next : NULL))
#define linklist_foreach_index(head, var, tmp, idx) \
for ((idx) = 0, var = (head), tmp = (var ? var->next : NULL); var != NULL; \
var = tmp, tmp = (var ? var->next : NULL), (idx)++)
#define linklist_foreach_index_limit(head, var, tmp, idx, max) \
for ((idx) = 0, var = (head), tmp = (var ? var->next : NULL); var != NULL && (idx) < (max); \
var = tmp, tmp = (var ? var->next : NULL), (idx)++)
#define linklist_back(type, head, out) \
do { \
(out) = NULL; \
if ((head) != NULL) { \
type __tmp = (head); \
while (__tmp->next != NULL) { \
__tmp = __tmp->next; \
} \
(out) = __tmp; \
} \
} while (0)
#endif // _KERNEL_LIBK_LIST_H #endif // _KERNEL_LIBK_LIST_H

323
kernel/libk/rbtree.h Normal file
View File

@@ -0,0 +1,323 @@
#ifndef _KERNEL_LIBK_RBTREE_H
#define _KERNEL_LIBK_RBTREE_H
struct rb_node_link {
struct rb_node_link* left;
struct rb_node_link* right;
struct rb_node_link* parent;
int color;
};
#define RBTREE_RED 0
#define RBTREE_BLACK 1
#define rbtree_parent(x) ((x)->parent)
#define rbtree_left(x) ((x)->left)
#define rbtree_right(x) ((x)->right)
#define rbtree_color(x) ((x)->color)
#define rbtree_entry(node, type, member) ((type*)((char*)(node) - offsetof (type, member)))
#define rbtree_node_color(x) ((x) ? (x)->color : RBTREE_BLACK)
#define rbtree_rotate_left(root_ptr, x_node) \
do { \
struct rb_node_link* __x = (x_node); \
struct rb_node_link* __y = __x->right; \
__x->right = __y->left; \
if (__y->left) \
__y->left->parent = __x; \
__y->parent = __x->parent; \
if (!__x->parent) \
*(root_ptr) = __y; \
else if (__x == __x->parent->left) \
__x->parent->left = __y; \
else \
__x->parent->right = __y; \
__y->left = __x; \
__x->parent = __y; \
} while (0)
#define rbtree_rotate_right(root_ptr, y_node) \
do { \
struct rb_node_link* __y = (y_node); \
struct rb_node_link* __x = __y->left; \
__y->left = __x->right; \
if (__x->right) \
__x->right->parent = __y; \
__x->parent = __y->parent; \
if (!__y->parent) \
*(root_ptr) = __x; \
else if (__y == __y->parent->right) \
__y->parent->right = __x; \
else \
__y->parent->left = __x; \
__x->right = __y; \
__y->parent = __x; \
} while (0)
#define rbtree_insert_fixup(root_ptr, z_node) \
do { \
struct rb_node_link* __z = (z_node); \
while (__z->parent && __z->parent->color == RBTREE_RED) { \
if (__z->parent == __z->parent->parent->left) { \
struct rb_node_link* __y = __z->parent->parent->right; \
if (rbtree_node_color (__y) == RBTREE_RED) { \
__z->parent->color = RBTREE_BLACK; \
__y->color = RBTREE_BLACK; \
__z->parent->parent->color = RBTREE_RED; \
__z = __z->parent->parent; \
} else { \
if (__z == __z->parent->right) { \
__z = __z->parent; \
rbtree_rotate_left (root_ptr, __z); \
} \
__z->parent->color = RBTREE_BLACK; \
__z->parent->parent->color = RBTREE_RED; \
rbtree_rotate_right (root_ptr, __z->parent->parent); \
} \
} else { \
struct rb_node_link* __y = __z->parent->parent->left; \
if (rbtree_node_color (__y) == RBTREE_RED) { \
__z->parent->color = RBTREE_BLACK; \
__y->color = RBTREE_BLACK; \
__z->parent->parent->color = RBTREE_RED; \
__z = __z->parent->parent; \
} else { \
if (__z == __z->parent->left) { \
__z = __z->parent; \
rbtree_rotate_right (root_ptr, __z); \
} \
__z->parent->color = RBTREE_BLACK; \
__z->parent->parent->color = RBTREE_RED; \
rbtree_rotate_left (root_ptr, __z->parent->parent); \
} \
} \
} \
(*(root_ptr))->color = RBTREE_BLACK; \
} while (0)
#define rbtree_insert(type, root_ptr, node, member, keyfield) \
do { \
struct rb_node_link** __link = (root_ptr); \
struct rb_node_link* __parent = NULL; \
struct rb_node_link* __new = (node); \
type* __nobj = rbtree_entry (__new, type, member); \
while (*__link) { \
__parent = *__link; \
type* __xobj = rbtree_entry (*__link, type, member); \
if (__nobj->keyfield < __xobj->keyfield) \
__link = &((*__link)->left); \
else \
__link = &((*__link)->right); \
} \
__new->parent = __parent; \
__new->left = __new->right = NULL; \
__new->color = RBTREE_RED; \
*__link = __new; \
rbtree_insert_fixup (root_ptr, __new); \
} while (0)
#define rbtree_find(type, root_ptr, keyval, out, member, keyfield) \
do { \
(out) = NULL; \
struct rb_node_link* __cur = *(root_ptr); \
while (__cur) { \
type* __obj = rbtree_entry (__cur, type, member); \
if ((keyval) == __obj->keyfield) { \
(out) = rbtree_entry (__cur, type, member); \
break; \
} else if ((keyval) < __obj->keyfield) \
__cur = __cur->left; \
else \
__cur = __cur->right; \
} \
} while (0)
#define rbtree_min(node, out) \
do { \
(out) = NULL; \
struct rb_node_link* __n = (node); \
while (__n && __n->left) \
__n = __n->left; \
(out) = __n; \
} while (0)
#define rbtree_max(node, out) \
do { \
(out) = NULL; \
struct rb_node_link* __n = (node); \
while (__n && __n->right) \
__n = __n->right; \
(out) = __n; \
} while (0)
#define rbtree_first(root_ptr, out) rbtree_min (*(root_ptr), out)
#define rbtree_last(root_ptr, out) rbtree_max (*(root_ptr), out)
#define rbtree_transplant(root_ptr, u_node, v_node) \
do { \
struct rb_node_link* __u = (u_node); \
struct rb_node_link* __v = (v_node); \
if (!__u->parent) \
*(root_ptr) = __v; \
else if (__u == __u->parent->left) \
__u->parent->left = __v; \
else \
__u->parent->right = __v; \
if (__v) \
__v->parent = __u->parent; \
} while (0)
#define rbtree_delete_fixup(root_ptr, x_node, xparent_node) \
do { \
struct rb_node_link* __rdf_x = (x_node); \
struct rb_node_link* __rdf_xp = (xparent_node); \
while (__rdf_xp && (__rdf_x == NULL || __rdf_x->color == RBTREE_BLACK)) { \
if (__rdf_x == __rdf_xp->left) { \
struct rb_node_link* __w = __rdf_xp->right; \
if (rbtree_node_color (__w) == RBTREE_RED) { \
__w->color = RBTREE_BLACK; \
__rdf_xp->color = RBTREE_RED; \
rbtree_rotate_left (root_ptr, __rdf_xp); \
__w = __rdf_xp->right; \
} \
if (rbtree_node_color (__w->left) == RBTREE_BLACK && \
rbtree_node_color (__w->right) == RBTREE_BLACK) { \
if (__w) \
__w->color = RBTREE_RED; \
__rdf_x = __rdf_xp; \
__rdf_xp = __rdf_x->parent; \
} else { \
if (rbtree_node_color (__w->right) == RBTREE_BLACK) { \
if (__w->left) \
__w->left->color = RBTREE_BLACK; \
__w->color = RBTREE_RED; \
rbtree_rotate_right (root_ptr, __w); \
__w = __rdf_xp->right; \
} \
__w->color = __rdf_xp->color; \
__rdf_xp->color = RBTREE_BLACK; \
if (__w->right) \
__w->right->color = RBTREE_BLACK; \
rbtree_rotate_left (root_ptr, __rdf_xp); \
__rdf_x = *(root_ptr); \
break; \
} \
} else { \
struct rb_node_link* __w = __rdf_xp->left; \
if (rbtree_node_color (__w) == RBTREE_RED) { \
__w->color = RBTREE_BLACK; \
__rdf_xp->color = RBTREE_RED; \
rbtree_rotate_right (root_ptr, __rdf_xp); \
__w = __rdf_xp->left; \
} \
if (rbtree_node_color (__w->right) == RBTREE_BLACK && \
rbtree_node_color (__w->left) == RBTREE_BLACK) { \
if (__w) \
__w->color = RBTREE_RED; \
__rdf_x = __rdf_xp; \
__rdf_xp = __rdf_x->parent; \
} else { \
if (rbtree_node_color (__w->left) == RBTREE_BLACK) { \
if (__w->right) \
__w->right->color = RBTREE_BLACK; \
__w->color = RBTREE_RED; \
rbtree_rotate_left (root_ptr, __w); \
__w = __rdf_xp->left; \
} \
__w->color = __rdf_xp->color; \
__rdf_xp->color = RBTREE_BLACK; \
if (__w->left) \
__w->left->color = RBTREE_BLACK; \
rbtree_rotate_right (root_ptr, __rdf_xp); \
__rdf_x = *(root_ptr); \
break; \
} \
} \
} \
if (__rdf_x) \
__rdf_x->color = RBTREE_BLACK; \
} while (0)
#define rbtree_delete(root_ptr, z_node) \
do { \
struct rb_node_link* __rd_z = (z_node); \
struct rb_node_link* __rd_y = __rd_z; \
struct rb_node_link* __rd_x = NULL; \
struct rb_node_link* __rd_xp = NULL; \
int __rd_y_orig_color = __rd_y->color; \
if (!__rd_z->left) { \
__rd_x = __rd_z->right; \
__rd_xp = __rd_z->parent; \
rbtree_transplant (root_ptr, __rd_z, __rd_z->right); \
} else if (!__rd_z->right) { \
__rd_x = __rd_z->left; \
__rd_xp = __rd_z->parent; \
rbtree_transplant (root_ptr, __rd_z, __rd_z->left); \
} else { \
rbtree_min (__rd_z->right, __rd_y); \
__rd_y_orig_color = __rd_y->color; \
__rd_x = __rd_y->right; \
if (__rd_y->parent == __rd_z) { \
__rd_xp = __rd_y; \
if (__rd_x) \
__rd_x->parent = __rd_y; \
} else { \
__rd_xp = __rd_y->parent; \
rbtree_transplant (root_ptr, __rd_y, __rd_y->right); \
__rd_y->right = __rd_z->right; \
__rd_y->right->parent = __rd_y; \
} \
rbtree_transplant (root_ptr, __rd_z, __rd_y); \
__rd_y->left = __rd_z->left; \
__rd_y->left->parent = __rd_y; \
__rd_y->color = __rd_z->color; \
} \
if (__rd_y_orig_color == RBTREE_BLACK) \
rbtree_delete_fixup (root_ptr, __rd_x, __rd_xp); \
} while (0)
#define rbtree_next(node, out) \
do { \
(out) = NULL; \
if (node) { \
if ((node)->right) { \
struct rb_node_link* __n = (node)->right; \
while (__n->left) \
__n = __n->left; \
(out) = __n; \
} else { \
struct rb_node_link* __n = (node); \
struct rb_node_link* __p = (node)->parent; \
while (__p && __n == __p->right) { \
__n = __p; \
__p = __p->parent; \
} \
(out) = __p; \
} \
} \
} while (0)
#define rbtree_prev(node, out) \
do { \
(out) = NULL; \
if (node) { \
if ((node)->left) { \
struct rb_node_link* __n = (node)->left; \
while (__n->right) \
__n = __n->right; \
(out) = __n; \
} else { \
struct rb_node_link* __n = (node); \
struct rb_node_link* __p = (node)->parent; \
while (__p && __n == __p->left) { \
__n = __p; \
__p = __p->parent; \
} \
(out) = __p; \
} \
} \
} while (0)
#endif // _KERNEL_LIBK_RBTREE_H

View File

@@ -1,6 +1,8 @@
#ifndef _KERNEL_LIBK_STRING_H #ifndef _KERNEL_LIBK_STRING_H
#define _KERNEL_LIBK_STRING_H #define _KERNEL_LIBK_STRING_H
#include <libk/std.h>
size_t memset (void* dst, uint8_t b, size_t n); size_t memset (void* dst, uint8_t b, size_t n);
size_t memcpy (void* dst, const void* src, size_t n); size_t memcpy (void* dst, const void* src, size_t n);
void strncpy (char* dst, const char* src, size_t n); void strncpy (char* dst, const char* src, size_t n);

View File

@@ -20,3 +20,4 @@ DECL_REQ (memmap, MEMMAP);
DECL_REQ (rsdp, RSDP); DECL_REQ (rsdp, RSDP);
DECL_REQ (mp, MP); DECL_REQ (mp, MP);
DECL_REQ (module, MODULE); DECL_REQ (module, MODULE);
DECL_REQ (framebuffer, FRAMEBUFFER);

View File

@@ -10,5 +10,6 @@ EXTERN_REQ (memmap);
EXTERN_REQ (rsdp); EXTERN_REQ (rsdp);
EXTERN_REQ (mp); EXTERN_REQ (mp);
EXTERN_REQ (module); EXTERN_REQ (module);
EXTERN_REQ (framebuffer);
#endif // _KERNEL_LIMINE_REQUESTS_H #endif // _KERNEL_LIMINE_REQUESTS_H

View File

@@ -11,13 +11,13 @@
spin_lock_t _liballoc_lock = SPIN_LOCK_INIT; spin_lock_t _liballoc_lock = SPIN_LOCK_INIT;
int liballoc_lock (void) { int liballoc_lock (void* ctx) {
spin_lock (&_liballoc_lock); spin_lock (&_liballoc_lock, (spin_lock_ctx_t*)ctx);
return 0; return 0;
} }
int liballoc_unlock (void) { int liballoc_unlock (void* ctx) {
spin_unlock (&_liballoc_lock); spin_unlock (&_liballoc_lock, (spin_lock_ctx_t*)ctx);
return 0; return 0;
} }
@@ -45,7 +45,7 @@ int liballoc_free (void* ptr, int pages) {
/** Durand's Ridiculously Amazing Super Duper Memory functions. */ /** Durand's Ridiculously Amazing Super Duper Memory functions. */
//#define DEBUG // #define DEBUG
#define LIBALLOC_MAGIC 0xc001c0de #define LIBALLOC_MAGIC 0xc001c0de
#define MAXCOMPLETE 5 #define MAXCOMPLETE 5
@@ -66,7 +66,7 @@ static int l_pageCount = 16; //< Minimum number of pages to allocate.
// *********** HELPER FUNCTIONS ******************************* // *********** HELPER FUNCTIONS *******************************
/** Returns the exponent required to manage 'size' amount of memory. /** Returns the exponent required to manage 'size' amount of memory.
* *
* Returns n where 2^n <= size < 2^(n+1) * Returns n where 2^n <= size < 2^(n+1)
*/ */
@@ -243,8 +243,9 @@ void* malloc (size_t size) {
int index; int index;
void* ptr; void* ptr;
struct boundary_tag* tag = NULL; struct boundary_tag* tag = NULL;
spin_lock_ctx_t ctxliba;
liballoc_lock (); liballoc_lock (&ctxliba);
if (l_initialized == 0) { if (l_initialized == 0) {
for (index = 0; index < MAXEXP; index++) { for (index = 0; index < MAXEXP; index++) {
@@ -272,7 +273,7 @@ void* malloc (size_t size) {
// No page found. Make one. // No page found. Make one.
if (tag == NULL) { if (tag == NULL) {
if ((tag = allocate_new_tag (size)) == NULL) { if ((tag = allocate_new_tag (size)) == NULL) {
liballoc_unlock (); liballoc_unlock (&ctxliba);
return NULL; return NULL;
} }
@@ -305,23 +306,24 @@ void* malloc (size_t size) {
ptr = (void*)((uintptr_t)tag + sizeof (struct boundary_tag)); ptr = (void*)((uintptr_t)tag + sizeof (struct boundary_tag));
liballoc_unlock (); liballoc_unlock (&ctxliba);
return ptr; return ptr;
} }
void free (void* ptr) { void free (void* ptr) {
int index; int index;
struct boundary_tag* tag; struct boundary_tag* tag;
spin_lock_ctx_t ctxliba;
if (ptr == NULL) if (ptr == NULL)
return; return;
liballoc_lock (); liballoc_lock (&ctxliba);
tag = (struct boundary_tag*)((uintptr_t)ptr - sizeof (struct boundary_tag)); tag = (struct boundary_tag*)((uintptr_t)ptr - sizeof (struct boundary_tag));
if (tag->magic != LIBALLOC_MAGIC) { if (tag->magic != LIBALLOC_MAGIC) {
liballoc_unlock (); // release the lock liballoc_unlock (&ctxliba); // release the lock
return; return;
} }
@@ -354,7 +356,7 @@ void free (void* ptr) {
liballoc_free (tag, pages); liballoc_free (tag, pages);
liballoc_unlock (); liballoc_unlock (&ctxliba);
return; return;
} }
@@ -365,7 +367,7 @@ void free (void* ptr) {
insert_tag (tag, index); insert_tag (tag, index);
liballoc_unlock (); liballoc_unlock (&ctxliba);
} }
void* calloc (size_t nobj, size_t size) { void* calloc (size_t nobj, size_t size) {
@@ -385,6 +387,7 @@ void* realloc (void* p, size_t size) {
void* ptr; void* ptr;
struct boundary_tag* tag; struct boundary_tag* tag;
int real_size; int real_size;
spin_lock_ctx_t ctxliba;
if (size == 0) { if (size == 0) {
free (p); free (p);
@@ -394,11 +397,11 @@ void* realloc (void* p, size_t size) {
return malloc (size); return malloc (size);
if (&liballoc_lock != NULL) if (&liballoc_lock != NULL)
liballoc_lock (); // lockit liballoc_lock (&ctxliba); // lockit
tag = (struct boundary_tag*)((uintptr_t)p - sizeof (struct boundary_tag)); tag = (struct boundary_tag*)((uintptr_t)p - sizeof (struct boundary_tag));
real_size = tag->size; real_size = tag->size;
if (&liballoc_unlock != NULL) if (&liballoc_unlock != NULL)
liballoc_unlock (); liballoc_unlock (&ctxliba);
if ((size_t)real_size > size) if ((size_t)real_size > size)
real_size = size; real_size = size;

View File

@@ -42,12 +42,12 @@ struct boundary_tag {
/** This function is supposed to lock the memory data structures. It /** This function is supposed to lock the memory data structures. It
* could be as simple as disabling interrupts or acquiring a spinlock. * could be as simple as disabling interrupts or acquiring a spinlock.
* It's up to you to decide. * It's up to you to decide.
* *
* \return 0 if the lock was acquired successfully. Anything else is * \return 0 if the lock was acquired successfully. Anything else is
* failure. * failure.
*/ */
extern int liballoc_lock (); extern int liballoc_lock (void* ctx);
/** This function unlocks what was previously locked by the liballoc_lock /** This function unlocks what was previously locked by the liballoc_lock
* function. If it disabled interrupts, it enables interrupts. If it * function. If it disabled interrupts, it enables interrupts. If it
@@ -55,7 +55,7 @@ extern int liballoc_lock ();
* *
* \return 0 if the lock was successfully released. * \return 0 if the lock was successfully released.
*/ */
extern int liballoc_unlock (); extern int liballoc_unlock (void* ctx);
/** This is the hook into the local system which allocates pages. It /** This is the hook into the local system which allocates pages. It
* accepts an integer parameter which is the number of pages * accepts an integer parameter which is the number of pages

View File

@@ -38,8 +38,8 @@ void pmm_init (void) {
struct pmm_region* pmm_region = &pmm.regions[region]; struct pmm_region* pmm_region = &pmm.regions[region];
/* /*
* We need to calculate sizes for the pmm region and the bitmap. The bitmap MUSTN'T include it's * We need to calculate sizes for the pmm region and the bitmap. The bitmap MUSTN'T include
* own region within the bit range. * it's own region within the bit range.
* */ * */
size_t size = align_down (entry->length, PAGE_SIZE); size_t size = align_down (entry->length, PAGE_SIZE);
@@ -100,6 +100,8 @@ static size_t pmm_find_free_space (struct pmm_region* pmm_region, size_t nblks)
} }
physaddr_t pmm_alloc (size_t nblks) { physaddr_t pmm_alloc (size_t nblks) {
spin_lock_ctx_t ctxpmmr;
for (size_t region = 0; region < PMM_REGIONS_MAX; region++) { for (size_t region = 0; region < PMM_REGIONS_MAX; region++) {
struct pmm_region* pmm_region = &pmm.regions[region]; struct pmm_region* pmm_region = &pmm.regions[region];
@@ -107,7 +109,7 @@ physaddr_t pmm_alloc (size_t nblks) {
if (!(pmm_region->flags & PMM_REGION_ACTIVE)) if (!(pmm_region->flags & PMM_REGION_ACTIVE))
continue; continue;
spin_lock (&pmm_region->lock); spin_lock (&pmm_region->lock, &ctxpmmr);
/* Find starting bit of the free bit range */ /* Find starting bit of the free bit range */
size_t bit = pmm_find_free_space (pmm_region, nblks); size_t bit = pmm_find_free_space (pmm_region, nblks);
@@ -116,18 +118,19 @@ physaddr_t pmm_alloc (size_t nblks) {
if (bit != (size_t)-1) { if (bit != (size_t)-1) {
/* Mark it */ /* Mark it */
bm_set_region (&pmm_region->bm, bit, nblks); bm_set_region (&pmm_region->bm, bit, nblks);
spin_unlock (&pmm_region->lock); spin_unlock (&pmm_region->lock, &ctxpmmr);
return pmm_region->membase + bit * PAGE_SIZE; return pmm_region->membase + bit * PAGE_SIZE;
} }
spin_unlock (&pmm_region->lock); spin_unlock (&pmm_region->lock, &ctxpmmr);
} }
return PMM_ALLOC_ERR; return PMM_ALLOC_ERR;
} }
void pmm_free (physaddr_t p_addr, size_t nblks) { void pmm_free (physaddr_t p_addr, size_t nblks) {
spin_lock_ctx_t ctxpmmr;
/* Round down to nearest page boundary */ /* Round down to nearest page boundary */
physaddr_t aligned_p_addr = align_down (p_addr, PAGE_SIZE); physaddr_t aligned_p_addr = align_down (p_addr, PAGE_SIZE);
@@ -145,11 +148,11 @@ void pmm_free (physaddr_t p_addr, size_t nblks) {
size_t bit = div_align_up (addr, PAGE_SIZE); size_t bit = div_align_up (addr, PAGE_SIZE);
spin_lock (&pmm_region->lock); spin_lock (&pmm_region->lock, &ctxpmmr);
bm_clear_region (&pmm_region->bm, bit, nblks); bm_clear_region (&pmm_region->bm, bit, nblks);
spin_unlock (&pmm_region->lock); spin_unlock (&pmm_region->lock, &ctxpmmr);
break; break;
} }

10
kernel/proc/locks.txt Normal file
View File

@@ -0,0 +1,10 @@
Lock hierarchy for process scheduling:
1. proc_tree_lock
2. cpu->lock
3. procgroup->lock
4. proc->lock
5. sq->lock
1. procgroup_tree_lock
2. procgroup->lock

130
kernel/proc/mutex.c Normal file
View File

@@ -0,0 +1,130 @@
#include <libk/assert.h>
#include <libk/rbtree.h>
#include <libk/std.h>
#include <libk/string.h>
#include <mm/liballoc.h>
#include <proc/mutex.h>
#include <proc/proc.h>
#include <proc/suspension_q.h>
#include <sync/spin_lock.h>
#include <sys/debug.h>
#include <sys/smp.h>
#include <sys/spin_lock.h>
void proc_mutexes_cleanup (struct proc* proc) {
spin_lock_ctx_t ctxpg, ctxrs;
spin_lock (&proc->procgroup->lock, &ctxpg);
struct rb_node_link* rnode;
rbtree_first (&proc->procgroup->resource_tree, rnode);
while (rnode) {
struct rb_node_link* next;
rbtree_next (rnode, next);
struct proc_resource* resource = rbtree_entry (rnode, struct proc_resource, resource_tree_link);
rnode = next;
spin_lock (&resource->lock, &ctxrs);
if (resource->type != PR_MUTEX) {
spin_unlock (&resource->lock, &ctxrs);
continue;
}
if (resource->u.mutex.owner == proc && resource->u.mutex.locked) {
spin_unlock (&resource->lock, &ctxrs);
proc_mutex_unlock (proc, &resource->u.mutex);
}
}
spin_unlock (&proc->procgroup->lock, &ctxpg);
}
bool proc_cleanup_resource_mutex (struct proc_resource* resource) {
struct proc_mutex* mutex = &resource->u.mutex;
spin_lock_ctx_t ctxmt, ctxsq;
spin_lock (&mutex->resource->lock, &ctxmt);
spin_lock (&mutex->suspension_q.lock, &ctxsq);
bool reschedule = PROC_NO_RESCHEDULE;
while (mutex->suspension_q.proc_list != NULL) {
struct list_node_link* node = mutex->suspension_q.proc_list;
struct proc_sq_entry* sq_entry = list_entry (node, struct proc_sq_entry, sq_link);
struct proc* suspended_proc = sq_entry->proc;
/* we will relock during resume */
spin_unlock (&mutex->suspension_q.lock, &ctxsq);
spin_unlock (&mutex->resource->lock, &ctxmt);
reschedule = reschedule || proc_sq_resume (suspended_proc, sq_entry);
/* reacquire */
spin_lock (&mutex->resource->lock, &ctxmt);
spin_lock (&mutex->suspension_q.lock, &ctxsq);
}
mutex->locked = false;
mutex->owner = NULL;
spin_unlock (&mutex->suspension_q.lock, &ctxsq);
spin_unlock (&mutex->resource->lock, &ctxmt);
return reschedule;
}
bool proc_mutex_lock (struct proc* proc, struct proc_mutex* mutex) {
spin_lock_ctx_t ctxmt;
spin_lock (&mutex->resource->lock, &ctxmt);
if (!mutex->locked || mutex->owner == proc) {
mutex->locked = true;
mutex->owner = proc;
spin_unlock (&mutex->resource->lock, &ctxmt);
return PROC_NO_RESCHEDULE;
}
return proc_sq_suspend (proc, &mutex->suspension_q, &mutex->resource->lock, &ctxmt);
}
bool proc_mutex_unlock (struct proc* proc, struct proc_mutex* mutex) {
spin_lock_ctx_t ctxmt, ctxsq;
spin_lock (&mutex->resource->lock, &ctxmt);
if (mutex->owner != proc) {
spin_unlock (&mutex->resource->lock, &ctxmt);
return PROC_NO_RESCHEDULE;
}
spin_lock (&mutex->suspension_q.lock, &ctxsq);
struct list_node_link* node = mutex->suspension_q.proc_list;
if (node) {
struct proc_sq_entry* sq_entry = list_entry (node, struct proc_sq_entry, sq_link);
struct proc* resumed_proc = sq_entry->proc;
mutex->owner = resumed_proc;
mutex->locked = true;
spin_unlock (&mutex->suspension_q.lock, &ctxsq);
spin_unlock (&mutex->resource->lock, &ctxmt);
return proc_sq_resume (resumed_proc, sq_entry);
}
mutex->locked = false;
mutex->owner = NULL;
spin_unlock (&mutex->suspension_q.lock, &ctxsq);
spin_unlock (&mutex->resource->lock, &ctxmt);
return PROC_NEED_RESCHEDULE;
}

23
kernel/proc/mutex.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef _KERNEL_PROC_MUTEX_H
#define _KERNEL_PROC_MUTEX_H
#include <libk/std.h>
#include <proc/suspension_q.h>
struct proc;
struct proc_resource;
struct proc_mutex {
struct proc_resource* resource;
bool locked;
struct proc_suspension_q suspension_q;
struct proc* owner;
};
bool proc_cleanup_resource_mutex (struct proc_resource* resource);
bool proc_mutex_lock (struct proc* proc, struct proc_mutex* mutex);
bool proc_mutex_unlock (struct proc* proc, struct proc_mutex* mutex);
void proc_mutexes_cleanup (struct proc* proc);
#endif // _KERNEL_PROC_MUTEX_H

View File

@@ -3,32 +3,34 @@
#include <irq/irq.h> #include <irq/irq.h>
#include <libk/align.h> #include <libk/align.h>
#include <libk/list.h> #include <libk/list.h>
#include <libk/rbtree.h>
#include <libk/std.h> #include <libk/std.h>
#include <libk/string.h> #include <libk/string.h>
#include <limine/requests.h> #include <limine/requests.h>
#include <mm/liballoc.h> #include <mm/liballoc.h>
#include <mm/pmm.h> #include <mm/pmm.h>
#include <proc/proc.h> #include <proc/proc.h>
#include <proc/procgroup.h>
#include <proc/resource.h>
#include <rd/rd.h> #include <rd/rd.h>
#include <sync/spin_lock.h> #include <sync/spin_lock.h>
#include <sys/debug.h> #include <sys/debug.h>
#include <sys/mm.h> #include <sys/mm.h>
#include <sys/proc.h>
#include <sys/sched.h> #include <sys/sched.h>
#include <sys/smp.h> #include <sys/smp.h>
#include <sys/spin.h>
#if defined(__x86_64__) #if defined(__x86_64__)
#include <amd64/intr_defs.h> #include <amd64/intr_defs.h>
#endif #endif
struct elf_aux { #define SCHED_REAP_FREQ 10
uint64_t entry;
uint64_t phdr;
uint64_t phent;
uint64_t phnum;
};
static struct procw* procs; static struct rb_node_link* proc_tree = NULL;
static spin_lock_t procs_lock = SPIN_LOCK_INIT; static spin_lock_t proc_tree_lock = SPIN_LOCK_INIT;
static atomic_int sched_cycles = 0;
static bool proc_check_elf (uint8_t* elf) { static bool proc_check_elf (uint8_t* elf) {
if (!((elf[0] == 0x7F) && (elf[1] == 'E') && (elf[2] == 'L') && (elf[3] == 'F'))) if (!((elf[0] == 0x7F) && (elf[1] == 'E') && (elf[2] == 'L') && (elf[3] == 'F')))
@@ -36,28 +38,7 @@ static bool proc_check_elf (uint8_t* elf) {
return true; return true;
} }
void proc_map (struct proc* proc, uintptr_t start_paddr, uintptr_t start_vaddr, size_t pages, struct elf_aux proc_load_segments (struct proc* proc, uint8_t* elf) {
uint32_t flags) {
struct proc_mapping* mapping = malloc (sizeof (*mapping));
mapping->paddr = start_paddr;
mapping->vaddr = start_vaddr;
mapping->size = pages * PAGE_SIZE;
flags &= ~MM_PD_LOCK; /* clear LOCK flag if present, because we lock manualy */
spin_lock (&proc->pd.lock);
linklist_append (struct proc_mapping*, proc->mappings, mapping);
for (uintptr_t vpage = start_vaddr, ppage = start_paddr; vpage < start_vaddr + pages * PAGE_SIZE;
vpage += PAGE_SIZE, ppage += PAGE_SIZE) {
mm_map_page (&proc->pd, ppage, vpage, flags);
}
spin_unlock (&proc->pd.lock);
}
static struct elf_aux proc_load_segments (struct proc* proc, uint8_t* elf) {
struct elf_aux aux; struct elf_aux aux;
Elf64_Ehdr* ehdr = (Elf64_Ehdr*)elf; Elf64_Ehdr* ehdr = (Elf64_Ehdr*)elf;
@@ -81,19 +62,37 @@ static struct elf_aux proc_load_segments (struct proc* proc, uint8_t* elf) {
size_t blks = div_align_up (phdr->p_memsz + off, PAGE_SIZE); size_t blks = div_align_up (phdr->p_memsz + off, PAGE_SIZE);
uintptr_t p_addr = pmm_alloc (blks);
if (p_addr == PMM_ALLOC_ERR)
DEBUG ("pmm oom error while loading ELF segments! (tried to alloc %zu blks)\n", blks);
memset ((void*)((uintptr_t)hhdm->offset + p_addr), 0, blks * PAGE_SIZE);
memcpy ((void*)((uintptr_t)hhdm->offset + p_addr + off),
(void*)((uintptr_t)elf + phdr->p_offset), phdr->p_filesz);
uint32_t pg_flags = MM_PG_USER | MM_PG_PRESENT; uint32_t pg_flags = MM_PG_USER | MM_PG_PRESENT;
if (phdr->p_flags & PF_W) if (phdr->p_flags & PF_W)
pg_flags |= MM_PG_RW; pg_flags |= MM_PG_RW;
proc_map (proc, p_addr, v_addr, blks, pg_flags); uintptr_t p_addr;
procgroup_map (proc->procgroup, v_addr, blks, pg_flags, &p_addr);
memset ((void*)((uintptr_t)hhdm->offset + p_addr), 0, blks * PAGE_SIZE);
memcpy ((void*)((uintptr_t)hhdm->offset + p_addr + off),
(void*)((uintptr_t)elf + phdr->p_offset), phdr->p_filesz);
} break;
case PT_TLS: {
#if defined(__x86_64__)
if (phdr->p_memsz > 0) {
size_t tls_align = phdr->p_align ? phdr->p_align : sizeof (uintptr_t);
size_t tls_size = align_up (phdr->p_memsz, tls_align);
size_t tls_total_needed = tls_size + sizeof (uintptr_t);
size_t blks = div_align_up (tls_total_needed, PAGE_SIZE);
proc->procgroup->tls.tls_tmpl_pages = blks;
proc->procgroup->tls.tls_tmpl_size = tls_size;
proc->procgroup->tls.tls_tmpl_total_size = tls_total_needed;
proc->procgroup->tls.tls_tmpl = malloc (blks * PAGE_SIZE);
memset (proc->procgroup->tls.tls_tmpl, 0, blks * PAGE_SIZE);
memcpy (proc->procgroup->tls.tls_tmpl, (void*)((uintptr_t)elf + phdr->p_offset),
phdr->p_filesz);
proc_init_tls (proc);
}
#endif
} break; } break;
} }
} }
@@ -101,145 +100,184 @@ static struct elf_aux proc_load_segments (struct proc* proc, uint8_t* elf) {
return aux; return aux;
} }
static struct proc* proc_spawn_rd (char* name) { struct proc* proc_spawn_rd (char* name) {
struct rd_file* rd_file = rd_get_file (name); struct rd_file* rd_file = rd_get_file (name);
bool ok = proc_check_elf (rd_file->content); bool ok = proc_check_elf (rd_file->content);
DEBUG ("ELF magic %s\n", (ok ? "OK" : "BAD"));
if (!ok) if (!ok)
return NULL; return NULL;
struct proc* proc = malloc (sizeof (*proc)); return proc_from_elf (rd_file->content);
if (proc == NULL) }
return NULL;
memset (proc, 0, sizeof (*proc)); struct proc* proc_find_pid (int pid) {
spin_lock_ctx_t ctxprtr;
struct proc* proc = NULL;
#if defined(__x86_64__) spin_lock (&proc_tree_lock, &ctxprtr);
proc->pd.lock = SPIN_LOCK_INIT; rbtree_find (struct proc, &proc_tree, pid, proc, proc_tree_link, pid);
proc->pd.cr3_paddr = mm_alloc_user_pd_phys (); spin_unlock (&proc_tree_lock, &ctxprtr);
if (proc->pd.cr3_paddr == 0) {
free (proc);
return NULL;
}
proc->pdata.syscall_stack = pmm_alloc (KSTACK_SIZE / PAGE_SIZE);
if (proc->pdata.syscall_stack == PMM_ALLOC_ERR) {
free (proc);
return NULL;
}
proc->pdata.user_stack = pmm_alloc (USTACK_SIZE / PAGE_SIZE);
if (proc->pdata.user_stack == PMM_ALLOC_ERR) {
free (proc);
pmm_free (proc->pdata.syscall_stack, USTACK_SIZE / PAGE_SIZE);
return NULL;
}
uintptr_t user_stack = proc->pdata.user_stack;
proc->pdata.syscall_stack += KSTACK_SIZE;
proc->pdata.user_stack += USTACK_SIZE;
proc_map (proc, user_stack, PROC_USTACK_TOP - USTACK_SIZE, USTACK_SIZE / PAGE_SIZE,
MM_PG_USER | MM_PG_PRESENT | MM_PG_RW);
struct elf_aux aux = proc_load_segments (proc, rd_file->content);
proc->pdata.regs.ss = 0x20 | 0x03;
proc->pdata.regs.rsp = (uint64_t)PROC_USTACK_TOP;
proc->pdata.regs.rflags = 0x202;
proc->pdata.regs.cs = 0x18 | 0x03;
proc->pdata.regs.rip = aux.entry;
proc->lock = SPIN_LOCK_INIT;
#endif
return proc; return proc;
} }
static void proc_register (struct proc* proc) { void proc_register (struct proc* proc, struct cpu* cpu1) {
/* make available globally. */ spin_lock_ctx_t ctxcpu, ctxprtr;
struct procw* procw = malloc (sizeof (*procw));
if (procw == NULL)
return;
procw->proc = proc;
proc->procw = procw;
spin_lock (&procs_lock); proc->cpu = cpu1 != NULL ? cpu1 : cpu_find_lightest ();
spin_lock (&thiscpu->lock); struct cpu* cpu = proc->cpu;
linklist_append (struct procw*, procs, procw); spin_lock (&proc_tree_lock, &ctxprtr);
linklist_append (struct proc*, thiscpu->proc_run_q, proc); spin_lock (&cpu->lock, &ctxcpu);
if (thiscpu->proc_current == NULL) rbtree_insert (struct proc, &proc_tree, &proc->proc_tree_link, proc_tree_link, pid);
thiscpu->proc_current = proc;
spin_unlock (&thiscpu->lock); atomic_fetch_add (&cpu->proc_run_q_count, 1);
list_append (cpu->proc_run_q, &proc->cpu_run_q_link);
if (cpu->proc_current == NULL)
cpu->proc_current = proc;
spin_unlock (&procs_lock); spin_unlock (&proc_tree_lock, &ctxprtr);
spin_unlock (&cpu->lock, &ctxcpu);
}
/* caller holds cpu->lock */
static struct proc* proc_find_sched (struct cpu* cpu) {
if (!cpu->proc_run_q)
return NULL;
struct list_node_link *current, *start;
if (cpu->proc_current)
current = cpu->proc_current->cpu_run_q_link.next;
else
current = cpu->proc_run_q;
if (!current)
current = cpu->proc_run_q;
start = current;
do {
struct proc* proc = list_entry (current, struct proc, cpu_run_q_link);
if (atomic_load (&proc->state) == PROC_READY)
return proc;
current = current->next ? current->next : cpu->proc_run_q;
} while (current != start);
return NULL;
}
static void proc_reap (void) {
struct proc* proc = NULL;
struct list_node_link* reap_list = NULL;
spin_lock_ctx_t ctxprtr;
spin_lock_ctx_t ctxpr;
spin_lock (&proc_tree_lock, &ctxprtr);
struct rb_node_link* node;
rbtree_first (&proc_tree, node);
while (node) {
struct rb_node_link* next;
rbtree_next (node, next);
proc = rbtree_entry (node, struct proc, proc_tree_link);
if (atomic_load (&proc->state) == PROC_DEAD) {
spin_lock (&proc->lock, &ctxpr);
rbtree_delete (&proc_tree, &proc->proc_tree_link);
list_append (reap_list, &proc->reap_link);
spin_unlock (&proc->lock, &ctxpr);
}
node = next;
}
spin_unlock (&proc_tree_lock, &ctxprtr);
struct list_node_link *reap_link, *reap_link_tmp;
list_foreach (reap_list, reap_link, reap_link_tmp) {
proc = list_entry (reap_link, struct proc, reap_link);
list_remove (reap_list, &proc->reap_link);
DEBUG ("cleanup PID %d\n", proc->pid);
proc_cleanup (proc);
}
} }
void proc_sched (void) { void proc_sched (void) {
spin_lock (&thiscpu->lock); spin_lock_ctx_t ctxcpu;
if (thiscpu->proc_run_q == NULL || thiscpu->proc_current == NULL) { int s_cycles = atomic_fetch_add (&sched_cycles, 1);
goto done;
if (s_cycles % SCHED_REAP_FREQ == 0)
proc_reap ();
struct proc* next = NULL;
struct cpu* cpu = thiscpu;
spin_lock (&cpu->lock, &ctxcpu);
next = proc_find_sched (cpu);
if (next) {
cpu->proc_current = next;
do_sched (next, &cpu->lock, &ctxcpu);
} else {
cpu->proc_current = NULL;
spin_unlock (&cpu->lock, &ctxcpu);
spin ();
} }
thiscpu->proc_current = thiscpu->proc_current->next;
if (thiscpu->proc_current == NULL) {
thiscpu->proc_current = thiscpu->proc_run_q;
}
done:
spin_unlock (&thiscpu->lock);
if (thiscpu->proc_current != NULL) {
do_sched (&thiscpu->proc_current->pdata.regs, &thiscpu->proc_current->pd);
}
#if defined(__x86_64__)
extern void amd64_spin (void);
amd64_spin ();
#endif
} }
void proc_kill (struct proc* proc) { void proc_kill (struct proc* proc) {
spin_lock (&procs_lock); spin_lock_ctx_t ctxpr, ctxcpu;
struct cpu* cpu = proc->cpu;
spin_lock (&thiscpu->lock); spin_lock (&proc->lock, &ctxpr);
atomic_store (&proc->state, PROC_DEAD);
proc->cpu = NULL;
spin_unlock (&proc->lock, &ctxpr);
linklist_remove (struct procw*, procs, proc->procw); spin_lock (&cpu->lock, &ctxcpu);
linklist_remove (struct proc*, thiscpu->proc_run_q, proc);
if (thiscpu->proc_current == proc) list_remove (cpu->proc_run_q, &proc->cpu_run_q_link);
thiscpu->proc_current = NULL; atomic_fetch_sub (&cpu->proc_run_q_count, 1);
if (cpu->proc_current == proc)
cpu->proc_current = NULL;
spin_unlock (&thiscpu->lock); spin_unlock (&cpu->lock, &ctxcpu);
spin_unlock (&procs_lock); DEBUG ("killed PID %d\n", proc->pid);
/* clean up */ cpu_request_sched (cpu);
free (proc->procw);
free (proc);
} }
static void proc_irq_sched (void* arg, void* regs) { static void proc_irq_sched (void* arg, void* regs) {
(void)arg, (void)regs; (void)arg;
proc_sched (); proc_sched ();
} }
void proc_init (void) { void proc_init (void) {
struct proc* init = proc_spawn_rd ("init.exe");
proc_register (init);
#if defined(__x86_64__) #if defined(__x86_64__)
irq_attach (&proc_irq_sched, NULL, SCHED_PREEMPT_TIMER, 0); irq_attach (&proc_irq_sched, NULL, SCHED_PREEMPT_TIMER);
irq_attach (&proc_irq_sched, NULL, CPU_REQUEST_SCHED);
#endif #endif
do_sched (&init->pdata.regs, &init->pd); struct proc* spin_proc = proc_spawn_rd ("spin.exe");
proc_register (spin_proc, thiscpu);
struct proc* init = proc_spawn_rd ("init.exe");
proc_register (init, NULL);
spin_lock_ctx_t ctxcpu;
spin_lock (&spin_proc->cpu->lock, &ctxcpu);
do_sched (spin_proc, &spin_proc->cpu->lock, &ctxcpu);
} }

View File

@@ -2,7 +2,13 @@
#define _KERNEL_PROC_PROC_H #define _KERNEL_PROC_PROC_H
#include <aux/compiler.h> #include <aux/compiler.h>
#include <aux/elf.h>
#include <libk/list.h>
#include <libk/rbtree.h>
#include <libk/std.h> #include <libk/std.h>
#include <proc/procgroup.h>
#include <proc/resource.h>
#include <proc/suspension_q.h>
#include <sync/spin_lock.h> #include <sync/spin_lock.h>
#include <sys/mm.h> #include <sys/mm.h>
@@ -11,39 +17,41 @@
#include <amd64/proc.h> /* USTACK_SIZE */ #include <amd64/proc.h> /* USTACK_SIZE */
#endif #endif
#define PROC_NEED_RESCHEDULE true
#define PROC_NO_RESCHEDULE false
/* process states */
#define PROC_READY 0
#define PROC_DEAD 1
#define PROC_SUSPENDED 2
/* process flags */
#define PROC_USTK_PREALLOC (1 << 0)
struct cpu; struct cpu;
struct proc_mapping {
struct proc_mapping* next;
uintptr_t paddr;
uintptr_t vaddr;
size_t size;
} PACKED;
struct procw;
struct proc { struct proc {
struct proc* next; int pid;
struct proc_mapping* mappings; /* pd.lock implicitly protects this field */ struct rb_node_link proc_tree_link;
struct rb_node_link procgroup_memb_tree_link;
struct list_node_link cpu_run_q_link;
struct list_node_link reap_link;
struct list_node_link* sq_entries;
struct procgroup* procgroup;
struct proc_platformdata pdata; struct proc_platformdata pdata;
struct pd pd; uint32_t flags;
spin_lock_t lock; spin_lock_t lock;
struct cpu* cpu; struct cpu* cpu;
struct procw* procw; /* link to it's global struct */ atomic_int state;
}; uintptr_t uvaddr_argument;
/*
* struct proc is a member of a CPU's proc_run_q.
* struct procw is a process wrapper that is a member of
* a global process list.
*/
struct procw {
struct procw* next;
struct proc* proc;
}; };
void proc_sched (void); void proc_sched (void);
void proc_kill (struct proc* proc); void proc_kill (struct proc* proc);
struct elf_aux proc_load_segments (struct proc* proc, uint8_t* elf);
void proc_register (struct proc* proc, struct cpu* cpu);
struct proc* proc_find_pid (int pid);
struct proc* proc_spawn_rd (char* name);
void proc_init (void); void proc_init (void);
#endif // _KERNEL_PROC_PROC_H #endif // _KERNEL_PROC_PROC_H

218
kernel/proc/procgroup.c Normal file
View File

@@ -0,0 +1,218 @@
#include <libk/rbtree.h>
#include <libk/std.h>
#include <mm/liballoc.h>
#include <mm/pmm.h>
#include <proc/proc.h>
#include <proc/procgroup.h>
#include <sync/spin_lock.h>
#include <sys/debug.h>
#include <sys/mm.h>
static struct rb_node_link* procgroup_tree = NULL;
static spin_lock_t procgroup_tree_lock = SPIN_LOCK_INIT;
static atomic_int pgids = 0;
uintptr_t procgroup_map (struct procgroup* procgroup, uintptr_t vaddr, size_t pages, uint32_t flags,
uintptr_t* out_paddr) {
spin_lock_ctx_t ctxpg;
spin_lock (&procgroup->lock, &ctxpg);
vaddr = (vaddr == 0) ? procgroup->map_base : vaddr;
struct proc_mapping* mapping = malloc (sizeof (*mapping));
if (mapping == NULL) {
spin_unlock (&procgroup->lock, &ctxpg);
return 0;
}
uintptr_t paddr = pmm_alloc (pages);
if (paddr == PMM_ALLOC_ERR) {
free (mapping);
spin_unlock (&procgroup->lock, &ctxpg);
return 0;
}
if (out_paddr != NULL)
*out_paddr = paddr;
mapping->paddr = paddr;
mapping->vaddr = vaddr;
mapping->size = pages * PAGE_SIZE;
procgroup->map_base += pages * PAGE_SIZE;
list_append (procgroup->mappings, &mapping->proc_mappings_link);
for (uintptr_t vpage = vaddr, ppage = paddr; vpage < vaddr + pages * PAGE_SIZE;
vpage += PAGE_SIZE, ppage += PAGE_SIZE) {
mm_map_page (&procgroup->pd, ppage, vpage, flags);
}
spin_unlock (&procgroup->lock, &ctxpg);
return vaddr;
}
bool procgroup_unmap (struct procgroup* procgroup, uintptr_t start_vaddr, size_t pages) {
size_t unmap_size = pages * PAGE_SIZE;
uintptr_t end_vaddr = start_vaddr + unmap_size;
struct list_node_link *mapping_link, *mapping_link_tmp;
bool used_tail_mapping = false;
spin_lock_ctx_t ctxpg;
struct proc_mapping* tail_mapping = malloc (sizeof (*tail_mapping));
if (tail_mapping == NULL)
return false;
spin_lock (&procgroup->lock, &ctxpg);
list_foreach (procgroup->mappings, mapping_link, mapping_link_tmp) {
struct proc_mapping* mapping =
list_entry (mapping_link, struct proc_mapping, proc_mappings_link);
uintptr_t m_start = mapping->vaddr;
uintptr_t m_end = mapping->vaddr + mapping->size;
/* check overlap */
if ((start_vaddr < m_end) && (end_vaddr > mapping->vaddr)) {
uintptr_t free_vstart = (start_vaddr > m_start) ? start_vaddr : m_start;
uintptr_t free_vend = (end_vaddr < m_end) ? end_vaddr : m_end;
size_t free_size = free_vend - free_vstart;
uintptr_t ppage_to_free = mapping->paddr + (free_vstart - m_start);
pmm_free (ppage_to_free, free_size / PAGE_SIZE);
/* split in the middle */
if ((start_vaddr > m_start) && (end_vaddr < m_end)) {
tail_mapping->vaddr = end_vaddr;
tail_mapping->paddr = mapping->paddr + (end_vaddr - m_start);
tail_mapping->size = m_end - end_vaddr;
mapping->size = start_vaddr - m_start;
list_insert_after (procgroup->mappings, &mapping->proc_mappings_link,
&tail_mapping->proc_mappings_link);
used_tail_mapping = true;
break;
} else if ((start_vaddr <= m_start) && (end_vaddr < m_end)) { /* shrink left */
size_t diff = end_vaddr - m_start;
mapping->vaddr += diff;
mapping->paddr += diff;
mapping->size -= diff;
} else if ((start_vaddr > m_start) && (end_vaddr >= m_end)) { /* shrink right */
mapping->size = start_vaddr - m_start;
} else { /* full overlap */
list_remove (procgroup->mappings, &mapping->proc_mappings_link);
free (mapping);
}
}
}
if (!used_tail_mapping)
free (tail_mapping);
for (uintptr_t vpage = start_vaddr; vpage < end_vaddr; vpage += PAGE_SIZE) {
mm_unmap_page (&procgroup->pd, vpage);
}
spin_unlock (&procgroup->lock, &ctxpg);
return true;
}
struct procgroup* procgroup_create (void) {
spin_lock_ctx_t ctxpgtr;
struct procgroup* procgroup = malloc (sizeof (*procgroup));
if (procgroup == NULL) {
return NULL;
}
procgroup->refs = 0;
procgroup->memb_proc_tree = NULL;
procgroup->lock = SPIN_LOCK_INIT;
procgroup->pgid = atomic_fetch_add (&pgids, 1);
procgroup->pd.cr3_paddr = mm_alloc_user_pd_phys ();
procgroup->map_base = PROC_MAP_BASE;
spin_lock (&procgroup_tree_lock, &ctxpgtr);
rbtree_insert (struct procgroup, &procgroup_tree, &procgroup->procgroup_tree_link,
procgroup_tree_link, pgid);
spin_unlock (&procgroup_tree_lock, &ctxpgtr);
return procgroup;
}
void procgroup_attach (struct procgroup* procgroup, struct proc* proc) {
spin_lock_ctx_t ctxpg, ctxpr;
spin_lock (&procgroup->lock, &ctxpg);
spin_lock (&proc->lock, &ctxpr);
rbtree_insert (struct proc, &procgroup->memb_proc_tree, &proc->procgroup_memb_tree_link,
procgroup_memb_tree_link, pid);
atomic_fetch_add (&procgroup->refs, 1);
spin_unlock (&proc->lock, &ctxpr);
spin_unlock (&procgroup->lock, &ctxpg);
}
void procgroup_detach (struct procgroup* procgroup, struct proc* proc) {
spin_lock_ctx_t ctxpg, ctxpr, ctxpgtr;
spin_lock (&procgroup->lock, &ctxpg);
spin_lock (&proc->lock, &ctxpr);
rbtree_delete (&procgroup->memb_proc_tree, &proc->procgroup_memb_tree_link);
int refs = atomic_fetch_sub (&procgroup->refs, 1);
spin_unlock (&proc->lock, &ctxpr);
spin_unlock (&procgroup->lock, &ctxpg);
if (refs == 1) {
spin_lock (&procgroup_tree_lock, &ctxpgtr);
spin_lock (&procgroup->lock, &ctxpg);
rbtree_delete (&procgroup_tree, &procgroup->procgroup_tree_link);
spin_unlock (&procgroup->lock, &ctxpg);
spin_unlock (&procgroup_tree_lock, &ctxpgtr);
/* delete resources */
struct rb_node_link* rnode;
rbtree_first (&procgroup->resource_tree, rnode);
while (rnode) {
struct rb_node_link* next;
rbtree_next (rnode, next);
struct proc_resource* resource =
rbtree_entry (rnode, struct proc_resource, resource_tree_link);
rnode = next;
proc_delete_resource (resource);
}
struct list_node_link *mapping_link, *mapping_link_tmp;
list_foreach (procgroup->mappings, mapping_link, mapping_link_tmp) {
struct proc_mapping* mapping =
list_entry (mapping_link, struct proc_mapping, proc_mappings_link);
pmm_free (mapping->paddr, mapping->size / PAGE_SIZE);
free (mapping);
}
pmm_free (procgroup->pd.cr3_paddr, 1);
free (procgroup->tls.tls_tmpl);
free (procgroup);
}
}

43
kernel/proc/procgroup.h Normal file
View File

@@ -0,0 +1,43 @@
#ifndef _KERNEL_PROC_PROCGROUP_H
#define _KERNEL_PROC_PROCGROUP_H
#include <libk/list.h>
#include <libk/rbtree.h>
#include <libk/std.h>
#include <proc/resource.h>
#include <sync/spin_lock.h>
#include <sys/mm.h>
#include <sys/procgroup.h>
struct proc;
struct proc_mapping {
struct list_node_link proc_mappings_link;
uintptr_t paddr;
uintptr_t vaddr;
size_t size;
};
struct procgroup {
int pgid;
struct rb_node_link procgroup_tree_link;
struct rb_node_link* memb_proc_tree;
spin_lock_t lock;
atomic_int refs;
struct rb_node_link* resource_tree;
atomic_int sys_rids;
struct pd pd;
struct list_node_link* mappings;
uintptr_t map_base;
struct procgroup_tls tls;
};
struct procgroup* procgroup_create (void);
void procgroup_attach (struct procgroup* procgroup, struct proc* proc);
void procgroup_detach (struct procgroup* procgroup, struct proc* proc);
uintptr_t procgroup_map (struct procgroup* procgroup, uintptr_t vaddr, size_t pages, uint32_t flags,
uintptr_t* out_paddr);
bool procgroup_unmap (struct procgroup* procgroup, uintptr_t start_vaddr, size_t pages);
#endif // _KERNEL_PROC_PROCGROUP_H

59
kernel/proc/resource.c Normal file
View File

@@ -0,0 +1,59 @@
#include <libk/assert.h>
#include <libk/list.h>
#include <libk/rbtree.h>
#include <libk/std.h>
#include <libk/string.h>
#include <mm/liballoc.h>
#include <mm/pmm.h>
#include <proc/mutex.h>
#include <proc/proc.h>
#include <proc/procgroup.h>
#include <proc/resource.h>
#include <sync/spin_lock.h>
#include <sys/debug.h>
struct proc_resource* proc_find_resource (struct procgroup* procgroup, int rid) {
spin_lock_ctx_t ctxpg;
struct proc_resource* resource = NULL;
spin_lock (&procgroup->lock, &ctxpg);
rbtree_find (struct proc_resource, &procgroup->resource_tree, rid, resource, resource_tree_link,
rid);
spin_unlock (&procgroup->lock, &ctxpg);
return resource;
}
struct proc_resource* proc_create_resource_mutex (struct procgroup* procgroup, int rid) {
spin_lock_ctx_t ctxpg;
struct proc_resource* resource;
resource = proc_find_resource (procgroup, rid);
if (resource != NULL)
return resource;
resource = malloc (sizeof (*resource));
if (resource == NULL)
return NULL;
memset (resource, 0, sizeof (*resource));
resource->lock = SPIN_LOCK_INIT;
resource->ops.cleanup = &proc_cleanup_resource_mutex;
resource->u.mutex.resource = resource;
resource->rid = rid;
resource->type = PR_MUTEX;
spin_lock (&procgroup->lock, &ctxpg);
rbtree_insert (struct proc_resource, &procgroup->resource_tree, &resource->resource_tree_link,
resource_tree_link, rid);
spin_unlock (&procgroup->lock, &ctxpg);
return resource;
}
bool proc_delete_resource (struct proc_resource* resource) {
bool reschedule = resource->ops.cleanup (resource);
free (resource);
return reschedule;
}

32
kernel/proc/resource.h Normal file
View File

@@ -0,0 +1,32 @@
#ifndef _KERNEL_PROC_RESOURCE_H
#define _KERNEL_PROC_RESOURCE_H
#include <libk/list.h>
#include <libk/rbtree.h>
#include <libk/std.h>
#include <proc/mutex.h>
#include <sync/spin_lock.h>
#define PR_MUTEX 1
struct proc;
struct procgroup;
struct proc_resource {
int type;
int rid;
spin_lock_t lock;
struct rb_node_link resource_tree_link;
union {
struct proc_mutex mutex;
} u;
struct {
bool (*cleanup) (struct proc_resource* resource);
} ops;
};
struct proc_resource* proc_find_resource (struct procgroup* procgroup, int rid);
struct proc_resource* proc_create_resource_mutex (struct procgroup* procgroup, int rid);
bool proc_delete_resource (struct proc_resource* resource);
#endif // _KERNEL_PROC_RESOURCE_H

View File

@@ -1,3 +1,11 @@
c += proc/proc.c c += proc/proc.c \
proc/resource.c \
proc/mutex.c \
proc/procgroup.c \
proc/suspension_q.c
o += proc/proc.o o += proc/proc.o \
proc/resource.o \
proc/mutex.o \
proc/procgroup.o \
proc/suspension_q.o

111
kernel/proc/suspension_q.c Normal file
View File

@@ -0,0 +1,111 @@
#include <libk/list.h>
#include <libk/std.h>
#include <mm/liballoc.h>
#include <proc/proc.h>
#include <proc/resource.h>
#include <proc/suspension_q.h>
#include <sync/spin_lock.h>
#include <sys/smp.h>
#include <sys/spin_lock.h>
bool proc_sq_suspend (struct proc* proc, struct proc_suspension_q* sq, spin_lock_t* resource_lock,
spin_lock_ctx_t* ctxrl) {
spin_lock_ctx_t ctxpr, ctxcpu, ctxsq;
struct cpu* cpu = proc->cpu;
struct proc_sq_entry* sq_entry = malloc (sizeof (*sq_entry));
if (!sq_entry) {
spin_unlock (resource_lock, ctxrl);
return PROC_NO_RESCHEDULE;
}
sq_entry->proc = proc;
sq_entry->sq = sq;
spin_lock (&cpu->lock, &ctxcpu);
spin_lock (&proc->lock, &ctxpr);
spin_lock (&sq->lock, &ctxsq);
spin_unlock (resource_lock, ctxrl);
atomic_store (&proc->state, PROC_SUSPENDED);
/* append to sq's list */
list_append (sq->proc_list, &sq_entry->sq_link);
/* append to proc's list */
list_append (proc->sq_entries, &sq_entry->proc_link);
list_remove (cpu->proc_run_q, &proc->cpu_run_q_link);
atomic_fetch_sub (&cpu->proc_run_q_count, 1);
if (cpu->proc_current == proc)
cpu->proc_current = NULL;
proc->cpu = NULL;
spin_unlock (&sq->lock, &ctxsq);
spin_unlock (&proc->lock, &ctxpr);
spin_unlock (&cpu->lock, &ctxcpu);
return PROC_NEED_RESCHEDULE;
}
bool proc_sq_resume (struct proc* proc, struct proc_sq_entry* sq_entry) {
spin_lock_ctx_t ctxsq, ctxpr, ctxcpu;
struct cpu* cpu = cpu_find_lightest ();
struct proc_suspension_q* sq = sq_entry->sq;
spin_lock (&cpu->lock, &ctxcpu);
spin_lock (&proc->lock, &ctxpr);
spin_lock (&sq->lock, &ctxsq);
/* remove from sq's list */
list_remove (sq->proc_list, &sq_entry->sq_link);
/* remove from proc's list */
list_remove (proc->sq_entries, &sq_entry->proc_link);
proc->cpu = cpu;
if (proc->sq_entries == NULL)
atomic_store (&proc->state, PROC_READY);
list_append (cpu->proc_run_q, &proc->cpu_run_q_link);
atomic_fetch_add (&cpu->proc_run_q_count, 1);
spin_unlock (&sq->lock, &ctxsq);
spin_unlock (&proc->lock, &ctxpr);
spin_unlock (&cpu->lock, &ctxcpu);
free (sq_entry);
return PROC_NEED_RESCHEDULE;
}
void proc_sqs_cleanup (struct proc* proc) {
spin_lock_ctx_t ctxsq, ctxpr;
spin_lock (&proc->lock, &ctxpr);
/* clean suspension queue entries */
struct list_node_link *sq_link, *sq_link_tmp;
list_foreach (proc->sq_entries, sq_link, sq_link_tmp) {
struct proc_sq_entry* sq_entry = list_entry (sq_link, struct proc_sq_entry, proc_link);
struct proc_suspension_q* sq = sq_entry->sq;
spin_lock (&sq->lock, &ctxsq);
/* remove from sq's list */
list_remove (sq->proc_list, &sq_entry->sq_link);
/* remove from proc's list */
list_remove (proc->sq_entries, &sq_entry->proc_link);
spin_unlock (&sq->lock, &ctxsq);
free (sq_entry);
}
spin_unlock (&proc->lock, &ctxpr);
}

View File

@@ -0,0 +1,26 @@
#ifndef _KERNEL_PROC_SUSPENTION_Q_H
#define _KERNEL_PROC_SUSPENTION_Q_H
#include <libk/list.h>
#include <sync/spin_lock.h>
struct proc;
struct proc_suspension_q {
struct list_node_link* proc_list;
spin_lock_t lock;
};
struct proc_sq_entry {
struct list_node_link sq_link;
struct list_node_link proc_link;
struct proc* proc;
struct proc_suspension_q* sq;
};
void proc_sqs_cleanup (struct proc* proc);
bool proc_sq_suspend (struct proc* proc, struct proc_suspension_q* sq, spin_lock_t* resource_lock,
spin_lock_ctx_t* ctxrl);
bool proc_sq_resume (struct proc* proc, struct proc_sq_entry* sq_entry);
#endif // _KERNEL_PROC_SUSPENTION_Q_H

View File

@@ -7,3 +7,4 @@ include uACPI/src.mk
include irq/src.mk include irq/src.mk
include rd/src.mk include rd/src.mk
include proc/src.mk include proc/src.mk
include syscall/src.mk

View File

@@ -3,15 +3,15 @@
#include <sys/irq.h> #include <sys/irq.h>
#include <sys/spin_lock.h> #include <sys/spin_lock.h>
void spin_lock (spin_lock_t* sl) { void spin_lock (spin_lock_t* sl, spin_lock_ctx_t* ctx) {
irq_save (); irq_save (ctx);
while (atomic_flag_test_and_set_explicit (sl, memory_order_acquire)) while (atomic_flag_test_and_set_explicit (sl, memory_order_acquire))
spin_lock_relax (); spin_lock_relax ();
} }
void spin_unlock (spin_lock_t* sl) { void spin_unlock (spin_lock_t* sl, spin_lock_ctx_t* ctx) {
atomic_flag_clear_explicit (sl, memory_order_release); atomic_flag_clear_explicit (sl, memory_order_release);
irq_restore (); irq_restore (ctx);
} }

Some files were not shown because too many files have changed in this diff Show More