mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-24 03:06:17 +00:00
thermosphere: begin to write virtual gic code in C++
This commit is contained in:
parent
b21c75b22b
commit
02bbe1bb40
6 changed files with 586 additions and 36 deletions
|
@ -34,3 +34,8 @@
|
||||||
static cl instance;\
|
static cl instance;\
|
||||||
public:\
|
public:\
|
||||||
static cl &GetInstance() { return instance; }
|
static cl &GetInstance() { return instance; }
|
||||||
|
|
||||||
|
//FIXME
|
||||||
|
#ifndef ENSURE
|
||||||
|
#define ENSURE(...)
|
||||||
|
#endif
|
||||||
|
|
|
@ -151,9 +151,9 @@ namespace ams::hvisor {
|
||||||
ClearInterruptPending(id);
|
ClearInterruptPending(id);
|
||||||
if (id >= 32) {
|
if (id >= 32) {
|
||||||
SetInterruptMode(id, isLevelSensitive);
|
SetInterruptMode(id, isLevelSensitive);
|
||||||
DoSetInterruptAffinity(id, 0xFF); // all possible processors
|
SetInterruptTargets(id, 0xFF); // all possible processors
|
||||||
}
|
}
|
||||||
SetInterruptShiftedPriority(id, prio << m_priorityShift);
|
SetInterruptPriority(id, prio);
|
||||||
SetInterruptEnabled(id);
|
SetInterruptEnabled(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ namespace ams::hvisor {
|
||||||
cpu::InterruptMaskGuard mg{};
|
cpu::InterruptMaskGuard mg{};
|
||||||
std::scoped_lock lk{m_lock};
|
std::scoped_lock lk{m_lock};
|
||||||
|
|
||||||
DoSetInterruptAffinity(id, affinity);
|
SetInterruptTargets(id, affinity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IrqManager::HandleInterrupt(ExceptionStackFrame *frame)
|
void IrqManager::HandleInterrupt(ExceptionStackFrame *frame)
|
||||||
|
|
|
@ -37,11 +37,12 @@ namespace ams::hvisor {
|
||||||
|
|
||||||
static bool IsGuestInterrupt(u32 id);
|
static bool IsGuestInterrupt(u32 id);
|
||||||
|
|
||||||
|
static u32 GetTypeRegister() { return gicd->typer; }
|
||||||
static void SetInterruptEnabled(u32 id) { gicd->isenabler[id / 32] = BIT(id % 32); }
|
static void SetInterruptEnabled(u32 id) { gicd->isenabler[id / 32] = BIT(id % 32); }
|
||||||
static void ClearInterruptEnabled(u32 id) { gicd->icenabler[id / 32] = BIT(id % 32); }
|
static void ClearInterruptEnabled(u32 id) { gicd->icenabler[id / 32] = BIT(id % 32); }
|
||||||
static void ClearInterruptPending(u32 id) { gicd->icpendr[id / 32] = BIT(id % 32); }
|
static void ClearInterruptPending(u32 id) { gicd->icpendr[id / 32] = BIT(id % 32); }
|
||||||
static void SetInterruptShiftedPriority(u32 id, u8 prio) { gicd->ipriorityr[id] = prio; }
|
static void SetInterruptShiftedPriority(u32 id, u8 prio) { gicd->ipriorityr[id] = prio; }
|
||||||
static void DoSetInterruptAffinity(u32 id, u8 targetList) { gicd->itargetsr[id] = targetList; }
|
static void SetInterruptTargets(u32 id, u8 targetList) { gicd->itargetsr[id] = targetList; }
|
||||||
static bool IsInterruptLevelSensitive(u32 id)
|
static bool IsInterruptLevelSensitive(u32 id)
|
||||||
{
|
{
|
||||||
return ((gicd->icfgr[id / 16] >> GicV2Distributor::GetCfgrShift(id)) & 2) != 0;
|
return ((gicd->icfgr[id / 16] >> GicV2Distributor::GetCfgrShift(id)) & 2) != 0;
|
||||||
|
@ -50,7 +51,7 @@ namespace ams::hvisor {
|
||||||
{
|
{
|
||||||
u32 cfgw = gicd->icfgr[id / 16];
|
u32 cfgw = gicd->icfgr[id / 16];
|
||||||
cfgw &= ~(2 << GicV2Distributor::GetCfgrShift(id));
|
cfgw &= ~(2 << GicV2Distributor::GetCfgrShift(id));
|
||||||
cfgw |= (isLevelSensitive ? 2 : 0) << GicV2Distributor::GetCfgrShift(id);
|
cfgw |= (!isLevelSensitive ? 2 : 0) << GicV2Distributor::GetCfgrShift(id);
|
||||||
gicd->icfgr[id / 16] = cfgw;
|
gicd->icfgr[id / 16] = cfgw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +68,8 @@ namespace ams::hvisor {
|
||||||
u8 m_numListRegisters = 0;
|
u8 m_numListRegisters = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void SetInterruptPriority(u32 id, u8 prio) { SetInterruptShiftedPriority(id, prio << m_priorityShift); }
|
||||||
|
|
||||||
void InitializeGic();
|
void InitializeGic();
|
||||||
void DoConfigureInterrupt(u32 id, u8 prio, bool isLevelSensitive);
|
void DoConfigureInterrupt(u32 id, u8 prio, bool isLevelSensitive);
|
||||||
|
|
||||||
|
|
248
thermosphere/src/hvisor_virtual_gic.cpp
Normal file
248
thermosphere/src/hvisor_virtual_gic.cpp
Normal file
|
@ -0,0 +1,248 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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 for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "hvisor_virtual_gic.hpp"
|
||||||
|
|
||||||
|
namespace ams::hvisor {
|
||||||
|
|
||||||
|
|
||||||
|
VirtualGic::VirqQueue::iterator VirtualGic::VirqQueue::insert(VirtualGic::VirqQueue::iterator pos, VirtualGic::VirqState &elem)
|
||||||
|
{
|
||||||
|
// Insert before
|
||||||
|
ENSURE(!elem.IsQueued());
|
||||||
|
|
||||||
|
// Empty list
|
||||||
|
if (begin() == end()) {
|
||||||
|
m_first = m_last = &elem;
|
||||||
|
elem.listPrev = elem.listNext = virqListEndIndex;
|
||||||
|
return begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos == end()) {
|
||||||
|
// Insert after last
|
||||||
|
VirqState &prev = back();
|
||||||
|
elem.listPrev = GetStateIndex(prev);
|
||||||
|
elem.listNext = prev.listNext;
|
||||||
|
prev.listNext = GetStateIndex(elem);
|
||||||
|
m_last = &elem;
|
||||||
|
} else {
|
||||||
|
u32 idx = GetStateIndex(elem);
|
||||||
|
u32 posidx = GetStateIndex(*pos);
|
||||||
|
u32 previdx = elem.listPrev;
|
||||||
|
|
||||||
|
elem.listNext = posidx;
|
||||||
|
elem.listPrev = previdx;
|
||||||
|
|
||||||
|
pos->listPrev = idx;
|
||||||
|
|
||||||
|
if (pos == begin()) {
|
||||||
|
m_first = &elem;
|
||||||
|
} else {
|
||||||
|
--pos;
|
||||||
|
pos->listNext = idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return iterator{&elem, m_storage};
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualGic::VirqQueue::iterator VirtualGic::VirqQueue::insert(VirtualGic::VirqState &elem)
|
||||||
|
{
|
||||||
|
// Insert in a stable sorted way
|
||||||
|
// Lower priority number is higher; we sort by descending priority, ie. ascending priority number
|
||||||
|
// Put the interrupts that were previously in the LR before the one which don't
|
||||||
|
return insert(
|
||||||
|
std::find_if(begin(), end(), [&a = elem](const VirqState &b) {
|
||||||
|
return a.priority == b.priority ? a.handled && !b.handled : a.priority < b.priority;
|
||||||
|
}),
|
||||||
|
elem
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualGic::VirqQueue::iterator VirtualGic::VirqQueue::erase(VirtualGic::VirqQueue::iterator startPos, VirtualGic::VirqQueue::iterator endPos)
|
||||||
|
{
|
||||||
|
VirqState &prev = m_storage[startPos->listPrev];
|
||||||
|
VirqState &next = *endPos;
|
||||||
|
u32 nextPos = GetStateIndex(*endPos);
|
||||||
|
|
||||||
|
if (startPos->listPrev != virqListEndIndex) {
|
||||||
|
prev.listNext = nextPos;
|
||||||
|
} else {
|
||||||
|
m_first = &next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextPos != virqListEndIndex) {
|
||||||
|
next.listPrev = startPos->listPrev;
|
||||||
|
} else {
|
||||||
|
m_last = &prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = startPos; it != endPos; ++it) {
|
||||||
|
it->listPrev = it->listNext = virqListInvalidIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VirtualGic::SetInterruptEnabledState(u32 id)
|
||||||
|
{
|
||||||
|
VirqState &state = GetVirqState(id);
|
||||||
|
|
||||||
|
if (id < 16 || !IrqManager::IsGuestInterrupt(id) || state.enabled) {
|
||||||
|
// Nothing to do...
|
||||||
|
// Also, ignore for SGIs
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Similar effects to setting the target list to non-0 when it was 0...
|
||||||
|
if (state.IsPending()) {
|
||||||
|
NotifyOtherCoreList(state.targetList);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.enabled = true;
|
||||||
|
IrqManager::SetInterruptEnabled(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VirtualGic::ClearInterruptEnabledState(u32 id)
|
||||||
|
{
|
||||||
|
VirqState &state = GetVirqState(id);
|
||||||
|
|
||||||
|
if (id < 16 || !IrqManager::IsGuestInterrupt(id) || !state.enabled) {
|
||||||
|
// Nothing to do...
|
||||||
|
// Also, ignore for SGIs
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Similar effects to setting the target list to 0, we may need to notify the core
|
||||||
|
// handling the interrupt if it's pending
|
||||||
|
if (state.handled) {
|
||||||
|
NotifyOtherCoreList(BIT(state.coreId));
|
||||||
|
}
|
||||||
|
|
||||||
|
state.enabled = false;
|
||||||
|
IrqManager::ClearInterruptEnabled(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VirtualGic::SetInterruptPriorityByte(u32 id, u8 priority)
|
||||||
|
{
|
||||||
|
if (!IrqManager::IsGuestInterrupt(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 32 priority levels max, bits [7:3]
|
||||||
|
priority >>= priorityShift;
|
||||||
|
|
||||||
|
if (id >= 16) {
|
||||||
|
// Ensure we have the correct priority on the physical distributor...
|
||||||
|
IrqManager::GetInstance().SetInterruptPriority(id, IrqManager::guestPriority);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirqState &state = GetVirqState(id);
|
||||||
|
if (priority == state.priority) {
|
||||||
|
// Nothing to do...
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.priority = priority;
|
||||||
|
u32 targets = state.targetList;
|
||||||
|
if (targets != 0 && state.IsPending()) {
|
||||||
|
NotifyOtherCoreList(targets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VirtualGic::SetInterruptTargets(u32 id, u8 coreList)
|
||||||
|
{
|
||||||
|
// Ignored for SGIs and PPIs, and non-guest interrupts
|
||||||
|
if (id < 32 || !IrqManager::IsGuestInterrupt(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interrupt not pending (inactive or active-only): nothing much to do (see reference manual)
|
||||||
|
// Otherwise, we may need to migrate the interrupt.
|
||||||
|
// In our model, while a physical interrupt can be pending on multiple cores, we decide that a pending SPI
|
||||||
|
// can only be handled on a single core (either it's in a LR, or in the global list), therefore we need to
|
||||||
|
// send a signal to (oldList XOR newList) to either put the interrupt back in the global list or potentially handle it
|
||||||
|
|
||||||
|
// Note that we take into account that the interrupt may be disabled.
|
||||||
|
VirqState &state = GetVirqState(id);
|
||||||
|
if (state.IsPending()) {
|
||||||
|
u8 oldList = state.targetList;
|
||||||
|
u8 diffList = (oldList ^ coreList) & getActiveCoreMask();
|
||||||
|
if (diffList != 0) {
|
||||||
|
NotifyOtherCoreList(diffList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.targetList = coreList;
|
||||||
|
IrqManager::SetInterruptTargets(id, state.targetList);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VirtualGic::SetInterruptConfigBits(u32 id, u32 config)
|
||||||
|
{
|
||||||
|
// Ignored for SGIs, implementation defined for PPIs
|
||||||
|
if (id < 32 || !IrqManager::IsGuestInterrupt(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirqState &state = GetVirqState(id);
|
||||||
|
|
||||||
|
// Expose bit(2n) as nonprogrammable to the guest no matter what the physical distributor actually behaves
|
||||||
|
bool newLvl = ((config & 2) << GicV2Distributor::GetCfgrShift(id)) == 0;
|
||||||
|
|
||||||
|
if (state.levelSensitive != newLvl) {
|
||||||
|
state.levelSensitive = newLvl;
|
||||||
|
IrqManager::SetInterruptMode(id, newLvl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VirtualGic::SetSgiPendingState(u32 id, u32 coreId, u32 srcCoreId)
|
||||||
|
{
|
||||||
|
VirqState &state = GetVirqState(coreId, id);
|
||||||
|
m_incomingSgiPendingSources[coreId][id] |= BIT(srcCoreId);
|
||||||
|
if (!state.handled && !state.IsQueued()) {
|
||||||
|
// The SGI was inactive on the target core...
|
||||||
|
state.SetPending();
|
||||||
|
state.srcCoreId = srcCoreId;
|
||||||
|
m_incomingSgiPendingSources[coreId][id] &= ~BIT(srcCoreId);
|
||||||
|
m_virqPendingQueue.insert(state);
|
||||||
|
NotifyOtherCoreList(BIT(coreId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VirtualGic::SendSgi(u32 id, GicV2Distributor::SgirTargetListFilter filter, u32 coreList)
|
||||||
|
{
|
||||||
|
switch (filter) {
|
||||||
|
case GicV2Distributor::ForwardToTargetList:
|
||||||
|
// Forward to coreList
|
||||||
|
break;
|
||||||
|
case GicV2Distributor::ForwardToAllOthers:
|
||||||
|
// Forward to all but current core
|
||||||
|
coreList = ~BIT(currentCoreCtx->coreId);
|
||||||
|
break;
|
||||||
|
case GicV2Distributor::ForwardToSelf:
|
||||||
|
// Forward to current core only
|
||||||
|
coreList = BIT(currentCoreCtx->coreId);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DEBUG("Emulated GCID_SGIR: invalid TargetListFilter value!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
coreList &= getActiveCoreMask();
|
||||||
|
for (u32 dstCore: util::BitsOf{coreList}) {
|
||||||
|
SetSgiPendingState(id, dstCore, currentCoreCtx->coreId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
325
thermosphere/src/hvisor_virtual_gic.hpp
Normal file
325
thermosphere/src/hvisor_virtual_gic.hpp
Normal file
|
@ -0,0 +1,325 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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 for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "defines.hpp"
|
||||||
|
#include "exceptions.h"
|
||||||
|
#include "cpu/hvisor_cpu_exception_sysregs.hpp"
|
||||||
|
#include "hvisor_irq_manager.hpp"
|
||||||
|
#include "memory_map.h"
|
||||||
|
|
||||||
|
namespace ams::hvisor {
|
||||||
|
|
||||||
|
class VirtualGic final {
|
||||||
|
SINGLETON(VirtualGic);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// For convenience, although they're already defined in irq manager header:
|
||||||
|
static inline volatile auto *const gicd = IrqManager::gicd;
|
||||||
|
static inline volatile auto *const gich = IrqManager::gich;
|
||||||
|
|
||||||
|
// Architectural properties
|
||||||
|
static constexpr u32 priorityShift = 3;
|
||||||
|
|
||||||
|
// List managament constants
|
||||||
|
static constexpr u32 spiEndIndex = GicV2Distributor::maxIrqId + 1 - 32;
|
||||||
|
static constexpr u32 maxNumIntStates = spiEndIndex + MAX_CORE * 32;
|
||||||
|
static constexpr u32 virqListEndIndex = maxNumIntStates;
|
||||||
|
static constexpr u32 virqListInvalidIndex = virqListEndIndex + 1;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct VirqState {
|
||||||
|
u32 listPrev : 11;
|
||||||
|
u32 listNext : 11;
|
||||||
|
u32 irqId : 10;
|
||||||
|
u32 priority : 5;
|
||||||
|
bool pending : 1;
|
||||||
|
bool active : 1;
|
||||||
|
bool handled : 1;
|
||||||
|
bool pendingLatch : 1;
|
||||||
|
bool levelSensitive : 1;
|
||||||
|
u32 coreId : 3;
|
||||||
|
u32 targetList : 8;
|
||||||
|
u32 srcCoreId : 3;
|
||||||
|
bool enabled : 1;
|
||||||
|
u64 : 0;
|
||||||
|
|
||||||
|
constexpr bool IsPending() const
|
||||||
|
{
|
||||||
|
return pendingLatch || (levelSensitive && pending);
|
||||||
|
}
|
||||||
|
constexpr void SetPending()
|
||||||
|
{
|
||||||
|
if (levelSensitive) {
|
||||||
|
pending = true;
|
||||||
|
} else {
|
||||||
|
pendingLatch = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
constexpr bool ClearPendingLine()
|
||||||
|
{
|
||||||
|
// Don't clear pending latch status
|
||||||
|
pending = false;
|
||||||
|
}
|
||||||
|
constexpr bool ClearPending()
|
||||||
|
{
|
||||||
|
// On ack, both pending line status and latch are cleared
|
||||||
|
pending = false;
|
||||||
|
pendingLatch = false;
|
||||||
|
}
|
||||||
|
constexpr bool IsQueued() const
|
||||||
|
{
|
||||||
|
return listPrev != virqListInvalidIndex && listNext != virqListInvalidIndex;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class VirqQueue final {
|
||||||
|
private:
|
||||||
|
VirqState *m_first = nullptr;
|
||||||
|
VirqState *m_last = nullptr;
|
||||||
|
VirqState *m_storage = nullptr;
|
||||||
|
public:
|
||||||
|
template<bool isConst>
|
||||||
|
class Iterator {
|
||||||
|
friend class Iterator<true>;
|
||||||
|
friend class VirqQueue;
|
||||||
|
private:
|
||||||
|
VirqState *m_node = nullptr;
|
||||||
|
VirqState *m_storage = nullptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit constexpr Iterator(VirqState *node, VirqState *storage) : m_node{node}, m_storage{storage} {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// allow implicit const->non-const
|
||||||
|
constexpr Iterator(const Iterator<false> &other) : m_node{other.m_storage}, m_storage{other.m_storage} {}
|
||||||
|
constexpr Iterator() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using iterator_category = std::bidirectional_iterator_tag;
|
||||||
|
using value_type = VirqState;
|
||||||
|
using difference_type = ptrdiff_t;
|
||||||
|
using pointer = typename std::conditional<isConst, const VirqState *, VirqState *>::type;
|
||||||
|
using reference = typename std::conditional<isConst, const VirqState &, VirqState &>::type;
|
||||||
|
|
||||||
|
constexpr bool operator==(const Iterator &other) const { return m_node == other.m_node; }
|
||||||
|
constexpr bool operator!=(const Iterator &other) const { return !(*this == other); }
|
||||||
|
constexpr reference operator*() { return *m_node; }
|
||||||
|
constexpr pointer operator->() { return m_node; }
|
||||||
|
|
||||||
|
constexpr Iterator &operator++()
|
||||||
|
{
|
||||||
|
m_node = &m_storage[m_node->listNext];
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Iterator &operator--()
|
||||||
|
{
|
||||||
|
m_node = &m_storage[m_node->listPrev];
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Iterator &operator++(int)
|
||||||
|
{
|
||||||
|
const Iterator v{*this};
|
||||||
|
++(*this);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Iterator &operator--(int)
|
||||||
|
{
|
||||||
|
const Iterator v{*this};
|
||||||
|
--(*this);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr u32 GetStateIndex(VirqState &elem) { return static_cast<u32>(&elem - &m_storage[0]); }
|
||||||
|
public:
|
||||||
|
using pointer = VirqState *;
|
||||||
|
using const_pointer = const VirqState *;
|
||||||
|
using reference = VirqState &;
|
||||||
|
using const_reference = const VirqState &;
|
||||||
|
using value_type = VirqState;
|
||||||
|
using size_type = size_t;
|
||||||
|
using difference_type = ptrdiff_t;
|
||||||
|
using iterator = Iterator<false>;
|
||||||
|
using const_iterator = Iterator<true>;
|
||||||
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||||
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||||
|
|
||||||
|
constexpr void Initialize(VirqState *storage) { m_storage = storage; }
|
||||||
|
|
||||||
|
constexpr VirqState &front() { return *m_first; };
|
||||||
|
constexpr const VirqState &front() const { return *m_first; };
|
||||||
|
|
||||||
|
constexpr VirqState &back() { return *m_last; };
|
||||||
|
constexpr const VirqState &back() const { return *m_last; };
|
||||||
|
|
||||||
|
constexpr const_iterator cbegin() const { return const_iterator{m_first, m_storage}; }
|
||||||
|
constexpr const_iterator cend() const { return const_iterator{&m_storage[virqListEndIndex], m_storage}; }
|
||||||
|
|
||||||
|
constexpr const_iterator begin() const { return cbegin(); }
|
||||||
|
constexpr const_iterator end() const { return cend(); }
|
||||||
|
|
||||||
|
constexpr iterator begin() { return iterator{m_first, m_storage}; }
|
||||||
|
constexpr iterator end() { return iterator{&m_storage[virqListEndIndex], m_storage}; }
|
||||||
|
|
||||||
|
constexpr const_reverse_iterator crbegin() const {
|
||||||
|
return const_reverse_iterator{const_iterator{m_last, m_storage}};
|
||||||
|
}
|
||||||
|
constexpr const_reverse_iterator crend() const { return const_reverse_iterator{cend()}; }
|
||||||
|
|
||||||
|
constexpr const_reverse_iterator rbegin() const { return crbegin(); }
|
||||||
|
constexpr const_reverse_iterator rend() const { return crend(); }
|
||||||
|
|
||||||
|
constexpr reverse_iterator rbegin() { return reverse_iterator{iterator{m_first, m_storage}}; }
|
||||||
|
constexpr reverse_iterator rend() { return reverse_iterator{end()}; }
|
||||||
|
|
||||||
|
|
||||||
|
iterator insert(iterator pos, VirqState &elem);
|
||||||
|
iterator insert(VirqState &elem);
|
||||||
|
|
||||||
|
iterator erase(iterator startPos, iterator endPos);
|
||||||
|
|
||||||
|
iterator erase(iterator pos) { return erase(pos, std::next(pos)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void NotifyOtherCoreList(u32 coreList)
|
||||||
|
{
|
||||||
|
coreList &= ~BIT(currentCoreCtx->coreId);
|
||||||
|
if (coreList != 0) {
|
||||||
|
IrqManager::GenerateSgiForList(IrqManager::VgicUpdateSgi, coreList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NotifyAllOtherCores()
|
||||||
|
{
|
||||||
|
IrqManager::GenerateSgiForAllOthers(IrqManager::VgicUpdateSgi);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::array<VirqState, maxNumIntStates> m_virqStates{};
|
||||||
|
std::array<std::array<u8, 32>, MAX_CORE> m_incomingSgiPendingSources{};
|
||||||
|
|
||||||
|
VirqQueue m_virqPendingQueue{};
|
||||||
|
bool m_distributorEnabled = false;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
constexpr VirqState &GetVirqState(u32 coreId, u32 id)
|
||||||
|
{
|
||||||
|
if (id >= 32) {
|
||||||
|
return m_virqStates[id - 32];
|
||||||
|
} else if (id <= GicV2Distributor::maxIrqId) {
|
||||||
|
return m_virqStates[spiEndIndex + 32 * coreId + id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VirqState &GetVirqState(u32 id) { return GetVirqState(currentCoreCtx->coreId, id); }
|
||||||
|
|
||||||
|
void SetDistributorControlRegister(u32 value)
|
||||||
|
{
|
||||||
|
// We implement a virtual distributor/interface w/o security extensions.
|
||||||
|
// Moreover, we forward all interrupts as Group 0 so that non-secure code that assumes GICv2
|
||||||
|
// *with* security extensions (and thus all interrupts fw as group 1 there) still works (bit are in the same positions).
|
||||||
|
|
||||||
|
// We don't implement Group 1 interrupts, either (so that's similar to GICv1).
|
||||||
|
bool old = m_distributorEnabled;
|
||||||
|
m_distributorEnabled = (value & 1) != 0;
|
||||||
|
|
||||||
|
// Enable bit is actually just a global enable bit for all irq forwarding, other functions of the GICD aren't affected by it
|
||||||
|
if (old != m_distributorEnabled) {
|
||||||
|
NotifyAllOtherCores();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 vgicGetDistributorControlRegister(void)
|
||||||
|
{
|
||||||
|
return m_distributorEnabled ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 vgicGetDistributorTypeRegister(void)
|
||||||
|
{
|
||||||
|
// See above comment.
|
||||||
|
// Therefore, LSPI = 0, SecurityExtn = 0, rest = from physical distributor
|
||||||
|
return IrqManager::GetTypeRegister() & 0x7F;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetDistributorImplementerIdentificationRegister(void)
|
||||||
|
{
|
||||||
|
u32 iidr = 'A' << 24; // Product Id: Atmosphère (?)
|
||||||
|
iidr |= 2 << 16; // Major revision 2 (GICv2)
|
||||||
|
iidr |= 0 << 12; // Minor revision 0
|
||||||
|
iidr |= 0x43B; // Implementer: Arm (value copied from physical GICD)
|
||||||
|
return iidr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetInterruptEnabledState(u32 id)
|
||||||
|
{
|
||||||
|
// SGIs are always enabled
|
||||||
|
return id < 16 || (IrqManager::IsGuestInterrupt(id) && GetVirqState(currentCoreCtx->coreId, id).enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 GetInterruptPriorityByte(u32 id)
|
||||||
|
{
|
||||||
|
return IrqManager::IsGuestInterrupt(id) ? GetVirqState(currentCoreCtx->coreId, id).priority << priorityShift : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 GetInterruptTargets(u16 id)
|
||||||
|
{
|
||||||
|
return id < 32 || (IrqManager::IsGuestInterrupt(id) && GetVirqState(currentCoreCtx->coreId, id).targetList);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetInterruptConfigBits(u16 id)
|
||||||
|
{
|
||||||
|
u32 oneNModel = id < 32 || !IrqManager::IsGuestInterrupt(id) ? 0 : 1;
|
||||||
|
return (IrqManager::IsGuestInterrupt(id) && !GetVirqState(id).levelSensitive) ? 2 | oneNModel : oneNModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetPeripheralId2Register(void)
|
||||||
|
{
|
||||||
|
return 2u << 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetInterruptEnabledState(u32 id);
|
||||||
|
void ClearInterruptEnabledState(u32 id);
|
||||||
|
void SetInterruptPriorityByte(u32 id, u8 priority);
|
||||||
|
void SetInterruptTargets(u32 id, u8 coreList);
|
||||||
|
void SetInterruptConfigBits(u32 id, u32 config);
|
||||||
|
void SetSgiPendingState(u32 id, u32 coreId, u32 srcCoreId);
|
||||||
|
void SendSgi(u32 id, GicV2Distributor::SgirTargetListFilter filter, u32 coreList);
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*bool vgicValidateGicdRegisterAccess(size_t offset, size_t sz);
|
||||||
|
void vgicWriteGicdRegister(u32 val, size_t offset, size_t sz);
|
||||||
|
u32 vgicReadGicdRegister(size_t offset, size_t sz);
|
||||||
|
|
||||||
|
void handleVgicdMmio(ExceptionStackFrame *frame, cpu::DataAbortIss dabtIss, size_t offset);
|
||||||
|
|
||||||
|
void vgicInit(void);
|
||||||
|
void vgicUpdateState(void);
|
||||||
|
void vgicMaintenanceInterruptHandler(void);
|
||||||
|
void vgicEnqueuePhysicalIrq(u16 irqId);*/
|
|
@ -1,31 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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 for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
#include "data_abort.h"
|
|
||||||
|
|
||||||
bool vgicValidateGicdRegisterAccess(size_t offset, size_t sz);
|
|
||||||
void vgicWriteGicdRegister(u32 val, size_t offset, size_t sz);
|
|
||||||
u32 vgicReadGicdRegister(size_t offset, size_t sz);
|
|
||||||
|
|
||||||
void handleVgicdMmio(ExceptionStackFrame *frame, DataAbortIss dabtIss, size_t offset);
|
|
||||||
|
|
||||||
void vgicInit(void);
|
|
||||||
void vgicUpdateState(void);
|
|
||||||
void vgicMaintenanceInterruptHandler(void);
|
|
||||||
void vgicEnqueuePhysicalIrq(u16 irqId);
|
|
Loading…
Reference in a new issue