322 lines
8.1 KiB
C
322 lines
8.1 KiB
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 <hwcore/paging.h>
|
|
#include <hwcore/irq.h>
|
|
|
|
#include <sos/assert.h>
|
|
#include <sos/list.h>
|
|
#include <sos/klibc.h>
|
|
#include <sos/physmem.h>
|
|
#include <sos/kmem_slab.h>
|
|
#include <sos/kmem_vmm.h>
|
|
|
|
#include "mm_context.h"
|
|
|
|
|
|
/**
|
|
* Definition of an MMU context.
|
|
*/
|
|
struct sos_mm_context
|
|
{
|
|
/** Physical address of the PD for this MMU context */
|
|
sos_paddr_t paddr_PD;
|
|
|
|
/** Virtual address where it is mapped into the Kernel space */
|
|
sos_vaddr_t vaddr_PD;
|
|
|
|
/** Reference counter for this mm_context */
|
|
sos_ui32_t ref_cnt;
|
|
|
|
/** List of MMU contexts in the system */
|
|
struct sos_mm_context *prev, *next;
|
|
};
|
|
|
|
|
|
/**
|
|
* The cache of mm_context structures
|
|
*/
|
|
struct sos_kslab_cache * cache_struct_mm_context;
|
|
|
|
|
|
/**
|
|
* The current MMU context corresponding to the current configuration
|
|
* of the MMU.
|
|
*/
|
|
static struct sos_mm_context *current_mm_context = NULL;
|
|
|
|
|
|
/**
|
|
* System-wide list of all the mm_contexts in the system
|
|
*/
|
|
static struct sos_mm_context *list_mm_context = NULL;
|
|
/* The "= NULL" here is FUNDAMENTAL, because paging.c must work
|
|
correctly, ie synch_PDE below must behave reasonably (eg do
|
|
nothing), until the mm_context subsystem has been initialized. */
|
|
|
|
|
|
sos_ret_t sos_mm_context_subsystem_setup()
|
|
{
|
|
struct sos_mm_context * initial_mm_context;
|
|
sos_ret_t retval;
|
|
|
|
/* Create the new mm_context cache */
|
|
cache_struct_mm_context = sos_kmem_cache_create("struct mm_context",
|
|
sizeof(struct sos_mm_context),
|
|
1, 0,
|
|
SOS_KSLAB_CREATE_MAP);
|
|
if (NULL == cache_struct_mm_context)
|
|
return -SOS_ENOMEM;
|
|
|
|
/*
|
|
* Allocate the initial mm_context structure
|
|
*/
|
|
initial_mm_context
|
|
= (struct sos_mm_context*) sos_kmem_cache_alloc(cache_struct_mm_context,
|
|
SOS_KSLAB_ALLOC_ATOMIC);
|
|
if (NULL == initial_mm_context)
|
|
return -SOS_ENOMEM;
|
|
|
|
/* Retrieve the address of the current page where the PD lies */
|
|
initial_mm_context->paddr_PD = sos_paging_get_current_PD_paddr();
|
|
|
|
/*
|
|
* Map it somewhere in kernel virtual memory
|
|
*/
|
|
|
|
/* Allocate 1 page of kernel Virtual memory */
|
|
initial_mm_context->vaddr_PD = sos_kmem_vmm_alloc(1, 0);
|
|
if (initial_mm_context->vaddr_PD == 0)
|
|
return -SOS_ENOMEM;
|
|
|
|
/* Map the PD at this virtual address: it will thus be mapped 2
|
|
times (1 time for the mirroring, 1 time for mm_context) ! */
|
|
retval = sos_paging_map(initial_mm_context->paddr_PD,
|
|
initial_mm_context->vaddr_PD,
|
|
FALSE,
|
|
SOS_VM_MAP_PROT_READ
|
|
| SOS_VM_MAP_PROT_WRITE);
|
|
if (SOS_OK != retval)
|
|
return retval;
|
|
|
|
/* Initialize the initial list of mm_contexts */
|
|
list_singleton(list_mm_context, initial_mm_context);
|
|
|
|
/* We just created this mm_context: mark it as "referenced" */
|
|
initial_mm_context->ref_cnt ++;
|
|
|
|
/* We are actually already using it ! */
|
|
initial_mm_context->ref_cnt ++; /* ie reference it a 2nd time ! */
|
|
current_mm_context = initial_mm_context;
|
|
|
|
return SOS_OK;
|
|
}
|
|
|
|
|
|
struct sos_mm_context * sos_mm_context_create(void)
|
|
{
|
|
sos_ui32_t flags;
|
|
struct sos_mm_context *mmctxt;
|
|
|
|
/*
|
|
* Allocate the initial mm_context structure
|
|
*/
|
|
mmctxt = (struct sos_mm_context*) sos_kmem_cache_alloc(cache_struct_mm_context, 0);
|
|
if (NULL == mmctxt)
|
|
return NULL;
|
|
|
|
/* Allocate a new page for the new PD and map it into the kernel */
|
|
mmctxt->vaddr_PD = sos_kmem_vmm_alloc(1, SOS_KMEM_VMM_MAP);
|
|
if (mmctxt->vaddr_PD == 0)
|
|
{
|
|
sos_kmem_cache_free((sos_vaddr_t) mmctxt);
|
|
return NULL;
|
|
}
|
|
|
|
/* Retrieve its physical address */
|
|
mmctxt->paddr_PD = sos_paging_get_paddr(mmctxt->vaddr_PD);
|
|
if (mmctxt->paddr_PD == 0)
|
|
{
|
|
sos_kmem_cache_free((sos_vaddr_t) mmctxt->vaddr_PD);
|
|
sos_kmem_cache_free((sos_vaddr_t) mmctxt);
|
|
return NULL;
|
|
}
|
|
|
|
/* Copy the current hardware MMU address translation tables */
|
|
if (SOS_OK != sos_paging_copy_kernel_space(mmctxt->vaddr_PD,
|
|
current_mm_context->vaddr_PD))
|
|
{
|
|
sos_kmem_cache_free((sos_vaddr_t) mmctxt->vaddr_PD);
|
|
sos_kmem_cache_free((sos_vaddr_t) mmctxt);
|
|
return NULL;
|
|
}
|
|
|
|
/* Mark the mm_context as "referenced" */
|
|
mmctxt->ref_cnt = 1;
|
|
|
|
/* Add it to the list of MMU contexts */
|
|
sos_disable_IRQs(flags);
|
|
list_add_tail(list_mm_context, mmctxt);
|
|
sos_restore_IRQs(flags);
|
|
|
|
return mmctxt;
|
|
}
|
|
|
|
|
|
struct sos_mm_context *
|
|
sos_mm_context_duplicate(const struct sos_mm_context *model)
|
|
{
|
|
struct sos_mm_context *mmctxt;
|
|
|
|
/* Create an mm_context, the kernel space will be copied in it */
|
|
mmctxt = sos_mm_context_create();
|
|
if (NULL == mmctxt)
|
|
return NULL;
|
|
|
|
/* Copy the user-space configuration of the MMU */
|
|
if (SOS_OK != sos_paging_copy_user_space(mmctxt->vaddr_PD,
|
|
model->vaddr_PD))
|
|
{
|
|
sos_mm_context_unref(mmctxt);
|
|
return NULL;
|
|
}
|
|
|
|
return mmctxt;
|
|
}
|
|
|
|
|
|
sos_ret_t sos_mm_context_unref(struct sos_mm_context *mmctxt)
|
|
{
|
|
sos_ui32_t flags;
|
|
|
|
sos_disable_IRQs(flags);
|
|
|
|
/* A valid mmctxt is one which is not yet unreferenced */
|
|
SOS_ASSERT_FATAL(mmctxt->ref_cnt > 0);
|
|
|
|
/* Unreference it */
|
|
mmctxt->ref_cnt --;
|
|
|
|
/* If somebody is still using it, don't release it now */
|
|
if (mmctxt->ref_cnt > 0)
|
|
{
|
|
sos_restore_IRQs(flags);
|
|
return SOS_OK;
|
|
}
|
|
|
|
/* If nobody uses it, then it cannot be the current mm_context ! */
|
|
SOS_ASSERT_FATAL(mmctxt != current_mm_context);
|
|
|
|
/* Remove it from the list of mm_contexts */
|
|
list_delete(list_mm_context, mmctxt);
|
|
|
|
sos_restore_IRQs(flags);
|
|
|
|
/* Remove all user mappings (if any) */
|
|
sos_paging_dispose(mmctxt->vaddr_PD);
|
|
|
|
/* Unmap the PD from the kernel */
|
|
sos_kmem_vmm_free(mmctxt->vaddr_PD);
|
|
|
|
memset(mmctxt, 0x0, sizeof(*mmctxt));
|
|
|
|
return SOS_OK;
|
|
}
|
|
|
|
|
|
sos_ret_t sos_mm_context_ref(struct sos_mm_context *mmctxt)
|
|
{
|
|
sos_ui32_t flags;
|
|
|
|
sos_disable_IRQs(flags);
|
|
|
|
/* A valid mmctxt is one which is not yet unreferenced */
|
|
SOS_ASSERT_FATAL(mmctxt->ref_cnt > 0);
|
|
|
|
/* Reference it once again */
|
|
mmctxt->ref_cnt ++;
|
|
|
|
sos_restore_IRQs(flags);
|
|
|
|
return SOS_OK;
|
|
}
|
|
|
|
|
|
sos_ret_t sos_mm_context_switch_to(struct sos_mm_context *mmctxt)
|
|
{
|
|
SOS_ASSERT_FATAL(NULL != mmctxt);
|
|
SOS_ASSERT_FATAL(mmctxt->ref_cnt > 0);
|
|
SOS_ASSERT_FATAL(current_mm_context->ref_cnt > 0);
|
|
if (mmctxt != current_mm_context)
|
|
{
|
|
sos_ui32_t flags;
|
|
struct sos_mm_context * prev_mm_context = current_mm_context;
|
|
|
|
/* This is the most dangerous part of the whole thing. If we set
|
|
the wrong MMU configuration in mmctxt, this will hang or
|
|
reboot the machine... */
|
|
sos_paging_set_current_PD_paddr(mmctxt->paddr_PD);
|
|
|
|
/* Exchange the mm_contexts */
|
|
current_mm_context = mmctxt;
|
|
|
|
/* Update the reference counts */
|
|
sos_disable_IRQs(flags);
|
|
mmctxt->ref_cnt ++;
|
|
sos_mm_context_unref(prev_mm_context);
|
|
sos_restore_IRQs(flags);
|
|
}
|
|
|
|
return SOS_OK;
|
|
}
|
|
|
|
|
|
struct sos_mm_context *get_current_mm_context(void)
|
|
{
|
|
SOS_ASSERT_FATAL(current_mm_context->ref_cnt > 0);
|
|
return current_mm_context;
|
|
}
|
|
|
|
|
|
/* ******************************************************
|
|
* Reserved functions
|
|
*/
|
|
|
|
|
|
sos_ret_t sos_mm_context_synch_kernel_PDE(unsigned int index_in_pd,
|
|
sos_ui32_t pde)
|
|
{
|
|
sos_ui32_t flags;
|
|
struct sos_mm_context * dest_mm_context;
|
|
int nb_mm_contexts;
|
|
|
|
sos_disable_IRQs(flags);
|
|
list_foreach_forward(list_mm_context, dest_mm_context, nb_mm_contexts)
|
|
{
|
|
sos_ui32_t * dest_pd;
|
|
|
|
SOS_ASSERT_FATAL(dest_mm_context->ref_cnt > 0);
|
|
|
|
dest_pd = (sos_ui32_t*) dest_mm_context->vaddr_PD;
|
|
dest_pd[index_in_pd] = pde;
|
|
}
|
|
sos_restore_IRQs(flags);
|
|
|
|
return SOS_OK;
|
|
}
|