using System; using NUnit.Framework; using VMdemo.Simulation; namespace VMdemo.Tests.EditMode { public class Step4PageTableAndFifoTests { [Test] public void TwoLevelPageTable_SetPresent_ThenTryGetPresentPfn_Hits() { var pageTable = new TwoLevelPageTable(3, 2); pageTable.SetPresent(5UL, 3UL, 9UL, dirty: true); var hit = pageTable.TryGetPresentPfn(5UL, 3UL, out var pfn); var exists = pageTable.TryGetEntry(5UL, 3UL, out var entry); Assert.IsTrue(hit); Assert.That(pfn, Is.EqualTo(9UL)); Assert.IsTrue(exists); Assert.That(entry.Present, Is.True); Assert.That(entry.Dirty, Is.True); } [Test] public void TwoLevelPageTable_MarkNotPresent_ThenMisses() { var pageTable = new TwoLevelPageTable(3, 2); pageTable.SetPresent(1UL, 2UL, 7UL); var changed = pageTable.MarkNotPresent(1UL, 2UL); var changedAgain = pageTable.MarkNotPresent(1UL, 2UL); var hitAfterMark = pageTable.TryGetPresentPfn(1UL, 2UL, out _); var exists = pageTable.TryGetEntry(1UL, 2UL, out var entry); Assert.IsTrue(changed); Assert.IsFalse(changedAgain); Assert.IsFalse(hitAfterMark); Assert.IsTrue(exists); Assert.IsFalse(entry.Present); } [Test] public void TwoLevelPageTable_Probe_CanDistinguishLookupReasons() { var pageTable = new TwoLevelPageTable(3, 2); var missingL1 = pageTable.Probe(1UL, 0UL); Assert.That(missingL1.Status, Is.EqualTo(PageTableProbeStatus.MissingL1Table)); pageTable.SetPresent(1UL, 2UL, 7UL); var missingL2 = pageTable.Probe(1UL, 3UL); Assert.That(missingL2.Status, Is.EqualTo(PageTableProbeStatus.MissingL2Entry)); pageTable.MarkNotPresent(1UL, 2UL); var notPresent = pageTable.Probe(1UL, 2UL); Assert.That(notPresent.Status, Is.EqualTo(PageTableProbeStatus.NotPresentEntry)); Assert.IsTrue(notPresent.HasEntry); pageTable.SetPresent(1UL, 2UL, 9UL); var present = pageTable.Probe(1UL, 2UL); Assert.That(present.Status, Is.EqualTo(PageTableProbeStatus.PresentEntry)); Assert.That(present.Entry.Pfn, Is.EqualTo(9UL)); } [Test] public void PhysicalMemoryManager_AllocateForVpn_EvictsInFifoOrder() { var manager = new PhysicalMemoryManager(2UL); var r1 = manager.AllocateForVpn(1UL); var r2 = manager.AllocateForVpn(2UL); var r3 = manager.AllocateForVpn(3UL); // evict 1 var r4 = manager.AllocateForVpn(4UL); // evict 2 Assert.That(r1.AssignedPfn, Is.EqualTo(0UL)); Assert.That(r2.AssignedPfn, Is.EqualTo(1UL)); Assert.That(r3.AssignedPfn, Is.EqualTo(0UL)); Assert.That(r4.AssignedPfn, Is.EqualTo(1UL)); Assert.IsFalse(r1.Eviction.HasEvicted); Assert.IsFalse(r2.Eviction.HasEvicted); Assert.IsTrue(r3.Eviction.HasEvicted); Assert.That(r3.Eviction.EvictedVpn, Is.EqualTo(1UL)); Assert.That(r3.Eviction.EvictedPfn, Is.EqualTo(0UL)); Assert.IsTrue(r4.Eviction.HasEvicted); Assert.That(r4.Eviction.EvictedVpn, Is.EqualTo(2UL)); Assert.That(r4.Eviction.EvictedPfn, Is.EqualTo(1UL)); var fifoOrder = manager.GetFifoVpnOrder(); Assert.That(fifoOrder.Count, Is.EqualTo(2)); Assert.That(fifoOrder[0], Is.EqualTo(3UL)); Assert.That(fifoOrder[1], Is.EqualTo(4UL)); } [Test] public void Resolve_WhenEvictionHappens_SyncsPageTableAndTlb() { var pageTable = new TwoLevelPageTable(4, 4); var memory = new PhysicalMemoryManager(1UL); var tlb = new TlbCache(4); const ulong vpn1 = 1UL; pageTable.GetIndicesFromVpn(vpn1, out var l1_1, out var l2_1); var first = MemoryAccessResolver.Resolve(vpn1, l1_1, l2_1, pageTable, memory, tlb); Assert.IsTrue(first.PageFault); Assert.IsFalse(first.PageTableHit); Assert.IsFalse(first.Eviction.HasEvicted); Assert.IsTrue(tlb.Lookup(vpn1, out _)); var second = MemoryAccessResolver.Resolve(vpn1, l1_1, l2_1, pageTable, memory, tlb); Assert.IsFalse(second.PageFault); Assert.IsTrue(second.PageTableHit); const ulong vpn2 = 2UL; pageTable.GetIndicesFromVpn(vpn2, out var l1_2, out var l2_2); var third = MemoryAccessResolver.Resolve(vpn2, l1_2, l2_2, pageTable, memory, tlb); Assert.IsTrue(third.PageFault); Assert.IsTrue(third.Eviction.HasEvicted); Assert.That(third.Eviction.EvictedVpn, Is.EqualTo(vpn1)); Assert.IsFalse(tlb.Lookup(vpn1, out _)); Assert.IsTrue(tlb.Lookup(vpn2, out _)); pageTable.GetIndicesFromVpn(vpn1, out var evictedL1, out var evictedL2); Assert.IsFalse(pageTable.TryGetPresentPfn(evictedL1, evictedL2, out _)); Assert.IsFalse(memory.TryGetResidentPfn(vpn1, out _)); Assert.IsTrue(memory.TryGetResidentPfn(vpn2, out _)); } [Test] public void HighVolumeAccess_N10000_RunsWithoutStateCorruption() { var pageTable = new TwoLevelPageTable(8, 8); var memory = new PhysicalMemoryManager(16UL); var tlb = new TlbCache(8); var random = new Random(123); for (var i = 0; i < 10000; i++) { var vpn = (ulong)random.Next(0, 512); pageTable.GetIndicesFromVpn(vpn, out var l1, out var l2); var result = MemoryAccessResolver.Resolve(vpn, l1, l2, pageTable, memory, tlb); var fifoOrder = memory.GetFifoVpnOrder(); Assert.That(memory.ResidentCount, Is.LessThanOrEqualTo(16)); Assert.That(fifoOrder.Count, Is.EqualTo(memory.ResidentCount)); if (result.PageTableHit) { Assert.IsFalse(result.PageFault); } } } } }