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 }