Merge branch 'master' of github.com:Minep/lunaix-os
[lunaix-os.git] / lunaix-os / kernel / mm / pmm.c
1 #include <lunaix/mm/page.h>
2 #include <lunaix/mm/pmm.h>
3 #include <lunaix/status.h>
4
5 // This is a very large array...
6 static struct pp_struct pm_table[PM_BMP_MAX_SIZE];
7
8 static uintptr_t max_pg;
9
10 void
11 pmm_mark_page_free(uintptr_t ppn)
12 {
13     pm_table[ppn].ref_counts = 0;
14 }
15
16 void
17 pmm_mark_page_occupied(pid_t owner, uintptr_t ppn, pp_attr_t attr)
18 {
19     pm_table[ppn] = (struct pp_struct) {
20         .owner = owner,
21         .ref_counts = 1,
22         .attr = attr
23     };
24 }
25
26 void
27 pmm_mark_chunk_free(uintptr_t start_ppn, size_t page_count)
28 {
29     for (size_t i = start_ppn; i < start_ppn + page_count && i < max_pg; i++)
30     {
31         pm_table[i].ref_counts = 0;
32     }
33 }
34
35 void
36 pmm_mark_chunk_occupied(pid_t owner, uint32_t start_ppn, size_t page_count, pp_attr_t attr)
37 {
38     for (size_t i = start_ppn; i < start_ppn + page_count && i < max_pg; i++)
39     {
40         pm_table[i] = (struct pp_struct) {
41             .owner = owner,
42             .ref_counts = 1,
43             .attr = attr
44         };
45     }
46 }
47
48 // 我们跳过位于0x0的页。我们不希望空指针是指向一个有效的内存空间。
49 #define LOOKUP_START 1
50
51 size_t pg_lookup_ptr;
52
53 void
54 pmm_init(uintptr_t mem_upper_lim)
55 {
56     max_pg = (PG_ALIGN(mem_upper_lim) >> 12);
57
58     pg_lookup_ptr = LOOKUP_START;
59
60     // mark all as occupied
61     for (size_t i = 0; i < PM_BMP_MAX_SIZE; i++) {
62         pm_table[i] = (struct pp_struct) {
63             .owner = 0,
64             .attr = 0,
65             .ref_counts = 1
66         };
67     }
68 }
69
70 void*
71 pmm_alloc_cpage(pid_t owner, size_t num_pages, pp_attr_t attr) {
72     size_t p1 = 0;
73     size_t p2 = 0;
74
75     while (p2 < max_pg && p2 - p1 < num_pages)
76     {
77         (!(&pm_table[p2])->ref_counts) ? (p2++) : (p1 = p2);
78     }
79
80     if (p2 < max_pg) {
81         return NULL;
82     }
83
84     pmm_mark_chunk_occupied(owner, p1, num_pages, attr);
85
86     return p1 << 12;
87 }
88
89 void*
90 pmm_alloc_page(pid_t owner, pp_attr_t attr)
91 {
92     // Next fit approach. Maximize the throughput!
93     uintptr_t good_page_found = (uintptr_t)NULL;
94     size_t old_pg_ptr = pg_lookup_ptr;
95     size_t upper_lim = max_pg;
96     struct pp_struct* pm;
97     while (!good_page_found && pg_lookup_ptr < upper_lim) {
98         pm = &pm_table[pg_lookup_ptr];
99
100         if (!pm->ref_counts) {
101             *pm = (struct pp_struct) {
102                 .attr = attr,
103                 .owner = owner,
104                 .ref_counts = 1
105             };
106             good_page_found = pg_lookup_ptr << 12;
107             break;
108         } else {
109             pg_lookup_ptr++;
110
111             // We've searched the interval [old_pg_ptr, max_pg) but failed
112             //   may be chances in [1, old_pg_ptr) ?
113             // Let's find out!
114             if (pg_lookup_ptr >= upper_lim && old_pg_ptr != LOOKUP_START) {
115                 upper_lim = old_pg_ptr;
116                 pg_lookup_ptr = LOOKUP_START;
117                 old_pg_ptr = LOOKUP_START;
118             }
119         }
120     }
121     if (!good_page_found) {
122         __current->k_status = LXOUTOFMEM;
123     }
124     return (void*)good_page_found;
125 }
126
127 int
128 pmm_free_page(pid_t owner, void* page)
129 {
130     struct pp_struct* pm = &pm_table[(intptr_t)page >> 12];
131     
132     // Is this a MMIO mapping or double free?
133     if (((intptr_t)page >> 12) >= max_pg || !(pm->ref_counts)) {
134         return 0;
135     }
136
137     // TODO: 检查权限,保证:1) 只有正在使用该页(包括被分享者)的进程可以释放; 2) 内核可释放所有页。
138     pm->ref_counts--;
139     return 1;
140 }
141
142 int pmm_ref_page(pid_t owner, void* page) {
143     (void*) owner;      // TODO: do smth with owner
144     
145     uint32_t ppn = (uintptr_t)page >> 12;
146     
147     if (ppn >= PM_BMP_MAX_SIZE) {
148         return 0;
149     }
150
151     struct pp_struct* pm = &pm_table[ppn];
152     if (ppn >= max_pg || !pm->ref_counts) {
153         return 0;
154     }
155
156     pm->ref_counts++;
157     return 1;
158 }
159
160 struct pp_struct* pmm_query(void* pa) {
161     uint32_t ppn = (uintptr_t)pa >> 12;
162     
163     if (ppn >= PM_BMP_MAX_SIZE) {
164         return NULL;
165     }
166
167     return &pm_table[ppn];
168 }