|
|
|
@@ -28,6 +28,56 @@ use crate::primitive::{self, ProcessedPrimitive};
|
|
|
|
use crate::register_file::RegisterFile;
|
|
|
|
use crate::register_file::RegisterFile;
|
|
|
|
use crate::ring_view::RingBufferView;
|
|
|
|
use crate::ring_view::RingBufferView;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// The guest-virtual window that physical allocations are committed into.
|
|
|
|
|
|
|
|
/// `xenia-kernel`'s `heap_alloc` bumps its cursor through `0x4000_0000..=
|
|
|
|
|
|
|
|
/// 0x6FFF_FFFF` and commits the host backing for `MmAllocatePhysicalMemoryEx`
|
|
|
|
|
|
|
|
/// there, so this write-combine mirror is the canonical home of physical DRAM.
|
|
|
|
|
|
|
|
/// Keep in sync with `KernelState::heap_cursor`'s initial value.
|
|
|
|
|
|
|
|
pub const PHYSICAL_BACKING_BASE: u32 = 0x4000_0000;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Re-project a guest *physical* address — as handed to the Vd/GPU ABI and
|
|
|
|
|
|
|
|
/// embedded in PM4 pointers (`INDIRECT_BUFFER`, `WAIT_REG_MEM`-memory,
|
|
|
|
|
|
|
|
/// `MEM_WRITE`, `EVENT_WRITE*`, `IM_LOAD`, …) — onto the guest-virtual window
|
|
|
|
|
|
|
|
/// where its host backing is actually committed.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// The Xbox 360 maps its 512 MB of physical DRAM into several virtual mirror
|
|
|
|
|
|
|
|
/// windows that differ only in cache policy: bare physical (`0x0xxxxxxx`),
|
|
|
|
|
|
|
|
/// write-combine (`0x4xxxxxxx`), and the cached `0xA/0xC/0xExxxxxxx` mirrors —
|
|
|
|
|
|
|
|
/// all aliasing `addr & 0x1FFF_FFFF`. On real hardware (and in xenia-canary
|
|
|
|
|
|
|
|
/// via overlapping `mmap`s) these are literally the same bytes.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// Ours has a single flat `membase` and `MmAllocatePhysicalMemoryEx` commits
|
|
|
|
|
|
|
|
/// physical backing in the write-combine `0x4xxxxxxx` window. The guest then
|
|
|
|
|
|
|
|
/// masks its allocation base to *bare physical* before passing it to
|
|
|
|
|
|
|
|
/// `VdInitializeRingBuffer` / `VdEnableRingBufferRPtrWriteBack`, and PM4
|
|
|
|
|
|
|
|
/// pointers are likewise bare-physical. A flat `membase + phys` access
|
|
|
|
|
|
|
|
/// therefore hits a never-committed, zero-filled page instead of the committed
|
|
|
|
|
|
|
|
/// `0x4xxxxxxx` backing — so the GPU decoded zero PM4 headers and never ran
|
|
|
|
|
|
|
|
/// the real command stream.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// Projecting any physical-mirror address back onto the `0x4xxxxxxx` window
|
|
|
|
|
|
|
|
/// lands on the page `heap_alloc` actually backed, regardless of which mirror
|
|
|
|
|
|
|
|
/// the guest used (idempotent for `0x4xxxxxxx` itself). The projection is
|
|
|
|
|
|
|
|
/// derived from `heap_alloc`'s placement, not a guess — if that window ever
|
|
|
|
|
|
|
|
/// moves, `PHYSICAL_BACKING_BASE` must move with it.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// This is deliberately applied only at the GPU/Vd boundary (where addresses
|
|
|
|
|
|
|
|
/// arrive in their bare-physical form), NOT on the CPU's flat load/store path:
|
|
|
|
|
|
|
|
/// the guest CPU already accesses its allocations through the `0x4xxxxxxx`
|
|
|
|
|
|
|
|
/// base, and non-physical guest-virtual addresses (image `0x82xxxxxx`, stacks
|
|
|
|
|
|
|
|
/// `0x7xxxxxxx`) must stay flat.
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
|
|
|
pub fn physical_to_backing(addr: u32) -> u32 {
|
|
|
|
|
|
|
|
match addr {
|
|
|
|
|
|
|
|
0x0000_0000..=0x1FFF_FFFF
|
|
|
|
|
|
|
|
| 0x4000_0000..=0x4FFF_FFFF
|
|
|
|
|
|
|
|
| 0xA000_0000..=0xBFFF_FFFF
|
|
|
|
|
|
|
|
| 0xC000_0000..=0xDFFF_FFFF
|
|
|
|
|
|
|
|
| 0xE000_0000..=0xFFFF_FFFF => PHYSICAL_BACKING_BASE | (addr & 0x1FFF_FFFF),
|
|
|
|
|
|
|
|
_ => addr,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Cached Xenos microcode blob, produced by `PM4_IM_LOAD*` packets.
|
|
|
|
/// Cached Xenos microcode blob, produced by `PM4_IM_LOAD*` packets.
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct ShaderBlob {
|
|
|
|
pub struct ShaderBlob {
|
|
|
|
@@ -58,21 +108,37 @@ pub enum WaitCmp {
|
|
|
|
GreaterEq,
|
|
|
|
GreaterEq,
|
|
|
|
/// value > ref
|
|
|
|
/// value > ref
|
|
|
|
Greater,
|
|
|
|
Greater,
|
|
|
|
/// Always — caller wants to sleep regardless.
|
|
|
|
/// Always — caller wants to sleep regardless (selector bit 7).
|
|
|
|
Always,
|
|
|
|
Always,
|
|
|
|
|
|
|
|
/// Never matches — `wait_info & 7 == 0` selects bit 0 of canary's
|
|
|
|
|
|
|
|
/// selector word, which is always zero.
|
|
|
|
|
|
|
|
Never,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl WaitCmp {
|
|
|
|
impl WaitCmp {
|
|
|
|
/// Interpret the lower 3 bits of `wait_info` per canary's `MatchValueAndRef`.
|
|
|
|
/// Interpret the lower 3 bits of `wait_info` per canary's `MatchValueAndRef`
|
|
|
|
|
|
|
|
/// (`pm4_command_processor_implement.h:685-696`). Canary forms a selector
|
|
|
|
|
|
|
|
/// `((value<ref)<<1) | ((value<=ref)<<2) | ((value==ref)<<3) |
|
|
|
|
|
|
|
|
/// ((value!=ref)<<4) | ((value>=ref)<<5) | ((value>ref)<<6) | (1<<7)` and
|
|
|
|
|
|
|
|
/// evaluates `(selector >> (wait_info & 7)) & 1`. So the index is the bit
|
|
|
|
|
|
|
|
/// position: 1=Less, 2=LessEq, 3=Equal, 4=NotEqual, 5=GreaterEq,
|
|
|
|
|
|
|
|
/// 6=Greater, 7=always-true, 0=never (bit 0 is always clear).
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// GPUBUG: the prior mapping was off by one (it started at `0 => Less`),
|
|
|
|
|
|
|
|
/// so `wait_info & 7 == 3` decoded as `NotEqual` instead of `Equal`. That
|
|
|
|
|
|
|
|
/// inverted the standard CP coherency wait
|
|
|
|
|
|
|
|
/// (`WAIT_REG_MEM COHER_STATUS_HOST, Equal 0`): the GPU parked forever on
|
|
|
|
|
|
|
|
/// the first INDIRECT_BUFFER and never reached any draw.
|
|
|
|
pub fn from_wait_info(wait_info: u32) -> Self {
|
|
|
|
pub fn from_wait_info(wait_info: u32) -> Self {
|
|
|
|
match wait_info & 0x7 {
|
|
|
|
match wait_info & 0x7 {
|
|
|
|
0 => WaitCmp::Less,
|
|
|
|
1 => WaitCmp::Less,
|
|
|
|
1 => WaitCmp::LessEq,
|
|
|
|
2 => WaitCmp::LessEq,
|
|
|
|
2 => WaitCmp::Equal,
|
|
|
|
3 => WaitCmp::Equal,
|
|
|
|
3 => WaitCmp::NotEqual,
|
|
|
|
4 => WaitCmp::NotEqual,
|
|
|
|
4 => WaitCmp::GreaterEq,
|
|
|
|
5 => WaitCmp::GreaterEq,
|
|
|
|
5 => WaitCmp::Greater,
|
|
|
|
6 => WaitCmp::Greater,
|
|
|
|
_ => WaitCmp::Always,
|
|
|
|
7 => WaitCmp::Always,
|
|
|
|
|
|
|
|
_ => WaitCmp::Never,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -85,6 +151,7 @@ impl WaitCmp {
|
|
|
|
WaitCmp::GreaterEq => value >= reference,
|
|
|
|
WaitCmp::GreaterEq => value >= reference,
|
|
|
|
WaitCmp::Greater => value > reference,
|
|
|
|
WaitCmp::Greater => value > reference,
|
|
|
|
WaitCmp::Always => true,
|
|
|
|
WaitCmp::Always => true,
|
|
|
|
|
|
|
|
WaitCmp::Never => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -561,6 +628,12 @@ impl GpuSystem {
|
|
|
|
pub fn execute_one(&mut self, mem: &dyn MemoryAccess) -> ExecOutcome {
|
|
|
|
pub fn execute_one(&mut self, mem: &dyn MemoryAccess) -> ExecOutcome {
|
|
|
|
// 0) If currently parked, probe the condition and either wake up or stay blocked.
|
|
|
|
// 0) If currently parked, probe the condition and either wake up or stay blocked.
|
|
|
|
if let Some(block) = self.pending_block.clone() {
|
|
|
|
if let Some(block) = self.pending_block.clone() {
|
|
|
|
|
|
|
|
// Re-service the CP coherency handshake on each probe so a
|
|
|
|
|
|
|
|
// COHER_STATUS_HOST wait can clear (canary does this in its WAIT
|
|
|
|
|
|
|
|
// loop body, not just at entry).
|
|
|
|
|
|
|
|
if let GpuBlock::WaitRegMem { poll_addr, is_memory: false, .. } = &block {
|
|
|
|
|
|
|
|
self.make_coherent(*poll_addr);
|
|
|
|
|
|
|
|
}
|
|
|
|
if block.is_satisfied(mem, &self.register_file) {
|
|
|
|
if block.is_satisfied(mem, &self.register_file) {
|
|
|
|
tracing::debug!(?block, "gpu: wait satisfied — resuming");
|
|
|
|
tracing::debug!(?block, "gpu: wait satisfied — resuming");
|
|
|
|
self.pending_block = None;
|
|
|
|
self.pending_block = None;
|
|
|
|
@@ -658,6 +731,10 @@ impl GpuSystem {
|
|
|
|
/// Called by `VdInitializeRingBuffer` to give us the primary ring.
|
|
|
|
/// Called by `VdInitializeRingBuffer` to give us the primary ring.
|
|
|
|
pub fn initialize_ring_buffer(&mut self, base: u32, size_log2: u32) {
|
|
|
|
pub fn initialize_ring_buffer(&mut self, base: u32, size_log2: u32) {
|
|
|
|
let size_bytes = 1u32 << size_log2.min(31);
|
|
|
|
let size_bytes = 1u32 << size_log2.min(31);
|
|
|
|
|
|
|
|
// The guest hands us a bare *physical* ring base; project it onto the
|
|
|
|
|
|
|
|
// committed backing window so ring reads hit real PM4 packets (see
|
|
|
|
|
|
|
|
// `physical_to_backing`).
|
|
|
|
|
|
|
|
let base = physical_to_backing(base);
|
|
|
|
self.ring.base = base;
|
|
|
|
self.ring.base = base;
|
|
|
|
self.ring.size_dwords = size_bytes / 4;
|
|
|
|
self.ring.size_dwords = size_bytes / 4;
|
|
|
|
self.ring.read_offset_dwords = 0;
|
|
|
|
self.ring.read_offset_dwords = 0;
|
|
|
|
@@ -675,6 +752,10 @@ impl GpuSystem {
|
|
|
|
/// Called by `VdEnableRingBufferRPtrWriteBack` to record where the guest
|
|
|
|
/// Called by `VdEnableRingBufferRPtrWriteBack` to record where the guest
|
|
|
|
/// expects us to mirror `read_offset_dwords`.
|
|
|
|
/// expects us to mirror `read_offset_dwords`.
|
|
|
|
pub fn enable_rptr_writeback(&mut self, addr: u32, block_log2: u32) {
|
|
|
|
pub fn enable_rptr_writeback(&mut self, addr: u32, block_log2: u32) {
|
|
|
|
|
|
|
|
// The guest registers a bare *physical* writeback address and polls
|
|
|
|
|
|
|
|
// the same allocation through its `0x4xxxxxxx` base; project so our
|
|
|
|
|
|
|
|
// RPtr store lands on the page the guest actually reads.
|
|
|
|
|
|
|
|
let addr = physical_to_backing(addr);
|
|
|
|
self.ring.rptr_writeback_addr = addr;
|
|
|
|
self.ring.rptr_writeback_addr = addr;
|
|
|
|
self.ring.rptr_writeback_block_dwords = 1u32 << block_log2.min(31);
|
|
|
|
self.ring.rptr_writeback_block_dwords = 1u32 << block_log2.min(31);
|
|
|
|
tracing::info!(
|
|
|
|
tracing::info!(
|
|
|
|
@@ -724,6 +805,26 @@ impl GpuSystem {
|
|
|
|
/// upstream packet effects (memory writes, register file updates
|
|
|
|
/// upstream packet effects (memory writes, register file updates
|
|
|
|
/// the guest reads via subsequent MMIO) happen-before the
|
|
|
|
/// the guest reads via subsequent MMIO) happen-before the
|
|
|
|
/// CPU-visible RPTR bump.
|
|
|
|
/// CPU-visible RPTR bump.
|
|
|
|
|
|
|
|
/// Service a CP coherency request, mirroring canary's
|
|
|
|
|
|
|
|
/// `CommandProcessor::MakeCoherent` (`command_processor.cc:801-838`).
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// The guest requests a vertex/texture-cache flush by writing
|
|
|
|
|
|
|
|
/// `COHER_STATUS_HOST` with its status bit (bit 31) set, then spins on a
|
|
|
|
|
|
|
|
/// `WAIT_REG_MEM COHER_STATUS_HOST, Equal 0`. We have no host cache to
|
|
|
|
|
|
|
|
/// flush (memory is shared, coherency is implicit), so completing the
|
|
|
|
|
|
|
|
/// request is simply clearing the register — which lets the wait satisfy.
|
|
|
|
|
|
|
|
/// No-op unless `poll_addr` is `COHER_STATUS_HOST` and its status bit is
|
|
|
|
|
|
|
|
/// set, so it is safe to call on every coherency-register WAIT probe.
|
|
|
|
|
|
|
|
fn make_coherent(&mut self, poll_addr: u32) {
|
|
|
|
|
|
|
|
if poll_addr != reg::COHER_STATUS_HOST {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
let status = self.register_file.read(reg::COHER_STATUS_HOST);
|
|
|
|
|
|
|
|
if status & 0x8000_0000 != 0 {
|
|
|
|
|
|
|
|
self.register_file.write(reg::COHER_STATUS_HOST, 0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn writeback_read_ptr(&mut self, mem: &dyn MemoryAccess) {
|
|
|
|
fn writeback_read_ptr(&mut self, mem: &dyn MemoryAccess) {
|
|
|
|
if self.ring.rptr_writeback_addr != 0 && self.ring.is_initialized() {
|
|
|
|
if self.ring.rptr_writeback_addr != 0 && self.ring.is_initialized() {
|
|
|
|
mem.write_u32_fence(
|
|
|
|
mem.write_u32_fence(
|
|
|
|
@@ -816,7 +917,9 @@ impl GpuSystem {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pm4::PM4_INDIRECT_BUFFER | pm4::PM4_INDIRECT_BUFFER_PFD => {
|
|
|
|
pm4::PM4_INDIRECT_BUFFER | pm4::PM4_INDIRECT_BUFFER_PFD => {
|
|
|
|
self.stats.indirect_buffer_jumps += 1;
|
|
|
|
self.stats.indirect_buffer_jumps += 1;
|
|
|
|
let ib_ptr = self.read_payload(mem, 1);
|
|
|
|
// The IB pointer is a guest *physical* address — project it
|
|
|
|
|
|
|
|
// onto the committed backing window (see `physical_to_backing`).
|
|
|
|
|
|
|
|
let ib_ptr = physical_to_backing(self.read_payload(mem, 1));
|
|
|
|
let ib_size = self.read_payload(mem, 2);
|
|
|
|
let ib_size = self.read_payload(mem, 2);
|
|
|
|
// Advance past the IB header + payload before recursing so
|
|
|
|
// Advance past the IB header + payload before recursing so
|
|
|
|
// the return location is correct.
|
|
|
|
// the return location is correct.
|
|
|
|
@@ -854,7 +957,8 @@ impl GpuSystem {
|
|
|
|
let is_memory = (wait_info & 0x10) != 0;
|
|
|
|
let is_memory = (wait_info & 0x10) != 0;
|
|
|
|
let cmp = WaitCmp::from_wait_info(wait_info);
|
|
|
|
let cmp = WaitCmp::from_wait_info(wait_info);
|
|
|
|
let poll_addr = if is_memory {
|
|
|
|
let poll_addr = if is_memory {
|
|
|
|
poll_addr_raw & !3
|
|
|
|
// Physical memory poll address → committed backing.
|
|
|
|
|
|
|
|
physical_to_backing(poll_addr_raw & !3)
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
poll_addr_raw
|
|
|
|
poll_addr_raw
|
|
|
|
};
|
|
|
|
};
|
|
|
|
@@ -865,6 +969,12 @@ impl GpuSystem {
|
|
|
|
mask,
|
|
|
|
mask,
|
|
|
|
cmp,
|
|
|
|
cmp,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
// A WAIT polling COHER_STATUS_HOST is the CP coherency
|
|
|
|
|
|
|
|
// handshake: service it now so the status bit clears (see
|
|
|
|
|
|
|
|
// `make_coherent`), exactly as canary does in its WAIT loop.
|
|
|
|
|
|
|
|
if !is_memory {
|
|
|
|
|
|
|
|
self.make_coherent(poll_addr);
|
|
|
|
|
|
|
|
}
|
|
|
|
if block.is_satisfied(mem, &self.register_file) {
|
|
|
|
if block.is_satisfied(mem, &self.register_file) {
|
|
|
|
// Condition already true; proceed past this packet.
|
|
|
|
// Condition already true; proceed past this packet.
|
|
|
|
tracing::trace!(?block, "gpu: WAIT_REG_MEM immediately satisfied");
|
|
|
|
tracing::trace!(?block, "gpu: WAIT_REG_MEM immediately satisfied");
|
|
|
|
@@ -908,7 +1018,7 @@ impl GpuSystem {
|
|
|
|
pm4::PM4_REG_TO_MEM => {
|
|
|
|
pm4::PM4_REG_TO_MEM => {
|
|
|
|
// payload[0] = reg_index, payload[1] = mem addr
|
|
|
|
// payload[0] = reg_index, payload[1] = mem addr
|
|
|
|
let reg_index = self.read_payload(mem, 1) & 0x1FFF;
|
|
|
|
let reg_index = self.read_payload(mem, 1) & 0x1FFF;
|
|
|
|
let dst = self.read_payload(mem, 2) & !3;
|
|
|
|
let dst = physical_to_backing(self.read_payload(mem, 2) & !3);
|
|
|
|
let value = self.register_file.read(reg_index);
|
|
|
|
let value = self.register_file.read(reg_index);
|
|
|
|
mem.write_u32(dst, value);
|
|
|
|
mem.write_u32(dst, value);
|
|
|
|
tracing::trace!(
|
|
|
|
tracing::trace!(
|
|
|
|
@@ -920,7 +1030,7 @@ impl GpuSystem {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pm4::PM4_MEM_WRITE => {
|
|
|
|
pm4::PM4_MEM_WRITE => {
|
|
|
|
// payload[0] = dst, payload[1..=count-1] = values
|
|
|
|
// payload[0] = dst, payload[1..=count-1] = values
|
|
|
|
let mut dst = self.read_payload(mem, 1) & !3;
|
|
|
|
let mut dst = physical_to_backing(self.read_payload(mem, 1) & !3);
|
|
|
|
for i in 2..=count {
|
|
|
|
for i in 2..=count {
|
|
|
|
let val = self.read_payload(mem, i);
|
|
|
|
let val = self.read_payload(mem, i);
|
|
|
|
mem.write_u32(dst, val);
|
|
|
|
mem.write_u32(dst, val);
|
|
|
|
@@ -936,7 +1046,7 @@ impl GpuSystem {
|
|
|
|
let mask = self.read_payload(mem, 4);
|
|
|
|
let mask = self.read_payload(mem, 4);
|
|
|
|
let is_memory = (wait_info & 0x10) != 0;
|
|
|
|
let is_memory = (wait_info & 0x10) != 0;
|
|
|
|
let cmp = WaitCmp::from_wait_info(wait_info);
|
|
|
|
let cmp = WaitCmp::from_wait_info(wait_info);
|
|
|
|
let poll_addr = if is_memory { poll_raw & !3 } else { poll_raw };
|
|
|
|
let poll_addr = if is_memory { physical_to_backing(poll_raw & !3) } else { poll_raw };
|
|
|
|
let cur_raw = if is_memory {
|
|
|
|
let cur_raw = if is_memory {
|
|
|
|
mem.read_u32(poll_addr)
|
|
|
|
mem.read_u32(poll_addr)
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
@@ -946,7 +1056,7 @@ impl GpuSystem {
|
|
|
|
let write_addr = self.read_payload(mem, 5);
|
|
|
|
let write_addr = self.read_payload(mem, 5);
|
|
|
|
let write_data = self.read_payload(mem, 6);
|
|
|
|
let write_data = self.read_payload(mem, 6);
|
|
|
|
if (wait_info & 0x100) != 0 {
|
|
|
|
if (wait_info & 0x100) != 0 {
|
|
|
|
mem.write_u32(write_addr & !3, write_data);
|
|
|
|
mem.write_u32(physical_to_backing(write_addr & !3), write_data);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
self.register_file
|
|
|
|
self.register_file
|
|
|
|
.write(write_addr & 0x1FFF, write_data);
|
|
|
|
.write(write_addr & 0x1FFF, write_data);
|
|
|
|
@@ -965,7 +1075,7 @@ impl GpuSystem {
|
|
|
|
// payload[0] = initiator (bit 31: write counter, else write `value`)
|
|
|
|
// payload[0] = initiator (bit 31: write counter, else write `value`)
|
|
|
|
// payload[1] = address, payload[2] = value
|
|
|
|
// payload[1] = address, payload[2] = value
|
|
|
|
let initiator = self.read_payload(mem, 1);
|
|
|
|
let initiator = self.read_payload(mem, 1);
|
|
|
|
let address = self.read_payload(mem, 2);
|
|
|
|
let address = physical_to_backing(self.read_payload(mem, 2));
|
|
|
|
let value = self.read_payload(mem, 3);
|
|
|
|
let value = self.read_payload(mem, 3);
|
|
|
|
self.register_file
|
|
|
|
self.register_file
|
|
|
|
.write(reg::VGT_EVENT_INITIATOR, initiator & 0x3F);
|
|
|
|
.write(reg::VGT_EVENT_INITIATOR, initiator & 0x3F);
|
|
|
|
@@ -993,7 +1103,7 @@ impl GpuSystem {
|
|
|
|
// payload[0] = initiator, [1] = address. Writes 6 u16 extents
|
|
|
|
// payload[0] = initiator, [1] = address. Writes 6 u16 extents
|
|
|
|
// (min/max x/y/z) — we're not tracking scissors yet, so write zeros.
|
|
|
|
// (min/max x/y/z) — we're not tracking scissors yet, so write zeros.
|
|
|
|
let initiator = self.read_payload(mem, 1);
|
|
|
|
let initiator = self.read_payload(mem, 1);
|
|
|
|
let address = self.read_payload(mem, 2) & !3;
|
|
|
|
let address = physical_to_backing(self.read_payload(mem, 2) & !3);
|
|
|
|
self.register_file
|
|
|
|
self.register_file
|
|
|
|
.write(reg::VGT_EVENT_INITIATOR, initiator & 0x3F);
|
|
|
|
.write(reg::VGT_EVENT_INITIATOR, initiator & 0x3F);
|
|
|
|
self.handle_event_initiator(initiator & 0x3F, mem);
|
|
|
|
self.handle_event_initiator(initiator & 0x3F, mem);
|
|
|
|
@@ -1123,7 +1233,7 @@ impl GpuSystem {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pm4::PM4_LOAD_ALU_CONSTANT => {
|
|
|
|
pm4::PM4_LOAD_ALU_CONSTANT => {
|
|
|
|
// payload[0] = source mem addr, [1] = offset_type, [2] = size_dwords
|
|
|
|
// payload[0] = source mem addr, [1] = offset_type, [2] = size_dwords
|
|
|
|
let src = self.read_payload(mem, 1) & !3;
|
|
|
|
let src = physical_to_backing(self.read_payload(mem, 1) & !3);
|
|
|
|
let offset_type = self.read_payload(mem, 2);
|
|
|
|
let offset_type = self.read_payload(mem, 2);
|
|
|
|
let size_dwords = self.read_payload(mem, 3);
|
|
|
|
let size_dwords = self.read_payload(mem, 3);
|
|
|
|
let index = offset_type & 0x7FF;
|
|
|
|
let index = offset_type & 0x7FF;
|
|
|
|
@@ -1155,7 +1265,7 @@ impl GpuSystem {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
v
|
|
|
|
v
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
let addr = self.read_payload(mem, 1) & !3;
|
|
|
|
let addr = physical_to_backing(self.read_payload(mem, 1) & !3);
|
|
|
|
let mut v = Vec::with_capacity(size_dwords as usize);
|
|
|
|
let mut v = Vec::with_capacity(size_dwords as usize);
|
|
|
|
for i in 0..size_dwords {
|
|
|
|
for i in 0..size_dwords {
|
|
|
|
v.push(mem.read_u32(addr + i * 4));
|
|
|
|
v.push(mem.read_u32(addr + i * 4));
|
|
|
|
@@ -1477,8 +1587,9 @@ mod tests {
|
|
|
|
// header
|
|
|
|
// header
|
|
|
|
let hdr = (3u32 << 30) | ((5u32 - 1) << 16) | ((pm4::PM4_WAIT_REG_MEM as u32) << 8);
|
|
|
|
let hdr = (3u32 << 30) | ((5u32 - 1) << 16) | ((pm4::PM4_WAIT_REG_MEM as u32) << 8);
|
|
|
|
mem.write_u32(0x4000_0000, hdr);
|
|
|
|
mem.write_u32(0x4000_0000, hdr);
|
|
|
|
// wait_info: is_memory=1 (bit 4), cmp=equal (bits 2:0 = 2)
|
|
|
|
// wait_info: is_memory=1 (bit 4), cmp=equal (bits 2:0 = 3, per canary's
|
|
|
|
mem.write_u32(0x4000_0004, 0x12);
|
|
|
|
// MatchValueAndRef selector: 1=Less, 2=LessEq, 3=Equal, …).
|
|
|
|
|
|
|
|
mem.write_u32(0x4000_0004, 0x13);
|
|
|
|
mem.write_u32(0x4000_0008, 0x4000_1000);
|
|
|
|
mem.write_u32(0x4000_0008, 0x4000_1000);
|
|
|
|
mem.write_u32(0x4000_000C, 0x42);
|
|
|
|
mem.write_u32(0x4000_000C, 0x42);
|
|
|
|
mem.write_u32(0x4000_0010, 0xFFFF_FFFF);
|
|
|
|
mem.write_u32(0x4000_0010, 0xFFFF_FFFF);
|
|
|
|
|