[Subsystem-fixes] 6 verified ours-vs-canary divergence fixes

From the 2026-06-12 5-subsystem differential audit. All verified against
canary as oracle; 660/660 workspace tests green (655 + 5 new).

1. nt_create_event polarity (exports.rs) — `manual_reset = gpr[5] != 0`
   was INVERTED. Canary xboxkrnl_threading.cc:668 `Initialize(!event_type,..)`
   + xevent.cc:41 (type 0 = NotificationEvent = manual, type 1 = Sync = auto).
   Now `== 0`. Was the dormant 2.AI fix on chore/portable-snapshot, never
   merged. The Ke-path was already correct; only the Nt-path was wrong.

2. 2.AF deadline drain (main.rs coord_pre_round) — expired KeWait/KeDelay
   deadlines never fired under load because advance_to_next_wake_if_due was
   only called in coord_idle_advance (no-Ready-threads path). Added a
   per-round drain loop; covers BOTH lockstep and parallel outer loops since
   both call coord_pre_round. Was the dormant 2.AF fix, never merged.

3. handle slab-recycle ABA guard (state.rs + scheduler.rs) — release_handle_slot
   (my round-34 regression) recycled a closed slot even with a thread still
   parked on it, risking a stale-waiter wake when the slot is re-minted. Added
   Scheduler::any_thread_waiting_on; decline to recycle a still-waited slot.

4. vpkpx pixel-pack (vmx.rs) — wrong field mapping (~100% mismatch). Now
   exact canary ppc_emit_altivec.cc:1795 shift/mask (red 6b out[15:10] from
   w[24:19], green out[9:5] from w[14:10], blue out[4:0] from w[7:3]; no
   fabricated alpha bit). +unit test.

