minix/minix/kernel/smp.c
google-labs-jules[bot] c3119ae881 I've refactored the MINIX kernel for architectural soundness.
This commit addresses critical architectural deficiencies in the MINIX kernel build and source structure. The changes are based on a comprehensive technical review and aim to establish a cleaner, more robust foundation for future kernel development.

Key changes include:

1.  **Kernel-Specific Type System:**
    *   I introduced `minix/kernel/include/minix/kernel_types.h`.
    *   I defined kernel-specific types (e.g., `k_size_t`, `k_pid_t`) and fixed-width integer types (e.g., `k_uint32_t`) to replace userspace types.

2.  **Kernel-Space Library (`klib`):**
    *   I created a new kernel library (`klib`) located in `minix/kernel/klib/`.
    *   I implemented basic string functions (`kstrlen`, `kstrlcpy`, `kstrcmp`) in `kstring.c`.
    *   I implemented basic memory functions (`kmemcpy`, `kmemset`, `kmemmove`) in `kmemory.c`.
    *   I added stub implementations for `kprintf`, `ksprintf`, and `kvprintf` in `kprintf_stub.c`, along with a `KASSERT_PLACEHOLDER` macro.

3.  **Purged Userspace Dependencies:**
    *   I systematically refactored all C and header files within `minix/kernel/` and its subdirectories (`system/`, `arch/i386/`, `arch/earm/`).
    *   I removed userspace includes (e.g., `<string.h>`, `<stdlib.h>`, `<stdio.h>`, `<assert.h>`, `<sys/types.h>`, `<signal.h>`, `<unistd.h>`).
    *   I replaced calls to userspace C library functions with their `klib` equivalents or marked them with `FIXME` comments if a direct replacement was not yet available (e.g., `atoi`, `strncmp`).
    *   I replaced userspace types with their `k_` prefixed kernel versions.

4.  **Build System Reconstruction (Meson):**
    *   I replaced `minix/kernel/meson.build` with a new script designed for a proper kernel build.
    *   The new build system defines kernel-specific compiler flags, handles architecture detection (i386, x86_64), sets up correct include paths, and builds `klib` and the main kernel executable.
    *   Assembly file compilation and linkage are noted as TODOs in the Meson script.

**Next Steps & Known Issues:**
The kernel is not expected to build successfully immediately after these changes. A significant number of `FIXME` comments and identified issues need to be addressed in a proper build environment. These include:
*   Defining numerous missing constants (error numbers, signal numbers, etc.).
*   Providing kernel-safe implementations for macros like `offsetof`.
*   Implementing missing `klib` functions (e.g., a real `kprintf`, `kstrncmp`, `kmemcmp`, `katoi`).
*   Developing kernel-space signal handling and ELF loading mechanisms.
*   Adding rules for compiling and linking assembly source files.

This refactoring establishes the necessary structure to tackle these remaining issues systematically.
2025-06-06 20:12:01 +00:00

211 lines
4.7 KiB
C

