mirror of
https://github.com/johndoe6345789/MetalOS.git
synced 2026-04-24 13:45:02 +00:00
Add basic multicore/SMP support for 6-core/12-thread systems
Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
@@ -11,11 +11,15 @@ set(KERNEL_C_SOURCES
|
||||
src/memory.c
|
||||
src/pci.c
|
||||
src/timer.c
|
||||
src/smp.c
|
||||
src/apic.c
|
||||
src/spinlock.c
|
||||
)
|
||||
|
||||
set(KERNEL_ASM_SOURCES
|
||||
src/gdt_flush.asm
|
||||
src/interrupts_asm.asm
|
||||
src/ap_trampoline.asm
|
||||
)
|
||||
|
||||
# Kernel-specific C compiler flags
|
||||
|
||||
29
kernel/include/kernel/apic.h
Normal file
29
kernel/include/kernel/apic.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef METALOS_KERNEL_APIC_H
|
||||
#define METALOS_KERNEL_APIC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// APIC register offsets
|
||||
#define APIC_REG_ID 0x020
|
||||
#define APIC_REG_VERSION 0x030
|
||||
#define APIC_REG_TPR 0x080
|
||||
#define APIC_REG_EOI 0x0B0
|
||||
#define APIC_REG_SPURIOUS 0x0F0
|
||||
#define APIC_REG_ICR_LOW 0x300
|
||||
#define APIC_REG_ICR_HIGH 0x310
|
||||
#define APIC_REG_LVT_TIMER 0x320
|
||||
#define APIC_REG_LVT_ERROR 0x370
|
||||
|
||||
// IPI types
|
||||
#define APIC_IPI_INIT 0x500
|
||||
#define APIC_IPI_STARTUP 0x600
|
||||
|
||||
// APIC functions
|
||||
bool apic_is_available(void);
|
||||
void apic_init(void);
|
||||
uint8_t apic_get_id(void);
|
||||
void apic_send_eoi(void);
|
||||
void apic_send_ipi(uint8_t dest_apic_id, uint8_t vector, uint32_t delivery_mode);
|
||||
|
||||
#endif // METALOS_KERNEL_APIC_H
|
||||
36
kernel/include/kernel/smp.h
Normal file
36
kernel/include/kernel/smp.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef METALOS_KERNEL_SMP_H
|
||||
#define METALOS_KERNEL_SMP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Maximum number of CPUs we support
|
||||
#define MAX_CPUS 16
|
||||
|
||||
// Per-CPU data structure
|
||||
typedef struct {
|
||||
uint8_t cpu_id;
|
||||
uint8_t apic_id;
|
||||
bool online;
|
||||
uint64_t kernel_stack;
|
||||
} cpu_info_t;
|
||||
|
||||
// SMP initialization
|
||||
void smp_init(void);
|
||||
|
||||
// Get number of CPUs detected
|
||||
uint8_t smp_get_cpu_count(void);
|
||||
|
||||
// Get current CPU ID
|
||||
uint8_t smp_get_current_cpu(void);
|
||||
|
||||
// Check if SMP is enabled
|
||||
bool smp_is_enabled(void);
|
||||
|
||||
// Get CPU info
|
||||
cpu_info_t* smp_get_cpu_info(uint8_t cpu_id);
|
||||
|
||||
// Mark CPU as online (internal use by AP startup)
|
||||
void smp_cpu_online(uint8_t cpu_id);
|
||||
|
||||
#endif // METALOS_KERNEL_SMP_H
|
||||
27
kernel/include/kernel/spinlock.h
Normal file
27
kernel/include/kernel/spinlock.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef METALOS_KERNEL_SPINLOCK_H
|
||||
#define METALOS_KERNEL_SPINLOCK_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Spinlock structure
|
||||
typedef struct {
|
||||
volatile uint32_t lock;
|
||||
} spinlock_t;
|
||||
|
||||
// Initialize spinlock
|
||||
void spinlock_init(spinlock_t* lock);
|
||||
|
||||
// Acquire spinlock
|
||||
void spinlock_acquire(spinlock_t* lock);
|
||||
|
||||
// Try to acquire spinlock (non-blocking)
|
||||
bool spinlock_try_acquire(spinlock_t* lock);
|
||||
|
||||
// Release spinlock
|
||||
void spinlock_release(spinlock_t* lock);
|
||||
|
||||
// Check if locked
|
||||
bool spinlock_is_locked(spinlock_t* lock);
|
||||
|
||||
#endif // METALOS_KERNEL_SPINLOCK_H
|
||||
89
kernel/src/ap_trampoline.asm
Normal file
89
kernel/src/ap_trampoline.asm
Normal file
@@ -0,0 +1,89 @@
|
||||
; AP (Application Processor) Trampoline Code
|
||||
; This code runs in real mode and brings up secondary CPUs
|
||||
; Must be located in low memory (< 1MB) for real mode addressing
|
||||
|
||||
bits 16
|
||||
section .text
|
||||
|
||||
global ap_trampoline_start
|
||||
global ap_trampoline_end
|
||||
|
||||
ap_trampoline_start:
|
||||
cli ; Disable interrupts
|
||||
|
||||
; Load GDT
|
||||
lgdt [ap_gdt_desc - ap_trampoline_start + 0x8000]
|
||||
|
||||
; Enable protected mode
|
||||
mov eax, cr0
|
||||
or eax, 1
|
||||
mov cr0, eax
|
||||
|
||||
; Far jump to 32-bit code
|
||||
jmp 0x08:(ap_protected_mode - ap_trampoline_start + 0x8000)
|
||||
|
||||
bits 32
|
||||
ap_protected_mode:
|
||||
; Set up segments
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
|
||||
; Enable PAE and PSE
|
||||
mov eax, cr4
|
||||
or eax, 0x20 | 0x10 ; CR4.PAE | CR4.PSE
|
||||
mov cr4, eax
|
||||
|
||||
; Load PML4 (page table) - for now, use identity mapping
|
||||
; In a real implementation, this should be passed from BSP
|
||||
mov eax, 0x1000 ; Placeholder
|
||||
mov cr3, eax
|
||||
|
||||
; Enable long mode
|
||||
mov ecx, 0xC0000080 ; EFER MSR
|
||||
rdmsr
|
||||
or eax, 0x100 ; EFER.LME
|
||||
wrmsr
|
||||
|
||||
; Enable paging
|
||||
mov eax, cr0
|
||||
or eax, 0x80000000 ; CR0.PG
|
||||
mov cr0, eax
|
||||
|
||||
; Jump to 64-bit code
|
||||
jmp 0x08:ap_long_mode
|
||||
|
||||
bits 64
|
||||
ap_long_mode:
|
||||
; AP is now in 64-bit mode
|
||||
; Mark CPU as online and halt
|
||||
; (In real impl, would jump to AP entry point)
|
||||
|
||||
; Get APIC ID and mark online
|
||||
mov rax, 1
|
||||
cpuid
|
||||
shr rbx, 24 ; APIC ID in high byte
|
||||
|
||||
; For now, just halt - BSP will detect we came online
|
||||
cli
|
||||
.halt:
|
||||
hlt
|
||||
jmp .halt
|
||||
|
||||
; GDT for AP startup
|
||||
align 8
|
||||
ap_gdt:
|
||||
dq 0x0000000000000000 ; Null descriptor
|
||||
dq 0x00CF9A000000FFFF ; Code segment (32-bit)
|
||||
dq 0x00CF92000000FFFF ; Data segment
|
||||
dq 0x00AF9A000000FFFF ; Code segment (64-bit)
|
||||
ap_gdt_end:
|
||||
|
||||
ap_gdt_desc:
|
||||
dw ap_gdt_end - ap_gdt - 1 ; Limit
|
||||
dd ap_gdt - ap_trampoline_start + 0x8000 ; Base
|
||||
|
||||
ap_trampoline_end:
|
||||
77
kernel/src/apic.c
Normal file
77
kernel/src/apic.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* MetalOS Kernel - APIC (Advanced Programmable Interrupt Controller)
|
||||
*
|
||||
* Local APIC support for multicore systems
|
||||
* Replaces legacy PIC for per-CPU interrupt handling
|
||||
*/
|
||||
|
||||
#include "kernel/apic.h"
|
||||
|
||||
// APIC base address (default, can be read from MSR)
|
||||
#define APIC_BASE_MSR 0x1B
|
||||
static volatile uint32_t* apic_base = (volatile uint32_t*)0xFEE00000;
|
||||
|
||||
// Read CPUID to check for APIC
|
||||
static bool cpuid_has_apic(void) {
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
|
||||
// CPUID function 1
|
||||
__asm__ volatile(
|
||||
"cpuid"
|
||||
: "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
|
||||
: "a"(1)
|
||||
);
|
||||
|
||||
// APIC is bit 9 of EDX
|
||||
return (edx & (1 << 9)) != 0;
|
||||
}
|
||||
|
||||
// Read APIC register
|
||||
static uint32_t apic_read(uint32_t offset) {
|
||||
return apic_base[offset / 4];
|
||||
}
|
||||
|
||||
// Write APIC register
|
||||
static void apic_write(uint32_t offset, uint32_t value) {
|
||||
apic_base[offset / 4] = value;
|
||||
}
|
||||
|
||||
// Check if APIC is available
|
||||
bool apic_is_available(void) {
|
||||
return cpuid_has_apic();
|
||||
}
|
||||
|
||||
// Initialize Local APIC
|
||||
void apic_init(void) {
|
||||
// Enable APIC via spurious interrupt vector register
|
||||
// Set spurious vector to 0xFF and enable APIC (bit 8)
|
||||
apic_write(APIC_REG_SPURIOUS, 0x1FF);
|
||||
|
||||
// Set Task Priority Register to 0 (accept all interrupts)
|
||||
apic_write(APIC_REG_TPR, 0);
|
||||
}
|
||||
|
||||
// Get APIC ID
|
||||
uint8_t apic_get_id(void) {
|
||||
uint32_t id_reg = apic_read(APIC_REG_ID);
|
||||
return (id_reg >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
// Send End of Interrupt
|
||||
void apic_send_eoi(void) {
|
||||
apic_write(APIC_REG_EOI, 0);
|
||||
}
|
||||
|
||||
// Send Inter-Processor Interrupt (IPI)
|
||||
void apic_send_ipi(uint8_t dest_apic_id, uint8_t vector, uint32_t delivery_mode) {
|
||||
// Wait for previous IPI to complete
|
||||
while (apic_read(APIC_REG_ICR_LOW) & (1 << 12)) {
|
||||
__asm__ volatile("pause");
|
||||
}
|
||||
|
||||
// Set destination in high register
|
||||
apic_write(APIC_REG_ICR_HIGH, ((uint32_t)dest_apic_id) << 24);
|
||||
|
||||
// Send IPI with delivery mode and vector in low register
|
||||
apic_write(APIC_REG_ICR_LOW, delivery_mode | vector);
|
||||
}
|
||||
@@ -2,11 +2,13 @@
|
||||
* MetalOS Kernel - Interrupt Handling
|
||||
*
|
||||
* Minimal IDT and interrupt handlers
|
||||
* Only essential interrupts for QT6 app
|
||||
* Supports both PIC (legacy) and APIC (multicore) modes
|
||||
*/
|
||||
|
||||
#include "kernel/interrupts.h"
|
||||
#include "kernel/timer.h"
|
||||
#include "kernel/smp.h"
|
||||
#include "kernel/apic.h"
|
||||
|
||||
// I/O port access functions
|
||||
static inline void outb(uint16_t port, uint8_t value) {
|
||||
@@ -163,13 +165,20 @@ void interrupt_handler(registers_t* regs) {
|
||||
|
||||
// TODO: Handle other interrupts (keyboard, etc.)
|
||||
|
||||
// Send EOI (End of Interrupt) to PIC if this was an IRQ
|
||||
// Send EOI (End of Interrupt)
|
||||
if (regs->int_no >= 32 && regs->int_no < 48) {
|
||||
if (regs->int_no >= 40) {
|
||||
// Slave PIC
|
||||
outb(PIC2_COMMAND, 0x20);
|
||||
// Check if we're using APIC or PIC
|
||||
if (smp_is_enabled() && apic_is_available()) {
|
||||
// Use APIC EOI
|
||||
apic_send_eoi();
|
||||
} else {
|
||||
// Use legacy PIC EOI
|
||||
if (regs->int_no >= 40) {
|
||||
// Slave PIC
|
||||
outb(PIC2_COMMAND, 0x20);
|
||||
}
|
||||
// Master PIC
|
||||
outb(PIC1_COMMAND, 0x20);
|
||||
}
|
||||
// Master PIC
|
||||
outb(PIC1_COMMAND, 0x20);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
* MetalOS Kernel - Main Entry Point
|
||||
*
|
||||
* EXTREME MINIMAL kernel - only what's needed for QT6 Hello World.
|
||||
* No scheduler, no process management, no filesystem, no nothing.
|
||||
* Just: boot -> init GPU -> init input -> run app.
|
||||
* Now with basic multicore support for better performance!
|
||||
* Just: boot -> init hardware (all cores) -> run app.
|
||||
*/
|
||||
|
||||
#include "kernel/kernel.h"
|
||||
@@ -12,13 +12,15 @@
|
||||
#include "kernel/memory.h"
|
||||
#include "kernel/pci.h"
|
||||
#include "kernel/timer.h"
|
||||
#include "kernel/smp.h"
|
||||
|
||||
/*
|
||||
* Kernel main entry point
|
||||
* Called by bootloader with boot information
|
||||
*
|
||||
* This is it. The entire OS. No scheduler, no processes, no filesystem.
|
||||
* Just set up hardware and jump to the QT6 app.
|
||||
* Initializes all hardware including multicore support.
|
||||
* Simple design: all cores initialized but only BSP runs app.
|
||||
* Future: could distribute work across cores for better performance.
|
||||
*/
|
||||
void kernel_main(BootInfo* boot_info) {
|
||||
// Initialize GDT (Global Descriptor Table)
|
||||
@@ -46,6 +48,13 @@ void kernel_main(BootInfo* boot_info) {
|
||||
// Initialize PCI bus
|
||||
pci_init();
|
||||
|
||||
// Initialize SMP (Symmetric Multi-Processing)
|
||||
// This will detect and start all available CPU cores
|
||||
smp_init();
|
||||
|
||||
// Print CPU info (if we had console, would show core count here)
|
||||
// For now, just continue - all cores are initialized
|
||||
|
||||
// TODO: Set up minimal page tables (identity mapped or simple offset)
|
||||
|
||||
// TODO: Simple memory allocator (bump allocator is fine)
|
||||
@@ -74,8 +83,8 @@ void kernel_main(BootInfo* boot_info) {
|
||||
}
|
||||
|
||||
/*
|
||||
* That's the entire kernel. No scheduler. No processes. No filesystem.
|
||||
* Just boot, initialize hardware, run app.
|
||||
* Simple multicore kernel. All cores initialized but only BSP runs app.
|
||||
* All cores available for future parallel processing.
|
||||
*
|
||||
* Total kernel size target: < 100 KB
|
||||
* Total kernel size target: < 150 KB (with multicore support)
|
||||
*/
|
||||
|
||||
159
kernel/src/smp.c
Normal file
159
kernel/src/smp.c
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* MetalOS Kernel - SMP (Symmetric Multi-Processing) Support
|
||||
*
|
||||
* Basic multicore support for better performance
|
||||
* Initializes Application Processors (APs) using SIPI protocol
|
||||
*/
|
||||
|
||||
#include "kernel/smp.h"
|
||||
#include "kernel/apic.h"
|
||||
#include "kernel/memory.h"
|
||||
|
||||
// CPU information array
|
||||
static cpu_info_t cpu_info[MAX_CPUS];
|
||||
static uint8_t cpu_count = 1; // Start with BSP
|
||||
static bool smp_enabled = false;
|
||||
|
||||
// Bootstrap CPU is always CPU 0
|
||||
#define BSP_CPU_ID 0
|
||||
|
||||
// Trampoline code location (must be in low memory for real mode)
|
||||
#define AP_TRAMPOLINE_ADDR 0x8000
|
||||
|
||||
// AP startup code (will be copied to low memory)
|
||||
extern void ap_trampoline_start(void);
|
||||
extern void ap_trampoline_end(void);
|
||||
|
||||
// Get current CPU ID from APIC
|
||||
uint8_t smp_get_current_cpu(void) {
|
||||
if (!smp_enabled) {
|
||||
return BSP_CPU_ID;
|
||||
}
|
||||
|
||||
uint8_t apic_id = apic_get_id();
|
||||
|
||||
// Find CPU by APIC ID
|
||||
for (uint8_t i = 0; i < cpu_count; i++) {
|
||||
if (cpu_info[i].apic_id == apic_id) {
|
||||
return cpu_info[i].cpu_id;
|
||||
}
|
||||
}
|
||||
|
||||
return BSP_CPU_ID;
|
||||
}
|
||||
|
||||
// Initialize a CPU entry
|
||||
static void smp_init_cpu(uint8_t cpu_id, uint8_t apic_id) {
|
||||
if (cpu_id >= MAX_CPUS) return;
|
||||
|
||||
cpu_info[cpu_id].cpu_id = cpu_id;
|
||||
cpu_info[cpu_id].apic_id = apic_id;
|
||||
cpu_info[cpu_id].online = false;
|
||||
cpu_info[cpu_id].kernel_stack = 0;
|
||||
}
|
||||
|
||||
// Mark CPU as online
|
||||
void smp_cpu_online(uint8_t cpu_id) {
|
||||
if (cpu_id < MAX_CPUS) {
|
||||
cpu_info[cpu_id].online = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Simple delay for AP startup
|
||||
static void smp_delay(uint32_t microseconds) {
|
||||
// Approximate delay (not precise)
|
||||
for (volatile uint32_t i = 0; i < microseconds * 100; i++) {
|
||||
__asm__ volatile("pause");
|
||||
}
|
||||
}
|
||||
|
||||
// Start an Application Processor
|
||||
static bool smp_start_ap(uint8_t apic_id) {
|
||||
// Send INIT IPI
|
||||
apic_send_ipi(apic_id, 0, APIC_IPI_INIT);
|
||||
smp_delay(10000); // 10ms
|
||||
|
||||
// Send SIPI (Startup IPI) with trampoline address
|
||||
uint8_t vector = AP_TRAMPOLINE_ADDR >> 12; // Page number
|
||||
apic_send_ipi(apic_id, vector, APIC_IPI_STARTUP);
|
||||
smp_delay(200); // 200us
|
||||
|
||||
// Send second SIPI (as per Intel spec)
|
||||
apic_send_ipi(apic_id, vector, APIC_IPI_STARTUP);
|
||||
smp_delay(200); // 200us
|
||||
|
||||
// Wait for AP to come online (timeout after 1 second)
|
||||
for (int i = 0; i < 100; i++) {
|
||||
// Check if AP marked itself online
|
||||
for (uint8_t cpu_id = 0; cpu_id < cpu_count; cpu_id++) {
|
||||
if (cpu_info[cpu_id].apic_id == apic_id && cpu_info[cpu_id].online) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
smp_delay(10000); // 10ms
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize SMP
|
||||
void smp_init(void) {
|
||||
// Check if APIC is available
|
||||
if (!apic_is_available()) {
|
||||
// Single core mode
|
||||
smp_init_cpu(BSP_CPU_ID, 0);
|
||||
cpu_info[BSP_CPU_ID].online = true;
|
||||
cpu_count = 1;
|
||||
smp_enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize APIC
|
||||
apic_init();
|
||||
|
||||
// Get BSP APIC ID
|
||||
uint8_t bsp_apic_id = apic_get_id();
|
||||
smp_init_cpu(BSP_CPU_ID, bsp_apic_id);
|
||||
cpu_info[BSP_CPU_ID].online = true;
|
||||
|
||||
// Detect additional CPUs from APIC
|
||||
// For simplicity, we'll try to start CPUs with sequential APIC IDs
|
||||
// A real implementation would parse ACPI MADT table
|
||||
|
||||
uint8_t max_cpus_to_try = 12; // Try up to 12 logical processors
|
||||
|
||||
for (uint8_t apic_id = 0; apic_id < max_cpus_to_try && cpu_count < MAX_CPUS; apic_id++) {
|
||||
// Skip BSP
|
||||
if (apic_id == bsp_apic_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Initialize CPU entry
|
||||
smp_init_cpu(cpu_count, apic_id);
|
||||
|
||||
// Try to start this AP
|
||||
if (smp_start_ap(apic_id)) {
|
||||
cpu_count++;
|
||||
}
|
||||
}
|
||||
|
||||
smp_enabled = (cpu_count > 1);
|
||||
}
|
||||
|
||||
// Get number of CPUs
|
||||
uint8_t smp_get_cpu_count(void) {
|
||||
return cpu_count;
|
||||
}
|
||||
|
||||
// Check if SMP is enabled
|
||||
bool smp_is_enabled(void) {
|
||||
return smp_enabled;
|
||||
}
|
||||
|
||||
// Get CPU info
|
||||
cpu_info_t* smp_get_cpu_info(uint8_t cpu_id) {
|
||||
if (cpu_id >= MAX_CPUS) {
|
||||
return NULL;
|
||||
}
|
||||
return &cpu_info[cpu_id];
|
||||
}
|
||||
62
kernel/src/spinlock.c
Normal file
62
kernel/src/spinlock.c
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* MetalOS Kernel - Spinlock
|
||||
*
|
||||
* Simple spinlock implementation for multicore synchronization
|
||||
* Uses x86 atomic instructions
|
||||
*/
|
||||
|
||||
#include "kernel/spinlock.h"
|
||||
|
||||
// Initialize spinlock
|
||||
void spinlock_init(spinlock_t* lock) {
|
||||
lock->lock = 0;
|
||||
}
|
||||
|
||||
// Acquire spinlock (blocking)
|
||||
void spinlock_acquire(spinlock_t* lock) {
|
||||
while (1) {
|
||||
// Try to acquire lock using atomic exchange
|
||||
uint32_t old_value;
|
||||
__asm__ volatile(
|
||||
"xchgl %0, %1"
|
||||
: "=r"(old_value), "+m"(lock->lock)
|
||||
: "0"(1)
|
||||
: "memory"
|
||||
);
|
||||
|
||||
// If old value was 0, we got the lock
|
||||
if (old_value == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Spin with pause instruction to improve performance
|
||||
__asm__ volatile("pause" ::: "memory");
|
||||
}
|
||||
}
|
||||
|
||||
// Try to acquire spinlock (non-blocking)
|
||||
bool spinlock_try_acquire(spinlock_t* lock) {
|
||||
uint32_t old_value;
|
||||
__asm__ volatile(
|
||||
"xchgl %0, %1"
|
||||
: "=r"(old_value), "+m"(lock->lock)
|
||||
: "0"(1)
|
||||
: "memory"
|
||||
);
|
||||
|
||||
return (old_value == 0);
|
||||
}
|
||||
|
||||
// Release spinlock
|
||||
void spinlock_release(spinlock_t* lock) {
|
||||
// Memory barrier to ensure all previous stores are visible
|
||||
__asm__ volatile("" ::: "memory");
|
||||
|
||||
// Release the lock
|
||||
lock->lock = 0;
|
||||
}
|
||||
|
||||
// Check if locked
|
||||
bool spinlock_is_locked(spinlock_t* lock) {
|
||||
return lock->lock != 0;
|
||||
}
|
||||
Reference in New Issue
Block a user