@ -1,28 +1,313 @@
/*
* \ brief Functions not offered by Genode ' s DDE - kit
* \ brief DDE iPXE wrappers to C + + backend
* \ author Sebastian Sumpf
* \ author Josef Soentgen
* \ date 2010 - 10 - 21
*/
/*
* Copyright ( C ) 2010 - 201 3 Genode Labs GmbH
* Copyright ( C ) 2010 - 201 5 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.
*/
/*
* The build system picks up stdarg . h from iPXE instead of the one
* provided by GCC . This header contains the FILE_LICENCE macro which
* is defined by iPXE ' s compiler . h .
*/
# define FILE_LICENCE(x)
/* Genode includes */
# include <base/allocator_avl.h>
# include <base/env.h>
# include <base/printf.h>
# include <base/slab.h>
# include <base/sleep.h>
# include <dataspace/client.h>
# include <io_mem_session/connection.h>
# include <io_port_session/connection.h>
# include <irq_session/connection.h>
# include <os/server.h>
# include <pci_device/client.h>
# include <pci_session/connection.h>
# include <rm_session/connection.h>
# include <timer_session/connection.h>
# include <util/misc_math.h>
/* local includes */
# include <dde_support.h>
//using namespace Genode;
/****************
* * Migriation * *
* * * * * * * * * * * * * * * */
static Server : : Entrypoint * _ep ;
extern " C " void dde_init ( void * ep )
{
_ep = ( Server : : Entrypoint * ) ep ;
}
/************
* * printf * *
* * * * * * * * * * * */
extern " C " void dde_vprintf ( const char * fmt , va_list va ) {
Genode : : vprintf ( fmt , va ) ; }
extern " C " void dde_printf ( const char * fmt , . . . )
{
va_list args ;
va_start ( args , fmt ) ;
dde_vprintf ( fmt , args ) ;
va_end ( args ) ;
}
/***********
* * Timer * *
* * * * * * * * * * */
extern " C " void dde_udelay ( unsigned long usecs )
{
/*
* This function is called only once during rdtsc calibration ( usecs will be
* 10000 , see dde . c ' udelay ' . We do not use DDE timers here , since Genode ' s
* timer connection is the most precise one around .
*/
Timer : : Connection timer ;
timer . usleep ( usecs ) ;
}
/***************************
* * locking / synchronizing * *
* * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
* DDE iPXE mutual exclusion lock
*/
static Genode : : Lock _ipxe_lock ;
extern " C " void dde_lock_enter ( void ) { _ipxe_lock . lock ( ) ; }
extern " C " void dde_lock_leave ( void ) { _ipxe_lock . unlock ( ) ; }
extern " C " void dde_mdelay ( unsigned long msecs )
{
/*
* Using one static timer connection here is safe because
* this function is only called while initializing the device
* and only be the same thread .
*/
static Timer : : Connection timer ;
timer . msleep ( msecs ) ;
}
/******************
* * PCI handling * *
* * * * * * * * * * * * * * * * * */
struct Pci_driver
{
enum {
PCI_BASE_CLASS_NETWORK = 0x02 ,
CLASS_MASK = 0xff0000 ,
CLASS_NETWORK = PCI_BASE_CLASS_NETWORK < < 16
} ;
Pci : : Connection _pci ;
Pci : : Device_capability _cap ;
Pci : : Device_capability _last_cap ;
struct Region
{
Genode : : addr_t base ;
Genode : : addr_t mapped_base ;
} _region ;
template < typename T >
Pci : : Device : : Access_size _access_size ( T t )
{
switch ( sizeof ( T ) ) {
case 1 :
return Pci : : Device : : ACCESS_8BIT ;
case 2 :
return Pci : : Device : : ACCESS_16BIT ;
default :
return Pci : : Device : : ACCESS_32BIT ;
}
}
void _bus_address ( int * bus , int * dev , int * fun )
{
Pci : : Device_client client ( _cap ) ;
unsigned char b , d , f ;
client . bus_address ( & b , & d , & f ) ;
* bus = b ;
* dev = d ;
* fun = f ;
}
Pci_driver ( ) { }
template < typename T >
void config_read ( unsigned int devfn , T * val )
{
Pci : : Device_client client ( _cap ) ;
* val = client . config_read ( devfn , _access_size ( * val ) ) ;
}
template < typename T >
void config_write ( unsigned int devfn , T val )
{
Pci : : Device_client client ( _cap ) ;
client . config_write ( devfn , val , _access_size ( val ) ) ;
}
int first_device ( int * bus , int * dev , int * fun )
{
_cap = _pci . first_device ( CLASS_NETWORK , CLASS_MASK ) ;
_bus_address ( bus , dev , fun ) ;
return 0 ;
}
int next_device ( int * bus , int * dev , int * fun )
{
_last_cap = _cap ;
_cap = _pci . next_device ( _cap , CLASS_NETWORK , CLASS_MASK ) ;
_bus_address ( bus , dev , fun ) ;
if ( _last_cap . valid ( ) )
_pci . release_device ( _last_cap ) ;
return 0 ;
}
Genode : : addr_t alloc_dma_memory ( Genode : : size_t size )
{
try {
using namespace Genode ;
/* trigger that the device gets assigned to this driver */
_pci . config_extended ( _cap ) ;
Ram_dataspace_capability ram_cap ;
ram_cap = _pci . alloc_dma_buffer ( _cap , size ) ;
_region . mapped_base = ( Genode : : addr_t ) env ( ) - > rm_session ( ) - > attach ( ram_cap ) ;
_region . base = Dataspace_client ( ram_cap ) . phys_addr ( ) ;
return _region . mapped_base ;
} catch ( . . . ) { return 0 ; }
}
Genode : : addr_t virt_to_phys ( Genode : : addr_t virt ) {
return virt - _region . mapped_base + _region . base ; }
} ;
static Pci_driver & pci_drv ( )
{
static Pci_driver _pci_drv ;
return _pci_drv ;
}
extern " C " int dde_pci_first_device ( int * bus , int * dev , int * fun ) {
return pci_drv ( ) . first_device ( bus , dev , fun ) ; }
extern " C " int dde_pci_next_device ( int * bus , int * dev , int * fun ) {
return pci_drv ( ) . next_device ( bus , dev , fun ) ; }
extern " C " void dde_pci_readb ( int pos , dde_uint8_t * val ) {
pci_drv ( ) . config_read ( pos , val ) ; }
extern " C " void dde_pci_readw ( int pos , dde_uint16_t * val ) {
pci_drv ( ) . config_read ( pos , val ) ; }
extern " C " void dde_pci_readl ( int pos , dde_uint32_t * val ) {
pci_drv ( ) . config_read ( pos , val ) ; }
extern " C " void dde_pci_writeb ( int pos , dde_uint8_t val ) {
pci_drv ( ) . config_write ( pos , val ) ; }
extern " C " void dde_pci_writew ( int pos , dde_uint16_t val ) {
pci_drv ( ) . config_write ( pos , val ) ; }
extern " C " void dde_pci_writel ( int pos , dde_uint32_t val ) {
pci_drv ( ) . config_write ( pos , val ) ; }
/************************
* * Interrupt handling * *
* * * * * * * * * * * * * * * * * * * * * * * */
struct Irq_handler
{
Server : : Entrypoint & ep ;
Genode : : Irq_connection irq ;
Genode : : Signal_rpc_member < Irq_handler > dispatcher ;
typedef void ( * irq_handler ) ( void * ) ;
irq_handler handler ;
void * priv ;
void handle ( unsigned )
{
handler ( priv ) ;
irq . ack_irq ( ) ;
}
Irq_handler ( Server : : Entrypoint & ep , int irqnr ,
irq_handler handler , void * priv )
:
ep ( ep ) , irq ( irqnr ) , dispatcher ( ep , * this , & Irq_handler : : handle ) ,
handler ( handler ) , priv ( priv )
{
irq . sigh ( dispatcher ) ;
/* intial ack so that we will receive IRQ signals */
irq . ack_irq ( ) ;
}
} ;
static Irq_handler * _irq_handler ;
extern " C " int dde_interrupt_attach ( int irq , void ( * handler ) ( void * ) , void * priv )
{
if ( _irq_handler ) {
PERR ( " Irq_handler already registered " ) ;
Genode : : sleep_forever ( ) ;
}
try {
_irq_handler = new ( Genode : : env ( ) - > heap ( ) )
Irq_handler ( * _ep , irq , handler , priv ) ;
} catch ( . . . ) { return - 1 ; }
extern " C " {
# include <dde_kit/pci.h>
# include "dde_support.h"
return 0 ;
}
using namespace Genode ;
/***************************************************
* * Support for aligned and DMA memory allocation * *
@ -31,56 +316,320 @@ using namespace Genode;
enum { BACKING_STORE_SIZE = 1024 * 1024 } ;
static Allocator_avl& allocator ( )
static Genode: : Allocator_avl& allocator ( )
{
static Allocator_avl _avl ( env ( ) - > heap ( ) ) ;
static Genode: : Allocator_avl _avl ( Genode : : env ( ) - > heap ( ) ) ;
return _avl ;
}
extern " C " int dde_mem_init ( int bus , int dev , int func )
extern " C " int dde_dma_mem_init ( )
{
try {
addr_t base = dde_kit_pci_alloc_dma_buffer ( bus , dev , func ,
BACKING_STORE_SIZE ) ;
Genode : : addr_t base = pci_drv ( ) . alloc_dma_memory ( BACKING_STORE_SIZE ) ;
/* add to allocator */
allocator ( ) . add_range ( base , BACKING_STORE_SIZE ) ;
} catch ( . . . ) {
return false ;
}
} catch ( . . . ) { return false ; }
return true ;
}
extern " C " void * dde_alloc_memblock ( dde_kit_size_t size , dde_kit_size_t align ,
dde_kit_size_t offset )
extern " C " void * dde_dma_alloc ( dde_size_t size , dde_size_t align ,
dde_size_t offset )
{
void * ptr ;
if ( allocator ( ) . alloc_aligned ( size , & ptr , log2( align ) ) . is_error ( ) ) {
PERR ( " memory allocation failed in alloc_memblock (size=%z d , align=%zx,"
" offset=%zx) " , size , align , offset ) ;
if ( allocator ( ) . alloc_aligned ( size , & ptr , Genode: : log2( align ) ) . is_error ( ) ) {
PERR ( " memory allocation failed in alloc_memblock (size=%z u , align=%zx,"
" offset=%zx) " , ( Genode : : size_t ) size , ( Genode : : size_t ) align , ( Genode : : size_t ) offset ) ;
return 0 ;
}
return ptr ;
}
extern " C " void dde_free_memblock ( void * p , dde_kit_size_t size )
extern " C " void dde_dma_free ( void * p , dde_size_t size ) {
allocator ( ) . free ( p , size ) ; }
extern " C " dde_addr_t dde_dma_get_physaddr ( void * virt ) {
return pci_drv ( ) . virt_to_phys ( ( Genode : : addr_t ) virt ) ; }
/**************
* * I / O port * *
* * * * * * * * * * * * * */
static Genode : : Io_port_connection * _io_port ;
extern " C " void dde_request_io ( dde_addr_t base , dde_size_t size )
{
allocator ( ) . free ( p , size ) ;
using namespace Genode ;
if ( _io_port ) {
PERR ( " Io_port_connection already open " ) ;
sleep_forever ( ) ;
}
_io_port = new ( env ( ) - > heap ( ) ) Io_port_connection ( base , size ) ;
}
/***********
* * Timer * *
* * * * * * * * * * */
extern " C " dde_uint8_t dde_inb ( dde_addr_t port ) {
return _io_port - > inb ( port ) ; }
extern " C " dde_uint16_t dde_inw ( dde_addr_t port ) {
return _io_port - > inw ( port ) ; }
extern " C " dde_uint32_t dde_inl ( dde_addr_t port ) {
return _io_port - > inl ( port ) ; }
extern " C " void dde_outb ( dde_addr_t port , dde_uint8_t data ) {
_io_port - > outb ( port , data ) ; }
extern " C " void dde_outw ( dde_addr_t port , dde_uint16_t data ) {
_io_port - > outw ( port , data ) ; }
extern " C " void dde_timer2_udelay ( unsigned long usecs )
extern " C " void dde_outl ( dde_addr_t port , dde_uint32_t data ) {
_io_port - > outl ( port , data ) ; }
/**********************
* * Slab memory pool * *
* * * * * * * * * * * * * * * * * * * * * */
struct Slab_backend_alloc : public Genode : : Allocator ,
public Genode : : Rm_connection
{
enum {
VM_SIZE = 512 * 1024 ,
BLOCK_SIZE = 64 * 1024 ,
ELEMENTS = VM_SIZE / BLOCK_SIZE ,
} ;
Genode : : addr_t _base ;
Genode : : Ram_dataspace_capability _ds_cap [ ELEMENTS ] ;
int _index ;
Genode : : Allocator_avl _range ;
Genode : : Ram_session & _ram ;
bool _alloc_block ( )
{
using namespace Genode ;
if ( _index = = ELEMENTS ) {
PERR ( " Slab-backend exhausted! " ) ;
return false ;
}
try {
_ds_cap [ _index ] = _ram . alloc ( BLOCK_SIZE ) ;
Rm_connection : : attach_at ( _ds_cap [ _index ] , _index * BLOCK_SIZE , BLOCK_SIZE , 0 ) ;
} catch ( . . . ) { return false ; }
/* return base + offset in VM area */
Genode : : addr_t block_base = _base + ( _index * BLOCK_SIZE ) ;
+ + _index ;
_range . add_range ( block_base , BLOCK_SIZE ) ;
return true ;
}
Slab_backend_alloc ( Genode : : Ram_session & ram )
:
Rm_connection ( 0 , VM_SIZE ) ,
_index ( 0 ) , _range ( Genode : : env ( ) - > heap ( ) ) , _ram ( ram )
{
/* reserver attach us, anywere */
_base = Genode : : env ( ) - > rm_session ( ) - > attach ( dataspace ( ) ) ;
}
Genode : : addr_t start ( ) const { return _base ; }
Genode : : addr_t end ( ) const { return _base + VM_SIZE - 1 ; }
/*************************
* * Allocator interface * *
* * * * * * * * * * * * * * * * * * * * * * * * */
bool alloc ( Genode : : size_t size , void * * out_addr )
{
bool done = _range . alloc ( size , out_addr ) ;
if ( done )
return done ;
done = _alloc_block ( ) ;
if ( ! done ) {
PERR ( " Backend allocator exhausted \n " ) ;
return false ;
}
return _range . alloc ( size , out_addr ) ;
}
void free ( void * addr , Genode : : size_t size ) { }
Genode : : size_t overhead ( Genode : : size_t size ) const { return 0 ; }
bool need_size_for_free ( ) const { return false ; }
} ;
struct Slab_alloc : public Genode : : Slab
{
/*
* This function is called only once during rdtsc calibration ( usecs will be
* 10000 , see dde . c ' udelay ' . We do not use DDE timers here , since Genode ' s
* timer connection is the most precise one around .
* Each slab block in the slab contains about 8 objects ( slab entries )
* as proposed in the paper by Bonwick and block sizes are multiples of
* page size .
*/
static Genode : : size_t _calculate_block_size ( Genode : : size_t object_size )
{
Genode : : size_t block_size = 8 * ( object_size + sizeof ( Genode : : Slab_entry ) )
+ sizeof ( Genode : : Slab_block ) ;
return Genode : : align_addr ( block_size , 12 ) ;
}
Slab_alloc ( Genode : : size_t object_size , Slab_backend_alloc & allocator )
: Slab ( object_size , _calculate_block_size ( object_size ) , 0 , & allocator ) { }
/**
* Convenience slabe - entry allocation
*/
Timer : : Connection timer ;
timer . usleep ( usecs ) ;
Genode : : addr_t alloc ( )
{
Genode : : addr_t result ;
return ( Slab : : alloc ( slab_size ( ) , ( void * * ) & result ) ? result : 0 ) ;
}
} ;
struct Slab
{
enum {
SLAB_START_LOG2 = 5 , /* 32 B */
SLAB_STOP_LOG2 = 10 , /* 1 KiB */
NUM_SLABS = ( SLAB_STOP_LOG2 - SLAB_START_LOG2 ) + 1 ,
} ;
Slab_backend_alloc & _back_alloc ;
Slab_alloc * _allocator [ NUM_SLABS ] ;
Genode : : addr_t _start ;
Genode : : addr_t _end ;
Slab ( Slab_backend_alloc & alloc )
: _back_alloc ( alloc ) , _start ( alloc . start ( ) ) , _end ( alloc . end ( ) )
{
for ( unsigned i = 0 ; i < NUM_SLABS ; i + + )
_allocator [ i ] = new ( Genode : : env ( ) - > heap ( ) )
Slab_alloc ( 1U < < ( SLAB_START_LOG2 + i ) , alloc ) ;
}
void * alloc ( Genode : : size_t size )
{
using namespace Genode ;
size + = sizeof ( Genode : : addr_t ) ;
int msb = Genode : : log2 ( size ) ;
if ( size > ( 1U < < msb ) )
msb + + ;
if ( size < ( 1U < < SLAB_START_LOG2 ) )
msb = SLAB_STOP_LOG2 ;
if ( msb > SLAB_STOP_LOG2 )
return 0 ;
Genode : : addr_t addr = _allocator [ msb - SLAB_START_LOG2 ] - > alloc ( ) ;
if ( ! addr )
return 0 ;
* ( Genode : : addr_t * ) addr = msb - SLAB_START_LOG2 ;
addr + = sizeof ( Genode : : addr_t ) ;
return ( void * ) addr ;
}
void free ( void * p )
{
using namespace Genode ;
Genode : : addr_t * addr = ( ( Genode : : addr_t * ) p ) - 1 ;
unsigned index = * ( unsigned * ) ( addr ) ;
_allocator [ index ] - > free ( ( void * ) ( addr ) ) ;
}
} ;
static : : Slab & slab ( )
{
static : : Slab_backend_alloc sb ( * Genode : : env ( ) - > ram_session ( ) ) ;
static : : Slab s ( sb ) ;
return s ;
}
extern " C " void * dde_slab_alloc ( dde_size_t size ) {
return slab ( ) . alloc ( size ) ; }
extern " C " void dde_slab_free ( void * p ) {
slab ( ) . free ( p ) ; }
/****************
* * I / O memory * *
* * * * * * * * * * * * * * * */
struct Io_memory
{
Genode : : Io_mem_connection _mem ;
Genode : : Io_mem_dataspace_capability _mem_ds ;
Genode : : addr_t _vaddr ;
Io_memory ( Genode : : addr_t base , Genode : : size_t size , bool wc )
:
_mem ( base , size , wc ) ,
_mem_ds ( _mem . dataspace ( ) )
{
if ( ! _mem_ds . valid ( ) )
throw Genode : : Exception ( ) ;
_vaddr = Genode : : env ( ) - > rm_session ( ) - > attach ( _mem_ds ) ;
_vaddr | = base & 0xfff ;
}
Genode : : addr_t vaddr ( ) const { return _vaddr ; }
} ;
static Io_memory * _io_mem ;
extern " C " int dde_request_iomem ( dde_addr_t start , dde_size_t size , int wc ,
dde_addr_t * vaddr )
{
if ( _io_mem ) {
PERR ( " Io_memory already requested " ) ;
Genode : : sleep_forever ( ) ;
}
try {
_io_mem = new ( Genode : : env ( ) - > heap ( ) ) Io_memory ( start , size , ! ! wc ) ;
} catch ( . . . ) { return - 1 ; }
* vaddr = _io_mem - > vaddr ( ) ;
return 0 ;
}
extern " C " int dde_release_iomem ( dde_addr_t start , dde_size_t size )
{
try {
destroy ( Genode : : env ( ) - > heap ( ) , _io_mem ) ;
_io_mem = 0 ;
return 0 ;
} catch ( . . . ) { return - 1 ; }
}