Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/kernel/hartlock.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include <hart_locals.h>
#include <hartlock.h>
#include <irq.h>
#include <panic.h>
#include <types.h>

void hartlock_acquire(void) {
bool prev_interrupt_enabled = are_interrupts_enabled();
disable_interrupts();
struct hart_locals *hart = get_hart_locals();
if (hart->interrupt_disabled_depth == 0) {
hart->prev_interrupt_enabled = prev_interrupt_enabled;
}
hart->interrupt_disabled_depth += 1;
}

void hartlock_release(void) {
if (are_interrupts_enabled()) {
panic("hartlock_release: interrupts are enabled when they should be disabled");
}
struct hart_locals *hart = get_hart_locals();
if (hart->interrupt_disabled_depth < 1) {
panic("hartlock_release: called without matching hartlock_acquire");
}
hart->interrupt_disabled_depth -= 1;
if (hart->interrupt_disabled_depth == 0 && hart->prev_interrupt_state) {
enable_interrupts();
}
}

bool is_hart_locked(void) {
// Currently, a hartlock is just enabling/disabling interrupts
return !are_interrupts_enabled();
}
14 changes: 14 additions & 0 deletions src/kernel/include/arch/riscv64/insns.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,20 @@ static inline void csrw(enum csr csr, u64 value) {
__asm__ volatile("csrw %0, %1" ::"i"(csr), "r"(value) : "memory");
}

/**
* Sets (OR) specified bits in a CSR.
*/
static inline void csrs(enum csr csr, u64 bits) {
__asm__ volatile("csrs %0, %1" :: "i"(csr), "r"(bits) : "memory");
}

/**
* Clear (AND NOT) specified bits in a CSR.
*/
static inline void csrc(enum csr csr, u64 bits) {
__asm__ volatile("csrc %0, %1" :: "i"(csr), "r"(bits) : "memory");
}

/**
* Fences modifications to page tables.
*/
Expand Down
37 changes: 37 additions & 0 deletions src/kernel/include/arch/riscv64/irq.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#ifndef UKO_OS_KERNEL__IRQ_H
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, didn't realize this was going to so closely correspond to what's in hartlock.c -- one thing you can do is to have an arch-independent foo.h, but put the .c under arch/

#define UKO_OS_KERNEL__IRQ_H 1

#include <insns.h>
#include <types.h>

constexpr u64 RISCV64_SSTATUS_SIE = (1UL << 1);

/**
* enable_interrupts - Enables interrupt requests.
*
* Sets the SIE (supervisor interrupt enable) flag in the SSTATUS CSR.
*/
static inline void enable_interrupts(void) {
csrs(RISCV64_CSR_SSTATUS, RISCV64_SSTATUS_SIE);
}

/**
* disable_interrupts - Disables interrupt requests.
*
* Clears the SIE (supervisor interrupt enable) flag in the SSTATUS CSR.
*/
static inline void disable_interrupts(void) {
csrc(RISCV64_CSR_SSTATUS, RISCV64_SSTATUS_SIE);
}

/**
* are_interrupts_enabled - Checks if interrupt requests are enabled.
*
* Return: true if interrupt requests are enabled, false otherwise.
*/
static inline bool are_interrupts_enabled(void) {
u64 sstatus = csrr(RISCV64_CSR_SSTATUS);
return (sstatus & RISCV64_SSTATUS_SIE) != 0;
}

