1 | // SPDX-License-Identifier: GPL-3.0-or-later |
2 | |
3 | #include "mos/mm/paging/iterator.hpp" |
4 | |
5 | #include "mos/assert.hpp" |
6 | #include "mos/mm/paging/pmlx/pml1.hpp" |
7 | #include "mos/mm/paging/pmlx/pml2.hpp" |
8 | #include "mos/mm/paging/pmlx/pml3.hpp" |
9 | #include "mos/mm/paging/pmlx/pml4.hpp" |
10 | #include "mos/platform/platform.hpp" |
11 | |
12 | #include <mos_string.hpp> |
13 | |
14 | static void pagetable_iterator_start_current_range(pagetable_iter_t *it, pml5_t *pml5, pml4_t *pml4, pml3_t *pml3, pml2_t *pml2, pml1_t *pml1) |
15 | { |
16 | it->range = (pagetable_iter_range_t) { .present: 0 }; |
17 | it->range.vaddr = it->vaddr; |
18 | *pml5 = it->pgd.max; |
19 | |
20 | *pml4 = pml5->next; |
21 | const pml4e_t *pml4e = pml4_entry(pml4: *pml4, vaddr: it->vaddr); |
22 | if (!pml4e_is_present(pml4e)) |
23 | return; |
24 | |
25 | #if MOS_CONFIG(PML4_HUGE_CAPABLE) |
26 | if (platform_pml4e_is_huge(pml4e)) |
27 | { |
28 | it->range.present = true; |
29 | it->range.flags = platform_pml4e_get_flags(pml4e); |
30 | it->range.pfn = platform_pml4e_get_huge_pfn(pml4e); |
31 | it->vaddr += PML4E_NPAGES * MOS_PAGE_SIZE; |
32 | it->range.pfn_end = it->range.pfn + PML4E_NPAGES - 1; |
33 | return; |
34 | } |
35 | #endif |
36 | |
37 | *pml3 = platform_pml4e_get_pml3(pml4: pml4e); |
38 | const pml3e_t *pml3e = pml3_entry(pml3: *pml3, vaddr: it->vaddr); |
39 | if (!pml3e_is_present(pml3e)) |
40 | return; |
41 | |
42 | #if MOS_CONFIG(PML3_HUGE_CAPABLE) |
43 | if (platform_pml3e_is_huge(pml3e)) |
44 | { |
45 | it->range.present = true; |
46 | it->range.flags = platform_pml3e_get_flags(pml3e); |
47 | it->range.pfn = platform_pml3e_get_huge_pfn(pml3e); |
48 | it->vaddr += PML3E_NPAGES * MOS_PAGE_SIZE; |
49 | it->range.pfn_end = it->range.pfn + PML3E_NPAGES - 1; |
50 | return; |
51 | } |
52 | #endif |
53 | |
54 | *pml2 = platform_pml3e_get_pml2(pml3e); |
55 | const pml2e_t *pml2e = pml2_entry(pml2: *pml2, vaddr: it->vaddr); |
56 | if (!pml2e_is_present(pml2e)) |
57 | return; |
58 | |
59 | #if MOS_CONFIG(PML2_HUGE_CAPABLE) |
60 | if (platform_pml2e_is_huge(pml2: pml2e)) |
61 | { |
62 | it->range.present = true; |
63 | it->range.flags = platform_pml2e_get_flags(pml2e); |
64 | it->range.pfn = platform_pml2e_get_huge_pfn(pml2: pml2e); |
65 | it->vaddr += PML2E_NPAGES * MOS_PAGE_SIZE; |
66 | it->range.pfn_end = it->range.pfn + PML2E_NPAGES - 1; |
67 | return; |
68 | } |
69 | #endif |
70 | |
71 | *pml1 = platform_pml2e_get_pml1(pml2: pml2e); |
72 | const pml1e_t *pml1e = pml1_entry(pml1: *pml1, vaddr: it->vaddr); |
73 | it->range.present = platform_pml1e_get_present(pml1: pml1e); |
74 | if (!it->range.present) |
75 | return; |
76 | |
77 | it->range.flags = platform_pml1e_get_flags(pml1e); |
78 | it->range.pfn = platform_pml1e_get_pfn(pml1: pml1e); |
79 | it->vaddr += PML1E_NPAGES * MOS_PAGE_SIZE; |
80 | it->range.pfn_end = it->range.pfn + PML1E_NPAGES - 1; |
81 | } |
82 | |
83 | void pagetable_iter_init(pagetable_iter_t *it, pgd_t pgd, ptr_t vaddr, ptr_t end) |
84 | { |
85 | memzero(s: it, n: sizeof(*it)); |
86 | it->pgd = pgd; |
87 | it->vaddr = it->start = vaddr; |
88 | it->end = end; |
89 | } |
90 | |
91 | #define yield_range() \ |
92 | do \ |
93 | { \ |
94 | it->range.vaddr_end = it->vaddr - 1; \ |
95 | return &it->range; \ |
96 | } while (0) |
97 | |
98 | pagetable_iter_range_t *pagetable_iter_next(pagetable_iter_t *it) |
99 | { |
100 | #define _IS_IN_RANGE MOS_IN_RANGE(it->vaddr, it->start, it->end) |
101 | if (!_IS_IN_RANGE) |
102 | return NULL; |
103 | |
104 | pml5_t pml5 = { .next.table: 0 }; |
105 | pml4_t pml4 = { .table: 0 }; |
106 | pml3_t pml3 = { .table: 0 }; |
107 | pml2_t pml2 = { .table: 0 }; |
108 | pml1_t pml1 = { .table: 0 }; |
109 | |
110 | pagetable_iterator_start_current_range(it, pml5: &pml5, pml4: &pml4, pml3: &pml3, pml2: &pml2, pml1: &pml1); |
111 | |
112 | if (!pml_null(pml1)) |
113 | goto iterate_pml1; |
114 | else if (!pml_null(pml2)) |
115 | goto iterate_pml2; |
116 | else if (!pml_null(pml3)) |
117 | goto iterate_pml3; |
118 | else if (!pml_null(pml4)) |
119 | goto iterate_pml4; |
120 | else |
121 | MOS_UNREACHABLE(); |
122 | |
123 | __builtin_unreachable(); |
124 | |
125 | iterate_pml1: |
126 | for (u32 i = pml1_index(it->vaddr); i < PML1_ENTRIES && _IS_IN_RANGE; i++) |
127 | { |
128 | const pml1e_t *pml1e = &pml1.table[i]; |
129 | const bool present = platform_pml1e_get_present(pml1: pml1e); |
130 | |
131 | if (!present && present != it->range.present) |
132 | { |
133 | it->range.vaddr_end = it->vaddr - 1; |
134 | return &it->range; |
135 | } |
136 | |
137 | if (!present) |
138 | { |
139 | it->vaddr += PML1E_NPAGES * MOS_PAGE_SIZE; |
140 | continue; |
141 | } |
142 | |
143 | const vm_flags new_flags = platform_pml1e_get_flags(pml1e); |
144 | const pfn_t new_pfn = platform_pml1e_get_pfn(pml1: pml1e); |
145 | |
146 | bool changed = false; |
147 | changed |= new_flags != it->range.flags; // flags changed |
148 | changed |= new_pfn != it->range.pfn_end + 1; // pfn changed |
149 | |
150 | if (changed) |
151 | yield_range(); |
152 | |
153 | it->vaddr += PML1E_NPAGES * MOS_PAGE_SIZE; // next time, we start from the next page |
154 | it->range.pfn_end = new_pfn + PML1E_NPAGES - 1; |
155 | } |
156 | |
157 | iterate_pml2: |
158 | for (u32 i = pml2_index(it->vaddr); i < PML2_ENTRIES && _IS_IN_RANGE; i++) |
159 | { |
160 | const pml2e_t *pml2e = &pml2.table[i]; |
161 | const bool present = pml2e_is_present(pml2e); |
162 | |
163 | if (!present && present != it->range.present) |
164 | yield_range(); |
165 | |
166 | if (!present) |
167 | { |
168 | it->vaddr += PML2E_NPAGES * MOS_PAGE_SIZE; |
169 | continue; |
170 | } |
171 | |
172 | #if MOS_CONFIG(PML2_HUGE_CAPABLE) |
173 | if (platform_pml2e_is_huge(pml2: pml2e)) |
174 | { |
175 | const vm_flags new_flags = platform_pml2e_get_flags(pml2e); |
176 | const pfn_t new_pfn = platform_pml2e_get_huge_pfn(pml2: pml2e); |
177 | |
178 | bool changed = false; |
179 | changed |= new_flags != it->range.flags; // flags changed |
180 | changed |= new_pfn != it->range.pfn_end + 1; // pfn changed |
181 | |
182 | if (changed) |
183 | yield_range(); |
184 | |
185 | it->vaddr += PML2E_NPAGES * MOS_PAGE_SIZE; |
186 | it->range.pfn_end = new_pfn + PML2E_NPAGES - 1; |
187 | } |
188 | else |
189 | #endif |
190 | { |
191 | pml1 = platform_pml2e_get_pml1(pml2: pml2e); |
192 | goto iterate_pml1; // iterate pml1 |
193 | } |
194 | } |
195 | |
196 | iterate_pml3: |
197 | for (u32 i = pml3_index(it->vaddr); i < PML3_ENTRIES && _IS_IN_RANGE; i++) |
198 | { |
199 | const pml3e_t *pml3e = pml3_entry(pml3, vaddr: it->vaddr); |
200 | const bool present = pml3e_is_present(pml3e); |
201 | |
202 | if (!present && present != it->range.present) |
203 | yield_range(); |
204 | |
205 | if (!present) |
206 | { |
207 | it->vaddr += PML3E_NPAGES * MOS_PAGE_SIZE; |
208 | continue; |
209 | } |
210 | |
211 | #if MOS_CONFIG(PML3_HUGE_CAPABLE) |
212 | if (platform_pml3e_is_huge(pml3e)) |
213 | { |
214 | const vm_flags new_flags = platform_pml3e_get_flags(pml3e); |
215 | const pfn_t new_pfn = platform_pml3e_get_huge_pfn(pml3e); |
216 | |
217 | bool changed = false; |
218 | changed |= new_flags != it->range.flags; // flags changed |
219 | changed |= new_pfn != it->range.pfn_end + 1; // pfn changed |
220 | |
221 | if (changed) |
222 | yield_range(); |
223 | |
224 | it->vaddr += PML3E_NPAGES * MOS_PAGE_SIZE; |
225 | it->range.pfn_end = new_pfn + PML3E_NPAGES - 1; |
226 | } |
227 | else |
228 | #endif |
229 | { |
230 | pml2 = platform_pml3e_get_pml2(pml3e); |
231 | goto iterate_pml2; // iterate pml2 |
232 | } |
233 | } |
234 | |
235 | iterate_pml4: |
236 | for (u32 i = pml4_index(it->vaddr); i < PML4_ENTRIES && _IS_IN_RANGE; i++) |
237 | { |
238 | const pml4e_t *pml4e = pml4_entry(pml4, vaddr: it->vaddr); |
239 | const bool present = pml4e_is_present(pml4e); |
240 | |
241 | if (!present && present != it->range.present) |
242 | yield_range(); |
243 | |
244 | if (!present) |
245 | { |
246 | it->vaddr += PML4E_NPAGES * MOS_PAGE_SIZE; |
247 | continue; |
248 | } |
249 | |
250 | pml3 = platform_pml4e_get_pml3(pml4: pml4e); |
251 | goto iterate_pml3; // iterate pml3 |
252 | } |
253 | |
254 | yield_range(); |
255 | } |
256 | |