2012-10-23 17:12:09 +02:00
|
|
|
/*
|
2014-04-09 12:14:38 +02:00
|
|
|
* \brief Processor driver for core
|
2012-10-23 17:12:09 +02:00
|
|
|
* \author Norman Feske
|
2012-12-03 17:21:35 +01:00
|
|
|
* \author Martin stein
|
2012-10-23 17:12:09 +02:00
|
|
|
* \date 2012-08-30
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2013-01-10 21:44:47 +01:00
|
|
|
* Copyright (C) 2012-2013 Genode Labs GmbH
|
2012-10-23 17:12:09 +02:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
|
|
* under the terms of the GNU General Public License version 2.
|
|
|
|
*/
|
|
|
|
|
2014-07-09 12:03:17 +02:00
|
|
|
#ifndef _PROCESSOR_DRIVER_H_
|
|
|
|
#define _PROCESSOR_DRIVER_H_
|
2012-10-23 17:12:09 +02:00
|
|
|
|
|
|
|
/* core includes */
|
2014-07-09 12:03:17 +02:00
|
|
|
#include <spec/arm/processor_driver_support.h>
|
|
|
|
#include <assert.h>
|
2012-12-10 13:55:19 +01:00
|
|
|
#include <board.h>
|
2012-10-23 17:12:09 +02:00
|
|
|
|
2014-07-09 12:03:17 +02:00
|
|
|
namespace Genode
|
2012-10-23 17:12:09 +02:00
|
|
|
{
|
|
|
|
/**
|
2014-04-09 12:14:38 +02:00
|
|
|
* Part of processor state that is not switched on every mode transition
|
|
|
|
*/
|
|
|
|
class Processor_lazy_state { };
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Processor driver for core
|
2012-10-23 17:12:09 +02:00
|
|
|
*/
|
2014-07-09 12:03:17 +02:00
|
|
|
class Processor_driver;
|
|
|
|
}
|
|
|
|
|
|
|
|
class Genode::Processor_driver : public Arm
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
2012-10-23 17:12:09 +02:00
|
|
|
/**
|
|
|
|
* Cache type register
|
|
|
|
*/
|
2014-07-09 12:03:17 +02:00
|
|
|
struct Ctr : Arm::Ctr
|
2012-10-23 17:12:09 +02:00
|
|
|
{
|
|
|
|
struct P : Bitfield<23, 1> { }; /* page mapping restriction on */
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* System control register
|
|
|
|
*/
|
2014-07-09 12:03:17 +02:00
|
|
|
struct Sctlr : Arm::Sctlr
|
2012-10-23 17:12:09 +02:00
|
|
|
{
|
|
|
|
struct W : Bitfield<3,1> { }; /* enable write buffer */
|
|
|
|
|
|
|
|
struct Unused_0 : Bitfield<4,3> { }; /* shall be ones */
|
|
|
|
|
|
|
|
struct B : Bitfield<7,1> /* Memory system endianess */
|
|
|
|
{
|
|
|
|
enum { LITTLE = 0 };
|
|
|
|
};
|
|
|
|
|
2013-05-13 21:00:14 +02:00
|
|
|
struct S : Bitfield<8,1> { }; /* enable MMU protection */
|
|
|
|
struct R : Bitfield<9,1> { }; /* enable ROM protection */
|
2012-10-23 17:12:09 +02:00
|
|
|
struct L4 : Bitfield<15,1> { }; /* raise T bit on LOAD-to-PC */
|
|
|
|
struct Dt : Bitfield<16,1> { }; /* global data TCM enable */
|
|
|
|
struct It : Bitfield<18,1> { }; /* global instruction TCM enable */
|
|
|
|
struct U : Bitfield<22,1> { }; /* enable unaligned data access */
|
|
|
|
struct Xp : Bitfield<23,1> { }; /* disable subpage AP bits */
|
|
|
|
|
|
|
|
struct Unused_1 : Bitfield<26,6> { }; /* shall not be modified */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get static base value for writes
|
|
|
|
*/
|
|
|
|
static access_t base_value() {
|
|
|
|
return Unused_0::reg_mask() | Unused_1::masked(read()); }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Value for the switch to virtual mode in kernel
|
|
|
|
*/
|
|
|
|
static access_t init_virt_kernel()
|
|
|
|
{
|
|
|
|
return base_value() |
|
2014-07-09 12:03:17 +02:00
|
|
|
Arm::Sctlr::init_virt_kernel() |
|
2012-10-23 17:12:09 +02:00
|
|
|
W::bits(0) |
|
|
|
|
B::bits(B::LITTLE) |
|
|
|
|
S::bits(0) |
|
|
|
|
R::bits(0) |
|
|
|
|
L4::bits(0) |
|
|
|
|
Dt::bits(0) |
|
|
|
|
It::bits(0) |
|
|
|
|
U::bits(0) |
|
|
|
|
Xp::bits(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Value for the initial kernel entry
|
|
|
|
*/
|
|
|
|
static access_t init_phys_kernel()
|
|
|
|
{
|
|
|
|
return base_value() |
|
2014-07-09 12:03:17 +02:00
|
|
|
Arm::Sctlr::init_phys_kernel() |
|
2012-10-23 17:12:09 +02:00
|
|
|
W::bits(0) |
|
|
|
|
B::bits(B::LITTLE) |
|
|
|
|
S::bits(0) |
|
|
|
|
R::bits(0) |
|
|
|
|
L4::bits(0) |
|
|
|
|
Dt::bits(1) |
|
|
|
|
It::bits(1) |
|
|
|
|
U::bits(0) |
|
|
|
|
Xp::bits(1);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Translation table base control register 0
|
|
|
|
*/
|
2014-07-09 12:03:17 +02:00
|
|
|
struct Ttbr0 : Arm::Ttbr0
|
2012-10-23 17:12:09 +02:00
|
|
|
{
|
2012-12-18 11:32:48 +01:00
|
|
|
struct C : Bitfield<0,1> /* inner cachable mode */
|
|
|
|
{
|
|
|
|
enum { NON_CACHEABLE = 0 };
|
|
|
|
};
|
|
|
|
|
2012-10-23 17:12:09 +02:00
|
|
|
struct P : Bitfield<2,1> { }; /* memory controller ECC enabled */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Value for the switch to virtual mode in kernel
|
|
|
|
*
|
|
|
|
* \param section_table initial section table
|
|
|
|
*/
|
|
|
|
static access_t init_virt_kernel(addr_t const sect_table)
|
|
|
|
{
|
2014-07-09 12:03:17 +02:00
|
|
|
return Arm::Ttbr0::init_virt_kernel(sect_table) |
|
2012-12-18 11:32:48 +01:00
|
|
|
P::bits(0) |
|
|
|
|
C::bits(C::NON_CACHEABLE);
|
2012-10-23 17:12:09 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If page descriptor bits [13:12] are restricted
|
|
|
|
*/
|
|
|
|
static bool restricted_page_mappings() {
|
|
|
|
return Ctr::P::get(Ctr::read()); }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Configure this module appropriately for the first kernel run
|
|
|
|
*/
|
|
|
|
static void init_phys_kernel()
|
|
|
|
{
|
2012-12-11 14:45:18 +01:00
|
|
|
Board::prepare_kernel();
|
2012-10-23 17:12:09 +02:00
|
|
|
Sctlr::write(Sctlr::init_phys_kernel());
|
|
|
|
flush_tlb();
|
|
|
|
|
|
|
|
/* check for mapping restrictions */
|
2014-07-09 12:03:17 +02:00
|
|
|
assert(!restricted_page_mappings());
|
2012-10-23 17:12:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Switch to the virtual mode in kernel
|
|
|
|
*
|
|
|
|
* \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 init_virt_kernel(addr_t const section_table,
|
2012-12-19 14:46:48 +01:00
|
|
|
unsigned const process_id)
|
2012-10-23 17:12:09 +02:00
|
|
|
{
|
|
|
|
Cidr::write(process_id);
|
|
|
|
Dacr::write(Dacr::init_virt_kernel());
|
|
|
|
Ttbr0::write(Ttbr0::init_virt_kernel(section_table));
|
|
|
|
Ttbcr::write(Ttbcr::init_virt_kernel());
|
|
|
|
Sctlr::write(Sctlr::init_virt_kernel());
|
|
|
|
}
|
2012-12-18 11:32:48 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Ensure that TLB insertions get applied
|
|
|
|
*/
|
|
|
|
static void tlb_insertions() { flush_tlb(); }
|
2013-12-17 18:10:02 +01:00
|
|
|
|
|
|
|
static void start_secondary_processors(void * const ip)
|
|
|
|
{
|
2014-07-09 12:03:17 +02:00
|
|
|
assert(!is_smp());
|
2013-12-17 18:10:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invalidate all predictions about the future control-flow
|
|
|
|
*/
|
|
|
|
static void invalidate_control_flow_predictions()
|
|
|
|
{
|
|
|
|
/* FIXME invalidation of branch prediction not implemented */
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Finish all previous data transfers
|
|
|
|
*/
|
|
|
|
static void data_synchronization_barrier()
|
|
|
|
{
|
|
|
|
/* FIXME data synchronization barrier not implemented */
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wait for the next interrupt as cheap as possible
|
|
|
|
*/
|
|
|
|
static void wait_for_interrupt()
|
|
|
|
{
|
|
|
|
/* FIXME cheap way of waiting is not implemented */
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return kernel name of the primary processor
|
|
|
|
*/
|
|
|
|
static unsigned primary_id() { return 0; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return kernel name of the executing processor
|
|
|
|
*/
|
2014-03-06 13:55:56 +01:00
|
|
|
static unsigned executing_id() { return primary_id(); }
|
2014-04-09 12:14:38 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prepare for the proceeding of a user
|
|
|
|
*/
|
|
|
|
static void prepare_proceeding(Processor_lazy_state *,
|
|
|
|
Processor_lazy_state *) { }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return wether to retry an undefined user instruction after this call
|
|
|
|
*/
|
|
|
|
bool retry_undefined_instr(Processor_lazy_state *) { return false; }
|
2014-07-04 11:47:41 +02:00
|
|
|
|
|
|
|
/**
|
2014-07-09 12:03:17 +02:00
|
|
|
* Post processing after a translation was added to a translation table
|
|
|
|
*
|
|
|
|
* \param addr virtual address of the translation
|
|
|
|
* \param size size of the translation
|
2014-07-04 11:47:41 +02:00
|
|
|
*/
|
2014-07-09 12:03:17 +02:00
|
|
|
static void translation_added(addr_t const addr, size_t const size)
|
2014-07-04 11:47:41 +02:00
|
|
|
{
|
|
|
|
/*
|
2014-07-09 12:03:17 +02:00
|
|
|
* The Cortex A8 processor can't use the L1 cache on page-table
|
|
|
|
* walks. Therefore, as the page-tables lie in write-back cacheable
|
|
|
|
* memory we've to clean the corresponding cache-lines even when a
|
|
|
|
* page table entry is added. We only do this as core as the kernel
|
|
|
|
* adds translations solely before MMU and caches are enabled.
|
2014-07-04 11:47:41 +02:00
|
|
|
*/
|
|
|
|
if (is_user()) Kernel::update_data_region(addr, size);
|
|
|
|
}
|
2014-07-09 12:03:17 +02:00
|
|
|
};
|
2012-10-23 17:12:09 +02:00
|
|
|
|
|
|
|
|
2014-07-09 12:03:17 +02:00
|
|
|
void Genode::Arm::flush_data_caches()
|
2013-12-17 18:10:02 +01:00
|
|
|
{
|
|
|
|
asm volatile ("mcr p15, 0, %[rd], c7, c14, 0" :: [rd]"r"(0) : );
|
|
|
|
}
|
2012-10-23 17:12:09 +02:00
|
|
|
|
|
|
|
|
2014-07-09 12:03:17 +02:00
|
|
|
void Genode::Arm::invalidate_data_caches()
|
2014-04-03 18:22:54 +02:00
|
|
|
{
|
|
|
|
asm volatile ("mcr p15, 0, %[rd], c7, c6, 0" :: [rd]"r"(0) : );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-09 12:03:17 +02:00
|
|
|
#endif /* _PROCESSOR_DRIVER_H_ */
|