/*
 * Bootstrap Code
 *
 * Copyright (C) 2009-2011 Udo Steinberg <udo@hypervisor.org>
 * Economic rights: Technische Universitaet Dresden (Germany)
 *
 * Copyright (C) 2012 Udo Steinberg, Intel Corporation.
 * Copyright (C) 2015 Alexander Boettcher, Genode Labs GmbH
 *
 * This file is part of the NOVA microhypervisor.
 *
 * NOVA is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * NOVA is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License version 2 for more details.
 */

#include "compiler.hpp"
#include "ec.hpp"
#include "sm.hpp"
#include "hip.hpp"
#include "msr.hpp"
#include "console_serial.hpp"
#include "acpi.hpp"
#include "ioapic.hpp"

extern "C" NORETURN
void bootstrap()
{
    static mword barrier;

    bool const resumed = !!Ec::current;

    if (resumed && Cpu::bsp) {
        barrier = 0;

        Console::enable_all();

        Ioapic::for_each([](auto ioapic) {
            ioapic->resume();
        });
    }

    Cpu::init(resumed);

    if (resumed) {
        // Barrier: wait for all ECs to arrive here
        for (Atomic::add (barrier, 1UL); barrier != Cpu::online; pause()) ;

        Msr::write (Msr::IA32_TSC, Acpi::resume_time);

        Timeout::sync();

        if (Cpu::bsp)
            Lapic::ap_code_cleanup();

        /* handle pending remote SC queue operation */
        Sc::rrq_handler();

        Sc::schedule();
    }

    // Create idle EC
    Ec::current = new (Pd::root) Ec (Pd::current = &Pd::kern, Ec::idle, Cpu::id);
    Ec::current->add_ref();
    Pd::current->add_ref();
    Space_obj::insert_root (Pd::kern.quota, Sc::current = new (Pd::root) Sc (&Pd::kern, Cpu::id, Ec::current));
    Sc::current->add_ref();
    Ec::ec_idle = Ec::current;

    // Barrier: wait for all ECs to arrive here
    for (Atomic::add (barrier, 1UL); barrier != Cpu::online; pause()) ;

    Msr::write (Msr::IA32_TSC, 0);

    // Create root task
    if (Cpu::bsp) {
        Hip::add_check();
        Ec *root_ec = new (Pd::root) Ec (&Pd::root, EC_ROOTTASK, &Pd::root, Ec::root_invoke, Cpu::id, 0, USER_ADDR - PAGE_H_SIZE - PAGE_SIZE, 0, nullptr);
        Sc *root_sc = new (Pd::root) Sc (&Pd::root, SC_ROOTTASK, root_ec, Cpu::id, Sc::default_prio, Sc::default_quantum);
        root_sc->remote_enqueue();

        Lapic::ap_code_cleanup();
    }

    Sc::schedule();
}
