k_page_table.cpp 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190
  1. // Copyright 2020 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include "common/alignment.h"
  5. #include "common/assert.h"
  6. #include "common/scope_exit.h"
  7. #include "core/core.h"
  8. #include "core/hle/kernel/k_address_space_info.h"
  9. #include "core/hle/kernel/k_memory_block.h"
  10. #include "core/hle/kernel/k_memory_block_manager.h"
  11. #include "core/hle/kernel/k_page_linked_list.h"
  12. #include "core/hle/kernel/k_page_table.h"
  13. #include "core/hle/kernel/k_resource_limit.h"
  14. #include "core/hle/kernel/k_scoped_resource_reservation.h"
  15. #include "core/hle/kernel/k_system_control.h"
  16. #include "core/hle/kernel/kernel.h"
  17. #include "core/hle/kernel/process.h"
  18. #include "core/hle/kernel/svc_results.h"
  19. #include "core/memory.h"
  20. namespace Kernel {
  21. namespace {
  22. constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType as_type) {
  23. switch (as_type) {
  24. case FileSys::ProgramAddressSpaceType::Is32Bit:
  25. case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
  26. return 32;
  27. case FileSys::ProgramAddressSpaceType::Is36Bit:
  28. return 36;
  29. case FileSys::ProgramAddressSpaceType::Is39Bit:
  30. return 39;
  31. default:
  32. UNREACHABLE();
  33. return {};
  34. }
  35. }
  36. constexpr u64 GetAddressInRange(const KMemoryInfo& info, VAddr addr) {
  37. if (info.GetAddress() < addr) {
  38. return addr;
  39. }
  40. return info.GetAddress();
  41. }
  42. constexpr std::size_t GetSizeInRange(const KMemoryInfo& info, VAddr start, VAddr end) {
  43. std::size_t size{info.GetSize()};
  44. if (info.GetAddress() < start) {
  45. size -= start - info.GetAddress();
  46. }
  47. if (info.GetEndAddress() > end) {
  48. size -= info.GetEndAddress() - end;
  49. }
  50. return size;
  51. }
  52. } // namespace
  53. KPageTable::KPageTable(Core::System& system) : system{system} {}
  54. ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type,
  55. bool enable_aslr, VAddr code_addr,
  56. std::size_t code_size, KMemoryManager::Pool pool) {
  57. const auto GetSpaceStart = [this](KAddressSpaceInfo::Type type) {
  58. return KAddressSpaceInfo::GetAddressSpaceStart(address_space_width, type);
  59. };
  60. const auto GetSpaceSize = [this](KAddressSpaceInfo::Type type) {
  61. return KAddressSpaceInfo::GetAddressSpaceSize(address_space_width, type);
  62. };
  63. // Set our width and heap/alias sizes
  64. address_space_width = GetAddressSpaceWidthFromType(as_type);
  65. const VAddr start = 0;
  66. const VAddr end{1ULL << address_space_width};
  67. std::size_t alias_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Alias)};
  68. std::size_t heap_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Heap)};
  69. ASSERT(start <= code_addr);
  70. ASSERT(code_addr < code_addr + code_size);
  71. ASSERT(code_addr + code_size - 1 <= end - 1);
  72. // Adjust heap/alias size if we don't have an alias region
  73. if (as_type == FileSys::ProgramAddressSpaceType::Is32BitNoMap) {
  74. heap_region_size += alias_region_size;
  75. alias_region_size = 0;
  76. }
  77. // Set code regions and determine remaining
  78. constexpr std::size_t RegionAlignment{2 * 1024 * 1024};
  79. VAddr process_code_start{};
  80. VAddr process_code_end{};
  81. std::size_t stack_region_size{};
  82. std::size_t kernel_map_region_size{};
  83. if (address_space_width == 39) {
  84. alias_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Alias);
  85. heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap);
  86. stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Stack);
  87. kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type::MapSmall);
  88. code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit);
  89. code_region_end = code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit);
  90. alias_code_region_start = code_region_start;
  91. alias_code_region_end = code_region_end;
  92. process_code_start = Common::AlignDown(code_addr, RegionAlignment);
  93. process_code_end = Common::AlignUp(code_addr + code_size, RegionAlignment);
  94. } else {
  95. stack_region_size = 0;
  96. kernel_map_region_size = 0;
  97. code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::MapSmall);
  98. code_region_end = code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::MapSmall);
  99. stack_region_start = code_region_start;
  100. alias_code_region_start = code_region_start;
  101. alias_code_region_end = GetSpaceStart(KAddressSpaceInfo::Type::MapLarge) +
  102. GetSpaceSize(KAddressSpaceInfo::Type::MapLarge);
  103. stack_region_end = code_region_end;
  104. kernel_map_region_start = code_region_start;
  105. kernel_map_region_end = code_region_end;
  106. process_code_start = code_region_start;
  107. process_code_end = code_region_end;
  108. }
  109. // Set other basic fields
  110. is_aslr_enabled = enable_aslr;
  111. address_space_start = start;
  112. address_space_end = end;
  113. is_kernel = false;
  114. // Determine the region we can place our undetermineds in
  115. VAddr alloc_start{};
  116. std::size_t alloc_size{};
  117. if ((process_code_start - code_region_start) >= (end - process_code_end)) {
  118. alloc_start = code_region_start;
  119. alloc_size = process_code_start - code_region_start;
  120. } else {
  121. alloc_start = process_code_end;
  122. alloc_size = end - process_code_end;
  123. }
  124. const std::size_t needed_size{
  125. (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size)};
  126. if (alloc_size < needed_size) {
  127. UNREACHABLE();
  128. return ResultOutOfMemory;
  129. }
  130. const std::size_t remaining_size{alloc_size - needed_size};
  131. // Determine random placements for each region
  132. std::size_t alias_rnd{}, heap_rnd{}, stack_rnd{}, kmap_rnd{};
  133. if (enable_aslr) {
  134. alias_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
  135. RegionAlignment;
  136. heap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
  137. RegionAlignment;
  138. stack_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
  139. RegionAlignment;
  140. kmap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
  141. RegionAlignment;
  142. }
  143. // Setup heap and alias regions
  144. alias_region_start = alloc_start + alias_rnd;
  145. alias_region_end = alias_region_start + alias_region_size;
  146. heap_region_start = alloc_start + heap_rnd;
  147. heap_region_end = heap_region_start + heap_region_size;
  148. if (alias_rnd <= heap_rnd) {
  149. heap_region_start += alias_region_size;
  150. heap_region_end += alias_region_size;
  151. } else {
  152. alias_region_start += heap_region_size;
  153. alias_region_end += heap_region_size;
  154. }
  155. // Setup stack region
  156. if (stack_region_size) {
  157. stack_region_start = alloc_start + stack_rnd;
  158. stack_region_end = stack_region_start + stack_region_size;
  159. if (alias_rnd < stack_rnd) {
  160. stack_region_start += alias_region_size;
  161. stack_region_end += alias_region_size;
  162. } else {
  163. alias_region_start += stack_region_size;
  164. alias_region_end += stack_region_size;
  165. }
  166. if (heap_rnd < stack_rnd) {
  167. stack_region_start += heap_region_size;
  168. stack_region_end += heap_region_size;
  169. } else {
  170. heap_region_start += stack_region_size;
  171. heap_region_end += stack_region_size;
  172. }
  173. }
  174. // Setup kernel map region
  175. if (kernel_map_region_size) {
  176. kernel_map_region_start = alloc_start + kmap_rnd;
  177. kernel_map_region_end = kernel_map_region_start + kernel_map_region_size;
  178. if (alias_rnd < kmap_rnd) {
  179. kernel_map_region_start += alias_region_size;
  180. kernel_map_region_end += alias_region_size;
  181. } else {
  182. alias_region_start += kernel_map_region_size;
  183. alias_region_end += kernel_map_region_size;
  184. }
  185. if (heap_rnd < kmap_rnd) {
  186. kernel_map_region_start += heap_region_size;
  187. kernel_map_region_end += heap_region_size;
  188. } else {
  189. heap_region_start += kernel_map_region_size;
  190. heap_region_end += kernel_map_region_size;
  191. }
  192. if (stack_region_size) {
  193. if (stack_rnd < kmap_rnd) {
  194. kernel_map_region_start += stack_region_size;
  195. kernel_map_region_end += stack_region_size;
  196. } else {
  197. stack_region_start += kernel_map_region_size;
  198. stack_region_end += kernel_map_region_size;
  199. }
  200. }
  201. }
  202. // Set heap members
  203. current_heap_end = heap_region_start;
  204. max_heap_size = 0;
  205. max_physical_memory_size = 0;
  206. // Ensure that we regions inside our address space
  207. auto IsInAddressSpace = [&](VAddr addr) {
  208. return address_space_start <= addr && addr <= address_space_end;
  209. };
  210. ASSERT(IsInAddressSpace(alias_region_start));
  211. ASSERT(IsInAddressSpace(alias_region_end));
  212. ASSERT(IsInAddressSpace(heap_region_start));
  213. ASSERT(IsInAddressSpace(heap_region_end));
  214. ASSERT(IsInAddressSpace(stack_region_start));
  215. ASSERT(IsInAddressSpace(stack_region_end));
  216. ASSERT(IsInAddressSpace(kernel_map_region_start));
  217. ASSERT(IsInAddressSpace(kernel_map_region_end));
  218. // Ensure that we selected regions that don't overlap
  219. const VAddr alias_start{alias_region_start};
  220. const VAddr alias_last{alias_region_end - 1};
  221. const VAddr heap_start{heap_region_start};
  222. const VAddr heap_last{heap_region_end - 1};
  223. const VAddr stack_start{stack_region_start};
  224. const VAddr stack_last{stack_region_end - 1};
  225. const VAddr kmap_start{kernel_map_region_start};
  226. const VAddr kmap_last{kernel_map_region_end - 1};
  227. ASSERT(alias_last < heap_start || heap_last < alias_start);
  228. ASSERT(alias_last < stack_start || stack_last < alias_start);
  229. ASSERT(alias_last < kmap_start || kmap_last < alias_start);
  230. ASSERT(heap_last < stack_start || stack_last < heap_start);
  231. ASSERT(heap_last < kmap_start || kmap_last < heap_start);
  232. current_heap_addr = heap_region_start;
  233. heap_capacity = 0;
  234. physical_memory_usage = 0;
  235. memory_pool = pool;
  236. page_table_impl.Resize(address_space_width, PageBits);
  237. return InitializeMemoryLayout(start, end);
  238. }
  239. ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryState state,
  240. KMemoryPermission perm) {
  241. std::lock_guard lock{page_table_lock};
  242. const u64 size{num_pages * PageSize};
  243. if (!CanContain(addr, size, state)) {
  244. return ResultInvalidCurrentMemory;
  245. }
  246. if (IsRegionMapped(addr, size)) {
  247. return ResultInvalidCurrentMemory;
  248. }
  249. KPageLinkedList page_linked_list;
  250. CASCADE_CODE(
  251. system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool));
  252. CASCADE_CODE(Operate(addr, num_pages, page_linked_list, OperationType::MapGroup));
  253. block_manager->Update(addr, num_pages, state, perm);
  254. return RESULT_SUCCESS;
  255. }
  256. ResultCode KPageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
  257. std::lock_guard lock{page_table_lock};
  258. const std::size_t num_pages{size / PageSize};
  259. KMemoryState state{};
  260. KMemoryPermission perm{};
  261. CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, src_addr, size, KMemoryState::All,
  262. KMemoryState::Normal, KMemoryPermission::Mask,
  263. KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask,
  264. KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
  265. if (IsRegionMapped(dst_addr, size)) {
  266. return ResultInvalidCurrentMemory;
  267. }
  268. KPageLinkedList page_linked_list;
  269. AddRegionToPages(src_addr, num_pages, page_linked_list);
  270. {
  271. auto block_guard = detail::ScopeExit(
  272. [&] { Operate(src_addr, num_pages, perm, OperationType::ChangePermissions); });
  273. CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None,
  274. OperationType::ChangePermissions));
  275. CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::None));
  276. block_guard.Cancel();
  277. }
  278. block_manager->Update(src_addr, num_pages, state, KMemoryPermission::None,
  279. KMemoryAttribute::Locked);
  280. block_manager->Update(dst_addr, num_pages, KMemoryState::AliasCode);
  281. return RESULT_SUCCESS;
  282. }
  283. ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
  284. std::lock_guard lock{page_table_lock};
  285. if (!size) {
  286. return RESULT_SUCCESS;
  287. }
  288. const std::size_t num_pages{size / PageSize};
  289. CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, src_addr, size, KMemoryState::All,
  290. KMemoryState::Normal, KMemoryPermission::None,
  291. KMemoryPermission::None, KMemoryAttribute::Mask,
  292. KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
  293. KMemoryState state{};
  294. CASCADE_CODE(CheckMemoryState(
  295. &state, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias,
  296. KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
  297. KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
  298. CASCADE_CODE(CheckMemoryState(dst_addr, size, KMemoryState::All, state, KMemoryPermission::None,
  299. KMemoryPermission::None, KMemoryAttribute::Mask,
  300. KMemoryAttribute::None));
  301. CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
  302. block_manager->Update(dst_addr, num_pages, KMemoryState::Free);
  303. block_manager->Update(src_addr, num_pages, KMemoryState::Normal,
  304. KMemoryPermission::ReadAndWrite);
  305. return RESULT_SUCCESS;
  306. }
  307. void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end) {
  308. auto node{page_linked_list.Nodes().begin()};
  309. PAddr map_addr{node->GetAddress()};
  310. std::size_t src_num_pages{node->GetNumPages()};
  311. block_manager->IterateForRange(start, end, [&](const KMemoryInfo& info) {
  312. if (info.state != KMemoryState::Free) {
  313. return;
  314. }
  315. std::size_t dst_num_pages{GetSizeInRange(info, start, end) / PageSize};
  316. VAddr dst_addr{GetAddressInRange(info, start)};
  317. while (dst_num_pages) {
  318. if (!src_num_pages) {
  319. node = std::next(node);
  320. map_addr = node->GetAddress();
  321. src_num_pages = node->GetNumPages();
  322. }
  323. const std::size_t num_pages{std::min(src_num_pages, dst_num_pages)};
  324. Operate(dst_addr, num_pages, KMemoryPermission::ReadAndWrite, OperationType::Map,
  325. map_addr);
  326. dst_addr += num_pages * PageSize;
  327. map_addr += num_pages * PageSize;
  328. src_num_pages -= num_pages;
  329. dst_num_pages -= num_pages;
  330. }
  331. });
  332. }
  333. ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
  334. std::lock_guard lock{page_table_lock};
  335. std::size_t mapped_size{};
  336. const VAddr end_addr{addr + size};
  337. block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
  338. if (info.state != KMemoryState::Free) {
  339. mapped_size += GetSizeInRange(info, addr, end_addr);
  340. }
  341. });
  342. if (mapped_size == size) {
  343. return RESULT_SUCCESS;
  344. }
  345. const std::size_t remaining_size{size - mapped_size};
  346. const std::size_t remaining_pages{remaining_size / PageSize};
  347. // Reserve the memory from the process resource limit.
  348. KScopedResourceReservation memory_reservation(
  349. system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
  350. remaining_size);
  351. if (!memory_reservation.Succeeded()) {
  352. LOG_ERROR(Kernel, "Could not reserve remaining {:X} bytes", remaining_size);
  353. return ResultResourceLimitedExceeded;
  354. }
  355. KPageLinkedList page_linked_list;
  356. CASCADE_CODE(
  357. system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages, memory_pool));
  358. // We succeeded, so commit the memory reservation.
  359. memory_reservation.Commit();
  360. MapPhysicalMemory(page_linked_list, addr, end_addr);
  361. physical_memory_usage += remaining_size;
  362. const std::size_t num_pages{size / PageSize};
  363. block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
  364. KMemoryAttribute::None, KMemoryState::Normal,
  365. KMemoryPermission::ReadAndWrite, KMemoryAttribute::None);
  366. return RESULT_SUCCESS;
  367. }
  368. ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
  369. std::lock_guard lock{page_table_lock};
  370. const VAddr end_addr{addr + size};
  371. ResultCode result{RESULT_SUCCESS};
  372. std::size_t mapped_size{};
  373. // Verify that the region can be unmapped
  374. block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
  375. if (info.state == KMemoryState::Normal) {
  376. if (info.attribute != KMemoryAttribute::None) {
  377. result = ResultInvalidCurrentMemory;
  378. return;
  379. }
  380. mapped_size += GetSizeInRange(info, addr, end_addr);
  381. } else if (info.state != KMemoryState::Free) {
  382. result = ResultInvalidCurrentMemory;
  383. }
  384. });
  385. if (result.IsError()) {
  386. return result;
  387. }
  388. if (!mapped_size) {
  389. return RESULT_SUCCESS;
  390. }
  391. CASCADE_CODE(UnmapMemory(addr, size));
  392. auto process{system.Kernel().CurrentProcess()};
  393. process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
  394. physical_memory_usage -= mapped_size;
  395. return RESULT_SUCCESS;
  396. }
  397. ResultCode KPageTable::UnmapMemory(VAddr addr, std::size_t size) {
  398. std::lock_guard lock{page_table_lock};
  399. const VAddr end_addr{addr + size};
  400. ResultCode result{RESULT_SUCCESS};
  401. KPageLinkedList page_linked_list;
  402. // Unmap each region within the range
  403. block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
  404. if (info.state == KMemoryState::Normal) {
  405. const std::size_t block_size{GetSizeInRange(info, addr, end_addr)};
  406. const std::size_t block_num_pages{block_size / PageSize};
  407. const VAddr block_addr{GetAddressInRange(info, addr)};
  408. AddRegionToPages(block_addr, block_size / PageSize, page_linked_list);
  409. if (result = Operate(block_addr, block_num_pages, KMemoryPermission::None,
  410. OperationType::Unmap);
  411. result.IsError()) {
  412. return;
  413. }
  414. }
  415. });
  416. if (result.IsError()) {
  417. return result;
  418. }
  419. const std::size_t num_pages{size / PageSize};
  420. system.Kernel().MemoryManager().Free(page_linked_list, num_pages, memory_pool);
  421. block_manager->Update(addr, num_pages, KMemoryState::Free);
  422. return RESULT_SUCCESS;
  423. }
  424. ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
  425. std::lock_guard lock{page_table_lock};
  426. KMemoryState src_state{};
  427. CASCADE_CODE(CheckMemoryState(
  428. &src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
  429. KMemoryState::FlagCanAlias, KMemoryPermission::Mask, KMemoryPermission::ReadAndWrite,
  430. KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
  431. if (IsRegionMapped(dst_addr, size)) {
  432. return ResultInvalidCurrentMemory;
  433. }
  434. KPageLinkedList page_linked_list;
  435. const std::size_t num_pages{size / PageSize};
  436. AddRegionToPages(src_addr, num_pages, page_linked_list);
  437. {
  438. auto block_guard = detail::ScopeExit([&] {
  439. Operate(src_addr, num_pages, KMemoryPermission::ReadAndWrite,
  440. OperationType::ChangePermissions);
  441. });
  442. CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None,
  443. OperationType::ChangePermissions));
  444. CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::ReadAndWrite));
  445. block_guard.Cancel();
  446. }
  447. block_manager->Update(src_addr, num_pages, src_state, KMemoryPermission::None,
  448. KMemoryAttribute::Locked);
  449. block_manager->Update(dst_addr, num_pages, KMemoryState::Stack,
  450. KMemoryPermission::ReadAndWrite);
  451. return RESULT_SUCCESS;
  452. }
  453. ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
  454. std::lock_guard lock{page_table_lock};
  455. KMemoryState src_state{};
  456. CASCADE_CODE(CheckMemoryState(
  457. &src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
  458. KMemoryState::FlagCanAlias, KMemoryPermission::Mask, KMemoryPermission::None,
  459. KMemoryAttribute::Mask, KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
  460. KMemoryPermission dst_perm{};
  461. CASCADE_CODE(CheckMemoryState(nullptr, &dst_perm, nullptr, dst_addr, size, KMemoryState::All,
  462. KMemoryState::Stack, KMemoryPermission::None,
  463. KMemoryPermission::None, KMemoryAttribute::Mask,
  464. KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
  465. KPageLinkedList src_pages;
  466. KPageLinkedList dst_pages;
  467. const std::size_t num_pages{size / PageSize};
  468. AddRegionToPages(src_addr, num_pages, src_pages);
  469. AddRegionToPages(dst_addr, num_pages, dst_pages);
  470. if (!dst_pages.IsEqual(src_pages)) {
  471. return ResultInvalidMemoryRange;
  472. }
  473. {
  474. auto block_guard = detail::ScopeExit([&] { MapPages(dst_addr, dst_pages, dst_perm); });
  475. CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
  476. CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::ReadAndWrite,
  477. OperationType::ChangePermissions));
  478. block_guard.Cancel();
  479. }
  480. block_manager->Update(src_addr, num_pages, src_state, KMemoryPermission::ReadAndWrite);
  481. block_manager->Update(dst_addr, num_pages, KMemoryState::Free);
  482. return RESULT_SUCCESS;
  483. }
  484. ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
  485. KMemoryPermission perm) {
  486. VAddr cur_addr{addr};
  487. for (const auto& node : page_linked_list.Nodes()) {
  488. if (const auto result{
  489. Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())};
  490. result.IsError()) {
  491. const std::size_t num_pages{(addr - cur_addr) / PageSize};
  492. ASSERT(Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap)
  493. .IsSuccess());
  494. return result;
  495. }
  496. cur_addr += node.GetNumPages() * PageSize;
  497. }
  498. return RESULT_SUCCESS;
  499. }
  500. ResultCode KPageTable::MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
  501. KMemoryPermission perm) {
  502. std::lock_guard lock{page_table_lock};
  503. const std::size_t num_pages{page_linked_list.GetNumPages()};
  504. const std::size_t size{num_pages * PageSize};
  505. if (!CanContain(addr, size, state)) {
  506. return ResultInvalidCurrentMemory;
  507. }
  508. if (IsRegionMapped(addr, num_pages * PageSize)) {
  509. return ResultInvalidCurrentMemory;
  510. }
  511. CASCADE_CODE(MapPages(addr, page_linked_list, perm));
  512. block_manager->Update(addr, num_pages, state, perm);
  513. return RESULT_SUCCESS;
  514. }
  515. ResultCode KPageTable::SetCodeMemoryPermission(VAddr addr, std::size_t size,
  516. KMemoryPermission perm) {
  517. std::lock_guard lock{page_table_lock};
  518. KMemoryState prev_state{};
  519. KMemoryPermission prev_perm{};
  520. CASCADE_CODE(CheckMemoryState(
  521. &prev_state, &prev_perm, nullptr, addr, size, KMemoryState::FlagCode,
  522. KMemoryState::FlagCode, KMemoryPermission::None, KMemoryPermission::None,
  523. KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
  524. KMemoryState state{prev_state};
  525. // Ensure state is mutable if permission allows write
  526. if ((perm & KMemoryPermission::Write) != KMemoryPermission::None) {
  527. if (prev_state == KMemoryState::Code) {
  528. state = KMemoryState::CodeData;
  529. } else if (prev_state == KMemoryState::AliasCode) {
  530. state = KMemoryState::AliasCodeData;
  531. } else {
  532. UNREACHABLE();
  533. }
  534. }
  535. // Return early if there is nothing to change
  536. if (state == prev_state && perm == prev_perm) {
  537. return RESULT_SUCCESS;
  538. }
  539. if ((prev_perm & KMemoryPermission::Execute) != (perm & KMemoryPermission::Execute)) {
  540. // Memory execution state is changing, invalidate CPU cache range
  541. system.InvalidateCpuInstructionCacheRange(addr, size);
  542. }
  543. const std::size_t num_pages{size / PageSize};
  544. const OperationType operation{(perm & KMemoryPermission::Execute) != KMemoryPermission::None
  545. ? OperationType::ChangePermissionsAndRefresh
  546. : OperationType::ChangePermissions};
  547. CASCADE_CODE(Operate(addr, num_pages, perm, operation));
  548. block_manager->Update(addr, num_pages, state, perm);
  549. return RESULT_SUCCESS;
  550. }
  551. KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) {
  552. std::lock_guard lock{page_table_lock};
  553. return block_manager->FindBlock(addr).GetMemoryInfo();
  554. }
  555. KMemoryInfo KPageTable::QueryInfo(VAddr addr) {
  556. if (!Contains(addr, 1)) {
  557. return {address_space_end, 0 - address_space_end, KMemoryState::Inaccessible,
  558. KMemoryPermission::None, KMemoryAttribute::None, KMemoryPermission::None};
  559. }
  560. return QueryInfoImpl(addr);
  561. }
  562. ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) {
  563. std::lock_guard lock{page_table_lock};
  564. KMemoryState state{};
  565. KMemoryAttribute attribute{};
  566. CASCADE_CODE(CheckMemoryState(
  567. &state, nullptr, &attribute, addr, size,
  568. KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
  569. KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryPermission::Mask,
  570. KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask, KMemoryAttribute::None,
  571. KMemoryAttribute::IpcAndDeviceMapped));
  572. block_manager->Update(addr, size / PageSize, state, perm, attribute | KMemoryAttribute::Locked);
  573. return RESULT_SUCCESS;
  574. }
  575. ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
  576. std::lock_guard lock{page_table_lock};
  577. KMemoryState state{};
  578. CASCADE_CODE(
  579. CheckMemoryState(&state, nullptr, nullptr, addr, size,
  580. KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
  581. KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
  582. KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::Mask,
  583. KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
  584. block_manager->Update(addr, size / PageSize, state, KMemoryPermission::ReadAndWrite);
  585. return RESULT_SUCCESS;
  586. }
  587. ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask,
  588. KMemoryAttribute value) {
  589. std::lock_guard lock{page_table_lock};
  590. KMemoryState state{};
  591. KMemoryPermission perm{};
  592. KMemoryAttribute attribute{};
  593. CASCADE_CODE(CheckMemoryState(
  594. &state, &perm, &attribute, addr, size, KMemoryState::FlagCanChangeAttribute,
  595. KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
  596. KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None,
  597. KMemoryAttribute::DeviceSharedAndUncached));
  598. attribute = attribute & ~mask;
  599. attribute = attribute | (mask & value);
  600. block_manager->Update(addr, size / PageSize, state, perm, attribute);
  601. return RESULT_SUCCESS;
  602. }
  603. ResultCode KPageTable::SetHeapCapacity(std::size_t new_heap_capacity) {
  604. std::lock_guard lock{page_table_lock};
  605. heap_capacity = new_heap_capacity;
  606. return RESULT_SUCCESS;
  607. }
  608. ResultVal<VAddr> KPageTable::SetHeapSize(std::size_t size) {
  609. if (size > heap_region_end - heap_region_start) {
  610. return ResultOutOfMemory;
  611. }
  612. const u64 previous_heap_size{GetHeapSize()};
  613. UNIMPLEMENTED_IF_MSG(previous_heap_size > size, "Heap shrink is unimplemented");
  614. // Increase the heap size
  615. {
  616. std::lock_guard lock{page_table_lock};
  617. const u64 delta{size - previous_heap_size};
  618. // Reserve memory for the heap extension.
  619. KScopedResourceReservation memory_reservation(
  620. system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
  621. delta);
  622. if (!memory_reservation.Succeeded()) {
  623. LOG_ERROR(Kernel, "Could not reserve heap extension of size {:X} bytes", delta);
  624. return ResultResourceLimitedExceeded;
  625. }
  626. KPageLinkedList page_linked_list;
  627. const std::size_t num_pages{delta / PageSize};
  628. CASCADE_CODE(
  629. system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool));
  630. if (IsRegionMapped(current_heap_addr, delta)) {
  631. return ResultInvalidCurrentMemory;
  632. }
  633. CASCADE_CODE(
  634. Operate(current_heap_addr, num_pages, page_linked_list, OperationType::MapGroup));
  635. // Succeeded in allocation, commit the resource reservation
  636. memory_reservation.Commit();
  637. block_manager->Update(current_heap_addr, num_pages, KMemoryState::Normal,
  638. KMemoryPermission::ReadAndWrite);
  639. current_heap_addr = heap_region_start + size;
  640. }
  641. return MakeResult<VAddr>(heap_region_start);
  642. }
  643. ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
  644. bool is_map_only, VAddr region_start,
  645. std::size_t region_num_pages, KMemoryState state,
  646. KMemoryPermission perm, PAddr map_addr) {
  647. std::lock_guard lock{page_table_lock};
  648. if (!CanContain(region_start, region_num_pages * PageSize, state)) {
  649. return ResultInvalidCurrentMemory;
  650. }
  651. if (region_num_pages <= needed_num_pages) {
  652. return ResultOutOfMemory;
  653. }
  654. const VAddr addr{
  655. AllocateVirtualMemory(region_start, region_num_pages, needed_num_pages, align)};
  656. if (!addr) {
  657. return ResultOutOfMemory;
  658. }
  659. if (is_map_only) {
  660. CASCADE_CODE(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
  661. } else {
  662. KPageLinkedList page_group;
  663. CASCADE_CODE(
  664. system.Kernel().MemoryManager().Allocate(page_group, needed_num_pages, memory_pool));
  665. CASCADE_CODE(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup));
  666. }
  667. block_manager->Update(addr, needed_num_pages, state, perm);
  668. return MakeResult<VAddr>(addr);
  669. }
  670. ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
  671. std::lock_guard lock{page_table_lock};
  672. KMemoryPermission perm{};
  673. if (const ResultCode result{CheckMemoryState(
  674. nullptr, &perm, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
  675. KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
  676. KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None,
  677. KMemoryAttribute::DeviceSharedAndUncached)};
  678. result.IsError()) {
  679. return result;
  680. }
  681. block_manager->UpdateLock(
  682. addr, size / PageSize,
  683. [](KMemoryBlockManager::iterator block, KMemoryPermission perm) {
  684. block->ShareToDevice(perm);
  685. },
  686. perm);
  687. return RESULT_SUCCESS;
  688. }
  689. ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) {
  690. std::lock_guard lock{page_table_lock};
  691. KMemoryPermission perm{};
  692. if (const ResultCode result{CheckMemoryState(
  693. nullptr, &perm, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
  694. KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
  695. KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None,
  696. KMemoryAttribute::DeviceSharedAndUncached)};
  697. result.IsError()) {
  698. return result;
  699. }
  700. block_manager->UpdateLock(
  701. addr, size / PageSize,
  702. [](KMemoryBlockManager::iterator block, KMemoryPermission perm) {
  703. block->UnshareToDevice(perm);
  704. },
  705. perm);
  706. return RESULT_SUCCESS;
  707. }
  708. ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
  709. block_manager = std::make_unique<KMemoryBlockManager>(start, end);
  710. return RESULT_SUCCESS;
  711. }
  712. bool KPageTable::IsRegionMapped(VAddr address, u64 size) {
  713. return CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free,
  714. KMemoryPermission::Mask, KMemoryPermission::None,
  715. KMemoryAttribute::Mask, KMemoryAttribute::None,
  716. KMemoryAttribute::IpcAndDeviceMapped)
  717. .IsError();
  718. }
  719. bool KPageTable::IsRegionContiguous(VAddr addr, u64 size) const {
  720. auto start_ptr = system.Memory().GetPointer(addr);
  721. for (u64 offset{}; offset < size; offset += PageSize) {
  722. if (start_ptr != system.Memory().GetPointer(addr + offset)) {
  723. return false;
  724. }
  725. start_ptr += PageSize;
  726. }
  727. return true;
  728. }
  729. void KPageTable::AddRegionToPages(VAddr start, std::size_t num_pages,
  730. KPageLinkedList& page_linked_list) {
  731. VAddr addr{start};
  732. while (addr < start + (num_pages * PageSize)) {
  733. const PAddr paddr{GetPhysicalAddr(addr)};
  734. if (!paddr) {
  735. UNREACHABLE();
  736. }
  737. page_linked_list.AddBlock(paddr, 1);
  738. addr += PageSize;
  739. }
  740. }
  741. VAddr KPageTable::AllocateVirtualMemory(VAddr start, std::size_t region_num_pages,
  742. u64 needed_num_pages, std::size_t align) {
  743. if (is_aslr_enabled) {
  744. UNIMPLEMENTED();
  745. }
  746. return block_manager->FindFreeArea(start, region_num_pages, needed_num_pages, align, 0,
  747. IsKernel() ? 1 : 4);
  748. }
  749. ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLinkedList& page_group,
  750. OperationType operation) {
  751. std::lock_guard lock{page_table_lock};
  752. ASSERT(Common::IsAligned(addr, PageSize));
  753. ASSERT(num_pages > 0);
  754. ASSERT(num_pages == page_group.GetNumPages());
  755. for (const auto& node : page_group.Nodes()) {
  756. const std::size_t size{node.GetNumPages() * PageSize};
  757. switch (operation) {
  758. case OperationType::MapGroup:
  759. system.Memory().MapMemoryRegion(page_table_impl, addr, size, node.GetAddress());
  760. break;
  761. default:
  762. UNREACHABLE();
  763. }
  764. addr += size;
  765. }
  766. return RESULT_SUCCESS;
  767. }
  768. ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm,
  769. OperationType operation, PAddr map_addr) {
  770. std::lock_guard lock{page_table_lock};
  771. ASSERT(num_pages > 0);
  772. ASSERT(Common::IsAligned(addr, PageSize));
  773. ASSERT(ContainsPages(addr, num_pages));
  774. switch (operation) {
  775. case OperationType::Unmap:
  776. system.Memory().UnmapRegion(page_table_impl, addr, num_pages * PageSize);
  777. break;
  778. case OperationType::Map: {
  779. ASSERT(map_addr);
  780. ASSERT(Common::IsAligned(map_addr, PageSize));
  781. system.Memory().MapMemoryRegion(page_table_impl, addr, num_pages * PageSize, map_addr);
  782. break;
  783. }
  784. case OperationType::ChangePermissions:
  785. case OperationType::ChangePermissionsAndRefresh:
  786. break;
  787. default:
  788. UNREACHABLE();
  789. }
  790. return RESULT_SUCCESS;
  791. }
  792. constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
  793. switch (state) {
  794. case KMemoryState::Free:
  795. case KMemoryState::Kernel:
  796. return address_space_start;
  797. case KMemoryState::Normal:
  798. return heap_region_start;
  799. case KMemoryState::Ipc:
  800. case KMemoryState::NonSecureIpc:
  801. case KMemoryState::NonDeviceIpc:
  802. return alias_region_start;
  803. case KMemoryState::Stack:
  804. return stack_region_start;
  805. case KMemoryState::Io:
  806. case KMemoryState::Static:
  807. case KMemoryState::ThreadLocal:
  808. return kernel_map_region_start;
  809. case KMemoryState::Shared:
  810. case KMemoryState::AliasCode:
  811. case KMemoryState::AliasCodeData:
  812. case KMemoryState::Transferred:
  813. case KMemoryState::SharedTransferred:
  814. case KMemoryState::SharedCode:
  815. case KMemoryState::GeneratedCode:
  816. case KMemoryState::CodeOut:
  817. return alias_code_region_start;
  818. case KMemoryState::Code:
  819. case KMemoryState::CodeData:
  820. return code_region_start;
  821. default:
  822. UNREACHABLE();
  823. return {};
  824. }
  825. }
  826. constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
  827. switch (state) {
  828. case KMemoryState::Free:
  829. case KMemoryState::Kernel:
  830. return address_space_end - address_space_start;
  831. case KMemoryState::Normal:
  832. return heap_region_end - heap_region_start;
  833. case KMemoryState::Ipc:
  834. case KMemoryState::NonSecureIpc:
  835. case KMemoryState::NonDeviceIpc:
  836. return alias_region_end - alias_region_start;
  837. case KMemoryState::Stack:
  838. return stack_region_end - stack_region_start;
  839. case KMemoryState::Io:
  840. case KMemoryState::Static:
  841. case KMemoryState::ThreadLocal:
  842. return kernel_map_region_end - kernel_map_region_start;
  843. case KMemoryState::Shared:
  844. case KMemoryState::AliasCode:
  845. case KMemoryState::AliasCodeData:
  846. case KMemoryState::Transferred:
  847. case KMemoryState::SharedTransferred:
  848. case KMemoryState::SharedCode:
  849. case KMemoryState::GeneratedCode:
  850. case KMemoryState::CodeOut:
  851. return alias_code_region_end - alias_code_region_start;
  852. case KMemoryState::Code:
  853. case KMemoryState::CodeData:
  854. return code_region_end - code_region_start;
  855. default:
  856. UNREACHABLE();
  857. return {};
  858. }
  859. }
  860. constexpr bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) const {
  861. const VAddr end{addr + size};
  862. const VAddr last{end - 1};
  863. const VAddr region_start{GetRegionAddress(state)};
  864. const std::size_t region_size{GetRegionSize(state)};
  865. const bool is_in_region{region_start <= addr && addr < end &&
  866. last <= region_start + region_size - 1};
  867. const bool is_in_heap{!(end <= heap_region_start || heap_region_end <= addr)};
  868. const bool is_in_alias{!(end <= alias_region_start || alias_region_end <= addr)};
  869. switch (state) {
  870. case KMemoryState::Free:
  871. case KMemoryState::Kernel:
  872. return is_in_region;
  873. case KMemoryState::Io:
  874. case KMemoryState::Static:
  875. case KMemoryState::Code:
  876. case KMemoryState::CodeData:
  877. case KMemoryState::Shared:
  878. case KMemoryState::AliasCode:
  879. case KMemoryState::AliasCodeData:
  880. case KMemoryState::Stack:
  881. case KMemoryState::ThreadLocal:
  882. case KMemoryState::Transferred:
  883. case KMemoryState::SharedTransferred:
  884. case KMemoryState::SharedCode:
  885. case KMemoryState::GeneratedCode:
  886. case KMemoryState::CodeOut:
  887. return is_in_region && !is_in_heap && !is_in_alias;
  888. case KMemoryState::Normal:
  889. ASSERT(is_in_heap);
  890. return is_in_region && !is_in_alias;
  891. case KMemoryState::Ipc:
  892. case KMemoryState::NonSecureIpc:
  893. case KMemoryState::NonDeviceIpc:
  894. ASSERT(is_in_alias);
  895. return is_in_region && !is_in_heap;
  896. default:
  897. return false;
  898. }
  899. }
  900. constexpr ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
  901. KMemoryState state, KMemoryPermission perm_mask,
  902. KMemoryPermission perm,
  903. KMemoryAttribute attr_mask,
  904. KMemoryAttribute attr) const {
  905. // Validate the states match expectation
  906. if ((info.state & state_mask) != state) {
  907. return ResultInvalidCurrentMemory;
  908. }
  909. if ((info.perm & perm_mask) != perm) {
  910. return ResultInvalidCurrentMemory;
  911. }
  912. if ((info.attribute & attr_mask) != attr) {
  913. return ResultInvalidCurrentMemory;
  914. }
  915. return RESULT_SUCCESS;
  916. }
  917. ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
  918. KMemoryAttribute* out_attr, VAddr addr, std::size_t size,
  919. KMemoryState state_mask, KMemoryState state,
  920. KMemoryPermission perm_mask, KMemoryPermission perm,
  921. KMemoryAttribute attr_mask, KMemoryAttribute attr,
  922. KMemoryAttribute ignore_attr) {
  923. std::lock_guard lock{page_table_lock};
  924. // Get information about the first block
  925. const VAddr last_addr{addr + size - 1};
  926. KMemoryBlockManager::const_iterator it{block_manager->FindIterator(addr)};
  927. KMemoryInfo info{it->GetMemoryInfo()};
  928. // Validate all blocks in the range have correct state
  929. const KMemoryState first_state{info.state};
  930. const KMemoryPermission first_perm{info.perm};
  931. const KMemoryAttribute first_attr{info.attribute};
  932. while (true) {
  933. // Validate the current block
  934. if (!(info.state == first_state)) {
  935. return ResultInvalidCurrentMemory;
  936. }
  937. if (!(info.perm == first_perm)) {
  938. return ResultInvalidCurrentMemory;
  939. }
  940. if (!((info.attribute | static_cast<KMemoryAttribute>(ignore_attr)) ==
  941. (first_attr | static_cast<KMemoryAttribute>(ignore_attr)))) {
  942. return ResultInvalidCurrentMemory;
  943. }
  944. // Validate against the provided masks
  945. CASCADE_CODE(CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
  946. // Break once we're done
  947. if (last_addr <= info.GetLastAddress()) {
  948. break;
  949. }
  950. // Advance our iterator
  951. it++;
  952. ASSERT(it != block_manager->cend());
  953. info = it->GetMemoryInfo();
  954. }
  955. // Write output state
  956. if (out_state) {
  957. *out_state = first_state;
  958. }
  959. if (out_perm) {
  960. *out_perm = first_perm;
  961. }
  962. if (out_attr) {
  963. *out_attr = first_attr & static_cast<KMemoryAttribute>(~ignore_attr);
  964. }
  965. return RESULT_SUCCESS;
  966. }
  967. } // namespace Kernel