Skip to content

Commit 1595383

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 c81df5c commit 1595383

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
@@ -31,6 +31,16 @@ struct vma_allocator *vma_allocator_new(uaddr lo, uaddr hi);
3131
*/
3232
void vma_allocator_print(struct vma_allocator *allocator);
3333

34+
/**
35+
* Attempts to allocate the address range lo to hi. Returns a used VMA that
36+
* covers exactly that range on success, or nullptr on failure.
37+
*
38+
* TODO: This should indicate "virtual memory already in use" separately from
39+
* "OOM when trying to allocate a VMA."
40+
*/
41+
struct vma *vma_alloc_by_addr(struct vma_allocator *allocator, uaddr lo,
42+
uaddr hi);
43+
3444
/**
3545
* Allocates a range of virtual address space from the given allocator.
3646
*

src/kernel/main.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ void main(u64 hart_id, paddr devicetree_start, paddr kernel_start,
3737
struct vma_allocator *kernel_virtual_allocator =
3838
vma_allocator_new(0xffffffe000000000, 0xffffffffc0000000);
3939
vma_allocator_print(kernel_virtual_allocator);
40+
assert(vma_alloc_by_addr(kernel_virtual_allocator, 0xffffffe000000000,
41+
0xffffffe000400000));
42+
vma_allocator_print(kernel_virtual_allocator);
43+
assert(vma_alloc_by_addr(kernel_virtual_allocator, 0xffffffe000400000,
44+
0xffffffe000600000));
45+
vma_allocator_print(kernel_virtual_allocator);
4046

4147
TODO();
4248
}

src/kernel/mm/virtual_alloc.c

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

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

0 commit comments

Comments
 (0)