5. VFS GDFX attribute plumbing (vfs/*, exports.rs query fns) — VfsEntry now
   carries the real on-disc attribute byte (GDFX dirent +12, canary
   disc_image_device.cc:136/154) instead of inferring directory-ness from
   path shape. Query exports report the real FILE_ATTRIBUTE_* bits. Candidate
   driver of the XamShowDirtyDiscErrorUI gate. +tests.

6. MmGetPhysicalAddress region-aware mirror (exports.rs) — flat 0x1FFFFFFF
   mask missed canary's +0x1000 host_address_offset for 0xE0000000+ mirror
   (memory.cc:2317). Read-only query; proven byte-identical 50M digest. +test.

Investigated and intentionally NOT changed:
- zero-on-recommit: no-op; ours has no region-reuse path (bump allocators,
  free is a stub).
- 32-bit ALU writeback truncation (PPCBUG-020): documented-deliberate; premise
  (MSR.SF=0) is questionable but flipping it is out of scope here.
- KeSetEvent/NtSetEvent return value: ours returns true previous state
  (hardware-faithful); canary returns constant 1 — NOT an ours bug.

sylpheed_n50m golden will need re-baselining (legit behavior change).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-12 14:57:38 +02:00
parent db90ad0f7d
commit b20c99f141
8 changed files with 319 additions and 44 deletions

View File

@@ -31,6 +31,9 @@ impl VfsDevice for HostPathDevice {
is_directory: metadata.is_dir(),
size: metadata.len(),
offset: 0,
// Host FS carries no Xbox attribute byte; synthesise the
// DIRECTORY/NORMAL split like canary's HostPathDevice.
attributes: if metadata.is_dir() { 0x10 } else { 0x80 },
});
}
Ok(entries)
@@ -49,6 +52,7 @@ impl VfsDevice for HostPathDevice {
is_directory: metadata.is_dir(),
size: metadata.len(),
offset: 0,
attributes: if metadata.is_dir() { 0x10 } else { 0x80 },
})
}
}

View File

@@ -29,6 +29,11 @@ const GDFX_MAGIC: &[u8; 20] = b"MICROSOFT*XBOX*MEDIA";
/// File attribute: directory
const FILE_ATTRIBUTE_DIRECTORY: u8 = 0x10;
/// File attribute: read-only. Canary OR's this into every GDFX entry's
/// attribute byte because a pressed disc is inherently read-only
/// (`disc_image_device.cc:154`: `attributes | kFileAttributeReadOnly`).
const FILE_ATTRIBUTE_READONLY: u8 = 0x01;
/// Known game partition offsets to try
const LIKELY_OFFSETS: &[u64] = &[
0x0000_0000,
@@ -131,6 +136,11 @@ impl DiscImageDevice {
let name = String::from_utf8_lossy(&buffer[p + 14..p + 14 + name_length]).to_string();
let is_directory = (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
// Match canary: the on-disc attribute byte (DIRECTORY/HIDDEN/SYSTEM/
// ARCHIVE/NORMAL bits as authored) OR the implicit READONLY bit for
// pressed media. We forward the FULL byte, not a path-shape guess, so
// attribute queries report exactly what the disc records.
let attributes = (attributes | FILE_ATTRIBUTE_READONLY) as u32;
let file_offset = self.game_offset + sector * SECTOR_SIZE;
let full_path = if prefix.is_empty() {
name.clone()
@@ -143,6 +153,7 @@ impl DiscImageDevice {
is_directory,
size: length,
offset: file_offset,
attributes,
});
// Descend into subdirectories. Zero-length directory entries exist
@@ -260,4 +271,73 @@ mod tests {
.expect("read_file on nested path");
assert!(!bytes.is_empty(), "nested read returned empty buffer");
}
/// Build a one-node GDFX directory buffer in memory and parse it with
/// `collect_entries`, asserting the real on-disc attribute byte is
/// forwarded into `VfsEntry.attributes` (with READONLY OR'd in, matching
/// canary `disc_image_device.cc:154`) rather than synthesised from the
/// path shape.
fn parse_single_entry(name: &str, on_disc_attr: u8) -> VfsEntry {
// GDFX dirent: node_l(u16) node_r(u16) sector(u32) length(u32)
// attributes(u8) name_length(u8) name(bytes). The directory bit
// gates subdirectory descent; use length=0 so a "directory" entry
// is treated as an empty leaf and we don't recurse off the buffer.
let mut buf = Vec::new();
buf.extend_from_slice(&0u16.to_le_bytes()); // node_l
buf.extend_from_slice(&0u16.to_le_bytes()); // node_r
buf.extend_from_slice(&0u32.to_le_bytes()); // sector
buf.extend_from_slice(&0u32.to_le_bytes()); // length (0 => leaf)
buf.push(on_disc_attr); // attributes
buf.push(name.len() as u8); // name_length
buf.extend_from_slice(name.as_bytes());
let mut dev = DiscImageDevice {
name: "test".into(),
path: std::path::PathBuf::new(),
game_offset: 0,
entries: Vec::new(),
};
// `file` is only touched when descending into a non-empty directory;
// our length=0 entries never recurse, so a dummy handle is fine.
let mut file = std::fs::File::open("/dev/null").expect("open /dev/null");
dev.collect_entries(&mut file, &buf, 0, "").expect("parse");
assert_eq!(dev.entries.len(), 1);
dev.entries.into_iter().next().unwrap()
}
#[test]
fn directory_entry_reports_directory_attribute() {
// On-disc 0x10 (DIRECTORY) -> attributes carries 0x10 and READONLY.
let e = parse_single_entry("dat", FILE_ATTRIBUTE_DIRECTORY);
assert!(e.is_directory, "directory bit not decoded");
assert_ne!(
e.attributes & 0x10,
0,
"FILE_ATTRIBUTE_DIRECTORY must be set for a directory entry"
);
assert_ne!(e.attributes & 0x01, 0, "READONLY must be OR'd in (canary)");
}
#[test]
fn file_entry_has_no_directory_attribute() {
// On-disc 0x80 (NORMAL) -> not a directory; READONLY still OR'd in.
let e = parse_single_entry("default.xex", 0x80);
assert!(!e.is_directory, "non-directory misdecoded as directory");
assert_eq!(
e.attributes & 0x10,
0,
"FILE_ATTRIBUTE_DIRECTORY must be clear for a file entry"
);
assert_ne!(e.attributes & 0x80, 0, "NORMAL bit must be preserved");
assert_ne!(e.attributes & 0x01, 0, "READONLY must be OR'd in (canary)");
}
#[test]
fn archive_and_hidden_bits_are_preserved() {
// ARCHIVE(0x20) | HIDDEN(0x02) authored on disc must survive intact.
let e = parse_single_entry("save.dat", 0x20 | 0x02);
assert_eq!(e.attributes & 0x20, 0x20, "ARCHIVE bit dropped");
assert_eq!(e.attributes & 0x02, 0x02, "HIDDEN bit dropped");
assert_eq!(e.attributes & 0x10, 0, "spurious DIRECTORY bit");
}
}

View File

@@ -22,6 +22,16 @@ pub struct VfsEntry {
pub is_directory: bool,
pub size: u64,
pub offset: u64,
/// Xbox `FILE_ATTRIBUTE_*` bitmask for this entry, sourced from the
/// backing device's real on-disc metadata rather than inferred from
/// the path shape. For GDFX disc images this is the on-disc attribute
/// byte at dirent offset +12 OR'd with `FILE_ATTRIBUTE_READONLY`
/// (matches xenia-canary `disc_image_device.cc:154`:
/// `entry->attributes_ = attributes | kFileAttributeReadOnly`).
///
/// Bit layout (canary `vfs/entry.h:66-76`): READONLY=0x01, HIDDEN=0x02,
/// SYSTEM=0x04, DIRECTORY=0x10, ARCHIVE=0x20, NORMAL=0x80.
pub attributes: u32,
}
/// Trait for VFS device implementations (XISO, STFS, host path, etc.)