github.com/jlmucb/cloudproxy@v0.0.0-20170830161738-b5aa0b619bc4/cpvmm/vmm/ipc/ipc_api.c (about)

     1  /*
     2   * Copyright (c) 2013 Intel Corporation
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9  
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  #include "file_codes.h"
    18  #define VMM_DEADLOOP()          VMM_DEADLOOP_LOG(IPC_API_C)
    19  #define VMM_ASSERT(__condition) VMM_ASSERT_LOG(IPC_API_C, __condition)
    20  #include "vmm_defs.h"
    21  #include "ipc_impl.h"
    22  #include "lock.h"
    23  #include "heap.h"
    24  #include "vmm_objects.h"
    25  #include "guest.h"
    26  #include "hw_utils.h"
    27  #include "scheduler.h"
    28  #include "vmm_dbg.h"
    29  #include "list.h"
    30  #include "memory_allocator.h"
    31  
    32  #pragma warning( disable : 4100)        // unreferenced formal parameter
    33  
    34  #define STOP_ALL_CONTEXT_ID      INVALID_GUEST_ID
    35  
    36  // callback to check if current CPU is destination for IPC
    37  typedef BOOLEAN (*is_destination_fn)(void* arg);
    38  static BOOLEAN all_cpus(void *arg);
    39  static BOOLEAN is_cpu_running_guest(void *arg);
    40  
    41  // Context for start/stop IPCs.
    42  // Send Stop IPC:
    43  //      -- context->stop = true;
    44  //      -- send IPC
    45  // Handle Stop IPC:
    46  //      -- busy-loop while context->stop == true
    47  // Send Start IPC (finish the busy loop):
    48  //      -- context->stop = false;
    49  // Timestamps are used in order not to miss change of the context->stop (fast start/stop)
    50  typedef struct _STOP_CPU_CONTEXT
    51  {
    52      volatile BOOLEAN         stop;                      // variable to busy-loop
    53      UINT32                   timestamp;                 // timestamp of the stop command
    54      volatile UINT32          current_timestamp;         // latest assigned timestamp.
    55                                                          // Can differ from timestamp (fast start/stop).
    56      UINT32                   num_stopped_cpus;          // number of CPUs stopped by CPU command
    57      volatile IPC_HANDLER_FN  on_start_handler;          // handler to execute when start command arrives
    58      volatile void            *on_start_handler_arg;     // argument to the start handler
    59      is_destination_fn        is_destination;            // function to check if current CPU is destination for IPC
    60      void                     *is_destination_arg;       // argument for is_destination()
    61      GUEST_ID                 guest_id;
    62      char                     padding[6];
    63      LIST_ELEMENT             list[1];
    64  } STOP_CPU_CONTEXT;
    65  
    66  static BOOLEAN execute_stop(STOP_CPU_CONTEXT *context, is_destination_fn is_destination, void* is_destination_arg);
    67  static UINT32 execute_start(STOP_CPU_CONTEXT *context, IPC_HANDLER_FN handler, void* arg);
    68  static STOP_CPU_CONTEXT *ipc_find_stop_guest_cpus_context(GUEST_ID guest_id);
    69  
    70  // Context for stop/start IPCs -- one context per guest to support start/stop guest CPUs
    71  // plus one context to support stop/start all CPUs
    72  typedef struct _IPC_START_STOP_CONTEXT
    73  {
    74      LIST_ELEMENT         ipc_stop_context[1];
    75  //    VMM_READ_WRITE_LOCK  context_lock[1];               // lock for context list
    76      VMM_LOCK             stop_lock[1];                  // lock for exclusive start/stop execution
    77  } IPC_START_STOP_CONTEXT;
    78  
    79  IPC_START_STOP_CONTEXT ipc_start_stop_context;
    80  
    81  // FUNCTION:        ipc_execute_handler
    82  // DESCRIPTION:     Execute handler on other CPUs. This function returns when all destination
    83  //                  CPUs are about to execute the handler
    84  // ARGUMENTS:       dst -- destination CPU(s)
    85  //                  handler -- handler for execution
    86  //                  arg -- argument to pass to the handler
    87  // RETURN VALUE:    number of CPUs on which handler is about to execute
    88  UINT32 ipc_execute_handler(IPC_DESTINATION dst, IPC_HANDLER_FN handler, void* arg)
    89  {
    90      return ipc_send_message(dst, IPC_TYPE_NORMAL, handler, arg);
    91  }
    92  
    93  // FUNCTION:        ipc_execute_handler
    94  // DESCRIPTION:     Execute handler on other CPUs. This function returns when all destination
    95  //                  CPUs finished to execute the handler
    96  // ARGUMENTS:       dst -- destination CPU(s)
    97  //                  handler -- handler for execution
    98  //                  arg -- argument to pass to the handler
    99  // RETURN VALUE:    number of CPUs on which handler is about to execute
   100  UINT32 ipc_execute_handler_sync(IPC_DESTINATION dst, IPC_HANDLER_FN handler, void* arg)
   101  {
   102      return ipc_send_message_sync(dst, IPC_TYPE_NORMAL, handler, arg);
   103  }
   104  
   105  // FUNCTION:        stop_all_cpus
   106  // DESCRIPTION:     Stop all other CPUs. Other CPUs will be executing the busy loop until
   107  //                  they are resumed by calling start_all_cpus()
   108  // RETURN VALUE:    TRUE if all processors has stopped, FALSE in case of failure
   109  BOOLEAN stop_all_cpus(void)
   110  {
   111      STOP_CPU_CONTEXT *context = ipc_find_stop_guest_cpus_context(STOP_ALL_CONTEXT_ID);
   112  
   113      return execute_stop(context, all_cpus, NULL);
   114  }
   115  
   116  // FUNCTION:        start_all_cpus
   117  // DESCRIPTION:     Start all other CPUs previously stopped by stop_all_cpus()
   118  // RETURN VALUE:    TRUE if all processors has resumed, FALSE in case of failure
   119  UINT32 start_all_cpus(IPC_HANDLER_FN handler, void* arg)
   120  {
   121      STOP_CPU_CONTEXT *context = ipc_find_stop_guest_cpus_context(STOP_ALL_CONTEXT_ID);
   122  
   123      return execute_start(context, handler, arg);
   124  }
   125  
   126  // FUNCTION:        stop_all_guest_cpus
   127  // DESCRIPTION:     Stop all CPUs running given guest. These CPUs will be executing the busy loop until
   128  //                  they are resumed by calling start_all_guest_cpus()
   129  // RETURN VALUE:    TRUE if CPUs running guest has stopped, FALSE in case of failure
   130  BOOLEAN stop_all_guest_cpus(GUEST_HANDLE guest)
   131  {
   132      STOP_CPU_CONTEXT *context = NULL;
   133      GUEST_ID guest_id = guest_get_id(guest);
   134  
   135      context = ipc_find_stop_guest_cpus_context(guest_id);
   136  
   137      return execute_stop(context, is_cpu_running_guest, (void *) (size_t) guest_id);
   138  }
   139  
   140  // FUNCTION:        start_all_guest_cpus
   141  // DESCRIPTION:     Start all CPUs running given guest previously stopped by stop_all_guest_cpus()
   142  // RETURN VALUE:    TRUE if all CPUs running guest has resumed, FALSE in case of failure
   143  UINT32 start_all_guest_cpus(GUEST_HANDLE guest, IPC_HANDLER_FN handler, void* arg)
   144  {
   145      STOP_CPU_CONTEXT *context = NULL;
   146  
   147      context = ipc_find_stop_guest_cpus_context(guest_get_id(guest));
   148  
   149      return execute_start(context, handler, arg);
   150  }
   151  
   152  // FUNCTION:        all_cpus
   153  // DESCRIPTION:     Callback to check if current CPU is destination for IPC.
   154  //                  all_cpus() is used for IPCs to all CPUs
   155  // RETURN VALUE:    TRUE
   156  BOOLEAN all_cpus(void *arg UNUSED)
   157  {
   158      return TRUE;
   159  }
   160  
   161  // FUNCTION:        is_cpu_running_guest
   162  // DESCRIPTION:     Callback to check if current CPU is destination for IPC.
   163  //                  is_cpu_running_guest() is used for IPCs to CPUs running a specific guest
   164  // ARGUMENTS:       arg -- guest id which CPUs need to stop
   165  // RETURN VALUE:    TRUE if CPU executes the guest, FALSE if CPU does NOT execute the guest
   166  BOOLEAN is_cpu_running_guest(void* arg)
   167  {
   168      GUEST_ID stop_guest_id = (GUEST_ID) (size_t) arg;
   169      CPU_ID cpu_id = IPC_CPU_ID();
   170      GUEST_CPU_HANDLE gcpu = NULL;
   171      SCHEDULER_GCPU_ITERATOR iter;
   172      const VIRTUAL_CPU_ID *vcpu = NULL;
   173  
   174      for(gcpu = scheduler_same_host_cpu_gcpu_first(&iter, cpu_id); gcpu != NULL; 
   175            gcpu = scheduler_same_host_cpu_gcpu_next(&iter)) {
   176          vcpu = guest_vcpu(gcpu);
   177          VMM_ASSERT(vcpu);
   178          if(vcpu->guest_id == stop_guest_id) {
   179              return TRUE;
   180          }
   181      }
   182      return FALSE;
   183  }
   184  
   185  
   186  // FUNCTION:        stop_cpu_handler
   187  // DESCRIPTION:     Handler that is called when IPC arrives. It stops the CPU
   188  //                  by entering the busy-wait loop until corresponding start() is executed.
   189  // ARGUMENTS:       arg -- IPC's context (STOP_CPU_CONTEXT)
   190  void stop_cpu_handler(CPU_ID from, void* arg)
   191  {
   192      STOP_CPU_CONTEXT *context = (STOP_CPU_CONTEXT *) arg;
   193  
   194      if (context->is_destination(context->is_destination_arg)) {
   195          // check the timestamp in order not to miss "edge" of context->stop
   196          while (context->stop && context->timestamp == context->current_timestamp) {
   197              hw_pause();
   198              ipc_process_one_ipc();
   199          }
   200  
   201          // Check if on START handler should be performed.
   202          if (context->on_start_handler != NULL)
   203              context->on_start_handler(from, (void *) context->on_start_handler_arg);
   204      }
   205  }
   206  
   207  
   208  // FUNCTION:        execute_stop
   209  // DESCRIPTION:     Send the stop IPC
   210  // ARGUMENTS:       context -- context for busy-waiting at the receiver
   211  //                  is_destination -- callback for destination CPU to check if it is target for IPC
   212  //                  is_destination_arg -- argument for is_destination()
   213  // RETURN VALUE:    TRUE if all destination processors has stopped, FALSE in case of failure
   214  static BOOLEAN execute_stop(STOP_CPU_CONTEXT *context, is_destination_fn is_destination, void* is_destination_arg)
   215  {
   216      IPC_DESTINATION dst;
   217  
   218      VMM_ASSERT( context != NULL );
   219      vmm_zeromem(&dst, sizeof(dst));
   220      interruptible_lock_acquire(ipc_start_stop_context.stop_lock);
   221  
   222      if (context->stop) {
   223                  // already stopped - should not be here
   224          VMM_ASSERT( FALSE == context->stop );
   225          lock_release(ipc_start_stop_context.stop_lock);
   226          return FALSE;
   227      }
   228  
   229      context->stop = TRUE;
   230      context->timestamp = ++context->current_timestamp;
   231      context->is_destination = is_destination;
   232      context->is_destination_arg = is_destination_arg;
   233  
   234      dst.addr_shorthand = IPI_DST_ALL_EXCLUDING_SELF;
   235      context->num_stopped_cpus = ipc_send_message(dst, IPC_TYPE_STOP, stop_cpu_handler, context);
   236      lock_release(ipc_start_stop_context.stop_lock);
   237      return TRUE;
   238  }
   239  
   240  
   241  // FUNCTION:        execute_start
   242  // DESCRIPTION:     Send the start IPC
   243  // ARGUMENTS:       context -- context used for busy-waiting at the receiver
   244  // RETURN VALUE:    TRUE if all destination processors has started, FALSE in case of failure
   245  static UINT32 execute_start(STOP_CPU_CONTEXT *context, IPC_HANDLER_FN handler, void* arg)
   246  {
   247      IPC_DESTINATION dst;
   248  
   249      VMM_ASSERT( context != NULL );
   250      vmm_zeromem(&dst, sizeof(dst));
   251      interruptible_lock_acquire(ipc_start_stop_context.stop_lock);
   252      if (FALSE == context->stop) {
   253                  // not stopped - should not be here
   254          VMM_ASSERT( TRUE == context->stop );
   255          lock_release(ipc_start_stop_context.stop_lock);
   256          return FALSE;
   257      }
   258  
   259      //if(handler != NULL)
   260      //{
   261      //    ipc_execute_handler_sync(dst, handler, arg);
   262      //}
   263      context->on_start_handler = handler;
   264      context->on_start_handler_arg = arg;
   265      context->stop = FALSE;
   266  
   267      dst.addr_shorthand = IPI_DST_ALL_EXCLUDING_SELF;
   268      ipc_send_message(dst, IPC_TYPE_START, NULL, NULL);
   269      lock_release(ipc_start_stop_context.stop_lock);
   270      return context->num_stopped_cpus;
   271  }
   272  
   273  
   274  // FUNCTION:        ipc_initialize
   275  // DESCRIPTION:     Initialize stop/start context and initialize IPC engine. Must be called before any IPCs can be generated.
   276  // RETURN VALUE:    TRUE for success, FALSE for failure
   277  BOOLEAN ipc_initialize(UINT16 number_of_host_processors)
   278  {
   279      BOOLEAN           status;
   280      STOP_CPU_CONTEXT  *stop_all_context = NULL;
   281      GUEST_HANDLE      guest = NULL;
   282      GUEST_ID          guest_id = INVALID_GUEST_ID;
   283      GUEST_ECONTEXT    context;
   284  
   285      (void)status;
   286      vmm_zeromem(&ipc_start_stop_context, sizeof(ipc_start_stop_context));
   287  
   288      status = ipc_state_init(number_of_host_processors);
   289      // BEFORE_VMLAUNCH. NOT_USED. OLD_IPC is not defined.
   290      VMM_ASSERT(status);
   291  
   292      list_init(ipc_start_stop_context.ipc_stop_context);
   293      lock_initialize(ipc_start_stop_context.stop_lock);
   294  
   295      stop_all_context = vmm_malloc(sizeof(STOP_CPU_CONTEXT));
   296      // BEFORE_VMLAUNCH. NOT_USED. OLD_IPC is not defined.
   297      VMM_ASSERT(stop_all_context);
   298      vmm_zeromem(stop_all_context, sizeof(STOP_CPU_CONTEXT));
   299      stop_all_context->guest_id = INVALID_GUEST_ID;
   300      list_add(ipc_start_stop_context.ipc_stop_context, stop_all_context->list);
   301  
   302      for(guest = guest_first(&context); guest != NULL; guest = guest_next(&context)) {
   303          guest_id = guest_get_id(guest);
   304          ipc_guest_initialize(guest_id);
   305      }
   306  
   307      return TRUE;
   308  }
   309  
   310  // FUNCTION:        ipc_guest_initialize
   311  // DESCRIPTION:     Initialize stop/start context and initialize IPC engine for a guest. Must be called when new guest is added.
   312  // RETURN VALUE:    TRUE for success, FALSE for failure
   313  BOOLEAN ipc_guest_initialize(GUEST_ID guest_id)
   314  {
   315      BOOLEAN           status;
   316      STOP_CPU_CONTEXT  *stop_guest_cpus_context = NULL;
   317  
   318      (void)status;
   319      status = ipc_guest_state_init(guest_id);
   320      // BEFORE_VMLAUNCH. NOT_USED. OLD_IPC is not defined.
   321      VMM_ASSERT(status);
   322  
   323      stop_guest_cpus_context = vmm_malloc(sizeof(STOP_CPU_CONTEXT));
   324      // BEFORE_VMLAUNCH. NOT_USED. OLD_IPC is not defined.
   325      VMM_ASSERT(stop_guest_cpus_context);
   326      vmm_zeromem(stop_guest_cpus_context, sizeof(STOP_CPU_CONTEXT));
   327      stop_guest_cpus_context->guest_id = guest_id;
   328      list_add(ipc_start_stop_context.ipc_stop_context, stop_guest_cpus_context->list);
   329      return TRUE;
   330  }
   331  
   332  static
   333  STOP_CPU_CONTEXT *ipc_find_stop_guest_cpus_context(GUEST_ID guest_id)
   334  {
   335      LIST_ELEMENT *iter = NULL;
   336      STOP_CPU_CONTEXT *stop_cpu_context = NULL;
   337  
   338      LIST_FOR_EACH(ipc_start_stop_context.ipc_stop_context, iter) {
   339          stop_cpu_context = LIST_ENTRY(iter, STOP_CPU_CONTEXT, list);
   340          if(stop_cpu_context->guest_id == guest_id) {
   341              return stop_cpu_context;
   342          }
   343      }
   344  
   345      return NULL;
   346  }