opengnsys_ipxe/src/include/gpxe/tables.h

356 lines
8.8 KiB
C

#ifndef _GPXE_TABLES_H
#define _GPXE_TABLES_H
/** @page ifdef_harmful #ifdef considered harmful
*
* Overuse of @c #ifdef has long been a problem in Etherboot.
* Etherboot provides a rich array of features, but all these features
* take up valuable space in a ROM image. The traditional solution to
* this problem has been for each feature to have its own @c #ifdef
* option, allowing the feature to be compiled in only if desired.
*
* The problem with this is that it becomes impossible to compile, let
* alone test, all possible versions of Etherboot. Code that is not
* typically used tends to suffer from bit-rot over time. It becomes
* extremely difficult to predict which combinations of compile-time
* options will result in code that can even compile and link
* correctly.
*
* To solve this problem, we have adopted a new approach from
* Etherboot 5.5 onwards. @c #ifdef is now "considered harmful", and
* its use should be minimised. Separate features should be
* implemented in separate @c .c files, and should \b always be
* compiled (i.e. they should \b not be guarded with a @c #ifdef @c
* MY_PET_FEATURE statement). By making (almost) all code always
* compile, we avoid the problem of bit-rot in rarely-used code.
*
* The file config.h, in combination with the @c make command line,
* specifies the objects that will be included in any particular build
* of Etherboot. For example, suppose that config.h includes the line
*
* @code
*
* #define CONSOLE_SERIAL
* #define DOWNLOAD_PROTO_TFTP
*
* @endcode
*
* When a particular Etherboot image (e.g. @c bin/rtl8139.zdsk) is
* built, the options specified in config.h are used to drag in the
* relevant objects at link-time. For the above example, serial.o and
* tftp.o would be linked in.
*
* There remains one problem to solve: how do these objects get used?
* Traditionally, we had code such as
*
* @code
*
* #ifdef CONSOLE_SERIAL
* serial_init();
* #endif
*
* @endcode
*
* in main.c, but this reintroduces @c #ifdef and so is a Bad Idea.
* We cannot simply remove the @c #ifdef and make it
*
* @code
*
* serial_init();
*
* @endcode
*
* because then serial.o would end up always being linked in.
*
* The solution is to use @link tables.h linker tables @endlink.
*
*/
/** @file
*
* Linker tables
*
* Read @ref ifdef_harmful first for some background on the motivation
* for using linker tables.
*
* This file provides macros for dealing with linker-generated tables
* of fixed-size symbols. We make fairly extensive use of these in
* order to avoid @c #ifdef spaghetti and/or linker symbol pollution.
* For example, instead of having code such as
*
* @code
*
* #ifdef CONSOLE_SERIAL
* serial_init();
* #endif
*
* @endcode
*
* we make serial.c generate an entry in the initialisation function
* table, and then have a function call_init_fns() that simply calls
* all functions present in this table. If and only if serial.o gets
* linked in, then its initialisation function will be called. We
* avoid linker symbol pollution (i.e. always dragging in serial.o
* just because of a call to serial_init()) and we also avoid @c
* #ifdef spaghetti (having to conditionalise every reference to
* functions in serial.c).
*
* The linker script takes care of assembling the tables for us. All
* our table sections have names of the format @c .tbl.NAME.NN where
* @c NAME designates the data structure stored in the table (e.g. @c
* init_fns) and @c NN is a two-digit decimal number used to impose an
* ordering upon the tables if required. @c NN=00 is reserved for the
* symbol indicating "table start", and @c NN=99 is reserved for the
* symbol indicating "table end".
*
* As an example, suppose that we want to create a "frobnicator"
* feature framework, and allow for several independent modules to
* provide frobnicating services. Then we would create a frob.h
* header file containing e.g.
*
* @code
*
* struct frobnicator {
* const char *name; // Name of the frobnicator
* void ( *frob ) ( void ); // The frobnicating function itself
* };
*
* #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
*
* #define __frobnicator __table_entry ( FROBNICATORS, 01 )
*
* @endcode
*
* Any module providing frobnicating services would look something
* like
*
* @code
*
* #include "frob.h"
*
* static void my_frob ( void ) {
* // Do my frobnicating
* ...
* }
*
* struct frob my_frobnicator __frobnicator = {
* .name = "my_frob",
* .frob = my_frob,
* };
*
* @endcode
*
* The central frobnicator code (frob.c) would use the frobnicating
* modules as follows
*
* @code
*
* #include "frob.h"
*
* // Call all linked-in frobnicators
* void frob_all ( void ) {
* struct frob *frob;
*
* for_each_table ( frob, FROBNICATORS ) {
* printf ( "Calling frobnicator \"%s\"\n", frob->name );
* frob->frob ();
* }
* }
*
* @endcode
*
* See init.h and init.c for a real-life example.
*
*/
#ifdef DOXYGEN
#define __attribute__( x )
#endif
/**
* Declare a linker table
*
* @v type Data type
* @v name Table name
* @ret table Linker table
*/
#define __table( type, name ) ( type, name )
/**
* Get linker table data type
*
* @v table Linker table
* @ret type Data type
*/
#define __table_type( table ) __table_extract_type table
#define __table_extract_type( type, name ) type
/**
* Get linker table name
*
* @v table Linker table
* @ret name Table name
*/
#define __table_name( table ) __table_extract_name table
#define __table_extract_name( type, name ) name
/**
* Get linker table section name
*
* @v table Linker table
* @v idx Sub-table index
* @ret section Section name
*/
#define __table_section( table, idx ) \
".tbl." __table_name ( table ) "." __table_str ( idx )
#define __table_str( x ) #x
/**
* Get linker table alignment
*
* @v table Linker table
* @ret align Alignment
*/
#define __table_alignment( table ) __alignof__ ( __table_type ( table ) )
/**
* Declare a linker table entry
*
* @v table Linker table
* @v idx Sub-table index
*
* Example usage:
*
* @code
*
* #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
*
* #define __frobnicator __table_entry ( FROBNICATORS, 01 )
*
* struct frobnicator my_frob __frobnicator = {
* ...
* };
*
* @endcode
*/
#define __table_entry( table, idx ) \
__attribute__ (( __section__ ( __table_section ( table, idx ) ) \
__aligned__ ( __table_alignment ( table ) ) ))
/**
* Get start of linker table
*
* @v table Linker table
* @ret start Start of linker table
*
* Example usage:
*
* @code
*
* #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
*
* struct frobnicator *frobs = table_start ( FROBNICATORS );
*
* @endcode
*/
#define table_start( table ) ( { \
static __table_type ( table ) __table_start[0] \
__table_entry ( table, 00 ); \
__table_start; } )
/**
* Get end of linker table
*
* @v table Linker table
* @ret end End of linker table
*
* Example usage:
*
* @code
*
* #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
*
* struct frobnicator *frobs_end = table_end ( FROBNICATORS );
*
* @endcode
*/
#define table_end( table ) ( { \
static __table_type ( table ) __table_end[0] \
__table_entry ( table, 99 ); \
__table_end; } )
/**
* Get number of entries in linker table
*
* @v table Linker table
* @ret num_entries Number of entries in linker table
*
* Example usage:
*
* @code
*
* #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
*
* unsigned int num_frobs = table_num_entries ( FROBNICATORS );
*
* @endcode
*
*/
#define table_num_entries( table ) \
( ( unsigned int ) ( table_end ( table ) - \
table_start ( table ) ) )
/**
* Iterate through all entries within a linker table
*
* @v pointer Entry pointer
* @v table Linker table
*
* Example usage:
*
* @code
*
* #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
*
* struct frobnicator *frob;
*
* for_each_table_entry ( frob, FROBNICATORS ) {
* ...
* }
*
* @endcode
*
*/
#define for_each_table_entry( pointer, table ) \
for ( pointer = table_start ( table ) ; \
pointer < table_end ( table ) ; \
pointer++ )
/**
* Iterate through all entries within a linker table in reverse order
*
* @v pointer Entry pointer
* @v table Linker table
*
* Example usage:
*
* @code
*
* #define FROBNICATORS __table ( struct frobnicator, "frobnicators" )
*
* struct frobnicator *frob;
*
* for_each_table_entry_reverse ( frob, FROBNICATORS ) {
* ...
* }
*
* @endcode
*
*/
#define for_each_table_entry_reverse( pointer, table ) \
for ( pointer = ( table_end ( table ) - 1 ) ; \
pointer >= table_start ( table ) ; \
pointer-- )
#endif /* _GPXE_TABLES_H */