164 lines
4.6 KiB
C#
164 lines
4.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace VMdemo.Simulation
|
|
{
|
|
public class TwoLevelPageTable
|
|
{
|
|
private readonly Dictionary<ulong, Dictionary<ulong, PageTableEntry>> _table;
|
|
private readonly ulong _l1Mask;
|
|
private readonly ulong _l2Mask;
|
|
private readonly ulong _vpnMask;
|
|
|
|
public TwoLevelPageTable(int l1Bits, int l2Bits)
|
|
{
|
|
if (l1Bits < 0 || l1Bits > 64)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(l1Bits), "l1Bits 必须在 0 到 64 之间。");
|
|
}
|
|
|
|
if (l2Bits < 0 || l2Bits > 64)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(l2Bits), "l2Bits 必须在 0 到 64 之间。");
|
|
}
|
|
|
|
if (l1Bits + l2Bits <= 0 || l1Bits + l2Bits > 64)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(l2Bits), "l1Bits + l2Bits 必须在 1 到 64 之间。");
|
|
}
|
|
|
|
L1Bits = l1Bits;
|
|
L2Bits = l2Bits;
|
|
_table = new Dictionary<ulong, Dictionary<ulong, PageTableEntry>>();
|
|
_l1Mask = GetBitMask(l1Bits);
|
|
_l2Mask = GetBitMask(l2Bits);
|
|
_vpnMask = GetBitMask(l1Bits + l2Bits);
|
|
}
|
|
|
|
public int L1Bits { get; }
|
|
public int L2Bits { get; }
|
|
|
|
public bool TryGetPresentPfn(ulong l1Index, ulong l2Index, out ulong pfn)
|
|
{
|
|
ValidateIndices(l1Index, l2Index);
|
|
|
|
if (!_table.TryGetValue(l1Index, out var l2Table))
|
|
{
|
|
pfn = 0UL;
|
|
return false;
|
|
}
|
|
|
|
if (!l2Table.TryGetValue(l2Index, out var entry) || !entry.Present)
|
|
{
|
|
pfn = 0UL;
|
|
return false;
|
|
}
|
|
|
|
pfn = entry.Pfn;
|
|
return true;
|
|
}
|
|
|
|
public bool TryGetEntry(ulong l1Index, ulong l2Index, out PageTableEntry entry)
|
|
{
|
|
ValidateIndices(l1Index, l2Index);
|
|
|
|
if (_table.TryGetValue(l1Index, out var l2Table) &&
|
|
l2Table.TryGetValue(l2Index, out var value))
|
|
{
|
|
entry = value;
|
|
return true;
|
|
}
|
|
|
|
entry = PageTableEntry.NotPresent;
|
|
return false;
|
|
}
|
|
|
|
public void SetPresent(ulong l1Index, ulong l2Index, ulong pfn, bool dirty = false)
|
|
{
|
|
ValidateIndices(l1Index, l2Index);
|
|
|
|
if (!_table.TryGetValue(l1Index, out var l2Table))
|
|
{
|
|
l2Table = new Dictionary<ulong, PageTableEntry>();
|
|
_table[l1Index] = l2Table;
|
|
}
|
|
|
|
l2Table[l2Index] = new PageTableEntry(pfn, true, dirty);
|
|
}
|
|
|
|
public bool MarkNotPresent(ulong l1Index, ulong l2Index)
|
|
{
|
|
ValidateIndices(l1Index, l2Index);
|
|
|
|
if (!_table.TryGetValue(l1Index, out var l2Table) ||
|
|
!l2Table.TryGetValue(l2Index, out var entry))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!entry.Present)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
l2Table[l2Index] = entry.MarkNotPresent();
|
|
return true;
|
|
}
|
|
|
|
public void GetIndicesFromVpn(ulong vpn, out ulong l1Index, out ulong l2Index)
|
|
{
|
|
if (L1Bits + L2Bits < 64 && (vpn & ~_vpnMask) != 0UL)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(vpn), "vpn 超出页表可表示范围。");
|
|
}
|
|
|
|
l2Index = vpn & _l2Mask;
|
|
l1Index = L2Bits >= 64 ? 0UL : vpn >> L2Bits;
|
|
ValidateIndices(l1Index, l2Index);
|
|
}
|
|
|
|
private void ValidateIndices(ulong l1Index, ulong l2Index)
|
|
{
|
|
if (!IsWithinBits(l1Index, L1Bits, _l1Mask))
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(l1Index), "l1Index 超出 L1 位宽范围。");
|
|
}
|
|
|
|
if (!IsWithinBits(l2Index, L2Bits, _l2Mask))
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(l2Index), "l2Index 超出 L2 位宽范围。");
|
|
}
|
|
}
|
|
|
|
private static bool IsWithinBits(ulong value, int bits, ulong mask)
|
|
{
|
|
if (bits == 64)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (bits == 0)
|
|
{
|
|
return value == 0UL;
|
|
}
|
|
|
|
return (value & ~mask) == 0UL;
|
|
}
|
|
|
|
private static ulong GetBitMask(int bits)
|
|
{
|
|
if (bits <= 0)
|
|
{
|
|
return 0UL;
|
|
}
|
|
|
|
if (bits >= 64)
|
|
{
|
|
return ulong.MaxValue;
|
|
}
|
|
|
|
return (1UL << bits) - 1UL;
|
|
}
|
|
}
|
|
}
|