# SMP services

## Introduction

The SMP services provide various abstractions related to SMP programming. It is defined inside `shelter/lib/include/std/smp.h` and implemented in `shelter/lib/src/std/smp.c`

## GS register

Regarding the GS register, two functions are provided:
- `sh_smp_write_gs_base(sh_uint64 value)`: Write the `IA32_GS_BASE` MSR. with a value, return nothing
- `sh_smp_gs_base()`: take no argument and return a `sh_ap_CPU_STRUCT*`

## Spinlocks

The SMP services provide a basic implementation for a spinlock, which look like this:

``` C
typedef struct {
  volatile sh_uint32 spinlock;
  sh_uint32 lapic_id;
} sh_SPIN_LOCK;
```

Two macros are provided to initialize an unlocked spinlock:
- `SH_LOCK()`: intended for spinlocks stored as global variables
- `SH_LOCK_LOCAL()`: intended for spinlocks stored as local variables or into structs

This spinlock implementation requires `sh_smp_gs_base()` to return a valid pointer to a CPU struct. It uses the atomics primitives provided by compilers. Spinlock operations provide full memory ordering guarantees.

Four functions are provided for spinlock manipulation:
- `sh_spin_lock(sh_SPIN_LOCK *lock)`: lock a spinlock, block until the lock is acquired. Return nothing
- `sh_spin_unlock(sh_SPIN_LOCK *lock)`: unlock a spinlock, return nothing
- `sh_spin_trylock(sh_SPIN_LOCK *lock)`: Attempt to acquire the lock without blocking., return `SH_TRUE` if successfull, `SH_FALSE` otherwise.
- `sh_spin_wholock(sh_SPIN_LOCK *lock)`: return the LAPIC id of the CPU locking the spinlock. Return `SH_UINT32_MAX` if the spinlock isn't owned by any CPU

Current implementation doesn't disable interrupts while holding locks.

## CPU count

SMP services can store the amount of CPU cores using the following functions:
- `sh_smp_set_cpu_count(sh_int16 cpu_count)`: set the count of CPU. It is only called one time by `sh_ap_prepare_for_smp_launch()`
- `sh_smp_get_cpu_count()`: return the count of CPU, return `-1` if value isn't initialized

## Memory barrier

The SMP services provide the following primitives:

Function name | Role
--------------|-----
`sh_mb()`     | Full memory barrier
`sh_rmb()`     | Read memory barrier
`sh_wmb()`     | Write memory barrier

Current x86_64 implementation uses mfence/lfence/sfence.
