iPXE
librm_test.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2014 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 /** @file
00027  *
00028  * Real mode transition self-tests
00029  *
00030  * This file allows for easy measurement of the time taken to perform
00031  * real mode transitions, which may have a substantial overhead when
00032  * running under a hypervisor.
00033  *
00034  */
00035 
00036 /* Forcibly enable assertions */
00037 #undef NDEBUG
00038 
00039 #include <ipxe/test.h>
00040 #include <ipxe/profile.h>
00041 #include <realmode.h>
00042 
00043 /** Number of sample iterations for profiling */
00044 #define PROFILE_COUNT 4096
00045 
00046 /** Protected-to-real mode transition profiler */
00047 static struct profiler p2r_profiler __profiler = { .name = "p2r" };
00048 
00049 /** Real-to-protected mode transition profiler */
00050 static struct profiler r2p_profiler __profiler = { .name = "r2p" };
00051 
00052 /** Real-mode call profiler */
00053 static struct profiler real_call_profiler __profiler = { .name = "real_call" };
00054 
00055 /** Virtual call profiler */
00056 static struct profiler virt_call_profiler __profiler = { .name = "virt_call" };
00057 
00058 /**
00059  * Dummy function for profiling tests
00060  */
00061 static __asmcall void librm_test_call ( struct i386_all_regs *ix86 __unused ) {
00062         /* Do nothing */
00063 }
00064 
00065 /**
00066  * Perform real mode transition self-tests
00067  *
00068  */
00069 static void librm_test_exec ( void ) {
00070         unsigned int i;
00071         unsigned long timestamp;
00072         uint32_t timestamp_lo;
00073         uint32_t timestamp_hi;
00074         uint32_t started;
00075         uint32_t stopped;
00076         uint32_t discard_d;
00077 
00078         /* Profile mode transitions.  We want to profile each
00079          * direction of the transition separately, so perform an RDTSC
00080          * while in real mode and tweak the profilers' start/stop
00081          * times appropriately.
00082          */
00083         for ( i = 0 ; i < PROFILE_COUNT ; i++ ) {
00084                 profile_start ( &p2r_profiler );
00085                 __asm__ __volatile__ ( REAL_CODE ( "rdtsc\n\t" )
00086                                        : "=a" ( timestamp_lo ),
00087                                          "=d" ( timestamp_hi )
00088                                        : );
00089                 timestamp = timestamp_lo;
00090                 if ( sizeof ( timestamp ) > sizeof ( timestamp_lo ) )
00091                         timestamp |= ( ( ( uint64_t ) timestamp_hi ) << 32 );
00092                 profile_start_at ( &r2p_profiler, timestamp );
00093                 profile_stop ( &r2p_profiler );
00094                 profile_stop_at ( &p2r_profiler, timestamp );
00095         }
00096 
00097         /* Profile complete real-mode call cycle */
00098         for ( i = 0 ; i < PROFILE_COUNT ; i++ ) {
00099                 profile_start ( &real_call_profiler );
00100                 __asm__ __volatile__ ( REAL_CODE ( "" ) : );
00101                 profile_stop ( &real_call_profiler );
00102         }
00103 
00104         /* Profile complete virtual call cycle */
00105         for ( i = 0 ; i < PROFILE_COUNT ; i++ ) {
00106                 __asm__ __volatile__ ( REAL_CODE ( "rdtsc\n\t"
00107                                                    "movl %k0, %k2\n\t"
00108                                                    VIRT_CALL ( librm_test_call )
00109                                                    "rdtsc\n\t" )
00110                                        : "=a" ( stopped ), "=d" ( discard_d ),
00111                                          "=R" ( started ) : );
00112                 profile_start_at ( &virt_call_profiler, started );
00113                 profile_stop_at ( &virt_call_profiler, stopped );
00114         }
00115 }
00116 
00117 /** Real mode transition self-test */
00118 struct self_test librm_test __self_test = {
00119         .name = "librm",
00120         .exec = librm_test_exec,
00121 };
00122 
00123 REQUIRING_SYMBOL ( librm_test );
00124 REQUIRE_OBJECT ( test );