genode/repos/base-hw/src/core/spec/smp/kernel/cpu.cc

109 lines
2.2 KiB
C++

/*
* \brief ARM with SMP support specific aspects of the kernel cpu objects
* \author Stefan Kalkowski
* \author Martin Stein
* \date 2016-01-07
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* core includes */
#include <kernel/cpu.h>
namespace Kernel
{
/**
* Lists all pending domain updates
*/
class Cpu_domain_update_list;
}
class Kernel::Cpu_domain_update_list
: public Double_list_typed<Cpu_domain_update>
{
public:
/**
* Perform all pending domain updates on the executing CPU
*/
void do_each() {
for_each([] (Cpu_domain_update * const u) { u->_do(); }); }
};
using namespace Kernel;
/**
* Return singleton of the CPU domain-udpate list
*/
Cpu_domain_update_list & cpu_domain_update_list() {
return *unmanaged_singleton<Cpu_domain_update_list>(); }
void Cpu::Ipi::occurred()
{
cpu_domain_update_list().do_each();
pending = false;
}
void Cpu::Ipi::trigger(unsigned const cpu_id)
{
if (pending) return;
pic()->send_ipi(cpu_id);
pending = true;
}
Cpu::Ipi::Ipi(Irq::Pool &p) : Irq(Pic::IPI, p) { }
/***********************
** Cpu_domain_update **
***********************/
void Cpu_domain_update::_do()
{
/* perform domain update locally and get pending bit */
unsigned const id = Cpu::executing_id();
if (!_pending[id]) return;
_domain_update();
_pending[id] = false;
/* check wether there are still CPUs pending */
for (unsigned i = 0; i < NR_OF_CPUS; i++)
if (_pending[i]) return;
/* as no CPU is pending anymore, end the domain update */
cpu_domain_update_list().remove(this);
_cpu_domain_update_unblocks();
}
bool Cpu_domain_update::_do_global(unsigned const domain_id)
{
/* perform locally and leave it at that if in uniprocessor mode */
_domain_id = domain_id;
_domain_update();
if (NR_OF_CPUS == 1) return false;
/* inform other CPUs and block until they are done */
cpu_domain_update_list().insert_tail(this);
unsigned const cpu_id = Cpu::executing_id();
for (unsigned i = 0; i < NR_OF_CPUS; i++) {
if (i == cpu_id) continue;
_pending[i] = true;
cpu_pool()->cpu(i)->trigger_ip_interrupt();
}
return true;
}