<commit_before>/*
 * Copyright 2019 <NAME>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use core::mem::{self, MaybeUninit};
use core::ptr;

use crate::addr::*;
use crate::arch::*;
use crate::mm::*;
use crate::page::*;
use crate::spinlock::*;
use crate::std::*;
use crate::types::*;
use crate::vm::*;

/// From inc/hf/arch/cpu.h.
extern "C" {
    /// Disables interrupts.
    fn arch_irq_enable();

    /// Enables interrupts.
    fn arch_irq_disable();

    /// Reset the register values other than the PC and argument which are set with
    /// `arch_regs_set_pc_arg()`.
    fn arch_regs_reset(
        r: *mut ArchRegs,
        is_primary: bool,
        vm_id: spci_vm_id_t,
        vcpu_id: cpu_id_t,
        table: paddr_t,
    );

    /// Updates the given registers so that when a vcpu runs, it starts off at the
    /// given address (pc) with the given argument.
    ///
    /// This function must only be called on an arch_regs that is known not be in use
    /// by any other physical CPU.
    fn arch_regs_set_pc_arg(r: *mut ArchRegs, pc: ipaddr_t, arg: uintreg_t);

    /// Updates the register holding the return value of a function.
    ///
    /// This function must only be called on an arch_regs that is known not be in use
    /// by any other physical CPU.
    fn arch_regs_set_retval(r: *mut ArchRegs, v: uintreg_t);
}

const STACK_SIZE: usize = PAGE_SIZE;

/// The number of bits in each element of the interrupt bitfields.
const INTERRUPT_REGISTER_BITS: usize = 32;

#[repr(C)]
pub enum VCpuStatus {
    /// The vcpu is switched off.
    Off,

    /// The vcpu is ready to be run.
    Ready,

    /// The vcpu is currently running.
    Running,

    /// The vcpu is waiting for a message.
    BlockedMailbox,

    /// The vcpu is waiting for an interrupt.
    BlockedInterrupt,

    /// The vcpu has aborted.
    Aborted,
}

#[repr(C)]
pub struct Interrupts {
    /// Bitfield keeping track of which interrupts are enabled.
    enabled: [u32; HF_NUM_INTIDS / INTERRUPT_REGISTER_BITS],

    /// Bitfield keeping track of which interrupts are pending.
    pending: [u32; HF_NUM_INTIDS / INTERRUPT_REGISTER_BITS],

    /// The number of interrupts which are currently both enabled and pending. i.e. the number of
    /// bits set in enabled & pending.
    enabled_and_pending_count: u32,
}

#[repr(C)]
pub struct VCpuFaultInfo {
    ipaddr: ipaddr_t,
    vaddr: vaddr_t,
    pc: vaddr_t,
    mode: c_int,
}

#[repr(C)]
pub struct VCpu {
    lock: RawSpinLock,

    /// The state is only changed in the context of the vCPU being run. This ensures the scheduler
    /// can easily keep track of the vCPU state as transitions are indicated by the return code from
    /// the run call.
    state: VCpuStatus,

    cpu: *mut Cpu,
    vm: *mut Vm,
    regs: ArchRegs,
    interrupts: Interrupts,

    /// Determine whether the 'regs' field is available for use. This is set
    /// to false when a vCPU is about to run on a physical CPU, and is set
    /// back to true when it is descheduled.
    regs_available: bool,
}

/// Encapsulates a vCPU whose lock is held.
#[repr(C)]
#[derive(Copy, Clone)]
pub struct VCpuLocked {
    vcpu: *mut VCpu,
}

// TODO: Update alignment such that cpus are in different cache lines.
#[repr(C)]
pub struct Cpu {
    /// CPU identifier. Doesn't have to be contiguous.
    id: cpu_id_t,

    /// Pointer to bottom of the stack.
    stack_bottom: *mut c_void,

    /// Enabling/disabling irqs are counted per-cpu. They are enabled when the count is zero, and
    /// disabled when it's non-zero.
    irq_disable_count: u32,

    /// See api.c for the partial ordering on locks.
    lock: RawSpinLock,

    /// Determines whether or not the cpu is currently on.
    is_on: bool,
}

unsafe impl Sync for Cpu {}

// TODO: alignas(2 * sizeof(uintreg_t))
// #[repr(align(16))]
#[no_mangle]
static mut callstacks: MaybeUninit<[[u8; STACK_SIZE]; MAX_CPUS]> = MaybeUninit::uninit();

/// State of all supported CPUs. The stack of the first one is initialized.
/// struct cpu cpus[MAX_CPUS] = {
///     {
// 	        .is_on = 1,
///         .stack_bottom = &callstacks[0][STACK_SIZE],
///     },
/// };
/// static mut CPUS: MaybeUninit<[Cpu; MAX_CPUS]> = MaybeUninit::uninit();
static mut cpu_count: u32 = 1;

/// Kernel loader writes booted CPU ID on `cpus.id` and initializes the CPU
/// stack by the address in `cpus.stack_bottom`
/// (See src/arch/aarch64/hypervisor/plat_entry.S and cpu_entry.S.)
/// TODO: Any better design?
/*
#[no_mangle]
pub static mut cpus: Cpu = Cpu {
    id: 0,
    stack_bottom: unsafe { (&callstacks as *const _ as usize) as _ },
    irq_disable_count: 0,
    lock: RawSpinLock::new(),
    is_on: true,
};*/
extern "C" {
    #[no_mangle]
    static mut cpus: MaybeUninit<[Cpu; MAX_CPUS]>;
}

