📄 umem_vmm.c
字号:
/* Copyright (C) 2005 David Decotigny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <sos/assert.h>#include <sos/list.h>#include <sos/physmem.h>#include <sos/kmem_slab.h>#include <drivers/bochs.h>#include <hwcore/mm_context.h>#include <hwcore/paging.h>#include <drivers/zero.h>#include "umem_vmm.h"struct sos_umem_vmm_as{ /** The process that owns this address space */ struct sos_process * process; /** The MMU configuration of this address space */ struct sos_mm_context * mm_context; /** The list of VRs in this address space */ struct sos_umem_vmm_vr * list_vr; /** Heap location */ sos_uaddr_t heap_start; sos_size_t heap_size; /**< Updated by sos_umem_vmm_brk() */ /* Memory usage statistics */ sos_size_t phys_total; /* shared + private */ struct vm_usage { sos_size_t overall; sos_size_t ro, rw, code /* all: non readable, read and read/write */; } vm_total, vm_shrd; /* Page fault counters */ sos_size_t pgflt_cow; sos_size_t pgflt_page_in; sos_size_t pgflt_invalid;};struct sos_umem_vmm_vr{ /** The address space owning this VR */ struct sos_umem_vmm_as *address_space; /** The location of the mapping in user space */ sos_uaddr_t start; sos_size_t size; /** What accesses are allowed (read, write, exec): @see SOS_VM_MAP_PROT_* flags in hwcore/paging.h */ sos_ui32_t access_rights; /** Flags of the VR. Allowed flags: * - SOS_VR_MAP_SHARED */ sos_ui32_t flags; /** * The callbacks for the VR called along map/unmapping of the * resource */ struct sos_umem_vmm_vr_ops *ops; /** Description of the resource being mapped, if any */ struct sos_umem_vmm_mapped_resource *mapped_resource; sos_luoffset_t offset_in_resource; /** The VRs of an AS are linked together and are accessible by way of as->list_vr */ struct sos_umem_vmm_vr *prev_in_as, *next_in_as; /** The VRs mapping a given resource are linked together and are accessible by way of mapped_resource->list_vr */ struct sos_umem_vmm_vr *prev_in_mapped_resource, *next_in_mapped_resource;};/* * We use special slab caches to allocate AS and VR data structures */static struct sos_kslab_cache * cache_of_as;static struct sos_kslab_cache * cache_of_vr;/** Temporary function to debug: list the VRs of the given As */void sos_dump_as(const struct sos_umem_vmm_as * as, const char *str){ struct sos_umem_vmm_vr *vr; int nb_vr; sos_bochs_printf("AS %p - %s:\n", as, str); sos_bochs_printf(" physical mem: %x\n", as->phys_total); sos_bochs_printf(" VM (all/ro+rw/exec) tot:%x/%x+%x/%x shrd:%x/%x+%x/%x\n", as->vm_total.overall, as->vm_total.ro, as->vm_total.rw, as->vm_total.code, as->vm_shrd.overall, as->vm_shrd.ro, as->vm_shrd.rw, as->vm_shrd.code); sos_bochs_printf(" pgflt cow=%d pgin=%d inv=%d\n", as->pgflt_cow, as->pgflt_page_in, as->pgflt_invalid); list_foreach_named(as->list_vr, vr, nb_vr, prev_in_as, next_in_as) { sos_bochs_printf(" VR[%d]=%x: [%x,%x[ (sz=%x) mr=(%x)+%llx %c%c%c fl=%x\n", nb_vr, (unsigned)vr, vr->start, vr->start + vr->size, vr->size, (unsigned)vr->mapped_resource, vr->offset_in_resource, (vr->access_rights & SOS_VM_MAP_PROT_READ)?'r':'-', (vr->access_rights & SOS_VM_MAP_PROT_WRITE)?'w':'-', (vr->access_rights & SOS_VM_MAP_PROT_EXEC)?'x':'-', (unsigned)vr->flags); } sos_bochs_printf("FIN (%s)\n", str);}/** * Physical address of THE page (full of 0s) used for anonymous * mappings */sos_paddr_t sos_zero_page = 0 /* Initial value prior to allocation */;/* * Helper functions defined at the bottom of the file *//** * Helper function to retrieve the first VR to have a vr->end >= uaddr */static struct sos_umem_vmm_vr *find_enclosing_or_next_vr(struct sos_umem_vmm_as * as, sos_uaddr_t uaddr);/** * Helper function to retrieve the first VR that overlaps the given * interval, if any */static struct sos_umem_vmm_vr *find_first_intersecting_vr(struct sos_umem_vmm_as * as, sos_uaddr_t start_uaddr, sos_size_t size);/** * Helper function to find first address where there is enough * space. Begin to look for such an interval at or after the given * address * * @param hint_addr The address where to begin the scan, or NULL */static sos_uaddr_tfind_first_free_interval(struct sos_umem_vmm_as * as, sos_uaddr_t hint_uaddr, sos_size_t size);/** Called each time a VR of the AS changes. Don't cope with any underlying physcal mapping/unmapping, COW, etc... */static voidas_account_change_of_vr_protection(struct sos_umem_vmm_as * as, sos_bool_t is_shared, sos_size_t size, sos_ui32_t prev_access_rights, sos_ui32_t new_access_rights);sos_ret_t sos_umem_vmm_subsystem_setup(){ sos_vaddr_t vaddr_zero_page; /* Allocate a new kernel physical page mapped into kernel space and reset it with 0s */ vaddr_zero_page = sos_kmem_vmm_alloc(1, SOS_KMEM_VMM_MAP); if (vaddr_zero_page == (sos_vaddr_t)NULL) return -SOS_ENOMEM; memset((void*)vaddr_zero_page, 0x0, SOS_PAGE_SIZE); /* Keep a reference to the underlying pphysical page... */ sos_zero_page = sos_paging_get_paddr(vaddr_zero_page); SOS_ASSERT_FATAL(NULL != (void*)sos_zero_page); sos_physmem_ref_physpage_at(sos_zero_page); /* ... but it is not needed in kernel space anymore, so we can safely unmap it from kernel space */ sos_paging_unmap(vaddr_zero_page); /* Allocate the VR/AS caches */ cache_of_as = sos_kmem_cache_create("Address space structures", sizeof(struct sos_umem_vmm_as), 1, 0, SOS_KSLAB_CREATE_MAP | SOS_KSLAB_CREATE_ZERO); if (! cache_of_as) { sos_physmem_unref_physpage(sos_zero_page); return -SOS_ENOMEM; } cache_of_vr = sos_kmem_cache_create("Virtual Region structures", sizeof(struct sos_umem_vmm_vr), 1, 0, SOS_KSLAB_CREATE_MAP | SOS_KSLAB_CREATE_ZERO); if (! cache_of_vr) { sos_physmem_unref_physpage(sos_zero_page); sos_kmem_cache_destroy(cache_of_as); return -SOS_ENOMEM; } return SOS_OK;}struct sos_umem_vmm_as *sos_umem_vmm_create_empty_as(struct sos_process *owner){ struct sos_umem_vmm_as * as = (struct sos_umem_vmm_as *) sos_kmem_cache_alloc(cache_of_as, 0); if (! as) return NULL; as->mm_context = sos_mm_context_create(); if (NULL == as->mm_context) { /* Error */ sos_kmem_cache_free((sos_vaddr_t)as); return NULL; } as->process = owner; return as;}struct sos_umem_vmm_as *sos_umem_vmm_duplicate_current_thread_as(struct sos_process *owner){ __label__ undo_creation; struct sos_umem_vmm_as * my_as; struct sos_umem_vmm_vr * model_vr; int nb_vr; struct sos_umem_vmm_as * new_as = (struct sos_umem_vmm_as *) sos_kmem_cache_alloc(cache_of_as, 0); if (! new_as) return NULL; my_as = sos_process_get_address_space(sos_thread_get_current()->process); new_as->process = owner; list_init_named(new_as->list_vr, prev_in_as, next_in_as); /* * Switch to the current threads' mm_context, as duplicating it implies * being able to configure some of its mappings as read-only (for * COW) */ SOS_ASSERT_FATAL(SOS_OK == sos_thread_prepare_user_space_access(my_as, (sos_vaddr_t) NULL)); /* Copy the virtual regions */ list_foreach_named(my_as->list_vr, model_vr, nb_vr, prev_in_as, next_in_as) { struct sos_umem_vmm_vr * vr; /* Prepare COW on the read/write private mappings */ if ( !(model_vr->flags & SOS_VR_MAP_SHARED) && (model_vr->access_rights & SOS_VM_MAP_PROT_WRITE) ) { /* Mark the underlying physical pages (if any) as read-only */ SOS_ASSERT_FATAL(SOS_OK == sos_paging_prepare_COW(model_vr->start, model_vr->size)); } /* Allocate a new virtual region and copy the 'model' into it */ vr = (struct sos_umem_vmm_vr *) sos_kmem_cache_alloc(cache_of_vr, 0); if (! vr) goto undo_creation; memcpy(vr, model_vr, sizeof(*vr)); vr->address_space = new_as; /* Signal the "new" mapping to the underlying VR mapper */ if (vr->ops && vr->ops->ref) vr->ops->ref(vr); /* Insert the new VR into the new AS */ list_add_tail_named(new_as->list_vr, vr, prev_in_as, next_in_as); /* Insert the new VR into the list of mappings of the resource */ list_add_tail_named(model_vr->mapped_resource->list_vr, vr, prev_in_mapped_resource, next_in_mapped_resource); } /* Now copy the current MMU configuration */ new_as->mm_context = sos_mm_context_duplicate(my_as->mm_context); if (NULL == new_as->mm_context) goto undo_creation; /* Correct behavior */ new_as->heap_start = my_as->heap_start; new_as->heap_size = my_as->heap_size; new_as->phys_total = my_as->phys_total; memcpy(& new_as->vm_total, & my_as->vm_total, sizeof(struct vm_usage)); memcpy(& new_as->vm_shrd, & my_as->vm_shrd, sizeof(struct vm_usage)); SOS_ASSERT_FATAL(SOS_OK == sos_thread_end_user_space_access()); return new_as; /* Handle erroneous behavior */ undo_creation: SOS_ASSERT_FATAL(SOS_OK == sos_thread_end_user_space_access()); sos_umem_vmm_delete_as(new_as); return NULL;}sos_ret_tsos_umem_vmm_delete_as(struct sos_umem_vmm_as * as){ while(! list_is_empty_named(as->list_vr, prev_in_as, next_in_as)) { struct sos_umem_vmm_vr * vr; vr = list_get_head_named(as->list_vr, prev_in_as, next_in_as); /* Remove the vr from the lists */ list_pop_head_named(as->list_vr, prev_in_as, next_in_as); list_delete_named(vr->mapped_resource->list_vr, vr, prev_in_mapped_resource, next_in_mapped_resource); /* Signal to the underlying VR mapper that the mapping is suppressed */ if (vr->ops) { if (vr->ops->unmap) vr->ops->unmap(vr, vr->start, vr->size); if (vr->ops->unref) vr->ops->unref(vr); } sos_kmem_cache_free((sos_vaddr_t)vr); } /* Release MMU configuration */ if (as->mm_context) sos_mm_context_unref(as->mm_context); /* Now unallocate main address space construct */ sos_kmem_cache_free((sos_vaddr_t)as); return SOS_OK;}struct sos_process *sos_umem_vmm_get_process(struct sos_umem_vmm_as * as){ return as->process;}struct sos_mm_context *sos_umem_vmm_get_mm_context(struct sos_umem_vmm_as * as){ return as->mm_context;}struct sos_umem_vmm_vr *sos_umem_vmm_get_vr_at_address(struct sos_umem_vmm_as * as, sos_uaddr_t uaddr){ struct sos_umem_vmm_vr * vr; vr = find_enclosing_or_next_vr(as, uaddr); if (! vr) return NULL; /* Ok uaddr <= vr->end, but do we have uaddr > vr->start ? */ if (uaddr < vr->start) return NULL; return vr;}struct sos_umem_vmm_as *sos_umem_vmm_get_as_of_vr(struct sos_umem_vmm_vr * vr){ return vr->address_space;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -