mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-11 21:32:57 +00:00
Fix several bugs in the Cortex A9 drivers.
Avoid the use of deprecated 'MASK' enum in CPU register 'Asid'. Enable the use of the 'K' bit in MMU translations. Treat any try to modify existing valid entries in section- and pagetables when doing 'insert_translation' as error. Beautify concerned files.
This commit is contained in:
parent
23772b620f
commit
9b0a54e980
@ -48,23 +48,25 @@ namespace Genode
|
||||
/* timer */
|
||||
PRIVATE_TIMER_MMIO_BASE = Board::CORTEX_A9_PRIVATE_MEM_BASE + 0x600,
|
||||
PRIVATE_TIMER_MMIO_SIZE = 0x10,
|
||||
PRIVATE_TIMER_IRQ = 29,
|
||||
TIMER_MMIO = PRIVATE_TIMER_MMIO_BASE,
|
||||
TIMER_IRQ = PRIVATE_TIMER_IRQ,
|
||||
PRIVATE_TIMER_IRQ = 29,
|
||||
PRIVATE_TIMER_CLK = PERIPH_CLK
|
||||
};
|
||||
|
||||
/**
|
||||
* Exceotion type IDs
|
||||
* CPU local timer module
|
||||
*/
|
||||
enum Exception_type
|
||||
class Private_timer : public Cortex_a9_timer<PRIVATE_TIMER_CLK>
|
||||
{
|
||||
RESET = 1,
|
||||
UNDEFINED_INSTRUCTION = 2,
|
||||
SUPERVISOR_CALL = 3,
|
||||
PREFETCH_ABORT = 4,
|
||||
DATA_ABORT = 5,
|
||||
INTERRUPT_REQUEST = 6,
|
||||
FAST_INTERRUPT_REQUEST = 7,
|
||||
public:
|
||||
|
||||
enum { IRQ = PRIVATE_TIMER_IRQ };
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Private_timer() :
|
||||
Cortex_a9_timer<PRIVATE_TIMER_CLK>(PRIVATE_TIMER_MMIO_BASE)
|
||||
{ }
|
||||
};
|
||||
|
||||
typedef Cortex_a9_timer<PERIPH_CLK> Timer;
|
||||
@ -170,10 +172,7 @@ namespace Genode
|
||||
*/
|
||||
struct Contextidr : Register<32>
|
||||
{
|
||||
struct Asid : Bitfield<0,8> /* ID part used by MMU */
|
||||
{
|
||||
enum { MAX = MASK };
|
||||
};
|
||||
struct Asid : Bitfield<0,8> { }; /* ID part used by MMU */
|
||||
struct Procid : Bitfield<8,24> { }; /* ID part used by debug/trace */
|
||||
|
||||
/**
|
||||
@ -286,8 +285,8 @@ namespace Genode
|
||||
/**
|
||||
* Translation table base register 0
|
||||
*
|
||||
* Typically for process specific spaces, references first level table
|
||||
* with a size between 128B and 16KB according to TTBCR.N
|
||||
* Typically for process specific spaces, references first level
|
||||
* table with a size between 128B and 16KB according to 'Ttbcr::N'.
|
||||
*/
|
||||
struct Ttbr0 : Register<32>
|
||||
{
|
||||
@ -356,22 +355,22 @@ namespace Genode
|
||||
*/
|
||||
struct Cpsr : Register<32>
|
||||
{
|
||||
struct M : Bitfield<0,5> /* processor mode */
|
||||
struct M : Bitfield<0,5> /* processor mode */
|
||||
{
|
||||
enum { /* <Privileged>, <Description> */
|
||||
USER = 0b10000, /* 0, application code */
|
||||
FIQ = 0b10001, /* 1, entered at fast interrupt */
|
||||
IRQ = 0b10010, /* 1, entered at normal interrupt */
|
||||
SUPERVISOR = 0b10011, /* 1, most kernel code */
|
||||
MONITOR = 0b10110, /* 1, a secure mode, switch sec./non-sec. */
|
||||
ABORT = 0b10111, /* 1, entered at aborts */
|
||||
enum { /* <Privileged>, <Description> */
|
||||
USER = 0b10000, /* 0, application code */
|
||||
FIQ = 0b10001, /* 1, entered at fast interrupt */
|
||||
IRQ = 0b10010, /* 1, entered at normal interrupt */
|
||||
SUPERVISOR = 0b10011, /* 1, most kernel code */
|
||||
MONITOR = 0b10110, /* 1, a secure mode, switch sec./non-sec. */
|
||||
ABORT = 0b10111, /* 1, entered at aborts */
|
||||
UNDEFINED = 0b11011, /* 1, entered at instruction-related error */
|
||||
SYSTEM = 0b11111, /* 1, applications that require privileged */
|
||||
};
|
||||
};
|
||||
struct F : Bitfield<6,1> { }; /* fast interrupt request disable */
|
||||
struct I : Bitfield<7,1> { }; /* interrupt request disable */
|
||||
struct A : Bitfield<8,1> { }; /* asynchronous abort disable */
|
||||
struct F : Bitfield<6,1> { }; /* fast interrupt request disable */
|
||||
struct I : Bitfield<7,1> { }; /* interrupt request disable */
|
||||
struct A : Bitfield<8,1> { }; /* asynchronous abort disable */
|
||||
|
||||
/**
|
||||
* Read whole register
|
||||
@ -415,35 +414,189 @@ namespace Genode
|
||||
*/
|
||||
struct Context
|
||||
{
|
||||
/* general purpose registers, offset 0*4 .. 15*4 */
|
||||
uint32_t
|
||||
r0, r1, r2, r3, r4, r5, r6, r7,
|
||||
r8, r9, r10, r11, r12, sp, lr, pc;
|
||||
enum {
|
||||
MAX_GPR = 15,
|
||||
MAX_CPU_EXCEPTION = 7,
|
||||
};
|
||||
|
||||
/* special registers, offset 16*4 .. 17*4 */
|
||||
uint32_t psr, contextidr;
|
||||
/**
|
||||
* Native exception types
|
||||
*/
|
||||
enum Cpu_exception {
|
||||
RESET = 1,
|
||||
UNDEFINED_INSTRUCTION = 2,
|
||||
SUPERVISOR_CALL = 3,
|
||||
PREFETCH_ABORT = 4,
|
||||
DATA_ABORT = 5,
|
||||
INTERRUPT_REQUEST = 6,
|
||||
FAST_INTERRUPT_REQUEST = 7,
|
||||
};
|
||||
|
||||
/* additional state info, offset 18*4 .. 19*4 */
|
||||
uint32_t exception_type, section_table;
|
||||
/* general purpose register backups, offsets 0*4 .. 15*4 */
|
||||
uint32_t r0, r1, r2, r3, r4, r5, r6, r7,
|
||||
r8, r9, r10, r11, r12, r13, r14, r15;
|
||||
|
||||
uint32_t psr; /* program status register backup, offset 16*4 */
|
||||
uint32_t contextidr; /* contextidr register backup, offset 17*4 */
|
||||
uint32_t cpu_exception; /* native type of last exception,
|
||||
* offset 18*4 */
|
||||
uint32_t section_table; /* base address of applied section table,
|
||||
* offset 19*4 */
|
||||
|
||||
/**
|
||||
* Read a general purpose register
|
||||
*
|
||||
* \param id ID of the targeted register
|
||||
* \param r Holds register value if this returns 1
|
||||
*/
|
||||
bool get_gpr(unsigned id, unsigned & r) const
|
||||
{
|
||||
if (id > MAX_GPR) return 0;
|
||||
r = *(&r0 + id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override a general purpose register
|
||||
*
|
||||
* \param id ID of the targeted register
|
||||
* \param r Has been written to register if this returns 1
|
||||
*/
|
||||
bool set_gpr(unsigned id, unsigned const r)
|
||||
{
|
||||
if (id > MAX_GPR) return 0;
|
||||
*(&r0 + id) = r;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/***************
|
||||
** Accessors **
|
||||
***************/
|
||||
|
||||
void software_tlb(Section_table * const st)
|
||||
{ section_table = (addr_t)st; }
|
||||
void software_tlb(Section_table * const st) {
|
||||
section_table = (addr_t)st; }
|
||||
|
||||
Section_table * software_tlb() { return (Section_table *)section_table; }
|
||||
Section_table * software_tlb() const {
|
||||
return (Section_table *)section_table; }
|
||||
|
||||
void instruction_ptr(addr_t const p) { pc = p; }
|
||||
void instruction_ptr(addr_t const p) { r15 = p; }
|
||||
|
||||
addr_t instruction_ptr() { return pc; }
|
||||
addr_t instruction_ptr() const { return r15; }
|
||||
|
||||
void return_ptr(addr_t const p) { lr = p; }
|
||||
void return_ptr(addr_t const p) { r14 = p; }
|
||||
|
||||
void stack_ptr(addr_t const p) { sp = p; }
|
||||
void stack_ptr(addr_t const p) { r13 = p; }
|
||||
|
||||
void pd_id(unsigned long const id) { contextidr = id; }
|
||||
void protection_domain(unsigned const id) { contextidr = id; }
|
||||
};
|
||||
|
||||
/**
|
||||
* An usermode execution state
|
||||
*/
|
||||
struct User_context : Context
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
User_context()
|
||||
{
|
||||
/* Execute in usermode with IRQ's enabled and FIQ's and
|
||||
* asynchronous aborts disabled */
|
||||
psr = Cpsr::M::bits(Cpsr::M::USER) | Cpsr::F::bits(1) |
|
||||
Cpsr::I::bits(0) | Cpsr::A::bits(1);
|
||||
}
|
||||
|
||||
/***************************************************
|
||||
** Communication between user and context holder **
|
||||
***************************************************/
|
||||
|
||||
void user_arg_0(unsigned const arg) { r0 = arg; }
|
||||
void user_arg_1(unsigned const arg) { r1 = arg; }
|
||||
void user_arg_2(unsigned const arg) { r2 = arg; }
|
||||
void user_arg_3(unsigned const arg) { r3 = arg; }
|
||||
void user_arg_4(unsigned const arg) { r4 = arg; }
|
||||
void user_arg_5(unsigned const arg) { r5 = arg; }
|
||||
void user_arg_6(unsigned const arg) { r6 = arg; }
|
||||
void user_arg_7(unsigned const arg) { r7 = arg; }
|
||||
unsigned user_arg_0() const { return r0; }
|
||||
unsigned user_arg_1() const { return r1; }
|
||||
unsigned user_arg_2() const { return r2; }
|
||||
unsigned user_arg_3() const { return r3; }
|
||||
unsigned user_arg_4() const { return r4; }
|
||||
unsigned user_arg_5() const { return r5; }
|
||||
unsigned user_arg_6() const { return r6; }
|
||||
unsigned user_arg_7() const { return r7; }
|
||||
|
||||
/**
|
||||
* Determine wich type of exception occured on this context lastly
|
||||
*
|
||||
* \return 0 If the exception is unknown by the kernel
|
||||
* 1 If the exception is an interrupt
|
||||
* 2 If the exception is a pagefault
|
||||
* 3 If the exception is a syscall
|
||||
*/
|
||||
unsigned exception() const
|
||||
{
|
||||
/* map all CPU-exception types to kernel-exception types */
|
||||
enum { INVALID = 0, INTERRUPT = 1, PAGEFAULT = 2, SYSCALL = 3 };
|
||||
static unsigned cpu_excpt_to_excpt[MAX_CPU_EXCEPTION + 1] = {
|
||||
INVALID, /* 0 */
|
||||
INVALID, /* 1 */
|
||||
INVALID, /* 2 */
|
||||
SYSCALL, /* 3 */
|
||||
PAGEFAULT, /* 4 */
|
||||
PAGEFAULT, /* 5 */
|
||||
INTERRUPT, /* 6 */
|
||||
INVALID /* 7 */
|
||||
};
|
||||
/* determine exception type */
|
||||
if (cpu_exception > MAX_CPU_EXCEPTION) return INVALID;
|
||||
return cpu_excpt_to_excpt[cpu_exception];
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a pagefault exist and originate from a lack of translation?
|
||||
*
|
||||
* \param va Holds the virtual fault-address if this
|
||||
* function returns 1
|
||||
* \param w Indicates wether the fault was caused by a write
|
||||
* access if this function returns 1
|
||||
*/
|
||||
bool translation_miss(addr_t & va, bool & w) const
|
||||
{
|
||||
/* determine fault type */
|
||||
switch (cpu_exception)
|
||||
{
|
||||
case PREFETCH_ABORT: {
|
||||
|
||||
/* check if fault was caused by a translation miss */
|
||||
Ifsr::Fault_status const fs = Ifsr::fault_status();
|
||||
if(fs == Ifsr::SECTION_TRANSLATION_FAULT ||
|
||||
fs == Ifsr::PAGE_TRANSLATION_FAULT)
|
||||
{
|
||||
/* fetch fault data */
|
||||
w = 0;
|
||||
va = Ifar::read();
|
||||
return 1;
|
||||
}
|
||||
return 0; }
|
||||
case DATA_ABORT: {
|
||||
|
||||
/* check if fault was caused by translation miss */
|
||||
Dfsr::Fault_status const fs = Dfsr::fault_status();
|
||||
if(fs == Dfsr::SECTION_TRANSLATION_FAULT ||
|
||||
fs == Dfsr::PAGE_TRANSLATION_FAULT)
|
||||
{
|
||||
/* fetch fault data */
|
||||
Dfsr::access_t const dfsr = Dfsr::read();
|
||||
w = Dfsr::Wnr::get(dfsr);
|
||||
va = Dfar::read();
|
||||
return 1;
|
||||
}
|
||||
return 0; }
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -541,7 +694,7 @@ namespace Genode
|
||||
/**
|
||||
* Invalidate all entries of the branch predictor array
|
||||
*
|
||||
* Must be inline to avoid dependence on the branch predictor
|
||||
* Must be inline to avoid dependence on the branch predictor.
|
||||
*/
|
||||
__attribute__((always_inline)) inline static void flush_branch_prediction()
|
||||
{
|
||||
@ -560,51 +713,6 @@ namespace Genode
|
||||
:: [asid]"r"(Contextidr::Asid::masked(process_id)) : );
|
||||
flush_branch_prediction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a pagefault exist and originate from a lack of translation?
|
||||
*
|
||||
* \param c CPU Context that triggered the page fault
|
||||
* \param va holds the virtual fault-address if this
|
||||
* function returns 1
|
||||
* \param w indicates whether the fault was caused by a write access
|
||||
* if this function returns 1
|
||||
*/
|
||||
static bool translation_miss(Context * c, addr_t & va, bool & w)
|
||||
{
|
||||
/* determine fault type */
|
||||
switch (c->exception_type)
|
||||
{
|
||||
case PREFETCH_ABORT: {
|
||||
|
||||
/* is fault caused by translation miss? */
|
||||
Ifsr::Fault_status const fs = Ifsr::fault_status();
|
||||
if(fs == Ifsr::SECTION_TRANSLATION_FAULT ||
|
||||
fs == Ifsr::PAGE_TRANSLATION_FAULT)
|
||||
{
|
||||
/* fetch fault data */
|
||||
w = 0;
|
||||
va = Ifar::read();
|
||||
return 1;
|
||||
}
|
||||
return 0; }
|
||||
case DATA_ABORT: {
|
||||
|
||||
/* is fault caused by translation miss? */
|
||||
Dfsr::Fault_status const fs = Dfsr::fault_status();
|
||||
if(fs == Dfsr::SECTION_TRANSLATION_FAULT ||
|
||||
fs == Dfsr::PAGE_TRANSLATION_FAULT)
|
||||
{
|
||||
/* fetch fault data */
|
||||
Dfsr::access_t const dfsr = Dfsr::read();
|
||||
w = Dfsr::Wnr::get(dfsr);
|
||||
va = Dfar::read();
|
||||
return 1;
|
||||
}
|
||||
return 0; }
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -34,8 +34,12 @@ namespace Genode
|
||||
*/
|
||||
struct Ap_1_0_bitfield
|
||||
{
|
||||
enum { KERNEL_AND_USER_NO_ACCESS = 0,
|
||||
KERNEL_AND_USER_SAME_ACCESS = 3 };
|
||||
enum {
|
||||
KERNEL_AND_USER_NO_ACCESS = 0,
|
||||
USER_NO_ACCESS = 1,
|
||||
USER_RO_ACCESS = 2,
|
||||
KERNEL_AND_USER_SAME_ACCESS = 3
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
@ -43,10 +47,49 @@ namespace Genode
|
||||
*/
|
||||
struct Ap_2_bitfield
|
||||
{
|
||||
enum { KERNEL_RW_OR_NO_ACCESS = 0,
|
||||
KERNEL_RO_ACCESS = 1 };
|
||||
enum {
|
||||
KERNEL_RW_OR_NO_ACCESS = 0,
|
||||
KERNEL_RO_ACCESS = 1
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Permission configuration according to given access rights
|
||||
*
|
||||
* \param T targeted translation-table-descriptor type
|
||||
* \param w see 'Section_table::insert_translation'
|
||||
* \param x see 'Section_table::insert_translation'
|
||||
* \param k see 'Section_table::insert_translation'
|
||||
*
|
||||
* \return descriptor value with requested perms and the rest left zero
|
||||
*/
|
||||
template <typename T>
|
||||
static typename T::access_t access_permission_bits(bool const w,
|
||||
bool const x,
|
||||
bool const k)
|
||||
{
|
||||
/* lookup table for AP bitfield values according to 'w' and 'k' */
|
||||
typedef typename T::Ap_1_0 Ap_1_0;
|
||||
typedef typename T::Ap_2 Ap_2;
|
||||
static typename T::access_t const ap_bits[2][2] = {{
|
||||
Ap_1_0::bits(Ap_1_0::USER_RO_ACCESS) | /* -- */
|
||||
Ap_2::bits(Ap_2::KERNEL_RW_OR_NO_ACCESS),
|
||||
|
||||
Ap_1_0::bits(Ap_1_0::USER_NO_ACCESS) | /* -k */
|
||||
Ap_2::bits(Ap_2::KERNEL_RO_ACCESS) }, {
|
||||
|
||||
Ap_1_0::bits(Ap_1_0::KERNEL_AND_USER_SAME_ACCESS) | /* w- */
|
||||
Ap_2::bits(Ap_2::KERNEL_RW_OR_NO_ACCESS),
|
||||
|
||||
Ap_1_0::bits(Ap_1_0::USER_NO_ACCESS) | /* wk */
|
||||
Ap_2::bits(Ap_2::KERNEL_RW_OR_NO_ACCESS) }
|
||||
};
|
||||
/* combine XN and AP bitfield values according to 'w', 'x' and 'k' */
|
||||
typedef typename T::Xn Xn;
|
||||
return Xn::bits(!x) | ap_bits[w][k];
|
||||
}
|
||||
|
||||
/**
|
||||
* Cortex A9 second level translation table
|
||||
*
|
||||
@ -111,17 +154,23 @@ namespace Genode
|
||||
static void type(access_t & v, Type const t)
|
||||
{
|
||||
switch (t) {
|
||||
case FAULT:
|
||||
|
||||
case FAULT: {
|
||||
|
||||
Type_1::set(v, 0);
|
||||
Type_2::set(v, 0);
|
||||
break;
|
||||
case SMALL_PAGE:
|
||||
break; }
|
||||
|
||||
case SMALL_PAGE: {
|
||||
|
||||
Type_1::set(v, 1);
|
||||
break;
|
||||
case LARGE_PAGE:
|
||||
break; }
|
||||
|
||||
case LARGE_PAGE: {
|
||||
|
||||
Type_1::set(v, 0);
|
||||
Type_2::set(v, 1);
|
||||
break;
|
||||
break; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,42 +245,12 @@ namespace Genode
|
||||
struct S : Bitfield<10, 1> { }; /* shareable bit */
|
||||
struct Ng : Bitfield<11, 1> { }; /* not global bit */
|
||||
struct Pa_31_12 : Bitfield<12, 20> { }; /* physical address bits [31:12] */
|
||||
|
||||
/**
|
||||
* Permission configuration according to given access rights
|
||||
*
|
||||
* \param r readability
|
||||
* \param w writeability
|
||||
* \param x executability
|
||||
* \return descriptor value configured with appropriate
|
||||
* access permissions and the rest left zero
|
||||
*/
|
||||
static access_t access_permission_bits(bool const r,
|
||||
bool const w,
|
||||
bool const x)
|
||||
{
|
||||
access_t v = Xn::bits(!x);
|
||||
if (r) {
|
||||
v |= Ap_1_0::bits(Ap_1_0::KERNEL_AND_USER_SAME_ACCESS);
|
||||
if(w) v |= Ap_2::bits(Ap_2::KERNEL_RW_OR_NO_ACCESS);
|
||||
else v |= Ap_2::bits(Ap_2::KERNEL_RO_ACCESS);
|
||||
}
|
||||
else if (w) {
|
||||
PDBG("Write only translations not supported");
|
||||
while (1) ;
|
||||
}
|
||||
else {
|
||||
v |= Ap_1_0::bits(Ap_1_0::KERNEL_AND_USER_NO_ACCESS)
|
||||
| Ap_2::bits(Ap_2::KERNEL_RO_ACCESS);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Table payload
|
||||
*
|
||||
* Attention: Must be the only member of this class
|
||||
* Must be the only member of this class
|
||||
*/
|
||||
Descriptor::access_t _entries[SIZE/sizeof(Descriptor::access_t)];
|
||||
|
||||
@ -240,10 +259,12 @@ namespace Genode
|
||||
/**
|
||||
* Get entry index by virtual offset
|
||||
*
|
||||
* \param i is overridden with the resulting index
|
||||
* \param vo virtual offset relative to the virtual table base
|
||||
* \retval <0 if virtual offset couldn't be resolved,
|
||||
* in this case 'i' reside invalid
|
||||
* \param i is overridden with the resulting index
|
||||
* \param vo virtual offset relative to the virtual table base
|
||||
*
|
||||
* \retval 0 on success
|
||||
* \retval <0 If virtual offset couldn't be resolved.
|
||||
* In this case 'i' reside invalid
|
||||
*/
|
||||
int _index_by_vo (unsigned long & i, addr_t const vo) const
|
||||
{
|
||||
@ -266,8 +287,8 @@ namespace Genode
|
||||
{
|
||||
/* check table alignment */
|
||||
if (!aligned((addr_t)this, ALIGNM_LOG2)
|
||||
|| (addr_t)this != (addr_t)_entries) {
|
||||
|
||||
|| (addr_t)this != (addr_t)_entries)
|
||||
{
|
||||
PDBG("Insufficient table alignment");
|
||||
while (1) ;
|
||||
}
|
||||
@ -289,25 +310,25 @@ namespace Genode
|
||||
/**
|
||||
* Insert one atomic translation into this table
|
||||
*
|
||||
* \param vo offset of the virtual region represented
|
||||
* by the translation within the virtual
|
||||
* region represented by this table
|
||||
* \param pa base of the physical backing store
|
||||
* \param size_log2 log2(Size of the translated region),
|
||||
* must be supported by this table
|
||||
* \param r shall one can read trough this translation
|
||||
* \param w shall one can write trough this translation
|
||||
* \param x shall one can execute trough this
|
||||
* translation
|
||||
* \param vo offset of the virtual region represented
|
||||
* by the translation within the virtual
|
||||
* region represented by this table
|
||||
* \param pa base of the physical backing store
|
||||
* \param size_log2 log2(Size of the translated region),
|
||||
* must be supported by this table
|
||||
* \param w see 'Section_table::insert_translation'
|
||||
* \param x see 'Section_table::insert_translation'
|
||||
* \param k see 'Section_table::insert_translation'
|
||||
* \param g see 'Section_table::insert_translation'
|
||||
*
|
||||
* This method overrides an existing translation in case that it
|
||||
* spans the the same virtual range and is not a link to another
|
||||
* table level.
|
||||
* This method overrides an existing translation in case
|
||||
* that it spans the the same virtual range and is not
|
||||
* a link to another table level.
|
||||
*/
|
||||
void insert_translation (addr_t const vo, addr_t const pa,
|
||||
unsigned long const size_log2,
|
||||
bool const r, bool const w, bool const x,
|
||||
bool const global)
|
||||
void insert_translation(addr_t const vo, addr_t const pa,
|
||||
unsigned long const size_log2,
|
||||
bool const w, bool const x,
|
||||
bool const k, bool const g)
|
||||
{
|
||||
/* validate virtual address */
|
||||
unsigned long i;
|
||||
@ -315,25 +336,32 @@ namespace Genode
|
||||
PDBG("Invalid virtual offset");
|
||||
while (1) ;
|
||||
}
|
||||
|
||||
/* select descriptor type by the translation size */
|
||||
if (size_log2 == Small_page::VIRT_SIZE_LOG2) {
|
||||
if (size_log2 == Small_page::VIRT_SIZE_LOG2)
|
||||
{
|
||||
/* compose new descriptor value */
|
||||
Descriptor::access_t entry =
|
||||
access_permission_bits<Small_page>(w, x, k)
|
||||
| Small_page::Ng::bits(!g)
|
||||
| Small_page::Pa_31_12::masked(pa);
|
||||
Descriptor::type(entry, Descriptor::SMALL_PAGE);
|
||||
|
||||
/*
|
||||
* Can we write to the targeted entry?
|
||||
*/
|
||||
if (Descriptor::valid(_entries[i]) &&
|
||||
Descriptor::type(_entries[i]) != Descriptor::SMALL_PAGE)
|
||||
/* check if we can we write to the targeted entry */
|
||||
if (Descriptor::valid(_entries[i]))
|
||||
{
|
||||
/*
|
||||
* It's possible that multiple threads fault at the
|
||||
* same time on the same translation, thus we need
|
||||
* this check.
|
||||
*/
|
||||
if (_entries[i] == entry) return;
|
||||
|
||||
/* never modify existing translations */
|
||||
PDBG("Couldn't override entry");
|
||||
while (1) ;
|
||||
}
|
||||
|
||||
/* compose descriptor */
|
||||
_entries[i] = Small_page::access_permission_bits(r, w, x)
|
||||
| Small_page::Ng::bits(!global)
|
||||
| Small_page::Pa_31_12::masked(pa);
|
||||
Descriptor::type(_entries[i], Descriptor::SMALL_PAGE);
|
||||
/* override table entry with new descriptor value */
|
||||
_entries[i] = entry;
|
||||
return;
|
||||
}
|
||||
PDBG("Translation size not supported");
|
||||
@ -343,9 +371,9 @@ namespace Genode
|
||||
/**
|
||||
* Remove translations, wich overlap with a given virtual region
|
||||
*
|
||||
* \param vo offset of the virtual region within the region
|
||||
* represented by this table
|
||||
* \param size region size
|
||||
* \param vo offset of the virtual region within the region
|
||||
* represented by this table
|
||||
* \param size region size
|
||||
*/
|
||||
void remove_region (addr_t const vo, size_t const size)
|
||||
{
|
||||
@ -354,21 +382,13 @@ namespace Genode
|
||||
unsigned long i;
|
||||
while (1)
|
||||
{
|
||||
/*
|
||||
* Is anything left over to remove?
|
||||
*/
|
||||
/* check if anything is left over to remove */
|
||||
if (residual_vo >= vo + size) return;
|
||||
|
||||
/*
|
||||
* Does the residual region overlap with the region
|
||||
* represented by this table?
|
||||
*/
|
||||
/* check if residual region overlaps with table */
|
||||
if (_index_by_vo(i, residual_vo)) return;
|
||||
|
||||
/*
|
||||
* Update current entry and recalculate the residual
|
||||
* region.
|
||||
*/
|
||||
/* update current entry and recalculate residual region */
|
||||
switch (Descriptor::type(_entries[i]))
|
||||
{
|
||||
case Descriptor::FAULT:
|
||||
@ -514,8 +534,7 @@ namespace Genode
|
||||
};
|
||||
|
||||
/**
|
||||
* References a second level translation table for the virtual
|
||||
* region it represents
|
||||
* Link to a second level translation table
|
||||
*/
|
||||
struct Page_table_descriptor : Descriptor
|
||||
{
|
||||
@ -578,58 +597,26 @@ namespace Genode
|
||||
struct Ng : Bitfield<17, 1> { }; /* not global bit */
|
||||
struct Ns : Bitfield<19, 1> { }; /* non-secure bit */
|
||||
struct Pa_31_20 : Bitfield<20, 12> { }; /* physical address bits [31:20] */
|
||||
|
||||
/**
|
||||
* Permission configuration according to given access rights
|
||||
*
|
||||
* \param r readability
|
||||
* \param w writeability
|
||||
* \param x executability
|
||||
* \return descriptor value configured with appropriate
|
||||
* access permissions and the rest left zero
|
||||
*/
|
||||
static access_t access_permission_bits(bool const r,
|
||||
bool const w,
|
||||
bool const x)
|
||||
{
|
||||
access_t v = Xn::bits(!x);
|
||||
if (r) {
|
||||
v |= Ap_1_0::bits(Ap_1_0::KERNEL_AND_USER_SAME_ACCESS);
|
||||
if(w) v |= Ap_2::bits(Ap_2::KERNEL_RW_OR_NO_ACCESS);
|
||||
else v |= Ap_2::bits(Ap_2::KERNEL_RO_ACCESS);
|
||||
}
|
||||
else if (w) {
|
||||
PDBG("Write only sections not supported");
|
||||
while (1) ;
|
||||
}
|
||||
else {
|
||||
v |= Ap_1_0::bits(Ap_1_0::KERNEL_AND_USER_NO_ACCESS)
|
||||
| Ap_2::bits(Ap_2::KERNEL_RW_OR_NO_ACCESS);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Table payload
|
||||
*
|
||||
* Attention: Must be the first member of this class
|
||||
*/
|
||||
/* table payload, must be the first member of this class */
|
||||
Descriptor::access_t _entries[SIZE/sizeof(Descriptor::access_t)];
|
||||
|
||||
enum { MAX_INDEX = sizeof(_entries) / sizeof(_entries[0]) - 1 };
|
||||
|
||||
/* is this table dedicated to secure mode or to non-secure mode */
|
||||
/* if this table dedicated to secure mode or to non-secure mode */
|
||||
bool _secure;
|
||||
|
||||
/**
|
||||
* Get entry index by virtual offset
|
||||
*
|
||||
* \param i is overridden with the resulting index
|
||||
* \param vo offset within the virtual region represented
|
||||
* by this table
|
||||
* \retval <0 if virtual offset couldn't be resolved,
|
||||
* in this case 'i' reside invalid
|
||||
* \param i is overridden with the resulting index
|
||||
* \param vo offset within the virtual region represented
|
||||
* by this table
|
||||
*
|
||||
* \retval 0 on success
|
||||
* \retval <0 if virtual offset couldn't be resolved,
|
||||
* in this case 'i' reside invalid
|
||||
*/
|
||||
int _index_by_vo(unsigned long & i, addr_t const vo) const
|
||||
{
|
||||
@ -647,8 +634,8 @@ namespace Genode
|
||||
{
|
||||
/* check table alignment */
|
||||
if (!aligned((addr_t)this, ALIGNM_LOG2)
|
||||
|| (addr_t)this != (addr_t)_entries) {
|
||||
|
||||
|| (addr_t)this != (addr_t)_entries)
|
||||
{
|
||||
PDBG("Insufficient table alignment");
|
||||
while (1) ;
|
||||
}
|
||||
@ -670,40 +657,45 @@ namespace Genode
|
||||
/**
|
||||
* Insert one atomic translation into this table
|
||||
*
|
||||
* \param vo offset of the virtual region represented
|
||||
* by the translation within the virtual
|
||||
* region represented by this table
|
||||
* \param pa base of the physical backing store
|
||||
* \param size_log2 size log2 of the translated region
|
||||
* \param r shall one can read trough this translation
|
||||
* \param w shall one can write trough this translation
|
||||
* \param x shall one can execute trough this translation
|
||||
* \param global shall the translation apply to all
|
||||
* address spaces
|
||||
* \param extra_space If > 0, it must point to a portion of
|
||||
* size-aligned memory space wich may be used
|
||||
* furthermore by the table for the incurring
|
||||
* administrative costs of the translation.
|
||||
* To determine the amount of additionally
|
||||
* needed memory one can instrument this
|
||||
* method with 'extra_space' set to 0.
|
||||
* The so donated memory may be regained by
|
||||
* using the method 'regain_memory'.
|
||||
* \retval 0 translation successfully inserted
|
||||
* \retval >0 translation not inserted, the return value
|
||||
* is the size log2 of additional size-aligned
|
||||
* space that is needed to do the translation.
|
||||
* This occurs solely when 'extra_space' is 0.
|
||||
* \param vo offset of the virtual region represented
|
||||
* by the translation within the virtual
|
||||
* region represented by this table
|
||||
* \param pa base of the physical backing store
|
||||
* \param size_log2 size log2 of the translated region
|
||||
* \param w if one can write trough this translation
|
||||
* \param x if one can execute trough this translation
|
||||
* \param k If set to 1, the given permissions apply
|
||||
* in kernel mode, while in user mode this
|
||||
* translations grants no type of access.
|
||||
* If set to 0, the given permissions apply
|
||||
* in user mode, while in kernel mode this
|
||||
* translation grants any type of access.
|
||||
* \param g if the translation applies to all spaces
|
||||
* \param extra_space If > 0, it must point to a portion of
|
||||
* size-aligned memory space wich may be used
|
||||
* furthermore by the table for the incurring
|
||||
* administrative costs of the translation.
|
||||
* To determine the amount of additionally
|
||||
* needed memory one can instrument this
|
||||
* method with 'extra_space' set to 0.
|
||||
* The so donated memory may be regained by
|
||||
* using the method 'regain_memory'.
|
||||
*
|
||||
* \retval 0 translation successfully inserted
|
||||
* \retval >0 Translation not inserted, the return value
|
||||
* is the size log2 of additional size-aligned
|
||||
* space that is needed to do the translation.
|
||||
* This occurs solely when 'extra_space' is 0.
|
||||
*
|
||||
* This method overrides an existing translation in case that it
|
||||
* spans the the same virtual range and is not a link to another
|
||||
* table level.
|
||||
*/
|
||||
unsigned long insert_translation (addr_t const vo, addr_t const pa,
|
||||
unsigned long const size_log2,
|
||||
bool const r, bool const w,
|
||||
bool const x, bool const global,
|
||||
void * const extra_space = 0)
|
||||
unsigned long insert_translation(addr_t const vo, addr_t const pa,
|
||||
unsigned long const size_log2,
|
||||
bool const w, bool const x,
|
||||
bool const k, bool const g,
|
||||
void * const extra_space = 0)
|
||||
{
|
||||
/* validate virtual address */
|
||||
unsigned long i;
|
||||
@ -713,34 +705,23 @@ namespace Genode
|
||||
}
|
||||
|
||||
/* select descriptor type by translation size */
|
||||
if (size_log2 < Section::VIRT_SIZE_LOG2) {
|
||||
|
||||
if (size_log2 < Section::VIRT_SIZE_LOG2)
|
||||
{
|
||||
/* check if an appropriate page table already exists */
|
||||
Page_table * pt;
|
||||
|
||||
/*
|
||||
* Does an appropriate page table already exist?
|
||||
*/
|
||||
if (Descriptor::type(_entries[i]) == Descriptor::PAGE_TABLE) {
|
||||
pt = (Page_table *)(addr_t)
|
||||
Page_table_descriptor::Pa_31_10::masked(_entries[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Is there some extra space to create a page table?
|
||||
*/
|
||||
else if (extra_space) {
|
||||
|
||||
/*
|
||||
* Can we write to the targeted entry?
|
||||
*/
|
||||
/* check if we have enough memory for the page table */
|
||||
else if (extra_space)
|
||||
{
|
||||
/* check if we can write to the targeted entry */
|
||||
if (Descriptor::valid(_entries[i])) {
|
||||
PDBG ("Couldn't override entry");
|
||||
while (1) ;
|
||||
}
|
||||
/*
|
||||
* Create and link page table. The page table checks
|
||||
* alignment by itself.
|
||||
*/
|
||||
/* create and link page table */
|
||||
pt = new (extra_space) Page_table();
|
||||
_entries[i] = Page_table_descriptor::Ns::bits(!_secure)
|
||||
| Page_table_descriptor::Pa_31_10::masked((addr_t)pt);
|
||||
@ -751,27 +732,35 @@ namespace Genode
|
||||
|
||||
/* insert translation */
|
||||
pt->insert_translation(vo - Section::Pa_31_20::masked(vo),
|
||||
pa, size_log2, r, w, x, global);
|
||||
pa, size_log2, w, x, k, g);
|
||||
return 0;
|
||||
}
|
||||
if (size_log2 == Section::VIRT_SIZE_LOG2) {
|
||||
if (size_log2 == Section::VIRT_SIZE_LOG2)
|
||||
{
|
||||
/* compose section descriptor */
|
||||
Descriptor::access_t entry =
|
||||
access_permission_bits<Section>(w, x, k)
|
||||
| Section::Ns::bits(!_secure)
|
||||
| Section::Ng::bits(!g)
|
||||
| Section::Pa_31_20::masked(pa);
|
||||
Descriptor::type(entry, Descriptor::SECTION);
|
||||
|
||||
/*
|
||||
* Can we write to the targeted entry?
|
||||
*/
|
||||
if (Descriptor::valid(_entries[i]) &&
|
||||
Descriptor::type(_entries[i]) != Descriptor::SECTION)
|
||||
/* check if we can we write to the targeted entry */
|
||||
if (Descriptor::valid(_entries[i]))
|
||||
{
|
||||
/*
|
||||
* It's possible that multiple threads fault at the
|
||||
* same time on the same translation, thus we need
|
||||
* this check.
|
||||
*/
|
||||
if (_entries[i] == entry) return 0;
|
||||
|
||||
/* never modify existing translations */
|
||||
PDBG("Couldn't override entry");
|
||||
while (1) ;
|
||||
}
|
||||
|
||||
/* compose section descriptor */
|
||||
_entries[i] = Section::access_permission_bits(r, w, x)
|
||||
| Section::Ns::bits(!_secure)
|
||||
| Section::Ng::bits(!global)
|
||||
| Section::Pa_31_20::masked(pa);
|
||||
Descriptor::type(_entries[i], Descriptor::SECTION);
|
||||
/* override the table entry */
|
||||
_entries[i] = entry;
|
||||
return 0;
|
||||
}
|
||||
PDBG("Translation size not supported");
|
||||
@ -781,9 +770,9 @@ namespace Genode
|
||||
/**
|
||||
* Remove translations, wich overlap with a given virtual region
|
||||
*
|
||||
* \param vo offset of the virtual region within the region
|
||||
* represented by this table
|
||||
* \param size region size
|
||||
* \param vo offset of the virtual region within the region
|
||||
* represented by this table
|
||||
* \param size region size
|
||||
*/
|
||||
void remove_region (addr_t const vo, size_t const size)
|
||||
{
|
||||
@ -792,21 +781,13 @@ namespace Genode
|
||||
unsigned long i;
|
||||
while (1)
|
||||
{
|
||||
/*
|
||||
* Is anything left over to remove?
|
||||
*/
|
||||
/* check if anything is left over to remove */
|
||||
if (residual_vo >= vo + size) return;
|
||||
|
||||
/*
|
||||
* Does the residual region overlap with the region
|
||||
* represented by this table?
|
||||
*/
|
||||
/* check if the residual region overlaps with this table */
|
||||
if (_index_by_vo(i, residual_vo)) return;
|
||||
|
||||
/*
|
||||
* Update current entry and recalculate the residual
|
||||
* region.
|
||||
*/
|
||||
/* update current entry and recalculate residual region */
|
||||
switch (Descriptor::type(_entries[i]))
|
||||
{
|
||||
case Descriptor::FAULT:
|
||||
@ -850,21 +831,22 @@ namespace Genode
|
||||
/**
|
||||
* Get a portion of memory that is no longer used by this table
|
||||
*
|
||||
* \param base base of regained memory portion if method returns 1
|
||||
* \param s size of regained memory portion if method returns 1
|
||||
* \param base base of regained mem portion if method returns 1
|
||||
* \param s size of regained mem portion if method returns 1
|
||||
*
|
||||
* \retval 0 no more memory to regain
|
||||
*/
|
||||
bool regain_memory (void * & base, size_t & s)
|
||||
{
|
||||
/* walk through all entries */
|
||||
for (unsigned i = 0; i <= MAX_INDEX; i++)
|
||||
{
|
||||
if (Descriptor::type(_entries[i]) == Descriptor::PAGE_TABLE) {
|
||||
|
||||
if (Descriptor::type(_entries[i]) == Descriptor::PAGE_TABLE)
|
||||
{
|
||||
Page_table * const pt = (Page_table *)
|
||||
(addr_t)Page_table_descriptor::Pa_31_10::masked(_entries[i]);
|
||||
|
||||
if (pt->empty()) {
|
||||
|
||||
if (pt->empty())
|
||||
{
|
||||
/* we've found an useless page table */
|
||||
Descriptor::invalidate(_entries[i]);
|
||||
base = (void *)pt;
|
||||
|
Loading…
x
Reference in New Issue
Block a user