Skip to content

Commit ad4db7e

Browse files
committed
Adds vma_alloc_by_addr, uses it to reserve the initial mappings.
Signed-off-by: Nathan Ringo <me@remexre.com>
1 parent c245080 commit ad4db7e

File tree

3 files changed

+133
-0
lines changed

3 files changed

+133
-0
lines changed

src/kernel/include/mm/virtual_alloc.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ struct vma_allocator *vma_allocator_new(uaddr lo, uaddr hi);
2525
*/
2626
void vma_allocator_print(struct vma_allocator *allocator);
2727

28+
/**
29+
* Attempts to allocate the address range lo to hi. Returns a used VMA that
30+
* covers exactly that range on success, or nullptr on failure.
31+
*
32+
* TODO: This should indicate "virtual memory already in use" separately from
33+
* "OOM when trying to allocate a VMA."
34+
*/
35+
struct vma *vma_alloc_by_addr(struct vma_allocator *allocator, uaddr lo,
36+
uaddr hi);
37+
2838
/**
2939
* Allocates a range of virtual address space from the given allocator.
3040
*

src/kernel/main.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ void main(u64 hart_id, paddr devicetree_start, paddr kernel_start,
3131
struct vma_allocator *kernel_virtual_allocator =
3232
vma_allocator_new(0xffffffe000000000, 0xffffffffc0000000);
3333
vma_allocator_print(kernel_virtual_allocator);
34+
assert(vma_alloc_by_addr(kernel_virtual_allocator, 0xffffffe000000000,
35+
0xffffffe000400000));
36+
vma_allocator_print(kernel_virtual_allocator);
37+
assert(vma_alloc_by_addr(kernel_virtual_allocator, 0xffffffe000400000,
38+
0xffffffe000600000));
39+
vma_allocator_print(kernel_virtual_allocator);
3440

3541
TODO();
3642
}

src/kernel/mm/virtual_alloc.c

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,32 @@ static void treap_insert(struct vma *vma, enum which_treap which) {
221221
}
222222
}
223223

224+
/**
225+
* Removes a VMA from a treap.
226+
*/
227+
static void treap_remove(struct vma *vma, enum which_treap which) {
228+
struct treap_head *node = &vma->treaps[which];
229+
assert(node->priority);
230+
231+
if (!node->children[0] && !node->children[1]) {
232+
// We're a leaf; we can simply remove ourselves without needing to
233+
// rebalance.
234+
if (node->parent) {
235+
// We're not the root.
236+
assert(node->parent->treaps[which].children[node->which_child] == vma);
237+
node->parent->treaps[which].children[node->which_child] = nullptr;
238+
} else {
239+
// We're the root.
240+
assert(vma->allocator->roots[which] == vma);
241+
vma->allocator->roots[which] = nullptr;
242+
}
243+
} else {
244+
TODO();
245+
}
246+
247+
treap_init(node, node->value);
248+
}
249+
224250
/**
225251
* Computes the bounds of a VMA.
226252
*/
@@ -315,3 +341,94 @@ void vma_allocator_print(struct vma_allocator *allocator) {
315341
is_vma_free(vma) ? "FREE" : "USED");
316342
}
317343
}
344+
345+
static struct vma *vma_find(struct vma_allocator *allocator, uaddr addr) {
346+
struct vma *vma = allocator->roots[BY_ADDR];
347+
while (vma) {
348+
uaddr start, end;
349+
vma_bounds(vma, &start, &end);
350+
if (addr < start)
351+
vma = vma->treaps[BY_ADDR].children[0];
352+
else if (end <= addr)
353+
vma = vma->treaps[BY_ADDR].children[1];
354+
else
355+
break;
356+
}
357+
return vma;
358+
}
359+
360+
struct vma *vma_alloc_by_addr(struct vma_allocator *allocator, uaddr lo,
361+
uaddr hi) {
362+
assert((lo & 0xfff) == 0 && (hi & 0xfff) == 0);
363+
assert(lo < hi);
364+
365+
struct vma *vma = vma_find(allocator, lo);
366+
if (!vma)
367+
return nullptr;
368+
369+
// If the VMA isn't free, we can't allocate.
370+
if (!is_vma_free(vma))
371+
return nullptr;
372+
373+
// If the same VMA doesn't contain the highest address, there must be another
374+
// allocation inside the range.
375+
uaddr vma_lo, vma_hi;
376+
vma_bounds(vma, &vma_lo, &vma_hi);
377+
if (vma_hi < hi)
378+
return nullptr;
379+
380+
// Now, we want to split the VMA we found to contain the allocation.
381+
//
382+
// The simplest thing to do is to remove the VMA from the treaps, split it,
383+
// and reinsert the split-out parts. We keep it in the list, so that we can
384+
// avoid re-traversing it later.
385+
//
386+
// Before we do, we allocate memory for the new VMAs to be stored, so we don't
387+
// have to "undo" work if allocation fails later.
388+
struct vma *new_vma_lo = nullptr, *new_vma_hi = nullptr;
389+
if (vma_lo != lo)
390+
if (!(new_vma_lo = alloc(sizeof(struct vma))))
391+
goto fail;
392+
if (vma_hi != hi)
393+
if (!(new_vma_hi = alloc(sizeof(struct vma))))
394+
goto fail;
395+
396+
// Save a pointer to the previous VMA (or the allocator), so we can insert all
397+
// the VMAs into the list without searching.
398+
struct list_head *prev = vma->list_head.prev;
399+
400+
// Remove the VMA. This breaks invariants (the allocator may now be empty),
401+
// but we restore them later.
402+
list_remove(&vma->list_head);
403+
treap_remove(vma, BY_ADDR);
404+
if (is_vma_free(vma))
405+
treap_remove(vma, BY_SIZE);
406+
407+
// Initialize the VMAs and insert them into the list and treaps.
408+
if (new_vma_hi) {
409+
vma_init(allocator, hi, vma_hi, new_vma_hi);
410+
list_unshift(prev, &new_vma_hi->list_head);
411+
treap_insert(new_vma_hi, BY_ADDR);
412+
treap_insert(new_vma_hi, BY_SIZE);
413+
}
414+
vma_init(allocator, lo, hi, vma);
415+
list_unshift(prev, &vma->list_head);
416+
treap_insert(vma, BY_ADDR);
417+
if (new_vma_lo) {
418+
vma_init(allocator, vma_lo, lo, new_vma_lo);
419+
list_unshift(prev, &new_vma_lo->list_head);
420+
treap_insert(new_vma_lo, BY_ADDR);
421+
treap_insert(new_vma_lo, BY_SIZE);
422+
}
423+
424+
// Mark our VMA as used.
425+
vma->treaps[BY_SIZE].priority = 0;
426+
427+
// Return the VMA.
428+
return vma;
429+
430+
fail:
431+
free(new_vma_lo);
432+
free(new_vma_hi);
433+
return nullptr;
434+
}

0 commit comments

Comments
 (0)