3#include "pedigree/kernel/LockGuard.h"
4#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
5#include "pedigree/kernel/processor/VirtualAddressSpace.h"
11constexpr uintptr_t DirectMapBase = 0xffff000000000000ULL;
12constexpr uintptr_t ItsControl = 0x0000;
13constexpr uintptr_t ItsType = 0x0008;
14constexpr uintptr_t ItsCommandBase = 0x0080;
15constexpr uintptr_t ItsCommandWrite = 0x0088;
16constexpr uintptr_t ItsCommandRead = 0x0090;
17constexpr uintptr_t ItsBaser = 0x0100;
18constexpr uintptr_t RedistControl = 0x0000;
19constexpr uintptr_t RedistType = 0x0008;
20constexpr uintptr_t RedistPropertyBase = 0x0070;
21constexpr uintptr_t RedistPendingBase = 0x0078;
22constexpr uint64_t TableValid = 1ULL << 63;
23constexpr uint64_t InnerShareable = 1ULL << 10;
24constexpr uint64_t CacheableBaser = 7ULL << 59;
25constexpr uint64_t CacheableRedist = 7ULL << 7;
26constexpr size_t PageSize = 4096;
27constexpr size_t LpiTableAlignment = 65536;
29uint32_t read32(uintptr_t base, uintptr_t offset) {
31 asm volatile(
"ldr %w0, [%1]" :
"=r"(value) :
"r"(base + offset) :
"memory");
35uint64_t read64(uintptr_t base, uintptr_t offset) {
37 asm volatile(
"ldr %0, [%1]" :
"=r"(value) :
"r"(base + offset) :
"memory");
41void write32(uintptr_t base, uintptr_t offset, uint32_t value) {
42 asm volatile(
"str %w1, [%0]" : :
"r"(base + offset),
"r"(value) :
"memory");
45void write64(uintptr_t base, uintptr_t offset, uint64_t value) {
46 asm volatile(
"str %1, [%0]" : :
"r"(base + offset),
"r"(value) :
"memory");
50 asm volatile(
"dsb sy\n\tisb" : : :
"memory");
56 : m_DeviceTable(
"ITS device table"),
57 m_CollectionTable(
"ITS collection table"),
58 m_CommandQueue(
"ITS command queue"),
59 m_Properties(
"GIC LPI properties"),
60 m_Pending(
"GIC LPI pending"),
68 m_CommandVirtual(nullptr),
69 m_PropertyVirtual(nullptr),
76bool GicIts::ready()
const {
80uint64_t GicIts::messageAddress()
const {
81 return m_Ready ? m_PhysicalBase + 0x10040 : 0;
84bool GicIts::allocateAligned(
MemoryRegion& region,
size_t size,
size_t alignment,
85 uint64_t& physical, uint8_t*& virtualAddress) {
87 if (!size || !alignment || (alignment & (alignment - 1)) ||
88 size > SIZE_MAX - alignment + PageSize) {
91 const size_t pages = (size + alignment - PageSize + PageSize - 1) / PageSize;
98 const size_t offset = (alignment - (base & (alignment - 1))) & (alignment - 1);
99 if (offset > region.
size() || size > region.
size() - offset) {
103 physical = base + offset;
104 virtualAddress =
static_cast<uint8_t*
>(region.
virtualAddress()) + offset;
105 memset(virtualAddress, 0, size);
113 (void)virtualAddress;
118bool GicIts::setupBaser(uint32_t type,
MemoryRegion& region,
size_t requiredEntries) {
120 for (
size_t index = 0; index < 8; ++index) {
121 const uintptr_t offset = ItsBaser + index * 8;
122 const uint64_t original = read64(m_Base, offset);
123 if (((original >> 56) & 7) != type) {
126 const size_t entrySize = ((original >> 48) & 31) + 1;
127 if (requiredEntries > SIZE_MAX / entrySize) {
130 const size_t bytes = requiredEntries * entrySize;
131 const size_t pages = (bytes + PageSize - 1) / PageSize;
132 if (!pages || pages > 256) {
135 uint64_t physical = 0;
136 uint8_t* virtualAddress =
nullptr;
137 if (!allocateAligned(region, pages * PageSize, PageSize, physical, virtualAddress)) {
140 const uint64_t value = (original & ((7ULL << 56) | (31ULL << 48))) | physical | CacheableBaser |
141 InnerShareable | TableValid | (pages - 1);
142 write64(m_Base, offset, value);
144 const uint64_t actual = read64(m_Base, offset);
145 const uint64_t mask =
146 TableValid | (0x000ffffffffff000ULL) | (3ULL << 10) | (3ULL << 8) | (7ULL << 59) | 0xffULL;
147 return (actual & mask) == (value & mask);
152 (void)requiredEntries;
157bool GicIts::send(uint64_t word0, uint64_t word1, uint64_t word2) {
160 if (!m_CommandVirtual || !m_CommandSize) {
163 auto* command =
reinterpret_cast<volatile uint64_t*
>(m_CommandVirtual + m_CommandWrite);
169 const size_t next = (m_CommandWrite + 32) % m_CommandSize;
170 write64(m_Base, ItsCommandWrite, next);
171 for (
size_t wait = 0; wait < 100000; ++wait) {
172 const uint64_t
reader = read64(m_Base, ItsCommandRead);
176 if ((
reader & (m_CommandSize - 1)) == next) {
177 m_CommandWrite = next;
180 asm volatile(
"yield");
191 return send(0x05, 0, m_Target);
194void GicIts::releaseTables() {
196 bool quiescent =
true;
198 write32(m_Base, ItsControl, read32(m_Base, ItsControl) & ~1U);
200 while (wait < 100000 && !(read32(m_Base, ItsControl) & (1U << 31))) {
202 asm volatile(
"yield");
204 quiescent = wait < 100000;
206 if (m_Redistributor) {
207 write32(m_Redistributor, RedistControl, read32(m_Redistributor, RedistControl) & ~1U);
209 while (wait < 100000 && (read32(m_Redistributor, RedistControl) & (1U << 3))) {
211 asm volatile(
"yield");
213 quiescent = quiescent && wait < 100000;
221 m_DeviceTable.free();
223 if (m_CollectionTable) {
224 m_CollectionTable.free();
226 if (m_CommandQueue) {
227 m_CommandQueue.free();
238bool GicIts::initialise(
const VirtMsiController& controller, uintptr_t redistributor,
239 uint64_t redistributorPhysical) {
244 if (controller.type != VirtMsiController::Type::GicV3Its || controller.size < 0x20000 ||
245 !redistributor || !redistributorPhysical || controller.base >= (64ULL << 30) ||
246 controller.base > UINT64_MAX - 0x10040) {
249 m_Base = DirectMapBase + controller.base;
250 m_PhysicalBase = controller.base;
251 m_Redistributor = redistributor;
252 if ((read32(m_Base, ItsControl) & 1U) || (read32(redistributor, RedistControl) & 1U) ||
253 !(read64(m_Base, ItsType) & 1U) || !(read64(redistributor, RedistType) & 1U)) {
256 const uint64_t typer = read64(m_Base, ItsType);
257 const uint32_t deviceBits = ((typer >> 13) & 31) + 1;
258 const uint32_t eventBits = ((typer >> 8) & 31) + 1;
259 const uintptr_t distributor = VirtDeviceTree::gicDistributorBase();
263 const uint32_t lpiBits = ((read32(distributor, 0x4) >> 19) & 31) + 1;
267 m_DeviceLimit = deviceBits < 16 ? 1U << deviceBits : 65536;
268 m_EventBits = eventBits;
269 const uint32_t usableLpiBits = lpiBits < 16 ? lpiBits : 16;
271 uint64_t propertyPhysical = 0, pendingPhysical = 0, commandPhysical = 0;
272 uint8_t* pendingVirtual =
nullptr;
273 if (!allocateAligned(m_Properties, LpiTableAlignment, LpiTableAlignment, propertyPhysical,
274 m_PropertyVirtual) ||
275 !allocateAligned(m_Pending, LpiTableAlignment, LpiTableAlignment, pendingPhysical,
280 memset(m_PropertyVirtual, 0xa2, LpiTableAlignment);
283 const uint64_t prop = propertyPhysical | CacheableRedist | InnerShareable | (usableLpiBits - 1);
284 const uint64_t pend = pendingPhysical | CacheableRedist | InnerShareable;
285 write64(redistributor, RedistPropertyBase, prop);
286 write64(redistributor, RedistPendingBase, pend);
288 const uint64_t propMask = 0x000ffffffffff000ULL | (7ULL << 7) | (3ULL << 10) | 31ULL;
289 const uint64_t pendMask = 0x000fffffffff0000ULL | (7ULL << 7) | (3ULL << 10);
290 if ((read64(redistributor, RedistPropertyBase) & propMask) != (prop & propMask) ||
291 (read64(redistributor, RedistPendingBase) & pendMask) != (pend & pendMask) ||
292 !setupBaser(1, m_DeviceTable, m_DeviceLimit) || !setupBaser(4, m_CollectionTable, 1) ||
293 !allocateAligned(m_CommandQueue, PageSize, PageSize, commandPhysical, m_CommandVirtual)) {
298 m_CommandSize = PageSize;
299 const uint64_t cbaser = commandPhysical | CacheableBaser | InnerShareable | TableValid;
300 write64(m_Base, ItsCommandBase, cbaser);
302 const uint64_t cbaserMask =
303 0x000ffffffffff000ULL | (7ULL << 59) | (3ULL << 10) | TableValid | 0xffULL;
304 if ((read64(m_Base, ItsCommandBase) & cbaserMask) != (cbaser & cbaserMask)) {
308 write64(m_Base, ItsCommandWrite, 0);
310 m_Target = (typer & (1ULL << 19)) ? redistributorPhysical
311 : ((read64(redistributor, RedistType) >> 8) & 0xffff) << 16;
312 if (m_Target & 0xffff) {
316 write32(redistributor, RedistControl, read32(redistributor, RedistControl) | 1U);
317 write32(m_Base, ItsControl, read32(m_Base, ItsControl) | 1U);
319 if (!(read32(redistributor, RedistControl) & 1U) || !(read32(m_Base, ItsControl) & 1U) ||
320 !send(0x09, 0, m_Target | TableValid) || !send(0x0d, 0, 0) || !sync()) {
329 (void)redistributorPhysical;
334bool GicIts::mapDevice(
size_t slot, uint32_t deviceId, uint32_t lpi) {
335 return lpi == FirstLpi + slot && mapDeviceGroup(&slot, 1, deviceId);
338bool GicIts::mapDeviceGroup(
const size_t* slots,
size_t count, uint32_t deviceId) {
341 if (!m_Ready || !slots || !count || count > MaxMappings || deviceId >= m_DeviceLimit) {
345 while ((
size_t(2) << size) < count) {
348 if (size >= m_EventBits) {
351 for (
size_t i = 0; i < count; ++i) {
352 if (slots[i] >= MaxMappings || m_Mappings[slots[i]].valid) {
355 for (
size_t j = 0; j < i; ++j) {
356 if (slots[j] == slots[i]) {
365 memset(
reinterpret_cast<void*
>(DirectMapBase + itt), 0, PageSize);
367 bool mapped = send(0x08 | (uint64_t(deviceId) << 32), size, itt | TableValid);
368 for (
size_t i = 0; mapped && i < count; ++i) {
369 mapped = send(0x0a | (uint64_t(deviceId) << 32), (uint64_t(FirstLpi + slots[i]) << 32) | i, 0);
371 mapped = mapped && sync();
373 if (send(0x08 | (uint64_t(deviceId) << 32), 0, 0) && sync()) {
376 for (
size_t i = 0; i < count; ++i) {
377 m_Mappings[slots[i]] = {deviceId,
static_cast<uint32_t
>(i), itt,
true};
382 for (
size_t i = 0; i < count; ++i) {
383 m_Mappings[slots[i]] = {deviceId,
static_cast<uint32_t
>(i), itt,
true};
394bool GicIts::mappingActive(
size_t slot)
const {
396 return slot < MaxMappings && m_Mappings[slot].valid;
399bool GicIts::unmapDevice(
size_t slot) {
402 if (!m_Ready || slot >= MaxMappings || !m_Mappings[slot].valid) {
405 const Mapping mapping = m_Mappings[slot];
407 for (
size_t i = 0; i < MaxMappings; ++i) {
408 if (i != slot && m_Mappings[i].valid && m_Mappings[i].deviceId == mapping.deviceId) {
413 const bool removed = last ? send(0x08 | (uint64_t(mapping.deviceId) << 32), 0, 0)
414 : send(0x0f | (uint64_t(mapping.deviceId) << 32), mapping.eventId, 0);
415 if (!removed || !sync()) {
421 m_Mappings[slot] = {};
429bool GicIts::setEnabled(uint32_t lpi,
bool enabled) {
432 if (!m_Ready || lpi < FirstLpi || lpi >= FirstLpi + MaxMappings ||
433 !m_Mappings[lpi - FirstLpi].valid) {
436 const Mapping& mapping = m_Mappings[lpi - FirstLpi];
437 m_PropertyVirtual[lpi - FirstLpi] = 0xa2 | (enabled ? 1U : 0U);
439 return send(0x0c | (uint64_t(mapping.deviceId) << 32), mapping.eventId, 0) && sync();
Special memory entity in the kernel's virtual address space.
void * virtualAddress() const
physical_uintptr_t physicalAddress() const
static const size_t continuous
static PhysicalMemoryManager & instance()
virtual void freePage(physical_uintptr_t page)=0
virtual bool allocateRegion(MemoryRegion &Region, size_t cPages, size_t pageConstraints, size_t Flags, physical_uintptr_t start=-1)=0
static const size_t KernelMode
static const size_t Write