PhysicalRegion.cpp 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/BuiltinWrappers.h>
  7. #include <Kernel/Assertions.h>
  8. #include <Kernel/Memory/MemoryManager.h>
  9. #include <Kernel/Memory/PhysicalRegion.h>
  10. #include <Kernel/Memory/PhysicalZone.h>
  11. #include <Kernel/Random.h>
  12. namespace Kernel::Memory {
  13. static constexpr u32 next_power_of_two(u32 value)
  14. {
  15. value--;
  16. value |= value >> 1;
  17. value |= value >> 2;
  18. value |= value >> 4;
  19. value |= value >> 8;
  20. value |= value >> 16;
  21. value++;
  22. return value;
  23. }
  24. PhysicalRegion::~PhysicalRegion() = default;
  25. PhysicalRegion::PhysicalRegion(PhysicalAddress lower, PhysicalAddress upper)
  26. : m_lower(lower)
  27. , m_upper(upper)
  28. {
  29. m_pages = (m_upper.get() - m_lower.get()) / PAGE_SIZE;
  30. }
  31. void PhysicalRegion::initialize_zones()
  32. {
  33. size_t remaining_pages = m_pages;
  34. auto base_address = m_lower;
  35. auto make_zones = [&](size_t zone_size) -> size_t {
  36. size_t pages_per_zone = zone_size / PAGE_SIZE;
  37. size_t zone_count = 0;
  38. auto first_address = base_address;
  39. while (remaining_pages >= pages_per_zone) {
  40. m_zones.append(adopt_nonnull_own_or_enomem(new (nothrow) PhysicalZone(base_address, pages_per_zone)).release_value_but_fixme_should_propagate_errors());
  41. base_address = base_address.offset(pages_per_zone * PAGE_SIZE);
  42. m_usable_zones.append(*m_zones.last());
  43. remaining_pages -= pages_per_zone;
  44. ++zone_count;
  45. }
  46. if (zone_count)
  47. dmesgln(" * {}x PhysicalZone ({} MiB) @ {:016x}-{:016x}", zone_count, pages_per_zone / 256, first_address.get(), base_address.get() - pages_per_zone * PAGE_SIZE - 1);
  48. return zone_count;
  49. };
  50. // First make 16 MiB zones (with 4096 pages each)
  51. m_large_zones = make_zones(large_zone_size);
  52. // Then divide any remaining space into 1 MiB zones (with 256 pages each)
  53. make_zones(small_zone_size);
  54. }
  55. OwnPtr<PhysicalRegion> PhysicalRegion::try_take_pages_from_beginning(size_t page_count)
  56. {
  57. VERIFY(page_count > 0);
  58. VERIFY(page_count < m_pages);
  59. auto taken_lower = m_lower;
  60. auto taken_upper = taken_lower.offset((PhysicalPtr)page_count * PAGE_SIZE);
  61. m_lower = m_lower.offset((PhysicalPtr)page_count * PAGE_SIZE);
  62. m_pages = (m_upper.get() - m_lower.get()) / PAGE_SIZE;
  63. return try_create(taken_lower, taken_upper);
  64. }
  65. Vector<NonnullRefPtr<PhysicalPage>> PhysicalRegion::take_contiguous_free_pages(size_t count)
  66. {
  67. auto rounded_page_count = next_power_of_two(count);
  68. auto order = count_trailing_zeroes(rounded_page_count);
  69. Optional<PhysicalAddress> page_base;
  70. for (auto& zone : m_usable_zones) {
  71. page_base = zone.allocate_block(order);
  72. if (page_base.has_value()) {
  73. if (zone.is_empty()) {
  74. // We've exhausted this zone, move it to the full zones list.
  75. m_full_zones.append(zone);
  76. }
  77. break;
  78. }
  79. }
  80. if (!page_base.has_value())
  81. return {};
  82. Vector<NonnullRefPtr<PhysicalPage>> physical_pages;
  83. physical_pages.ensure_capacity(count);
  84. for (size_t i = 0; i < count; ++i)
  85. physical_pages.append(PhysicalPage::create(page_base.value().offset(i * PAGE_SIZE)));
  86. return physical_pages;
  87. }
  88. RefPtr<PhysicalPage> PhysicalRegion::take_free_page()
  89. {
  90. if (m_usable_zones.is_empty())
  91. return nullptr;
  92. auto& zone = *m_usable_zones.first();
  93. auto page = zone.allocate_block(0);
  94. VERIFY(page.has_value());
  95. if (zone.is_empty()) {
  96. // We've exhausted this zone, move it to the full zones list.
  97. m_full_zones.append(zone);
  98. }
  99. return PhysicalPage::create(page.value());
  100. }
  101. void PhysicalRegion::return_page(PhysicalAddress paddr)
  102. {
  103. auto large_zone_base = lower().get();
  104. auto small_zone_base = lower().get() + (m_large_zones * large_zone_size);
  105. size_t zone_index;
  106. if (paddr.get() < small_zone_base)
  107. zone_index = (paddr.get() - large_zone_base) / large_zone_size;
  108. else
  109. zone_index = m_large_zones + (paddr.get() - small_zone_base) / small_zone_size;
  110. auto& zone = m_zones[zone_index];
  111. VERIFY(zone->contains(paddr));
  112. zone->deallocate_block(paddr, 0);
  113. if (m_full_zones.contains(*zone))
  114. m_usable_zones.append(*zone);
  115. }
  116. }