KMemoryManager.cs 110 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317
  1. using Ryujinx.Common;
  2. using Ryujinx.HLE.HOS.Kernel.Common;
  3. using Ryujinx.HLE.HOS.Kernel.Process;
  4. using Ryujinx.Memory;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Diagnostics;
  8. namespace Ryujinx.HLE.HOS.Kernel.Memory
  9. {
  10. class KMemoryManager
  11. {
  12. private static readonly int[] MappingUnitSizes = new int[]
  13. {
  14. 0x1000,
  15. 0x10000,
  16. 0x200000,
  17. 0x400000,
  18. 0x2000000,
  19. 0x40000000
  20. };
  21. public const int PageSize = 0x1000;
  22. private const int KMemoryBlockSize = 0x40;
  23. // We need 2 blocks for the case where a big block
  24. // needs to be split in 2, plus one block that will be the new one inserted.
  25. private const int MaxBlocksNeededForInsertion = 2;
  26. private readonly LinkedList<KMemoryBlock> _blocks;
  27. private readonly IVirtualMemoryManager _cpuMemory;
  28. private readonly KernelContext _context;
  29. public ulong AddrSpaceStart { get; private set; }
  30. public ulong AddrSpaceEnd { get; private set; }
  31. public ulong CodeRegionStart { get; private set; }
  32. public ulong CodeRegionEnd { get; private set; }
  33. public ulong HeapRegionStart { get; private set; }
  34. public ulong HeapRegionEnd { get; private set; }
  35. private ulong _currentHeapAddr;
  36. public ulong AliasRegionStart { get; private set; }
  37. public ulong AliasRegionEnd { get; private set; }
  38. public ulong StackRegionStart { get; private set; }
  39. public ulong StackRegionEnd { get; private set; }
  40. public ulong TlsIoRegionStart { get; private set; }
  41. public ulong TlsIoRegionEnd { get; private set; }
  42. private ulong _heapCapacity;
  43. public ulong PhysicalMemoryUsage { get; private set; }
  44. private MemoryRegion _memRegion;
  45. private bool _aslrDisabled;
  46. public int AddrSpaceWidth { get; private set; }
  47. private bool _isKernel;
  48. private bool _aslrEnabled;
  49. private KMemoryBlockAllocator _blockAllocator;
  50. private int _contextId;
  51. private MersenneTwister _randomNumberGenerator;
  52. public KMemoryManager(KernelContext context, IVirtualMemoryManager cpuMemory)
  53. {
  54. _context = context;
  55. _cpuMemory = cpuMemory;
  56. _blocks = new LinkedList<KMemoryBlock>();
  57. _isKernel = false;
  58. }
  59. private static readonly int[] AddrSpaceSizes = new int[] { 32, 36, 32, 39 };
  60. public KernelResult InitializeForProcess(
  61. AddressSpaceType addrSpaceType,
  62. bool aslrEnabled,
  63. bool aslrDisabled,
  64. MemoryRegion memRegion,
  65. ulong address,
  66. ulong size,
  67. KMemoryBlockAllocator blockAllocator)
  68. {
  69. if ((uint)addrSpaceType > (uint)AddressSpaceType.Addr39Bits)
  70. {
  71. throw new ArgumentException(nameof(addrSpaceType));
  72. }
  73. _contextId = _context.ContextIdManager.GetId();
  74. ulong addrSpaceBase = 0;
  75. ulong addrSpaceSize = 1UL << AddrSpaceSizes[(int)addrSpaceType];
  76. KernelResult result = CreateUserAddressSpace(
  77. addrSpaceType,
  78. aslrEnabled,
  79. aslrDisabled,
  80. addrSpaceBase,
  81. addrSpaceSize,
  82. memRegion,
  83. address,
  84. size,
  85. blockAllocator);
  86. if (result != KernelResult.Success)
  87. {
  88. _context.ContextIdManager.PutId(_contextId);
  89. }
  90. return result;
  91. }
  92. private class Region
  93. {
  94. public ulong Start;
  95. public ulong End;
  96. public ulong Size;
  97. public ulong AslrOffset;
  98. }
  99. private KernelResult CreateUserAddressSpace(
  100. AddressSpaceType addrSpaceType,
  101. bool aslrEnabled,
  102. bool aslrDisabled,
  103. ulong addrSpaceStart,
  104. ulong addrSpaceEnd,
  105. MemoryRegion memRegion,
  106. ulong address,
  107. ulong size,
  108. KMemoryBlockAllocator blockAllocator)
  109. {
  110. ulong endAddr = address + size;
  111. Region aliasRegion = new Region();
  112. Region heapRegion = new Region();
  113. Region stackRegion = new Region();
  114. Region tlsIoRegion = new Region();
  115. ulong codeRegionSize;
  116. ulong stackAndTlsIoStart;
  117. ulong stackAndTlsIoEnd;
  118. ulong baseAddress;
  119. switch (addrSpaceType)
  120. {
  121. case AddressSpaceType.Addr32Bits:
  122. aliasRegion.Size = 0x40000000;
  123. heapRegion.Size = 0x40000000;
  124. stackRegion.Size = 0;
  125. tlsIoRegion.Size = 0;
  126. CodeRegionStart = 0x200000;
  127. codeRegionSize = 0x3fe00000;
  128. stackAndTlsIoStart = 0x200000;
  129. stackAndTlsIoEnd = 0x40000000;
  130. baseAddress = 0x200000;
  131. AddrSpaceWidth = 32;
  132. break;
  133. case AddressSpaceType.Addr36Bits:
  134. aliasRegion.Size = 0x180000000;
  135. heapRegion.Size = 0x180000000;
  136. stackRegion.Size = 0;
  137. tlsIoRegion.Size = 0;
  138. CodeRegionStart = 0x8000000;
  139. codeRegionSize = 0x78000000;
  140. stackAndTlsIoStart = 0x8000000;
  141. stackAndTlsIoEnd = 0x80000000;
  142. baseAddress = 0x8000000;
  143. AddrSpaceWidth = 36;
  144. break;
  145. case AddressSpaceType.Addr32BitsNoMap:
  146. aliasRegion.Size = 0;
  147. heapRegion.Size = 0x80000000;
  148. stackRegion.Size = 0;
  149. tlsIoRegion.Size = 0;
  150. CodeRegionStart = 0x200000;
  151. codeRegionSize = 0x3fe00000;
  152. stackAndTlsIoStart = 0x200000;
  153. stackAndTlsIoEnd = 0x40000000;
  154. baseAddress = 0x200000;
  155. AddrSpaceWidth = 32;
  156. break;
  157. case AddressSpaceType.Addr39Bits:
  158. aliasRegion.Size = 0x1000000000;
  159. heapRegion.Size = 0x180000000;
  160. stackRegion.Size = 0x80000000;
  161. tlsIoRegion.Size = 0x1000000000;
  162. CodeRegionStart = BitUtils.AlignDown(address, 0x200000);
  163. codeRegionSize = BitUtils.AlignUp (endAddr, 0x200000) - CodeRegionStart;
  164. stackAndTlsIoStart = 0;
  165. stackAndTlsIoEnd = 0;
  166. baseAddress = 0x8000000;
  167. AddrSpaceWidth = 39;
  168. break;
  169. default: throw new ArgumentException(nameof(addrSpaceType));
  170. }
  171. CodeRegionEnd = CodeRegionStart + codeRegionSize;
  172. ulong mapBaseAddress;
  173. ulong mapAvailableSize;
  174. if (CodeRegionStart - baseAddress >= addrSpaceEnd - CodeRegionEnd)
  175. {
  176. // Has more space before the start of the code region.
  177. mapBaseAddress = baseAddress;
  178. mapAvailableSize = CodeRegionStart - baseAddress;
  179. }
  180. else
  181. {
  182. // Has more space after the end of the code region.
  183. mapBaseAddress = CodeRegionEnd;
  184. mapAvailableSize = addrSpaceEnd - CodeRegionEnd;
  185. }
  186. ulong mapTotalSize = aliasRegion.Size + heapRegion.Size + stackRegion.Size + tlsIoRegion.Size;
  187. ulong aslrMaxOffset = mapAvailableSize - mapTotalSize;
  188. _aslrEnabled = aslrEnabled;
  189. AddrSpaceStart = addrSpaceStart;
  190. AddrSpaceEnd = addrSpaceEnd;
  191. _blockAllocator = blockAllocator;
  192. if (mapAvailableSize < mapTotalSize)
  193. {
  194. return KernelResult.OutOfMemory;
  195. }
  196. if (aslrEnabled)
  197. {
  198. aliasRegion.AslrOffset = GetRandomValue(0, aslrMaxOffset >> 21) << 21;
  199. heapRegion.AslrOffset = GetRandomValue(0, aslrMaxOffset >> 21) << 21;
  200. stackRegion.AslrOffset = GetRandomValue(0, aslrMaxOffset >> 21) << 21;
  201. tlsIoRegion.AslrOffset = GetRandomValue(0, aslrMaxOffset >> 21) << 21;
  202. }
  203. // Regions are sorted based on ASLR offset.
  204. // When ASLR is disabled, the order is Map, Heap, NewMap and TlsIo.
  205. aliasRegion.Start = mapBaseAddress + aliasRegion.AslrOffset;
  206. aliasRegion.End = aliasRegion.Start + aliasRegion.Size;
  207. heapRegion.Start = mapBaseAddress + heapRegion.AslrOffset;
  208. heapRegion.End = heapRegion.Start + heapRegion.Size;
  209. stackRegion.Start = mapBaseAddress + stackRegion.AslrOffset;
  210. stackRegion.End = stackRegion.Start + stackRegion.Size;
  211. tlsIoRegion.Start = mapBaseAddress + tlsIoRegion.AslrOffset;
  212. tlsIoRegion.End = tlsIoRegion.Start + tlsIoRegion.Size;
  213. SortRegion(heapRegion, aliasRegion);
  214. if (stackRegion.Size != 0)
  215. {
  216. SortRegion(stackRegion, aliasRegion);
  217. SortRegion(stackRegion, heapRegion);
  218. }
  219. else
  220. {
  221. stackRegion.Start = stackAndTlsIoStart;
  222. stackRegion.End = stackAndTlsIoEnd;
  223. }
  224. if (tlsIoRegion.Size != 0)
  225. {
  226. SortRegion(tlsIoRegion, aliasRegion);
  227. SortRegion(tlsIoRegion, heapRegion);
  228. SortRegion(tlsIoRegion, stackRegion);
  229. }
  230. else
  231. {
  232. tlsIoRegion.Start = stackAndTlsIoStart;
  233. tlsIoRegion.End = stackAndTlsIoEnd;
  234. }
  235. AliasRegionStart = aliasRegion.Start;
  236. AliasRegionEnd = aliasRegion.End;
  237. HeapRegionStart = heapRegion.Start;
  238. HeapRegionEnd = heapRegion.End;
  239. StackRegionStart = stackRegion.Start;
  240. StackRegionEnd = stackRegion.End;
  241. TlsIoRegionStart = tlsIoRegion.Start;
  242. TlsIoRegionEnd = tlsIoRegion.End;
  243. _currentHeapAddr = HeapRegionStart;
  244. _heapCapacity = 0;
  245. PhysicalMemoryUsage = 0;
  246. _memRegion = memRegion;
  247. _aslrDisabled = aslrDisabled;
  248. return InitializeBlocks(addrSpaceStart, addrSpaceEnd);
  249. }
  250. private ulong GetRandomValue(ulong min, ulong max)
  251. {
  252. return (ulong)GetRandomValue((long)min, (long)max);
  253. }
  254. private long GetRandomValue(long min, long max)
  255. {
  256. if (_randomNumberGenerator == null)
  257. {
  258. _randomNumberGenerator = new MersenneTwister(0);
  259. }
  260. return _randomNumberGenerator.GenRandomNumber(min, max);
  261. }
  262. private static void SortRegion(Region lhs, Region rhs)
  263. {
  264. if (lhs.AslrOffset < rhs.AslrOffset)
  265. {
  266. rhs.Start += lhs.Size;
  267. rhs.End += lhs.Size;
  268. }
  269. else
  270. {
  271. lhs.Start += rhs.Size;
  272. lhs.End += rhs.Size;
  273. }
  274. }
  275. private KernelResult InitializeBlocks(ulong addrSpaceStart, ulong addrSpaceEnd)
  276. {
  277. // First insertion will always need only a single block,
  278. // because there's nothing else to split.
  279. if (!_blockAllocator.CanAllocate(1))
  280. {
  281. return KernelResult.OutOfResource;
  282. }
  283. ulong addrSpacePagesCount = (addrSpaceEnd - addrSpaceStart) / PageSize;
  284. _blocks.AddFirst(new KMemoryBlock(
  285. addrSpaceStart,
  286. addrSpacePagesCount,
  287. MemoryState.Unmapped,
  288. KMemoryPermission.None,
  289. MemoryAttribute.None));
  290. return KernelResult.Success;
  291. }
  292. public KernelResult MapPages(
  293. ulong address,
  294. KPageList pageList,
  295. MemoryState state,
  296. KMemoryPermission permission)
  297. {
  298. ulong pagesCount = pageList.GetPagesCount();
  299. ulong size = pagesCount * PageSize;
  300. if (!CanContain(address, size, state))
  301. {
  302. return KernelResult.InvalidMemState;
  303. }
  304. lock (_blocks)
  305. {
  306. if (!IsUnmapped(address, pagesCount * PageSize))
  307. {
  308. return KernelResult.InvalidMemState;
  309. }
  310. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
  311. {
  312. return KernelResult.OutOfResource;
  313. }
  314. KernelResult result = MapPages(address, pageList, permission);
  315. if (result == KernelResult.Success)
  316. {
  317. InsertBlock(address, pagesCount, state, permission);
  318. }
  319. return result;
  320. }
  321. }
  322. public KernelResult UnmapPages(ulong address, KPageList pageList, MemoryState stateExpected)
  323. {
  324. ulong pagesCount = pageList.GetPagesCount();
  325. ulong size = pagesCount * PageSize;
  326. ulong endAddr = address + size;
  327. ulong addrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize;
  328. if (AddrSpaceStart > address)
  329. {
  330. return KernelResult.InvalidMemState;
  331. }
  332. if (addrSpacePagesCount < pagesCount)
  333. {
  334. return KernelResult.InvalidMemState;
  335. }
  336. if (endAddr - 1 > AddrSpaceEnd - 1)
  337. {
  338. return KernelResult.InvalidMemState;
  339. }
  340. lock (_blocks)
  341. {
  342. KPageList currentPageList = new KPageList();
  343. AddVaRangeToPageList(currentPageList, address, pagesCount);
  344. if (!currentPageList.IsEqual(pageList))
  345. {
  346. return KernelResult.InvalidMemRange;
  347. }
  348. if (CheckRange(
  349. address,
  350. size,
  351. MemoryState.Mask,
  352. stateExpected,
  353. KMemoryPermission.None,
  354. KMemoryPermission.None,
  355. MemoryAttribute.Mask,
  356. MemoryAttribute.None,
  357. MemoryAttribute.IpcAndDeviceMapped,
  358. out MemoryState state,
  359. out _,
  360. out _))
  361. {
  362. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
  363. {
  364. return KernelResult.OutOfResource;
  365. }
  366. KernelResult result = MmuUnmap(address, pagesCount);
  367. if (result == KernelResult.Success)
  368. {
  369. InsertBlock(address, pagesCount, MemoryState.Unmapped);
  370. }
  371. return result;
  372. }
  373. else
  374. {
  375. return KernelResult.InvalidMemState;
  376. }
  377. }
  378. }
  379. public KernelResult MapNormalMemory(long address, long size, KMemoryPermission permission)
  380. {
  381. // TODO.
  382. return KernelResult.Success;
  383. }
  384. public KernelResult MapIoMemory(long address, long size, KMemoryPermission permission)
  385. {
  386. // TODO.
  387. return KernelResult.Success;
  388. }
  389. public KernelResult AllocateOrMapPa(
  390. ulong neededPagesCount,
  391. int alignment,
  392. ulong srcPa,
  393. bool map,
  394. ulong regionStart,
  395. ulong regionPagesCount,
  396. MemoryState state,
  397. KMemoryPermission permission,
  398. out ulong address)
  399. {
  400. address = 0;
  401. ulong regionSize = regionPagesCount * PageSize;
  402. ulong regionEndAddr = regionStart + regionSize;
  403. if (!CanContain(regionStart, regionSize, state))
  404. {
  405. return KernelResult.InvalidMemState;
  406. }
  407. if (regionPagesCount <= neededPagesCount)
  408. {
  409. return KernelResult.OutOfMemory;
  410. }
  411. lock (_blocks)
  412. {
  413. address = AllocateVa(regionStart, regionPagesCount, neededPagesCount, alignment);
  414. if (address == 0)
  415. {
  416. return KernelResult.OutOfMemory;
  417. }
  418. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
  419. {
  420. return KernelResult.OutOfResource;
  421. }
  422. MemoryOperation operation = map
  423. ? MemoryOperation.MapPa
  424. : MemoryOperation.Allocate;
  425. KernelResult result = DoMmuOperation(
  426. address,
  427. neededPagesCount,
  428. srcPa,
  429. map,
  430. permission,
  431. operation);
  432. if (result != KernelResult.Success)
  433. {
  434. return result;
  435. }
  436. InsertBlock(address, neededPagesCount, state, permission);
  437. }
  438. return KernelResult.Success;
  439. }
  440. public KernelResult MapNewProcessCode(
  441. ulong address,
  442. ulong pagesCount,
  443. MemoryState state,
  444. KMemoryPermission permission)
  445. {
  446. ulong size = pagesCount * PageSize;
  447. if (!CanContain(address, size, state))
  448. {
  449. return KernelResult.InvalidMemState;
  450. }
  451. lock (_blocks)
  452. {
  453. if (!IsUnmapped(address, size))
  454. {
  455. return KernelResult.InvalidMemState;
  456. }
  457. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
  458. {
  459. return KernelResult.OutOfResource;
  460. }
  461. KernelResult result = DoMmuOperation(
  462. address,
  463. pagesCount,
  464. 0,
  465. false,
  466. permission,
  467. MemoryOperation.Allocate);
  468. if (result == KernelResult.Success)
  469. {
  470. InsertBlock(address, pagesCount, state, permission);
  471. }
  472. return result;
  473. }
  474. }
  475. public KernelResult MapProcessCodeMemory(ulong dst, ulong src, ulong size)
  476. {
  477. ulong pagesCount = size / PageSize;
  478. lock (_blocks)
  479. {
  480. bool success = CheckRange(
  481. src,
  482. size,
  483. MemoryState.Mask,
  484. MemoryState.Heap,
  485. KMemoryPermission.Mask,
  486. KMemoryPermission.ReadAndWrite,
  487. MemoryAttribute.Mask,
  488. MemoryAttribute.None,
  489. MemoryAttribute.IpcAndDeviceMapped,
  490. out MemoryState state,
  491. out KMemoryPermission permission,
  492. out _);
  493. success &= IsUnmapped(dst, size);
  494. if (success)
  495. {
  496. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
  497. {
  498. return KernelResult.OutOfResource;
  499. }
  500. KPageList pageList = new KPageList();
  501. AddVaRangeToPageList(pageList, src, pagesCount);
  502. KernelResult result = MmuChangePermission(src, pagesCount, KMemoryPermission.None);
  503. if (result != KernelResult.Success)
  504. {
  505. return result;
  506. }
  507. result = MapPages(dst, pageList, KMemoryPermission.None);
  508. if (result != KernelResult.Success)
  509. {
  510. MmuChangePermission(src, pagesCount, permission);
  511. return result;
  512. }
  513. InsertBlock(src, pagesCount, state, KMemoryPermission.None, MemoryAttribute.Borrowed);
  514. InsertBlock(dst, pagesCount, MemoryState.ModCodeStatic);
  515. return KernelResult.Success;
  516. }
  517. else
  518. {
  519. return KernelResult.InvalidMemState;
  520. }
  521. }
  522. }
  523. public KernelResult UnmapProcessCodeMemory(ulong dst, ulong src, ulong size)
  524. {
  525. ulong pagesCount = size / PageSize;
  526. lock (_blocks)
  527. {
  528. bool success = CheckRange(
  529. src,
  530. size,
  531. MemoryState.Mask,
  532. MemoryState.Heap,
  533. KMemoryPermission.None,
  534. KMemoryPermission.None,
  535. MemoryAttribute.Mask,
  536. MemoryAttribute.Borrowed,
  537. MemoryAttribute.IpcAndDeviceMapped,
  538. out _,
  539. out _,
  540. out _);
  541. success &= CheckRange(
  542. dst,
  543. PageSize,
  544. MemoryState.UnmapProcessCodeMemoryAllowed,
  545. MemoryState.UnmapProcessCodeMemoryAllowed,
  546. KMemoryPermission.None,
  547. KMemoryPermission.None,
  548. MemoryAttribute.Mask,
  549. MemoryAttribute.None,
  550. MemoryAttribute.IpcAndDeviceMapped,
  551. out MemoryState state,
  552. out _,
  553. out _);
  554. success &= CheckRange(
  555. dst,
  556. size,
  557. MemoryState.Mask,
  558. state,
  559. KMemoryPermission.None,
  560. KMemoryPermission.None,
  561. MemoryAttribute.Mask,
  562. MemoryAttribute.None);
  563. if (success)
  564. {
  565. KernelResult result = MmuUnmap(dst, pagesCount);
  566. if (result != KernelResult.Success)
  567. {
  568. return result;
  569. }
  570. // TODO: Missing some checks here.
  571. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
  572. {
  573. return KernelResult.OutOfResource;
  574. }
  575. InsertBlock(dst, pagesCount, MemoryState.Unmapped);
  576. InsertBlock(src, pagesCount, MemoryState.Heap, KMemoryPermission.ReadAndWrite);
  577. return KernelResult.Success;
  578. }
  579. else
  580. {
  581. return KernelResult.InvalidMemState;
  582. }
  583. }
  584. }
  585. public KernelResult SetHeapSize(ulong size, out ulong address)
  586. {
  587. address = 0;
  588. if (size > HeapRegionEnd - HeapRegionStart)
  589. {
  590. return KernelResult.OutOfMemory;
  591. }
  592. KProcess currentProcess = KernelStatic.GetCurrentProcess();
  593. lock (_blocks)
  594. {
  595. ulong currentHeapSize = GetHeapSize();
  596. if (currentHeapSize <= size)
  597. {
  598. // Expand.
  599. ulong sizeDelta = size - currentHeapSize;
  600. if (currentProcess.ResourceLimit != null && sizeDelta != 0 &&
  601. !currentProcess.ResourceLimit.Reserve(LimitableResource.Memory, sizeDelta))
  602. {
  603. return KernelResult.ResLimitExceeded;
  604. }
  605. ulong pagesCount = sizeDelta / PageSize;
  606. KMemoryRegionManager region = GetMemoryRegionManager();
  607. KernelResult result = region.AllocatePages(pagesCount, _aslrDisabled, out KPageList pageList);
  608. void CleanUpForError()
  609. {
  610. if (pageList != null)
  611. {
  612. region.FreePages(pageList);
  613. }
  614. if (currentProcess.ResourceLimit != null && sizeDelta != 0)
  615. {
  616. currentProcess.ResourceLimit.Release(LimitableResource.Memory, sizeDelta);
  617. }
  618. }
  619. if (result != KernelResult.Success)
  620. {
  621. CleanUpForError();
  622. return result;
  623. }
  624. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
  625. {
  626. CleanUpForError();
  627. return KernelResult.OutOfResource;
  628. }
  629. if (!IsUnmapped(_currentHeapAddr, sizeDelta))
  630. {
  631. CleanUpForError();
  632. return KernelResult.InvalidMemState;
  633. }
  634. result = DoMmuOperation(
  635. _currentHeapAddr,
  636. pagesCount,
  637. pageList,
  638. KMemoryPermission.ReadAndWrite,
  639. MemoryOperation.MapVa);
  640. if (result != KernelResult.Success)
  641. {
  642. CleanUpForError();
  643. return result;
  644. }
  645. InsertBlock(_currentHeapAddr, pagesCount, MemoryState.Heap, KMemoryPermission.ReadAndWrite);
  646. }
  647. else
  648. {
  649. // Shrink.
  650. ulong freeAddr = HeapRegionStart + size;
  651. ulong sizeDelta = currentHeapSize - size;
  652. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
  653. {
  654. return KernelResult.OutOfResource;
  655. }
  656. if (!CheckRange(
  657. freeAddr,
  658. sizeDelta,
  659. MemoryState.Mask,
  660. MemoryState.Heap,
  661. KMemoryPermission.Mask,
  662. KMemoryPermission.ReadAndWrite,
  663. MemoryAttribute.Mask,
  664. MemoryAttribute.None,
  665. MemoryAttribute.IpcAndDeviceMapped,
  666. out _,
  667. out _,
  668. out _))
  669. {
  670. return KernelResult.InvalidMemState;
  671. }
  672. ulong pagesCount = sizeDelta / PageSize;
  673. KernelResult result = MmuUnmap(freeAddr, pagesCount);
  674. if (result != KernelResult.Success)
  675. {
  676. return result;
  677. }
  678. currentProcess.ResourceLimit?.Release(LimitableResource.Memory, sizeDelta);
  679. InsertBlock(freeAddr, pagesCount, MemoryState.Unmapped);
  680. }
  681. _currentHeapAddr = HeapRegionStart + size;
  682. }
  683. address = HeapRegionStart;
  684. return KernelResult.Success;
  685. }
  686. public ulong GetTotalHeapSize()
  687. {
  688. lock (_blocks)
  689. {
  690. return GetHeapSize() + PhysicalMemoryUsage;
  691. }
  692. }
  693. private ulong GetHeapSize()
  694. {
  695. return _currentHeapAddr - HeapRegionStart;
  696. }
  697. public KernelResult SetHeapCapacity(ulong capacity)
  698. {
  699. lock (_blocks)
  700. {
  701. _heapCapacity = capacity;
  702. }
  703. return KernelResult.Success;
  704. }
  705. public KernelResult SetMemoryAttribute(
  706. ulong address,
  707. ulong size,
  708. MemoryAttribute attributeMask,
  709. MemoryAttribute attributeValue)
  710. {
  711. lock (_blocks)
  712. {
  713. if (CheckRange(
  714. address,
  715. size,
  716. MemoryState.AttributeChangeAllowed,
  717. MemoryState.AttributeChangeAllowed,
  718. KMemoryPermission.None,
  719. KMemoryPermission.None,
  720. MemoryAttribute.BorrowedAndIpcMapped,
  721. MemoryAttribute.None,
  722. MemoryAttribute.DeviceMappedAndUncached,
  723. out MemoryState state,
  724. out KMemoryPermission permission,
  725. out MemoryAttribute attribute))
  726. {
  727. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
  728. {
  729. return KernelResult.OutOfResource;
  730. }
  731. ulong pagesCount = size / PageSize;
  732. attribute &= ~attributeMask;
  733. attribute |= attributeMask & attributeValue;
  734. InsertBlock(address, pagesCount, state, permission, attribute);
  735. return KernelResult.Success;
  736. }
  737. else
  738. {
  739. return KernelResult.InvalidMemState;
  740. }
  741. }
  742. }
  743. public KMemoryInfo QueryMemory(ulong address)
  744. {
  745. if (address >= AddrSpaceStart &&
  746. address < AddrSpaceEnd)
  747. {
  748. lock (_blocks)
  749. {
  750. return FindBlock(address).GetInfo();
  751. }
  752. }
  753. else
  754. {
  755. return new KMemoryInfo(
  756. AddrSpaceEnd,
  757. ~AddrSpaceEnd + 1,
  758. MemoryState.Reserved,
  759. KMemoryPermission.None,
  760. MemoryAttribute.None,
  761. KMemoryPermission.None,
  762. 0,
  763. 0);
  764. }
  765. }
  766. public KernelResult Map(ulong dst, ulong src, ulong size)
  767. {
  768. bool success;
  769. lock (_blocks)
  770. {
  771. success = CheckRange(
  772. src,
  773. size,
  774. MemoryState.MapAllowed,
  775. MemoryState.MapAllowed,
  776. KMemoryPermission.Mask,
  777. KMemoryPermission.ReadAndWrite,
  778. MemoryAttribute.Mask,
  779. MemoryAttribute.None,
  780. MemoryAttribute.IpcAndDeviceMapped,
  781. out MemoryState srcState,
  782. out _,
  783. out _);
  784. success &= IsUnmapped(dst, size);
  785. if (success)
  786. {
  787. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
  788. {
  789. return KernelResult.OutOfResource;
  790. }
  791. ulong pagesCount = size / PageSize;
  792. KPageList pageList = new KPageList();
  793. AddVaRangeToPageList(pageList, src, pagesCount);
  794. KernelResult result = MmuChangePermission(src, pagesCount, KMemoryPermission.None);
  795. if (result != KernelResult.Success)
  796. {
  797. return result;
  798. }
  799. result = MapPages(dst, pageList, KMemoryPermission.ReadAndWrite);
  800. if (result != KernelResult.Success)
  801. {
  802. if (MmuChangePermission(src, pagesCount, KMemoryPermission.ReadAndWrite) != KernelResult.Success)
  803. {
  804. throw new InvalidOperationException("Unexpected failure reverting memory permission.");
  805. }
  806. return result;
  807. }
  808. InsertBlock(src, pagesCount, srcState, KMemoryPermission.None, MemoryAttribute.Borrowed);
  809. InsertBlock(dst, pagesCount, MemoryState.Stack, KMemoryPermission.ReadAndWrite);
  810. return KernelResult.Success;
  811. }
  812. else
  813. {
  814. return KernelResult.InvalidMemState;
  815. }
  816. }
  817. }
  818. public KernelResult UnmapForKernel(ulong address, ulong pagesCount, MemoryState stateExpected)
  819. {
  820. ulong size = pagesCount * PageSize;
  821. lock (_blocks)
  822. {
  823. if (CheckRange(
  824. address,
  825. size,
  826. MemoryState.Mask,
  827. stateExpected,
  828. KMemoryPermission.None,
  829. KMemoryPermission.None,
  830. MemoryAttribute.Mask,
  831. MemoryAttribute.None,
  832. MemoryAttribute.IpcAndDeviceMapped,
  833. out _,
  834. out _,
  835. out _))
  836. {
  837. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
  838. {
  839. return KernelResult.OutOfResource;
  840. }
  841. KernelResult result = MmuUnmap(address, pagesCount);
  842. if (result == KernelResult.Success)
  843. {
  844. InsertBlock(address, pagesCount, MemoryState.Unmapped);
  845. }
  846. return KernelResult.Success;
  847. }
  848. else
  849. {
  850. return KernelResult.InvalidMemState;
  851. }
  852. }
  853. }
  854. public KernelResult Unmap(ulong dst, ulong src, ulong size)
  855. {
  856. bool success;
  857. lock (_blocks)
  858. {
  859. success = CheckRange(
  860. src,
  861. size,
  862. MemoryState.MapAllowed,
  863. MemoryState.MapAllowed,
  864. KMemoryPermission.Mask,
  865. KMemoryPermission.None,
  866. MemoryAttribute.Mask,
  867. MemoryAttribute.Borrowed,
  868. MemoryAttribute.IpcAndDeviceMapped,
  869. out MemoryState srcState,
  870. out _,
  871. out _);
  872. success &= CheckRange(
  873. dst,
  874. size,
  875. MemoryState.Mask,
  876. MemoryState.Stack,
  877. KMemoryPermission.None,
  878. KMemoryPermission.None,
  879. MemoryAttribute.Mask,
  880. MemoryAttribute.None,
  881. MemoryAttribute.IpcAndDeviceMapped,
  882. out _,
  883. out KMemoryPermission dstPermission,
  884. out _);
  885. if (success)
  886. {
  887. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
  888. {
  889. return KernelResult.OutOfResource;
  890. }
  891. ulong pagesCount = size / PageSize;
  892. KPageList srcPageList = new KPageList();
  893. KPageList dstPageList = new KPageList();
  894. AddVaRangeToPageList(srcPageList, src, pagesCount);
  895. AddVaRangeToPageList(dstPageList, dst, pagesCount);
  896. if (!dstPageList.IsEqual(srcPageList))
  897. {
  898. return KernelResult.InvalidMemRange;
  899. }
  900. KernelResult result = MmuUnmap(dst, pagesCount);
  901. if (result != KernelResult.Success)
  902. {
  903. return result;
  904. }
  905. result = MmuChangePermission(src, pagesCount, KMemoryPermission.ReadAndWrite);
  906. if (result != KernelResult.Success)
  907. {
  908. MapPages(dst, dstPageList, dstPermission);
  909. return result;
  910. }
  911. InsertBlock(src, pagesCount, srcState, KMemoryPermission.ReadAndWrite);
  912. InsertBlock(dst, pagesCount, MemoryState.Unmapped);
  913. return KernelResult.Success;
  914. }
  915. else
  916. {
  917. return KernelResult.InvalidMemState;
  918. }
  919. }
  920. }
  921. public KernelResult SetProcessMemoryPermission(ulong address, ulong size, KMemoryPermission permission)
  922. {
  923. lock (_blocks)
  924. {
  925. if (CheckRange(
  926. address,
  927. size,
  928. MemoryState.ProcessPermissionChangeAllowed,
  929. MemoryState.ProcessPermissionChangeAllowed,
  930. KMemoryPermission.None,
  931. KMemoryPermission.None,
  932. MemoryAttribute.Mask,
  933. MemoryAttribute.None,
  934. MemoryAttribute.IpcAndDeviceMapped,
  935. out MemoryState oldState,
  936. out KMemoryPermission oldPermission,
  937. out _))
  938. {
  939. MemoryState newState = oldState;
  940. // If writing into the code region is allowed, then we need
  941. // to change it to mutable.
  942. if ((permission & KMemoryPermission.Write) != 0)
  943. {
  944. if (oldState == MemoryState.CodeStatic)
  945. {
  946. newState = MemoryState.CodeMutable;
  947. }
  948. else if (oldState == MemoryState.ModCodeStatic)
  949. {
  950. newState = MemoryState.ModCodeMutable;
  951. }
  952. else
  953. {
  954. throw new InvalidOperationException($"Memory state \"{oldState}\" not valid for this operation.");
  955. }
  956. }
  957. if (newState != oldState || permission != oldPermission)
  958. {
  959. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
  960. {
  961. return KernelResult.OutOfResource;
  962. }
  963. ulong pagesCount = size / PageSize;
  964. MemoryOperation operation = (permission & KMemoryPermission.Execute) != 0
  965. ? MemoryOperation.ChangePermsAndAttributes
  966. : MemoryOperation.ChangePermRw;
  967. KernelResult result = DoMmuOperation(address, pagesCount, 0, false, permission, operation);
  968. if (result != KernelResult.Success)
  969. {
  970. return result;
  971. }
  972. InsertBlock(address, pagesCount, newState, permission);
  973. }
  974. return KernelResult.Success;
  975. }
  976. else
  977. {
  978. return KernelResult.InvalidMemState;
  979. }
  980. }
  981. }
  982. public KernelResult MapPhysicalMemory(ulong address, ulong size)
  983. {
  984. ulong endAddr = address + size;
  985. lock (_blocks)
  986. {
  987. ulong mappedSize = 0;
  988. foreach (KMemoryInfo info in IterateOverRange(address, endAddr))
  989. {
  990. if (info.State != MemoryState.Unmapped)
  991. {
  992. mappedSize += GetSizeInRange(info, address, endAddr);
  993. }
  994. }
  995. if (mappedSize == size)
  996. {
  997. return KernelResult.Success;
  998. }
  999. ulong remainingSize = size - mappedSize;
  1000. ulong remainingPages = remainingSize / PageSize;
  1001. KProcess currentProcess = KernelStatic.GetCurrentProcess();
  1002. if (currentProcess.ResourceLimit != null &&
  1003. !currentProcess.ResourceLimit.Reserve(LimitableResource.Memory, remainingSize))
  1004. {
  1005. return KernelResult.ResLimitExceeded;
  1006. }
  1007. KMemoryRegionManager region = GetMemoryRegionManager();
  1008. KernelResult result = region.AllocatePages(remainingPages, _aslrDisabled, out KPageList pageList);
  1009. void CleanUpForError()
  1010. {
  1011. if (pageList != null)
  1012. {
  1013. region.FreePages(pageList);
  1014. }
  1015. currentProcess.ResourceLimit?.Release(LimitableResource.Memory, remainingSize);
  1016. }
  1017. if (result != KernelResult.Success)
  1018. {
  1019. CleanUpForError();
  1020. return result;
  1021. }
  1022. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
  1023. {
  1024. CleanUpForError();
  1025. return KernelResult.OutOfResource;
  1026. }
  1027. MapPhysicalMemory(pageList, address, endAddr);
  1028. PhysicalMemoryUsage += remainingSize;
  1029. ulong pagesCount = size / PageSize;
  1030. InsertBlock(
  1031. address,
  1032. pagesCount,
  1033. MemoryState.Unmapped,
  1034. KMemoryPermission.None,
  1035. MemoryAttribute.None,
  1036. MemoryState.Heap,
  1037. KMemoryPermission.ReadAndWrite,
  1038. MemoryAttribute.None);
  1039. }
  1040. return KernelResult.Success;
  1041. }
  1042. public KernelResult UnmapPhysicalMemory(ulong address, ulong size)
  1043. {
  1044. ulong endAddr = address + size;
  1045. lock (_blocks)
  1046. {
  1047. // Scan, ensure that the region can be unmapped (all blocks are heap or
  1048. // already unmapped), fill pages list for freeing memory.
  1049. ulong heapMappedSize = 0;
  1050. KPageList pageList = new KPageList();
  1051. foreach (KMemoryInfo info in IterateOverRange(address, endAddr))
  1052. {
  1053. if (info.State == MemoryState.Heap)
  1054. {
  1055. if (info.Attribute != MemoryAttribute.None)
  1056. {
  1057. return KernelResult.InvalidMemState;
  1058. }
  1059. ulong blockSize = GetSizeInRange(info, address, endAddr);
  1060. ulong blockAddress = GetAddrInRange(info, address);
  1061. AddVaRangeToPageList(pageList, blockAddress, blockSize / PageSize);
  1062. heapMappedSize += blockSize;
  1063. }
  1064. else if (info.State != MemoryState.Unmapped)
  1065. {
  1066. return KernelResult.InvalidMemState;
  1067. }
  1068. }
  1069. if (heapMappedSize == 0)
  1070. {
  1071. return KernelResult.Success;
  1072. }
  1073. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
  1074. {
  1075. return KernelResult.OutOfResource;
  1076. }
  1077. // Try to unmap all the heap mapped memory inside range.
  1078. KernelResult result = KernelResult.Success;
  1079. foreach (KMemoryInfo info in IterateOverRange(address, endAddr))
  1080. {
  1081. if (info.State == MemoryState.Heap)
  1082. {
  1083. ulong blockSize = GetSizeInRange(info, address, endAddr);
  1084. ulong blockAddress = GetAddrInRange(info, address);
  1085. ulong blockPagesCount = blockSize / PageSize;
  1086. result = MmuUnmap(blockAddress, blockPagesCount);
  1087. if (result != KernelResult.Success)
  1088. {
  1089. // If we failed to unmap, we need to remap everything back again.
  1090. MapPhysicalMemory(pageList, address, blockAddress + blockSize);
  1091. break;
  1092. }
  1093. }
  1094. }
  1095. if (result == KernelResult.Success)
  1096. {
  1097. GetMemoryRegionManager().FreePages(pageList);
  1098. PhysicalMemoryUsage -= heapMappedSize;
  1099. KProcess currentProcess = KernelStatic.GetCurrentProcess();
  1100. currentProcess.ResourceLimit?.Release(LimitableResource.Memory, heapMappedSize);
  1101. ulong pagesCount = size / PageSize;
  1102. InsertBlock(address, pagesCount, MemoryState.Unmapped);
  1103. }
  1104. return result;
  1105. }
  1106. }
  1107. private void MapPhysicalMemory(KPageList pageList, ulong address, ulong endAddr)
  1108. {
  1109. LinkedListNode<KPageNode> pageListNode = pageList.Nodes.First;
  1110. KPageNode pageNode = pageListNode.Value;
  1111. ulong srcPa = pageNode.Address;
  1112. ulong srcPaPages = pageNode.PagesCount;
  1113. foreach (KMemoryInfo info in IterateOverRange(address, endAddr))
  1114. {
  1115. if (info.State == MemoryState.Unmapped)
  1116. {
  1117. ulong blockSize = GetSizeInRange(info, address, endAddr);
  1118. ulong dstVaPages = blockSize / PageSize;
  1119. ulong dstVa = GetAddrInRange(info, address);
  1120. while (dstVaPages > 0)
  1121. {
  1122. if (srcPaPages == 0)
  1123. {
  1124. pageListNode = pageListNode.Next;
  1125. pageNode = pageListNode.Value;
  1126. srcPa = pageNode.Address;
  1127. srcPaPages = pageNode.PagesCount;
  1128. }
  1129. ulong pagesCount = srcPaPages;
  1130. if (pagesCount > dstVaPages)
  1131. {
  1132. pagesCount = dstVaPages;
  1133. }
  1134. DoMmuOperation(
  1135. dstVa,
  1136. pagesCount,
  1137. srcPa,
  1138. true,
  1139. KMemoryPermission.ReadAndWrite,
  1140. MemoryOperation.MapPa);
  1141. dstVa += pagesCount * PageSize;
  1142. srcPa += pagesCount * PageSize;
  1143. srcPaPages -= pagesCount;
  1144. dstVaPages -= pagesCount;
  1145. }
  1146. }
  1147. }
  1148. }
  1149. public KernelResult CopyDataToCurrentProcess(
  1150. ulong dst,
  1151. ulong size,
  1152. ulong src,
  1153. MemoryState stateMask,
  1154. MemoryState stateExpected,
  1155. KMemoryPermission permission,
  1156. MemoryAttribute attributeMask,
  1157. MemoryAttribute attributeExpected)
  1158. {
  1159. // Client -> server.
  1160. return CopyDataFromOrToCurrentProcess(
  1161. size,
  1162. src,
  1163. dst,
  1164. stateMask,
  1165. stateExpected,
  1166. permission,
  1167. attributeMask,
  1168. attributeExpected,
  1169. toServer: true);
  1170. }
  1171. public KernelResult CopyDataFromCurrentProcess(
  1172. ulong dst,
  1173. ulong size,
  1174. MemoryState stateMask,
  1175. MemoryState stateExpected,
  1176. KMemoryPermission permission,
  1177. MemoryAttribute attributeMask,
  1178. MemoryAttribute attributeExpected,
  1179. ulong src)
  1180. {
  1181. // Server -> client.
  1182. return CopyDataFromOrToCurrentProcess(
  1183. size,
  1184. dst,
  1185. src,
  1186. stateMask,
  1187. stateExpected,
  1188. permission,
  1189. attributeMask,
  1190. attributeExpected,
  1191. toServer: false);
  1192. }
  1193. private KernelResult CopyDataFromOrToCurrentProcess(
  1194. ulong size,
  1195. ulong clientAddress,
  1196. ulong serverAddress,
  1197. MemoryState stateMask,
  1198. MemoryState stateExpected,
  1199. KMemoryPermission permission,
  1200. MemoryAttribute attributeMask,
  1201. MemoryAttribute attributeExpected,
  1202. bool toServer)
  1203. {
  1204. if (AddrSpaceStart > clientAddress)
  1205. {
  1206. return KernelResult.InvalidMemState;
  1207. }
  1208. ulong srcEndAddr = clientAddress + size;
  1209. if (srcEndAddr <= clientAddress || srcEndAddr - 1 > AddrSpaceEnd - 1)
  1210. {
  1211. return KernelResult.InvalidMemState;
  1212. }
  1213. lock (_blocks)
  1214. {
  1215. if (CheckRange(
  1216. clientAddress,
  1217. size,
  1218. stateMask,
  1219. stateExpected,
  1220. permission,
  1221. permission,
  1222. attributeMask | MemoryAttribute.Uncached,
  1223. attributeExpected))
  1224. {
  1225. KProcess currentProcess = KernelStatic.GetCurrentProcess();
  1226. serverAddress = currentProcess.MemoryManager.GetDramAddressFromVa(serverAddress);
  1227. if (toServer)
  1228. {
  1229. _context.Memory.Copy(serverAddress, GetDramAddressFromVa(clientAddress), size);
  1230. }
  1231. else
  1232. {
  1233. _context.Memory.Copy(GetDramAddressFromVa(clientAddress), serverAddress, size);
  1234. }
  1235. return KernelResult.Success;
  1236. }
  1237. else
  1238. {
  1239. return KernelResult.InvalidMemState;
  1240. }
  1241. }
  1242. }
  1243. public KernelResult MapBufferFromClientProcess(
  1244. ulong size,
  1245. ulong src,
  1246. KMemoryManager sourceMemMgr,
  1247. KMemoryPermission permission,
  1248. MemoryState state,
  1249. bool copyData,
  1250. out ulong dst)
  1251. {
  1252. dst = 0;
  1253. KernelResult result = sourceMemMgr.GetPagesForMappingIntoAnotherProcess(
  1254. src,
  1255. size,
  1256. permission,
  1257. state,
  1258. copyData,
  1259. _aslrDisabled,
  1260. _memRegion,
  1261. out KPageList pageList);
  1262. if (result != KernelResult.Success)
  1263. {
  1264. return result;
  1265. }
  1266. result = MapPagesFromAnotherProcess(size, src, permission, state, pageList, out ulong va);
  1267. if (result != KernelResult.Success)
  1268. {
  1269. sourceMemMgr.UnmapIpcRestorePermission(src, size, state);
  1270. }
  1271. else
  1272. {
  1273. dst = va;
  1274. }
  1275. return result;
  1276. }
  1277. private KernelResult GetPagesForMappingIntoAnotherProcess(
  1278. ulong address,
  1279. ulong size,
  1280. KMemoryPermission permission,
  1281. MemoryState state,
  1282. bool copyData,
  1283. bool aslrDisabled,
  1284. MemoryRegion region,
  1285. out KPageList pageList)
  1286. {
  1287. pageList = null;
  1288. if (AddrSpaceStart > address)
  1289. {
  1290. return KernelResult.InvalidMemState;
  1291. }
  1292. ulong endAddr = address + size;
  1293. if (endAddr <= address || endAddr - 1 > AddrSpaceEnd - 1)
  1294. {
  1295. return KernelResult.InvalidMemState;
  1296. }
  1297. MemoryState stateMask;
  1298. switch (state)
  1299. {
  1300. case MemoryState.IpcBuffer0: stateMask = MemoryState.IpcSendAllowedType0; break;
  1301. case MemoryState.IpcBuffer1: stateMask = MemoryState.IpcSendAllowedType1; break;
  1302. case MemoryState.IpcBuffer3: stateMask = MemoryState.IpcSendAllowedType3; break;
  1303. default: return KernelResult.InvalidCombination;
  1304. }
  1305. KMemoryPermission permissionMask = permission == KMemoryPermission.ReadAndWrite
  1306. ? KMemoryPermission.None
  1307. : KMemoryPermission.Read;
  1308. MemoryAttribute attributeMask = MemoryAttribute.Borrowed | MemoryAttribute.Uncached;
  1309. if (state == MemoryState.IpcBuffer0)
  1310. {
  1311. attributeMask |= MemoryAttribute.DeviceMapped;
  1312. }
  1313. ulong addressRounded = BitUtils.AlignUp (address, PageSize);
  1314. ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
  1315. ulong endAddrRounded = BitUtils.AlignUp (endAddr, PageSize);
  1316. ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
  1317. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
  1318. {
  1319. return KernelResult.OutOfResource;
  1320. }
  1321. ulong visitedSize = 0;
  1322. void CleanUpForError()
  1323. {
  1324. if (visitedSize == 0)
  1325. {
  1326. return;
  1327. }
  1328. ulong endAddrVisited = address + visitedSize;
  1329. foreach (KMemoryInfo info in IterateOverRange(addressRounded, endAddrVisited))
  1330. {
  1331. if ((info.Permission & KMemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0)
  1332. {
  1333. ulong blockAddress = GetAddrInRange(info, addressRounded);
  1334. ulong blockSize = GetSizeInRange(info, addressRounded, endAddrVisited);
  1335. ulong blockPagesCount = blockSize / PageSize;
  1336. if (DoMmuOperation(
  1337. blockAddress,
  1338. blockPagesCount,
  1339. 0,
  1340. false,
  1341. info.Permission,
  1342. MemoryOperation.ChangePermRw) != KernelResult.Success)
  1343. {
  1344. throw new InvalidOperationException("Unexpected failure trying to restore permission.");
  1345. }
  1346. }
  1347. }
  1348. }
  1349. // Signal a read for any resources tracking reads in the region, as the other process is likely to use their data.
  1350. _cpuMemory.SignalMemoryTracking(addressTruncated, endAddrRounded - addressTruncated, false);
  1351. lock (_blocks)
  1352. {
  1353. KernelResult result;
  1354. if (addressRounded < endAddrTruncated)
  1355. {
  1356. foreach (KMemoryInfo info in IterateOverRange(addressRounded, endAddrTruncated))
  1357. {
  1358. // Check if the block state matches what we expect.
  1359. if ((info.State & stateMask) != stateMask ||
  1360. (info.Permission & permission) != permission ||
  1361. (info.Attribute & attributeMask) != MemoryAttribute.None)
  1362. {
  1363. CleanUpForError();
  1364. return KernelResult.InvalidMemState;
  1365. }
  1366. ulong blockAddress = GetAddrInRange(info, addressRounded);
  1367. ulong blockSize = GetSizeInRange(info, addressRounded, endAddrTruncated);
  1368. ulong blockPagesCount = blockSize / PageSize;
  1369. if ((info.Permission & KMemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0)
  1370. {
  1371. result = DoMmuOperation(
  1372. blockAddress,
  1373. blockPagesCount,
  1374. 0,
  1375. false,
  1376. permissionMask,
  1377. MemoryOperation.ChangePermRw);
  1378. if (result != KernelResult.Success)
  1379. {
  1380. CleanUpForError();
  1381. return result;
  1382. }
  1383. }
  1384. visitedSize += blockSize;
  1385. }
  1386. }
  1387. result = GetPagesForIpcTransfer(address, size, copyData, aslrDisabled, region, out pageList);
  1388. if (result != KernelResult.Success)
  1389. {
  1390. CleanUpForError();
  1391. return result;
  1392. }
  1393. if (visitedSize != 0)
  1394. {
  1395. InsertBlock(addressRounded, visitedSize / PageSize, SetIpcMappingPermissions, permissionMask);
  1396. }
  1397. }
  1398. return KernelResult.Success;
  1399. }
  1400. private KernelResult GetPagesForIpcTransfer(
  1401. ulong address,
  1402. ulong size,
  1403. bool copyData,
  1404. bool aslrDisabled,
  1405. MemoryRegion region,
  1406. out KPageList pageList)
  1407. {
  1408. // When the start address is unaligned, we can't safely map the
  1409. // first page as it would expose other undesirable information on the
  1410. // target process. So, instead we allocate new pages, copy the data
  1411. // inside the range, and then clear the remaining space.
  1412. // The same also holds for the last page, if the end address
  1413. // (address + size) is also not aligned.
  1414. pageList = null;
  1415. KPageList pages = new KPageList();
  1416. ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
  1417. ulong addressRounded = BitUtils.AlignUp (address, PageSize);
  1418. ulong endAddr = address + size;
  1419. ulong dstFirstPagePa = 0;
  1420. ulong dstLastPagePa = 0;
  1421. void CleanUpForError()
  1422. {
  1423. if (dstFirstPagePa != 0)
  1424. {
  1425. FreeSinglePage(region, dstFirstPagePa);
  1426. }
  1427. if (dstLastPagePa != 0)
  1428. {
  1429. FreeSinglePage(region, dstLastPagePa);
  1430. }
  1431. }
  1432. // Is the first page address aligned?
  1433. // If not, allocate a new page and copy the unaligned chunck.
  1434. if (addressTruncated < addressRounded)
  1435. {
  1436. dstFirstPagePa = AllocateSinglePage(region, aslrDisabled);
  1437. if (dstFirstPagePa == 0)
  1438. {
  1439. return KernelResult.OutOfMemory;
  1440. }
  1441. ulong firstPageFillAddress = dstFirstPagePa;
  1442. if (!TryConvertVaToPa(addressTruncated, out ulong srcFirstPagePa))
  1443. {
  1444. CleanUpForError();
  1445. return KernelResult.InvalidMemState;
  1446. }
  1447. ulong unusedSizeAfter;
  1448. if (copyData)
  1449. {
  1450. ulong unusedSizeBefore = address - addressTruncated;
  1451. _context.Memory.ZeroFill(GetDramAddressFromPa(dstFirstPagePa), unusedSizeBefore);
  1452. ulong copySize = addressRounded <= endAddr ? addressRounded - address : size;
  1453. _context.Memory.Copy(
  1454. GetDramAddressFromPa(dstFirstPagePa + unusedSizeBefore),
  1455. GetDramAddressFromPa(srcFirstPagePa + unusedSizeBefore), copySize);
  1456. firstPageFillAddress += unusedSizeBefore + copySize;
  1457. unusedSizeAfter = addressRounded > endAddr ? addressRounded - endAddr : 0;
  1458. }
  1459. else
  1460. {
  1461. unusedSizeAfter = PageSize;
  1462. }
  1463. if (unusedSizeAfter != 0)
  1464. {
  1465. _context.Memory.ZeroFill(GetDramAddressFromPa(firstPageFillAddress), unusedSizeAfter);
  1466. }
  1467. if (pages.AddRange(dstFirstPagePa, 1) != KernelResult.Success)
  1468. {
  1469. CleanUpForError();
  1470. return KernelResult.OutOfResource;
  1471. }
  1472. }
  1473. ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
  1474. ulong endAddrRounded = BitUtils.AlignUp (endAddr, PageSize);
  1475. if (endAddrTruncated > addressRounded)
  1476. {
  1477. ulong alignedPagesCount = (endAddrTruncated - addressRounded) / PageSize;
  1478. AddVaRangeToPageList(pages, addressRounded, alignedPagesCount);
  1479. }
  1480. // Is the last page end address aligned?
  1481. // If not, allocate a new page and copy the unaligned chunck.
  1482. if (endAddrTruncated < endAddrRounded && (addressTruncated == addressRounded || addressTruncated < endAddrTruncated))
  1483. {
  1484. dstLastPagePa = AllocateSinglePage(region, aslrDisabled);
  1485. if (dstLastPagePa == 0)
  1486. {
  1487. CleanUpForError();
  1488. return KernelResult.OutOfMemory;
  1489. }
  1490. ulong lastPageFillAddr = dstLastPagePa;
  1491. if (!TryConvertVaToPa(endAddrTruncated, out ulong srcLastPagePa))
  1492. {
  1493. CleanUpForError();
  1494. return KernelResult.InvalidMemState;
  1495. }
  1496. ulong unusedSizeAfter;
  1497. if (copyData)
  1498. {
  1499. ulong copySize = endAddr - endAddrTruncated;
  1500. _context.Memory.Copy(
  1501. GetDramAddressFromPa(dstLastPagePa),
  1502. GetDramAddressFromPa(srcLastPagePa), copySize);
  1503. lastPageFillAddr += copySize;
  1504. unusedSizeAfter = PageSize - copySize;
  1505. }
  1506. else
  1507. {
  1508. unusedSizeAfter = PageSize;
  1509. }
  1510. _context.Memory.ZeroFill(GetDramAddressFromPa(lastPageFillAddr), unusedSizeAfter);
  1511. if (pages.AddRange(dstLastPagePa, 1) != KernelResult.Success)
  1512. {
  1513. CleanUpForError();
  1514. return KernelResult.OutOfResource;
  1515. }
  1516. }
  1517. pageList = pages;
  1518. return KernelResult.Success;
  1519. }
  1520. private ulong AllocateSinglePage(MemoryRegion region, bool aslrDisabled)
  1521. {
  1522. KMemoryRegionManager regionMgr = _context.MemoryRegions[(int)region];
  1523. return regionMgr.AllocatePagesContiguous(1, aslrDisabled);
  1524. }
  1525. private void FreeSinglePage(MemoryRegion region, ulong address)
  1526. {
  1527. KMemoryRegionManager regionMgr = _context.MemoryRegions[(int)region];
  1528. regionMgr.FreePage(address);
  1529. }
  1530. private KernelResult MapPagesFromAnotherProcess(
  1531. ulong size,
  1532. ulong address,
  1533. KMemoryPermission permission,
  1534. MemoryState state,
  1535. KPageList pageList,
  1536. out ulong dst)
  1537. {
  1538. dst = 0;
  1539. lock (_blocks)
  1540. {
  1541. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
  1542. {
  1543. return KernelResult.OutOfResource;
  1544. }
  1545. ulong endAddr = address + size;
  1546. ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
  1547. ulong endAddrRounded = BitUtils.AlignUp (endAddr, PageSize);
  1548. ulong neededSize = endAddrRounded - addressTruncated;
  1549. ulong neededPagesCount = neededSize / PageSize;
  1550. ulong regionPagesCount = (AliasRegionEnd - AliasRegionStart) / PageSize;
  1551. ulong va = 0;
  1552. for (int unit = MappingUnitSizes.Length - 1; unit >= 0 && va == 0; unit--)
  1553. {
  1554. int alignment = MappingUnitSizes[unit];
  1555. va = AllocateVa(AliasRegionStart, regionPagesCount, neededPagesCount, alignment);
  1556. }
  1557. if (va == 0)
  1558. {
  1559. return KernelResult.OutOfVaSpace;
  1560. }
  1561. if (pageList.Nodes.Count != 0)
  1562. {
  1563. KernelResult result = MapPages(va, pageList, permission);
  1564. if (result != KernelResult.Success)
  1565. {
  1566. return result;
  1567. }
  1568. }
  1569. InsertBlock(va, neededPagesCount, state, permission);
  1570. dst = va + (address - addressTruncated);
  1571. }
  1572. return KernelResult.Success;
  1573. }
  1574. public KernelResult UnmapNoAttributeIfStateEquals(ulong address, ulong size, MemoryState state)
  1575. {
  1576. if (AddrSpaceStart > address)
  1577. {
  1578. return KernelResult.InvalidMemState;
  1579. }
  1580. ulong endAddr = address + size;
  1581. if (endAddr <= address || endAddr - 1 > AddrSpaceEnd - 1)
  1582. {
  1583. return KernelResult.InvalidMemState;
  1584. }
  1585. lock (_blocks)
  1586. {
  1587. if (CheckRange(
  1588. address,
  1589. size,
  1590. MemoryState.Mask,
  1591. state,
  1592. KMemoryPermission.Read,
  1593. KMemoryPermission.Read,
  1594. MemoryAttribute.Mask,
  1595. MemoryAttribute.None,
  1596. MemoryAttribute.IpcAndDeviceMapped,
  1597. out _,
  1598. out _,
  1599. out _))
  1600. {
  1601. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
  1602. {
  1603. return KernelResult.OutOfResource;
  1604. }
  1605. ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
  1606. ulong addressRounded = BitUtils.AlignUp (address, PageSize);
  1607. ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
  1608. ulong endAddrRounded = BitUtils.AlignUp (endAddr, PageSize);
  1609. ulong pagesCount = (endAddrRounded - addressTruncated) / PageSize;
  1610. // Free pages we had to create on-demand, if any of the buffer was not page aligned.
  1611. // Real kernel has page ref counting, so this is done as part of the unmap operation.
  1612. if (addressTruncated != addressRounded)
  1613. {
  1614. FreeSinglePage(_memRegion, ConvertVaToPa(addressTruncated));
  1615. }
  1616. if (endAddrTruncated < endAddrRounded && (addressTruncated == addressRounded || addressTruncated < endAddrTruncated))
  1617. {
  1618. FreeSinglePage(_memRegion, ConvertVaToPa(endAddrTruncated));
  1619. }
  1620. KernelResult result = DoMmuOperation(
  1621. addressTruncated,
  1622. pagesCount,
  1623. 0,
  1624. false,
  1625. KMemoryPermission.None,
  1626. MemoryOperation.Unmap);
  1627. if (result == KernelResult.Success)
  1628. {
  1629. InsertBlock(addressTruncated, pagesCount, MemoryState.Unmapped);
  1630. }
  1631. return result;
  1632. }
  1633. else
  1634. {
  1635. return KernelResult.InvalidMemState;
  1636. }
  1637. }
  1638. }
  1639. public KernelResult UnmapIpcRestorePermission(ulong address, ulong size, MemoryState state)
  1640. {
  1641. ulong endAddr = address + size;
  1642. ulong addressRounded = BitUtils.AlignUp (address, PageSize);
  1643. ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
  1644. ulong endAddrRounded = BitUtils.AlignUp (endAddr, PageSize);
  1645. ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
  1646. ulong pagesCount = addressRounded < endAddrTruncated ? (endAddrTruncated - addressRounded) / PageSize : 0;
  1647. if (pagesCount == 0)
  1648. {
  1649. return KernelResult.Success;
  1650. }
  1651. MemoryState stateMask;
  1652. switch (state)
  1653. {
  1654. case MemoryState.IpcBuffer0: stateMask = MemoryState.IpcSendAllowedType0; break;
  1655. case MemoryState.IpcBuffer1: stateMask = MemoryState.IpcSendAllowedType1; break;
  1656. case MemoryState.IpcBuffer3: stateMask = MemoryState.IpcSendAllowedType3; break;
  1657. default: return KernelResult.InvalidCombination;
  1658. }
  1659. MemoryAttribute attributeMask =
  1660. MemoryAttribute.Borrowed |
  1661. MemoryAttribute.IpcMapped |
  1662. MemoryAttribute.Uncached;
  1663. if (state == MemoryState.IpcBuffer0)
  1664. {
  1665. attributeMask |= MemoryAttribute.DeviceMapped;
  1666. }
  1667. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
  1668. {
  1669. return KernelResult.OutOfResource;
  1670. }
  1671. // Anything on the client side should see this memory as modified.
  1672. _cpuMemory.SignalMemoryTracking(addressTruncated, endAddrRounded - addressTruncated, true);
  1673. lock (_blocks)
  1674. {
  1675. foreach (KMemoryInfo info in IterateOverRange(addressRounded, endAddrTruncated))
  1676. {
  1677. // Check if the block state matches what we expect.
  1678. if ((info.State & stateMask) != stateMask ||
  1679. (info.Attribute & attributeMask) != MemoryAttribute.IpcMapped)
  1680. {
  1681. return KernelResult.InvalidMemState;
  1682. }
  1683. if (info.Permission != info.SourcePermission && info.IpcRefCount == 1)
  1684. {
  1685. ulong blockAddress = GetAddrInRange(info, addressRounded);
  1686. ulong blockSize = GetSizeInRange(info, addressRounded, endAddrTruncated);
  1687. ulong blockPagesCount = blockSize / PageSize;
  1688. KernelResult result = DoMmuOperation(
  1689. blockAddress,
  1690. blockPagesCount,
  1691. 0,
  1692. false,
  1693. info.SourcePermission,
  1694. MemoryOperation.ChangePermRw);
  1695. if (result != KernelResult.Success)
  1696. {
  1697. return result;
  1698. }
  1699. }
  1700. }
  1701. InsertBlock(addressRounded, pagesCount, RestoreIpcMappingPermissions);
  1702. return KernelResult.Success;
  1703. }
  1704. }
  1705. public KernelResult BorrowIpcBuffer(ulong address, ulong size)
  1706. {
  1707. return SetAttributesAndChangePermission(
  1708. address,
  1709. size,
  1710. MemoryState.IpcBufferAllowed,
  1711. MemoryState.IpcBufferAllowed,
  1712. KMemoryPermission.Mask,
  1713. KMemoryPermission.ReadAndWrite,
  1714. MemoryAttribute.Mask,
  1715. MemoryAttribute.None,
  1716. KMemoryPermission.None,
  1717. MemoryAttribute.Borrowed);
  1718. }
  1719. public KernelResult BorrowTransferMemory(KPageList pageList, ulong address, ulong size, KMemoryPermission permission)
  1720. {
  1721. return SetAttributesAndChangePermission(
  1722. address,
  1723. size,
  1724. MemoryState.TransferMemoryAllowed,
  1725. MemoryState.TransferMemoryAllowed,
  1726. KMemoryPermission.Mask,
  1727. KMemoryPermission.ReadAndWrite,
  1728. MemoryAttribute.Mask,
  1729. MemoryAttribute.None,
  1730. permission,
  1731. MemoryAttribute.Borrowed,
  1732. pageList);
  1733. }
  1734. private KernelResult SetAttributesAndChangePermission(
  1735. ulong address,
  1736. ulong size,
  1737. MemoryState stateMask,
  1738. MemoryState stateExpected,
  1739. KMemoryPermission permissionMask,
  1740. KMemoryPermission permissionExpected,
  1741. MemoryAttribute attributeMask,
  1742. MemoryAttribute attributeExpected,
  1743. KMemoryPermission newPermission,
  1744. MemoryAttribute attributeSetMask,
  1745. KPageList pageList = null)
  1746. {
  1747. if (address + size <= address || !InsideAddrSpace(address, size))
  1748. {
  1749. return KernelResult.InvalidMemState;
  1750. }
  1751. lock (_blocks)
  1752. {
  1753. if (CheckRange(
  1754. address,
  1755. size,
  1756. stateMask | MemoryState.IsPoolAllocated,
  1757. stateExpected | MemoryState.IsPoolAllocated,
  1758. permissionMask,
  1759. permissionExpected,
  1760. attributeMask,
  1761. attributeExpected,
  1762. MemoryAttribute.IpcAndDeviceMapped,
  1763. out MemoryState oldState,
  1764. out KMemoryPermission oldPermission,
  1765. out MemoryAttribute oldAttribute))
  1766. {
  1767. ulong pagesCount = size / PageSize;
  1768. if (pageList != null)
  1769. {
  1770. AddVaRangeToPageList(pageList, address, pagesCount);
  1771. }
  1772. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
  1773. {
  1774. return KernelResult.OutOfResource;
  1775. }
  1776. if (newPermission == KMemoryPermission.None)
  1777. {
  1778. newPermission = oldPermission;
  1779. }
  1780. if (newPermission != oldPermission)
  1781. {
  1782. KernelResult result = DoMmuOperation(
  1783. address,
  1784. pagesCount,
  1785. 0,
  1786. false,
  1787. newPermission,
  1788. MemoryOperation.ChangePermRw);
  1789. if (result != KernelResult.Success)
  1790. {
  1791. return result;
  1792. }
  1793. }
  1794. MemoryAttribute newAttribute = oldAttribute | attributeSetMask;
  1795. InsertBlock(address, pagesCount, oldState, newPermission, newAttribute);
  1796. return KernelResult.Success;
  1797. }
  1798. else
  1799. {
  1800. return KernelResult.InvalidMemState;
  1801. }
  1802. }
  1803. }
  1804. public KernelResult UnborrowIpcBuffer(ulong address, ulong size)
  1805. {
  1806. return ClearAttributesAndChangePermission(
  1807. address,
  1808. size,
  1809. MemoryState.IpcBufferAllowed,
  1810. MemoryState.IpcBufferAllowed,
  1811. KMemoryPermission.None,
  1812. KMemoryPermission.None,
  1813. MemoryAttribute.Mask,
  1814. MemoryAttribute.Borrowed,
  1815. KMemoryPermission.ReadAndWrite,
  1816. MemoryAttribute.Borrowed);
  1817. }
  1818. public KernelResult UnborrowTransferMemory(ulong address, ulong size, KPageList pageList)
  1819. {
  1820. return ClearAttributesAndChangePermission(
  1821. address,
  1822. size,
  1823. MemoryState.TransferMemoryAllowed,
  1824. MemoryState.TransferMemoryAllowed,
  1825. KMemoryPermission.None,
  1826. KMemoryPermission.None,
  1827. MemoryAttribute.Mask,
  1828. MemoryAttribute.Borrowed,
  1829. KMemoryPermission.ReadAndWrite,
  1830. MemoryAttribute.Borrowed,
  1831. pageList);
  1832. }
  1833. private KernelResult ClearAttributesAndChangePermission(
  1834. ulong address,
  1835. ulong size,
  1836. MemoryState stateMask,
  1837. MemoryState stateExpected,
  1838. KMemoryPermission permissionMask,
  1839. KMemoryPermission permissionExpected,
  1840. MemoryAttribute attributeMask,
  1841. MemoryAttribute attributeExpected,
  1842. KMemoryPermission newPermission,
  1843. MemoryAttribute attributeClearMask,
  1844. KPageList pageList = null)
  1845. {
  1846. if (address + size <= address || !InsideAddrSpace(address, size))
  1847. {
  1848. return KernelResult.InvalidMemState;
  1849. }
  1850. lock (_blocks)
  1851. {
  1852. if (CheckRange(
  1853. address,
  1854. size,
  1855. stateMask | MemoryState.IsPoolAllocated,
  1856. stateExpected | MemoryState.IsPoolAllocated,
  1857. permissionMask,
  1858. permissionExpected,
  1859. attributeMask,
  1860. attributeExpected,
  1861. MemoryAttribute.IpcAndDeviceMapped,
  1862. out MemoryState oldState,
  1863. out KMemoryPermission oldPermission,
  1864. out MemoryAttribute oldAttribute))
  1865. {
  1866. ulong pagesCount = size / PageSize;
  1867. if (pageList != null)
  1868. {
  1869. KPageList currPageList = new KPageList();
  1870. AddVaRangeToPageList(currPageList, address, pagesCount);
  1871. if (!currPageList.IsEqual(pageList))
  1872. {
  1873. return KernelResult.InvalidMemRange;
  1874. }
  1875. }
  1876. if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
  1877. {
  1878. return KernelResult.OutOfResource;
  1879. }
  1880. if (newPermission == KMemoryPermission.None)
  1881. {
  1882. newPermission = oldPermission;
  1883. }
  1884. if (newPermission != oldPermission)
  1885. {
  1886. KernelResult result = DoMmuOperation(
  1887. address,
  1888. pagesCount,
  1889. 0,
  1890. false,
  1891. newPermission,
  1892. MemoryOperation.ChangePermRw);
  1893. if (result != KernelResult.Success)
  1894. {
  1895. return result;
  1896. }
  1897. }
  1898. MemoryAttribute newAttribute = oldAttribute & ~attributeClearMask;
  1899. InsertBlock(address, pagesCount, oldState, newPermission, newAttribute);
  1900. return KernelResult.Success;
  1901. }
  1902. else
  1903. {
  1904. return KernelResult.InvalidMemState;
  1905. }
  1906. }
  1907. }
  1908. private void AddVaRangeToPageList(KPageList pageList, ulong start, ulong pagesCount)
  1909. {
  1910. ulong address = start;
  1911. while (address < start + pagesCount * PageSize)
  1912. {
  1913. if (!TryConvertVaToPa(address, out ulong pa))
  1914. {
  1915. throw new InvalidOperationException("Unexpected failure translating virtual address.");
  1916. }
  1917. pageList.AddRange(pa, 1);
  1918. address += PageSize;
  1919. }
  1920. }
  1921. private static ulong GetAddrInRange(KMemoryInfo info, ulong start)
  1922. {
  1923. if (info.Address < start)
  1924. {
  1925. return start;
  1926. }
  1927. return info.Address;
  1928. }
  1929. private static ulong GetSizeInRange(KMemoryInfo info, ulong start, ulong end)
  1930. {
  1931. ulong endAddr = info.Size + info.Address;
  1932. ulong size = info.Size;
  1933. if (info.Address < start)
  1934. {
  1935. size -= start - info.Address;
  1936. }
  1937. if (endAddr > end)
  1938. {
  1939. size -= endAddr - end;
  1940. }
  1941. return size;
  1942. }
  1943. private bool IsUnmapped(ulong address, ulong size)
  1944. {
  1945. return CheckRange(
  1946. address,
  1947. size,
  1948. MemoryState.Mask,
  1949. MemoryState.Unmapped,
  1950. KMemoryPermission.Mask,
  1951. KMemoryPermission.None,
  1952. MemoryAttribute.Mask,
  1953. MemoryAttribute.None,
  1954. MemoryAttribute.IpcAndDeviceMapped,
  1955. out _,
  1956. out _,
  1957. out _);
  1958. }
  1959. private bool CheckRange(
  1960. ulong address,
  1961. ulong size,
  1962. MemoryState stateMask,
  1963. MemoryState stateExpected,
  1964. KMemoryPermission permissionMask,
  1965. KMemoryPermission permissionExpected,
  1966. MemoryAttribute attributeMask,
  1967. MemoryAttribute attributeExpected,
  1968. MemoryAttribute attributeIgnoreMask,
  1969. out MemoryState outState,
  1970. out KMemoryPermission outPermission,
  1971. out MemoryAttribute outAttribute)
  1972. {
  1973. ulong endAddr = address + size;
  1974. LinkedListNode<KMemoryBlock> node = FindBlockNode(address);
  1975. KMemoryInfo info = node.Value.GetInfo();
  1976. MemoryState firstState = info.State;
  1977. KMemoryPermission firstPermission = info.Permission;
  1978. MemoryAttribute firstAttribute = info.Attribute;
  1979. do
  1980. {
  1981. info = node.Value.GetInfo();
  1982. // Check if the block state matches what we expect.
  1983. if ( firstState != info.State ||
  1984. firstPermission != info.Permission ||
  1985. (info.Attribute & attributeMask) != attributeExpected ||
  1986. (firstAttribute | attributeIgnoreMask) != (info.Attribute | attributeIgnoreMask) ||
  1987. (firstState & stateMask) != stateExpected ||
  1988. (firstPermission & permissionMask) != permissionExpected)
  1989. {
  1990. outState = MemoryState.Unmapped;
  1991. outPermission = KMemoryPermission.None;
  1992. outAttribute = MemoryAttribute.None;
  1993. return false;
  1994. }
  1995. }
  1996. while (info.Address + info.Size - 1 < endAddr - 1 && (node = node.Next) != null);
  1997. outState = firstState;
  1998. outPermission = firstPermission;
  1999. outAttribute = firstAttribute & ~attributeIgnoreMask;
  2000. return true;
  2001. }
  2002. private bool CheckRange(
  2003. ulong address,
  2004. ulong size,
  2005. MemoryState stateMask,
  2006. MemoryState stateExpected,
  2007. KMemoryPermission permissionMask,
  2008. KMemoryPermission permissionExpected,
  2009. MemoryAttribute attributeMask,
  2010. MemoryAttribute attributeExpected)
  2011. {
  2012. foreach (KMemoryInfo info in IterateOverRange(address, address + size))
  2013. {
  2014. // Check if the block state matches what we expect.
  2015. if ((info.State & stateMask) != stateExpected ||
  2016. (info.Permission & permissionMask) != permissionExpected ||
  2017. (info.Attribute & attributeMask) != attributeExpected)
  2018. {
  2019. return false;
  2020. }
  2021. }
  2022. return true;
  2023. }
  2024. private IEnumerable<KMemoryInfo> IterateOverRange(ulong start, ulong end)
  2025. {
  2026. LinkedListNode<KMemoryBlock> node = FindBlockNode(start);
  2027. KMemoryInfo info;
  2028. do
  2029. {
  2030. info = node.Value.GetInfo();
  2031. yield return info;
  2032. }
  2033. while (info.Address + info.Size - 1 < end - 1 && (node = node.Next) != null);
  2034. }
  2035. private void InsertBlock(
  2036. ulong baseAddress,
  2037. ulong pagesCount,
  2038. MemoryState oldState,
  2039. KMemoryPermission oldPermission,
  2040. MemoryAttribute oldAttribute,
  2041. MemoryState newState,
  2042. KMemoryPermission newPermission,
  2043. MemoryAttribute newAttribute)
  2044. {
  2045. // Insert new block on the list only on areas where the state
  2046. // of the block matches the state specified on the old* state
  2047. // arguments, otherwise leave it as is.
  2048. int oldCount = _blocks.Count;
  2049. oldAttribute |= MemoryAttribute.IpcAndDeviceMapped;
  2050. ulong endAddr = baseAddress + pagesCount * PageSize;
  2051. LinkedListNode<KMemoryBlock> node = _blocks.First;
  2052. while (node != null)
  2053. {
  2054. LinkedListNode<KMemoryBlock> newNode = node;
  2055. KMemoryBlock currBlock = node.Value;
  2056. ulong currBaseAddr = currBlock.BaseAddress;
  2057. ulong currEndAddr = currBlock.PagesCount * PageSize + currBaseAddr;
  2058. if (baseAddress < currEndAddr && currBaseAddr < endAddr)
  2059. {
  2060. MemoryAttribute currBlockAttr = currBlock.Attribute | MemoryAttribute.IpcAndDeviceMapped;
  2061. if (currBlock.State != oldState ||
  2062. currBlock.Permission != oldPermission ||
  2063. currBlockAttr != oldAttribute)
  2064. {
  2065. node = node.Next;
  2066. continue;
  2067. }
  2068. if (baseAddress > currBaseAddr)
  2069. {
  2070. _blocks.AddBefore(node, currBlock.SplitRightAtAddress(baseAddress));
  2071. }
  2072. if (endAddr < currEndAddr)
  2073. {
  2074. newNode = _blocks.AddBefore(node, currBlock.SplitRightAtAddress(endAddr));
  2075. }
  2076. newNode.Value.SetState(newPermission, newState, newAttribute);
  2077. newNode = MergeEqualStateNeighbors(newNode);
  2078. }
  2079. if (currEndAddr - 1 >= endAddr - 1)
  2080. {
  2081. break;
  2082. }
  2083. node = newNode.Next;
  2084. }
  2085. _blockAllocator.Count += _blocks.Count - oldCount;
  2086. ValidateInternalState();
  2087. }
  2088. private void InsertBlock(
  2089. ulong baseAddress,
  2090. ulong pagesCount,
  2091. MemoryState state,
  2092. KMemoryPermission permission = KMemoryPermission.None,
  2093. MemoryAttribute attribute = MemoryAttribute.None)
  2094. {
  2095. // Inserts new block at the list, replacing and splitting
  2096. // existing blocks as needed.
  2097. int oldCount = _blocks.Count;
  2098. ulong endAddr = baseAddress + pagesCount * PageSize;
  2099. LinkedListNode<KMemoryBlock> node = _blocks.First;
  2100. while (node != null)
  2101. {
  2102. LinkedListNode<KMemoryBlock> newNode = node;
  2103. KMemoryBlock currBlock = node.Value;
  2104. ulong currBaseAddr = currBlock.BaseAddress;
  2105. ulong currEndAddr = currBlock.PagesCount * PageSize + currBaseAddr;
  2106. if (baseAddress < currEndAddr && currBaseAddr < endAddr)
  2107. {
  2108. if (baseAddress > currBaseAddr)
  2109. {
  2110. _blocks.AddBefore(node, currBlock.SplitRightAtAddress(baseAddress));
  2111. }
  2112. if (endAddr < currEndAddr)
  2113. {
  2114. newNode = _blocks.AddBefore(node, currBlock.SplitRightAtAddress(endAddr));
  2115. }
  2116. newNode.Value.SetState(permission, state, attribute);
  2117. newNode = MergeEqualStateNeighbors(newNode);
  2118. }
  2119. if (currEndAddr - 1 >= endAddr - 1)
  2120. {
  2121. break;
  2122. }
  2123. node = newNode.Next;
  2124. }
  2125. _blockAllocator.Count += _blocks.Count - oldCount;
  2126. ValidateInternalState();
  2127. }
  2128. private static void SetIpcMappingPermissions(KMemoryBlock block, KMemoryPermission permission)
  2129. {
  2130. block.SetIpcMappingPermission(permission);
  2131. }
  2132. private static void RestoreIpcMappingPermissions(KMemoryBlock block, KMemoryPermission permission)
  2133. {
  2134. block.RestoreIpcMappingPermission();
  2135. }
  2136. private delegate void BlockMutator(KMemoryBlock block, KMemoryPermission newPerm);
  2137. private void InsertBlock(
  2138. ulong baseAddress,
  2139. ulong pagesCount,
  2140. BlockMutator blockMutate,
  2141. KMemoryPermission permission = KMemoryPermission.None)
  2142. {
  2143. // Inserts new block at the list, replacing and splitting
  2144. // existing blocks as needed, then calling the callback
  2145. // function on the new block.
  2146. int oldCount = _blocks.Count;
  2147. ulong endAddr = baseAddress + pagesCount * PageSize;
  2148. LinkedListNode<KMemoryBlock> node = _blocks.First;
  2149. while (node != null)
  2150. {
  2151. LinkedListNode<KMemoryBlock> newNode = node;
  2152. KMemoryBlock currBlock = node.Value;
  2153. ulong currBaseAddr = currBlock.BaseAddress;
  2154. ulong currEndAddr = currBlock.PagesCount * PageSize + currBaseAddr;
  2155. if (baseAddress < currEndAddr && currBaseAddr < endAddr)
  2156. {
  2157. if (baseAddress > currBaseAddr)
  2158. {
  2159. _blocks.AddBefore(node, currBlock.SplitRightAtAddress(baseAddress));
  2160. }
  2161. if (endAddr < currEndAddr)
  2162. {
  2163. newNode = _blocks.AddBefore(node, currBlock.SplitRightAtAddress(endAddr));
  2164. }
  2165. KMemoryBlock newBlock = newNode.Value;
  2166. blockMutate(newBlock, permission);
  2167. newNode = MergeEqualStateNeighbors(newNode);
  2168. }
  2169. if (currEndAddr - 1 >= endAddr - 1)
  2170. {
  2171. break;
  2172. }
  2173. node = newNode.Next;
  2174. }
  2175. _blockAllocator.Count += _blocks.Count - oldCount;
  2176. ValidateInternalState();
  2177. }
  2178. [Conditional("DEBUG")]
  2179. private void ValidateInternalState()
  2180. {
  2181. ulong expectedAddress = 0;
  2182. LinkedListNode<KMemoryBlock> node = _blocks.First;
  2183. while (node != null)
  2184. {
  2185. LinkedListNode<KMemoryBlock> newNode = node;
  2186. KMemoryBlock currBlock = node.Value;
  2187. Debug.Assert(currBlock.BaseAddress == expectedAddress);
  2188. expectedAddress = currBlock.BaseAddress + currBlock.PagesCount * PageSize;
  2189. node = newNode.Next;
  2190. }
  2191. Debug.Assert(expectedAddress == AddrSpaceEnd);
  2192. }
  2193. private LinkedListNode<KMemoryBlock> MergeEqualStateNeighbors(LinkedListNode<KMemoryBlock> node)
  2194. {
  2195. KMemoryBlock block = node.Value;
  2196. if (node.Previous != null)
  2197. {
  2198. KMemoryBlock previousBlock = node.Previous.Value;
  2199. if (BlockStateEquals(block, previousBlock))
  2200. {
  2201. LinkedListNode<KMemoryBlock> previousNode = node.Previous;
  2202. _blocks.Remove(node);
  2203. previousBlock.AddPages(block.PagesCount);
  2204. node = previousNode;
  2205. block = previousBlock;
  2206. }
  2207. }
  2208. if (node.Next != null)
  2209. {
  2210. KMemoryBlock nextBlock = node.Next.Value;
  2211. if (BlockStateEquals(block, nextBlock))
  2212. {
  2213. _blocks.Remove(node.Next);
  2214. block.AddPages(nextBlock.PagesCount);
  2215. }
  2216. }
  2217. return node;
  2218. }
  2219. private static bool BlockStateEquals(KMemoryBlock lhs, KMemoryBlock rhs)
  2220. {
  2221. return lhs.State == rhs.State &&
  2222. lhs.Permission == rhs.Permission &&
  2223. lhs.Attribute == rhs.Attribute &&
  2224. lhs.SourcePermission == rhs.SourcePermission &&
  2225. lhs.DeviceRefCount == rhs.DeviceRefCount &&
  2226. lhs.IpcRefCount == rhs.IpcRefCount;
  2227. }
  2228. private ulong AllocateVa(
  2229. ulong regionStart,
  2230. ulong regionPagesCount,
  2231. ulong neededPagesCount,
  2232. int alignment)
  2233. {
  2234. ulong address = 0;
  2235. ulong regionEndAddr = regionStart + regionPagesCount * PageSize;
  2236. ulong reservedPagesCount = _isKernel ? 1UL : 4UL;
  2237. if (_aslrEnabled)
  2238. {
  2239. ulong totalNeededSize = (reservedPagesCount + neededPagesCount) * PageSize;
  2240. ulong remainingPages = regionPagesCount - neededPagesCount;
  2241. ulong aslrMaxOffset = ((remainingPages + reservedPagesCount) * PageSize) / (ulong)alignment;
  2242. for (int attempt = 0; attempt < 8; attempt++)
  2243. {
  2244. address = BitUtils.AlignDown(regionStart + GetRandomValue(0, aslrMaxOffset) * (ulong)alignment, alignment);
  2245. ulong endAddr = address + totalNeededSize;
  2246. KMemoryInfo info = FindBlock(address).GetInfo();
  2247. if (info.State != MemoryState.Unmapped)
  2248. {
  2249. continue;
  2250. }
  2251. ulong currBaseAddr = info.Address + reservedPagesCount * PageSize;
  2252. ulong currEndAddr = info.Address + info.Size;
  2253. if (address >= regionStart &&
  2254. address >= currBaseAddr &&
  2255. endAddr - 1 <= regionEndAddr - 1 &&
  2256. endAddr - 1 <= currEndAddr - 1)
  2257. {
  2258. break;
  2259. }
  2260. }
  2261. if (address == 0)
  2262. {
  2263. ulong aslrPage = GetRandomValue(0, aslrMaxOffset);
  2264. address = FindFirstFit(
  2265. regionStart + aslrPage * PageSize,
  2266. regionPagesCount - aslrPage,
  2267. neededPagesCount,
  2268. alignment,
  2269. 0,
  2270. reservedPagesCount);
  2271. }
  2272. }
  2273. if (address == 0)
  2274. {
  2275. address = FindFirstFit(
  2276. regionStart,
  2277. regionPagesCount,
  2278. neededPagesCount,
  2279. alignment,
  2280. 0,
  2281. reservedPagesCount);
  2282. }
  2283. return address;
  2284. }
  2285. private ulong FindFirstFit(
  2286. ulong regionStart,
  2287. ulong regionPagesCount,
  2288. ulong neededPagesCount,
  2289. int alignment,
  2290. ulong reservedStart,
  2291. ulong reservedPagesCount)
  2292. {
  2293. ulong reservedSize = reservedPagesCount * PageSize;
  2294. ulong totalNeededSize = reservedSize + neededPagesCount * PageSize;
  2295. ulong regionEndAddr = regionStart + regionPagesCount * PageSize;
  2296. LinkedListNode<KMemoryBlock> node = FindBlockNode(regionStart);
  2297. KMemoryInfo info = node.Value.GetInfo();
  2298. while (regionEndAddr >= info.Address)
  2299. {
  2300. if (info.State == MemoryState.Unmapped)
  2301. {
  2302. ulong currBaseAddr = info.Address + reservedSize;
  2303. ulong currEndAddr = info.Address + info.Size - 1;
  2304. ulong address = BitUtils.AlignDown(currBaseAddr, alignment) + reservedStart;
  2305. if (currBaseAddr > address)
  2306. {
  2307. address += (ulong)alignment;
  2308. }
  2309. ulong allocationEndAddr = address + totalNeededSize - 1;
  2310. if (allocationEndAddr <= regionEndAddr &&
  2311. allocationEndAddr <= currEndAddr &&
  2312. address < allocationEndAddr)
  2313. {
  2314. return address;
  2315. }
  2316. }
  2317. node = node.Next;
  2318. if (node == null)
  2319. {
  2320. break;
  2321. }
  2322. info = node.Value.GetInfo();
  2323. }
  2324. return 0;
  2325. }
  2326. private KMemoryBlock FindBlock(ulong address)
  2327. {
  2328. return FindBlockNode(address)?.Value;
  2329. }
  2330. private LinkedListNode<KMemoryBlock> FindBlockNode(ulong address)
  2331. {
  2332. lock (_blocks)
  2333. {
  2334. LinkedListNode<KMemoryBlock> node = _blocks.First;
  2335. while (node != null)
  2336. {
  2337. KMemoryBlock block = node.Value;
  2338. ulong currEndAddr = block.PagesCount * PageSize + block.BaseAddress;
  2339. if (block.BaseAddress <= address && currEndAddr - 1 >= address)
  2340. {
  2341. return node;
  2342. }
  2343. node = node.Next;
  2344. }
  2345. }
  2346. return null;
  2347. }
  2348. public bool CanContain(ulong address, ulong size, MemoryState state)
  2349. {
  2350. ulong endAddr = address + size;
  2351. ulong regionBaseAddr = GetBaseAddress(state);
  2352. ulong regionEndAddr = regionBaseAddr + GetSize(state);
  2353. bool InsideRegion()
  2354. {
  2355. return regionBaseAddr <= address &&
  2356. endAddr > address &&
  2357. endAddr - 1 <= regionEndAddr - 1;
  2358. }
  2359. bool OutsideHeapRegion() => endAddr <= HeapRegionStart || address >= HeapRegionEnd;
  2360. bool OutsideAliasRegion() => endAddr <= AliasRegionStart || address >= AliasRegionEnd;
  2361. switch (state)
  2362. {
  2363. case MemoryState.Io:
  2364. case MemoryState.Normal:
  2365. case MemoryState.CodeStatic:
  2366. case MemoryState.CodeMutable:
  2367. case MemoryState.SharedMemory:
  2368. case MemoryState.ModCodeStatic:
  2369. case MemoryState.ModCodeMutable:
  2370. case MemoryState.Stack:
  2371. case MemoryState.ThreadLocal:
  2372. case MemoryState.TransferMemoryIsolated:
  2373. case MemoryState.TransferMemory:
  2374. case MemoryState.ProcessMemory:
  2375. case MemoryState.CodeReadOnly:
  2376. case MemoryState.CodeWritable:
  2377. return InsideRegion() && OutsideHeapRegion() && OutsideAliasRegion();
  2378. case MemoryState.Heap:
  2379. return InsideRegion() && OutsideAliasRegion();
  2380. case MemoryState.IpcBuffer0:
  2381. case MemoryState.IpcBuffer1:
  2382. case MemoryState.IpcBuffer3:
  2383. return InsideRegion() && OutsideHeapRegion();
  2384. case MemoryState.KernelStack:
  2385. return InsideRegion();
  2386. }
  2387. throw new ArgumentException($"Invalid state value \"{state}\".");
  2388. }
  2389. private ulong GetBaseAddress(MemoryState state)
  2390. {
  2391. switch (state)
  2392. {
  2393. case MemoryState.Io:
  2394. case MemoryState.Normal:
  2395. case MemoryState.ThreadLocal:
  2396. return TlsIoRegionStart;
  2397. case MemoryState.CodeStatic:
  2398. case MemoryState.CodeMutable:
  2399. case MemoryState.SharedMemory:
  2400. case MemoryState.ModCodeStatic:
  2401. case MemoryState.ModCodeMutable:
  2402. case MemoryState.TransferMemoryIsolated:
  2403. case MemoryState.TransferMemory:
  2404. case MemoryState.ProcessMemory:
  2405. case MemoryState.CodeReadOnly:
  2406. case MemoryState.CodeWritable:
  2407. return GetAddrSpaceBaseAddr();
  2408. case MemoryState.Heap:
  2409. return HeapRegionStart;
  2410. case MemoryState.IpcBuffer0:
  2411. case MemoryState.IpcBuffer1:
  2412. case MemoryState.IpcBuffer3:
  2413. return AliasRegionStart;
  2414. case MemoryState.Stack:
  2415. return StackRegionStart;
  2416. case MemoryState.KernelStack:
  2417. return AddrSpaceStart;
  2418. }
  2419. throw new ArgumentException($"Invalid state value \"{state}\".");
  2420. }
  2421. private ulong GetSize(MemoryState state)
  2422. {
  2423. switch (state)
  2424. {
  2425. case MemoryState.Io:
  2426. case MemoryState.Normal:
  2427. case MemoryState.ThreadLocal:
  2428. return TlsIoRegionEnd - TlsIoRegionStart;
  2429. case MemoryState.CodeStatic:
  2430. case MemoryState.CodeMutable:
  2431. case MemoryState.SharedMemory:
  2432. case MemoryState.ModCodeStatic:
  2433. case MemoryState.ModCodeMutable:
  2434. case MemoryState.TransferMemoryIsolated:
  2435. case MemoryState.TransferMemory:
  2436. case MemoryState.ProcessMemory:
  2437. case MemoryState.CodeReadOnly:
  2438. case MemoryState.CodeWritable:
  2439. return GetAddrSpaceSize();
  2440. case MemoryState.Heap:
  2441. return HeapRegionEnd - HeapRegionStart;
  2442. case MemoryState.IpcBuffer0:
  2443. case MemoryState.IpcBuffer1:
  2444. case MemoryState.IpcBuffer3:
  2445. return AliasRegionEnd - AliasRegionStart;
  2446. case MemoryState.Stack:
  2447. return StackRegionEnd - StackRegionStart;
  2448. case MemoryState.KernelStack:
  2449. return AddrSpaceEnd - AddrSpaceStart;
  2450. }
  2451. throw new ArgumentException($"Invalid state value \"{state}\".");
  2452. }
  2453. public ulong GetAddrSpaceBaseAddr()
  2454. {
  2455. if (AddrSpaceWidth == 36 || AddrSpaceWidth == 39)
  2456. {
  2457. return 0x8000000;
  2458. }
  2459. else if (AddrSpaceWidth == 32)
  2460. {
  2461. return 0x200000;
  2462. }
  2463. else
  2464. {
  2465. throw new InvalidOperationException("Invalid address space width!");
  2466. }
  2467. }
  2468. public ulong GetAddrSpaceSize()
  2469. {
  2470. if (AddrSpaceWidth == 36)
  2471. {
  2472. return 0xff8000000;
  2473. }
  2474. else if (AddrSpaceWidth == 39)
  2475. {
  2476. return 0x7ff8000000;
  2477. }
  2478. else if (AddrSpaceWidth == 32)
  2479. {
  2480. return 0xffe00000;
  2481. }
  2482. else
  2483. {
  2484. throw new InvalidOperationException("Invalid address space width!");
  2485. }
  2486. }
  2487. private KernelResult MapPages(ulong address, KPageList pageList, KMemoryPermission permission)
  2488. {
  2489. ulong currAddr = address;
  2490. KernelResult result = KernelResult.Success;
  2491. foreach (KPageNode pageNode in pageList)
  2492. {
  2493. result = DoMmuOperation(
  2494. currAddr,
  2495. pageNode.PagesCount,
  2496. pageNode.Address,
  2497. true,
  2498. permission,
  2499. MemoryOperation.MapPa);
  2500. if (result != KernelResult.Success)
  2501. {
  2502. KMemoryInfo info = FindBlock(currAddr).GetInfo();
  2503. ulong pagesCount = (address - currAddr) / PageSize;
  2504. result = MmuUnmap(address, pagesCount);
  2505. break;
  2506. }
  2507. currAddr += pageNode.PagesCount * PageSize;
  2508. }
  2509. return result;
  2510. }
  2511. private KernelResult MmuUnmap(ulong address, ulong pagesCount)
  2512. {
  2513. return DoMmuOperation(
  2514. address,
  2515. pagesCount,
  2516. 0,
  2517. false,
  2518. KMemoryPermission.None,
  2519. MemoryOperation.Unmap);
  2520. }
  2521. private KernelResult MmuChangePermission(ulong address, ulong pagesCount, KMemoryPermission permission)
  2522. {
  2523. return DoMmuOperation(
  2524. address,
  2525. pagesCount,
  2526. 0,
  2527. false,
  2528. permission,
  2529. MemoryOperation.ChangePermRw);
  2530. }
  2531. private KernelResult DoMmuOperation(
  2532. ulong dstVa,
  2533. ulong pagesCount,
  2534. ulong srcPa,
  2535. bool map,
  2536. KMemoryPermission permission,
  2537. MemoryOperation operation)
  2538. {
  2539. if (map != (operation == MemoryOperation.MapPa))
  2540. {
  2541. throw new ArgumentException(nameof(map) + " value is invalid for this operation.");
  2542. }
  2543. KernelResult result;
  2544. switch (operation)
  2545. {
  2546. case MemoryOperation.MapPa:
  2547. {
  2548. ulong size = pagesCount * PageSize;
  2549. _cpuMemory.Map(dstVa, srcPa - DramMemoryMap.DramBase, size);
  2550. result = KernelResult.Success;
  2551. break;
  2552. }
  2553. case MemoryOperation.Allocate:
  2554. {
  2555. KMemoryRegionManager region = GetMemoryRegionManager();
  2556. result = region.AllocatePages(pagesCount, _aslrDisabled, out KPageList pageList);
  2557. if (result == KernelResult.Success)
  2558. {
  2559. result = MmuMapPages(dstVa, pageList);
  2560. }
  2561. break;
  2562. }
  2563. case MemoryOperation.Unmap:
  2564. {
  2565. ulong size = pagesCount * PageSize;
  2566. _cpuMemory.Unmap(dstVa, size);
  2567. result = KernelResult.Success;
  2568. break;
  2569. }
  2570. case MemoryOperation.ChangePermRw: result = KernelResult.Success; break;
  2571. case MemoryOperation.ChangePermsAndAttributes: result = KernelResult.Success; break;
  2572. default: throw new ArgumentException($"Invalid operation \"{operation}\".");
  2573. }
  2574. return result;
  2575. }
  2576. private KernelResult DoMmuOperation(
  2577. ulong address,
  2578. ulong pagesCount,
  2579. KPageList pageList,
  2580. KMemoryPermission permission,
  2581. MemoryOperation operation)
  2582. {
  2583. if (operation != MemoryOperation.MapVa)
  2584. {
  2585. throw new ArgumentException($"Invalid memory operation \"{operation}\" specified.");
  2586. }
  2587. return MmuMapPages(address, pageList);
  2588. }
  2589. private KMemoryRegionManager GetMemoryRegionManager()
  2590. {
  2591. return _context.MemoryRegions[(int)_memRegion];
  2592. }
  2593. private KernelResult MmuMapPages(ulong address, KPageList pageList)
  2594. {
  2595. foreach (KPageNode pageNode in pageList)
  2596. {
  2597. ulong size = pageNode.PagesCount * PageSize;
  2598. _cpuMemory.Map(address, pageNode.Address - DramMemoryMap.DramBase, size);
  2599. address += size;
  2600. }
  2601. return KernelResult.Success;
  2602. }
  2603. public ulong GetDramAddressFromVa(ulong va)
  2604. {
  2605. return _cpuMemory.GetPhysicalAddress(va);
  2606. }
  2607. public ulong ConvertVaToPa(ulong va)
  2608. {
  2609. if (!TryConvertVaToPa(va, out ulong pa))
  2610. {
  2611. throw new ArgumentException($"Invalid virtual address 0x{va:X} specified.");
  2612. }
  2613. return pa;
  2614. }
  2615. public bool TryConvertVaToPa(ulong va, out ulong pa)
  2616. {
  2617. pa = DramMemoryMap.DramBase + _cpuMemory.GetPhysicalAddress(va);
  2618. return true;
  2619. }
  2620. public static ulong GetDramAddressFromPa(ulong pa)
  2621. {
  2622. return pa - DramMemoryMap.DramBase;
  2623. }
  2624. public long GetMmUsedPages()
  2625. {
  2626. lock (_blocks)
  2627. {
  2628. return BitUtils.DivRoundUp(GetMmUsedSize(), PageSize);
  2629. }
  2630. }
  2631. private long GetMmUsedSize()
  2632. {
  2633. return _blocks.Count * KMemoryBlockSize;
  2634. }
  2635. public bool IsInvalidRegion(ulong address, ulong size)
  2636. {
  2637. return address + size - 1 > GetAddrSpaceBaseAddr() + GetAddrSpaceSize() - 1;
  2638. }
  2639. public bool InsideAddrSpace(ulong address, ulong size)
  2640. {
  2641. return AddrSpaceStart <= address && address + size - 1 <= AddrSpaceEnd - 1;
  2642. }
  2643. public bool InsideAliasRegion(ulong address, ulong size)
  2644. {
  2645. return address + size > AliasRegionStart && AliasRegionEnd > address;
  2646. }
  2647. public bool InsideHeapRegion(ulong address, ulong size)
  2648. {
  2649. return address + size > HeapRegionStart && HeapRegionEnd > address;
  2650. }
  2651. public bool InsideStackRegion(ulong address, ulong size)
  2652. {
  2653. return address + size > StackRegionStart && StackRegionEnd > address;
  2654. }
  2655. public bool OutsideAliasRegion(ulong address, ulong size)
  2656. {
  2657. return AliasRegionStart > address || address + size - 1 > AliasRegionEnd - 1;
  2658. }
  2659. public bool OutsideAddrSpace(ulong address, ulong size)
  2660. {
  2661. return AddrSpaceStart > address || address + size - 1 > AddrSpaceEnd - 1;
  2662. }
  2663. public bool OutsideStackRegion(ulong address, ulong size)
  2664. {
  2665. return StackRegionStart > address || address + size - 1 > StackRegionEnd - 1;
  2666. }
  2667. }
  2668. }