#endif // UKO_OS_KERNEL__IRQ_H
10 changes: 10 additions & 0 deletions src/kernel/include/hart_locals.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ struct hart_locals {
* The ID of the hart. Not guaranteed to be small or densely packed.
*/
u64 hart_id;

/**
* Number of times interrupts have been disabled.
*/
u32 interrupt_disabled_depth;

/**
* If interrupts request were previously enabled.
*/
bool prev_interrupt_enabled;

/**
* The current heap.
Expand Down
30 changes: 30 additions & 0 deletions src/kernel/include/hartlock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#ifndef UKO_OS_KERNEL__HARTLOCK_H
#define UKO_OS_KERNEL__HARTLOCK_H 1

/**
* hartlock_acquire - Locks the executing thread to the current hart.
*
* Disables interrupts to prevent the current thread from being moved
* to another hart. This does not provide mutual exclusion, nor does it
* prevent the thread from blocking. It only ensures that the
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nor does it prevent the thread from blocking

Shouldn't it? Blocking operations should allow context-switching.

* thread continues to execute on the same hart by preventing
* preemption via interrupts.
*/
void hartlock_acquire(void);

/**
* hartlock_release - Unlocks the executing thread from the current hart.
*
* Re-enables interrupts if they were enabled at the time hartlock_acquire()
* was called. If interrupts were already disabled, they remain disabled.
*/
void hartlock_release(void);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_hart_locked would be nice-to-have for e.g. get_hart_locals to assert on

/**
* is_hart_locked - Checks if the executing thread has a hartlock.
*
* Return: true if the thread has a hartlock, false otherwise.
*/
bool is_hart_locked(void);

#endif // UKO_OS_KERNEL__HARTLOCK_H
47 changes: 47 additions & 0 deletions src/kernel/include/spinlock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#ifndef UKO_OS_KERNEL__SPINLOCK_H
#define UKO_OS_KERNEL__SPINLOCK_H 1

#include <hart_locals.h>
#include <stdatomic.h>
#include <types.h>

/**
* struct spinlock - Basic spinlock structure.
* @lock: Atomic lock flag (true if acquired, false otherwise).
* @hart: Pointer to the hart holding the lock.
* @name: Name of the lock (for debugging).
*/
struct spinlock {
volatile atomic_flag lock;
struct hart_locals *hart;
const char *name;
};

/**
* spinlock_init - Initializes a spinlock.
* @sp: Pointer to the spinlock structure.
* @name: Name of the lock.
*
* Initializes a spinlock that disables interrupts.
*/
void spinlock_init(struct spinlock *sp, const char *name);

/**
* spinlock_acquire - Acquire a spinlock.
* @sp: Pointer to the spinlock structure.
*
* Acquires the specified spinlock. If the lock is already held,
* the caller will spin until the lock becomes available.
*/
void spinlock_acquire(struct spinlock *sp);

/**
* spinlock_release - Release a spinlock.
* @sp: Pointer to the spinlock structure.
*
* Releases the previously acquired spinlock. The lock must have
* been acquired by the same context.
*/
void spinlock_release(struct spinlock *sp);

#endif // UKO_OS_KERNEL__SPINLOCK_H
33 changes: 33 additions & 0 deletions src/kernel/spinlock.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include <hartlock.h>
#include <panic.h>
#include <spinlock.h>

/* Returns true if thread is holding the specified spinlock, false otherwise. */
static bool holding(struct spinlock *sp) {
struct hart_locals *hart = get_hart_locals();
return atomic_load(&sp->lock) && (sp->hart == hart);
}

void spinlock_init(struct spinlock *sp, const char *name) {
sp->lock = ATOMIC_FLAG_INIT;
sp->hart = nullptr;
sp->name = name;
}

void spinlock_acquire(struct spinlock *sp) {
hartlock_acquire(); // Lock execution to current hart
if (holding(sp)) {
panic("spinlock_acquire: {cstr} already held by current thread", sp->name);
}
while (atomic_flag_test_and_set(&sp->lock, memory_order_acquire)) {}
sp->hart = get_hart_locals();
}

void spinlock_release(struct spinlock *sp) {
if (!holding(sp)) {
panic("spinlock_release: {cstr} not held by current thread", sp->name);
}
sp->hart = nullptr;
atomic_flag_clear_explicit(&sp->lock, memory_order_release);
hartlock_release(); // Unlock execution if necessary
}
Loading