Skip to content

Commit 1b5db3c

Browse files
committed
Merge branch 'lab8' into 310551038
2 parents 4f696ab + f5012a6 commit 1b5db3c

File tree

9 files changed

+345
-54
lines changed

9 files changed

+345
-54
lines changed

lab6/c/include/oscos/mem/vm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ void vm_drop_addr_space(vm_addr_space_t pgd);
5555
vm_map_page_result_t vm_map_page(vm_addr_space_t *addr_space, void *va);
5656
vm_map_page_result_t vm_handle_permission_fault(vm_addr_space_t *addr_space,
5757
void *va, int access_mode);
58+
bool vm_remove_region(vm_addr_space_t *addr_space, void *start_va);
5859
void vm_switch_to_addr_space(const vm_addr_space_t *addr_space);
5960

6061
void *vm_decide_mmap_addr(vm_addr_space_t addr_space, void *va, size_t len);

lab6/c/src/mem/vm.c

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ static void _vm_drop_page_table(page_table_entry_t *const page_table,
128128
for (size_t i = 0; i < 512; i++) {
129129
if (page_table[i].b0) {
130130
const pa_t next_level_pa = page_table[i].addr << PAGE_ORDER;
131-
if (level == 1) {
131+
if (level == 0) {
132132
shared_page_decref(pa_to_page_id(next_level_pa));
133133
} else {
134134
page_table_entry_t *const next_level_page_table =
@@ -511,6 +511,113 @@ vm_handle_permission_fault(vm_addr_space_t *const addr_space, void *const va,
511511
}
512512
}
513513

514+
static page_table_entry_t *
515+
_vm_remove_region_from_pgd_rec(page_table_entry_t *const page_table,
516+
void *const start_va, void *const end_va,
517+
size_t level, void *const block_start,
518+
page_table_entry_t *const prev_level_entry) {
519+
void *const block_end = (char *)block_start + (1 << (9 * level + PAGE_ORDER));
520+
521+
if (start_va == block_start && end_va == block_end) {
522+
_vm_drop_page_table(page_table, level);
523+
if (prev_level_entry) {
524+
prev_level_entry->b0 = false;
525+
}
526+
return NULL;
527+
} else {
528+
page_table_entry_t *const new_page_table =
529+
level == 0 ? _vm_clone_unshare_pte(page_table)
530+
: _vm_clone_unshare_page_table(page_table);
531+
if (!new_page_table)
532+
return NULL;
533+
534+
if (prev_level_entry) {
535+
prev_level_entry->addr = kernel_va_to_pa(new_page_table) >> PAGE_ORDER;
536+
537+
// Since the page table is not shared, we can remove the read-only bit
538+
// now.
539+
540+
union {
541+
table_descriptor_upper_t s;
542+
unsigned u;
543+
} upper = {.u = prev_level_entry->upper};
544+
upper.s.aptable = 0x0;
545+
prev_level_entry->upper = upper.u;
546+
}
547+
548+
size_t subblock_stride = 1 << (9 * (level - 1) + PAGE_ORDER);
549+
550+
for (size_t i = 0; i < 512; i++) {
551+
if (new_page_table[i].b0) {
552+
void *const subblock_start = (char *)block_start + i * subblock_stride,
553+
*const subblock_end =
554+
(char *)subblock_start + subblock_stride;
555+
556+
void *const max_start =
557+
start_va > subblock_start ? start_va : subblock_start,
558+
*const min_end =
559+
end_va < subblock_end ? end_va : subblock_end;
560+
561+
if (max_start < min_end) {
562+
if (level == 0) {
563+
const page_id_t page_id =
564+
pa_to_page_id(new_page_table[i].addr << PAGE_ORDER);
565+
shared_page_decref(page_id);
566+
} else {
567+
page_table_entry_t *const next_level_page_table =
568+
pa_to_kernel_va(new_page_table[i].addr << PAGE_ORDER);
569+
_vm_remove_region_from_pgd_rec(next_level_page_table, max_start,
570+
min_end, level - 1, subblock_start,
571+
&new_page_table[i]);
572+
}
573+
}
574+
}
575+
}
576+
577+
return new_page_table;
578+
}
579+
}
580+
581+
static page_table_entry_t *
582+
_vm_remove_region_from_pgd(page_table_entry_t *const pgd, void *const start_va,
583+
void *const end_va) {
584+
return _vm_remove_region_from_pgd_rec(pgd, start_va, end_va, 3, (void *)0,
585+
NULL);
586+
}
587+
588+
bool vm_remove_region(vm_addr_space_t *const addr_space, void *const start_va) {
589+
uint64_t daif_val;
590+
CRITICAL_SECTION_ENTER(daif_val);
591+
592+
const mem_region_t *const mem_region = rb_search(
593+
addr_space->mem_regions.root, start_va,
594+
(int (*)(const void *, const void *, void *))_vm_cmp_va_and_mem_region,
595+
NULL);
596+
void *const end_va = (char *)start_va + mem_region->len;
597+
598+
rb_delete(
599+
&addr_space->mem_regions.root, start_va,
600+
(int (*)(const void *, const void *, void *))_vm_cmp_va_and_mem_region,
601+
NULL);
602+
603+
page_table_entry_t *const new_pgd =
604+
_vm_remove_region_from_pgd(addr_space->pgd, start_va, end_va);
605+
// Note: `_vm_remove_region_from_pgd` returns NULL when the last region is
606+
// removed, i.e., a new page table need not be allocated. However, in all
607+
// places where `vm_remove_region` is used, the region to be removed is never
608+
// the last one. Therefore, checking for an out-of-memory condition in this
609+
// way is fine.
610+
if (!new_pgd) {
611+
CRITICAL_SECTION_LEAVE(daif_val);
612+
return false;
613+
}
614+
615+
addr_space->pgd = new_pgd;
616+
617+
CRITICAL_SECTION_LEAVE(daif_val);
618+
return true;
619+
}
620+
514621
void vm_switch_to_addr_space(const vm_addr_space_t *const addr_space) {
515622
const pa_t pgd_pa = kernel_va_to_pa(addr_space->pgd);
516623
__asm__ __volatile__(

lab6/c/src/sched/sched.c

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -286,29 +286,18 @@ void switch_vm(const thread_t *const thread) {
286286
}
287287

288288
static void _exec_generic(const void *const text_start, const size_t text_len,
289-
const bool free_old_pages) {
289+
const bool remove_text_region) {
290290
thread_t *const curr_thread = current_thread();
291291
process_t *const curr_process = curr_thread->process;
292292

293-
// Allocate memory.
293+
// Remove old text region.
294294

295-
vm_addr_space_t addr_space = {.pgd = NULL};
296-
if (free_old_pages) {
297-
addr_space = vm_new_addr_space();
298-
if (!addr_space.pgd)
295+
if (remove_text_region) {
296+
if (!vm_remove_region(&curr_process->addr_space, (void *)0x0))
299297
return;
300-
}
301-
302-
// Free old pages.
303-
304-
if (free_old_pages) {
305-
vm_drop_addr_space(curr_process->addr_space);
306-
}
307-
308-
// Set process data.
309298

310-
if (free_old_pages) {
311-
curr_process->addr_space = addr_space;
299+
// Set ttbr0_el1 again, as it might have changed.
300+
vm_switch_to_addr_space(&curr_process->addr_space);
312301
}
313302

314303
// We don't know exactly how large the .bss section of a user program is, so

lab7/c/include/oscos/mem/vm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ void vm_drop_addr_space(vm_addr_space_t pgd);
5555
vm_map_page_result_t vm_map_page(vm_addr_space_t *addr_space, void *va);
5656
vm_map_page_result_t vm_handle_permission_fault(vm_addr_space_t *addr_space,
5757
void *va, int access_mode);
58+
bool vm_remove_region(vm_addr_space_t *addr_space, void *start_va);
5859
void vm_switch_to_addr_space(const vm_addr_space_t *addr_space);
5960

6061
void *vm_decide_mmap_addr(vm_addr_space_t addr_space, void *va, size_t len);

lab7/c/src/mem/vm.c

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ static void _vm_drop_page_table(page_table_entry_t *const page_table,
128128
for (size_t i = 0; i < 512; i++) {
129129
if (page_table[i].b0) {
130130
const pa_t next_level_pa = page_table[i].addr << PAGE_ORDER;
131-
if (level == 1) {
131+
if (level == 0) {
132132
shared_page_decref(pa_to_page_id(next_level_pa));
133133
} else {
134134
page_table_entry_t *const next_level_page_table =
@@ -511,6 +511,113 @@ vm_handle_permission_fault(vm_addr_space_t *const addr_space, void *const va,
511511
}
512512
}
513513

514+
static page_table_entry_t *
515+
_vm_remove_region_from_pgd_rec(page_table_entry_t *const page_table,
516+
void *const start_va, void *const end_va,
517+
size_t level, void *const block_start,
518+
page_table_entry_t *const prev_level_entry) {
519+
void *const block_end = (char *)block_start + (1 << (9 * level + PAGE_ORDER));
520+
521+
if (start_va == block_start && end_va == block_end) {
522+
_vm_drop_page_table(page_table, level);
523+
if (prev_level_entry) {
524+
prev_level_entry->b0 = false;
525+
}
526+
return NULL;
527+
} else {
528+
page_table_entry_t *const new_page_table =
529+
level == 0 ? _vm_clone_unshare_pte(page_table)
530+
: _vm_clone_unshare_page_table(page_table);
531+
if (!new_page_table)
532+
return NULL;
533+
534+
if (prev_level_entry) {
535+
prev_level_entry->addr = kernel_va_to_pa(new_page_table) >> PAGE_ORDER;
536+
537+
// Since the page table is not shared, we can remove the read-only bit
538+
// now.
539+
540+
union {
541+
table_descriptor_upper_t s;
542+
unsigned u;
543+
} upper = {.u = prev_level_entry->upper};
544+
upper.s.aptable = 0x0;
545+
prev_level_entry->upper = upper.u;
546+
}
547+
548+
size_t subblock_stride = 1 << (9 * (level - 1) + PAGE_ORDER);
549+
550+
for (size_t i = 0; i < 512; i++) {
551+
if (new_page_table[i].b0) {
552+
void *const subblock_start = (char *)block_start + i * subblock_stride,
553+
*const subblock_end =
554+
(char *)subblock_start + subblock_stride;
555+
556+
void *const max_start =
557+
start_va > subblock_start ? start_va : subblock_start,
558+
*const min_end =
559+
end_va < subblock_end ? end_va : subblock_end;
560+
561+
if (max_start < min_end) {
562+
if (level == 0) {
563+
const page_id_t page_id =
564+
pa_to_page_id(new_page_table[i].addr << PAGE_ORDER);
565+
shared_page_decref(page_id);
566+
} else {
567+
page_table_entry_t *const next_level_page_table =
568+
pa_to_kernel_va(new_page_table[i].addr << PAGE_ORDER);
569+
_vm_remove_region_from_pgd_rec(next_level_page_table, max_start,
570+
min_end, level - 1, subblock_start,
571+
&new_page_table[i]);
572+
}
573+
}
574+
}
575+
}
576+
577+
return new_page_table;
578+
}
579+
}
580+
581+
static page_table_entry_t *
582+
_vm_remove_region_from_pgd(page_table_entry_t *const pgd, void *const start_va,
583+
void *const end_va) {
584+
return _vm_remove_region_from_pgd_rec(pgd, start_va, end_va, 3, (void *)0,
585+
NULL);
586+
}
587+
588+
bool vm_remove_region(vm_addr_space_t *const addr_space, void *const start_va) {
589+
uint64_t daif_val;
590+
CRITICAL_SECTION_ENTER(daif_val);
591+
592+
const mem_region_t *const mem_region = rb_search(
593+
addr_space->mem_regions.root, start_va,
594+
(int (*)(const void *, const void *, void *))_vm_cmp_va_and_mem_region,
595+
NULL);
596+
void *const end_va = (char *)start_va + mem_region->len;
597+
598+
rb_delete(
599+
&addr_space->mem_regions.root, start_va,
600+
(int (*)(const void *, const void *, void *))_vm_cmp_va_and_mem_region,
601+
NULL);
602+
603+
page_table_entry_t *const new_pgd =
604+
_vm_remove_region_from_pgd(addr_space->pgd, start_va, end_va);
605+
// Note: `_vm_remove_region_from_pgd` returns NULL when the last region is
606+
// removed, i.e., a new page table need not be allocated. However, in all
607+
// places where `vm_remove_region` is used, the region to be removed is never
608+
// the last one. Therefore, checking for an out-of-memory condition in this
609+
// way is fine.
610+
if (!new_pgd) {
611+
CRITICAL_SECTION_LEAVE(daif_val);
612+
return false;
613+
}
614+
615+
addr_space->pgd = new_pgd;
616+
617+
CRITICAL_SECTION_LEAVE(daif_val);
618+
return true;
619+
}
620+
514621
void vm_switch_to_addr_space(const vm_addr_space_t *const addr_space) {
515622
const pa_t pgd_pa = kernel_va_to_pa(addr_space->pgd);
516623
__asm__ __volatile__(

lab7/c/src/sched/sched.c

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -290,29 +290,18 @@ void switch_vm(const thread_t *const thread) {
290290
}
291291

292292
static void _exec_generic(const void *const text_start, const size_t text_len,
293-
const bool free_old_pages) {
293+
const bool remove_text_region) {
294294
thread_t *const curr_thread = current_thread();
295295
process_t *const curr_process = curr_thread->process;
296296

297-
// Allocate memory.
297+
// Remove old text region.
298298

299-
vm_addr_space_t addr_space = {.pgd = NULL};
300-
if (free_old_pages) {
301-
addr_space = vm_new_addr_space();
302-
if (!addr_space.pgd)
299+
if (remove_text_region) {
300+
if (!vm_remove_region(&curr_process->addr_space, (void *)0x0))
303301
return;
304-
}
305-
306-
// Free old pages.
307-
308-
if (free_old_pages) {
309-
vm_drop_addr_space(curr_process->addr_space);
310-
}
311-
312-
// Set process data.
313302

314-
if (free_old_pages) {
315-
curr_process->addr_space = addr_space;
303+
// Set ttbr0_el1 again, as it might have changed.
304+
vm_switch_to_addr_space(&curr_process->addr_space);
316305
}
317306

318307
// We don't know exactly how large the .bss section of a user program is, so

lab8/c/include/oscos/mem/vm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ void vm_drop_addr_space(vm_addr_space_t pgd);
5555
vm_map_page_result_t vm_map_page(vm_addr_space_t *addr_space, void *va);
5656
vm_map_page_result_t vm_handle_permission_fault(vm_addr_space_t *addr_space,
5757
void *va, int access_mode);
58+
bool vm_remove_region(vm_addr_space_t *addr_space, void *start_va);
5859
void vm_switch_to_addr_space(const vm_addr_space_t *addr_space);
5960

6061
void *vm_decide_mmap_addr(vm_addr_space_t addr_space, void *va, size_t len);

0 commit comments

Comments
 (0)