665 lines
15 KiB
C++
665 lines
15 KiB
C++
/*
|
|
* \brief CPU driver for core
|
|
* \author Martin stein
|
|
* \date 2012-09-11
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2012-2013 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU General Public License version 2.
|
|
*/
|
|
|
|
#ifndef _CPU__ARM_H_
|
|
#define _CPU__ARM_H_
|
|
|
|
/* Genode includes */
|
|
#include <util/register.h>
|
|
#include <cpu/cpu_state.h>
|
|
|
|
/* local includes */
|
|
#include <board.h>
|
|
#include <util.h>
|
|
|
|
namespace Arm
|
|
{
|
|
using namespace Genode;
|
|
|
|
/**
|
|
* CPU driver for core
|
|
*/
|
|
struct Cpu
|
|
{
|
|
enum {
|
|
TTBCR_N = 0,
|
|
EXCEPTION_ENTRY = 0xffff0000,
|
|
DATA_ACCESS_ALIGNM = 4,
|
|
};
|
|
|
|
/**
|
|
* Cache type register
|
|
*/
|
|
struct Ctr : Register<32>
|
|
{
|
|
/**
|
|
* Read register value
|
|
*/
|
|
static access_t read()
|
|
{
|
|
access_t v;
|
|
asm volatile ("mrc p15, 0, %[v], c0, c0, 1" : [v]"=r"(v) :: );
|
|
return v;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* System control register
|
|
*/
|
|
struct Sctlr : Register<32>
|
|
{
|
|
struct M : Bitfield<0,1> { }; /* enable MMU */
|
|
struct A : Bitfield<1,1> { }; /* strict data addr. alignment on */
|
|
struct C : Bitfield<2,1> { }; /* enable data cache */
|
|
struct Z : Bitfield<11,1> { }; /* enable program flow prediction */
|
|
struct I : Bitfield<12,1> { }; /* enable instruction caches */
|
|
|
|
/*
|
|
* These must be set all ones
|
|
*/
|
|
struct Static1 : Bitfield<3,4> { };
|
|
struct Static2 : Bitfield<16,1> { };
|
|
struct Static3 : Bitfield<18,1> { };
|
|
struct Static4 : Bitfield<22,2> { };
|
|
|
|
struct V : Bitfield<13,1> /* select exception-entry base */
|
|
{
|
|
enum { XFFFF0000 = 1 };
|
|
};
|
|
|
|
struct Rr : Bitfield<14,1> /* replacement strategy */
|
|
{
|
|
enum { RANDOM = 0 };
|
|
};
|
|
|
|
struct Fi : Bitfield<21,1> { }; /* enable fast IRQ config */
|
|
|
|
struct Ve : Bitfield<24,1> /* interrupt vector config */
|
|
{
|
|
enum { FIXED = 0 };
|
|
};
|
|
|
|
struct Ee : Bitfield<25,1> { }; /* raise CPSR.E on exceptions */
|
|
|
|
/**
|
|
* Common bitfield values for all modes
|
|
*/
|
|
static access_t common()
|
|
{
|
|
return Static1::bits(~0) |
|
|
Static2::bits(~0) |
|
|
Static3::bits(~0) |
|
|
Static4::bits(~0) |
|
|
A::bits(0) |
|
|
C::bits(1) |
|
|
Z::bits(0) |
|
|
I::bits(1) |
|
|
V::bits(V::XFFFF0000) |
|
|
Rr::bits(Rr::RANDOM) |
|
|
Fi::bits(0) |
|
|
Ve::bits(Ve::FIXED) |
|
|
Ee::bits(0);
|
|
}
|
|
|
|
/**
|
|
* Value for the switch to virtual mode in kernel
|
|
*/
|
|
static access_t init_virt_kernel() {
|
|
return common() | M::bits(1); }
|
|
|
|
/**
|
|
* Value for the initial kernel entry
|
|
*/
|
|
static access_t init_phys_kernel() {
|
|
return common() | M::bits(0); }
|
|
|
|
/**
|
|
* Read register value
|
|
*/
|
|
static access_t read()
|
|
{
|
|
access_t v;
|
|
asm volatile ("mrc p15, 0, %[v], c1, c0, 0" : [v]"=r"(v) :: );
|
|
return v;
|
|
}
|
|
|
|
/**
|
|
* Write register value
|
|
*/
|
|
static void write(access_t const v) {
|
|
asm volatile ("mcr p15, 0, %[v], c1, c0, 0" :: [v]"r"(v) : ); }
|
|
};
|
|
|
|
/**
|
|
* Translation table base control register
|
|
*/
|
|
struct Ttbcr : Register<32>
|
|
{
|
|
struct N : Bitfield<0, 3> { }; /* base address width */
|
|
|
|
/**
|
|
* Write register, only in privileged CPU mode
|
|
*/
|
|
static void write(access_t const v) {
|
|
asm volatile ("mcr p15, 0, %[v], c2, c0, 2" :: [v]"r"(v) : ); }
|
|
|
|
/**
|
|
* Read register value
|
|
*/
|
|
static access_t read()
|
|
{
|
|
access_t v;
|
|
asm volatile ("mrc p15, 0, %[v], c2, c0, 2" : [v]"=r"(v) :: );
|
|
return v;
|
|
}
|
|
|
|
/**
|
|
* Value for the switch to virtual mode in kernel
|
|
*/
|
|
static access_t init_virt_kernel() { return N::bits(TTBCR_N); }
|
|
};
|
|
|
|
/**
|
|
* Translation table base register 0
|
|
*/
|
|
struct Ttbr0 : Register<32>
|
|
{
|
|
struct S : Bitfield<1,1> { }; /* shareable */
|
|
|
|
struct Rgn : Bitfield<3, 2> /* outer cachable attributes */
|
|
{
|
|
enum { NON_CACHEABLE = 0 };
|
|
};
|
|
|
|
struct Ba : Bitfield<14-TTBCR_N, 18+TTBCR_N> { }; /* translation
|
|
* table base */
|
|
|
|
/**
|
|
* Write register, only in privileged CPU mode
|
|
*/
|
|
static void write(access_t const v) {
|
|
asm volatile ("mcr p15, 0, %[v], c2, c0, 0" :: [v]"r"(v) : ); }
|
|
|
|
/**
|
|
* Read register value
|
|
*/
|
|
static access_t read()
|
|
{
|
|
access_t v;
|
|
asm volatile ("mrc p15, 0, %[v], c2, c0, 0" : [v]"=r"(v) :: );
|
|
return v;
|
|
}
|
|
|
|
/**
|
|
* Value for the switch to virtual mode in kernel
|
|
*
|
|
* \param sect_table pointer to initial section table
|
|
*/
|
|
static access_t init_virt_kernel(addr_t const sect_table)
|
|
{
|
|
return S::bits(0) |
|
|
Rgn::bits(Rgn::NON_CACHEABLE) |
|
|
Ba::masked((addr_t)sect_table);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Domain access control register
|
|
*/
|
|
struct Dacr : Register<32>
|
|
{
|
|
enum Dx_values { NO_ACCESS = 0, CLIENT = 1 };
|
|
|
|
/**
|
|
* Access values for the 16 available domains
|
|
*/
|
|
struct D0 : Bitfield<0,2> { };
|
|
struct D1 : Bitfield<2,2> { };
|
|
struct D2 : Bitfield<4,2> { };
|
|
struct D3 : Bitfield<6,2> { };
|
|
struct D4 : Bitfield<8,2> { };
|
|
struct D5 : Bitfield<10,2> { };
|
|
struct D6 : Bitfield<12,2> { };
|
|
struct D7 : Bitfield<14,2> { };
|
|
struct D8 : Bitfield<16,2> { };
|
|
struct D9 : Bitfield<18,2> { };
|
|
struct D10 : Bitfield<20,2> { };
|
|
struct D11 : Bitfield<22,2> { };
|
|
struct D12 : Bitfield<24,2> { };
|
|
struct D13 : Bitfield<26,2> { };
|
|
struct D14 : Bitfield<28,2> { };
|
|
struct D15 : Bitfield<30,2> { };
|
|
|
|
/**
|
|
* Write register, only in privileged CPU mode
|
|
*/
|
|
static void write(access_t const v) {
|
|
asm volatile ("mcr p15, 0, %[v], c3, c0, 0" :: [v]"r"(v) : ); }
|
|
|
|
/**
|
|
* Initialize for Genodes operational mode
|
|
*/
|
|
static access_t init_virt_kernel()
|
|
{
|
|
return D0::bits(CLIENT) | D1::bits(NO_ACCESS) |
|
|
D2::bits(NO_ACCESS) | D3::bits(NO_ACCESS) |
|
|
D4::bits(NO_ACCESS) | D5::bits(NO_ACCESS) |
|
|
D6::bits(NO_ACCESS) | D7::bits(NO_ACCESS) |
|
|
D8::bits(NO_ACCESS) | D9::bits(NO_ACCESS) |
|
|
D10::bits(NO_ACCESS) | D11::bits(NO_ACCESS) |
|
|
D12::bits(NO_ACCESS) | D13::bits(NO_ACCESS) |
|
|
D14::bits(NO_ACCESS) | D15::bits(NO_ACCESS);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Context identification register
|
|
*/
|
|
struct Cidr : Register<32>
|
|
{
|
|
/**
|
|
* Write register value
|
|
*/
|
|
static void write(access_t const v)
|
|
{
|
|
asm volatile ("mcr p15, 0, %[v], c13, c0, 1" :: [v]"r"(v) : );
|
|
}
|
|
|
|
/**
|
|
* Read register value
|
|
*/
|
|
static access_t read()
|
|
{
|
|
access_t v;
|
|
asm volatile ("mrc p15, 0, %[v], c13, c0, 1" : [v]"=r"(v) :: );
|
|
return v;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Program status register
|
|
*/
|
|
struct Psr : Register<32>
|
|
{
|
|
struct M : Bitfield<0,5> /* processor mode */
|
|
{
|
|
enum { USER = 0b10000, SUPERVISOR = 0b10011 };
|
|
};
|
|
|
|
struct T : Bitfield<5,1> /* instruction state */
|
|
{
|
|
enum { ARM = 0 };
|
|
};
|
|
|
|
struct F : Bitfield<6,1> { }; /* FIQ disable */
|
|
struct I : Bitfield<7,1> { }; /* IRQ disable */
|
|
struct A : Bitfield<8,1> { }; /* asynchronous abort disable */
|
|
|
|
struct E : Bitfield<9,1> /* load/store endianess */
|
|
{
|
|
enum { LITTLE = 0 };
|
|
};
|
|
|
|
struct J : Bitfield<24,1> /* instruction state */
|
|
{
|
|
enum { ARM = 0 };
|
|
};
|
|
|
|
/**
|
|
* Read register
|
|
*/
|
|
static access_t read()
|
|
{
|
|
access_t v;
|
|
asm volatile ("mrs %[v], cpsr" : [v] "=r" (v) : : );
|
|
return v;
|
|
}
|
|
|
|
/**
|
|
* Write register
|
|
*/
|
|
static void write(access_t const v) {
|
|
asm volatile ("msr cpsr, %[v]" : : [v] "r" (v) : ); }
|
|
|
|
/**
|
|
* Initial value for a user execution context with trustzone
|
|
*
|
|
* FIXME: This function should not be declared in 'Arm' but in
|
|
* 'Arm_v7', but for now the declaration is necessary
|
|
* because of 'User_context::User_context()'.
|
|
*/
|
|
inline static access_t init_user_with_trustzone();
|
|
|
|
/**
|
|
* Initial value for an userland execution context
|
|
*/
|
|
static access_t init_user()
|
|
{
|
|
return M::bits(M::USER) |
|
|
T::bits(T::ARM) |
|
|
F::bits(1) |
|
|
I::bits(0) |
|
|
A::bits(1) |
|
|
E::bits(E::LITTLE) |
|
|
J::bits(J::ARM);
|
|
}
|
|
|
|
/**
|
|
* Initial value for the kernel execution context
|
|
*/
|
|
static access_t init_kernel()
|
|
{
|
|
return M::bits(M::SUPERVISOR) |
|
|
T::bits(T::ARM) |
|
|
F::bits(1) |
|
|
I::bits(1) |
|
|
A::bits(1) |
|
|
E::bits(E::LITTLE) |
|
|
J::bits(J::ARM);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Common parts of fault status registers
|
|
*/
|
|
struct Fsr : Register<32>
|
|
{
|
|
/**
|
|
* Fault status encoding
|
|
*/
|
|
enum Fault_status
|
|
{
|
|
SECTION_TRANSLATION = 5,
|
|
PAGE_TRANSLATION = 7,
|
|
};
|
|
|
|
struct Fs_3_0 : Bitfield<0, 4> { }; /* fault status */
|
|
struct Fs_4 : Bitfield<10, 1> { }; /* fault status */
|
|
};
|
|
|
|
/**
|
|
* Instruction fault status register
|
|
*/
|
|
struct Ifsr : Fsr
|
|
{
|
|
/**
|
|
* Read register value
|
|
*/
|
|
static access_t read()
|
|
{
|
|
access_t v;
|
|
asm volatile ("mrc p15, 0, %[v], c5, c0, 1" : [v]"=r"(v) :: );
|
|
return v;
|
|
}
|
|
|
|
/**
|
|
* Read fault status
|
|
*/
|
|
static Fault_status fault_status()
|
|
{
|
|
access_t const v = read();
|
|
return (Fault_status)(Fs_3_0::get(v) |
|
|
(Fs_4::get(v) << Fs_3_0::WIDTH));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Data fault status register
|
|
*/
|
|
struct Dfsr : Fsr
|
|
{
|
|
struct Wnr : Bitfield<11, 1> { }; /* write not read bit */
|
|
|
|
/**
|
|
* Read register value
|
|
*/
|
|
static access_t read()
|
|
{
|
|
access_t v;
|
|
asm volatile ("mrc p15, 0, %[v], c5, c0, 0" : [v]"=r"(v) :: );
|
|
return v;
|
|
}
|
|
|
|
/**
|
|
* Read fault status
|
|
*/
|
|
static Fault_status fault_status() {
|
|
access_t const v = read();
|
|
return (Fault_status)(Fs_3_0::get(v) |
|
|
(Fs_4::get(v) << Fs_3_0::WIDTH));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Data fault address register
|
|
*/
|
|
struct Dfar : Register<32>
|
|
{
|
|
/**
|
|
* Read register value
|
|
*/
|
|
static access_t read() {
|
|
access_t v;
|
|
asm volatile ("mrc p15, 0, %[v], c6, c0, 0" : [v]"=r"(v) :: );
|
|
return v;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Extend basic CPU state by members relevant for 'base-hw' only
|
|
*/
|
|
struct Context : Cpu_state
|
|
{
|
|
/**********************************************************
|
|
** The offset and width of any of these classmembers is **
|
|
** silently expected to be this way by several assembly **
|
|
** files. So take care if you attempt to change them. **
|
|
**********************************************************/
|
|
|
|
uint32_t cidr; /* context ID register backup */
|
|
uint32_t section_table; /* base address of applied section table */
|
|
|
|
/***************
|
|
** Accessors **
|
|
***************/
|
|
|
|
void tlb(addr_t const st) { section_table = st; }
|
|
|
|
addr_t tlb() const { return section_table; }
|
|
|
|
void protection_domain(unsigned const id) { cidr = id; }
|
|
|
|
/**
|
|
* Copy CPU state data to 'c'
|
|
*/
|
|
void read_cpu_state(Cpu_state * const s)
|
|
{
|
|
s->r0 = r0;
|
|
s->r1 = r1;
|
|
s->r2 = r2;
|
|
s->r3 = r3;
|
|
s->r4 = r4;
|
|
s->r5 = r5;
|
|
s->r6 = r6;
|
|
s->r7 = r7;
|
|
s->r8 = r8;
|
|
s->r9 = r9;
|
|
s->r10 = r10;
|
|
s->r11 = r11;
|
|
s->r12 = r12;
|
|
s->sp = sp;
|
|
s->lr = lr;
|
|
s->ip = ip;
|
|
}
|
|
|
|
/**
|
|
* Override CPU state with data from 'c'
|
|
*/
|
|
void write_cpu_state(Cpu_state * const s)
|
|
{
|
|
r0 = s->r0;
|
|
r1 = s->r1;
|
|
r2 = s->r2;
|
|
r3 = s->r3;
|
|
r4 = s->r4;
|
|
r5 = s->r5;
|
|
r6 = s->r6;
|
|
r7 = s->r7;
|
|
r8 = s->r8;
|
|
r9 = s->r9;
|
|
r10 = s->r10;
|
|
r11 = s->r11;
|
|
r12 = s->r12;
|
|
sp = s->sp;
|
|
lr = s->lr;
|
|
ip = s->ip;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* An usermode execution state
|
|
*/
|
|
struct User_context : Context
|
|
{
|
|
/**
|
|
* Constructor
|
|
*/
|
|
User_context();
|
|
|
|
/***************************************************
|
|
** 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; }
|
|
|
|
/**
|
|
* Check if a pagefault has occured due to a translation miss
|
|
*
|
|
* \param va holds the virtual fault-address if this returns 1
|
|
* \param w wether it is a write fault if this 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 ||
|
|
fs == Ifsr::PAGE_TRANSLATION)
|
|
{
|
|
/* fetch fault data */
|
|
w = 0;
|
|
va = ip;
|
|
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 ||
|
|
fs == Dfsr::PAGE_TRANSLATION)
|
|
{
|
|
/* 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;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Flush all instruction caches
|
|
*/
|
|
__attribute__((always_inline)) static void flush_instr_caches() {
|
|
asm volatile ("mcr p15, 0, %[rd], c7, c5, 0" :: [rd]"r"(0) : ); }
|
|
|
|
/**
|
|
* Flush all data caches
|
|
*/
|
|
inline static void flush_data_caches();
|
|
|
|
/**
|
|
* Flush all caches
|
|
*/
|
|
static void flush_caches()
|
|
{
|
|
flush_data_caches();
|
|
flush_instr_caches();
|
|
}
|
|
|
|
/**
|
|
* Invalidate all TLB entries of one address space
|
|
*
|
|
* \param pid ID of the targeted address space
|
|
*/
|
|
static void flush_tlb_by_pid(unsigned const pid)
|
|
{
|
|
asm volatile ("mcr p15, 0, %[pid], c8, c7, 2" :: [pid]"r"(pid) : );
|
|
flush_caches();
|
|
}
|
|
|
|
/**
|
|
* Invalidate all TLB entries
|
|
*/
|
|
static void flush_tlb()
|
|
{
|
|
asm volatile ("mcr p15, 0, %[rd], c8, c7, 0" :: [rd]"r"(0) : );
|
|
flush_caches();
|
|
}
|
|
|
|
/*
|
|
* Clean every data-cache entry within a region via MVA
|
|
*/
|
|
static void flush_data_cache_by_virt_region(addr_t base, size_t const size)
|
|
{
|
|
enum {
|
|
CACHE_LINE_SIZE = 1 << Board::CACHE_LINE_SIZE_LOG2,
|
|
CACHE_LINE_ALIGNM_MASK = ~(CACHE_LINE_SIZE - 1),
|
|
};
|
|
addr_t const top = base + size;
|
|
base = base & CACHE_LINE_ALIGNM_MASK;
|
|
for (; base < top; base += CACHE_LINE_SIZE)
|
|
asm volatile ("mcr p15, 0, %[base], c7, c10, 1\n" /* DCCMVAC */
|
|
:: [base] "r" (base) : );
|
|
}
|
|
};
|
|
}
|
|
|
|
#endif /* _CPU__ARM_H_ */
|
|
|