| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458 |
- using ChocolArm64.Memory;
- using Ryujinx.Common;
- using System;
- using System.Collections.Generic;
- namespace Ryujinx.HLE.HOS.Kernel
- {
- class KMemoryManager
- {
- public const int PageSize = 0x1000;
- private const int KMemoryBlockSize = 0x40;
- //We need 2 blocks for the case where a big block
- //needs to be split in 2, plus one block that will be the new one inserted.
- private const int MaxBlocksNeededForInsertion = 2;
- private LinkedList<KMemoryBlock> Blocks;
- private MemoryManager CpuMemory;
- private Horizon System;
- public ulong AddrSpaceStart { get; private set; }
- public ulong AddrSpaceEnd { get; private set; }
- public ulong CodeRegionStart { get; private set; }
- public ulong CodeRegionEnd { get; private set; }
- public ulong HeapRegionStart { get; private set; }
- public ulong HeapRegionEnd { get; private set; }
- private ulong CurrentHeapAddr;
- public ulong AliasRegionStart { get; private set; }
- public ulong AliasRegionEnd { get; private set; }
- public ulong StackRegionStart { get; private set; }
- public ulong StackRegionEnd { get; private set; }
- public ulong TlsIoRegionStart { get; private set; }
- public ulong TlsIoRegionEnd { get; private set; }
- private ulong HeapCapacity;
- public ulong PhysicalMemoryUsage { get; private set; }
- private MemoryRegion MemRegion;
- private bool AslrDisabled;
- public int AddrSpaceWidth { get; private set; }
- private bool IsKernel;
- private bool AslrEnabled;
- private KMemoryBlockAllocator BlockAllocator;
- private int ContextId;
- private MersenneTwister RandomNumberGenerator;
- public KMemoryManager(Horizon System, MemoryManager CpuMemory)
- {
- this.System = System;
- this.CpuMemory = CpuMemory;
- Blocks = new LinkedList<KMemoryBlock>();
- }
- private static readonly int[] AddrSpaceSizes = new int[] { 32, 36, 32, 39 };
- public KernelResult InitializeForProcess(
- AddressSpaceType AddrSpaceType,
- bool AslrEnabled,
- bool AslrDisabled,
- MemoryRegion MemRegion,
- ulong Address,
- ulong Size,
- KMemoryBlockAllocator BlockAllocator)
- {
- if ((uint)AddrSpaceType > (uint)AddressSpaceType.Addr39Bits)
- {
- throw new ArgumentException(nameof(AddrSpaceType));
- }
- ContextId = System.ContextIdManager.GetId();
- ulong AddrSpaceBase = 0;
- ulong AddrSpaceSize = 1UL << AddrSpaceSizes[(int)AddrSpaceType];
- KernelResult Result = CreateUserAddressSpace(
- AddrSpaceType,
- AslrEnabled,
- AslrDisabled,
- AddrSpaceBase,
- AddrSpaceSize,
- MemRegion,
- Address,
- Size,
- BlockAllocator);
- if (Result != KernelResult.Success)
- {
- System.ContextIdManager.PutId(ContextId);
- }
- return Result;
- }
- private class Region
- {
- public ulong Start;
- public ulong End;
- public ulong Size;
- public ulong AslrOffset;
- }
- private KernelResult CreateUserAddressSpace(
- AddressSpaceType AddrSpaceType,
- bool AslrEnabled,
- bool AslrDisabled,
- ulong AddrSpaceStart,
- ulong AddrSpaceEnd,
- MemoryRegion MemRegion,
- ulong Address,
- ulong Size,
- KMemoryBlockAllocator BlockAllocator)
- {
- ulong EndAddr = Address + Size;
- Region AliasRegion = new Region();
- Region HeapRegion = new Region();
- Region StackRegion = new Region();
- Region TlsIoRegion = new Region();
- ulong CodeRegionSize;
- ulong StackAndTlsIoStart;
- ulong StackAndTlsIoEnd;
- ulong BaseAddress;
- switch (AddrSpaceType)
- {
- case AddressSpaceType.Addr32Bits:
- AliasRegion.Size = 0x40000000;
- HeapRegion.Size = 0x40000000;
- StackRegion.Size = 0;
- TlsIoRegion.Size = 0;
- CodeRegionStart = 0x200000;
- CodeRegionSize = 0x3fe00000;
- StackAndTlsIoStart = 0x200000;
- StackAndTlsIoEnd = 0x40000000;
- BaseAddress = 0x200000;
- AddrSpaceWidth = 32;
- break;
- case AddressSpaceType.Addr36Bits:
- AliasRegion.Size = 0x180000000;
- HeapRegion.Size = 0x180000000;
- StackRegion.Size = 0;
- TlsIoRegion.Size = 0;
- CodeRegionStart = 0x8000000;
- CodeRegionSize = 0x78000000;
- StackAndTlsIoStart = 0x8000000;
- StackAndTlsIoEnd = 0x80000000;
- BaseAddress = 0x8000000;
- AddrSpaceWidth = 36;
- break;
- case AddressSpaceType.Addr32BitsNoMap:
- AliasRegion.Size = 0;
- HeapRegion.Size = 0x80000000;
- StackRegion.Size = 0;
- TlsIoRegion.Size = 0;
- CodeRegionStart = 0x200000;
- CodeRegionSize = 0x3fe00000;
- StackAndTlsIoStart = 0x200000;
- StackAndTlsIoEnd = 0x40000000;
- BaseAddress = 0x200000;
- AddrSpaceWidth = 32;
- break;
- case AddressSpaceType.Addr39Bits:
- AliasRegion.Size = 0x1000000000;
- HeapRegion.Size = 0x180000000;
- StackRegion.Size = 0x80000000;
- TlsIoRegion.Size = 0x1000000000;
- CodeRegionStart = BitUtils.AlignDown(Address, 0x200000);
- CodeRegionSize = BitUtils.AlignUp (EndAddr, 0x200000) - CodeRegionStart;
- StackAndTlsIoStart = 0;
- StackAndTlsIoEnd = 0;
- BaseAddress = 0x8000000;
- AddrSpaceWidth = 39;
- break;
- default: throw new ArgumentException(nameof(AddrSpaceType));
- }
- CodeRegionEnd = CodeRegionStart + CodeRegionSize;
- ulong MapBaseAddress;
- ulong MapAvailableSize;
- if (CodeRegionStart - BaseAddress >= AddrSpaceEnd - CodeRegionEnd)
- {
- //Has more space before the start of the code region.
- MapBaseAddress = BaseAddress;
- MapAvailableSize = CodeRegionStart - BaseAddress;
- }
- else
- {
- //Has more space after the end of the code region.
- MapBaseAddress = CodeRegionEnd;
- MapAvailableSize = AddrSpaceEnd - CodeRegionEnd;
- }
- ulong MapTotalSize = AliasRegion.Size + HeapRegion.Size + StackRegion.Size + TlsIoRegion.Size;
- ulong AslrMaxOffset = MapAvailableSize - MapTotalSize;
- this.AslrEnabled = AslrEnabled;
- this.AddrSpaceStart = AddrSpaceStart;
- this.AddrSpaceEnd = AddrSpaceEnd;
- this.BlockAllocator = BlockAllocator;
- if (MapAvailableSize < MapTotalSize)
- {
- return KernelResult.OutOfMemory;
- }
- if (AslrEnabled)
- {
- AliasRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
- HeapRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
- StackRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
- TlsIoRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
- }
- //Regions are sorted based on ASLR offset.
- //When ASLR is disabled, the order is Map, Heap, NewMap and TlsIo.
- AliasRegion.Start = MapBaseAddress + AliasRegion.AslrOffset;
- AliasRegion.End = AliasRegion.Start + AliasRegion.Size;
- HeapRegion.Start = MapBaseAddress + HeapRegion.AslrOffset;
- HeapRegion.End = HeapRegion.Start + HeapRegion.Size;
- StackRegion.Start = MapBaseAddress + StackRegion.AslrOffset;
- StackRegion.End = StackRegion.Start + StackRegion.Size;
- TlsIoRegion.Start = MapBaseAddress + TlsIoRegion.AslrOffset;
- TlsIoRegion.End = TlsIoRegion.Start + TlsIoRegion.Size;
- SortRegion(HeapRegion, AliasRegion);
- if (StackRegion.Size != 0)
- {
- SortRegion(StackRegion, AliasRegion);
- SortRegion(StackRegion, HeapRegion);
- }
- else
- {
- StackRegion.Start = StackAndTlsIoStart;
- StackRegion.End = StackAndTlsIoEnd;
- }
- if (TlsIoRegion.Size != 0)
- {
- SortRegion(TlsIoRegion, AliasRegion);
- SortRegion(TlsIoRegion, HeapRegion);
- SortRegion(TlsIoRegion, StackRegion);
- }
- else
- {
- TlsIoRegion.Start = StackAndTlsIoStart;
- TlsIoRegion.End = StackAndTlsIoEnd;
- }
- AliasRegionStart = AliasRegion.Start;
- AliasRegionEnd = AliasRegion.End;
- HeapRegionStart = HeapRegion.Start;
- HeapRegionEnd = HeapRegion.End;
- StackRegionStart = StackRegion.Start;
- StackRegionEnd = StackRegion.End;
- TlsIoRegionStart = TlsIoRegion.Start;
- TlsIoRegionEnd = TlsIoRegion.End;
- CurrentHeapAddr = HeapRegionStart;
- HeapCapacity = 0;
- PhysicalMemoryUsage = 0;
- this.MemRegion = MemRegion;
- this.AslrDisabled = AslrDisabled;
- return InitializeBlocks(AddrSpaceStart, AddrSpaceEnd);
- }
- private ulong GetRandomValue(ulong Min, ulong Max)
- {
- return (ulong)GetRandomValue((long)Min, (long)Max);
- }
- private long GetRandomValue(long Min, long Max)
- {
- if (RandomNumberGenerator == null)
- {
- RandomNumberGenerator = new MersenneTwister(0);
- }
- return RandomNumberGenerator.GenRandomNumber(Min, Max);
- }
- private static void SortRegion(Region Lhs, Region Rhs)
- {
- if (Lhs.AslrOffset < Rhs.AslrOffset)
- {
- Rhs.Start += Lhs.Size;
- Rhs.End += Lhs.Size;
- }
- else
- {
- Lhs.Start += Rhs.Size;
- Lhs.End += Rhs.Size;
- }
- }
- private KernelResult InitializeBlocks(ulong AddrSpaceStart, ulong AddrSpaceEnd)
- {
- //First insertion will always need only a single block,
- //because there's nothing else to split.
- if (!BlockAllocator.CanAllocate(1))
- {
- return KernelResult.OutOfResource;
- }
- ulong AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize;
- InsertBlock(AddrSpaceStart, AddrSpacePagesCount, MemoryState.Unmapped);
- return KernelResult.Success;
- }
- public KernelResult MapPages(
- ulong Address,
- KPageList PageList,
- MemoryState State,
- MemoryPermission Permission)
- {
- ulong PagesCount = PageList.GetPagesCount();
- ulong Size = PagesCount * PageSize;
- if (!ValidateRegionForState(Address, Size, State))
- {
- return KernelResult.InvalidMemState;
- }
- lock (Blocks)
- {
- if (!IsUnmapped(Address, PagesCount * PageSize))
- {
- return KernelResult.InvalidMemState;
- }
- if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
- {
- return KernelResult.OutOfResource;
- }
- KernelResult Result = MapPages(Address, PageList, Permission);
- if (Result == KernelResult.Success)
- {
- InsertBlock(Address, PagesCount, State, Permission);
- }
- return Result;
- }
- }
- public KernelResult UnmapPages(ulong Address, KPageList PageList, MemoryState StateExpected)
- {
- ulong PagesCount = PageList.GetPagesCount();
- ulong Size = PagesCount * PageSize;
- ulong EndAddr = Address + Size;
- ulong AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize;
- if (AddrSpaceStart > Address)
- {
- return KernelResult.InvalidMemState;
- }
- if (AddrSpacePagesCount < PagesCount)
- {
- return KernelResult.InvalidMemState;
- }
- if (EndAddr - 1 > AddrSpaceEnd - 1)
- {
- return KernelResult.InvalidMemState;
- }
- lock (Blocks)
- {
- KPageList CurrentPageList = new KPageList();
- AddVaRangeToPageList(CurrentPageList, Address, PagesCount);
- if (!CurrentPageList.IsEqual(PageList))
- {
- return KernelResult.InvalidMemRange;
- }
- if (CheckRange(
- Address,
- Size,
- MemoryState.Mask,
- StateExpected,
- MemoryPermission.None,
- MemoryPermission.None,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out MemoryState State,
- out _,
- out _))
- {
- if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
- {
- return KernelResult.OutOfResource;
- }
- KernelResult Result = MmuUnmap(Address, PagesCount);
- if (Result == KernelResult.Success)
- {
- InsertBlock(Address, PagesCount, MemoryState.Unmapped);
- }
- return Result;
- }
- else
- {
- return KernelResult.InvalidMemState;
- }
- }
- }
- public KernelResult MapNormalMemory(long Address, long Size, MemoryPermission Permission)
- {
- //TODO.
- return KernelResult.Success;
- }
- public KernelResult MapIoMemory(long Address, long Size, MemoryPermission Permission)
- {
- //TODO.
- return KernelResult.Success;
- }
- public KernelResult AllocateOrMapPa(
- ulong NeededPagesCount,
- int Alignment,
- ulong SrcPa,
- bool Map,
- ulong RegionStart,
- ulong RegionPagesCount,
- MemoryState State,
- MemoryPermission Permission,
- out ulong Address)
- {
- Address = 0;
- ulong RegionSize = RegionPagesCount * PageSize;
- ulong RegionEndAddr = RegionStart + RegionSize;
- if (!ValidateRegionForState(RegionStart, RegionSize, State))
- {
- return KernelResult.InvalidMemState;
- }
- if (RegionPagesCount <= NeededPagesCount)
- {
- return KernelResult.OutOfMemory;
- }
- ulong ReservedPagesCount = IsKernel ? 1UL : 4UL;
- lock (Blocks)
- {
- if (AslrEnabled)
- {
- ulong TotalNeededSize = (ReservedPagesCount + NeededPagesCount) * PageSize;
- ulong RemainingPages = RegionPagesCount - NeededPagesCount;
- ulong AslrMaxOffset = ((RemainingPages + ReservedPagesCount) * PageSize) / (ulong)Alignment;
- for (int Attempt = 0; Attempt < 8; Attempt++)
- {
- Address = BitUtils.AlignDown(RegionStart + GetRandomValue(0, AslrMaxOffset) * (ulong)Alignment, Alignment);
- ulong EndAddr = Address + TotalNeededSize;
- KMemoryInfo Info = FindBlock(Address).GetInfo();
- if (Info.State != MemoryState.Unmapped)
- {
- continue;
- }
- ulong CurrBaseAddr = Info.Address + ReservedPagesCount * PageSize;
- ulong CurrEndAddr = Info.Address + Info.Size;
- if (Address >= RegionStart &&
- Address >= CurrBaseAddr &&
- EndAddr - 1 <= RegionEndAddr - 1 &&
- EndAddr - 1 <= CurrEndAddr - 1)
- {
- break;
- }
- }
- if (Address == 0)
- {
- ulong AslrPage = GetRandomValue(0, AslrMaxOffset);
- Address = FindFirstFit(
- RegionStart + AslrPage * PageSize,
- RegionPagesCount - AslrPage,
- NeededPagesCount,
- Alignment,
- 0,
- ReservedPagesCount);
- }
- }
- if (Address == 0)
- {
- Address = FindFirstFit(
- RegionStart,
- RegionPagesCount,
- NeededPagesCount,
- Alignment,
- 0,
- ReservedPagesCount);
- }
- if (Address == 0)
- {
- return KernelResult.OutOfMemory;
- }
- if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
- {
- return KernelResult.OutOfResource;
- }
- MemoryOperation Operation = Map
- ? MemoryOperation.MapPa
- : MemoryOperation.Allocate;
- KernelResult Result = DoMmuOperation(
- Address,
- NeededPagesCount,
- SrcPa,
- Map,
- Permission,
- Operation);
- if (Result != KernelResult.Success)
- {
- return Result;
- }
- InsertBlock(Address, NeededPagesCount, State, Permission);
- }
- return KernelResult.Success;
- }
- public KernelResult MapNewProcessCode(
- ulong Address,
- ulong PagesCount,
- MemoryState State,
- MemoryPermission Permission)
- {
- ulong Size = PagesCount * PageSize;
- if (!ValidateRegionForState(Address, Size, State))
- {
- return KernelResult.InvalidMemState;
- }
- lock (Blocks)
- {
- if (!IsUnmapped(Address, Size))
- {
- return KernelResult.InvalidMemState;
- }
- if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
- {
- return KernelResult.OutOfResource;
- }
- KernelResult Result = DoMmuOperation(
- Address,
- PagesCount,
- 0,
- false,
- Permission,
- MemoryOperation.Allocate);
- if (Result == KernelResult.Success)
- {
- InsertBlock(Address, PagesCount, State, Permission);
- }
- return Result;
- }
- }
- public KernelResult MapProcessCodeMemory(ulong Dst, ulong Src, ulong Size)
- {
- ulong PagesCount = Size / PageSize;
- lock (Blocks)
- {
- bool Success = CheckRange(
- Src,
- Size,
- MemoryState.Mask,
- MemoryState.Heap,
- MemoryPermission.Mask,
- MemoryPermission.ReadAndWrite,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out MemoryState State,
- out MemoryPermission Permission,
- out _);
- Success &= IsUnmapped(Dst, Size);
- if (Success)
- {
- if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
- {
- return KernelResult.OutOfResource;
- }
- KPageList PageList = new KPageList();
- AddVaRangeToPageList(PageList, Src, PagesCount);
- KernelResult Result = MmuChangePermission(Src, PagesCount, MemoryPermission.None);
- if (Result != KernelResult.Success)
- {
- return Result;
- }
- Result = MapPages(Dst, PageList, MemoryPermission.None);
- if (Result != KernelResult.Success)
- {
- MmuChangePermission(Src, PagesCount, Permission);
- return Result;
- }
- InsertBlock(Src, PagesCount, State, MemoryPermission.None, MemoryAttribute.Borrowed);
- InsertBlock(Dst, PagesCount, MemoryState.ModCodeStatic);
- return KernelResult.Success;
- }
- else
- {
- return KernelResult.InvalidMemState;
- }
- }
- }
- public KernelResult UnmapProcessCodeMemory(ulong Dst, ulong Src, ulong Size)
- {
- ulong PagesCount = Size / PageSize;
- lock (Blocks)
- {
- bool Success = CheckRange(
- Src,
- Size,
- MemoryState.Mask,
- MemoryState.Heap,
- MemoryPermission.None,
- MemoryPermission.None,
- MemoryAttribute.Mask,
- MemoryAttribute.Borrowed,
- MemoryAttribute.IpcAndDeviceMapped,
- out _,
- out _,
- out _);
- Success &= CheckRange(
- Dst,
- PageSize,
- MemoryState.UnmapProcessCodeMemoryAllowed,
- MemoryState.UnmapProcessCodeMemoryAllowed,
- MemoryPermission.None,
- MemoryPermission.None,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out MemoryState State,
- out _,
- out _);
- Success &= CheckRange(
- Dst,
- Size,
- MemoryState.Mask,
- State,
- MemoryPermission.None,
- MemoryPermission.None,
- MemoryAttribute.Mask,
- MemoryAttribute.None);
- if (Success)
- {
- KernelResult Result = MmuUnmap(Dst, PagesCount);
- if (Result != KernelResult.Success)
- {
- return Result;
- }
- //TODO: Missing some checks here.
- if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
- {
- return KernelResult.OutOfResource;
- }
- InsertBlock(Dst, PagesCount, MemoryState.Unmapped);
- InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
- return KernelResult.Success;
- }
- else
- {
- return KernelResult.InvalidMemState;
- }
- }
- }
- public KernelResult SetHeapSize(ulong Size, out ulong Address)
- {
- Address = 0;
- if (Size > HeapRegionEnd - HeapRegionStart)
- {
- return KernelResult.OutOfMemory;
- }
- KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
- ulong CurrentHeapSize = GetHeapSize();
- if (CurrentHeapSize <= Size)
- {
- //Expand.
- ulong DiffSize = Size - CurrentHeapSize;
- lock (Blocks)
- {
- if (CurrentProcess.ResourceLimit != null && DiffSize != 0 &&
- !CurrentProcess.ResourceLimit.Reserve(LimitableResource.Memory, DiffSize))
- {
- return KernelResult.ResLimitExceeded;
- }
- ulong PagesCount = DiffSize / PageSize;
- KMemoryRegionManager Region = GetMemoryRegionManager();
- KernelResult Result = Region.AllocatePages(PagesCount, AslrDisabled, out KPageList PageList);
- void CleanUpForError()
- {
- if (PageList != null)
- {
- Region.FreePages(PageList);
- }
- if (CurrentProcess.ResourceLimit != null && DiffSize != 0)
- {
- CurrentProcess.ResourceLimit.Release(LimitableResource.Memory, DiffSize);
- }
- }
- if (Result != KernelResult.Success)
- {
- CleanUpForError();
- return Result;
- }
- if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
- {
- CleanUpForError();
- return KernelResult.OutOfResource;
- }
- if (!IsUnmapped(CurrentHeapAddr, DiffSize))
- {
- CleanUpForError();
- return KernelResult.InvalidMemState;
- }
- Result = DoMmuOperation(
- CurrentHeapAddr,
- PagesCount,
- PageList,
- MemoryPermission.ReadAndWrite,
- MemoryOperation.MapVa);
- if (Result != KernelResult.Success)
- {
- CleanUpForError();
- return Result;
- }
- InsertBlock(CurrentHeapAddr, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
- }
- }
- else
- {
- //Shrink.
- ulong FreeAddr = HeapRegionStart + Size;
- ulong DiffSize = CurrentHeapSize - Size;
- lock (Blocks)
- {
- if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
- {
- return KernelResult.OutOfResource;
- }
- if (!CheckRange(
- FreeAddr,
- DiffSize,
- MemoryState.Mask,
- MemoryState.Heap,
- MemoryPermission.Mask,
- MemoryPermission.ReadAndWrite,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out _,
- out _,
- out _))
- {
- return KernelResult.InvalidMemState;
- }
- ulong PagesCount = DiffSize / PageSize;
- KernelResult Result = MmuUnmap(FreeAddr, PagesCount);
- if (Result != KernelResult.Success)
- {
- return Result;
- }
- CurrentProcess.ResourceLimit?.Release(LimitableResource.Memory, BitUtils.AlignDown(DiffSize, PageSize));
- InsertBlock(FreeAddr, PagesCount, MemoryState.Unmapped);
- }
- }
- CurrentHeapAddr = HeapRegionStart + Size;
- Address = HeapRegionStart;
- return KernelResult.Success;
- }
- public ulong GetTotalHeapSize()
- {
- lock (Blocks)
- {
- return GetHeapSize() + PhysicalMemoryUsage;
- }
- }
- private ulong GetHeapSize()
- {
- return CurrentHeapAddr - HeapRegionStart;
- }
- public KernelResult SetHeapCapacity(ulong Capacity)
- {
- lock (Blocks)
- {
- HeapCapacity = Capacity;
- }
- return KernelResult.Success;
- }
- public KernelResult SetMemoryAttribute(
- ulong Address,
- ulong Size,
- MemoryAttribute AttributeMask,
- MemoryAttribute AttributeValue)
- {
- lock (Blocks)
- {
- if (CheckRange(
- Address,
- Size,
- MemoryState.AttributeChangeAllowed,
- MemoryState.AttributeChangeAllowed,
- MemoryPermission.None,
- MemoryPermission.None,
- MemoryAttribute.BorrowedAndIpcMapped,
- MemoryAttribute.None,
- MemoryAttribute.DeviceMappedAndUncached,
- out MemoryState State,
- out MemoryPermission Permission,
- out MemoryAttribute Attribute))
- {
- if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
- {
- return KernelResult.OutOfResource;
- }
- ulong PagesCount = Size / PageSize;
- Attribute &= ~AttributeMask;
- Attribute |= AttributeMask & AttributeValue;
- InsertBlock(Address, PagesCount, State, Permission, Attribute);
- return KernelResult.Success;
- }
- else
- {
- return KernelResult.InvalidMemState;
- }
- }
- }
- public KMemoryInfo QueryMemory(ulong Address)
- {
- if (Address >= AddrSpaceStart &&
- Address < AddrSpaceEnd)
- {
- lock (Blocks)
- {
- return FindBlock(Address).GetInfo();
- }
- }
- else
- {
- return new KMemoryInfo(
- AddrSpaceEnd,
- ~AddrSpaceEnd + 1,
- MemoryState.Reserved,
- MemoryPermission.None,
- MemoryAttribute.None,
- 0,
- 0);
- }
- }
- public KernelResult Map(ulong Dst, ulong Src, ulong Size)
- {
- bool Success;
- lock (Blocks)
- {
- Success = CheckRange(
- Src,
- Size,
- MemoryState.MapAllowed,
- MemoryState.MapAllowed,
- MemoryPermission.Mask,
- MemoryPermission.ReadAndWrite,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out MemoryState SrcState,
- out _,
- out _);
- Success &= IsUnmapped(Dst, Size);
- if (Success)
- {
- if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
- {
- return KernelResult.OutOfResource;
- }
- ulong PagesCount = Size / PageSize;
- KPageList PageList = new KPageList();
- AddVaRangeToPageList(PageList, Src, PagesCount);
- KernelResult Result = MmuChangePermission(Src, PagesCount, MemoryPermission.None);
- if (Result != KernelResult.Success)
- {
- return Result;
- }
- Result = MapPages(Dst, PageList, MemoryPermission.ReadAndWrite);
- if (Result != KernelResult.Success)
- {
- if (MmuChangePermission(Src, PagesCount, MemoryPermission.ReadAndWrite) != KernelResult.Success)
- {
- throw new InvalidOperationException("Unexpected failure reverting memory permission.");
- }
- return Result;
- }
- InsertBlock(Src, PagesCount, SrcState, MemoryPermission.None, MemoryAttribute.Borrowed);
- InsertBlock(Dst, PagesCount, MemoryState.Stack, MemoryPermission.ReadAndWrite);
- return KernelResult.Success;
- }
- else
- {
- return KernelResult.InvalidMemState;
- }
- }
- }
- public KernelResult UnmapForKernel(ulong Address, ulong PagesCount, MemoryState StateExpected)
- {
- ulong Size = PagesCount * PageSize;
- lock (Blocks)
- {
- if (CheckRange(
- Address,
- Size,
- MemoryState.Mask,
- StateExpected,
- MemoryPermission.None,
- MemoryPermission.None,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out _,
- out _,
- out _))
- {
- if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
- {
- return KernelResult.OutOfResource;
- }
- KernelResult Result = MmuUnmap(Address, PagesCount);
- if (Result == KernelResult.Success)
- {
- InsertBlock(Address, PagesCount, MemoryState.Unmapped);
- }
- return KernelResult.Success;
- }
- else
- {
- return KernelResult.InvalidMemState;
- }
- }
- }
- public KernelResult Unmap(ulong Dst, ulong Src, ulong Size)
- {
- bool Success;
- lock (Blocks)
- {
- Success = CheckRange(
- Src,
- Size,
- MemoryState.MapAllowed,
- MemoryState.MapAllowed,
- MemoryPermission.Mask,
- MemoryPermission.None,
- MemoryAttribute.Mask,
- MemoryAttribute.Borrowed,
- MemoryAttribute.IpcAndDeviceMapped,
- out MemoryState SrcState,
- out _,
- out _);
- Success &= CheckRange(
- Dst,
- Size,
- MemoryState.Mask,
- MemoryState.Stack,
- MemoryPermission.None,
- MemoryPermission.None,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out _,
- out MemoryPermission DstPermission,
- out _);
- if (Success)
- {
- if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
- {
- return KernelResult.OutOfResource;
- }
- ulong PagesCount = Size / PageSize;
- KPageList SrcPageList = new KPageList();
- KPageList DstPageList = new KPageList();
- AddVaRangeToPageList(SrcPageList, Src, PagesCount);
- AddVaRangeToPageList(DstPageList, Dst, PagesCount);
- if (!DstPageList.IsEqual(SrcPageList))
- {
- return KernelResult.InvalidMemRange;
- }
- KernelResult Result = MmuUnmap(Dst, PagesCount);
- if (Result != KernelResult.Success)
- {
- return Result;
- }
- Result = MmuChangePermission(Src, PagesCount, MemoryPermission.ReadAndWrite);
- if (Result != KernelResult.Success)
- {
- MapPages(Dst, DstPageList, DstPermission);
- return Result;
- }
- InsertBlock(Src, PagesCount, SrcState, MemoryPermission.ReadAndWrite);
- InsertBlock(Dst, PagesCount, MemoryState.Unmapped);
- return KernelResult.Success;
- }
- else
- {
- return KernelResult.InvalidMemState;
- }
- }
- }
- public KernelResult ReserveTransferMemory(ulong Address, ulong Size, MemoryPermission Permission)
- {
- lock (Blocks)
- {
- if (CheckRange(
- Address,
- Size,
- MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
- MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
- MemoryPermission.Mask,
- MemoryPermission.ReadAndWrite,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out MemoryState State,
- out _,
- out MemoryAttribute Attribute))
- {
- //TODO: Missing checks.
- if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
- {
- return KernelResult.OutOfResource;
- }
- ulong PagesCount = Size / PageSize;
- Attribute |= MemoryAttribute.Borrowed;
- InsertBlock(Address, PagesCount, State, Permission, Attribute);
- return KernelResult.Success;
- }
- else
- {
- return KernelResult.InvalidMemState;
- }
- }
- }
- public KernelResult ResetTransferMemory(ulong Address, ulong Size)
- {
- lock (Blocks)
- {
- if (CheckRange(
- Address,
- Size,
- MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
- MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
- MemoryPermission.None,
- MemoryPermission.None,
- MemoryAttribute.Mask,
- MemoryAttribute.Borrowed,
- MemoryAttribute.IpcAndDeviceMapped,
- out MemoryState State,
- out _,
- out _))
- {
- if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
- {
- return KernelResult.OutOfResource;
- }
- ulong PagesCount = Size / PageSize;
- InsertBlock(Address, PagesCount, State, MemoryPermission.ReadAndWrite);
- return KernelResult.Success;
- }
- else
- {
- return KernelResult.InvalidMemState;
- }
- }
- }
- public KernelResult SetProcessMemoryPermission(ulong Address, ulong Size, MemoryPermission Permission)
- {
- lock (Blocks)
- {
- if (CheckRange(
- Address,
- Size,
- MemoryState.ProcessPermissionChangeAllowed,
- MemoryState.ProcessPermissionChangeAllowed,
- MemoryPermission.None,
- MemoryPermission.None,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out MemoryState OldState,
- out MemoryPermission OldPermission,
- out _))
- {
- MemoryState NewState = OldState;
- //If writing into the code region is allowed, then we need
- //to change it to mutable.
- if ((Permission & MemoryPermission.Write) != 0)
- {
- if (OldState == MemoryState.CodeStatic)
- {
- NewState = MemoryState.CodeMutable;
- }
- else if (OldState == MemoryState.ModCodeStatic)
- {
- NewState = MemoryState.ModCodeMutable;
- }
- else
- {
- throw new InvalidOperationException($"Memory state \"{OldState}\" not valid for this operation.");
- }
- }
- if (NewState != OldState || Permission != OldPermission)
- {
- if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
- {
- return KernelResult.OutOfResource;
- }
- ulong PagesCount = Size / PageSize;
- MemoryOperation Operation = (Permission & MemoryPermission.Execute) != 0
- ? MemoryOperation.ChangePermsAndAttributes
- : MemoryOperation.ChangePermRw;
- KernelResult Result = DoMmuOperation(Address, PagesCount, 0, false, Permission, Operation);
- if (Result != KernelResult.Success)
- {
- return Result;
- }
- InsertBlock(Address, PagesCount, NewState, Permission);
- }
- return KernelResult.Success;
- }
- else
- {
- return KernelResult.InvalidMemState;
- }
- }
- }
- public KernelResult MapPhysicalMemory(ulong Address, ulong Size)
- {
- ulong EndAddr = Address + Size;
- lock (Blocks)
- {
- ulong MappedSize = 0;
- KMemoryInfo Info;
- LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
- do
- {
- Info = Node.Value.GetInfo();
- if (Info.State != MemoryState.Unmapped)
- {
- MappedSize += GetSizeInRange(Info, Address, EndAddr);
- }
- Node = Node.Next;
- }
- while (Info.Address + Info.Size < EndAddr && Node != null);
- if (MappedSize == Size)
- {
- return KernelResult.Success;
- }
- ulong RemainingSize = Size - MappedSize;
- ulong RemainingPages = RemainingSize / PageSize;
- KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
- if (CurrentProcess.ResourceLimit != null &&
- !CurrentProcess.ResourceLimit.Reserve(LimitableResource.Memory, RemainingSize))
- {
- return KernelResult.ResLimitExceeded;
- }
- KMemoryRegionManager Region = GetMemoryRegionManager();
- KernelResult Result = Region.AllocatePages(RemainingPages, AslrDisabled, out KPageList PageList);
- void CleanUpForError()
- {
- if (PageList != null)
- {
- Region.FreePages(PageList);
- }
- CurrentProcess.ResourceLimit?.Release(LimitableResource.Memory, RemainingSize);
- }
- if (Result != KernelResult.Success)
- {
- CleanUpForError();
- return Result;
- }
- if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
- {
- CleanUpForError();
- return KernelResult.OutOfResource;
- }
- MapPhysicalMemory(PageList, Address, EndAddr);
- PhysicalMemoryUsage += RemainingSize;
- ulong PagesCount = Size / PageSize;
- InsertBlock(
- Address,
- PagesCount,
- MemoryState.Unmapped,
- MemoryPermission.None,
- MemoryAttribute.None,
- MemoryState.Heap,
- MemoryPermission.ReadAndWrite,
- MemoryAttribute.None);
- }
- return KernelResult.Success;
- }
- public KernelResult UnmapPhysicalMemory(ulong Address, ulong Size)
- {
- ulong EndAddr = Address + Size;
- lock (Blocks)
- {
- //Scan, ensure that the region can be unmapped (all blocks are heap or
- //already unmapped), fill pages list for freeing memory.
- ulong HeapMappedSize = 0;
- KPageList PageList = new KPageList();
- KMemoryInfo Info;
- LinkedListNode<KMemoryBlock> BaseNode = FindBlockNode(Address);
- LinkedListNode<KMemoryBlock> Node = BaseNode;
- do
- {
- Info = Node.Value.GetInfo();
- if (Info.State == MemoryState.Heap)
- {
- if (Info.Attribute != MemoryAttribute.None)
- {
- return KernelResult.InvalidMemState;
- }
- ulong BlockSize = GetSizeInRange(Info, Address, EndAddr);
- ulong BlockAddress = GetAddrInRange(Info, Address);
- AddVaRangeToPageList(PageList, BlockAddress, BlockSize / PageSize);
- HeapMappedSize += BlockSize;
- }
- else if (Info.State != MemoryState.Unmapped)
- {
- return KernelResult.InvalidMemState;
- }
- Node = Node.Next;
- }
- while (Info.Address + Info.Size < EndAddr && Node != null);
- if (HeapMappedSize == 0)
- {
- return KernelResult.Success;
- }
- if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
- {
- return KernelResult.OutOfResource;
- }
- //Try to unmap all the heap mapped memory inside range.
- KernelResult Result = KernelResult.Success;
- Node = BaseNode;
- do
- {
- Info = Node.Value.GetInfo();
- if (Info.State == MemoryState.Heap)
- {
- ulong BlockSize = GetSizeInRange(Info, Address, EndAddr);
- ulong BlockAddress = GetAddrInRange(Info, Address);
- ulong BlockPagesCount = BlockSize / PageSize;
- Result = MmuUnmap(BlockAddress, BlockPagesCount);
- if (Result != KernelResult.Success)
- {
- //If we failed to unmap, we need to remap everything back again.
- MapPhysicalMemory(PageList, Address, BlockAddress + BlockSize);
- break;
- }
- }
- Node = Node.Next;
- }
- while (Info.Address + Info.Size < EndAddr && Node != null);
- if (Result == KernelResult.Success)
- {
- GetMemoryRegionManager().FreePages(PageList);
- PhysicalMemoryUsage -= HeapMappedSize;
- KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
- CurrentProcess.ResourceLimit?.Release(LimitableResource.Memory, HeapMappedSize);
- ulong PagesCount = Size / PageSize;
- InsertBlock(Address, PagesCount, MemoryState.Unmapped);
- }
- return Result;
- }
- }
- private void MapPhysicalMemory(KPageList PageList, ulong Address, ulong EndAddr)
- {
- KMemoryInfo Info;
- LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
- LinkedListNode<KPageNode> PageListNode = PageList.Nodes.First;
- KPageNode PageNode = PageListNode.Value;
- ulong SrcPa = PageNode.Address;
- ulong SrcPaPages = PageNode.PagesCount;
- do
- {
- Info = Node.Value.GetInfo();
- if (Info.State == MemoryState.Unmapped)
- {
- ulong BlockSize = GetSizeInRange(Info, Address, EndAddr);
- ulong DstVaPages = BlockSize / PageSize;
- ulong DstVa = GetAddrInRange(Info, Address);
- while (DstVaPages > 0)
- {
- if (SrcPaPages == 0)
- {
- PageListNode = PageListNode.Next;
- PageNode = PageListNode.Value;
- SrcPa = PageNode.Address;
- SrcPaPages = PageNode.PagesCount;
- }
- ulong PagesCount = SrcPaPages;
- if (PagesCount > DstVaPages)
- {
- PagesCount = DstVaPages;
- }
- DoMmuOperation(
- DstVa,
- PagesCount,
- SrcPa,
- true,
- MemoryPermission.ReadAndWrite,
- MemoryOperation.MapPa);
- DstVa += PagesCount * PageSize;
- SrcPa += PagesCount * PageSize;
- SrcPaPages -= PagesCount;
- DstVaPages -= PagesCount;
- }
- }
- Node = Node.Next;
- }
- while (Info.Address + Info.Size < EndAddr && Node != null);
- }
- private static ulong GetSizeInRange(KMemoryInfo Info, ulong Start, ulong End)
- {
- ulong EndAddr = Info.Size + Info.Address;
- ulong Size = Info.Size;
- if (Info.Address < Start)
- {
- Size -= Start - Info.Address;
- }
- if (EndAddr > End)
- {
- Size -= EndAddr - End;
- }
- return Size;
- }
- private static ulong GetAddrInRange(KMemoryInfo Info, ulong Start)
- {
- if (Info.Address < Start)
- {
- return Start;
- }
- return Info.Address;
- }
- private void AddVaRangeToPageList(KPageList PageList, ulong Start, ulong PagesCount)
- {
- ulong Address = Start;
- while (Address < Start + PagesCount * PageSize)
- {
- KernelResult Result = ConvertVaToPa(Address, out ulong Pa);
- if (Result != KernelResult.Success)
- {
- throw new InvalidOperationException("Unexpected failure translating virtual address.");
- }
- PageList.AddRange(Pa, 1);
- Address += PageSize;
- }
- }
- private bool IsUnmapped(ulong Address, ulong Size)
- {
- return CheckRange(
- Address,
- Size,
- MemoryState.Mask,
- MemoryState.Unmapped,
- MemoryPermission.Mask,
- MemoryPermission.None,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out _,
- out _,
- out _);
- }
- private bool CheckRange(
- ulong Address,
- ulong Size,
- MemoryState StateMask,
- MemoryState StateExpected,
- MemoryPermission PermissionMask,
- MemoryPermission PermissionExpected,
- MemoryAttribute AttributeMask,
- MemoryAttribute AttributeExpected,
- MemoryAttribute AttributeIgnoreMask,
- out MemoryState OutState,
- out MemoryPermission OutPermission,
- out MemoryAttribute OutAttribute)
- {
- ulong EndAddr = Address + Size - 1;
- LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
- KMemoryInfo Info = Node.Value.GetInfo();
- MemoryState FirstState = Info.State;
- MemoryPermission FirstPermission = Info.Permission;
- MemoryAttribute FirstAttribute = Info.Attribute;
- do
- {
- Info = Node.Value.GetInfo();
- //Check if the block state matches what we expect.
- if ( FirstState != Info.State ||
- FirstPermission != Info.Permission ||
- (Info.Attribute & AttributeMask) != AttributeExpected ||
- (FirstAttribute | AttributeIgnoreMask) != (Info.Attribute | AttributeIgnoreMask) ||
- (FirstState & StateMask) != StateExpected ||
- (FirstPermission & PermissionMask) != PermissionExpected)
- {
- break;
- }
- //Check if this is the last block on the range, if so return success.
- if (EndAddr <= Info.Address + Info.Size - 1)
- {
- OutState = FirstState;
- OutPermission = FirstPermission;
- OutAttribute = FirstAttribute & ~AttributeIgnoreMask;
- return true;
- }
- Node = Node.Next;
- }
- while (Node != null);
- OutState = MemoryState.Unmapped;
- OutPermission = MemoryPermission.None;
- OutAttribute = MemoryAttribute.None;
- return false;
- }
- private bool CheckRange(
- ulong Address,
- ulong Size,
- MemoryState StateMask,
- MemoryState StateExpected,
- MemoryPermission PermissionMask,
- MemoryPermission PermissionExpected,
- MemoryAttribute AttributeMask,
- MemoryAttribute AttributeExpected)
- {
- ulong EndAddr = Address + Size - 1;
- LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
- do
- {
- KMemoryInfo Info = Node.Value.GetInfo();
- //Check if the block state matches what we expect.
- if ((Info.State & StateMask) != StateExpected ||
- (Info.Permission & PermissionMask) != PermissionExpected ||
- (Info.Attribute & AttributeMask) != AttributeExpected)
- {
- break;
- }
- //Check if this is the last block on the range, if so return success.
- if (EndAddr <= Info.Address + Info.Size - 1)
- {
- return true;
- }
- Node = Node.Next;
- }
- while (Node != null);
- return false;
- }
- private void InsertBlock(
- ulong BaseAddress,
- ulong PagesCount,
- MemoryState OldState,
- MemoryPermission OldPermission,
- MemoryAttribute OldAttribute,
- MemoryState NewState,
- MemoryPermission NewPermission,
- MemoryAttribute NewAttribute)
- {
- //Insert new block on the list only on areas where the state
- //of the block matches the state specified on the Old* state
- //arguments, otherwise leave it as is.
- int OldCount = Blocks.Count;
- OldAttribute |= MemoryAttribute.IpcAndDeviceMapped;
- ulong EndAddr = PagesCount * PageSize + BaseAddress;
- LinkedListNode<KMemoryBlock> Node = Blocks.First;
- while (Node != null)
- {
- LinkedListNode<KMemoryBlock> NewNode = Node;
- LinkedListNode<KMemoryBlock> NextNode = Node.Next;
- KMemoryBlock CurrBlock = Node.Value;
- ulong CurrBaseAddr = CurrBlock.BaseAddress;
- ulong CurrEndAddr = CurrBlock.PagesCount * PageSize + CurrBaseAddr;
- if (BaseAddress < CurrEndAddr && CurrBaseAddr < EndAddr)
- {
- MemoryAttribute CurrBlockAttr = CurrBlock.Attribute | MemoryAttribute.IpcAndDeviceMapped;
- if (CurrBlock.State != OldState ||
- CurrBlock.Permission != OldPermission ||
- CurrBlockAttr != OldAttribute)
- {
- Node = NextNode;
- continue;
- }
- if (CurrBaseAddr >= BaseAddress && CurrEndAddr <= EndAddr)
- {
- CurrBlock.State = NewState;
- CurrBlock.Permission = NewPermission;
- CurrBlock.Attribute &= ~MemoryAttribute.IpcAndDeviceMapped;
- CurrBlock.Attribute |= NewAttribute;
- }
- else if (CurrBaseAddr >= BaseAddress)
- {
- CurrBlock.BaseAddress = EndAddr;
- CurrBlock.PagesCount = (CurrEndAddr - EndAddr) / PageSize;
- ulong NewPagesCount = (EndAddr - CurrBaseAddr) / PageSize;
- NewNode = Blocks.AddBefore(Node, new KMemoryBlock(
- CurrBaseAddr,
- NewPagesCount,
- NewState,
- NewPermission,
- NewAttribute));
- }
- else if (CurrEndAddr <= EndAddr)
- {
- CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
- ulong NewPagesCount = (CurrEndAddr - BaseAddress) / PageSize;
- NewNode = Blocks.AddAfter(Node, new KMemoryBlock(
- BaseAddress,
- NewPagesCount,
- NewState,
- NewPermission,
- NewAttribute));
- }
- else
- {
- CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
- ulong NextPagesCount = (CurrEndAddr - EndAddr) / PageSize;
- NewNode = Blocks.AddAfter(Node, new KMemoryBlock(
- BaseAddress,
- PagesCount,
- NewState,
- NewPermission,
- NewAttribute));
- Blocks.AddAfter(NewNode, new KMemoryBlock(
- EndAddr,
- NextPagesCount,
- CurrBlock.State,
- CurrBlock.Permission,
- CurrBlock.Attribute));
- NextNode = null;
- }
- MergeEqualStateNeighbours(NewNode);
- }
- Node = NextNode;
- }
- BlockAllocator.Count += Blocks.Count - OldCount;
- }
- private void InsertBlock(
- ulong BaseAddress,
- ulong PagesCount,
- MemoryState State,
- MemoryPermission Permission = MemoryPermission.None,
- MemoryAttribute Attribute = MemoryAttribute.None)
- {
- //Inserts new block at the list, replacing and spliting
- //existing blocks as needed.
- KMemoryBlock Block = new KMemoryBlock(BaseAddress, PagesCount, State, Permission, Attribute);
- int OldCount = Blocks.Count;
- ulong EndAddr = PagesCount * PageSize + BaseAddress;
- LinkedListNode<KMemoryBlock> NewNode = null;
- LinkedListNode<KMemoryBlock> Node = Blocks.First;
- while (Node != null)
- {
- KMemoryBlock CurrBlock = Node.Value;
- LinkedListNode<KMemoryBlock> NextNode = Node.Next;
- ulong CurrBaseAddr = CurrBlock.BaseAddress;
- ulong CurrEndAddr = CurrBlock.PagesCount * PageSize + CurrBaseAddr;
- if (BaseAddress < CurrEndAddr && CurrBaseAddr < EndAddr)
- {
- if (BaseAddress >= CurrBaseAddr && EndAddr <= CurrEndAddr)
- {
- Block.Attribute |= CurrBlock.Attribute & MemoryAttribute.IpcAndDeviceMapped;
- }
- if (BaseAddress > CurrBaseAddr && EndAddr < CurrEndAddr)
- {
- CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
- ulong NextPagesCount = (CurrEndAddr - EndAddr) / PageSize;
- NewNode = Blocks.AddAfter(Node, Block);
- Blocks.AddAfter(NewNode, new KMemoryBlock(
- EndAddr,
- NextPagesCount,
- CurrBlock.State,
- CurrBlock.Permission,
- CurrBlock.Attribute));
- break;
- }
- else if (BaseAddress <= CurrBaseAddr && EndAddr < CurrEndAddr)
- {
- CurrBlock.BaseAddress = EndAddr;
- CurrBlock.PagesCount = (CurrEndAddr - EndAddr) / PageSize;
- if (NewNode == null)
- {
- NewNode = Blocks.AddBefore(Node, Block);
- }
- }
- else if (BaseAddress > CurrBaseAddr && EndAddr >= CurrEndAddr)
- {
- CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
- if (NewNode == null)
- {
- NewNode = Blocks.AddAfter(Node, Block);
- }
- }
- else
- {
- if (NewNode == null)
- {
- NewNode = Blocks.AddBefore(Node, Block);
- }
- Blocks.Remove(Node);
- }
- }
- Node = NextNode;
- }
- if (NewNode == null)
- {
- NewNode = Blocks.AddFirst(Block);
- }
- MergeEqualStateNeighbours(NewNode);
- BlockAllocator.Count += Blocks.Count - OldCount;
- }
- private void MergeEqualStateNeighbours(LinkedListNode<KMemoryBlock> Node)
- {
- KMemoryBlock Block = Node.Value;
- ulong EndAddr = Block.PagesCount * PageSize + Block.BaseAddress;
- if (Node.Previous != null)
- {
- KMemoryBlock Previous = Node.Previous.Value;
- if (BlockStateEquals(Block, Previous))
- {
- Blocks.Remove(Node.Previous);
- Block.BaseAddress = Previous.BaseAddress;
- }
- }
- if (Node.Next != null)
- {
- KMemoryBlock Next = Node.Next.Value;
- if (BlockStateEquals(Block, Next))
- {
- Blocks.Remove(Node.Next);
- EndAddr = Next.BaseAddress + Next.PagesCount * PageSize;
- }
- }
- Block.PagesCount = (EndAddr - Block.BaseAddress) / PageSize;
- }
- private static bool BlockStateEquals(KMemoryBlock Lhs, KMemoryBlock Rhs)
- {
- return Lhs.State == Rhs.State &&
- Lhs.Permission == Rhs.Permission &&
- Lhs.Attribute == Rhs.Attribute &&
- Lhs.DeviceRefCount == Rhs.DeviceRefCount &&
- Lhs.IpcRefCount == Rhs.IpcRefCount;
- }
- private ulong FindFirstFit(
- ulong RegionStart,
- ulong RegionPagesCount,
- ulong NeededPagesCount,
- int Alignment,
- ulong ReservedStart,
- ulong ReservedPagesCount)
- {
- ulong ReservedSize = ReservedPagesCount * PageSize;
- ulong TotalNeededSize = ReservedSize + NeededPagesCount * PageSize;
- ulong RegionEndAddr = RegionStart + RegionPagesCount * PageSize;
- LinkedListNode<KMemoryBlock> Node = FindBlockNode(RegionStart);
- KMemoryInfo Info = Node.Value.GetInfo();
- while (RegionEndAddr >= Info.Address)
- {
- if (Info.State == MemoryState.Unmapped)
- {
- ulong CurrBaseAddr = Info.Address + ReservedSize;
- ulong CurrEndAddr = Info.Address + Info.Size - 1;
- ulong Address = BitUtils.AlignDown(CurrBaseAddr, Alignment) + ReservedStart;
- if (CurrBaseAddr > Address)
- {
- Address += (ulong)Alignment;
- }
- ulong AllocationEndAddr = Address + TotalNeededSize - 1;
- if (AllocationEndAddr <= RegionEndAddr &&
- AllocationEndAddr <= CurrEndAddr &&
- Address < AllocationEndAddr)
- {
- return Address;
- }
- }
- Node = Node.Next;
- if (Node == null)
- {
- break;
- }
- Info = Node.Value.GetInfo();
- }
- return 0;
- }
- private KMemoryBlock FindBlock(ulong Address)
- {
- return FindBlockNode(Address)?.Value;
- }
- private LinkedListNode<KMemoryBlock> FindBlockNode(ulong Address)
- {
- lock (Blocks)
- {
- LinkedListNode<KMemoryBlock> Node = Blocks.First;
- while (Node != null)
- {
- KMemoryBlock Block = Node.Value;
- ulong CurrEndAddr = Block.PagesCount * PageSize + Block.BaseAddress;
- if (Block.BaseAddress <= Address && CurrEndAddr - 1 >= Address)
- {
- return Node;
- }
- Node = Node.Next;
- }
- }
- return null;
- }
- private bool ValidateRegionForState(ulong Address, ulong Size, MemoryState State)
- {
- ulong EndAddr = Address + Size;
- ulong RegionBaseAddr = GetBaseAddrForState(State);
- ulong RegionEndAddr = RegionBaseAddr + GetSizeForState(State);
- bool InsideRegion()
- {
- return RegionBaseAddr <= Address &&
- EndAddr > Address &&
- EndAddr - 1 <= RegionEndAddr - 1;
- }
- bool OutsideHeapRegion()
- {
- return EndAddr <= HeapRegionStart ||
- Address >= HeapRegionEnd;
- }
- bool OutsideMapRegion()
- {
- return EndAddr <= AliasRegionStart ||
- Address >= AliasRegionEnd;
- }
- switch (State)
- {
- case MemoryState.Io:
- case MemoryState.Normal:
- case MemoryState.CodeStatic:
- case MemoryState.CodeMutable:
- case MemoryState.SharedMemory:
- case MemoryState.ModCodeStatic:
- case MemoryState.ModCodeMutable:
- case MemoryState.Stack:
- case MemoryState.ThreadLocal:
- case MemoryState.TransferMemoryIsolated:
- case MemoryState.TransferMemory:
- case MemoryState.ProcessMemory:
- case MemoryState.CodeReadOnly:
- case MemoryState.CodeWritable:
- return InsideRegion() && OutsideHeapRegion() && OutsideMapRegion();
- case MemoryState.Heap:
- return InsideRegion() && OutsideMapRegion();
- case MemoryState.IpcBuffer0:
- case MemoryState.IpcBuffer1:
- case MemoryState.IpcBuffer3:
- return InsideRegion() && OutsideHeapRegion();
- case MemoryState.KernelStack:
- return InsideRegion();
- }
- throw new ArgumentException($"Invalid state value \"{State}\".");
- }
- private ulong GetBaseAddrForState(MemoryState State)
- {
- switch (State)
- {
- case MemoryState.Io:
- case MemoryState.Normal:
- case MemoryState.ThreadLocal:
- return TlsIoRegionStart;
- case MemoryState.CodeStatic:
- case MemoryState.CodeMutable:
- case MemoryState.SharedMemory:
- case MemoryState.ModCodeStatic:
- case MemoryState.ModCodeMutable:
- case MemoryState.TransferMemoryIsolated:
- case MemoryState.TransferMemory:
- case MemoryState.ProcessMemory:
- case MemoryState.CodeReadOnly:
- case MemoryState.CodeWritable:
- return GetAddrSpaceBaseAddr();
- case MemoryState.Heap:
- return HeapRegionStart;
- case MemoryState.IpcBuffer0:
- case MemoryState.IpcBuffer1:
- case MemoryState.IpcBuffer3:
- return AliasRegionStart;
- case MemoryState.Stack:
- return StackRegionStart;
- case MemoryState.KernelStack:
- return AddrSpaceStart;
- }
- throw new ArgumentException($"Invalid state value \"{State}\".");
- }
- private ulong GetSizeForState(MemoryState State)
- {
- switch (State)
- {
- case MemoryState.Io:
- case MemoryState.Normal:
- case MemoryState.ThreadLocal:
- return TlsIoRegionEnd - TlsIoRegionStart;
- case MemoryState.CodeStatic:
- case MemoryState.CodeMutable:
- case MemoryState.SharedMemory:
- case MemoryState.ModCodeStatic:
- case MemoryState.ModCodeMutable:
- case MemoryState.TransferMemoryIsolated:
- case MemoryState.TransferMemory:
- case MemoryState.ProcessMemory:
- case MemoryState.CodeReadOnly:
- case MemoryState.CodeWritable:
- return GetAddrSpaceSize();
- case MemoryState.Heap:
- return HeapRegionEnd - HeapRegionStart;
- case MemoryState.IpcBuffer0:
- case MemoryState.IpcBuffer1:
- case MemoryState.IpcBuffer3:
- return AliasRegionEnd - AliasRegionStart;
- case MemoryState.Stack:
- return StackRegionEnd - StackRegionStart;
- case MemoryState.KernelStack:
- return AddrSpaceEnd - AddrSpaceStart;
- }
- throw new ArgumentException($"Invalid state value \"{State}\".");
- }
- public ulong GetAddrSpaceBaseAddr()
- {
- if (AddrSpaceWidth == 36 || AddrSpaceWidth == 39)
- {
- return 0x8000000;
- }
- else if (AddrSpaceWidth == 32)
- {
- return 0x200000;
- }
- else
- {
- throw new InvalidOperationException("Invalid address space width!");
- }
- }
- public ulong GetAddrSpaceSize()
- {
- if (AddrSpaceWidth == 36)
- {
- return 0xff8000000;
- }
- else if (AddrSpaceWidth == 39)
- {
- return 0x7ff8000000;
- }
- else if (AddrSpaceWidth == 32)
- {
- return 0xffe00000;
- }
- else
- {
- throw new InvalidOperationException("Invalid address space width!");
- }
- }
- private KernelResult MapPages(ulong Address, KPageList PageList, MemoryPermission Permission)
- {
- ulong CurrAddr = Address;
- KernelResult Result = KernelResult.Success;
- foreach (KPageNode PageNode in PageList)
- {
- Result = DoMmuOperation(
- CurrAddr,
- PageNode.PagesCount,
- PageNode.Address,
- true,
- Permission,
- MemoryOperation.MapPa);
- if (Result != KernelResult.Success)
- {
- KMemoryInfo Info = FindBlock(CurrAddr).GetInfo();
- ulong PagesCount = (Address - CurrAddr) / PageSize;
- Result = MmuUnmap(Address, PagesCount);
- break;
- }
- CurrAddr += PageNode.PagesCount * PageSize;
- }
- return Result;
- }
- private KernelResult MmuUnmap(ulong Address, ulong PagesCount)
- {
- return DoMmuOperation(
- Address,
- PagesCount,
- 0,
- false,
- MemoryPermission.None,
- MemoryOperation.Unmap);
- }
- private KernelResult MmuChangePermission(ulong Address, ulong PagesCount, MemoryPermission Permission)
- {
- return DoMmuOperation(
- Address,
- PagesCount,
- 0,
- false,
- Permission,
- MemoryOperation.ChangePermRw);
- }
- private KernelResult DoMmuOperation(
- ulong DstVa,
- ulong PagesCount,
- ulong SrcPa,
- bool Map,
- MemoryPermission Permission,
- MemoryOperation Operation)
- {
- if (Map != (Operation == MemoryOperation.MapPa))
- {
- throw new ArgumentException(nameof(Map) + " value is invalid for this operation.");
- }
- KernelResult Result;
- switch (Operation)
- {
- case MemoryOperation.MapPa:
- {
- ulong Size = PagesCount * PageSize;
- CpuMemory.Map((long)DstVa, (long)(SrcPa - DramMemoryMap.DramBase), (long)Size);
- Result = KernelResult.Success;
- break;
- }
- case MemoryOperation.Allocate:
- {
- KMemoryRegionManager Region = GetMemoryRegionManager();
- Result = Region.AllocatePages(PagesCount, AslrDisabled, out KPageList PageList);
- if (Result == KernelResult.Success)
- {
- Result = MmuMapPages(DstVa, PageList);
- }
- break;
- }
- case MemoryOperation.Unmap:
- {
- ulong Size = PagesCount * PageSize;
- CpuMemory.Unmap((long)DstVa, (long)Size);
- Result = KernelResult.Success;
- break;
- }
- case MemoryOperation.ChangePermRw: Result = KernelResult.Success; break;
- case MemoryOperation.ChangePermsAndAttributes: Result = KernelResult.Success; break;
- default: throw new ArgumentException($"Invalid operation \"{Operation}\".");
- }
- return Result;
- }
- private KernelResult DoMmuOperation(
- ulong Address,
- ulong PagesCount,
- KPageList PageList,
- MemoryPermission Permission,
- MemoryOperation Operation)
- {
- if (Operation != MemoryOperation.MapVa)
- {
- throw new ArgumentException($"Invalid memory operation \"{Operation}\" specified.");
- }
- return MmuMapPages(Address, PageList);
- }
- private KMemoryRegionManager GetMemoryRegionManager()
- {
- return System.MemoryRegions[(int)MemRegion];
- }
- private KernelResult MmuMapPages(ulong Address, KPageList PageList)
- {
- foreach (KPageNode PageNode in PageList)
- {
- ulong Size = PageNode.PagesCount * PageSize;
- CpuMemory.Map((long)Address, (long)(PageNode.Address - DramMemoryMap.DramBase), (long)Size);
- Address += Size;
- }
- return KernelResult.Success;
- }
- public KernelResult ConvertVaToPa(ulong Va, out ulong Pa)
- {
- Pa = DramMemoryMap.DramBase + (ulong)CpuMemory.GetPhysicalAddress((long)Va);
- return KernelResult.Success;
- }
- public long GetMmUsedPages()
- {
- lock (Blocks)
- {
- return BitUtils.DivRoundUp(GetMmUsedSize(), PageSize);
- }
- }
- private long GetMmUsedSize()
- {
- return Blocks.Count * KMemoryBlockSize;
- }
- public bool IsInvalidRegion(ulong Address, ulong Size)
- {
- return Address + Size - 1 > GetAddrSpaceBaseAddr() + GetAddrSpaceSize() - 1;
- }
- public bool InsideAddrSpace(ulong Address, ulong Size)
- {
- return AddrSpaceStart <= Address && Address + Size - 1 <= AddrSpaceEnd - 1;
- }
- public bool InsideAliasRegion(ulong Address, ulong Size)
- {
- return Address + Size > AliasRegionStart && AliasRegionEnd > Address;
- }
- public bool InsideHeapRegion(ulong Address, ulong Size)
- {
- return Address + Size > HeapRegionStart && HeapRegionEnd > Address;
- }
- public bool InsideStackRegion(ulong Address, ulong Size)
- {
- return Address + Size > StackRegionStart && StackRegionEnd > Address;
- }
- public bool OutsideAliasRegion(ulong Address, ulong Size)
- {
- return AliasRegionStart > Address || Address + Size - 1 > AliasRegionEnd - 1;
- }
- public bool OutsideAddrSpace(ulong Address, ulong Size)
- {
- return AddrSpaceStart > Address || Address + Size - 1 > AddrSpaceEnd - 1;
- }
- public bool OutsideStackRegion(ulong Address, ulong Size)
- {
- return StackRegionStart > Address || Address + Size - 1 > StackRegionEnd - 1;
- }
- }
- }
|