001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020 #include <sos/list.h>
021 #include <sos/physmem.h>
022 #include <hwcore/paging.h>
023 #include <sos/assert.h>
024
025 #include "kmem_vmm.h"
026
027
028 struct sos_kmem_range
029 {
030 sos_vaddr_t base_vaddr;
031 sos_count_t nb_pages;
032
033
034 struct sos_kslab *slab;
035
036 struct sos_kmem_range *prev, *next;
037 };
038 const int sizeof_struct_sos_kmem_range = sizeof(struct sos_kmem_range);
039
040
041 static struct sos_kmem_range *kmem_free_range_list, *kmem_used_range_list;
042
043
044 static struct sos_kslab_cache *kmem_range_cache;
045
046
047
048
049
050 static struct sos_kmem_range *
051 get_closest_preceding_kmem_range(struct sos_kmem_range *the_list,
052 sos_vaddr_t vaddr)
053 {
054 int nb_elements;
055 struct sos_kmem_range *a_range, *ret_range;
056
057
058
059 ret_range = NULL;
060 list_foreach(the_list, a_range, nb_elements)
061 {
062 if (vaddr < a_range->base_vaddr)
063 return ret_range;
064 ret_range = a_range;
065 }
066
067
068 return ret_range;
069 }
070
071
072
073
074
075
076 static struct sos_kmem_range *find_suitable_free_range(sos_count_t nb_pages)
077 {
078 int nb_elements;
079 struct sos_kmem_range *r;
080
081 list_foreach(kmem_free_range_list, r, nb_elements)
082 {
083 if (r->nb_pages >= nb_pages)
084 return r;
085 }
086
087 return NULL;
088 }
089
090
091
092
093
094
095
096 static struct sos_kmem_range *insert_range(struct sos_kmem_range *the_list,
097 struct sos_kmem_range *a_range)
098 {
099 struct sos_kmem_range *prec_used;
100
101
102 prec_used = get_closest_preceding_kmem_range(the_list,
103 a_range->base_vaddr);
104
105 if (prec_used != NULL)
106 list_insert_after(the_list, prec_used, a_range);
107 else
108 list_add_head(the_list, a_range);
109
110 return the_list;
111 }
112
113
114
115
116
117
118 static struct sos_kmem_range *lookup_range(sos_vaddr_t vaddr)
119 {
120 struct sos_kmem_range *range;
121
122
123 sos_paddr_t ppage_paddr = sos_paging_get_paddr(vaddr);
124 if (! ppage_paddr)
125 {
126 range = sos_physmem_get_kmem_range(ppage_paddr);
127
128
129 SOS_ASSERT_FATAL(range != NULL);
130 }
131
132
133
134 else
135 {
136 range = get_closest_preceding_kmem_range(kmem_used_range_list,
137 vaddr);
138
139 if (! range)
140 return NULL;
141 }
142
143 return range;
144 }
145
146
147
148
149
150
151
152 static struct sos_kmem_range *
153 create_range(sos_bool_t is_free,
154 sos_vaddr_t base_vaddr,
155 sos_vaddr_t top_addr,
156 struct sos_kslab *associated_slab)
157 {
158 struct sos_kmem_range *range;
159 range = (struct sos_kmem_range*)sos_kmem_cache_alloc(kmem_range_cache,
160 SOS_KSLAB_ALLOC_ATOMIC);
161 SOS_ASSERT_FATAL(range != NULL);
162
163 range->base_vaddr = base_vaddr;
164 range->nb_pages = (top_addr - base_vaddr) / SOS_PAGE_SIZE;
165
166 if (is_free)
167 {
168 list_add_tail(kmem_free_range_list,
169 range);
170 }
171 else
172 {
173 sos_vaddr_t vaddr;
174 range->slab = associated_slab;
175 list_add_tail(kmem_used_range_list,
176 range);
177
178
179 for (vaddr = base_vaddr ;
180 vaddr < top_addr ;
181 vaddr += SOS_PAGE_SIZE)
182 {
183 sos_paddr_t ppage_paddr = sos_paging_get_paddr(vaddr);
184 SOS_ASSERT_FATAL((void*)ppage_paddr != NULL);
185 sos_physmem_set_kmem_range(ppage_paddr, range);
186 }
187 }
188
189 return range;
190 }
191
192
193 sos_ret_t sos_kmem_vmm_setup(sos_vaddr_t kernel_core_base,
194 sos_vaddr_t kernel_core_top)
195 {
196 struct sos_kslab *first_struct_slab_of_caches,
197 *first_struct_slab_of_ranges;
198 sos_vaddr_t first_slab_of_caches_base,
199 first_slab_of_caches_nb_pages,
200 first_slab_of_ranges_base,
201 first_slab_of_ranges_nb_pages;
202 struct sos_kmem_range *first_range_of_caches,
203 *first_range_of_ranges;
204
205 list_init(kmem_free_range_list);
206 list_init(kmem_used_range_list);
207
208 kmem_range_cache
209 = sos_kmem_cache_setup_prepare(kernel_core_base,
210 kernel_core_top,
211 sizeof(struct sos_kmem_range),
212 & first_struct_slab_of_caches,
213 & first_slab_of_caches_base,
214 & first_slab_of_caches_nb_pages,
215 & first_struct_slab_of_ranges,
216 & first_slab_of_ranges_base,
217 & first_slab_of_ranges_nb_pages);
218 SOS_ASSERT_FATAL(kmem_range_cache != NULL);
219
220
221 create_range(TRUE,
222 SOS_KMEM_VMM_BASE,
223 SOS_PAGE_ALIGN_INF(BIOS_N_VIDEO_START),
224 NULL);
225
226
227 create_range(FALSE,
228 SOS_PAGE_ALIGN_INF(BIOS_N_VIDEO_START),
229 SOS_PAGE_ALIGN_SUP(BIOS_N_VIDEO_END),
230 NULL);
231
232
233 create_range(TRUE,
234 SOS_PAGE_ALIGN_SUP(BIOS_N_VIDEO_END),
235 SOS_PAGE_ALIGN_INF(kernel_core_base),
236 NULL);
237
238
239 create_range(FALSE,
240 SOS_PAGE_ALIGN_INF(kernel_core_base),
241 SOS_PAGE_ALIGN_SUP(kernel_core_top),
242 NULL);
243
244
245
246 SOS_ASSERT_FATAL(SOS_PAGE_ALIGN_SUP(kernel_core_top)
247 == first_slab_of_caches_base);
248 SOS_ASSERT_FATAL(first_struct_slab_of_caches != NULL);
249 first_range_of_caches
250 = create_range(FALSE,
251 first_slab_of_caches_base,
252 first_slab_of_caches_base
253 + first_slab_of_caches_nb_pages*SOS_PAGE_SIZE,
254 first_struct_slab_of_caches);
255
256
257
258 SOS_ASSERT_FATAL((first_slab_of_caches_base
259 + first_slab_of_caches_nb_pages*SOS_PAGE_SIZE)
260 == first_slab_of_ranges_base);
261 SOS_ASSERT_FATAL(first_struct_slab_of_ranges != NULL);
262 first_range_of_ranges
263 = create_range(FALSE,
264 first_slab_of_ranges_base,
265 first_slab_of_ranges_base
266 + first_slab_of_ranges_nb_pages*SOS_PAGE_SIZE,
267 first_struct_slab_of_ranges);
268
269
270 create_range(TRUE,
271 first_slab_of_ranges_base
272 + first_slab_of_ranges_nb_pages*SOS_PAGE_SIZE,
273 SOS_KMEM_VMM_TOP,
274 NULL);
275
276
277
278
279 sos_kmem_cache_setup_commit(first_struct_slab_of_caches,
280 first_range_of_caches,
281 first_struct_slab_of_ranges,
282 first_range_of_ranges);
283
284 return SOS_OK;
285 }
286
287
288
289
290
291
292
293 struct sos_kmem_range *sos_kmem_vmm_new_range(sos_count_t nb_pages,
294 sos_ui32_t flags,
295 sos_vaddr_t * range_start)
296 {
297 struct sos_kmem_range *free_range, *new_range;
298
299 if (nb_pages <= 0)
300 return NULL;
301
302
303 free_range = find_suitable_free_range(nb_pages);
304 if (free_range == NULL)
305 return NULL;
306
307
308
309 if(free_range->nb_pages == nb_pages)
310 {
311 list_delete(kmem_free_range_list, free_range);
312 kmem_used_range_list = insert_range(kmem_used_range_list,
313 free_range);
314
315 new_range = free_range;
316 }
317
318
319
320
321 else
322 {
323
324 new_range = (struct sos_kmem_range*)
325 sos_kmem_cache_alloc(kmem_range_cache,
326 (flags & SOS_KMEM_VMM_ATOMIC)?
327 SOS_KSLAB_ALLOC_ATOMIC:0);
328 if (! new_range)
329 return NULL;
330
331 new_range->base_vaddr = free_range->base_vaddr;
332 new_range->nb_pages = nb_pages;
333 free_range->base_vaddr += nb_pages*SOS_PAGE_SIZE;
334 free_range->nb_pages -= nb_pages;
335
336
337
338 kmem_used_range_list = insert_range(kmem_used_range_list,
339 new_range);
340 }
341
342
343 new_range->slab = NULL;
344
345
346 if (flags & SOS_KMEM_VMM_MAP)
347 {
348 int i;
349 for (i = 0 ; i < nb_pages ; i ++)
350 {
351
352 sos_paddr_t ppage_paddr
353 = sos_physmem_ref_physpage_new(! (flags & SOS_KMEM_VMM_ATOMIC));
354
355
356 if (ppage_paddr)
357 {
358 if (sos_paging_map(ppage_paddr,
359 new_range->base_vaddr
360 + i * SOS_PAGE_SIZE,
361 FALSE ,
362 ((flags & SOS_KMEM_VMM_ATOMIC)?
363 SOS_VM_MAP_ATOMIC:0)
364 | SOS_VM_MAP_PROT_READ
365 | SOS_VM_MAP_PROT_WRITE))
366 {
367
368 sos_physmem_unref_physpage(ppage_paddr);
369 ppage_paddr = (sos_paddr_t)NULL;
370 }
371 else
372 {
373
374
375 sos_physmem_unref_physpage(ppage_paddr);
376 }
377 }
378
379
380 if (! ppage_paddr)
381 {
382 sos_kmem_vmm_del_range(new_range);
383 return NULL;
384 }
385
386
387 sos_physmem_set_kmem_range(ppage_paddr, new_range);
388 }
389 }
390
391
392
393 else
394 SOS_ASSERT_FATAL(! "No demand paging yet");
395
396 if (range_start)
397 *range_start = new_range->base_vaddr;
398
399 return new_range;
400 }
401
402
403 sos_vaddr_t sos_kmem_vmm_del_range(struct sos_kmem_range *range)
404 {
405 int i;
406 struct sos_kmem_range *ranges_to_free;
407 list_init(ranges_to_free);
408
409 SOS_ASSERT_FATAL(range != NULL);
410 SOS_ASSERT_FATAL(range->slab == NULL);
411
412
413 list_delete(kmem_used_range_list, range);
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431 do
432 {
433
434 kmem_free_range_list = insert_range(kmem_free_range_list, range);
435
436
437 for (i = 0 ; i < range->nb_pages ; i ++)
438 {
439
440 sos_paging_unmap(range->base_vaddr + i*SOS_PAGE_SIZE);
441 }
442
443
444
445
446
447
448
449 if (range->prev->base_vaddr + range->prev->nb_pages*SOS_PAGE_SIZE
450 == range->base_vaddr)
451 {
452 struct sos_kmem_range *empty_range_of_ranges = NULL;
453 struct sos_kmem_range *prec_free = range->prev;
454
455
456 prec_free->nb_pages += range->nb_pages;
457 list_delete(kmem_free_range_list, range);
458
459
460
461 empty_range_of_ranges =
462 sos_kmem_cache_release_struct_range(range);
463
464
465
466
467
468 if (empty_range_of_ranges != NULL)
469 {
470 list_delete(kmem_used_range_list, empty_range_of_ranges);
471 list_add_tail(ranges_to_free, empty_range_of_ranges);
472 }
473
474
475 range = prec_free;
476 }
477
478
479
480 if (range->base_vaddr + range->nb_pages*SOS_PAGE_SIZE
481 == range->next->base_vaddr)
482 {
483 struct sos_kmem_range *empty_range_of_ranges = NULL;
484 struct sos_kmem_range *next_range = range->next;
485
486
487 range->nb_pages += next_range->nb_pages;
488 list_delete(kmem_free_range_list, next_range);
489
490
491
492 empty_range_of_ranges =
493 sos_kmem_cache_release_struct_range(next_range);
494
495
496
497
498
499
500 if (empty_range_of_ranges != NULL)
501 {
502 list_delete(kmem_used_range_list, empty_range_of_ranges);
503 list_add_tail(ranges_to_free, empty_range_of_ranges);
504 }
505 }
506
507
508
509
510 if (list_is_empty(ranges_to_free))
511 range = NULL;
512 else
513 range = list_pop_head(ranges_to_free);
514
515 }
516
517 while (range != NULL);
518
519 return SOS_OK;
520 }
521
522
523 sos_vaddr_t sos_kmem_vmm_alloc(sos_count_t nb_pages,
524 sos_ui32_t flags)
525 {
526 struct sos_kmem_range *range
527 = sos_kmem_vmm_new_range(nb_pages,
528 flags,
529 NULL);
530 if (! range)
531 return (sos_vaddr_t)NULL;
532
533 return range->base_vaddr;
534 }
535
536
537 sos_vaddr_t sos_kmem_vmm_free(sos_vaddr_t vaddr)
538 {
539 struct sos_kmem_range *range = lookup_range(vaddr);
540
541
542
543 if (!range || (range->base_vaddr != vaddr))
544 return -SOS_EINVAL;
545
546
547 if (range->slab != NULL)
548 return -SOS_EBUSY;
549
550 return sos_kmem_vmm_del_range(range);
551 }
552
553
554 sos_ret_t sos_kmem_vmm_set_slab(struct sos_kmem_range *range,
555 struct sos_kslab *slab)
556 {
557 if (! range)
558 return -SOS_EINVAL;
559
560 range->slab = slab;
561 return SOS_OK;
562 }
563
564 struct sos_kslab * sos_kmem_vmm_resolve_slab(sos_vaddr_t vaddr)
565 {
566 struct sos_kmem_range *range = lookup_range(vaddr);
567 if (! range)
568 return NULL;
569
570 return range->slab;
571 }
572