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