memory: handle GPU faults at the mapped boundary

Co-authored-by: CheesyPoofs346 <jjs777406@gmail.com>
This commit is contained in:
nmzik
2026-09-06 11:08:05 +04:00
parent 50e78d717d
commit d83f1f8235
2 changed files with 49 additions and 1 deletions
@@ -12,7 +12,9 @@ GpuResourceManager::GpuResourceManager(GraphicContext& graphics, CommandSchedule
GpuResourceManager::~GpuResourceManager() = default;
bool GpuResourceManager::HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept {
constexpr uint64_t fault_size = 8;
// The host reports the faulting byte, not the instruction's access width. Both caches
// resolve its page; guessing a width can cross the end of a valid guest mapping.
constexpr uint64_t fault_size = 1;
if (!IsMapped(fault_vaddr, fault_size)) {
return false;
}
+46
View File
@@ -7620,6 +7620,52 @@ public:
m_device.destroyDescriptorSetLayout(observer_set_layout, nullptr);
m_device.destroyShaderModule(ms_depth_module, nullptr);
constexpr uint64_t mapped_end = base + allocation_size;
constexpr uint64_t final_word_address = mapped_end - sizeof(uint32_t);
auto *final_word = reinterpret_cast<volatile uint32_t *>(final_word_address);
auto *final_byte = reinterpret_cast<volatile uint8_t *>(mapped_end - 1);
Require(name, "fault boundary containment",
resources.IsMapped(final_word_address, sizeof(uint32_t)) &&
!resources.IsMapped(final_word_address, sizeof(uint64_t)) &&
!resources.HandleFault(PageFaultAccess::Write, mapped_end) &&
!resources.HandleFault(PageFaultAccess::Read, mapped_end),
"fault handling changed the mapped range's exclusive end");
Require(name, "fault boundary setup",
resources.InvalidateMemory(final_word_address, sizeof(uint32_t)),
"the final mapped word could not be initialized");
*final_word = 0;
auto final_word_desc = MakeLinearDesc(
final_word_address, sizeof(uint32_t), vk::Format::eR32Uint,
Prospero::BufferFormat::k32UInt, Prospero::ImageType::kColor2D,
{1, 1, 1}, 1, 4, 1);
const auto final_word_image = texture_cache.FindImage(final_word_desc);
(void)texture_cache.FindTexture(final_word_image, final_word_desc);
Require(name, "fault boundary image tracking",
texture_cache.GetImage(final_word_image).IsTracked() &&
!texture_cache.GetImage(final_word_image).IsCpuDirty(),
"the final mapped word's image was not clean before its CPU store");
*final_word = 45;
Require(name, "fault boundary CPU store",
*final_word == 45 &&
texture_cache.GetImage(final_word_image).IsDefinitelyCpuDirty(),
"a valid four-byte store at the mapping end did not invalidate its image");
constexpr uint32_t final_gpu_value = 0xa1b2c3d4u;
auto &boundary_buffer_cache = resources.GetBufferCache();
(void)boundary_buffer_cache.ObtainBuffer(final_word_address, sizeof(uint32_t), true);
boundary_buffer_cache.FillBuffer(final_word_address, sizeof(uint32_t),
final_gpu_value, false);
Require(name, "fault boundary GPU ownership",
boundary_buffer_cache.HasGpuDirtyBytes(final_word_address, sizeof(uint32_t)) &&
!HostMemoryIsReadable(mapped_end - 1),
"the final mapped byte was not protected by GPU ownership");
const uint8_t final_gpu_byte = *final_byte;
Require(name, "fault boundary GPU readback",
final_gpu_byte == static_cast<uint8_t>(final_gpu_value >> 24u) &&
*final_word == final_gpu_value &&
!boundary_buffer_cache.HasGpuDirtyBytes(final_word_address, sizeof(uint32_t)),
"a final-byte CPU read did not publish the GPU-owned final word");
resources.SetGpu(nullptr);
resources.UnmapMemory(base, allocation_size);
scheduler.Finish();