iPXE
efi_timer.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <string.h>
00027 #include <errno.h>
00028 #include <unistd.h>
00029 #include <ipxe/timer.h>
00030 #include <ipxe/init.h>
00031 #include <ipxe/efi/efi.h>
00032 
00033 /** @file
00034  *
00035  * iPXE timer API for EFI
00036  *
00037  */
00038 
00039 /**
00040  * Number of jiffies per second
00041  *
00042  * This is a policy decision.
00043  */
00044 #define EFI_JIFFIES_PER_SEC 32
00045 
00046 /** Current tick count */
00047 static unsigned long efi_jiffies;
00048 
00049 /** Timer tick event */
00050 static EFI_EVENT efi_tick_event;
00051 
00052 /** Colour for debug messages */
00053 #define colour &efi_jiffies
00054 
00055 /**
00056  * Delay for a fixed number of microseconds
00057  *
00058  * @v usecs             Number of microseconds for which to delay
00059  */
00060 static void efi_udelay ( unsigned long usecs ) {
00061         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
00062         EFI_STATUS efirc;
00063         int rc;
00064 
00065         if ( ( efirc = bs->Stall ( usecs ) ) != 0 ) {
00066                 rc = -EEFI ( efirc );
00067                 DBGC ( colour, "EFI could not delay for %ldus: %s\n",
00068                        usecs, strerror ( rc ) );
00069                 /* Probably screwed */
00070         }
00071 }
00072 
00073 /**
00074  * Get current system time in ticks
00075  *
00076  * @ret ticks           Current time, in ticks
00077  */
00078 static unsigned long efi_currticks ( void ) {
00079         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
00080 
00081         /* UEFI manages to ingeniously combine the worst aspects of
00082          * both polling and interrupt-driven designs.  There is no way
00083          * to support proper interrupt-driven operation, since there
00084          * is no way to hook in an interrupt service routine.  A
00085          * mockery of interrupts is provided by UEFI timers, which
00086          * trigger at a preset rate and can fire at any time.
00087          *
00088          * We therefore have all of the downsides of a polling design
00089          * (inefficiency and inability to sleep until something
00090          * interesting happens) combined with all of the downsides of
00091          * an interrupt-driven design (the complexity of code that
00092          * could be preempted at any time).
00093          *
00094          * The UEFI specification expects us to litter the entire
00095          * codebase with calls to RaiseTPL() as needed for sections of
00096          * code that are not reentrant.  Since this doesn't actually
00097          * gain us any substantive benefits (since even with such
00098          * calls we would still be suffering from the limitations of a
00099          * polling design), we instead choose to run at TPL_CALLBACK
00100          * almost all of the time, dropping to TPL_APPLICATION to
00101          * allow timer ticks to occur.
00102          *
00103          *
00104          * For added excitement, UEFI provides no clean way for device
00105          * drivers to shut down in preparation for handover to a
00106          * booted operating system.  The platform firmware simply
00107          * doesn't bother to call the drivers' Stop() methods.
00108          * Instead, all non-trivial drivers must register an
00109          * EVT_SIGNAL_EXIT_BOOT_SERVICES event to be signalled when
00110          * ExitBootServices() is called, and clean up without any
00111          * reference to the EFI driver model.
00112          *
00113          * Unfortunately, all timers silently stop working when
00114          * ExitBootServices() is called.  Even more unfortunately, and
00115          * for no discernible reason, this happens before any
00116          * EVT_SIGNAL_EXIT_BOOT_SERVICES events are signalled.  The
00117          * net effect of this entertaining design choice is that any
00118          * timeout loops on the shutdown path (e.g. for gracefully
00119          * closing outstanding TCP connections) may wait indefinitely.
00120          *
00121          * There is no way to report failure from currticks(), since
00122          * the API lazily assumes that the host system continues to
00123          * travel through time in the usual direction.  Work around
00124          * EFI's violation of this assumption by falling back to a
00125          * simple free-running monotonic counter during shutdown.
00126          */
00127         if ( efi_shutdown_in_progress ) {
00128                 efi_jiffies++;
00129         } else {
00130                 bs->RestoreTPL ( TPL_APPLICATION );
00131                 bs->RaiseTPL ( TPL_CALLBACK );
00132         }
00133 
00134         return ( efi_jiffies * ( TICKS_PER_SEC / EFI_JIFFIES_PER_SEC ) );
00135 }
00136 
00137 /**
00138  * Timer tick
00139  *
00140  * @v event             Timer tick event
00141  * @v context           Event context
00142  */
00143 static EFIAPI void efi_tick ( EFI_EVENT event __unused,
00144                               void *context __unused ) {
00145 
00146         /* Increment tick count */
00147         efi_jiffies++;
00148 }
00149 
00150 /**
00151  * Start timer tick
00152  *
00153  */
00154 static void efi_tick_startup ( void ) {
00155         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
00156         EFI_STATUS efirc;
00157         int rc;
00158 
00159         /* Create timer tick event */
00160         if ( ( efirc = bs->CreateEvent ( ( EVT_TIMER | EVT_NOTIFY_SIGNAL ),
00161                                          TPL_CALLBACK, efi_tick, NULL,
00162                                          &efi_tick_event ) ) != 0 ) {
00163                 rc = -EEFI ( efirc );
00164                 DBGC ( colour, "EFI could not create timer tick: %s\n",
00165                        strerror ( rc ) );
00166                 /* Nothing we can do about it */
00167                 return;
00168         }
00169 
00170         /* Start timer tick */
00171         if ( ( efirc = bs->SetTimer ( efi_tick_event, TimerPeriodic,
00172                                       ( 10000000 / EFI_JIFFIES_PER_SEC ) ))!=0){
00173                 rc = -EEFI ( efirc );
00174                 DBGC ( colour, "EFI could not start timer tick: %s\n",
00175                        strerror ( rc ) );
00176                 /* Nothing we can do about it */
00177                 return;
00178         }
00179         DBGC ( colour, "EFI timer started at %d ticks per second\n",
00180                EFI_JIFFIES_PER_SEC );
00181 }
00182 
00183 /**
00184  * Stop timer tick
00185  *
00186  * @v booting           System is shutting down in order to boot
00187  */
00188 static void efi_tick_shutdown ( int booting __unused ) {
00189         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
00190         EFI_STATUS efirc;
00191         int rc;
00192 
00193         /* Stop timer tick */
00194         if ( ( efirc = bs->SetTimer ( efi_tick_event, TimerCancel, 0 ) ) != 0 ){
00195                 rc = -EEFI ( efirc );
00196                 DBGC ( colour, "EFI could not stop timer tick: %s\n",
00197                        strerror ( rc ) );
00198                 /* Self-destruct initiated */
00199                 return;
00200         }
00201         DBGC ( colour, "EFI timer stopped\n" );
00202 
00203         /* Destroy timer tick event */
00204         if ( ( efirc = bs->CloseEvent ( efi_tick_event ) ) != 0 ) {
00205                 rc = -EEFI ( efirc );
00206                 DBGC ( colour, "EFI could not destroy timer tick: %s\n",
00207                        strerror ( rc ) );
00208                 /* Probably non-fatal */
00209                 return;
00210         }
00211 }
00212 
00213 /** Timer tick startup function */
00214 struct startup_fn efi_tick_startup_fn __startup_fn ( STARTUP_EARLY ) = {
00215         .startup = efi_tick_startup,
00216         .shutdown = efi_tick_shutdown,
00217 };
00218 
00219 /** EFI timer */
00220 struct timer efi_timer __timer ( TIMER_NORMAL ) = {
00221         .name = "efi",
00222         .currticks = efi_currticks,
00223         .udelay = efi_udelay,
00224 };