// #include <assert.h> // Replaced
// Added kernel headers
#include <minix/kernel_types.h>
#include <klib/include/kprintf.h>
#include <klib/include/kstring.h>
#include <klib/include/kmemory.h>
#include "smp.h"
#include "interrupt.h"
#include "clock.h"
unsigned ncpus;
unsigned ht_per_core;
unsigned bsp_cpu_id;
struct cpu cpus[CONFIG_MAX_CPUS];
/* info passed to another cpu along with a sched ipi */
struct sched_ipi_data {
volatile u32_t flags;
volatile u32_t data;
};
static struct sched_ipi_data sched_ipi_data[CONFIG_MAX_CPUS];
#define SCHED_IPI_STOP_PROC 1
#define SCHED_IPI_VM_INHIBIT 2
#define SCHED_IPI_SAVE_CTX 4
static volatile unsigned ap_cpus_booted;
SPINLOCK_DEFINE(big_kernel_lock)
SPINLOCK_DEFINE(boot_lock)
void wait_for_APs_to_finish_booting(void)
{
unsigned n = 0;
int i;
/* check how many cpus are actually alive */
for (i = 0 ; i < ncpus ; i++) {
if (cpu_test_flag(i, CPU_IS_READY))
n++;
}
if (n != ncpus)
kprintf_stub("WARNING only %d out of %d cpus booted\n", n, ncpus); // MODIFIED
/* we must let the other CPUs to run in kernel mode first */
BKL_UNLOCK();
while (ap_cpus_booted != (n - 1))
arch_pause();
/* now we have to take the lock again as we continue execution */
BKL_LOCK();
}
void ap_boot_finished(unsigned cpu)
{
ap_cpus_booted++;
}
void smp_ipi_halt_handler(void)
{
ipi_ack();
stop_local_timer();
arch_smp_halt_cpu();
}
void smp_schedule(unsigned cpu)
{
arch_send_smp_schedule_ipi(cpu);
}
void smp_sched_handler(void);
/*
* tell another cpu about a task to do and return only after the cpu acks that
* the task is finished. Also wait before it finishes task sent by another cpu
* to the same one.
*/
static void smp_schedule_sync(struct proc * p, unsigned task)
{
unsigned cpu = p->p_cpu;
unsigned mycpu = cpuid;
KASSERT_PLACEHOLDER(cpu != mycpu); // MODIFIED
/*
* if some other cpu made a request to the same cpu, wait until it is
* done before proceeding
*/
if (sched_ipi_data[cpu].flags != 0) {
BKL_UNLOCK();
while (sched_ipi_data[cpu].flags != 0) {
if (sched_ipi_data[mycpu].flags) {
BKL_LOCK();
smp_sched_handler();
BKL_UNLOCK();
}
}
BKL_LOCK();
}
sched_ipi_data[cpu].data = (u32_t) p;
sched_ipi_data[cpu].flags |= task;
__insn_barrier();
arch_send_smp_schedule_ipi(cpu);
/* wait until the destination cpu finishes its job */
BKL_UNLOCK();
while (sched_ipi_data[cpu].flags != 0) {
if (sched_ipi_data[mycpu].flags) {
BKL_LOCK();
smp_sched_handler();
BKL_UNLOCK();
}
}
BKL_LOCK();
}
void smp_schedule_stop_proc(struct proc * p)
{
if (proc_is_runnable(p))
smp_schedule_sync(p, SCHED_IPI_STOP_PROC);
else
RTS_SET(p, RTS_PROC_STOP);
KASSERT_PLACEHOLDER(RTS_ISSET(p, RTS_PROC_STOP)); // MODIFIED
}
void smp_schedule_vminhibit(struct proc * p)
{
if (proc_is_runnable(p))
smp_schedule_sync(p, SCHED_IPI_VM_INHIBIT);
else
RTS_SET(p, RTS_VMINHIBIT);
KASSERT_PLACEHOLDER(RTS_ISSET(p, RTS_VMINHIBIT)); // MODIFIED
}
void smp_schedule_stop_proc_save_ctx(struct proc * p)
{
/*
* stop the processes and force the complete context of the process to
* be saved (i.e. including FPU state and such)
*/
smp_schedule_sync(p, SCHED_IPI_STOP_PROC | SCHED_IPI_SAVE_CTX);
KASSERT_PLACEHOLDER(RTS_ISSET(p, RTS_PROC_STOP)); // MODIFIED
}
void smp_schedule_migrate_proc(struct proc * p, unsigned dest_cpu)
{
/*
* stop the processes and force the complete context of the process to
* be saved (i.e. including FPU state and such)
*/
smp_schedule_sync(p, SCHED_IPI_STOP_PROC | SCHED_IPI_SAVE_CTX);
KASSERT_PLACEHOLDER(RTS_ISSET(p, RTS_PROC_STOP)); // MODIFIED
/* assign the new cpu and let the process run again */
p->p_cpu = dest_cpu;
RTS_UNSET(p, RTS_PROC_STOP);
}
void smp_sched_handler(void)
{
unsigned flgs;
unsigned cpu = cpuid;
flgs = sched_ipi_data[cpu].flags;
if (flgs) {
struct proc * p;
p = (struct proc *)sched_ipi_data[cpu].data;
if (flgs & SCHED_IPI_STOP_PROC) {
RTS_SET(p, RTS_PROC_STOP);
}
if (flgs & SCHED_IPI_SAVE_CTX) {
/* all context has been saved already, FPU remains */
if (proc_used_fpu(p) &&
get_cpulocal_var(fpu_owner) == p) {
disable_fpu_exception();
save_local_fpu(p, FALSE /*retain*/);
/* we're preparing to migrate somewhere else */
release_fpu(p);
}
}
if (flgs & SCHED_IPI_VM_INHIBIT) {
RTS_SET(p, RTS_VMINHIBIT);
}
}
__insn_barrier();
sched_ipi_data[cpu].flags = 0;
}
/*
* This function gets always called only after smp_sched_handler() has been
* already called. It only serves the purpose of acknowledging the IPI and
* preempting the current process if the CPU was not idle.
*/
void smp_ipi_sched_handler(void)
{
struct proc * curr;
ipi_ack();
curr = get_cpulocal_var(proc_ptr);
if (curr->p_endpoint != IDLE) {
RTS_SET(curr, RTS_PREEMPTED);
}
}