k_page_table.cpp 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472
  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/literals.h"
  7. #include "common/scope_exit.h"
  8. #include "core/core.h"
  9. #include "core/hle/kernel/k_address_space_info.h"
  10. #include "core/hle/kernel/k_memory_block.h"
  11. #include "core/hle/kernel/k_memory_block_manager.h"
  12. #include "core/hle/kernel/k_page_linked_list.h"
  13. #include "core/hle/kernel/k_page_table.h"
  14. #include "core/hle/kernel/k_process.h"
  15. #include "core/hle/kernel/k_resource_limit.h"
  16. #include "core/hle/kernel/k_scoped_resource_reservation.h"
  17. #include "core/hle/kernel/k_system_control.h"
  18. #include "core/hle/kernel/kernel.h"
  19. #include "core/hle/kernel/svc_results.h"
  20. #include "core/memory.h"
  21. namespace Kernel {
  22. namespace {
  23. using namespace Common::Literals;
  24. constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType as_type) {
  25. switch (as_type) {
  26. case FileSys::ProgramAddressSpaceType::Is32Bit:
  27. case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
  28. return 32;
  29. case FileSys::ProgramAddressSpaceType::Is36Bit:
  30. return 36;
  31. case FileSys::ProgramAddressSpaceType::Is39Bit:
  32. return 39;
  33. default:
  34. UNREACHABLE();
  35. return {};
  36. }
  37. }
  38. constexpr u64 GetAddressInRange(const KMemoryInfo& info, VAddr addr) {
  39. if (info.GetAddress() < addr) {
  40. return addr;
  41. }
  42. return info.GetAddress();
  43. }
  44. constexpr std::size_t GetSizeInRange(const KMemoryInfo& info, VAddr start, VAddr end) {
  45. std::size_t size{info.GetSize()};
  46. if (info.GetAddress() < start) {
  47. size -= start - info.GetAddress();
  48. }
  49. if (info.GetEndAddress() > end) {
  50. size -= info.GetEndAddress() - end;
  51. }
  52. return size;
  53. }
  54. } // namespace
  55. KPageTable::KPageTable(Core::System& system_) : system{system_} {}
  56. KPageTable::~KPageTable() = default;
  57. ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type,
  58. bool enable_aslr, VAddr code_addr,
  59. std::size_t code_size, KMemoryManager::Pool pool) {
  60. const auto GetSpaceStart = [this](KAddressSpaceInfo::Type type) {
  61. return KAddressSpaceInfo::GetAddressSpaceStart(address_space_width, type);
  62. };
  63. const auto GetSpaceSize = [this](KAddressSpaceInfo::Type type) {
  64. return KAddressSpaceInfo::GetAddressSpaceSize(address_space_width, type);
  65. };
  66. // Set our width and heap/alias sizes
  67. address_space_width = GetAddressSpaceWidthFromType(as_type);
  68. const VAddr start = 0;
  69. const VAddr end{1ULL << address_space_width};
  70. std::size_t alias_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Alias)};
  71. std::size_t heap_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Heap)};
  72. ASSERT(start <= code_addr);
  73. ASSERT(code_addr < code_addr + code_size);
  74. ASSERT(code_addr + code_size - 1 <= end - 1);
  75. // Adjust heap/alias size if we don't have an alias region
  76. if (as_type == FileSys::ProgramAddressSpaceType::Is32BitNoMap) {
  77. heap_region_size += alias_region_size;
  78. alias_region_size = 0;
  79. }
  80. // Set code regions and determine remaining
  81. constexpr std::size_t RegionAlignment{2_MiB};
  82. VAddr process_code_start{};
  83. VAddr process_code_end{};
  84. std::size_t stack_region_size{};
  85. std::size_t kernel_map_region_size{};
  86. if (address_space_width == 39) {
  87. alias_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Alias);
  88. heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Heap);
  89. stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type::Stack);
  90. kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type::MapSmall);
  91. code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::Map39Bit);
  92. code_region_end = code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit);
  93. alias_code_region_start = code_region_start;
  94. alias_code_region_end = code_region_end;
  95. process_code_start = Common::AlignDown(code_addr, RegionAlignment);
  96. process_code_end = Common::AlignUp(code_addr + code_size, RegionAlignment);
  97. } else {
  98. stack_region_size = 0;
  99. kernel_map_region_size = 0;
  100. code_region_start = GetSpaceStart(KAddressSpaceInfo::Type::MapSmall);
  101. code_region_end = code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::MapSmall);
  102. stack_region_start = code_region_start;
  103. alias_code_region_start = code_region_start;
  104. alias_code_region_end = GetSpaceStart(KAddressSpaceInfo::Type::MapLarge) +
  105. GetSpaceSize(KAddressSpaceInfo::Type::MapLarge);
  106. stack_region_end = code_region_end;
  107. kernel_map_region_start = code_region_start;
  108. kernel_map_region_end = code_region_end;
  109. process_code_start = code_region_start;
  110. process_code_end = code_region_end;
  111. }
  112. // Set other basic fields
  113. is_aslr_enabled = enable_aslr;
  114. address_space_start = start;
  115. address_space_end = end;
  116. is_kernel = false;
  117. // Determine the region we can place our undetermineds in
  118. VAddr alloc_start{};
  119. std::size_t alloc_size{};
  120. if ((process_code_start - code_region_start) >= (end - process_code_end)) {
  121. alloc_start = code_region_start;
  122. alloc_size = process_code_start - code_region_start;
  123. } else {
  124. alloc_start = process_code_end;
  125. alloc_size = end - process_code_end;
  126. }
  127. const std::size_t needed_size{
  128. (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size)};
  129. if (alloc_size < needed_size) {
  130. UNREACHABLE();
  131. return ResultOutOfMemory;
  132. }
  133. const std::size_t remaining_size{alloc_size - needed_size};
  134. // Determine random placements for each region
  135. std::size_t alias_rnd{}, heap_rnd{}, stack_rnd{}, kmap_rnd{};
  136. if (enable_aslr) {
  137. alias_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
  138. RegionAlignment;
  139. heap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
  140. RegionAlignment;
  141. stack_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
  142. RegionAlignment;
  143. kmap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
  144. RegionAlignment;
  145. }
  146. // Setup heap and alias regions
  147. alias_region_start = alloc_start + alias_rnd;
  148. alias_region_end = alias_region_start + alias_region_size;
  149. heap_region_start = alloc_start + heap_rnd;
  150. heap_region_end = heap_region_start + heap_region_size;
  151. if (alias_rnd <= heap_rnd) {
  152. heap_region_start += alias_region_size;
  153. heap_region_end += alias_region_size;
  154. } else {
  155. alias_region_start += heap_region_size;
  156. alias_region_end += heap_region_size;
  157. }
  158. // Setup stack region
  159. if (stack_region_size) {
  160. stack_region_start = alloc_start + stack_rnd;
  161. stack_region_end = stack_region_start + stack_region_size;
  162. if (alias_rnd < stack_rnd) {
  163. stack_region_start += alias_region_size;
  164. stack_region_end += alias_region_size;
  165. } else {
  166. alias_region_start += stack_region_size;
  167. alias_region_end += stack_region_size;
  168. }
  169. if (heap_rnd < stack_rnd) {
  170. stack_region_start += heap_region_size;
  171. stack_region_end += heap_region_size;
  172. } else {
  173. heap_region_start += stack_region_size;
  174. heap_region_end += stack_region_size;
  175. }
  176. }
  177. // Setup kernel map region
  178. if (kernel_map_region_size) {
  179. kernel_map_region_start = alloc_start + kmap_rnd;
  180. kernel_map_region_end = kernel_map_region_start + kernel_map_region_size;
  181. if (alias_rnd < kmap_rnd) {
  182. kernel_map_region_start += alias_region_size;
  183. kernel_map_region_end += alias_region_size;
  184. } else {
  185. alias_region_start += kernel_map_region_size;
  186. alias_region_end += kernel_map_region_size;
  187. }
  188. if (heap_rnd < kmap_rnd) {
  189. kernel_map_region_start += heap_region_size;
  190. kernel_map_region_end += heap_region_size;
  191. } else {
  192. heap_region_start += kernel_map_region_size;
  193. heap_region_end += kernel_map_region_size;
  194. }
  195. if (stack_region_size) {
  196. if (stack_rnd < kmap_rnd) {
  197. kernel_map_region_start += stack_region_size;
  198. kernel_map_region_end += stack_region_size;
  199. } else {
  200. stack_region_start += kernel_map_region_size;
  201. stack_region_end += kernel_map_region_size;
  202. }
  203. }
  204. }
  205. // Set heap members
  206. current_heap_end = heap_region_start;
  207. max_heap_size = 0;
  208. max_physical_memory_size = 0;
  209. // Ensure that we regions inside our address space
  210. auto IsInAddressSpace = [&](VAddr addr) {
  211. return address_space_start <= addr && addr <= address_space_end;
  212. };
  213. ASSERT(IsInAddressSpace(alias_region_start));
  214. ASSERT(IsInAddressSpace(alias_region_end));
  215. ASSERT(IsInAddressSpace(heap_region_start));
  216. ASSERT(IsInAddressSpace(heap_region_end));
  217. ASSERT(IsInAddressSpace(stack_region_start));
  218. ASSERT(IsInAddressSpace(stack_region_end));
  219. ASSERT(IsInAddressSpace(kernel_map_region_start));
  220. ASSERT(IsInAddressSpace(kernel_map_region_end));
  221. // Ensure that we selected regions that don't overlap
  222. const VAddr alias_start{alias_region_start};
  223. const VAddr alias_last{alias_region_end - 1};
  224. const VAddr heap_start{heap_region_start};
  225. const VAddr heap_last{heap_region_end - 1};
  226. const VAddr stack_start{stack_region_start};
  227. const VAddr stack_last{stack_region_end - 1};
  228. const VAddr kmap_start{kernel_map_region_start};
  229. const VAddr kmap_last{kernel_map_region_end - 1};
  230. ASSERT(alias_last < heap_start || heap_last < alias_start);
  231. ASSERT(alias_last < stack_start || stack_last < alias_start);
  232. ASSERT(alias_last < kmap_start || kmap_last < alias_start);
  233. ASSERT(heap_last < stack_start || stack_last < heap_start);
  234. ASSERT(heap_last < kmap_start || kmap_last < heap_start);
  235. current_heap_end = heap_region_start;
  236. max_heap_size = 0;
  237. mapped_physical_memory_size = 0;
  238. memory_pool = pool;
  239. page_table_impl.Resize(address_space_width, PageBits);
  240. return InitializeMemoryLayout(start, end);
  241. }
  242. ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryState state,
  243. KMemoryPermission perm) {
  244. const u64 size{num_pages * PageSize};
  245. // Validate the mapping request.
  246. R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory);
  247. // Lock the table.
  248. std::lock_guard lock{page_table_lock};
  249. // Verify that the destination memory is unmapped.
  250. R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free,
  251. KMemoryPermission::None, KMemoryPermission::None,
  252. KMemoryAttribute::None, KMemoryAttribute::None));
  253. KPageLinkedList page_linked_list;
  254. R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool,
  255. allocation_option));
  256. R_TRY(Operate(addr, num_pages, page_linked_list, OperationType::MapGroup));
  257. block_manager->Update(addr, num_pages, state, perm);
  258. return ResultSuccess;
  259. }
  260. ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
  261. std::lock_guard lock{page_table_lock};
  262. const std::size_t num_pages{size / PageSize};
  263. KMemoryState state{};
  264. KMemoryPermission perm{};
  265. CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, nullptr, src_addr, size,
  266. KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All,
  267. KMemoryPermission::UserReadWrite, KMemoryAttribute::Mask,
  268. KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
  269. if (IsRegionMapped(dst_addr, size)) {
  270. return ResultInvalidCurrentMemory;
  271. }
  272. KPageLinkedList page_linked_list;
  273. AddRegionToPages(src_addr, num_pages, page_linked_list);
  274. {
  275. auto block_guard = detail::ScopeExit(
  276. [&] { Operate(src_addr, num_pages, perm, OperationType::ChangePermissions); });
  277. CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None,
  278. OperationType::ChangePermissions));
  279. CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::None));
  280. block_guard.Cancel();
  281. }
  282. block_manager->Update(src_addr, num_pages, state, KMemoryPermission::None,
  283. KMemoryAttribute::Locked);
  284. block_manager->Update(dst_addr, num_pages, KMemoryState::AliasCode);
  285. return ResultSuccess;
  286. }
  287. ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
  288. std::lock_guard lock{page_table_lock};
  289. if (!size) {
  290. return ResultSuccess;
  291. }
  292. const std::size_t num_pages{size / PageSize};
  293. CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, nullptr, src_addr, size,
  294. KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
  295. KMemoryPermission::None, KMemoryAttribute::Mask,
  296. KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
  297. KMemoryState state{};
  298. CASCADE_CODE(CheckMemoryState(
  299. &state, nullptr, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias,
  300. KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
  301. KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
  302. CASCADE_CODE(CheckMemoryState(dst_addr, size, KMemoryState::All, state, KMemoryPermission::None,
  303. KMemoryPermission::None, KMemoryAttribute::Mask,
  304. KMemoryAttribute::None));
  305. CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
  306. block_manager->Update(dst_addr, num_pages, KMemoryState::Free);
  307. block_manager->Update(src_addr, num_pages, KMemoryState::Normal,
  308. KMemoryPermission::UserReadWrite);
  309. system.InvalidateCpuInstructionCacheRange(dst_addr, size);
  310. return ResultSuccess;
  311. }
  312. ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
  313. KPageTable& src_page_table, VAddr src_addr) {
  314. std::lock_guard lock{page_table_lock};
  315. const std::size_t num_pages{size / PageSize};
  316. // Check that the memory is mapped in the destination process.
  317. size_t num_allocator_blocks;
  318. R_TRY(CheckMemoryState(&num_allocator_blocks, dst_addr, size, KMemoryState::All,
  319. KMemoryState::SharedCode, KMemoryPermission::UserReadWrite,
  320. KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
  321. KMemoryAttribute::None));
  322. // Check that the memory is mapped in the source process.
  323. R_TRY(src_page_table.CheckMemoryState(src_addr, size, KMemoryState::FlagCanMapProcess,
  324. KMemoryState::FlagCanMapProcess, KMemoryPermission::None,
  325. KMemoryPermission::None, KMemoryAttribute::All,
  326. KMemoryAttribute::None));
  327. CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
  328. // Apply the memory block update.
  329. block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
  330. KMemoryAttribute::None);
  331. return ResultSuccess;
  332. }
  333. ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
  334. // Lock the physical memory lock.
  335. std::lock_guard phys_lk(map_physical_memory_lock);
  336. // Lock the table.
  337. std::lock_guard lock{page_table_lock};
  338. std::size_t mapped_size{};
  339. const VAddr end_addr{addr + size};
  340. block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
  341. if (info.state != KMemoryState::Free) {
  342. mapped_size += GetSizeInRange(info, addr, end_addr);
  343. }
  344. });
  345. if (mapped_size == size) {
  346. return ResultSuccess;
  347. }
  348. const std::size_t remaining_size{size - mapped_size};
  349. const std::size_t remaining_pages{remaining_size / PageSize};
  350. // Reserve the memory from the process resource limit.
  351. KScopedResourceReservation memory_reservation(
  352. system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
  353. remaining_size);
  354. if (!memory_reservation.Succeeded()) {
  355. LOG_ERROR(Kernel, "Could not reserve remaining {:X} bytes", remaining_size);
  356. return ResultLimitReached;
  357. }
  358. KPageLinkedList page_linked_list;
  359. CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages,
  360. memory_pool, allocation_option));
  361. // We succeeded, so commit the memory reservation.
  362. memory_reservation.Commit();
  363. // Map the memory.
  364. auto node{page_linked_list.Nodes().begin()};
  365. PAddr map_addr{node->GetAddress()};
  366. std::size_t src_num_pages{node->GetNumPages()};
  367. block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
  368. if (info.state != KMemoryState::Free) {
  369. return;
  370. }
  371. std::size_t dst_num_pages{GetSizeInRange(info, addr, end_addr) / PageSize};
  372. VAddr dst_addr{GetAddressInRange(info, addr)};
  373. while (dst_num_pages) {
  374. if (!src_num_pages) {
  375. node = std::next(node);
  376. map_addr = node->GetAddress();
  377. src_num_pages = node->GetNumPages();
  378. }
  379. const std::size_t num_pages{std::min(src_num_pages, dst_num_pages)};
  380. Operate(dst_addr, num_pages, KMemoryPermission::UserReadWrite, OperationType::Map,
  381. map_addr);
  382. dst_addr += num_pages * PageSize;
  383. map_addr += num_pages * PageSize;
  384. src_num_pages -= num_pages;
  385. dst_num_pages -= num_pages;
  386. }
  387. });
  388. mapped_physical_memory_size += remaining_size;
  389. const std::size_t num_pages{size / PageSize};
  390. block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
  391. KMemoryAttribute::None, KMemoryState::Normal,
  392. KMemoryPermission::UserReadWrite, KMemoryAttribute::None);
  393. return ResultSuccess;
  394. }
  395. ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
  396. std::lock_guard lock{page_table_lock};
  397. const VAddr end_addr{addr + size};
  398. ResultCode result{ResultSuccess};
  399. std::size_t mapped_size{};
  400. // Verify that the region can be unmapped
  401. block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
  402. if (info.state == KMemoryState::Normal) {
  403. if (info.attribute != KMemoryAttribute::None) {
  404. result = ResultInvalidCurrentMemory;
  405. return;
  406. }
  407. mapped_size += GetSizeInRange(info, addr, end_addr);
  408. } else if (info.state != KMemoryState::Free) {
  409. result = ResultInvalidCurrentMemory;
  410. }
  411. });
  412. if (result.IsError()) {
  413. return result;
  414. }
  415. if (!mapped_size) {
  416. return ResultSuccess;
  417. }
  418. // Unmap each region within the range
  419. KPageLinkedList page_linked_list;
  420. block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
  421. if (info.state == KMemoryState::Normal) {
  422. const std::size_t block_size{GetSizeInRange(info, addr, end_addr)};
  423. const std::size_t block_num_pages{block_size / PageSize};
  424. const VAddr block_addr{GetAddressInRange(info, addr)};
  425. AddRegionToPages(block_addr, block_size / PageSize, page_linked_list);
  426. if (result = Operate(block_addr, block_num_pages, KMemoryPermission::None,
  427. OperationType::Unmap);
  428. result.IsError()) {
  429. return;
  430. }
  431. }
  432. });
  433. if (result.IsError()) {
  434. return result;
  435. }
  436. const std::size_t num_pages{size / PageSize};
  437. system.Kernel().MemoryManager().Free(page_linked_list, num_pages, memory_pool,
  438. allocation_option);
  439. block_manager->Update(addr, num_pages, KMemoryState::Free);
  440. auto process{system.Kernel().CurrentProcess()};
  441. process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
  442. mapped_physical_memory_size -= mapped_size;
  443. return ResultSuccess;
  444. }
  445. ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
  446. std::lock_guard lock{page_table_lock};
  447. KMemoryState src_state{};
  448. CASCADE_CODE(CheckMemoryState(
  449. &src_state, nullptr, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
  450. KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::UserReadWrite,
  451. KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
  452. if (IsRegionMapped(dst_addr, size)) {
  453. return ResultInvalidCurrentMemory;
  454. }
  455. KPageLinkedList page_linked_list;
  456. const std::size_t num_pages{size / PageSize};
  457. AddRegionToPages(src_addr, num_pages, page_linked_list);
  458. {
  459. auto block_guard = detail::ScopeExit([&] {
  460. Operate(src_addr, num_pages, KMemoryPermission::UserReadWrite,
  461. OperationType::ChangePermissions);
  462. });
  463. CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None,
  464. OperationType::ChangePermissions));
  465. CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::UserReadWrite));
  466. block_guard.Cancel();
  467. }
  468. block_manager->Update(src_addr, num_pages, src_state, KMemoryPermission::None,
  469. KMemoryAttribute::Locked);
  470. block_manager->Update(dst_addr, num_pages, KMemoryState::Stack,
  471. KMemoryPermission::UserReadWrite);
  472. return ResultSuccess;
  473. }
  474. ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
  475. std::lock_guard lock{page_table_lock};
  476. KMemoryState src_state{};
  477. CASCADE_CODE(CheckMemoryState(
  478. &src_state, nullptr, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
  479. KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::None,
  480. KMemoryAttribute::Mask, KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
  481. KMemoryPermission dst_perm{};
  482. CASCADE_CODE(CheckMemoryState(nullptr, &dst_perm, nullptr, nullptr, dst_addr, size,
  483. KMemoryState::All, KMemoryState::Stack, KMemoryPermission::None,
  484. KMemoryPermission::None, KMemoryAttribute::Mask,
  485. KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
  486. KPageLinkedList src_pages;
  487. KPageLinkedList dst_pages;
  488. const std::size_t num_pages{size / PageSize};
  489. AddRegionToPages(src_addr, num_pages, src_pages);
  490. AddRegionToPages(dst_addr, num_pages, dst_pages);
  491. if (!dst_pages.IsEqual(src_pages)) {
  492. return ResultInvalidMemoryRegion;
  493. }
  494. {
  495. auto block_guard = detail::ScopeExit([&] { MapPages(dst_addr, dst_pages, dst_perm); });
  496. CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
  497. CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::UserReadWrite,
  498. OperationType::ChangePermissions));
  499. block_guard.Cancel();
  500. }
  501. block_manager->Update(src_addr, num_pages, src_state, KMemoryPermission::UserReadWrite);
  502. block_manager->Update(dst_addr, num_pages, KMemoryState::Free);
  503. return ResultSuccess;
  504. }
  505. ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
  506. KMemoryPermission perm) {
  507. VAddr cur_addr{addr};
  508. for (const auto& node : page_linked_list.Nodes()) {
  509. if (const auto result{
  510. Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())};
  511. result.IsError()) {
  512. const std::size_t num_pages{(addr - cur_addr) / PageSize};
  513. ASSERT(Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap)
  514. .IsSuccess());
  515. return result;
  516. }
  517. cur_addr += node.GetNumPages() * PageSize;
  518. }
  519. return ResultSuccess;
  520. }
  521. ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list,
  522. KMemoryState state, KMemoryPermission perm) {
  523. // Check that the map is in range.
  524. const std::size_t num_pages{page_linked_list.GetNumPages()};
  525. const std::size_t size{num_pages * PageSize};
  526. R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
  527. // Lock the table.
  528. std::lock_guard lock{page_table_lock};
  529. // Check the memory state.
  530. R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free,
  531. KMemoryPermission::None, KMemoryPermission::None,
  532. KMemoryAttribute::None, KMemoryAttribute::None));
  533. // Map the pages.
  534. R_TRY(MapPages(address, page_linked_list, perm));
  535. // Update the blocks.
  536. block_manager->Update(address, num_pages, state, perm);
  537. return ResultSuccess;
  538. }
  539. ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) {
  540. VAddr cur_addr{addr};
  541. for (const auto& node : page_linked_list.Nodes()) {
  542. const std::size_t num_pages{(addr - cur_addr) / PageSize};
  543. if (const auto result{
  544. Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap)};
  545. result.IsError()) {
  546. return result;
  547. }
  548. cur_addr += node.GetNumPages() * PageSize;
  549. }
  550. return ResultSuccess;
  551. }
  552. ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
  553. KMemoryState state) {
  554. // Check that the unmap is in range.
  555. const std::size_t num_pages{page_linked_list.GetNumPages()};
  556. const std::size_t size{num_pages * PageSize};
  557. R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
  558. // Lock the table.
  559. std::lock_guard lock{page_table_lock};
  560. // Check the memory state.
  561. R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, state, KMemoryPermission::None,
  562. KMemoryPermission::None, KMemoryAttribute::All,
  563. KMemoryAttribute::None));
  564. // Perform the unmap.
  565. R_TRY(UnmapPages(addr, page_linked_list));
  566. // Update the blocks.
  567. block_manager->Update(addr, num_pages, state, KMemoryPermission::None);
  568. return ResultSuccess;
  569. }
  570. ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
  571. Svc::MemoryPermission svc_perm) {
  572. const size_t num_pages = size / PageSize;
  573. // Lock the table.
  574. std::lock_guard lock{page_table_lock};
  575. // Verify we can change the memory permission.
  576. KMemoryState old_state;
  577. KMemoryPermission old_perm;
  578. size_t num_allocator_blocks;
  579. R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), nullptr,
  580. std::addressof(num_allocator_blocks), addr, size,
  581. KMemoryState::FlagCode, KMemoryState::FlagCode,
  582. KMemoryPermission::None, KMemoryPermission::None,
  583. KMemoryAttribute::All, KMemoryAttribute::None));
  584. // Determine new perm/state.
  585. const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm);
  586. KMemoryState new_state = old_state;
  587. const bool is_w = (new_perm & KMemoryPermission::UserWrite) == KMemoryPermission::UserWrite;
  588. const bool is_x = (new_perm & KMemoryPermission::UserExecute) == KMemoryPermission::UserExecute;
  589. const bool was_x =
  590. (old_perm & KMemoryPermission::UserExecute) == KMemoryPermission::UserExecute;
  591. ASSERT(!(is_w && is_x));
  592. if (is_w) {
  593. switch (old_state) {
  594. case KMemoryState::Code:
  595. new_state = KMemoryState::CodeData;
  596. break;
  597. case KMemoryState::AliasCode:
  598. new_state = KMemoryState::AliasCodeData;
  599. break;
  600. default:
  601. UNREACHABLE();
  602. }
  603. }
  604. // Succeed if there's nothing to do.
  605. R_SUCCEED_IF(old_perm == new_perm && old_state == new_state);
  606. // Perform mapping operation.
  607. const auto operation =
  608. was_x ? OperationType::ChangePermissionsAndRefresh : OperationType::ChangePermissions;
  609. R_TRY(Operate(addr, num_pages, new_perm, operation));
  610. // Update the blocks.
  611. block_manager->Update(addr, num_pages, new_state, new_perm, KMemoryAttribute::None);
  612. // Ensure cache coherency, if we're setting pages as executable.
  613. if (is_x) {
  614. system.InvalidateCpuInstructionCacheRange(addr, size);
  615. }
  616. return ResultSuccess;
  617. }
  618. KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) {
  619. std::lock_guard lock{page_table_lock};
  620. return block_manager->FindBlock(addr).GetMemoryInfo();
  621. }
  622. KMemoryInfo KPageTable::QueryInfo(VAddr addr) {
  623. if (!Contains(addr, 1)) {
  624. return {address_space_end, 0 - address_space_end, KMemoryState::Inaccessible,
  625. KMemoryPermission::None, KMemoryAttribute::None, KMemoryPermission::None};
  626. }
  627. return QueryInfoImpl(addr);
  628. }
  629. ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) {
  630. std::lock_guard lock{page_table_lock};
  631. KMemoryState state{};
  632. KMemoryAttribute attribute{};
  633. R_TRY(CheckMemoryState(&state, nullptr, &attribute, nullptr, addr, size,
  634. KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
  635. KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
  636. KMemoryPermission::All, KMemoryPermission::UserReadWrite,
  637. KMemoryAttribute::Mask, KMemoryAttribute::None,
  638. KMemoryAttribute::IpcAndDeviceMapped));
  639. block_manager->Update(addr, size / PageSize, state, perm, attribute | KMemoryAttribute::Locked);
  640. return ResultSuccess;
  641. }
  642. ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
  643. std::lock_guard lock{page_table_lock};
  644. KMemoryState state{};
  645. R_TRY(CheckMemoryState(&state, nullptr, nullptr, nullptr, addr, size,
  646. KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
  647. KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
  648. KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::Mask,
  649. KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
  650. block_manager->Update(addr, size / PageSize, state, KMemoryPermission::UserReadWrite);
  651. return ResultSuccess;
  652. }
  653. ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
  654. Svc::MemoryPermission svc_perm) {
  655. const size_t num_pages = size / PageSize;
  656. // Lock the table.
  657. std::lock_guard lock{page_table_lock};
  658. // Verify we can change the memory permission.
  659. KMemoryState old_state;
  660. KMemoryPermission old_perm;
  661. R_TRY(this->CheckMemoryState(
  662. std::addressof(old_state), std::addressof(old_perm), nullptr, nullptr, addr, size,
  663. KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect, KMemoryPermission::None,
  664. KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));
  665. // Determine new perm.
  666. const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm);
  667. R_SUCCEED_IF(old_perm == new_perm);
  668. // Perform mapping operation.
  669. R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
  670. // Update the blocks.
  671. block_manager->Update(addr, num_pages, old_state, new_perm, KMemoryAttribute::None);
  672. return ResultSuccess;
  673. }
  674. ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr) {
  675. const size_t num_pages = size / PageSize;
  676. ASSERT((static_cast<KMemoryAttribute>(mask) | KMemoryAttribute::SetMask) ==
  677. KMemoryAttribute::SetMask);
  678. // Lock the table.
  679. std::lock_guard lock{page_table_lock};
  680. // Verify we can change the memory attribute.
  681. KMemoryState old_state;
  682. KMemoryPermission old_perm;
  683. KMemoryAttribute old_attr;
  684. size_t num_allocator_blocks;
  685. constexpr auto AttributeTestMask =
  686. ~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared);
  687. R_TRY(this->CheckMemoryState(
  688. std::addressof(old_state), std::addressof(old_perm), std::addressof(old_attr),
  689. std::addressof(num_allocator_blocks), addr, size, KMemoryState::FlagCanChangeAttribute,
  690. KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
  691. AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
  692. // Determine the new attribute.
  693. const KMemoryAttribute new_attr =
  694. static_cast<KMemoryAttribute>(((old_attr & static_cast<KMemoryAttribute>(~mask)) |
  695. static_cast<KMemoryAttribute>(attr & mask)));
  696. // Perform operation.
  697. this->Operate(addr, num_pages, old_perm, OperationType::ChangePermissionsAndRefresh);
  698. // Update the blocks.
  699. block_manager->Update(addr, num_pages, old_state, old_perm, new_attr);
  700. return ResultSuccess;
  701. }
  702. ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
  703. // Lock the table.
  704. std::lock_guard lock{page_table_lock};
  705. // Only process page tables are allowed to set heap size.
  706. ASSERT(!this->IsKernel());
  707. max_heap_size = size;
  708. return ResultSuccess;
  709. }
  710. ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
  711. // Lock the physical memory lock.
  712. std::lock_guard phys_lk(map_physical_memory_lock);
  713. // Try to perform a reduction in heap, instead of an extension.
  714. VAddr cur_address{};
  715. std::size_t allocation_size{};
  716. {
  717. // Lock the table.
  718. std::lock_guard lk(page_table_lock);
  719. // Validate that setting heap size is possible at all.
  720. R_UNLESS(!is_kernel, ResultOutOfMemory);
  721. R_UNLESS(size <= static_cast<std::size_t>(heap_region_end - heap_region_start),
  722. ResultOutOfMemory);
  723. R_UNLESS(size <= max_heap_size, ResultOutOfMemory);
  724. if (size < GetHeapSize()) {
  725. // The size being requested is less than the current size, so we need to free the end of
  726. // the heap.
  727. // Validate memory state.
  728. std::size_t num_allocator_blocks;
  729. R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks),
  730. heap_region_start + size, GetHeapSize() - size,
  731. KMemoryState::All, KMemoryState::Normal,
  732. KMemoryPermission::All, KMemoryPermission::UserReadWrite,
  733. KMemoryAttribute::All, KMemoryAttribute::None));
  734. // Unmap the end of the heap.
  735. const auto num_pages = (GetHeapSize() - size) / PageSize;
  736. R_TRY(Operate(heap_region_start + size, num_pages, KMemoryPermission::None,
  737. OperationType::Unmap));
  738. // Release the memory from the resource limit.
  739. system.Kernel().CurrentProcess()->GetResourceLimit()->Release(
  740. LimitableResource::PhysicalMemory, num_pages * PageSize);
  741. // Apply the memory block update.
  742. block_manager->Update(heap_region_start + size, num_pages, KMemoryState::Free,
  743. KMemoryPermission::None, KMemoryAttribute::None);
  744. // Update the current heap end.
  745. current_heap_end = heap_region_start + size;
  746. // Set the output.
  747. *out = heap_region_start;
  748. return ResultSuccess;
  749. } else if (size == GetHeapSize()) {
  750. // The size requested is exactly the current size.
  751. *out = heap_region_start;
  752. return ResultSuccess;
  753. } else {
  754. // We have to allocate memory. Determine how much to allocate and where while the table
  755. // is locked.
  756. cur_address = current_heap_end;
  757. allocation_size = size - GetHeapSize();
  758. }
  759. }
  760. // Reserve memory for the heap extension.
  761. KScopedResourceReservation memory_reservation(
  762. system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
  763. allocation_size);
  764. R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
  765. // Allocate pages for the heap extension.
  766. KPageLinkedList page_linked_list;
  767. R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, allocation_size / PageSize,
  768. memory_pool, allocation_option));
  769. // Map the pages.
  770. {
  771. // Lock the table.
  772. std::lock_guard lk(page_table_lock);
  773. // Ensure that the heap hasn't changed since we began executing.
  774. ASSERT(cur_address == current_heap_end);
  775. // Check the memory state.
  776. std::size_t num_allocator_blocks{};
  777. R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), current_heap_end,
  778. allocation_size, KMemoryState::All, KMemoryState::Free,
  779. KMemoryPermission::None, KMemoryPermission::None,
  780. KMemoryAttribute::None, KMemoryAttribute::None));
  781. // Map the pages.
  782. const auto num_pages = allocation_size / PageSize;
  783. R_TRY(Operate(current_heap_end, num_pages, page_linked_list, OperationType::MapGroup));
  784. // Clear all the newly allocated pages.
  785. for (std::size_t cur_page = 0; cur_page < num_pages; ++cur_page) {
  786. std::memset(system.Memory().GetPointer(current_heap_end + (cur_page * PageSize)), 0,
  787. PageSize);
  788. }
  789. // We succeeded, so commit our memory reservation.
  790. memory_reservation.Commit();
  791. // Apply the memory block update.
  792. block_manager->Update(current_heap_end, num_pages, KMemoryState::Normal,
  793. KMemoryPermission::UserReadWrite, KMemoryAttribute::None);
  794. // Update the current heap end.
  795. current_heap_end = heap_region_start + size;
  796. // Set the output.
  797. *out = heap_region_start;
  798. return ResultSuccess;
  799. }
  800. }
  801. ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
  802. bool is_map_only, VAddr region_start,
  803. std::size_t region_num_pages, KMemoryState state,
  804. KMemoryPermission perm, PAddr map_addr) {
  805. std::lock_guard lock{page_table_lock};
  806. if (!CanContain(region_start, region_num_pages * PageSize, state)) {
  807. return ResultInvalidCurrentMemory;
  808. }
  809. if (region_num_pages <= needed_num_pages) {
  810. return ResultOutOfMemory;
  811. }
  812. const VAddr addr{
  813. AllocateVirtualMemory(region_start, region_num_pages, needed_num_pages, align)};
  814. if (!addr) {
  815. return ResultOutOfMemory;
  816. }
  817. if (is_map_only) {
  818. R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
  819. } else {
  820. KPageLinkedList page_group;
  821. R_TRY(system.Kernel().MemoryManager().Allocate(page_group, needed_num_pages, memory_pool,
  822. allocation_option));
  823. R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup));
  824. }
  825. block_manager->Update(addr, needed_num_pages, state, perm);
  826. return addr;
  827. }
  828. ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
  829. std::lock_guard lock{page_table_lock};
  830. KMemoryPermission perm{};
  831. if (const ResultCode result{CheckMemoryState(
  832. nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
  833. KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
  834. KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None,
  835. KMemoryAttribute::DeviceSharedAndUncached)};
  836. result.IsError()) {
  837. return result;
  838. }
  839. block_manager->UpdateLock(
  840. addr, size / PageSize,
  841. [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
  842. block->ShareToDevice(permission);
  843. },
  844. perm);
  845. return ResultSuccess;
  846. }
  847. ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) {
  848. std::lock_guard lock{page_table_lock};
  849. KMemoryPermission perm{};
  850. if (const ResultCode result{CheckMemoryState(
  851. nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
  852. KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
  853. KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None,
  854. KMemoryAttribute::DeviceSharedAndUncached)};
  855. result.IsError()) {
  856. return result;
  857. }
  858. block_manager->UpdateLock(
  859. addr, size / PageSize,
  860. [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
  861. block->UnshareToDevice(permission);
  862. },
  863. perm);
  864. return ResultSuccess;
  865. }
  866. ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
  867. std::lock_guard lock{page_table_lock};
  868. KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite;
  869. KMemoryPermission old_perm{};
  870. if (const ResultCode result{CheckMemoryState(
  871. nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
  872. KMemoryState::FlagCanCodeMemory, KMemoryPermission::All,
  873. KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
  874. result.IsError()) {
  875. return result;
  876. }
  877. new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
  878. block_manager->UpdateLock(
  879. addr, size / PageSize,
  880. [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
  881. block->ShareToDevice(permission);
  882. },
  883. new_perm);
  884. return ResultSuccess;
  885. }
  886. ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
  887. std::lock_guard lock{page_table_lock};
  888. KMemoryPermission new_perm = KMemoryPermission::UserReadWrite;
  889. KMemoryPermission old_perm{};
  890. if (const ResultCode result{CheckMemoryState(
  891. nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
  892. KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
  893. KMemoryAttribute::All, KMemoryAttribute::Locked)};
  894. result.IsError()) {
  895. return result;
  896. }
  897. new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
  898. block_manager->UpdateLock(
  899. addr, size / PageSize,
  900. [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
  901. block->UnshareToDevice(permission);
  902. },
  903. new_perm);
  904. return ResultSuccess;
  905. }
  906. ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
  907. block_manager = std::make_unique<KMemoryBlockManager>(start, end);
  908. return ResultSuccess;
  909. }
  910. bool KPageTable::IsRegionMapped(VAddr address, u64 size) {
  911. return CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free,
  912. KMemoryPermission::All, KMemoryPermission::None, KMemoryAttribute::Mask,
  913. KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)
  914. .IsError();
  915. }
  916. bool KPageTable::IsRegionContiguous(VAddr addr, u64 size) const {
  917. auto start_ptr = system.Memory().GetPointer(addr);
  918. for (u64 offset{}; offset < size; offset += PageSize) {
  919. if (start_ptr != system.Memory().GetPointer(addr + offset)) {
  920. return false;
  921. }
  922. start_ptr += PageSize;
  923. }
  924. return true;
  925. }
  926. void KPageTable::AddRegionToPages(VAddr start, std::size_t num_pages,
  927. KPageLinkedList& page_linked_list) {
  928. VAddr addr{start};
  929. while (addr < start + (num_pages * PageSize)) {
  930. const PAddr paddr{GetPhysicalAddr(addr)};
  931. if (!paddr) {
  932. UNREACHABLE();
  933. }
  934. page_linked_list.AddBlock(paddr, 1);
  935. addr += PageSize;
  936. }
  937. }
  938. VAddr KPageTable::AllocateVirtualMemory(VAddr start, std::size_t region_num_pages,
  939. u64 needed_num_pages, std::size_t align) {
  940. if (is_aslr_enabled) {
  941. UNIMPLEMENTED();
  942. }
  943. return block_manager->FindFreeArea(start, region_num_pages, needed_num_pages, align, 0,
  944. IsKernel() ? 1 : 4);
  945. }
  946. ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLinkedList& page_group,
  947. OperationType operation) {
  948. ASSERT(this->IsLockedByCurrentThread());
  949. ASSERT(Common::IsAligned(addr, PageSize));
  950. ASSERT(num_pages > 0);
  951. ASSERT(num_pages == page_group.GetNumPages());
  952. for (const auto& node : page_group.Nodes()) {
  953. const std::size_t size{node.GetNumPages() * PageSize};
  954. switch (operation) {
  955. case OperationType::MapGroup:
  956. system.Memory().MapMemoryRegion(page_table_impl, addr, size, node.GetAddress());
  957. break;
  958. default:
  959. UNREACHABLE();
  960. }
  961. addr += size;
  962. }
  963. return ResultSuccess;
  964. }
  965. ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm,
  966. OperationType operation, PAddr map_addr) {
  967. ASSERT(this->IsLockedByCurrentThread());
  968. ASSERT(num_pages > 0);
  969. ASSERT(Common::IsAligned(addr, PageSize));
  970. ASSERT(ContainsPages(addr, num_pages));
  971. switch (operation) {
  972. case OperationType::Unmap:
  973. system.Memory().UnmapRegion(page_table_impl, addr, num_pages * PageSize);
  974. break;
  975. case OperationType::Map: {
  976. ASSERT(map_addr);
  977. ASSERT(Common::IsAligned(map_addr, PageSize));
  978. system.Memory().MapMemoryRegion(page_table_impl, addr, num_pages * PageSize, map_addr);
  979. break;
  980. }
  981. case OperationType::ChangePermissions:
  982. case OperationType::ChangePermissionsAndRefresh:
  983. break;
  984. default:
  985. UNREACHABLE();
  986. }
  987. return ResultSuccess;
  988. }
  989. constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
  990. switch (state) {
  991. case KMemoryState::Free:
  992. case KMemoryState::Kernel:
  993. return address_space_start;
  994. case KMemoryState::Normal:
  995. return heap_region_start;
  996. case KMemoryState::Ipc:
  997. case KMemoryState::NonSecureIpc:
  998. case KMemoryState::NonDeviceIpc:
  999. return alias_region_start;
  1000. case KMemoryState::Stack:
  1001. return stack_region_start;
  1002. case KMemoryState::Static:
  1003. case KMemoryState::ThreadLocal:
  1004. return kernel_map_region_start;
  1005. case KMemoryState::Io:
  1006. case KMemoryState::Shared:
  1007. case KMemoryState::AliasCode:
  1008. case KMemoryState::AliasCodeData:
  1009. case KMemoryState::Transfered:
  1010. case KMemoryState::SharedTransfered:
  1011. case KMemoryState::SharedCode:
  1012. case KMemoryState::GeneratedCode:
  1013. case KMemoryState::CodeOut:
  1014. case KMemoryState::Coverage:
  1015. return alias_code_region_start;
  1016. case KMemoryState::Code:
  1017. case KMemoryState::CodeData:
  1018. return code_region_start;
  1019. default:
  1020. UNREACHABLE();
  1021. return {};
  1022. }
  1023. }
  1024. constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
  1025. switch (state) {
  1026. case KMemoryState::Free:
  1027. case KMemoryState::Kernel:
  1028. return address_space_end - address_space_start;
  1029. case KMemoryState::Normal:
  1030. return heap_region_end - heap_region_start;
  1031. case KMemoryState::Ipc:
  1032. case KMemoryState::NonSecureIpc:
  1033. case KMemoryState::NonDeviceIpc:
  1034. return alias_region_end - alias_region_start;
  1035. case KMemoryState::Stack:
  1036. return stack_region_end - stack_region_start;
  1037. case KMemoryState::Static:
  1038. case KMemoryState::ThreadLocal:
  1039. return kernel_map_region_end - kernel_map_region_start;
  1040. case KMemoryState::Io:
  1041. case KMemoryState::Shared:
  1042. case KMemoryState::AliasCode:
  1043. case KMemoryState::AliasCodeData:
  1044. case KMemoryState::Transfered:
  1045. case KMemoryState::SharedTransfered:
  1046. case KMemoryState::SharedCode:
  1047. case KMemoryState::GeneratedCode:
  1048. case KMemoryState::CodeOut:
  1049. case KMemoryState::Coverage:
  1050. return alias_code_region_end - alias_code_region_start;
  1051. case KMemoryState::Code:
  1052. case KMemoryState::CodeData:
  1053. return code_region_end - code_region_start;
  1054. default:
  1055. UNREACHABLE();
  1056. return {};
  1057. }
  1058. }
  1059. bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) const {
  1060. const VAddr end = addr + size;
  1061. const VAddr last = end - 1;
  1062. const VAddr region_start = this->GetRegionAddress(state);
  1063. const size_t region_size = this->GetRegionSize(state);
  1064. const bool is_in_region =
  1065. region_start <= addr && addr < end && last <= region_start + region_size - 1;
  1066. const bool is_in_heap = !(end <= heap_region_start || heap_region_end <= addr ||
  1067. heap_region_start == heap_region_end);
  1068. const bool is_in_alias = !(end <= alias_region_start || alias_region_end <= addr ||
  1069. alias_region_start == alias_region_end);
  1070. switch (state) {
  1071. case KMemoryState::Free:
  1072. case KMemoryState::Kernel:
  1073. return is_in_region;
  1074. case KMemoryState::Io:
  1075. case KMemoryState::Static:
  1076. case KMemoryState::Code:
  1077. case KMemoryState::CodeData:
  1078. case KMemoryState::Shared:
  1079. case KMemoryState::AliasCode:
  1080. case KMemoryState::AliasCodeData:
  1081. case KMemoryState::Stack:
  1082. case KMemoryState::ThreadLocal:
  1083. case KMemoryState::Transfered:
  1084. case KMemoryState::SharedTransfered:
  1085. case KMemoryState::SharedCode:
  1086. case KMemoryState::GeneratedCode:
  1087. case KMemoryState::CodeOut:
  1088. case KMemoryState::Coverage:
  1089. return is_in_region && !is_in_heap && !is_in_alias;
  1090. case KMemoryState::Normal:
  1091. ASSERT(is_in_heap);
  1092. return is_in_region && !is_in_alias;
  1093. case KMemoryState::Ipc:
  1094. case KMemoryState::NonSecureIpc:
  1095. case KMemoryState::NonDeviceIpc:
  1096. ASSERT(is_in_alias);
  1097. return is_in_region && !is_in_heap;
  1098. default:
  1099. return false;
  1100. }
  1101. }
  1102. ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
  1103. KMemoryState state, KMemoryPermission perm_mask,
  1104. KMemoryPermission perm, KMemoryAttribute attr_mask,
  1105. KMemoryAttribute attr) const {
  1106. // Validate the states match expectation.
  1107. R_UNLESS((info.state & state_mask) == state, ResultInvalidCurrentMemory);
  1108. R_UNLESS((info.perm & perm_mask) == perm, ResultInvalidCurrentMemory);
  1109. R_UNLESS((info.attribute & attr_mask) == attr, ResultInvalidCurrentMemory);
  1110. return ResultSuccess;
  1111. }
  1112. ResultCode KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
  1113. std::size_t size, KMemoryState state_mask,
  1114. KMemoryState state, KMemoryPermission perm_mask,
  1115. KMemoryPermission perm,
  1116. KMemoryAttribute attr_mask,
  1117. KMemoryAttribute attr) const {
  1118. ASSERT(this->IsLockedByCurrentThread());
  1119. // Get information about the first block.
  1120. const VAddr last_addr = addr + size - 1;
  1121. KMemoryBlockManager::const_iterator it = block_manager->FindIterator(addr);
  1122. KMemoryInfo info = it->GetMemoryInfo();
  1123. // If the start address isn't aligned, we need a block.
  1124. const size_t blocks_for_start_align =
  1125. (Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0;
  1126. while (true) {
  1127. // Validate against the provided masks.
  1128. R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
  1129. // Break once we're done.
  1130. if (last_addr <= info.GetLastAddress()) {
  1131. break;
  1132. }
  1133. // Advance our iterator.
  1134. it++;
  1135. ASSERT(it != block_manager->cend());
  1136. info = it->GetMemoryInfo();
  1137. }
  1138. // If the end address isn't aligned, we need a block.
  1139. const size_t blocks_for_end_align =
  1140. (Common::AlignUp(addr + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
  1141. if (out_blocks_needed != nullptr) {
  1142. *out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
  1143. }
  1144. return ResultSuccess;
  1145. }
  1146. ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
  1147. KMemoryAttribute* out_attr, std::size_t* out_blocks_needed,
  1148. VAddr addr, std::size_t size, KMemoryState state_mask,
  1149. KMemoryState state, KMemoryPermission perm_mask,
  1150. KMemoryPermission perm, KMemoryAttribute attr_mask,
  1151. KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
  1152. ASSERT(this->IsLockedByCurrentThread());
  1153. // Get information about the first block.
  1154. const VAddr last_addr = addr + size - 1;
  1155. KMemoryBlockManager::const_iterator it = block_manager->FindIterator(addr);
  1156. KMemoryInfo info = it->GetMemoryInfo();
  1157. // If the start address isn't aligned, we need a block.
  1158. const size_t blocks_for_start_align =
  1159. (Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0;
  1160. // Validate all blocks in the range have correct state.
  1161. const KMemoryState first_state = info.state;
  1162. const KMemoryPermission first_perm = info.perm;
  1163. const KMemoryAttribute first_attr = info.attribute;
  1164. while (true) {
  1165. // Validate the current block.
  1166. R_UNLESS(info.state == first_state, ResultInvalidCurrentMemory);
  1167. R_UNLESS(info.perm == first_perm, ResultInvalidCurrentMemory);
  1168. R_UNLESS((info.attribute | ignore_attr) == (first_attr | ignore_attr),
  1169. ResultInvalidCurrentMemory);
  1170. // Validate against the provided masks.
  1171. R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
  1172. // Break once we're done.
  1173. if (last_addr <= info.GetLastAddress()) {
  1174. break;
  1175. }
  1176. // Advance our iterator.
  1177. it++;
  1178. ASSERT(it != block_manager->cend());
  1179. info = it->GetMemoryInfo();
  1180. }
  1181. // If the end address isn't aligned, we need a block.
  1182. const size_t blocks_for_end_align =
  1183. (Common::AlignUp(addr + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
  1184. // Write output state.
  1185. if (out_state != nullptr) {
  1186. *out_state = first_state;
  1187. }
  1188. if (out_perm != nullptr) {
  1189. *out_perm = first_perm;
  1190. }
  1191. if (out_attr != nullptr) {
  1192. *out_attr = static_cast<KMemoryAttribute>(first_attr & ~ignore_attr);
  1193. }
  1194. if (out_blocks_needed != nullptr) {
  1195. *out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
  1196. }
  1197. return ResultSuccess;
  1198. }
  1199. } // namespace Kernel