@@ -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+
514621void 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__(
0 commit comments