PhysicalRegion.cpp 4.1 KB

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