KMemoryManager.cs 104 KB

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