Simple drivers for the Cortex A9 components
This commit is contained in:
parent
056f980d4e
commit
dce09679bc
607
base/include/drivers/cpu/cortex_a9/core.h
Normal file
607
base/include/drivers/cpu/cortex_a9/core.h
Normal file
|
@ -0,0 +1,607 @@
|
||||||
|
/*
|
||||||
|
* \brief Simple Driver for the ARM Cortex A9
|
||||||
|
* \author Martin stein
|
||||||
|
* \date 2011-11-03
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2012 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 _BASE__INCLUDE__DRIVERS__CPU__CORTEX_A9__CORE_H_
|
||||||
|
#define _BASE__INCLUDE__DRIVERS__CPU__CORTEX_A9__CORE_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <util/register.h>
|
||||||
|
#include <util/mmio.h>
|
||||||
|
#include <drivers/board.h>
|
||||||
|
#include <drivers/cpu/cortex_a9/timer.h>
|
||||||
|
|
||||||
|
namespace Genode
|
||||||
|
{
|
||||||
|
class Section_table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cortex A9 driver
|
||||||
|
*/
|
||||||
|
struct Cortex_a9
|
||||||
|
{
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
/* Common */
|
||||||
|
DATA_ACCESS_ALIGNM = 4,
|
||||||
|
CLK = Board::CORTEX_A9_CLOCK, /* CPU interface clock */
|
||||||
|
PERIPH_CLK = CLK, /* Clock for CPU internal components */
|
||||||
|
MIN_PAGE_SIZE_LOG2 = 12,
|
||||||
|
MAX_PAGE_SIZE_LOG2 = 20,
|
||||||
|
HIGHEST_EXCEPTION_ENTRY = 0xffff0000,
|
||||||
|
|
||||||
|
/* Interrupt controller */
|
||||||
|
PL390_DISTRIBUTOR_MMIO_BASE = Board::CORTEX_A9_PRIVATE_MEM_BASE + 0x1000,
|
||||||
|
PL390_DISTRIBUTOR_MMIO_SIZE = 0x1000,
|
||||||
|
PL390_CPU_MMIO_BASE = Board::CORTEX_A9_PRIVATE_MEM_BASE + 0x100,
|
||||||
|
PL390_CPU_MMIO_SIZE = 0x100,
|
||||||
|
|
||||||
|
/* 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Exceotion type IDs */
|
||||||
|
enum Exception_type
|
||||||
|
{
|
||||||
|
RESET = 1,
|
||||||
|
UNDEFINED_INSTRUCTION = 2,
|
||||||
|
SUPERVISOR_CALL = 3,
|
||||||
|
PREFETCH_ABORT = 4,
|
||||||
|
DATA_ABORT = 5,
|
||||||
|
INTERRUPT_REQUEST = 6,
|
||||||
|
FAST_INTERRUPT_REQUEST = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef Cortex_a9_timer<PERIPH_CLK> Timer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common parts of fault status registers
|
||||||
|
*/
|
||||||
|
struct Fsr : Register<32>
|
||||||
|
{
|
||||||
|
/* Fault status encoding */
|
||||||
|
enum Fault_status {
|
||||||
|
SECTION_TRANSLATION_FAULT = 5,
|
||||||
|
PAGE_TRANSLATION_FAULT = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Fs_3_0 : Bitfield<0, 4> { }; /* Fault status bits [3:0] */
|
||||||
|
struct Fs_4 : Bitfield<10, 1> { }; /* Fault status bits [4] */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instruction fault status register
|
||||||
|
*/
|
||||||
|
struct Ifsr : Fsr
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Read register
|
||||||
|
*/
|
||||||
|
static access_t read() {
|
||||||
|
access_t v;
|
||||||
|
asm volatile ("mrc p15, 0, %[v], c5, c0, 1\n" : [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));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instruction fault address register
|
||||||
|
*/
|
||||||
|
struct Ifar : Register<32>
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Read register
|
||||||
|
*/
|
||||||
|
static access_t read() {
|
||||||
|
access_t v;
|
||||||
|
asm volatile ("mrc p15, 0, %[v], c6, c0, 2\n" : [v]"=r"(v) :: );
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data fault status register
|
||||||
|
*/
|
||||||
|
struct Dfsr : Fsr
|
||||||
|
{
|
||||||
|
struct Wnr : Bitfield<11, 1> { }; /* Write not read bit */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read register
|
||||||
|
*/
|
||||||
|
static access_t read() {
|
||||||
|
access_t v;
|
||||||
|
asm volatile ("mrc p15, 0, %[v], c5, c0, 0\n" : [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) | (4<<Fs_4::get(v)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data fault address register
|
||||||
|
*/
|
||||||
|
struct Dfar : Register<32>
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Read register
|
||||||
|
*/
|
||||||
|
static access_t read() {
|
||||||
|
access_t v;
|
||||||
|
asm volatile ("mrc p15, 0, %[v], c6, c0, 0\n" : [v]"=r"(v) :: );
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process identification register
|
||||||
|
*/
|
||||||
|
struct Contextidr : Register<32>
|
||||||
|
{
|
||||||
|
struct Asid : Bitfield<0,8> /* ID part used by MMU */
|
||||||
|
{
|
||||||
|
enum { MAX = MASK };
|
||||||
|
};
|
||||||
|
struct Procid : Bitfield<8,24> { }; /* ID part used by debug/trace */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write whole register
|
||||||
|
*/
|
||||||
|
static void write(access_t const v)
|
||||||
|
{
|
||||||
|
asm volatile ("mcr p15, 0, %[v], c13, c0, 1\n" :: [v]"r"(v) : );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A system control register
|
||||||
|
*/
|
||||||
|
struct Sctlr : Register<32>
|
||||||
|
{
|
||||||
|
struct M : Bitfield<0,1> { }; /* MMU enable bit */
|
||||||
|
struct C : Bitfield<2,1> { }; /* Cache enable bit */
|
||||||
|
struct I : Bitfield<12,1> { }; /* Instruction cache enable bit */
|
||||||
|
struct V : Bitfield<13,1> { }; /* Exception vectors bit */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read whole register
|
||||||
|
*/
|
||||||
|
static access_t read()
|
||||||
|
{
|
||||||
|
access_t v;
|
||||||
|
asm volatile ("mrc p15, 0, %[v], c1, c0, 0\n" : [v]"=r"(v) :: );
|
||||||
|
return v;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write whole register
|
||||||
|
*/
|
||||||
|
static void write(access_t const v)
|
||||||
|
{
|
||||||
|
asm volatile ("mcr p15, 0, %[v], c1, c0, 0\n" :: [v]"r"(v) : );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The translation table base control register
|
||||||
|
*/
|
||||||
|
struct Ttbcr : Register<32>
|
||||||
|
{
|
||||||
|
/********************
|
||||||
|
* Always available *
|
||||||
|
********************/
|
||||||
|
|
||||||
|
struct N : Bitfield<0,3> /* Base address width */
|
||||||
|
{ };
|
||||||
|
|
||||||
|
/******************************************
|
||||||
|
* Only available with security extension *
|
||||||
|
******************************************/
|
||||||
|
|
||||||
|
struct Pd0 : Bitfield<4,1> { }; /* Translation table walk disable bit for TTBR0 */
|
||||||
|
struct Pd1 : Bitfield<5,1> { }; /* Translation table walk disable bit for TTBR1 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read whole register, only in privileged CPU mode
|
||||||
|
*/
|
||||||
|
static access_t read();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write whole 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) : );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The domain access control register
|
||||||
|
*/
|
||||||
|
struct Dacr : Register<32>
|
||||||
|
{
|
||||||
|
enum Dx_values { NO_ACCESS = 0, CLIENT = 1, MANAGER = 3 };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 whole 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) : );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translation table base register 0
|
||||||
|
*
|
||||||
|
* \detail Typically for process specific spaces, references first level
|
||||||
|
* table with a size between 128B and 16KB according to TTBCR.N,
|
||||||
|
*/
|
||||||
|
struct Ttbr0 : Register<32>
|
||||||
|
{
|
||||||
|
/********************
|
||||||
|
* Always available *
|
||||||
|
********************/
|
||||||
|
|
||||||
|
struct S : Bitfield<1,1> { }; /* Shareable bit */
|
||||||
|
struct Rgn : Bitfield<3,2> /* Region bits */
|
||||||
|
{
|
||||||
|
enum { OUTER_NON_CACHEABLE = 0b00,
|
||||||
|
OUTER_WBACK_WALLOCATE_CACHEABLE = 0b01,
|
||||||
|
OUTER_WTHROUGH_CACHEABLE = 0b10,
|
||||||
|
OUTER_WBACK_NO_WALLCOATE_CACHEABLE = 0b11,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Nos : Bitfield<5,1> { }; /* Not outer shareable bit */
|
||||||
|
struct Base_address : Bitfield<14,18> { }; /* Translation table base address (Driver supports only 16KB alignment) */
|
||||||
|
|
||||||
|
/*********************************************
|
||||||
|
* Only available without security extension *
|
||||||
|
*********************************************/
|
||||||
|
|
||||||
|
struct C : Bitfield<0,1> { }; /* Cacheable bit */
|
||||||
|
|
||||||
|
/******************************************
|
||||||
|
* Only available with security extension *
|
||||||
|
******************************************/
|
||||||
|
|
||||||
|
struct Irgn_1 : Bitfield<0,1> /* Inner region bit 0 */
|
||||||
|
{
|
||||||
|
enum { INNER_NON_CACHEABLE = 0b0,
|
||||||
|
INNER_WBACK_WALLOCATE_CACHEABLE = 0b0,
|
||||||
|
INNER_WTHROUGH_CACHEABLE = 0b1,
|
||||||
|
INNER_WBACK_NO_WALLCOATE_CACHEABLE = 0b1,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Irgn_0 : Bitfield<6,1> /* Inner region bit 1 */
|
||||||
|
{
|
||||||
|
enum { INNER_NON_CACHEABLE = 0b0,
|
||||||
|
INNER_WBACK_WALLOCATE_CACHEABLE = 0b1,
|
||||||
|
INNER_WTHROUGH_CACHEABLE = 0b0,
|
||||||
|
INNER_WBACK_NO_WALLCOATE_CACHEABLE = 0b1,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read whole register, only in privileged CPU mode
|
||||||
|
*/
|
||||||
|
static access_t read();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write whole 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) : );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A current program status register
|
||||||
|
*/
|
||||||
|
struct Cpsr : Register<32>
|
||||||
|
{
|
||||||
|
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 */
|
||||||
|
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 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read whole register
|
||||||
|
*/
|
||||||
|
static access_t read()
|
||||||
|
{
|
||||||
|
access_t v;
|
||||||
|
asm volatile ("mrs %[v], cpsr\n" : [v] "=r" (v) : : );
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write whole register
|
||||||
|
*/
|
||||||
|
static void write(access_t & v)
|
||||||
|
{
|
||||||
|
asm volatile ("msr cpsr, %[v]\n" : : [v] "r" (v) : );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Secure configuration register
|
||||||
|
*/
|
||||||
|
struct Scr : Register<32>
|
||||||
|
{
|
||||||
|
struct Ns : Bitfield<0, 1> { }; /* Non secure bit */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read whole register
|
||||||
|
*/
|
||||||
|
static access_t read()
|
||||||
|
{
|
||||||
|
access_t v;
|
||||||
|
asm volatile ("mrc p15, 0, %[v], c1, c1, 0" : [v]"=r"(v) ::);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An execution state
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* Special registers, offset 16*4 .. 17*4 */
|
||||||
|
uint32_t psr, contextidr;
|
||||||
|
|
||||||
|
/* Additional state info, offset 18*4 .. 19*4 */
|
||||||
|
uint32_t exception_type, section_table;
|
||||||
|
|
||||||
|
/***************
|
||||||
|
** Accessors **
|
||||||
|
***************/
|
||||||
|
|
||||||
|
void software_tlb(Section_table * const st)
|
||||||
|
{ section_table = (addr_t)st; }
|
||||||
|
|
||||||
|
Section_table * software_tlb() { return (Section_table *)section_table; }
|
||||||
|
|
||||||
|
void instruction_ptr(addr_t const p) { pc = p; }
|
||||||
|
|
||||||
|
addr_t instruction_ptr() { return pc; }
|
||||||
|
|
||||||
|
void return_ptr(addr_t const p) { lr = p; }
|
||||||
|
|
||||||
|
void stack_ptr(addr_t const p) { sp = p; }
|
||||||
|
|
||||||
|
void pd_id(unsigned long const id) { contextidr = id; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable interrupt requests
|
||||||
|
*/
|
||||||
|
static void enable_irqs()
|
||||||
|
{
|
||||||
|
Cpsr::access_t cpsr = Cpsr::read();
|
||||||
|
Cpsr::I::clear(cpsr);
|
||||||
|
Cpsr::write(cpsr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set CPU exception entry to a given address
|
||||||
|
*
|
||||||
|
* \return 0 Exception entry set to the given address
|
||||||
|
* <0 Otherwise
|
||||||
|
*/
|
||||||
|
static int exception_entry_at(addr_t a)
|
||||||
|
{
|
||||||
|
Sctlr::access_t sctlr = Sctlr::read();
|
||||||
|
switch (a) {
|
||||||
|
case 0x0:
|
||||||
|
Sctlr::V::clear(sctlr);
|
||||||
|
break;
|
||||||
|
case 0xffff0000:
|
||||||
|
Sctlr::V::set(sctlr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
Sctlr::write(sctlr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Are we in secure mode?
|
||||||
|
*/
|
||||||
|
static bool secure_mode_active()
|
||||||
|
{
|
||||||
|
if (!Board::CORTEX_A9_SECURITY_EXTENSION) return 0;
|
||||||
|
if (Cpsr::M::get(Cpsr::read()) != Cpsr::M::MONITOR)
|
||||||
|
{
|
||||||
|
return !Scr::Ns::get(Scr::read());
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the MMU
|
||||||
|
*
|
||||||
|
* \param section_table Section translation table of the initial
|
||||||
|
* address space this function switches to
|
||||||
|
* \param process_id Process ID of the initial address space
|
||||||
|
*/
|
||||||
|
static void enable_mmu (Section_table * const section_table,
|
||||||
|
unsigned long const process_id)
|
||||||
|
{
|
||||||
|
/* Initialize domains */
|
||||||
|
Dacr::write (Dacr::D0::bits (Dacr::CLIENT)
|
||||||
|
| Dacr::D1::bits (Dacr::NO_ACCESS)
|
||||||
|
| Dacr::D2::bits (Dacr::NO_ACCESS)
|
||||||
|
| Dacr::D3::bits (Dacr::NO_ACCESS)
|
||||||
|
| Dacr::D4::bits (Dacr::NO_ACCESS)
|
||||||
|
| Dacr::D5::bits (Dacr::NO_ACCESS)
|
||||||
|
| Dacr::D6::bits (Dacr::NO_ACCESS)
|
||||||
|
| Dacr::D7::bits (Dacr::NO_ACCESS)
|
||||||
|
| Dacr::D8::bits (Dacr::NO_ACCESS)
|
||||||
|
| Dacr::D9::bits (Dacr::NO_ACCESS)
|
||||||
|
| Dacr::D10::bits (Dacr::NO_ACCESS)
|
||||||
|
| Dacr::D11::bits (Dacr::NO_ACCESS)
|
||||||
|
| Dacr::D12::bits (Dacr::NO_ACCESS)
|
||||||
|
| Dacr::D13::bits (Dacr::NO_ACCESS)
|
||||||
|
| Dacr::D14::bits (Dacr::NO_ACCESS)
|
||||||
|
| Dacr::D15::bits (Dacr::NO_ACCESS));
|
||||||
|
|
||||||
|
/* Switch process ID */
|
||||||
|
Contextidr::write(process_id);
|
||||||
|
|
||||||
|
/* Install section table */
|
||||||
|
Ttbr0::write (Ttbr0::Base_address::masked ((addr_t)section_table));
|
||||||
|
Ttbcr::write (Ttbcr::N::bits(0)
|
||||||
|
| Ttbcr::Pd0::bits(0)
|
||||||
|
| Ttbcr::Pd1::bits(0) );
|
||||||
|
|
||||||
|
/* Enable MMU without instruction-, data-, or unified caches */
|
||||||
|
Sctlr::access_t sctlr = Sctlr::read();
|
||||||
|
Sctlr::M::set(sctlr);
|
||||||
|
Sctlr::I::clear(sctlr);
|
||||||
|
Sctlr::C::clear(sctlr);
|
||||||
|
Sctlr::write(sctlr);
|
||||||
|
flush_branch_prediction();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate all entries of the branch predictor array
|
||||||
|
*
|
||||||
|
* \detail Must be inline to avoid dependence on the branch predictor
|
||||||
|
*/
|
||||||
|
__attribute__((always_inline)) inline static void flush_branch_prediction()
|
||||||
|
{
|
||||||
|
asm volatile ("mcr p15, 0, r0, c7, c5, 6\n"
|
||||||
|
"isb");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate at least all TLB entries regarding a specific process
|
||||||
|
*
|
||||||
|
* \param process_id ID of the targeted process
|
||||||
|
*/
|
||||||
|
static void flush_tlb_by_pid (unsigned const process_id)
|
||||||
|
{
|
||||||
|
asm volatile ("mcr p15, 0, %[asid], c8, c7, 2 \n"
|
||||||
|
:: [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 pagefault
|
||||||
|
* \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
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _BASE__INCLUDE__DRIVERS__CPU__CORTEX_A9__CORE_H_ */
|
||||||
|
|
845
base/include/drivers/cpu/cortex_a9/section_table.h
Normal file
845
base/include/drivers/cpu/cortex_a9/section_table.h
Normal file
|
@ -0,0 +1,845 @@
|
||||||
|
/*
|
||||||
|
* \brief Driver for Cortex A9 section tables as software TLB
|
||||||
|
* \author Martin Stein
|
||||||
|
* \date 2012-02-22
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 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 _BASE__INCLUDE__DRIVERS__CPU__CORTEX_A9__SECTION_TABLE_H_
|
||||||
|
#define _BASE__INCLUDE__DRIVERS__CPU__CORTEX_A9__SECTION_TABLE_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <util/register.h>
|
||||||
|
#include <base/printf.h>
|
||||||
|
#include <drivers/cpu/cortex_a9/core.h>
|
||||||
|
|
||||||
|
namespace Genode
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Check if 'p' is aligned to 1 << 'alignm_log2'
|
||||||
|
*/
|
||||||
|
bool inline aligned(addr_t const a, unsigned long const alignm_log2)
|
||||||
|
{
|
||||||
|
return a == ((a >> alignm_log2) << alignm_log2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common access permission [1:0] bitfield values
|
||||||
|
*/
|
||||||
|
struct Ap_1_0_bitfield
|
||||||
|
{
|
||||||
|
enum { KERNEL_AND_USER_NO_ACCESS = 0,
|
||||||
|
KERNEL_AND_USER_SAME_ACCESS = 3 };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common access permission [2] bitfield values
|
||||||
|
*/
|
||||||
|
struct Ap_2_bitfield
|
||||||
|
{
|
||||||
|
enum { KERNEL_RW_OR_NO_ACCESS = 0,
|
||||||
|
KERNEL_RO_ACCESS = 1 };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cortex A9 second level translation table
|
||||||
|
*
|
||||||
|
* \detail A table is dedicated to either secure or non-secure
|
||||||
|
* mode. All translations done by this table apply
|
||||||
|
* domain 0. They are not shareable and have zero-filled
|
||||||
|
* memory region attributes.
|
||||||
|
*/
|
||||||
|
class Page_table
|
||||||
|
{
|
||||||
|
enum {
|
||||||
|
_1KB_LOG2 = 10,
|
||||||
|
_4KB_LOG2 = 12,
|
||||||
|
_64KB_LOG2 = 16,
|
||||||
|
_1MB_LOG2 = 20,
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SIZE_LOG2 = _1KB_LOG2,
|
||||||
|
SIZE = 1 << SIZE_LOG2,
|
||||||
|
ALIGNM_LOG2 = SIZE_LOG2,
|
||||||
|
|
||||||
|
VIRT_SIZE_LOG2 = _1MB_LOG2,
|
||||||
|
VIRT_SIZE = 1 << VIRT_SIZE_LOG2,
|
||||||
|
VIRT_BASE_MASK = ~((1 << VIRT_SIZE_LOG2) - 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common descriptor structure
|
||||||
|
*/
|
||||||
|
struct Descriptor : Register<32>
|
||||||
|
{
|
||||||
|
/* Descriptor types */
|
||||||
|
enum Type { FAULT, SMALL_PAGE, LARGE_PAGE };
|
||||||
|
|
||||||
|
struct Type_1 : Bitfield<1, 1> { };
|
||||||
|
struct Type_2 : Bitfield<0, 1> { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get descriptor type of 'v'
|
||||||
|
*/
|
||||||
|
static Type type(access_t const v)
|
||||||
|
{
|
||||||
|
access_t const t1 = Type_1::get(v);
|
||||||
|
if (t1 == 0) {
|
||||||
|
access_t const t2 = Type_2::get(v);
|
||||||
|
if (t2 == 0) return FAULT;
|
||||||
|
if (t2 == 1) return LARGE_PAGE;
|
||||||
|
}
|
||||||
|
if (t1 == 1) return SMALL_PAGE;
|
||||||
|
return FAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set descriptor type of 'v'
|
||||||
|
*/
|
||||||
|
static void type(access_t & v, Type const t)
|
||||||
|
{
|
||||||
|
switch (t) {
|
||||||
|
case FAULT:
|
||||||
|
Type_1::set(v, 0);
|
||||||
|
Type_2::set(v, 0);
|
||||||
|
break;
|
||||||
|
case SMALL_PAGE:
|
||||||
|
Type_1::set(v, 1);
|
||||||
|
break;
|
||||||
|
case LARGE_PAGE:
|
||||||
|
Type_1::set(v, 0);
|
||||||
|
Type_2::set(v, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate descriptor 'v'
|
||||||
|
*/
|
||||||
|
static void invalidate(access_t & v) { type(v, FAULT); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if descriptor 'v' is valid
|
||||||
|
*/
|
||||||
|
static bool valid(access_t & v) { return type(v) != FAULT; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an untranslated virtual region
|
||||||
|
*/
|
||||||
|
struct Fault : Descriptor
|
||||||
|
{
|
||||||
|
enum {
|
||||||
|
VIRT_SIZE_LOG2 = _4KB_LOG2,
|
||||||
|
VIRT_SIZE = 1 << VIRT_SIZE_LOG2,
|
||||||
|
VIRT_BASE_MASK = ~((1 << VIRT_SIZE_LOG2) - 1)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Large page descriptor structure
|
||||||
|
*
|
||||||
|
* \detail Must always occur as group of 16 consecutive copies, this
|
||||||
|
* groups must be aligned on a 16 word boundary (Represents
|
||||||
|
* 64KB = 16 * Small page size)
|
||||||
|
*/
|
||||||
|
struct Large_page : Descriptor
|
||||||
|
{
|
||||||
|
enum { VIRT_SIZE_LOG2 = _64KB_LOG2,
|
||||||
|
VIRT_SIZE = 1 << VIRT_SIZE_LOG2,
|
||||||
|
VIRT_BASE_MASK = ~((1 << VIRT_SIZE_LOG2) - 1) };
|
||||||
|
|
||||||
|
struct B : Bitfield<2, 1> { }; /* Part of the memory region attributes */
|
||||||
|
struct C : Bitfield<3, 1> { }; /* Part of the memory region attributes */
|
||||||
|
struct Ap_1_0 : Bitfield<4, 2>, /* Access permission bits [1:0] */
|
||||||
|
Ap_1_0_bitfield { };
|
||||||
|
struct Ap_2 : Bitfield<9, 1>, /* Access permission bits [2] */
|
||||||
|
Ap_2_bitfield { };
|
||||||
|
struct S : Bitfield<10, 1> { }; /* Shareable bit */
|
||||||
|
struct Ng : Bitfield<11, 1> { }; /* Not global bit */
|
||||||
|
struct Tex : Bitfield<12, 3> { }; /* Part of the memory region attributes */
|
||||||
|
struct Xn : Bitfield<15, 1> { }; /* Execute never bit */
|
||||||
|
struct Pa_31_16 : Bitfield<16, 16> { }; /* Physical address bits [31:16] */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Small page descriptor structure
|
||||||
|
*/
|
||||||
|
struct Small_page : Descriptor
|
||||||
|
{
|
||||||
|
enum {
|
||||||
|
VIRT_SIZE_LOG2 = _4KB_LOG2,
|
||||||
|
VIRT_SIZE = 1 << VIRT_SIZE_LOG2,
|
||||||
|
VIRT_BASE_MASK = ~((1 << VIRT_SIZE_LOG2) - 1)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Xn : Bitfield<0, 1> { }; /* Execute never bit */
|
||||||
|
struct B : Bitfield<2, 1> { }; /* Part of the memory region attributes */
|
||||||
|
struct C : Bitfield<3, 1> { }; /* Part of the memory region attributes */
|
||||||
|
struct Ap_1_0 : Bitfield<4, 2>, /* Access permission bits [1:0] */
|
||||||
|
Ap_1_0_bitfield { };
|
||||||
|
struct Tex : Bitfield<6, 3> { }; /* Part of the memory region attributes */
|
||||||
|
struct Ap_2 : Bitfield<9, 1>, /* Access permission bits [2] */
|
||||||
|
Ap_2_bitfield { };
|
||||||
|
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 */
|
||||||
|
Descriptor::access_t _entries[SIZE/sizeof(Descriptor::access_t)];
|
||||||
|
|
||||||
|
enum { MAX_INDEX = sizeof(_entries) / sizeof(_entries[0]) - 1 };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
int _index_by_vo (unsigned long & i, addr_t const vo) const
|
||||||
|
{
|
||||||
|
if (vo > max_virt_offset()) return -1;
|
||||||
|
i = vo >> Small_page::VIRT_SIZE_LOG2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Placement new operator
|
||||||
|
*/
|
||||||
|
void * operator new (size_t, void * p) { return p; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
Page_table()
|
||||||
|
{
|
||||||
|
/* Check table alignment */
|
||||||
|
if (!aligned((addr_t)this, ALIGNM_LOG2)
|
||||||
|
|| (addr_t)this != (addr_t)_entries)
|
||||||
|
{
|
||||||
|
PDBG("Insufficient table alignment");
|
||||||
|
while (1) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start with an empty table */
|
||||||
|
for (unsigned i = 0; i <= MAX_INDEX; i++)
|
||||||
|
Descriptor::invalidate(_entries[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum virtual offset that can be translated by this table
|
||||||
|
*/
|
||||||
|
static addr_t max_virt_offset()
|
||||||
|
{
|
||||||
|
return (MAX_INDEX << Small_page::VIRT_SIZE_LOG2)
|
||||||
|
+ (Small_page::VIRT_SIZE - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* \detail 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)
|
||||||
|
{
|
||||||
|
/* Validate virtual address */
|
||||||
|
unsigned long i;
|
||||||
|
if (_index_by_vo (i, vo)) {
|
||||||
|
PDBG("Invalid virtual offset");
|
||||||
|
while (1) ;
|
||||||
|
}
|
||||||
|
/* Select descriptor type by the translation size */
|
||||||
|
if (size_log2 == Small_page::VIRT_SIZE_LOG2)
|
||||||
|
{
|
||||||
|
/* Can we write to the targeted entry? */
|
||||||
|
if (Descriptor::valid(_entries[i]) &&
|
||||||
|
Descriptor::type(_entries[i]) != Descriptor::SMALL_PAGE)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PDBG("Translation size not supported");
|
||||||
|
while (1) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
void remove_region (addr_t const vo, size_t const size)
|
||||||
|
{
|
||||||
|
/* Traverse all possibly affected entries */
|
||||||
|
addr_t residual_vo = vo;
|
||||||
|
unsigned long i;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
/* Is anything left over to remove? */
|
||||||
|
if (residual_vo >= vo + size) return;
|
||||||
|
|
||||||
|
/* Does the residual region overlap with the region
|
||||||
|
* represented by this table? */
|
||||||
|
if (_index_by_vo(i, residual_vo)) return;
|
||||||
|
|
||||||
|
/* Update current entry and recalculate the residual region */
|
||||||
|
switch (Descriptor::type(_entries[i]))
|
||||||
|
{
|
||||||
|
case Descriptor::FAULT:
|
||||||
|
{
|
||||||
|
residual_vo = (residual_vo & Fault::VIRT_BASE_MASK)
|
||||||
|
+ Fault::VIRT_SIZE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Descriptor::SMALL_PAGE:
|
||||||
|
{
|
||||||
|
residual_vo = (residual_vo & Small_page::VIRT_BASE_MASK)
|
||||||
|
+ Small_page::VIRT_SIZE;
|
||||||
|
Descriptor::invalidate(_entries[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Descriptor::LARGE_PAGE:
|
||||||
|
{
|
||||||
|
PDBG("Removal of large pages not implemented");
|
||||||
|
while (1) ;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this table solely contain invalid entries
|
||||||
|
*/
|
||||||
|
bool empty()
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i <= MAX_INDEX; i++) {
|
||||||
|
if (Descriptor::valid(_entries[i])) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} __attribute__((aligned(1<<Page_table::ALIGNM_LOG2)));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cortex A9 first level translation table
|
||||||
|
*
|
||||||
|
* \detail A table is dedicated to either secure or non-secure
|
||||||
|
* mode. All translations done by this table apply
|
||||||
|
* domain 0. They are not shareable and have zero-filled
|
||||||
|
* memory region attributes. The size of this table is fixed
|
||||||
|
* to such a value that this table translates a space wich is
|
||||||
|
* addressable by 32 bit.
|
||||||
|
*/
|
||||||
|
class Section_table
|
||||||
|
{
|
||||||
|
enum {
|
||||||
|
_16KB_LOG2 = 14,
|
||||||
|
_1MB_LOG2 = 20,
|
||||||
|
_16MB_LOG2 = 24,
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SIZE_LOG2 = _16KB_LOG2,
|
||||||
|
SIZE = 1 << SIZE_LOG2,
|
||||||
|
ALIGNM_LOG2 = SIZE_LOG2,
|
||||||
|
|
||||||
|
VIRT_SIZE_LOG2 = _1MB_LOG2,
|
||||||
|
VIRT_SIZE = 1 << VIRT_SIZE_LOG2,
|
||||||
|
VIRT_BASE_MASK = ~((1 << VIRT_SIZE_LOG2) - 1),
|
||||||
|
|
||||||
|
MAX_COSTS_PER_TRANSLATION = sizeof(Page_table),
|
||||||
|
|
||||||
|
MAX_TRANSL_SIZE_LOG2 = 20,
|
||||||
|
MIN_TRANSL_SIZE_LOG2 = 12,
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A first level translation descriptor
|
||||||
|
*/
|
||||||
|
struct Descriptor : Register<32>
|
||||||
|
{
|
||||||
|
/* Descriptor types */
|
||||||
|
enum Type { FAULT, PAGE_TABLE, SECTION, SUPERSECTION };
|
||||||
|
|
||||||
|
struct Type_1 : Bitfield<0, 2> { }; /* Entry type encoding 1 */
|
||||||
|
struct Type_2 : Bitfield<18, 1> { }; /* Entry type encoding 2 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get descriptor type of 'v'
|
||||||
|
*/
|
||||||
|
static Type type(access_t const v)
|
||||||
|
{
|
||||||
|
access_t const t1 = Type_1::get(v);
|
||||||
|
if (t1 == 0) return FAULT;
|
||||||
|
if (t1 == 1) return PAGE_TABLE;
|
||||||
|
if (t1 == 2) {
|
||||||
|
access_t const t2 = Type_2::get(v);
|
||||||
|
if (t2 == 0) return SECTION;
|
||||||
|
if (t2 == 1) return SUPERSECTION;
|
||||||
|
}
|
||||||
|
return FAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set descriptor type of 'v'
|
||||||
|
*/
|
||||||
|
static void type(access_t & v, Type const t)
|
||||||
|
{
|
||||||
|
switch (t) {
|
||||||
|
case FAULT: Type_1::set(v, 0); break;
|
||||||
|
case PAGE_TABLE: Type_1::set(v, 1); break;
|
||||||
|
case SECTION:
|
||||||
|
Type_1::set(v, 2);
|
||||||
|
Type_2::set(v, 0); break;
|
||||||
|
case SUPERSECTION:
|
||||||
|
Type_1::set(v, 2);
|
||||||
|
Type_2::set(v, 1); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate descriptor 'v'
|
||||||
|
*/
|
||||||
|
static void invalidate(access_t & v) { type(v, FAULT); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if descriptor 'v' is valid
|
||||||
|
*/
|
||||||
|
static bool valid(access_t & v) { return type(v) != FAULT; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an untranslated virtual region
|
||||||
|
*/
|
||||||
|
struct Fault : Descriptor
|
||||||
|
{
|
||||||
|
enum {
|
||||||
|
VIRT_SIZE_LOG2 = _1MB_LOG2,
|
||||||
|
VIRT_SIZE = 1 << VIRT_SIZE_LOG2,
|
||||||
|
VIRT_BASE_MASK = ~((1 << VIRT_SIZE_LOG2) - 1)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* References a second level translation table for the virtual
|
||||||
|
* region it represents
|
||||||
|
*/
|
||||||
|
struct Page_table_descriptor : Descriptor
|
||||||
|
{
|
||||||
|
struct Ns : Bitfield<3, 1> { }; /* Non-secure bit */
|
||||||
|
struct Domain : Bitfield<5, 4> { }; /* Domain field */
|
||||||
|
struct Pa_31_10 : Bitfield<10, 22> { }; /* Physical address bits [31:10] */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supersection-descriptor structure
|
||||||
|
*
|
||||||
|
* \detail Must always occur as group of 16 consecutive copies, this
|
||||||
|
* groups must be aligned on a 16 word boundary.
|
||||||
|
*/
|
||||||
|
struct Supersection : Descriptor
|
||||||
|
{
|
||||||
|
enum {
|
||||||
|
VIRT_SIZE_LOG2 = _16MB_LOG2,
|
||||||
|
VIRT_SIZE = 1 << VIRT_SIZE_LOG2,
|
||||||
|
VIRT_BASE_MASK = ~((1 << VIRT_SIZE_LOG2) - 1)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct B : Bitfield<2, 1> { }; /* Part of the memory region attributes */
|
||||||
|
struct C : Bitfield<3, 1> { }; /* Part of the memory region attributes */
|
||||||
|
struct Xn : Bitfield<4, 1> { }; /* Execute never bit */
|
||||||
|
struct Pa_39_36 : Bitfield<5, 4> { }; /* Extendend physical address bits [39:36] */
|
||||||
|
struct Ap_1_0 : Bitfield<10, 2>, /* Access permission bits [1:0] */
|
||||||
|
Ap_1_0_bitfield { };
|
||||||
|
struct Tex : Bitfield<12, 3> { }; /* Part of the memory region attributes */
|
||||||
|
struct Ap_2 : Bitfield<15, 1>, /* Access permission bits [2] */
|
||||||
|
Ap_2_bitfield { };
|
||||||
|
struct S : Bitfield<16, 1> { }; /* Shareable bit */
|
||||||
|
struct Ng : Bitfield<17, 1> { }; /* Not global bit */
|
||||||
|
struct Ns : Bitfield<19, 1> { }; /* Non-secure bit */
|
||||||
|
struct Pa_35_32 : Bitfield<20, 4> { }; /* Extendend physical address bits [35:32] */
|
||||||
|
struct Pa_31_24 : Bitfield<24, 8> { }; /* Physical address bits [31:24] */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Section-descriptor structure
|
||||||
|
*/
|
||||||
|
struct Section : Descriptor
|
||||||
|
{
|
||||||
|
enum {
|
||||||
|
VIRT_SIZE_LOG2 = _1MB_LOG2,
|
||||||
|
VIRT_SIZE = 1 << VIRT_SIZE_LOG2,
|
||||||
|
VIRT_BASE_MASK = ~((1 << VIRT_SIZE_LOG2) - 1)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct B : Bitfield<2, 1> { }; /* Part of the memory region attributes */
|
||||||
|
struct C : Bitfield<3, 1> { }; /* Part of the memory region attributes */
|
||||||
|
struct Xn : Bitfield<4, 1> { }; /* Execute never bit */
|
||||||
|
struct Domain : Bitfield<5, 4> { }; /* Domain field */
|
||||||
|
struct Ap_1_0 : Bitfield<10, 2>, /* Access permission bits [1:0] */
|
||||||
|
Ap_1_0_bitfield { };
|
||||||
|
struct Tex : Bitfield<12, 3> { }; /* Part of the memory region attributes */
|
||||||
|
struct Ap_2 : Bitfield<15, 1>, /* Access permission bits [2] */
|
||||||
|
Ap_2_bitfield { };
|
||||||
|
struct S : Bitfield<16, 1> { }; /* Shareable bit */
|
||||||
|
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 */
|
||||||
|
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 */
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
int _index_by_vo(unsigned long & i, addr_t const vo) const
|
||||||
|
{
|
||||||
|
if (vo > max_virt_offset()) return -1;
|
||||||
|
i = vo >> Section::VIRT_SIZE_LOG2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for a table that adopts current secure mode status
|
||||||
|
*/
|
||||||
|
Section_table() : _secure(Cortex_a9::secure_mode_active())
|
||||||
|
{
|
||||||
|
/* Check table alignment */
|
||||||
|
if (!aligned((addr_t)this, ALIGNM_LOG2)
|
||||||
|
|| (addr_t)this != (addr_t)_entries)
|
||||||
|
{
|
||||||
|
PDBG("Insufficient table alignment");
|
||||||
|
while (1) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start with an empty table */
|
||||||
|
for (unsigned i = 0; i <= MAX_INDEX; i++)
|
||||||
|
Descriptor::invalidate(_entries[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum virtual offset that can be translated by this table
|
||||||
|
*/
|
||||||
|
static addr_t max_virt_offset()
|
||||||
|
{
|
||||||
|
return (MAX_INDEX << Section::VIRT_SIZE_LOG2)
|
||||||
|
+ (Section::VIRT_SIZE - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* \detail 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)
|
||||||
|
{
|
||||||
|
/* Validate virtual address */
|
||||||
|
unsigned long i;
|
||||||
|
if (_index_by_vo (i, vo)) {
|
||||||
|
PDBG("Invalid virtual offset");
|
||||||
|
while (1) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select descriptor type by translation size */
|
||||||
|
if (size_log2 < Section::VIRT_SIZE_LOG2)
|
||||||
|
{
|
||||||
|
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? */
|
||||||
|
if (Descriptor::valid(_entries[i])) {
|
||||||
|
PDBG ("Couldn't override entry");
|
||||||
|
while (1) ;
|
||||||
|
}
|
||||||
|
/* Create and link page table,
|
||||||
|
* the page table checks alignment by itself */
|
||||||
|
pt = new (extra_space) Page_table();
|
||||||
|
_entries[i] = Page_table_descriptor::Ns::bits(!_secure)
|
||||||
|
| Page_table_descriptor::Pa_31_10::masked((addr_t)pt);
|
||||||
|
Descriptor::type(_entries[i], Descriptor::PAGE_TABLE);
|
||||||
|
}
|
||||||
|
/* Request additional memory to create a page table */
|
||||||
|
else return Page_table::SIZE_LOG2;
|
||||||
|
|
||||||
|
/* Insert translation */
|
||||||
|
pt->insert_translation(vo - Section::Pa_31_20::masked(vo),
|
||||||
|
pa, size_log2, r, w, x, global);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (size_log2 == Section::VIRT_SIZE_LOG2)
|
||||||
|
{
|
||||||
|
/* Can we write to the targeted entry? */
|
||||||
|
if (Descriptor::valid(_entries[i]) &&
|
||||||
|
Descriptor::type(_entries[i]) != Descriptor::SECTION)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
PDBG("Translation size not supported");
|
||||||
|
while (1) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
void remove_region (addr_t const vo, size_t const size)
|
||||||
|
{
|
||||||
|
/* Traverse all possibly affected entries */
|
||||||
|
addr_t residual_vo = vo;
|
||||||
|
unsigned long i;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
/* Is anything left over to remove? */
|
||||||
|
if (residual_vo >= vo + size) return;
|
||||||
|
|
||||||
|
/* Does the residual region overlap with the region
|
||||||
|
* represented by this table? */
|
||||||
|
if (_index_by_vo(i, residual_vo)) return;
|
||||||
|
|
||||||
|
/* Update current entry and recalculate the residual region */
|
||||||
|
switch (Descriptor::type(_entries[i]))
|
||||||
|
{
|
||||||
|
case Descriptor::FAULT:
|
||||||
|
{
|
||||||
|
residual_vo = (residual_vo & Fault::VIRT_BASE_MASK)
|
||||||
|
+ Fault::VIRT_SIZE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Descriptor::PAGE_TABLE:
|
||||||
|
{
|
||||||
|
/* Instruct page table to remove residual region */
|
||||||
|
Page_table * const pt = (Page_table *)
|
||||||
|
(addr_t)Page_table_descriptor::Pa_31_10::masked(_entries[i]);
|
||||||
|
size_t const residual_size = vo + size - residual_vo;
|
||||||
|
addr_t const pt_vo = residual_vo
|
||||||
|
- Section::Pa_31_20::masked(residual_vo);
|
||||||
|
pt->remove_region(pt_vo, residual_size);
|
||||||
|
|
||||||
|
/* Recalculate residual region */
|
||||||
|
residual_vo = (residual_vo & Page_table::VIRT_BASE_MASK)
|
||||||
|
+ Page_table::VIRT_SIZE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Descriptor::SECTION:
|
||||||
|
{
|
||||||
|
Descriptor::invalidate(_entries[i]);
|
||||||
|
residual_vo = (residual_vo & Section::VIRT_BASE_MASK)
|
||||||
|
+ Section::VIRT_SIZE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Descriptor::SUPERSECTION:
|
||||||
|
{
|
||||||
|
PDBG("Removal of supersections not implemented");
|
||||||
|
while (1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
Page_table * const pt = (Page_table *)
|
||||||
|
(addr_t)Page_table_descriptor::Pa_31_10::masked(_entries[i]);
|
||||||
|
if (pt->empty())
|
||||||
|
{
|
||||||
|
/* We've found an useless page table */
|
||||||
|
Descriptor::invalidate(_entries[i]);
|
||||||
|
base = (void *)pt;
|
||||||
|
s = sizeof(Page_table);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} __attribute__((aligned(1<<Section_table::ALIGNM_LOG2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _BASE__INCLUDE__DRIVERS__CPU__CORTEX_A9__SECTION_TABLE_H_ */
|
||||||
|
|
112
base/include/drivers/cpu/cortex_a9/timer.h
Normal file
112
base/include/drivers/cpu/cortex_a9/timer.h
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* \brief Driver base for the private timer of the ARM Cortex-A9
|
||||||
|
* \author Martin stein
|
||||||
|
* \date 2011-12-13
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2012 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 _BASE__INCLUDE__DRIVERS__CPU__CORTEX_A9__TIMER_H_
|
||||||
|
#define _BASE__INCLUDE__DRIVERS__CPU__CORTEX_A9__TIMER_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <util/mmio.h>
|
||||||
|
|
||||||
|
namespace Genode
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Driver base for the private timer of the ARM Cortex-A9
|
||||||
|
*/
|
||||||
|
template <unsigned long CLK>
|
||||||
|
struct Cortex_a9_timer : public Mmio
|
||||||
|
{
|
||||||
|
enum { TICS_PER_MS = CLK / 1000, };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load value rgeister
|
||||||
|
*/
|
||||||
|
struct Load : Register<0x0, 32> { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timer counter value register
|
||||||
|
*/
|
||||||
|
struct Counter : Register<0x4, 32> { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timer control register
|
||||||
|
*/
|
||||||
|
struct Control : Register<0x8, 32>
|
||||||
|
{
|
||||||
|
struct Timer_enable : Bitfield<0,1> { }; /* 1: 'Counter' decrements, 0: 'Counter' stays 0 */
|
||||||
|
struct Auto_reload : Bitfield<1,1> { }; /* 1: Auto reload mode, 0: One shot mode */
|
||||||
|
struct Irq_enable : Bitfield<2,1> { }; /* 1: IRQ = 'Interrupt_status::Event' 0: IRQ = 0 */
|
||||||
|
struct Prescaler : Bitfield<8,8> { }; /* modifies the clock period for the decrementing */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timer interrupt status register
|
||||||
|
*/
|
||||||
|
struct Interrupt_status : Register<0xc, 32>
|
||||||
|
{
|
||||||
|
struct Event : Bitfield<0,1> { }; /* 'Event' = !'Counter' */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor, clears the interrupt output
|
||||||
|
*/
|
||||||
|
Cortex_a9_timer(addr_t const mmio_base) : Mmio(mmio_base) {
|
||||||
|
clear_interrupt(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a one-shot run
|
||||||
|
* \param tics native timer value used to assess the delay
|
||||||
|
* of the timer interrupt as of the call
|
||||||
|
*/
|
||||||
|
inline void start_one_shot(uint32_t const tics);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate milliseconds to a native timer value
|
||||||
|
*/
|
||||||
|
static uint32_t ms_to_tics(unsigned long const ms) {
|
||||||
|
return ms * TICS_PER_MS; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the timer and return last timer value
|
||||||
|
*/
|
||||||
|
unsigned long stop()
|
||||||
|
{
|
||||||
|
unsigned long const v = read<Counter>();
|
||||||
|
write<typename Control::Timer_enable>(0);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear interrupt output line
|
||||||
|
*/
|
||||||
|
void clear_interrupt() { write<typename Interrupt_status::Event>(1); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <unsigned long CLOCK>
|
||||||
|
void Genode::Cortex_a9_timer<CLOCK>::start_one_shot(uint32_t const tics)
|
||||||
|
{
|
||||||
|
/* Reset timer */
|
||||||
|
clear_interrupt();
|
||||||
|
write<Control>(Control::Timer_enable::bits(0) |
|
||||||
|
Control::Auto_reload::bits(0) |
|
||||||
|
Control::Irq_enable::bits(1) |
|
||||||
|
Control::Prescaler::bits(0));
|
||||||
|
|
||||||
|
/* Load timer and start decrementing */
|
||||||
|
write<Load>(tics);
|
||||||
|
write<typename Control::Timer_enable>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _BASE__INCLUDE__DRIVERS__CPU__CORTEX_A9__TIMER_H_ */
|
Loading…
Reference in New Issue
Block a user