aboutsummaryrefslogtreecommitdiff
path: root/kernel/src/memory/mmio_allocator.cpp
blob: f77f14fa942b2946d69f75dd41c77d9e776c4d40 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include "kernel/memory/mmio_allocator.hpp"

#include "kapi/memory.hpp"
#include "kapi/system.hpp"

#include <kstd/allocator>

#include <cstddef>
#include <memory>
#include <utility>

namespace kernel::memory
{

  mmio_allocator::mmio_allocator(kapi::memory::linear_address base, std::size_t pages)
      : m_head{make_node(base, pages, nullptr, nullptr, true)}
  {}

  auto mmio_allocator::allocate(std::size_t count) -> kapi::memory::linear_address
  {
    if (count == 0 || !m_head)
    {
      return {};
    }

    auto current = m_head;
    while (current)
    {
      if (current->page_count > count)
      {
        auto new_base = current->base + (count * kapi::memory::page::size);
        auto split_node = make_node(new_base, current->page_count - count, std::move(current->next), current, true);

        if (current->next)
        {
          current->next->previous = split_node;
        }
        current->next = split_node;
        current->page_count = count;
      }

      current->is_free = false;
      return current->base;
    }

    kapi::system::panic("[OS:MEM] MMIO alloctor out of memory!");
    return {};
  }

  auto mmio_allocator::release(kapi::memory::linear_address base) -> void
  {
    auto current = m_head;

    while (current)
    {
      if (current->base == base && !current->is_free)
      {
        current->is_free = true;

        if (current->next && current->next->is_free)
        {
          auto removed = current->next;
          current->page_count += removed->page_count;
          current->next = removed->next;
          if (current->next)
          {
            current->next->previous = current;
          }
          destroy_node(removed);
        }

        if (current->previous && current->previous->is_free)
        {
          auto removed = current;
          removed->previous->page_count += removed->page_count;
          removed->previous->next = removed->next;
          if (removed->next)
          {
            removed->next->previous = removed->previous;
          }
          destroy_node(removed);
        }
        return;
      }
      current = current->next;
    }
  }

  auto mmio_allocator::make_node(kapi::memory::linear_address base, std::size_t page_count, node * next,
                                 node * previous, bool is_free) -> node *
  {
    using traits = std::allocator_traits<kstd::allocator<node>>;

    auto new_node = traits::allocate(m_allocator, 1);
    traits::construct(m_allocator, new_node, base, page_count, next, previous, is_free);
    return new_node;
  }

  auto mmio_allocator::destroy_node(node * instance) -> void
  {
    using traits = std::allocator_traits<kstd::allocator<node>>;

    traits::destroy(m_allocator, instance);
    traits::deallocate(m_allocator, instance, 1);
  }

}  // namespace kernel::memory