genode/repos/base-hw/src/core/include/spec/arm/cpu_support.h
Stefan Kalkowski c850462f43 hw: replace kernel's object id allocators
Instead of having an ID allocator per object class use one global allocator for
all. Thereby artificial limitations for the different object types are
superfluent. Moreover, replace the base-hw specific id allocator implementation
with the generic Bit_allocator, which is also memory saving.

Ref #1443
2015-04-17 16:13:20 +02:00

541 lines
12 KiB
C++

/*
* \brief CPU driver for core
* \author Martin stein
* \author Stefan Kalkowski
* \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 _SPEC__ARM__CPU_SUPPORT_H_
#define _SPEC__ARM__CPU_SUPPORT_H_
/* Genode includes */
#include <util/register.h>
#include <cpu/cpu_state.h>
/* local includes */
#include <board.h>
#include <util.h>
namespace Genode
{
/**
* CPU driver for core
*/
class Arm;
}
class Genode::Arm
{
public:
static constexpr addr_t exception_entry = 0xffff0000;
static constexpr addr_t mtc_size = 1 << MIN_PAGE_SIZE_LOG2;
static constexpr addr_t data_access_align = 4;
/**
* Multiprocessor affinity register
*/
struct Mpidr : Register<32>
{
struct Aff_0 : Bitfield<0, 8> { }; /* affinity value 0 */
static access_t read()
{
access_t v;
asm volatile ("mrc p15, 0, %0, c0, c0, 5" : "=r" (v) :: );
return v;
}
};
/**
* Cache type register
*/
struct Ctr : Register<32>
{
static access_t read()
{
access_t v;
asm volatile ("mrc p15, 0, %0, c0, c0, 1" : "=r" (v) :: );
return v;
}
};
/**
* System control register
*/
struct Sctlr : Register<32>
{
struct M : Bitfield<0,1> { }; /* enable MMU */
struct C : Bitfield<2,1> { }; /* enable data cache */
struct I : Bitfield<12,1> { }; /* enable instruction caches */
struct V : Bitfield<13,1> { }; /* select exception entry */
static access_t read()
{
access_t v;
asm volatile ("mrc p15, 0, %0, c1, c0, 0" : "=r" (v) :: );
return v;
}
static void write(access_t const v) {
asm volatile ("mcr p15, 0, %0, c1, c0, 0" :: "r" (v) : ); }
/**
* Do initialization that is common on value 'v'
*/
static void init_common(access_t & v)
{
C::set(v, 1);
I::set(v, 1);
V::set(v, 1);
}
/**
* Do initialization for virtual mode in kernel on value 'v'
*/
static void init_virt_kernel(access_t & v) { M::set(v, 1); }
};
/**
* Translation table base control register
*/
struct Ttbcr : Register<32>
{
static void write(access_t const v) {
asm volatile ("mcr p15, 0, %0, c2, c0, 2" :: "r" (v) : ); }
static access_t read()
{
access_t v;
asm volatile ("mrc p15, 0, %0, c2, c0, 2" : "=r" (v) :: );
return v;
}
};
/**
* Translation table base register 0
*/
struct Ttbr0 : Register<32>
{
enum Memory_region { NON_CACHEABLE = 0, CACHEABLE = 1 };
struct C : Bitfield<0,1> { }; /* inner cacheable */
struct S : Bitfield<1,1> { }; /* shareable */
struct Rgn : Bitfield<3,2> { }; /* outer cachable mode */
struct Nos : Bitfield<5,1> { }; /* not outer shareable */
struct Ba : Bitfield<14, 18> { }; /* translation table base */
/*************************************
* with multiprocessing extension **
*************************************/
struct Irgn_1 : Bitfield<0,1> { };
struct Irgn_0 : Bitfield<6,1> { };
struct Irgn : Bitset_2<Irgn_0, Irgn_1> { }; /* inner cache mode */
static void write(access_t const v) {
asm volatile ("mcr p15, 0, %0, c2, c0, 0" :: "r" (v) : ); }
static access_t read()
{
access_t v;
asm volatile ("mrc p15, 0, %0, c2, c0, 0" : "=r" (v) :: );
return v;
}
/**
* Return initialized value
*
* \param table base of targeted translation table
*/
static access_t init(addr_t const table)
{
access_t v = Ba::masked((addr_t)table);
Rgn::set(v, CACHEABLE);
S::set(v, Board::is_smp() ? 1 : 0);
if (Board::is_smp()) Irgn::set(v, CACHEABLE);
else C::set(v, 1);
return v;
}
};
/**
* Domain access control register
*/
struct Dacr : Register<32>
{
struct D0 : Bitfield<0,2> { }; /* access mode for domain 0 */
static void write(access_t const v) {
asm volatile ("mcr p15, 0, %0, c3, c0, 0" :: "r" (v) : ); }
/**
* Return value initialized for virtual mode in kernel
*/
static access_t init_virt_kernel() { return D0::bits(1); }
};
/**
* Instruction Cache Invalidate by MVA to PoU
*/
struct Icimvau : Register<32>
{
static void write(access_t const v) {
asm volatile ("mcr p15, 0, %0, c7, c5, 1" :: "r" (v) : ); }
};
/**
* Data Cache Clean and Invalidate by MVA to PoC
*/
struct Dccimvac : Register<32>
{
static void write(access_t const v) {
asm volatile ("mcr p15, 0, %0, c7, c14, 1" :: "r" (v) : ); }
};
/**
* Context identification register
*/
struct Cidr : Register<32>
{
static void write(access_t const v) {
asm volatile ("mcr p15, 0, %0, c13, c0, 1" :: "r" (v) : ); }
static access_t read()
{
access_t v;
asm volatile ("mrc p15, 0, %0, c13, c0, 1" : "=r" (v) :: );
return v;
}
};
/**
* Program status register
*/
struct Psr : Register<32>
{
/**
* CPU mode
*/
struct M : Bitfield<0,5>
{
enum {
USR = 16,
SVC = 19,
MON = 22,
HYP = 26,
};
};
struct F : Bitfield<6,1> { }; /* FIQ disable */
struct I : Bitfield<7,1> { }; /* IRQ disable */
struct A : Bitfield<8,1> { }; /* async. abort disable */
static access_t read()
{
access_t v;
asm volatile ("mrs %0, cpsr" : "=r" (v) :: );
return v;
}
static void write(access_t const v) {
asm volatile ("msr cpsr, %0" :: "r" (v) : ); }
/**
* Return value initialized for user execution with trustzone
*/
static access_t init_user_with_trustzone();
/**
* Do common initialization on register value 'v'
*/
static void init_common(access_t & v)
{
F::set(v, 1);
A::set(v, 1);
}
/**
* Return initial value for user execution
*/
static access_t init_user()
{
access_t v = 0;
init_common(v);
M::set(v, M::USR);
return v;
}
/**
* Return initial value for the kernel
*/
static access_t init_kernel()
{
access_t v = 0;
init_common(v);
M::set(v, M::SVC);
I::set(v, 1);
return v;
}
};
/**
* Common parts of fault status registers
*/
struct Fsr : Register<32>
{
static constexpr access_t section = 5;
static constexpr access_t page = 7;
struct Fs_0 : Bitfield<0, 4> { }; /* fault status */
struct Fs_1 : Bitfield<10, 1> { }; /* fault status */
struct Fs : Bitset_2<Fs_0, Fs_1> { }; /* fault status */
};
/**
* Instruction fault status register
*/
struct Ifsr : Fsr
{
static access_t read()
{
access_t v;
asm volatile ("mrc p15, 0, %0, c5, c0, 1" : "=r" (v) :: );
return v;
}
};
/**
* Data fault status register
*/
struct Dfsr : Fsr
{
struct Wnr : Bitfield<11, 1> { }; /* write not read bit */
static access_t read()
{
access_t v;
asm volatile ("mrc p15, 0, %0, c5, c0, 0" : "=r" (v) :: );
return v;
}
};
/**
* Data fault address register
*/
struct Dfar : Register<32>
{
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
{
Cidr::access_t cidr;
Ttbr0::access_t ttbr0;
/**
* Return base of assigned translation table
*/
addr_t translation_table() const {
return Ttbr0::Ba::masked(ttbr0); }
/**
* Assign translation-table base 'table'
*/
void translation_table(addr_t const table) {
ttbr0 = Arm::Ttbr0::init(table); }
/**
* Assign protection domain
*/
void protection_domain(Genode::uint8_t const id) { cidr = id; }
};
/**
* This class comprises ARM specific protection domain attributes
*/
struct Pd
{
Genode::uint8_t asid; /* address space id */
Pd(Genode::uint8_t id) : asid(id) {}
};
/**
* An usermode execution state
*/
struct User_context : Context
{
/**
* Constructor
*/
User_context();
/**
* Support for kernel calls
*/
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; }
/**
* Initialize thread context
*
* \param table physical base of appropriate translation table
* \param pd_id kernel name of appropriate protection domain
*/
void init_thread(addr_t const table, unsigned const pd_id)
{
protection_domain(pd_id);
translation_table(table);
}
/**
* Return if the context is in a page fault due to translation miss
*
* \param va holds the virtual fault-address if call returns 1
* \param w holds wether it's a write fault if call returns 1
*/
bool in_fault(addr_t & va, addr_t & w) const
{
switch (cpu_exception) {
case PREFETCH_ABORT:
{
/* check if fault was caused by a translation miss */
Ifsr::access_t const fs = Ifsr::Fs::get(Ifsr::read());
if (fs != Ifsr::section && fs != Ifsr::page)
return false;
/* fetch fault data */
w = 0;
va = ip;
return true;
}
case DATA_ABORT:
{
/* check if fault was caused by translation miss */
Dfsr::access_t const fs = Dfsr::Fs::get(Dfsr::read());
if (fs != Dfsr::section && fs != Dfsr::page)
return false;
/* fetch fault data */
Dfsr::access_t const dfsr = Dfsr::read();
w = Dfsr::Wnr::get(dfsr);
va = Dfar::read();
return true;
}
default:
return false;
};
}
};
/**
* Returns true if current execution context is running in user mode
*/
static bool is_user() { return Psr::M::get(Psr::read()) == Psr::M::USR; }
/**
* Invalidate all entries of all instruction caches
*/
__attribute__((always_inline)) static void invalidate_instr_caches() {
asm volatile ("mcr p15, 0, %0, c7, c5, 0" :: "r" (0) : ); }
/**
* Flush all entries of all data caches
*/
static void flush_data_caches();
/**
* Invalidate all entries of all data caches
*/
static void invalidate_data_caches();
/**
* Flush all caches
*/
static void flush_caches()
{
flush_data_caches();
invalidate_instr_caches();
}
/**
* Invalidate all TLB entries of the address space named 'pid'
*/
static void flush_tlb_by_pid(unsigned const pid)
{
flush_caches();
asm volatile ("mcr p15, 0, %0, c8, c7, 2" :: "r" (pid) : );
}
/**
* Invalidate all TLB entries
*/
static void flush_tlb()
{
flush_caches();
asm volatile ("mcr p15, 0, %0, c8, c7, 0" :: "r" (0) : );
}
static constexpr addr_t line_size = 1 << Board::CACHE_LINE_SIZE_LOG2;
static constexpr addr_t line_align_mask = ~(line_size - 1);
/**
* Flush data-cache entries for virtual region ['base', 'base + size')
*/
static void
flush_data_caches_by_virt_region(addr_t base, size_t const size)
{
addr_t const top = base + size;
base &= line_align_mask;
for (; base < top; base += line_size) { Dccimvac::write(base); }
}
/**
* Bin instr.-cache entries for virtual region ['base', 'base + size')
*/
static void
invalidate_instr_caches_by_virt_region(addr_t base, size_t const size)
{
addr_t const top = base + size;
base &= line_align_mask;
for (; base < top; base += line_size) { Icimvau::write(base); }
}
};
#endif /* _SPEC__ARM__CPU_SUPPORT_H_ */