Virtual memory & paging
[lunaix-os.git] / lunaix-os / kernel / mm / pmm.c
1 #include <lunaix/mm/page.h>
2 #include <lunaix/mm/pmm.h>
3
4 #define MARK_PG_AUX_VAR(ppn)                                                   \
5     uint32_t group = ppn / 8;                                                  \
6     uint32_t msk = (0x80U >> (ppn % 8));                                       \
7
8 #define MARK_CHUNK_AUX_VAR(start_ppn, page_count)                              \
9     uint32_t group = start_ppn / 8;                                            \
10     uint32_t offset = start_ppn % 8;                                           \
11     uint32_t group_count = (page_count + offset) / 8;                          \
12     uint32_t remainder = (page_count + offset) % 8;                            \
13     uint32_t leading_shifts =                                                  \
14       (page_count + offset) < 8 ? page_count : 8 - offset;
15
16 uint8_t pm_bitmap[PM_BMP_MAX_SIZE];
17
18 uintptr_t max_pg;
19
20 void
21 pmm_mark_page_free(uintptr_t ppn)
22 {
23     MARK_PG_AUX_VAR(ppn)
24     pm_bitmap[group] = pm_bitmap[group] & ~msk;
25 }
26
27 void
28 pmm_mark_page_occupied(uintptr_t ppn)
29 {
30     MARK_PG_AUX_VAR(ppn)
31     pm_bitmap[group] = pm_bitmap[group] | msk;
32 }
33
34 void
35 pmm_mark_chunk_free(uintptr_t start_ppn, size_t page_count)
36 {
37     MARK_CHUNK_AUX_VAR(start_ppn, page_count)
38
39     // nasty bit level hacks but it reduce # of iterations.
40
41     pm_bitmap[group] &= ~(((1U << leading_shifts) - 1) << (8 - offset - leading_shifts));
42
43     group++;
44
45     // prevent unsigned overflow
46     for (uint32_t i = 0; group_count !=0 && i < group_count - 1; i++, group++) {
47         pm_bitmap[group] = 0;
48     }
49
50     pm_bitmap[group] &=
51       ~(((1U << (page_count > 8 ? remainder : 0)) - 1) << (8 - remainder));
52 }
53
54 void
55 pmm_mark_chunk_occupied(uint32_t start_ppn, size_t page_count)
56 {
57     MARK_CHUNK_AUX_VAR(start_ppn, page_count)
58
59     pm_bitmap[group] |= (((1U << leading_shifts) - 1) << (8 - offset - leading_shifts));
60
61     group++;
62
63     // prevent unsigned overflow
64     for (uint32_t i = 0; group_count !=0 && i < group_count - 1; i++, group++) {
65         pm_bitmap[group] = 0xFFU;
66     }
67
68     pm_bitmap[group] |=
69       (((1U << (page_count > 8 ? remainder : 0)) - 1) << (8 - remainder));
70 }
71
72 // 我们跳过位于0x0的页。我们不希望空指针是指向一个有效的内存空间。
73 #define LOOKUP_START 1
74
75 size_t pg_lookup_ptr;
76
77 void
78 pmm_init(uintptr_t mem_upper_lim)
79 {
80     max_pg = (PG_ALIGN(mem_upper_lim) >> 12) + 1;
81
82     pg_lookup_ptr = LOOKUP_START;
83
84     // mark all as occupied
85     for (size_t i = 0; i < PM_BMP_MAX_SIZE; i++) {
86         pm_bitmap[i] = 0xFFU;
87     }
88 }
89
90 void*
91 pmm_alloc_page()
92 {
93     // Next fit approach. Maximize the throughput!
94     uintptr_t good_page_found = NULL;
95     size_t old_pg_ptr = pg_lookup_ptr;
96     size_t upper_lim = max_pg;
97     uint8_t chunk = 0;
98     while (!good_page_found && pg_lookup_ptr < upper_lim) {
99         chunk = pm_bitmap[pg_lookup_ptr >> 3];
100
101         // skip the fully occupied chunk, reduce # of iterations
102         if (chunk != 0xFFU) {
103             for (size_t i = pg_lookup_ptr % 8; i < 8; i++, pg_lookup_ptr++) {
104                 if (!(chunk & (0x80U >> i))) {
105                     pmm_mark_page_occupied(pg_lookup_ptr);
106                     good_page_found = pg_lookup_ptr << 12;
107                     break;
108                 }
109             }
110         } else {
111             pg_lookup_ptr += 8;
112
113             // We've searched the interval [old_pg_ptr, max_pg) but failed
114             //   may be chances in [1, old_pg_ptr) ?
115             // Let's find out!
116             if (pg_lookup_ptr >= upper_lim && old_pg_ptr != LOOKUP_START) {
117                 upper_lim = old_pg_ptr;
118                 pg_lookup_ptr = LOOKUP_START;
119                 old_pg_ptr = LOOKUP_START;
120             }
121         }
122     }
123     return (void*)good_page_found;
124 }
125
126 int
127 pmm_free_page(void* page)
128 {
129     // TODO: Add kernel reserved memory page check
130     uint32_t pg = (uintptr_t)page >> 12;
131     if (pg && pg < max_pg)
132     {
133         pmm_mark_page_free(pg);
134         return 1;
135     }
136     return 0;
137 }