summaryrefslogtreecommitdiff
path: root/kernel/paging.cpp
blob: 3bd27d0f608adfda5101b96db0f919dc61f032f8 (plain) (blame)
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
108
109
110
111
112
113
114
115
#include <mercury/kernel/utility.hpp>
#include <mercury/kernel/paging.hpp>

//see also ../documentation/memory.txt

extern "C" {
  uint64_t __kernel_p4_paddr;
}

namespace mercury::kernel::paging {

  static constexpr uint64_t kernel_vram_start = 0xffffffffc0000000;
  static constexpr uint64_t kernel_vram_pages = 261888;
  static constexpr uint64_t kernel_stack_bottom = 0xfffffffffff01000;
  static constexpr uint64_t kernel_stack_top = 0xfffffffffffff000;

  static constexpr uint64_t pram_pages = 1 << 23;
  static uint64_t pram_usage_bitmap[pram_pages / 64];

  void mark_all_pram_used() {
    utility::mark_bitmap_region_one(pram_usage_bitmap, 0, pram_pages);
  }

  void mark_pram_region_free(uint64_t start_addr, uint64_t end_addr) {
    utility::mark_bitmap_region_zero(
      pram_usage_bitmap, start_addr / 4096,
      utility::min(end_addr / 4096, pram_pages));
  }

  [[gnu::aligned(4096)]] uint64_t kernel_p4[512];
  [[gnu::aligned(4096)]] uint64_t kernel_p3[512];
  [[gnu::aligned(4096)]] uint64_t kernel_p2[512];
  [[gnu::aligned(4096)]] uint64_t kernel_p1s[512 * 512];

  static uint64_t encode_pte(
      uint64_t addr, bool user, bool write, bool execute) {
    return (addr & 0x0000ffffffffffff) | (execute ? 0 : (1ULL << 63))
         | (user << 2) | (write << 1) | 1;
  }

  void init_kernel_page_tables(uint64_t kernel_offset) {
    __kernel_p4_paddr = (uint64_t)kernel_p4 - kernel_offset;
    for (int i = 0; i < 511; ++i)
      kernel_p4[i] = 0;
    kernel_p4[511] = encode_pte(
      (uint64_t)kernel_p3 - kernel_offset, false, true, true);
    for (int i = 0; i < 511; ++i)
      kernel_p3[i] = 0;
    kernel_p3[511] = encode_pte(
      (uint64_t)kernel_p2 - kernel_offset, false, true, true);
    for (int i = 0; i < 512; ++i)
      kernel_p2[i] = encode_pte(
        (uint64_t)kernel_p1s + 4096 * i - kernel_offset, false, true, true);
    for (int i = 0; i < 512 * 512; ++i)
      kernel_p1s[i] = 0;
  }

  void map_kernel_page(
      uint64_t paddr, uint64_t vaddr, bool write, bool execute) {
    uint64_t i = (vaddr - kernel_vram_start) / 4096;
    kernel_p1s[i] = encode_pte(paddr, false, write, execute);
  }

  static uint64_t take_pram_page() {
    for (uint64_t i = 0; i < pram_pages / 64; ++i)
      if (pram_usage_bitmap[i] != 0xffffffffffffffff)
        for (int j = 0; j < 64; ++j)
          if (!(pram_usage_bitmap[i] & (1ULL << j))) {
            pram_usage_bitmap[i] |= (1ULL << j);
            return 4096 * (i * 64 + j);
          }
    //TODO: handle error
    return 0;
  }

  void map_kernel_stack() {
    for (uint64_t vaddr = kernel_stack_bottom;
         vaddr < kernel_stack_top; vaddr += 4096)
      map_kernel_page(take_pram_page(), vaddr, true, false);
  }

  uint64_t find_unmapped_vram_region(uint64_t page_count) {
    uint64_t start = 0;
    uint64_t len = 0;
    for (uint64_t i = 0; i < kernel_vram_pages; ++i)
      if (kernel_p1s[i] == 0) {
        ++len;
        if (len == page_count)
          return start * 4096 + kernel_vram_start;
      }
      else {
        start = i + 1;
        len = 0;
      }
    //TODO: handle error
    return 0;
  }

  uint64_t get_used_vram_page_count() {
    uint64_t count = 0;
    for (uint64_t i = 0; i < kernel_vram_pages; ++i)
      if (kernel_p1s[i] != 0)
        ++count;
    return count;
  }

  uint64_t get_free_pram_page_count() {
    uint64_t used_count = 0;
    for (uint64_t i = 0; i < pram_pages / 64; ++i)
      for (uint64_t j = 0; j < 64; ++j)
        used_count += (pram_usage_bitmap[i] >> j) & 1;
    return pram_pages - used_count;
  }

}