#[no_mangle]
pub unsafe extern "C" fn cpu_init(c: *mut Cpu) {
    // TODO: Assumes that c is zeroed out already.
    sl_init(&mut (*c).lock);
}

#[no_mangle]
pub unsafe extern "C" fn cpu_module_init(cpu_ids: *mut cpu_id_t, count: usize) {
    let mut j: u32;
    let boot_cpu_id: cpu_id_t = cpus.get_ref()[0].id;
    let mut found_boot_cpu: bool = false;

    cpu_count = count as u32;

    // Initialize CPUs with the IDs from the configuration passed in. The
    // CPUs after the boot CPU are initialized in reverse order. The boot
    // CPU is initialized when it is found or in place of the last CPU if it
    // is not found.
    j = cpu_count;
    for i in 0..cpu_count {
        let c: *mut Cpu;
        let id: cpu_id_t = *cpu_ids.offset(i as isize);

        if found_boot_cpu || id != boot_cpu_id {
            j -= 1;
            c = &mut cpus.get_mut()[j as usize];
        } else {
            found_boot_cpu = true;<commit_msg>Add some comments, and remove `Sync` marker on `Cpu`.<commit_after>/*
 * Copyright 2019 <NAME>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use core::mem::{self, MaybeUninit};
use core::ptr;

use crate::addr::*;
use crate::arch::*;
use crate::mm::*;
use crate::page::*;
use crate::spinlock::*;
use crate::std::*;
use crate::types::*;
use crate::vm::*;

/// From inc/hf/arch/cpu.h.
extern "C" {
    /// Disables interrupts.
    fn arch_irq_enable();

    /// Enables interrupts.
    fn arch_irq_disable();

    /// Reset the register values other than the PC and argument which are set with
    /// `arch_regs_set_pc_arg()`.
    fn arch_regs_reset(
        r: *mut ArchRegs,
        is_primary: bool,
        vm_id: spci_vm_id_t,
        vcpu_id: cpu_id_t,
        table: paddr_t,
    );

    /// Updates the given registers so that when a vcpu runs, it starts off at the
    /// given address (pc) with the given argument.
    ///
    /// This function must only be called on an arch_regs that is known not be in use
    /// by any other physical CPU.
    fn arch_regs_set_pc_arg(r: *mut ArchRegs, pc: ipaddr_t, arg: uintreg_t);

    /// Updates the register holding the return value of a function.
    ///
    /// This function must only be called on an arch_regs that is known not be in use
    /// by any other physical CPU.
    fn arch_regs_set_retval(r: *mut ArchRegs, v: uintreg_t);
}

const STACK_SIZE: usize = PAGE_SIZE;

/// The number of bits in each element of the interrupt bitfields.
const INTERRUPT_REGISTER_BITS: usize = 32;

#[repr(C)]
pub enum VCpuStatus {
    /// The vcpu is switched off.
    Off,

    /// The vcpu is ready to be run.
    Ready,

    /// The vcpu is currently running.
    Running,

    /// The vcpu is waiting for a message.
    BlockedMailbox,

    /// The vcpu is waiting for an interrupt.
    BlockedInterrupt,

    /// The vcpu has aborted.
    Aborted,
}

#[repr(C)]
pub struct Interrupts {
    /// Bitfield keeping track of which interrupts are enabled.
    enabled: [u32; HF_NUM_INTIDS / INTERRUPT_REGISTER_BITS],

    /// Bitfield keeping track of which interrupts are pending.
    pending: [u32; HF_NUM_INTIDS / INTERRUPT_REGISTER_BITS],

    /// The number of interrupts which are currently both enabled and pending. i.e. the number of
    /// bits set in enabled & pending.
    enabled_and_pending_count: u32,
}

#[repr(C)]
pub struct VCpuFaultInfo {
    ipaddr: ipaddr_t,
    vaddr: vaddr_t,
    pc: vaddr_t,
    mode: c_int,
}

#[repr(C)]
pub struct VCpu {
    lock: RawSpinLock,

    /// The state is only changed in the context of the vCPU being run. This ensures the scheduler
    /// can easily keep track of the vCPU state as transitions are indicated by the return code from
    /// the run call.
    state: VCpuStatus,

    cpu: *mut Cpu,
    vm: *mut Vm,
    regs: ArchRegs,
    interrupts: Interrupts,

    /// Determine whether the 'regs' field is available for use. This is set
    /// to false when a vCPU is about to run on a physical CPU, and is set
    /// back to true when it is descheduled.
    regs_available: bool,
}

/// Encapsulates a vCPU whose lock is held.
#[repr(C)]
#[derive(Copy, Clone)]
pub struct VCpuLocked {
    vcpu: *mut VCpu,
}

// TODO: Update alignment such that cpus are in different cache lines.
#[repr(C)]
pub struct Cpu {
    /// CPU identifier. Doesn't have to be contiguous.
    id: cpu_id_t,

    /// Pointer to bottom of the stack.
    stack_bottom: *mut c_void,

    /// Enabling/disabling irqs are counted per-cpu. They are enabled when the count is zero, and
    /// disabled when it's non-zero.
    irq_disable_count: u32,

    /// See api.c for the partial ordering on locks.
    lock: RawSpinLock,

    /// Determines whether or not the cpu is currently on.
    is_on: bool,
}

// unsafe impl Sync for Cpu {}

/// The stack to be used by the CPUs.
/// TODO: alignas(2 * sizeof(uintreg_t))
#[no_mangle]
static mut callstacks: MaybeUninit<[[u8; STACK_SIZE]; MAX_CPUS]> = MaybeUninit::uninit();

/// State of all supported CPUs. The stack of the first one is initialized.
/// Kernel loader writes booted CPU ID on `cpus.id` and initializes the CPU
/// stack by the address in `cpus.stack_bottom`.
/// (See src/arch/aarch64/hypervisor/plat_entry.S and cpu_entry.S.)
///
/// Initializing static variables with pointers in Rust failed here. We left
/// the initialization code of `cpus` in `src/cpu.c`.
extern "C" {
    #[no_mangle]
    static mut cpus: MaybeUninit<[Cpu; MAX_CPUS]>;
}

static mut cpu_count: u32 = 1;

#[no_mangle]
pub unsafe extern "C" fn cpu_init(c: *mut Cpu) {
    // TODO: Assumes that c is zeroed out already.
    sl_init(&mut (*c).lock);
}

#[no_mangle]
pub unsafe extern "C" fn cpu_module_init(cpu_ids: *mut cpu_id_t, count: usize) {
    let mut j: u32;
    let boot_cpu_id: cpu_id_t = cpus.get_ref()[0].id;
    let mut found_boot_cpu: bool = false;

    cpu_count = count as u32;

    // Initialize CPUs with the IDs from the configuration passed in. The
    // CPUs after the boot CPU are initialized in reverse order. The boot
    // CPU is initialized when it is found or in place of the last CPU if it
    // is not found.
    j = cpu_count;
    for i in 0..cpu_count {
        let c: *mut Cpu;
        let id: cpu_id_t = *cpu_ids.offset(i as isize);

        if found_boot_cpu || id != boot_cpu_id {
            j -= 1;
            c = &mut cpus.get_mut()[j as usize];
        } else {
            found_boot_cpu = true;
{'fraction_non_alphanumeric': 0.1092057761732852, 'fraction_numerical': 0.005743354118805382, 'mean_word_length': 2.722968845448992, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 10, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6109449', 'n_tokens_mistral': 4047, 'n_tokens_neox': 3934, 'n_words': 1518}