[docs] start building more comprehensive documentation
About half and half AI generated stuff and manually curated, but it's a pretty good start. Add a helpful markdown addon to the workspace.
This commit is contained in:
42
README.md
42
README.md
@@ -2,9 +2,13 @@
|
|||||||
|
|
||||||
The LK kernel is an SMP-aware kernel designed for small systems ported to a variety of platforms and cpu architectures.
|
The LK kernel is an SMP-aware kernel designed for small systems ported to a variety of platforms and cpu architectures.
|
||||||
|
|
||||||
|
It is used in a variety of open source and closed source projects, notably the bootloader for a lot of Android phones of various make.
|
||||||
|
|
||||||
See https://github.com/littlekernel/lk for the latest version.
|
See https://github.com/littlekernel/lk for the latest version.
|
||||||
|
|
||||||
### High Level Features
|
For comprehensive documentation, see [Index](docs/index.md).
|
||||||
|
|
||||||
|
## High Level Features
|
||||||
|
|
||||||
- Fully-reentrant multi-threaded preemptive kernel
|
- Fully-reentrant multi-threaded preemptive kernel
|
||||||
- Portable to many 32 and 64 bit architectures
|
- Portable to many 32 and 64 bit architectures
|
||||||
@@ -12,7 +16,7 @@ See https://github.com/littlekernel/lk for the latest version.
|
|||||||
- Powerful modular build system
|
- Powerful modular build system
|
||||||
- Large number of utility components selectable at build time
|
- Large number of utility components selectable at build time
|
||||||
|
|
||||||
### Supported architectures
|
## Supported architectures
|
||||||
|
|
||||||
- ARM32
|
- ARM32
|
||||||
- Cortex-M class cores (armv6m - armv8m)
|
- Cortex-M class cores (armv6m - armv8m)
|
||||||
@@ -26,37 +30,3 @@ See https://github.com/littlekernel/lk for the latest version.
|
|||||||
- MIPS
|
- MIPS
|
||||||
- OpenRISC 1000
|
- OpenRISC 1000
|
||||||
- VAX (experimental)
|
- VAX (experimental)
|
||||||
|
|
||||||
### [TODO](docs/todo.md)
|
|
||||||
|
|
||||||
### To build and test for ARM64 on linux
|
|
||||||
|
|
||||||
1. install or build qemu. v2.4 and above is recommended.
|
|
||||||
2. install gcc for arm64 (see note 1)
|
|
||||||
3. run scripts/do-qemuarm -6 (from the lk directory)
|
|
||||||
4. you should see 'welcome to lk/MP'
|
|
||||||
|
|
||||||
This will get you a interactive prompt into LK which is running in qemu
|
|
||||||
arm64 machine 'virt' emulation. type 'help' for commands.
|
|
||||||
|
|
||||||
Note: for ubuntu x86-64
|
|
||||||
sudo apt-get install gcc-aarch64-linux-gnu
|
|
||||||
or fetch a prebuilt toolchain from
|
|
||||||
https://newos.org/toolchains/aarch64-elf-14.1.0-Linux-x86_64.tar.xz
|
|
||||||
|
|
||||||
|
|
||||||
### Building with LLVM-based toolchains
|
|
||||||
|
|
||||||
To build LK with a LLVM-based toolchain you will have to manually specify the compiler and linker in the environemnt.
|
|
||||||
Unlike GCC clang is a cross-compiler by default, so the target needs to be specified as part of the CC/CXX/CPP variables.
|
|
||||||
For example, assuming LLVM is in `/opt/llvm/bin/`, the following command will work to build for 64-bit RISC-V:
|
|
||||||
|
|
||||||
```
|
|
||||||
gmake qemu-virt-riscv64-test 'CC=/opt/llvm/bin/clang --target=riscv64-unknown-elf' 'CPP=/opt/llvm/bin/clang-cpp --target=riscv64-unknown-elf' 'CXX=/opt/llvm/bin/clang++ --target=riscv64-unknown-elf' 'LD=/opt/llvm/bin/ld.lld' TOOLCHAIN_PREFIX=/opt/llvm/bin/llvm- CPPFILT=/opt/llvm/bin/llvm-cxxfilt
|
|
||||||
```
|
|
||||||
TOOLCHAIN_PREFIX can be set to use the LLVM binutils, but due to the different naming of `llvm-cxxfilt` vs `c++filt` it needs to be set explicitly.
|
|
||||||
|
|
||||||
To build for AArch64 the command looks similar, just with a different `--target=` flag.
|
|
||||||
```
|
|
||||||
gmake qemu-virt-arm64-test 'CC=/opt/llvm/bin/clang --target=aarch64-unknown-elf' 'CPP=/opt/llvm/bin/clang-cpp --target=aarch64-unknown-elf' 'CXX=/opt/llvm/bin/clang++ --target=aarch64-unknown-elf' 'LD=/opt/llvm/bin/ld.lld' TOOLCHAIN_PREFIX=/opt/llvm/bin/llvm- CPPFILT=/opt/llvm/bin/llvm-cxxfilt
|
|
||||||
```
|
|
||||||
|
|||||||
609
docs/blocking_primitives.md
Normal file
609
docs/blocking_primitives.md
Normal file
@@ -0,0 +1,609 @@
|
|||||||
|
# LK Kernel Blocking Primitives
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The LK kernel provides a comprehensive set of synchronization primitives for coordinating access to shared resources and enabling communication between threads. These primitives are built on top of the wait queue system and provide different semantics for various synchronization patterns.
|
||||||
|
|
||||||
|
## Core Architecture
|
||||||
|
|
||||||
|
### Wait Queues Foundation
|
||||||
|
|
||||||
|
All blocking primitives in LK are built upon wait queues (`wait_queue_t`), which provide the fundamental blocking and wakeup mechanisms:
|
||||||
|
|
||||||
|
- **Thread blocking**: Threads can be placed on wait queues and blocked until signaled
|
||||||
|
- **Timeout support**: All wait operations support optional timeouts
|
||||||
|
- **Wake semantics**: Support for waking one thread or all threads
|
||||||
|
- **Priority preservation**: Threads maintain their priority when blocked and resumed
|
||||||
|
|
||||||
|
### Locking Requirements
|
||||||
|
|
||||||
|
All synchronization primitives require the global thread lock (`thread_lock`) to be held when manipulating their internal state. This ensures atomic operations and prevents race conditions during state transitions.
|
||||||
|
|
||||||
|
## Synchronization Primitives
|
||||||
|
|
||||||
|
### 1. Mutexes
|
||||||
|
|
||||||
|
Mutexes provide exclusive access to shared resources with ownership semantics.
|
||||||
|
|
||||||
|
#### Structure
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct mutex {
|
||||||
|
uint32_t magic; // Magic number for validation
|
||||||
|
int count; // Contention counter
|
||||||
|
thread_t *holder; // Currently owning thread
|
||||||
|
wait_queue_t wait; // Wait queue for blocked threads
|
||||||
|
} mutex_t;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Key Properties
|
||||||
|
|
||||||
|
- **Ownership**: Only the thread that acquired the mutex can release it
|
||||||
|
- **Non-recursive**: A thread cannot acquire the same mutex multiple times
|
||||||
|
- **Thread context only**: Cannot be used from interrupt context
|
||||||
|
- **Priority inheritance**: Implicit through wait queue mechanism
|
||||||
|
|
||||||
|
#### API
|
||||||
|
|
||||||
|
```c
|
||||||
|
void mutex_init(mutex_t *m);
|
||||||
|
void mutex_destroy(mutex_t *m);
|
||||||
|
status_t mutex_acquire(mutex_t *m); // Infinite timeout
|
||||||
|
status_t mutex_acquire_timeout(mutex_t *m, lk_time_t timeout);
|
||||||
|
status_t mutex_release(mutex_t *m);
|
||||||
|
bool is_mutex_held(const mutex_t *m); // Check ownership
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Usage Example
|
||||||
|
|
||||||
|
```c
|
||||||
|
mutex_t resource_lock = MUTEX_INITIAL_VALUE(resource_lock);
|
||||||
|
|
||||||
|
void protected_function(void) {
|
||||||
|
status_t result = mutex_acquire(&resource_lock);
|
||||||
|
if (result == NO_ERROR) {
|
||||||
|
// Critical section - exclusive access to resource
|
||||||
|
access_shared_resource();
|
||||||
|
mutex_release(&resource_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### C++ Wrapper
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class Mutex {
|
||||||
|
public:
|
||||||
|
status_t acquire(lk_time_t timeout = INFINITE_TIME);
|
||||||
|
status_t release();
|
||||||
|
bool is_held();
|
||||||
|
};
|
||||||
|
|
||||||
|
class AutoLock {
|
||||||
|
public:
|
||||||
|
explicit AutoLock(mutex_t *mutex); // RAII lock acquisition
|
||||||
|
~AutoLock(); // Automatic release
|
||||||
|
void release(); // Early release
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Semaphores
|
||||||
|
|
||||||
|
Semaphores control access to a finite number of resources using a counter mechanism.
|
||||||
|
|
||||||
|
#### Structure
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct semaphore {
|
||||||
|
int magic; // Magic number for validation
|
||||||
|
int count; // Available resource count
|
||||||
|
wait_queue_t wait; // Wait queue for blocked threads
|
||||||
|
} semaphore_t;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Key Properties
|
||||||
|
|
||||||
|
- **Resource counting**: Tracks available resource instances
|
||||||
|
- **No ownership**: Any thread can post/wait on a semaphore
|
||||||
|
- **Thread context only**: Cannot be used from interrupt context
|
||||||
|
- **FIFO ordering**: Waiting threads are woken in FIFO order
|
||||||
|
|
||||||
|
#### API
|
||||||
|
|
||||||
|
```c
|
||||||
|
void sem_init(semaphore_t *sem, unsigned int value);
|
||||||
|
void sem_destroy(semaphore_t *sem);
|
||||||
|
status_t sem_wait(semaphore_t *sem); // Infinite timeout
|
||||||
|
status_t sem_timedwait(semaphore_t *sem, lk_time_t timeout);
|
||||||
|
status_t sem_trywait(semaphore_t *sem); // Non-blocking
|
||||||
|
int sem_post(semaphore_t *sem, bool resched); // Signal availability
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Usage Example
|
||||||
|
|
||||||
|
```c
|
||||||
|
semaphore_t resource_pool;
|
||||||
|
|
||||||
|
void init_resource_pool(void) {
|
||||||
|
sem_init(&resource_pool, 5); // 5 available resources
|
||||||
|
}
|
||||||
|
|
||||||
|
void use_resource(void) {
|
||||||
|
if (sem_wait(&resource_pool) == NO_ERROR) {
|
||||||
|
// Use one resource
|
||||||
|
use_shared_resource();
|
||||||
|
sem_post(&resource_pool, true); // Return resource
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Semaphore Semantics
|
||||||
|
|
||||||
|
- **Positive count**: Resources available, wait operations succeed immediately
|
||||||
|
- **Zero count**: No resources available, threads block on wait operations
|
||||||
|
- **Negative count**: Represents number of waiting threads
|
||||||
|
|
||||||
|
### 3. Events
|
||||||
|
|
||||||
|
Events provide signaling mechanisms for thread coordination and notification.
|
||||||
|
|
||||||
|
#### Structure
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct event {
|
||||||
|
int magic; // Magic number for validation
|
||||||
|
bool signaled; // Current signal state
|
||||||
|
uint flags; // Behavior flags
|
||||||
|
wait_queue_t wait; // Wait queue for blocked threads
|
||||||
|
} event_t;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Event Flags
|
||||||
|
|
||||||
|
- **EVENT_FLAG_AUTOUNSIGNAL**: Automatically clear signal after waking one thread
|
||||||
|
- **Default (no flags)**: Remain signaled until manually cleared
|
||||||
|
|
||||||
|
#### Key Properties
|
||||||
|
|
||||||
|
- **State-based**: Maintains signaled/unsignaled state
|
||||||
|
- **Flexible semantics**: Support for one-shot and persistent signaling
|
||||||
|
- **Interrupt safe**: Can be signaled from interrupt context (with resched=false)
|
||||||
|
- **No ownership**: Any thread can signal or wait
|
||||||
|
|
||||||
|
#### API
|
||||||
|
|
||||||
|
```c
|
||||||
|
void event_init(event_t *e, bool initial, uint flags);
|
||||||
|
void event_destroy(event_t *e);
|
||||||
|
status_t event_wait(event_t *e); // Infinite timeout
|
||||||
|
status_t event_wait_timeout(event_t *e, lk_time_t timeout);
|
||||||
|
status_t event_signal(event_t *e, bool reschedule);
|
||||||
|
status_t event_unsignal(event_t *e);
|
||||||
|
bool event_initialized(event_t *e);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Usage Examples
|
||||||
|
|
||||||
|
#### One-shot Event (Auto-unsignal)
|
||||||
|
|
||||||
|
```c
|
||||||
|
event_t completion_event;
|
||||||
|
|
||||||
|
void init_completion(void) {
|
||||||
|
event_init(&completion_event, false, EVENT_FLAG_AUTOUNSIGNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait_for_completion(void) {
|
||||||
|
event_wait(&completion_event); // Blocks until signaled
|
||||||
|
}
|
||||||
|
|
||||||
|
void signal_completion(void) {
|
||||||
|
event_signal(&completion_event, true); // Wake one waiter
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Persistent Event
|
||||||
|
|
||||||
|
```c
|
||||||
|
event_t ready_event;
|
||||||
|
|
||||||
|
void init_ready_state(void) {
|
||||||
|
event_init(&ready_event, false, 0); // No auto-unsignal
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait_until_ready(void) {
|
||||||
|
event_wait(&ready_event); // All waiters proceed when signaled
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_ready(void) {
|
||||||
|
event_signal(&ready_event, true); // Wake all waiters
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_ready(void) {
|
||||||
|
event_unsignal(&ready_event); // Manual clear
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Ports
|
||||||
|
|
||||||
|
Ports provide message-passing communication channels between threads with buffering.
|
||||||
|
|
||||||
|
#### Structure
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct {
|
||||||
|
char value[PORT_PACKET_LEN]; // Packet payload
|
||||||
|
} port_packet_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void *ctx; // Associated context
|
||||||
|
port_packet_t packet; // Message data
|
||||||
|
} port_result_t;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Port Types
|
||||||
|
|
||||||
|
- **Write Port**: Sending side of a communication channel
|
||||||
|
- **Read Port**: Receiving side of a communication channel
|
||||||
|
- **Port Group**: Collection of read ports for multiplexed waiting
|
||||||
|
|
||||||
|
#### Port Modes
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef enum {
|
||||||
|
PORT_MODE_BROADCAST, // Multiple readers can connect
|
||||||
|
PORT_MODE_UNICAST, // Single reader connection
|
||||||
|
PORT_MODE_BIG_BUFFER // Larger internal buffer
|
||||||
|
} port_mode_t;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Key Properties
|
||||||
|
|
||||||
|
- **Named channels**: Ports are identified by name strings
|
||||||
|
- **Buffered communication**: Internal buffering prevents blocking on send
|
||||||
|
- **Flexible topology**: Broadcast and unicast communication patterns
|
||||||
|
- **Context passing**: Associate arbitrary context with messages
|
||||||
|
|
||||||
|
#### API
|
||||||
|
|
||||||
|
```c
|
||||||
|
void port_init(void);
|
||||||
|
status_t port_create(const char *name, port_mode_t mode, port_t *port);
|
||||||
|
status_t port_destroy(port_t port);
|
||||||
|
status_t port_open(const char *name, void *ctx, port_t *port);
|
||||||
|
status_t port_close(port_t port);
|
||||||
|
status_t port_write(port_t port, const port_packet_t *pk, size_t count);
|
||||||
|
status_t port_read(port_t port, port_result_t *result);
|
||||||
|
status_t port_read_timeout(port_t port, port_result_t *result, lk_time_t timeout);
|
||||||
|
|
||||||
|
// Port groups for multiplexed reading
|
||||||
|
status_t port_group_create(port_t *group);
|
||||||
|
status_t port_group_add(port_t group, port_t port);
|
||||||
|
status_t port_group_remove(port_t group, port_t port);
|
||||||
|
status_t port_group_read(port_t group, port_result_t *result);
|
||||||
|
status_t port_group_read_timeout(port_t group, port_result_t *result, lk_time_t timeout);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Usage Example
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Producer thread
|
||||||
|
void producer_thread(void *arg) {
|
||||||
|
port_t write_port;
|
||||||
|
port_create("data_channel", PORT_MODE_BROADCAST, &write_port);
|
||||||
|
|
||||||
|
port_packet_t packet;
|
||||||
|
// Fill packet with data
|
||||||
|
fill_packet_data(&packet);
|
||||||
|
|
||||||
|
port_write(write_port, &packet, 1);
|
||||||
|
port_destroy(write_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consumer thread
|
||||||
|
void consumer_thread(void *arg) {
|
||||||
|
port_t read_port;
|
||||||
|
port_open("data_channel", NULL, &read_port);
|
||||||
|
|
||||||
|
port_result_t result;
|
||||||
|
if (port_read_timeout(read_port, &result, 1000) == NO_ERROR) {
|
||||||
|
// Process received data
|
||||||
|
process_packet_data(&result.packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
port_close(read_port);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Spinlocks
|
||||||
|
|
||||||
|
Spinlocks provide lightweight mutual exclusion for short critical sections.
|
||||||
|
|
||||||
|
#### Structure
|
||||||
|
|
||||||
|
Architecture-specific spinlock implementation with common interface:
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef arch_spin_lock_t spin_lock_t;
|
||||||
|
typedef arch_spin_lock_saved_state_t spin_lock_saved_state_t;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Key Properties
|
||||||
|
|
||||||
|
- **Non-blocking**: Busy-wait instead of sleeping
|
||||||
|
- **Interrupt safe**: Can be used from interrupt context
|
||||||
|
- **Short critical sections**: Designed for brief atomic operations
|
||||||
|
- **No recursion**: Cannot be acquired recursively
|
||||||
|
- **Priority inversion risk**: Can cause priority inversion
|
||||||
|
|
||||||
|
#### API
|
||||||
|
|
||||||
|
```c
|
||||||
|
void spin_lock_init(spin_lock_t *lock);
|
||||||
|
void spin_lock(spin_lock_t *lock); // Assumes interrupts disabled
|
||||||
|
int spin_trylock(spin_lock_t *lock); // Non-blocking attempt
|
||||||
|
void spin_unlock(spin_lock_t *lock);
|
||||||
|
bool spin_lock_held(spin_lock_t *lock);
|
||||||
|
|
||||||
|
// IRQ-safe variants
|
||||||
|
void spin_lock_irqsave(spin_lock_t *lock, spin_lock_saved_state_t *state);
|
||||||
|
void spin_unlock_irqrestore(spin_lock_t *lock, spin_lock_saved_state_t state);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Usage Example
|
||||||
|
|
||||||
|
```c
|
||||||
|
spin_lock_t hardware_lock = SPIN_LOCK_INITIAL_VALUE;
|
||||||
|
|
||||||
|
void access_hardware_register(void) {
|
||||||
|
spin_lock_saved_state_t state;
|
||||||
|
spin_lock_irqsave(&hardware_lock, &state);
|
||||||
|
|
||||||
|
// Brief critical section
|
||||||
|
write_hardware_register(value);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&hardware_lock, state);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### C++ Wrapper
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class SpinLock {
|
||||||
|
public:
|
||||||
|
void lock();
|
||||||
|
int trylock();
|
||||||
|
void unlock();
|
||||||
|
bool is_held();
|
||||||
|
void lock_irqsave(spin_lock_saved_state_t *state);
|
||||||
|
void unlock_irqrestore(spin_lock_saved_state_t state);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AutoSpinLock {
|
||||||
|
public:
|
||||||
|
explicit AutoSpinLock(spin_lock_t *lock); // RAII with IRQ save
|
||||||
|
~AutoSpinLock();
|
||||||
|
void release();
|
||||||
|
};
|
||||||
|
|
||||||
|
class AutoSpinLockNoIrqSave {
|
||||||
|
public:
|
||||||
|
explicit AutoSpinLockNoIrqSave(spin_lock_t *lock); // RAII without IRQ save
|
||||||
|
~AutoSpinLockNoIrqSave();
|
||||||
|
void release();
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Topics
|
||||||
|
|
||||||
|
### Wait Queues
|
||||||
|
|
||||||
|
The foundation primitive underlying all blocking synchronization:
|
||||||
|
|
||||||
|
#### API
|
||||||
|
|
||||||
|
```c
|
||||||
|
void wait_queue_init(wait_queue_t *wait);
|
||||||
|
void wait_queue_destroy(wait_queue_t *wait, bool reschedule);
|
||||||
|
status_t wait_queue_block(wait_queue_t *wait, lk_time_t timeout);
|
||||||
|
int wait_queue_wake_one(wait_queue_t *wait, bool reschedule, status_t error);
|
||||||
|
int wait_queue_wake_all(wait_queue_t *wait, bool reschedule, status_t error);
|
||||||
|
status_t thread_unblock_from_wait_queue(thread_t *t, status_t error);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Timeout Handling
|
||||||
|
|
||||||
|
- **INFINITE_TIME**: Block indefinitely until signaled
|
||||||
|
- **0**: Return immediately with ERR_TIMED_OUT if would block
|
||||||
|
- **Positive values**: Block for specified milliseconds, return ERR_TIMED_OUT on expiration
|
||||||
|
|
||||||
|
### Priority Inheritance
|
||||||
|
|
||||||
|
While not explicitly implemented, the LK synchronization primitives provide implicit priority inheritance through the wait queue mechanism:
|
||||||
|
|
||||||
|
- **FIFO ordering**: Threads are generally woken in the order they were blocked
|
||||||
|
- **Priority-based scheduling**: High-priority threads are scheduled immediately when unblocked
|
||||||
|
- **Head insertion**: Newly unblocked threads are inserted at the head of their priority run queue
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
#### Common Error Codes
|
||||||
|
|
||||||
|
- **NO_ERROR**: Operation successful
|
||||||
|
- **ERR_TIMED_OUT**: Operation timed out
|
||||||
|
- **ERR_NOT_READY**: Resource not available (try operations)
|
||||||
|
- **ERR_INVALID_ARGS**: Invalid parameters
|
||||||
|
- **ERR_OBJECT_DESTROYED**: Primitive was destroyed while waiting
|
||||||
|
- **ERR_NOT_ENOUGH_BUFFER**: Insufficient buffer space (ports)
|
||||||
|
- **ERR_THREAD_DETACHED**: Thread was detached (join operations)
|
||||||
|
|
||||||
|
#### Panic Conditions
|
||||||
|
|
||||||
|
Debug builds include assertions that will panic on:
|
||||||
|
|
||||||
|
- **Double acquisition**: Attempting to acquire a mutex already owned
|
||||||
|
- **Invalid release**: Releasing a mutex not owned by current thread
|
||||||
|
- **Magic number corruption**: Corrupted primitive structures
|
||||||
|
- **Invalid state transitions**: Inconsistent internal state
|
||||||
|
|
||||||
|
### SMP Considerations
|
||||||
|
|
||||||
|
#### CPU Wakeup
|
||||||
|
|
||||||
|
When threads are unblocked, the scheduler automatically handles CPU wakeup:
|
||||||
|
|
||||||
|
- **Pinned threads**: Wake the specific CPU where the thread is pinned
|
||||||
|
- **Unpinned threads**: Wake all CPUs except the local one
|
||||||
|
- **Load balancing**: Distribution across available CPUs
|
||||||
|
|
||||||
|
#### Memory Ordering
|
||||||
|
|
||||||
|
- **Architecture barriers**: Spinlocks include appropriate memory barriers
|
||||||
|
- **Cache coherency**: Hardware ensures cache coherence for shared data
|
||||||
|
- **Atomic operations**: Underlying atomic primitives ensure consistency
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### Choosing the Right Primitive
|
||||||
|
|
||||||
|
1. **Mutexes**: For exclusive access to shared resources with ownership
|
||||||
|
2. **Semaphores**: For counting resources or limiting concurrency
|
||||||
|
3. **Events**: For signaling and notification between threads
|
||||||
|
4. **Ports**: For message passing and producer-consumer patterns
|
||||||
|
5. **Spinlocks**: For very short critical sections or interrupt contexts
|
||||||
|
|
||||||
|
### Performance Considerations
|
||||||
|
|
||||||
|
#### Mutex vs Spinlock Trade-offs
|
||||||
|
|
||||||
|
**Use Mutexes when:**
|
||||||
|
|
||||||
|
- Critical sections may be long
|
||||||
|
- Thread context is available
|
||||||
|
- Resource contention is possible
|
||||||
|
- Priority inheritance is needed
|
||||||
|
|
||||||
|
**Use Spinlocks when:**
|
||||||
|
|
||||||
|
- Critical sections are very short (< 100 cycles)
|
||||||
|
- Interrupt context or high-priority threads
|
||||||
|
- Low contention expected
|
||||||
|
- Immediate response required
|
||||||
|
|
||||||
|
#### Avoiding Priority Inversion
|
||||||
|
|
||||||
|
- **Keep critical sections short**: Minimize time holding locks
|
||||||
|
- **Consistent lock ordering**: Prevent deadlock with multiple locks
|
||||||
|
- **Avoid nested locking**: Reduce complexity and deadlock risk
|
||||||
|
- **Use appropriate primitives**: Match primitive to use case
|
||||||
|
|
||||||
|
### Common Patterns
|
||||||
|
|
||||||
|
#### Producer-Consumer
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Using semaphores
|
||||||
|
semaphore_t empty_slots, full_slots;
|
||||||
|
mutex_t buffer_lock;
|
||||||
|
|
||||||
|
void producer(void) {
|
||||||
|
sem_wait(&empty_slots); // Wait for space
|
||||||
|
mutex_acquire(&buffer_lock); // Protect buffer
|
||||||
|
add_to_buffer(data);
|
||||||
|
mutex_release(&buffer_lock);
|
||||||
|
sem_post(&full_slots, true); // Signal data available
|
||||||
|
}
|
||||||
|
|
||||||
|
void consumer(void) {
|
||||||
|
sem_wait(&full_slots); // Wait for data
|
||||||
|
mutex_acquire(&buffer_lock); // Protect buffer
|
||||||
|
data = remove_from_buffer();
|
||||||
|
mutex_release(&buffer_lock);
|
||||||
|
sem_post(&empty_slots, true); // Signal space available
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Event Notification
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Using events for completion notification
|
||||||
|
event_t work_complete;
|
||||||
|
|
||||||
|
void worker_thread(void) {
|
||||||
|
// Perform work
|
||||||
|
do_work();
|
||||||
|
|
||||||
|
// Signal completion
|
||||||
|
event_signal(&work_complete, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void coordinator_thread(void) {
|
||||||
|
// Start work
|
||||||
|
start_work();
|
||||||
|
|
||||||
|
// Wait for completion
|
||||||
|
event_wait(&work_complete);
|
||||||
|
|
||||||
|
// Process results
|
||||||
|
process_results();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Resource Pool Management
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Using semaphores for resource pool
|
||||||
|
semaphore_t resource_pool;
|
||||||
|
mutex_t pool_lock;
|
||||||
|
resource_t *resources[MAX_RESOURCES];
|
||||||
|
|
||||||
|
void init_pool(void) {
|
||||||
|
sem_init(&resource_pool, MAX_RESOURCES);
|
||||||
|
mutex_init(&pool_lock);
|
||||||
|
// Initialize resource array
|
||||||
|
}
|
||||||
|
|
||||||
|
resource_t *acquire_resource(void) {
|
||||||
|
if (sem_wait(&resource_pool) == NO_ERROR) {
|
||||||
|
mutex_acquire(&pool_lock);
|
||||||
|
resource_t *res = find_free_resource();
|
||||||
|
mark_resource_used(res);
|
||||||
|
mutex_release(&pool_lock);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void release_resource(resource_t *res) {
|
||||||
|
mutex_acquire(&pool_lock);
|
||||||
|
mark_resource_free(res);
|
||||||
|
mutex_release(&pool_lock);
|
||||||
|
sem_post(&resource_pool, true);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integration with Threading System
|
||||||
|
|
||||||
|
### Lock Context Requirements
|
||||||
|
|
||||||
|
All blocking primitives (except spinlocks) require:
|
||||||
|
|
||||||
|
- **Thread context**: Cannot be used from interrupt handlers
|
||||||
|
- **Interrupts enabled**: Should not be called with interrupts disabled
|
||||||
|
- **No spinlocks held**: Deadlock risk if thread spinlocks are held
|
||||||
|
|
||||||
|
### Scheduler Integration
|
||||||
|
|
||||||
|
- **Automatic blocking**: Primitives automatically use `thread_block()`
|
||||||
|
- **Priority preservation**: Blocked threads maintain their priority
|
||||||
|
- **Fair scheduling**: FIFO ordering within priority levels
|
||||||
|
- **Efficient wakeup**: Minimal overhead for thread state transitions
|
||||||
|
|
||||||
|
### Memory Management
|
||||||
|
|
||||||
|
- **Static initialization**: All primitives support compile-time initialization
|
||||||
|
- **Dynamic allocation**: Runtime initialization for dynamically allocated primitives
|
||||||
|
- **Cleanup requirements**: Proper destruction prevents resource leaks
|
||||||
|
- **Magic number validation**: Debug builds validate primitive integrity
|
||||||
|
|
||||||
|
This comprehensive set of blocking primitives provides the foundation for safe, efficient multi-threaded programming in the LK kernel, supporting everything from simple mutual exclusion to complex communication patterns.
|
||||||
@@ -19,7 +19,7 @@ scripts/fetch-toolchains.py --prefix riscv64-elf
|
|||||||
```
|
```
|
||||||
4- Add toolchain to PATH
|
4- Add toolchain to PATH
|
||||||
```
|
```
|
||||||
export PATH=$PWD/toolchain/riscv64-elf-14.2.0-Linux-x86_64/bin:$PATH
|
export PATH=$PWD/toolchain/riscv64-elf-15.1.0-Linux-x86_64/bin:$PATH
|
||||||
```
|
```
|
||||||
5- Find available project
|
5- Find available project
|
||||||
```
|
```
|
||||||
|
|||||||
126
docs/index.md
Normal file
126
docs/index.md
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
# LK (Little Kernel) Documentation
|
||||||
|
|
||||||
|
Welcome to the LK (Little Kernel) documentation. LK is a small operating system designed for embedded systems, offering a lightweight kernel with threading, memory management, and device drivers.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
### Getting Started
|
||||||
|
- [Getting Started Guide](getting_started.md) - Quick start guide for building and running LK
|
||||||
|
- [TODO List](todo.md) - Current development tasks and known issues
|
||||||
|
|
||||||
|
### Core Kernel Documentation
|
||||||
|
- [Threading and Scheduler System](threading_and_scheduler.md) - Comprehensive guide to LK's preemptive multithreading system
|
||||||
|
- [Blocking Primitives](blocking_primitives.md) - Synchronization primitives (mutexes, semaphores, events, ports, spinlocks)
|
||||||
|
- [VMM Overview](vmm_overview.md) - Virtual Memory Management system architecture and implementation
|
||||||
|
- [Source Tree Structure](source_tree_structure.md) - Organization and structure of the LK source code repository
|
||||||
|
|
||||||
|
### Platform-Specific Documentation
|
||||||
|
|
||||||
|
#### ARM Fixed Virtual Platform (FVP)
|
||||||
|
- [FVP Base - How to Run](fvp-base/how_to_run.md) - Build and run instructions for FVP Base platform
|
||||||
|
- [FVP Base - Internal Overview](fvp-base/internal.md) - Boot sequence, memory layout, and peripheral details
|
||||||
|
|
||||||
|
### Development and Testing
|
||||||
|
- [Network Setup for QEMU](lk_tap.md) - Setting up tun/tap networking for QEMU testing
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
LK is designed as a modular kernel with the following key components:
|
||||||
|
|
||||||
|
- **Threading System**: Preemptive multithreading with priority-based scheduling
|
||||||
|
- **Memory Management**: Physical memory management (PMM) and virtual memory management (VMM)
|
||||||
|
- **Synchronization**: Comprehensive set of blocking primitives for thread coordination
|
||||||
|
- **Device Support**: Drivers for common embedded peripherals
|
||||||
|
- **Platform Abstraction**: Support for multiple CPU architectures and platforms
|
||||||
|
|
||||||
|
## Supported Architectures
|
||||||
|
|
||||||
|
- ARM (32-bit and 64-bit)
|
||||||
|
- RISC-V (32-bit and 64-bit)
|
||||||
|
- x86/x86_64
|
||||||
|
- Motorola 68000
|
||||||
|
- Microblaze
|
||||||
|
- MIPS
|
||||||
|
- OpenRISC 1000
|
||||||
|
- VAX (experimental)
|
||||||
|
|
||||||
|
## Supported Platforms
|
||||||
|
|
||||||
|
- QEMU virtual platforms (ARM, RISC-V, x86)
|
||||||
|
- ARM Fixed Virtual Platform (FVP)
|
||||||
|
- Various embedded development boards
|
||||||
|
- Custom hardware platforms
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
- **Real-time capable**: Priority-based preemptive scheduler with real-time thread support
|
||||||
|
- **Memory protection**: Virtual memory management with address space isolation
|
||||||
|
- **SMP support**: Symmetric multiprocessing for multi-core systems
|
||||||
|
- **Modular design**: Component-based architecture for easy customization
|
||||||
|
- **Small footprint**: Designed for resource-constrained embedded systems
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
LK is an open-source project. For the latest development status and to contribute:
|
||||||
|
|
||||||
|
- [GitHub Repository](https://github.com/littlekernel/lk)
|
||||||
|
- [Issue Tracker](https://github.com/littlekernel/lk/issues)
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
1. Install or build a recent version of qemu. Make sure qemu-system-riscv64 is in your path.
|
||||||
|
|
||||||
|
2. Clone the repository:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/littlekernel/lk
|
||||||
|
cd lk
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Download a toolchain:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scripts/fetch-toolchains.py --prefix riscv64-elf
|
||||||
|
export PATH=$PWD/toolchain/riscv64-elf-15.1.0-Linux-x86_64/bin:$PATH
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Build and run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make qemu-virt-riscv64-test
|
||||||
|
scripts/do-qemuriscv -6S
|
||||||
|
```
|
||||||
|
|
||||||
|
For detailed instructions, see the [Getting Started Guide](getting_started.md).
|
||||||
|
|
||||||
|
This will get you a interactive prompt into LK which is running in qemu
|
||||||
|
arm64 machine 'virt' emulation. type 'help' for commands.
|
||||||
|
|
||||||
|
Note: for ubuntu x86-64
|
||||||
|
sudo apt-get install gcc-aarch64-linux-gnu
|
||||||
|
or fetch a prebuilt toolchain from
|
||||||
|
https://newos.org/toolchains/aarch64-elf-15.1.0-Linux-x86_64.tar.xz
|
||||||
|
|
||||||
|
### Building with LLVM-based toolchains
|
||||||
|
|
||||||
|
To build LK with a LLVM-based toolchain you will have to manually specify the compiler and linker in the environemnt.
|
||||||
|
Unlike GCC clang is a cross-compiler by default, so the target needs to be specified as part of the CC/CXX/CPP variables.
|
||||||
|
For example, assuming LLVM is in `/opt/llvm/bin/`, the following command will work to build for 64-bit RISC-V:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gmake qemu-virt-riscv64-test 'CC=/opt/llvm/bin/clang --target=riscv64-unknown-elf' 'CPP=/opt/llvm/bin/clang-cpp --target=riscv64-unknown-elf' 'CXX=/opt/llvm/bin/clang++ --target=riscv64-unknown-elf' 'LD=/opt/llvm/bin/ld.lld' TOOLCHAIN_PREFIX=/opt/llvm/bin/llvm- CPPFILT=/opt/llvm/bin/llvm-cxxfilt
|
||||||
|
```
|
||||||
|
|
||||||
|
TOOLCHAIN_PREFIX can be set to use the LLVM binutils, but due to the different naming of `llvm-cxxfilt` vs `c++filt` it needs to be set explicitly.
|
||||||
|
|
||||||
|
To build for AArch64 the command looks similar, just with a different `--target=` flag.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gmake qemu-virt-arm64-test 'CC=/opt/llvm/bin/clang --target=aarch64-unknown-elf' 'CPP=/opt/llvm/bin/clang-cpp --target=aarch64-unknown-elf' 'CXX=/opt/llvm/bin/clang++ --target=aarch64-unknown-elf' 'LD=/opt/llvm/bin/ld.lld' TOOLCHAIN_PREFIX=/opt/llvm/bin/llvm- CPPFILT=/opt/llvm/bin/llvm-cxxfilt
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [TODO](todo.md)
|
||||||
|
|
||||||
|
*This documentation covers the core aspects of the LK kernel. For specific implementation details, refer to the source code and individual documentation files.*
|
||||||
177
docs/source_tree_structure.md
Normal file
177
docs/source_tree_structure.md
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
# LK Source Tree Structure
|
||||||
|
|
||||||
|
This document describes the organization and structure of the Little Kernel (LK) source code repository.
|
||||||
|
|
||||||
|
## Top-Level Directories
|
||||||
|
|
||||||
|
### `/arch/`
|
||||||
|
Architecture-specific code for different CPU architectures:
|
||||||
|
- `arm/` - ARM 32-bit and cortex-m support
|
||||||
|
- `arm64/` - ARM 32-bit and 64-bit support
|
||||||
|
- `riscv/` - RISC-V 32-bit and 64-bit support
|
||||||
|
- `m68k/` - Motorola 68000 support
|
||||||
|
- `microblaze/` - Microblaze support
|
||||||
|
- `mips/` - MIPS support
|
||||||
|
- `or1k/` - OpenRISC 1000 support
|
||||||
|
- `x86/` - x86 and x86_64 support
|
||||||
|
- `vax/` - VAX support (experimental)
|
||||||
|
|
||||||
|
Each architecture directory contains:
|
||||||
|
- CPU-specific initialization code
|
||||||
|
- Memory management unit (MMU) setup
|
||||||
|
- Exception and interrupt handling
|
||||||
|
- Low level context switching and thread support
|
||||||
|
- Architecture-specific assembly code
|
||||||
|
|
||||||
|
### `/kernel/`
|
||||||
|
Core kernel functionality:
|
||||||
|
- `mutex.c`, `semaphore.c` - Synchronization primitives
|
||||||
|
- `mp.c` - Symmetric multiprocessing support
|
||||||
|
- `timer.c` - Kernel timers
|
||||||
|
- `event.c` - Event handling
|
||||||
|
- `port.c` - Message passing
|
||||||
|
- `thread.c` - Threading system and scheduler
|
||||||
|
- `vm/` - Virtual memory management
|
||||||
|
- `novm/` - Memory management for architectures without an mmu
|
||||||
|
|
||||||
|
### `/lib/`
|
||||||
|
Library and utility code:
|
||||||
|
- `libc/` - Basic C library functions
|
||||||
|
- `libm/` - Math library
|
||||||
|
- `bio/` - Block I/O layer
|
||||||
|
- `fs/` - File system support
|
||||||
|
- `heap/` - Dynamic memory allocation
|
||||||
|
- `io/` - I/O utilities
|
||||||
|
- `console/` - Console and debugging support
|
||||||
|
- `cksum/` - Checksum algorithms
|
||||||
|
- `debug/` - Debugging utilities
|
||||||
|
- many more helper libraries and utility routines
|
||||||
|
|
||||||
|
### `/dev/`
|
||||||
|
Device drivers organized by category:
|
||||||
|
- `bus/` - Bus drivers (PCI, etc.)
|
||||||
|
- `display/` - Display and graphics drivers
|
||||||
|
- `interrupt/` - Interrupt controller drivers
|
||||||
|
- `net/` - Network device drivers
|
||||||
|
- `timer/` - Hardware timer drivers
|
||||||
|
- `uart/` - Serial port drivers
|
||||||
|
- `usb/` - USB device side drivers
|
||||||
|
- `virtio/` - VirtIO device support
|
||||||
|
|
||||||
|
### `/platform/`
|
||||||
|
Platform specific code (not all listed):
|
||||||
|
- `qemu-*/` - QEMU virtual machine platforms
|
||||||
|
- `fvp-base/` - ARM Fixed Virtual Platform support
|
||||||
|
- `stm32*/` - Support for various STM32 SOCs
|
||||||
|
- Hardware-specific board support packages
|
||||||
|
- Platform initialization and device tree handling
|
||||||
|
|
||||||
|
### `/target/`
|
||||||
|
Target specific code:
|
||||||
|
- A specific board or combination of platform and architecture. For example:
|
||||||
|
- `stm32f4-discovery`
|
||||||
|
- `pc-x86`
|
||||||
|
- `pico`
|
||||||
|
|
||||||
|
### `/app/`
|
||||||
|
Application and test code:
|
||||||
|
- Test applications for kernel functionality
|
||||||
|
- Sample applications demonstrating LK features
|
||||||
|
- Shell and command-line interface
|
||||||
|
- Network applications and services
|
||||||
|
- Location where general embedded device logic go
|
||||||
|
|
||||||
|
### `/external/`
|
||||||
|
Third-party code and libraries:
|
||||||
|
- External dependencies
|
||||||
|
- Imported code from other projects
|
||||||
|
- Third-party drivers and utilities
|
||||||
|
|
||||||
|
### `/scripts/`
|
||||||
|
Build and development scripts:
|
||||||
|
- `do-qemu*` - QEMU launch scripts for different architectures
|
||||||
|
- `fetch-toolchains.py` - Toolchain download utility
|
||||||
|
- Build automation and testing scripts
|
||||||
|
|
||||||
|
### `/tools/`
|
||||||
|
Development and build tools:
|
||||||
|
- Custom build utilities
|
||||||
|
- Code generation tools
|
||||||
|
- Development helpers
|
||||||
|
|
||||||
|
### `/docs/`
|
||||||
|
Documentation:
|
||||||
|
- Architecture documentation
|
||||||
|
- Platform-specific guides
|
||||||
|
- API documentation
|
||||||
|
- Development guides
|
||||||
|
|
||||||
|
## Key Files
|
||||||
|
|
||||||
|
### `/make/`
|
||||||
|
Build system components:
|
||||||
|
- Makefile fragments for different components
|
||||||
|
- Architecture and platform build rules
|
||||||
|
- Toolchain configuration
|
||||||
|
|
||||||
|
### Root Directory Files
|
||||||
|
- `Makefile` - Main build system entry point
|
||||||
|
- `engine.mk` - Core build engine
|
||||||
|
- `project/*.mk` - Project configuration files
|
||||||
|
- `target/*.mk` - Target-specific build configurations
|
||||||
|
|
||||||
|
## Build System Organization
|
||||||
|
|
||||||
|
The LK build system is modular and component-based:
|
||||||
|
|
||||||
|
1. **Projects** (`project/*.mk`) define high-level configurations
|
||||||
|
2. **Targets** (`target/*.mk`) specify platform and architecture combinations
|
||||||
|
3. **Modules** (scattered throughout source tree) define buildable components
|
||||||
|
4. **Rules** (`make/*.mk`) provide build logic for different file types
|
||||||
|
|
||||||
|
Each directory can contain:
|
||||||
|
- `rules.mk` - Module definition and build rules
|
||||||
|
- Source files (`.c`, `.cpp`, `.S`)
|
||||||
|
- Local headers and includes
|
||||||
|
|
||||||
|
## Module System
|
||||||
|
|
||||||
|
LK uses a module-based build system where each component is defined as a module:
|
||||||
|
|
||||||
|
```make
|
||||||
|
# Example module definition in rules.mk
|
||||||
|
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||||
|
MODULE := $(LOCAL_DIR)
|
||||||
|
|
||||||
|
MODULE_SRCS += \
|
||||||
|
$(LOCAL_DIR)/file1.c \
|
||||||
|
$(LOCAL_DIR)/file2.c
|
||||||
|
|
||||||
|
MODULE_DEPS += \
|
||||||
|
lib/libc \
|
||||||
|
kernel
|
||||||
|
|
||||||
|
include make/module.mk
|
||||||
|
```
|
||||||
|
|
||||||
|
This allows for:
|
||||||
|
- Selective compilation of components
|
||||||
|
- Dependency management between modules
|
||||||
|
- Platform-specific module inclusion/exclusion
|
||||||
|
- Modular kernel configuration
|
||||||
|
|
||||||
|
## Platform and Architecture Interaction
|
||||||
|
|
||||||
|
The relationship between platforms and architectures:
|
||||||
|
|
||||||
|
1. **Architecture** (`/arch/`) provides CPU-specific low-level support
|
||||||
|
2. **Platform** (`/platform/`) provides system-specific support (generally system-on-chip)
|
||||||
|
3. **Target** (`/target/`) combines a specific platform with board specific customization (GPIOs, etc)
|
||||||
|
4. **Project** (`/project/`) selects the target and a list of modules to include for a complete system
|
||||||
|
|
||||||
|
Example target `stm32f4-discovery` combines:
|
||||||
|
- Platform: `stm32f4xx` (support for STM32 F4 series SOC)
|
||||||
|
- Architecture: `arm` (32-bit ARM)
|
||||||
|
- Modules: Selected via project configuration
|
||||||
|
|
||||||
|
This structure allows LK to support diverse hardware while maintaining code reuse and modularity.
|
||||||
339
docs/threading_and_scheduler.md
Normal file
339
docs/threading_and_scheduler.md
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
# LK Threading and Scheduler System
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The LK kernel provides a comprehensive preemptive multithreading system with a priority-based scheduler. The threading system is designed to support both single-core and multi-core (SMP) systems with features including real-time scheduling, thread synchronization primitives, and comprehensive debugging capabilities.
|
||||||
|
|
||||||
|
## Core Components
|
||||||
|
|
||||||
|
### Thread Structure
|
||||||
|
|
||||||
|
Each thread in the system is represented by a `thread_t` structure containing:
|
||||||
|
|
||||||
|
- **Identification**: Magic number, name, and list membership
|
||||||
|
- **Scheduling State**: Priority, state, remaining quantum, and CPU affinity
|
||||||
|
- **Stack Management**: Stack pointer, size, and bounds checking
|
||||||
|
- **Synchronization**: Wait queue blocking and return codes
|
||||||
|
- **Architecture-specific**: CPU registers and context
|
||||||
|
- **Statistics**: Runtime statistics and performance counters (debug builds)
|
||||||
|
|
||||||
|
### Thread States
|
||||||
|
|
||||||
|
Threads can exist in one of the following states:
|
||||||
|
|
||||||
|
- **THREAD_SUSPENDED**: Newly created thread not yet running
|
||||||
|
- **THREAD_READY**: Thread ready to run, in the run queue
|
||||||
|
- **THREAD_RUNNING**: Currently executing thread
|
||||||
|
- **THREAD_BLOCKED**: Waiting on a synchronization primitive
|
||||||
|
- **THREAD_SLEEPING**: Sleeping for a specified time period
|
||||||
|
- **THREAD_DEATH**: Thread has exited and awaits cleanup
|
||||||
|
|
||||||
|
## Priority System
|
||||||
|
|
||||||
|
### Priority Levels
|
||||||
|
|
||||||
|
The scheduler uses a 32-level priority system (0-31):
|
||||||
|
|
||||||
|
- **LOWEST_PRIORITY (0)**: Minimum priority level
|
||||||
|
- **IDLE_PRIORITY (0)**: Reserved for idle threads
|
||||||
|
- **LOW_PRIORITY (8)**: Low priority tasks
|
||||||
|
- **DEFAULT_PRIORITY (16)**: Standard thread priority
|
||||||
|
- **HIGH_PRIORITY (24)**: High priority tasks
|
||||||
|
- **DPC_PRIORITY (30)**: Deferred Procedure Call priority
|
||||||
|
- **HIGHEST_PRIORITY (31)**: Maximum priority level
|
||||||
|
|
||||||
|
### Real-time Threads
|
||||||
|
|
||||||
|
Threads can be marked as real-time using `thread_set_real_time()`. Real-time threads:
|
||||||
|
|
||||||
|
- Bypass preemption timers when priority > DEFAULT_PRIORITY
|
||||||
|
- Receive preferential scheduling treatment
|
||||||
|
- Are tracked separately for CPU load balancing
|
||||||
|
|
||||||
|
## Scheduler Algorithm
|
||||||
|
|
||||||
|
### Run Queue Management
|
||||||
|
|
||||||
|
The scheduler maintains:
|
||||||
|
|
||||||
|
- **Per-priority run queues**: Array of linked lists, one per priority level
|
||||||
|
- **Run queue bitmap**: Bit field indicating which priority levels have ready threads
|
||||||
|
- **Round-robin within priority**: Threads of equal priority are time-sliced
|
||||||
|
|
||||||
|
### Thread Selection
|
||||||
|
|
||||||
|
The scheduler uses the following algorithm:
|
||||||
|
|
||||||
|
1. Find the highest priority level with ready threads (using `__builtin_clz`)
|
||||||
|
2. Select the first thread from that priority's run queue
|
||||||
|
3. For SMP systems, consider CPU affinity and pinning
|
||||||
|
4. Fall back to idle thread if no threads are ready
|
||||||
|
|
||||||
|
### Preemption and Time Slicing
|
||||||
|
|
||||||
|
- **Quantum-based preemption**: Non-real-time threads receive time slices
|
||||||
|
- **Timer-driven preemption**: Periodic timer interrupts check quantum expiration
|
||||||
|
- **Voluntary yielding**: Threads can yield CPU with `thread_yield()`
|
||||||
|
- **Priority preemption**: Higher priority threads immediately preempt lower priority ones
|
||||||
|
|
||||||
|
## SMP (Symmetric Multiprocessing) Support
|
||||||
|
|
||||||
|
### CPU Affinity
|
||||||
|
|
||||||
|
- **Pinned threads**: Can be restricted to specific CPUs
|
||||||
|
- **Load balancing**: Unpinned threads distribute across available CPUs
|
||||||
|
- **CPU state tracking**: Idle, busy, and real-time CPU states
|
||||||
|
|
||||||
|
### Inter-CPU Communication
|
||||||
|
|
||||||
|
- **Reschedule IPIs**: Wake up remote CPUs for newly ready threads
|
||||||
|
- **CPU-specific idle threads**: Each CPU has its own idle thread
|
||||||
|
- **Per-CPU preemption timers**: Independent timer management per CPU
|
||||||
|
|
||||||
|
## Thread Management API
|
||||||
|
|
||||||
|
### Thread Creation
|
||||||
|
|
||||||
|
```c
|
||||||
|
thread_t *thread_create(const char *name,
|
||||||
|
thread_start_routine entry,
|
||||||
|
void *arg,
|
||||||
|
int priority,
|
||||||
|
size_t stack_size);
|
||||||
|
|
||||||
|
thread_t *thread_create_etc(thread_t *t,
|
||||||
|
const char *name,
|
||||||
|
thread_start_routine entry,
|
||||||
|
void *arg,
|
||||||
|
int priority,
|
||||||
|
void *stack,
|
||||||
|
size_t stack_size);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Thread Control
|
||||||
|
|
||||||
|
```c
|
||||||
|
status_t thread_resume(thread_t *t); // Start suspended thread
|
||||||
|
void thread_exit(int retcode); // Terminate current thread
|
||||||
|
void thread_sleep(lk_time_t delay); // Sleep for specified time
|
||||||
|
status_t thread_join(thread_t *t, int *retcode, lk_time_t timeout);
|
||||||
|
status_t thread_detach(thread_t *t); // Detach thread for auto-cleanup
|
||||||
|
```
|
||||||
|
|
||||||
|
### Thread Properties
|
||||||
|
|
||||||
|
```c
|
||||||
|
void thread_set_name(const char *name); // Change thread name
|
||||||
|
void thread_set_priority(int priority); // Change thread priority
|
||||||
|
status_t thread_set_real_time(thread_t *t); // Mark as real-time
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scheduler Control
|
||||||
|
|
||||||
|
```c
|
||||||
|
void thread_yield(void); // Voluntary CPU yield
|
||||||
|
void thread_preempt(void); // Handle preemption
|
||||||
|
void thread_block(void); // Block current thread
|
||||||
|
void thread_unblock(thread_t *t, bool resched); // Unblock thread
|
||||||
|
```
|
||||||
|
|
||||||
|
## Wait Queues
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
|
||||||
|
Wait queues provide the foundation for thread synchronization primitives:
|
||||||
|
|
||||||
|
- Mutexes and semaphores
|
||||||
|
- Condition variables
|
||||||
|
- Event notifications
|
||||||
|
- Resource waiting
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
```c
|
||||||
|
void wait_queue_init(wait_queue_t *wait);
|
||||||
|
status_t wait_queue_block(wait_queue_t *wait, lk_time_t timeout);
|
||||||
|
int wait_queue_wake_one(wait_queue_t *wait, bool reschedule, status_t error);
|
||||||
|
int wait_queue_wake_all(wait_queue_t *wait, bool reschedule, status_t error);
|
||||||
|
void wait_queue_destroy(wait_queue_t *wait, bool reschedule);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Timeout Support
|
||||||
|
|
||||||
|
- **INFINITE_TIME**: Block indefinitely
|
||||||
|
- **0**: Return immediately with ERR_TIMED_OUT
|
||||||
|
- **Positive values**: Block for specified milliseconds
|
||||||
|
- **Timer-based wakeup**: Automatic unblocking on timeout
|
||||||
|
|
||||||
|
## Memory Management
|
||||||
|
|
||||||
|
### Stack Management
|
||||||
|
|
||||||
|
- **Automatic allocation**: Default stack allocation during thread creation
|
||||||
|
- **Custom stacks**: Support for user-provided stack memory
|
||||||
|
- **Stack bounds checking**: Debug-mode overflow detection (THREAD_STACK_BOUNDS_CHECK)
|
||||||
|
- **Stack usage tracking**: High-water mark monitoring (THREAD_STACK_HIGHWATER)
|
||||||
|
- **Delayed cleanup**: Safe stack deallocation after context switch
|
||||||
|
|
||||||
|
### Thread Structure Cleanup
|
||||||
|
|
||||||
|
- **Reference counting**: Automatic cleanup for detached threads
|
||||||
|
- **Join semantics**: Manual cleanup for joined threads
|
||||||
|
- **Delayed free**: Safe memory reclamation using heap_delayed_free()
|
||||||
|
|
||||||
|
## Thread Local Storage (TLS)
|
||||||
|
|
||||||
|
### Predefined TLS Slots
|
||||||
|
|
||||||
|
- **TLS_ENTRY_CONSOLE**: Current console context
|
||||||
|
- **TLS_ENTRY_UTHREAD**: User-mode thread context
|
||||||
|
- **TLS_ENTRY_LKUSER**: LK user library context
|
||||||
|
|
||||||
|
### TLS API
|
||||||
|
|
||||||
|
```c
|
||||||
|
uintptr_t tls_get(uint entry);
|
||||||
|
uintptr_t tls_set(uint entry, uintptr_t val);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging and Statistics
|
||||||
|
|
||||||
|
### Thread Statistics (THREAD_STATS)
|
||||||
|
|
||||||
|
Per-thread statistics:
|
||||||
|
|
||||||
|
- Total runtime
|
||||||
|
- Schedule count
|
||||||
|
- Last run timestamp
|
||||||
|
|
||||||
|
Per-CPU statistics:
|
||||||
|
|
||||||
|
- Idle time
|
||||||
|
- Context switches
|
||||||
|
- Preemptions and yields
|
||||||
|
- Reschedule IPIs
|
||||||
|
|
||||||
|
### Debug Features
|
||||||
|
|
||||||
|
- **Magic number validation**: Corruption detection
|
||||||
|
- **Stack overflow detection**: Bounds checking with guard pages
|
||||||
|
- **Thread state validation**: Assertion checking
|
||||||
|
- **Thread dumping**: Complete thread state inspection
|
||||||
|
|
||||||
|
### Debug API
|
||||||
|
|
||||||
|
```c
|
||||||
|
void dump_thread(thread_t *t); // Dump single thread
|
||||||
|
void dump_all_threads(void); // Dump all threads
|
||||||
|
void dump_threads_stats(void); // Dump statistics
|
||||||
|
```
|
||||||
|
|
||||||
|
## Initialization
|
||||||
|
|
||||||
|
### Early Initialization
|
||||||
|
|
||||||
|
```c
|
||||||
|
void thread_init_early(void);
|
||||||
|
```
|
||||||
|
|
||||||
|
- Initialize global data structures
|
||||||
|
- Create bootstrap thread
|
||||||
|
- Set up run queues and thread list
|
||||||
|
|
||||||
|
### Full Initialization
|
||||||
|
|
||||||
|
```c
|
||||||
|
void thread_init(void);
|
||||||
|
```
|
||||||
|
|
||||||
|
- Initialize preemption timers
|
||||||
|
- Complete threading system setup
|
||||||
|
|
||||||
|
### SMP Initialization
|
||||||
|
|
||||||
|
```c
|
||||||
|
void thread_secondary_cpu_init_early(void); // Per-CPU early init
|
||||||
|
void thread_secondary_cpu_entry(void); // Secondary CPU entry point
|
||||||
|
void thread_become_idle(void); // Become idle thread
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Considerations
|
||||||
|
|
||||||
|
### Scheduler Overhead
|
||||||
|
|
||||||
|
- **O(1) thread selection**: Bitmap-based priority queue
|
||||||
|
- **Minimal context switch overhead**: Architecture-optimized switching
|
||||||
|
- **Lockless fast paths**: Reduced lock contention where possible
|
||||||
|
|
||||||
|
### SMP Scalability
|
||||||
|
|
||||||
|
- **CPU-local data structures**: Reduced cache line bouncing
|
||||||
|
- **Intelligent load balancing**: CPU affinity and pinning support
|
||||||
|
- **Efficient IPI usage**: Targeted CPU wakeups
|
||||||
|
|
||||||
|
### Real-time Responsiveness
|
||||||
|
|
||||||
|
- **Priority inheritance**: Through wait queue mechanisms
|
||||||
|
- **Bounded latency**: Real-time thread preemption guarantees
|
||||||
|
- **Timer precision**: High-resolution timer support
|
||||||
|
|
||||||
|
## Integration Points
|
||||||
|
|
||||||
|
### Architecture Layer
|
||||||
|
|
||||||
|
- **arch_thread_initialize()**: Architecture-specific thread setup
|
||||||
|
- **arch_context_switch()**: Low-level context switching
|
||||||
|
- **arch_get/set_current_thread()**: Current thread management
|
||||||
|
|
||||||
|
### Platform Layer
|
||||||
|
|
||||||
|
- **Timer integration**: Preemption timer management
|
||||||
|
- **Power management**: CPU idle state handling
|
||||||
|
- **Debug LED control**: Visual debugging support
|
||||||
|
|
||||||
|
### Virtual Memory
|
||||||
|
|
||||||
|
- **Address space switching**: VMM integration for process isolation
|
||||||
|
- **Stack allocation**: Virtual memory for thread stacks
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
### Common Error Codes
|
||||||
|
|
||||||
|
- **ERR_TIMED_OUT**: Wait operation timeout
|
||||||
|
- **ERR_NOT_BLOCKED**: Thread not in blocked state
|
||||||
|
- **ERR_THREAD_DETACHED**: Operation on detached thread
|
||||||
|
- **ERR_OBJECT_DESTROYED**: Wait queue destroyed
|
||||||
|
- **ERR_INVALID_ARGS**: Invalid function arguments
|
||||||
|
|
||||||
|
### Panic Conditions
|
||||||
|
|
||||||
|
- Stack overflow detection
|
||||||
|
- Invalid thread magic numbers
|
||||||
|
- Inconsistent thread state
|
||||||
|
- Failed assertions in debug builds
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### Thread Creation
|
||||||
|
|
||||||
|
- Use appropriate priority levels
|
||||||
|
- Specify reasonable stack sizes
|
||||||
|
- Choose meaningful thread names
|
||||||
|
- Consider CPU affinity for performance-critical threads
|
||||||
|
|
||||||
|
### Synchronization
|
||||||
|
|
||||||
|
- Always use proper locking with wait queues
|
||||||
|
- Handle timeout conditions appropriately
|
||||||
|
- Avoid priority inversion through careful design
|
||||||
|
- Use real-time threads sparingly
|
||||||
|
|
||||||
|
### Resource Management
|
||||||
|
|
||||||
|
- Detach threads that don't need join semantics
|
||||||
|
- Handle thread cleanup properly
|
||||||
|
- Monitor stack usage in debug builds
|
||||||
|
- Use TLS appropriately for per-thread data
|
||||||
|
|
||||||
|
This threading and scheduler system provides a robust foundation for kernel and application development in the LK operating system, with careful attention to performance, scalability, and debugging capabilities.
|
||||||
2
external/README
vendored
2
external/README
vendored
@@ -1,2 +1,4 @@
|
|||||||
|
# External Source Modules
|
||||||
|
|
||||||
This directory contains source modules that are composed of
|
This directory contains source modules that are composed of
|
||||||
software originating from external to the lk project.
|
software originating from external to the lk project.
|
||||||
|
|||||||
@@ -23,13 +23,17 @@
|
|||||||
"**/.cache": true,
|
"**/.cache": true,
|
||||||
"**/.clangd": true,
|
"**/.clangd": true,
|
||||||
"build-*": true,
|
"build-*": true,
|
||||||
|
},
|
||||||
|
"markdownlint.config": {
|
||||||
|
"MD024": { "siblings_only": true },
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extensions": {
|
"extensions": {
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
"mkornelsen.vscode-arm64",
|
"mkornelsen.vscode-arm64",
|
||||||
"llvm-vs-code-extensions.vscode-clangd",
|
"llvm-vs-code-extensions.vscode-clangd",
|
||||||
"sunshaoce.risc-v"
|
"sunshaoce.risc-v",
|
||||||
|
"davidanson.vscode-markdownlint"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user