2 * This file is part of the Palacios Virtual Machine Monitor developed
3 * by the V3VEE Project with funding from the United States National
4 * Science Foundation and the Department of Energy.
6 * The V3VEE Project is a joint project between Northwestern University
7 * and the University of New Mexico. You can find out more at
10 * Copyright (c) 2012, Kyle C. Hale <kh@u.norhtwestern.edu>
11 * Copyright (c) 2012, The V3VEE Project <http://www.v3vee.org>
12 * All rights reserved.
14 * Author: Kyle C. Hale <kh@u.northwestern.edu>
16 * This is free software. You are permitted to use,
17 * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
20 #include <palacios/vmm.h>
21 #include <palacios/vm_guest.h>
22 #include <palacios/vm_guest_mem.h>
23 #include <palacios/vmm_extensions.h>
24 #include <palacios/vmm_decoder.h>
25 #include <palacios/vmm_types.h>
26 #include <palacios/vmm_hypercall.h>
27 #include <palacios/vmm_hashtable.h>
28 #include <palacios/vmm_util.h>
29 #include <palacios/vmm_mem_hook.h>
30 #include <palacios/vmcb.h>
31 #include <gears/privilege.h>
32 #include <gears/guard_mods.h>
34 #ifndef V3_CONFIG_DEBUG_EXT_GUARD_MODS
36 #define PrintDebug(fmt, args...)
40 static struct v3_guarded_mods global_mods;
42 static struct v3_gm * current_gm;
44 /* generates an id for a module using
45 * the TSC and a hash */
47 gen_mod_id (ullong_t * id)
51 ret = (ullong_t)v3_hash_long((ulong_t)val, sizeof(ullong_t)*8);
52 PrintDebug(VM_NONE, VCORE_NONE,"GM: Generating new module ID: 0x%llx\n", ret);
58 free_mod (struct v3_gm * mod)
60 struct v3_priv * p = NULL;
61 struct v3_priv * tmp = NULL;
62 struct v3_entry_point * er = NULL;
63 struct v3_entry_point * etmp = NULL;
66 /* free the privilege list */
67 list_for_each_entry_safe(p, tmp, &(mod->priv_list), priv_node) {
68 list_del(&(p->priv_node));
73 V3_Free(mod->content_hash);
75 list_for_each_entry_safe(er, etmp, &(mod->er_list), er_node) {
76 list_del(&er->er_node);
79 for (i = 0; i < mod->num_entries; i++) {
80 V3_Free(mod->entry_points[i].name);
83 V3_Free(mod->entry_points);
89 add_privs_to_list (struct v3_vm_info * vm,
90 struct list_head * list,
94 struct v3_priv * priv = NULL;
97 for (i = 0 ; i < nprivs; i++) {
98 if ((priv = v3_lookup_priv(vm, priv_array[i])) == NULL) {
99 PrintError(VM_NONE, VCORE_NONE,"Guarded module requested non-existent privilege: %s\n", priv_array[i]);
102 list_add(&(priv->priv_node), list);
111 mod_name_hash_fn (addr_t key)
113 char * name = (char*)key;
114 return v3_hash_buffer((uint8_t*)name, strlen(name));
119 mod_name_eq_fn (addr_t key1, addr_t key2)
121 char * name1 = (char*)key1;
122 char * name2 = (char*)key2;
123 return (strcmp(name1, name2) == 0);
128 mod_id_hash_fn (addr_t key)
130 return v3_hash_long(key, sizeof(ullong_t) * 8);
135 mod_id_eq_fn (addr_t key1, addr_t key2)
137 return (key1 == key2);
142 entry_addr_hash_fn (addr_t key)
144 return v3_hash_long(key, sizeof(addr_t) * 8);
149 entry_addr_eq_fn (addr_t key1, addr_t key2)
151 return (key1 == key2);
157 * This is invoked through a hypercall that has been instrumented in the
158 * guarded module's routine denoted by the module_init macro.
160 /* TODO: add text hash check here */
162 guard_init (struct guest_info * core, unsigned int hcall_id, void * priv_data)
166 struct v3_guarded_mods * mods = &global_mods;
167 struct v3_entry_point * er = NULL;
168 struct v3_entry_point * tmp = NULL;
169 struct v3_priv * priv = NULL;
170 struct v3_priv * ptmp = NULL;
172 /* the guarded module should provide its ID in RBX */
173 mod_id = core->vm_regs.rbx;
175 V3_Print(VM_NONE, VCORE_NONE, "Received init request from GM (id=0x%llx)\n", mod_id);
177 /* check if the corresponding module exists */
178 if ((gm = (struct v3_gm*)v3_htable_search(mods->mod_id_table, (addr_t)mod_id)) == 0) {
179 PrintError(VM_NONE, VCORE_NONE,"Module (id=0x%llx) not found\n", mod_id);
183 PrintDebug(VM_NONE, VCORE_NONE,"GM: initializing guarded module %s\n", gm->name);
185 /* infer load address */
186 gm->load_addr = core->rip - HCALL_INSTR_LEN - gm->hcall_offset;
188 PrintDebug(VM_NONE, VCORE_NONE,"\tNew GM load address: %p\n", (void*)gm->load_addr);
190 list_for_each_entry_safe(er, tmp, &(gm->er_list), er_node) {
192 /* fix up the dynamic address of the entry point */
193 er->addr += gm->load_addr;
195 /* check if this entry point already exists */
196 if (v3_htable_search(mods->er_hash, er->addr)) {
197 PrintError(VM_NONE, VCORE_NONE,"GM: Error! Entry point already exists!\n");
201 /* add the new entry point to the global hash */
202 v3_htable_insert(mods->er_hash, er->addr, (addr_t)er);
205 PrintDebug(VM_NONE, VCORE_NONE,"GM: Raising privileges\n");
208 /* module will come up in privileged mode */
211 /* raise all of the module's privileges */
212 list_for_each_entry_safe(priv, ptmp, &(gm->priv_list), priv_node) {
213 if (priv->raise(core, priv->private_data) < 0) {
214 PrintError(VM_NONE, VCORE_NONE,"GM: Problem raising privilege\n");
219 /* get the entry RSP for integrity checks */
221 if (v3_gva_to_hva(core,
222 get_addr_linear(core, (addr_t)core->vm_regs.rsp, &(core->segments.ss)),
224 PrintError(VM_NONE, VCORE_NONE,"GM: Problem translating stack address\n");
229 /* this is just to be consistent with other entry types, r11 isn't used in init */
230 gm->r11_stack_callback[gm->callback_nesting] = core->vm_regs.r11;
231 gm->callback_nesting++;
233 PrintDebug(VM_NONE, VCORE_NONE,"GM: Guarded module initialized, set to PRIV\n");
243 * This is invoked when the kernel calls the module (through a callback function)
245 * TODO: the stack checking model here is crappy, but will be updated to only
246 * look at frame pointers and return addresses.
249 border_in_call (struct guest_info * core, unsigned int hcall_id, void * priv_data)
251 struct v3_guarded_mods * mods = &global_mods;
252 struct v3_entry_point * er;
253 struct v3_gm * gm = current_gm;
254 struct v3_priv * priv = NULL;
255 struct v3_priv * tmp = NULL;
258 addr_t entry = core->rip - HCALL_INSTR_LEN;
260 er = (struct v3_entry_point*)v3_htable_search(mods->er_hash, entry);
262 PrintError(VM_NONE, VCORE_NONE,"Attempt to enter at an invalid entry point (%p)\n", (void*)entry);
267 /* if this is first entry, record rsp */
268 if (gm->callback_nesting == 0) {
270 if (v3_gva_to_hva(core,
271 get_addr_linear(core, (addr_t)core->vm_regs.rsp, &core->segments.ss),
273 PrintError(VM_NONE, VCORE_NONE,"GM: Problem translating stack address\n");
279 if (gm->callback_nesting >= MAX_BORDER_NESTING) {
280 PrintError(VM_NONE, VCORE_NONE,"GM: Too much nesting of border crossings\n");
283 gm->r11_stack_callback[gm->callback_nesting] = core->vm_regs.r11;
284 gm->callback_nesting++;
287 if (gm->state == INIT) {
288 PrintError(VM_NONE, VCORE_NONE,"ERROR: module reached guard entry without being initialized!\n");
294 PrintDebug(VM_NONE, VCORE_NONE,"Entry request at %p granted to GM (%s)\n", (void*)entry, gm->name);
296 list_for_each_entry_safe(priv, tmp, &(gm->priv_list), priv_node) {
298 if (priv->raise(core, priv->private_data) < 0) {
299 PrintError(VM_NONE, VCORE_NONE,"GM: Problem raising privilege\n");
313 * This is invoked when the module returns to the kernel, after being invoked through
314 * a callback function.
317 border_out_return (struct guest_info * core, unsigned int hcall_id, void * priv_data)
319 struct v3_gm * gm = current_gm;
320 struct v3_priv * priv = NULL;
321 struct v3_priv * tmp = NULL;
323 gm->callback_nesting--;
325 /* restore the guest's return address */
326 core->vm_regs.r11 = gm->r11_stack_callback[gm->callback_nesting];
327 gm->r11_stack_callback[gm->callback_nesting] = 0;
329 /* Only lower privilege when we reach nesting 0 */
330 if (gm->callback_nesting <= 0) {
332 PrintDebug(VM_NONE, VCORE_NONE,"GM: Granting exit request to GM (%s)\n", gm->name);
333 list_for_each_entry_safe(priv, tmp, &(gm->priv_list), priv_node) {
335 if (priv->lower(core, priv->private_data) < 0) {
336 PrintError(VM_NONE, VCORE_NONE,"GM: Problem lowering privilege\n");
345 PrintDebug(VM_NONE, VCORE_NONE,"Priv Lower requested, but still at nesting level (%d)\n", gm->callback_nesting);
354 * This is invoked when the module calls out to the kernel, results in lowering of privilege
358 border_out_call (struct guest_info * core, unsigned int hcall_id, void * priv_data) {
360 struct v3_gm * gm = current_gm;
361 struct v3_priv * priv = NULL;
362 struct v3_priv * tmp = NULL;
364 /* TODO: um...we seem to be getting a border out call in NO_PRIV state */
365 if (1 || gm->state == PRIV || gm->state == INIT) {
367 gm->r11_stack_kernel[gm->kernel_nesting] = core->vm_regs.r11;
368 gm->kernel_nesting++;
370 PrintDebug(VM_NONE, VCORE_NONE,"GM: Granting border out call request to GM (%s)\n", gm->name);
371 list_for_each_entry_safe(priv, tmp, &(gm->priv_list), priv_node) {
372 if (priv->lower(core, priv->private_data) < 0) {
373 PrintError(VM_NONE, VCORE_NONE,"GM: Problem lowering privilege\n");
379 if (v3_gva_to_hva(core,
380 get_addr_linear(core, (addr_t)core->vm_regs.rsp, &(core->segments.ss)),
381 &gm->exit_rsp) < 0) {
382 PrintError(VM_NONE, VCORE_NONE,"GM: border out, error translating rsp addr\n");
386 if (gm->entry_rsp && gm->exit_rsp && gm->state == PRIV && (gm->entry_rsp >= gm->exit_rsp)) {
387 gm->stack_hash = v3_hash_buffer((uchar_t*)gm->exit_rsp, gm->entry_rsp - gm->exit_rsp);
393 PrintError(VM_NONE, VCORE_NONE,"GM: Trying to run border-out without privilege\n");
396 /* else, do nothing */
403 * This is invoked when the kernel returns control to the module after a border-out call
404 * Results in privilege raise
408 border_in_return (struct guest_info * core, unsigned int hcall_id, void * priv_data) {
409 struct v3_guarded_mods * mods = &global_mods;
410 struct v3_gm * gm = NULL;
411 struct v3_priv * priv = NULL;
412 struct v3_priv * tmp = NULL;
413 struct v3_entry_point * er = NULL;
415 addr_t entry = core->rip - HCALL_INSTR_LEN;
416 if ((er = (struct v3_entry_point*)v3_htable_search(mods->er_hash, entry)) == 0) {
417 PrintError(VM_NONE, VCORE_NONE,"Attempt to enter at an invalid entry point (%p)\n", (void*)entry);
421 /* restore the guest's return address */
422 gm->kernel_nesting--;
423 core->vm_regs.r11 = gm->r11_stack_kernel[gm->kernel_nesting];
424 gm->r11_stack_kernel[gm->kernel_nesting] = 0;
427 if (gm->state == NO_PRIV) {
428 PrintDebug(VM_NONE, VCORE_NONE,"GM: Granting border in return request to GM (%s)\n", gm->name);
429 list_for_each_entry_safe(priv, tmp, &(gm->priv_list), priv_node) {
430 if (priv->raise(core, priv->private_data) < 0) {
431 PrintError(VM_NONE, VCORE_NONE,"GM: Problem raising privilege\n");
436 if (gm->entry_rsp && gm->exit_rsp && (gm->entry_rsp > gm->exit_rsp)) {
437 if (v3_hash_buffer((uchar_t*)gm->exit_rsp, gm->entry_rsp - gm->exit_rsp) != gm->stack_hash) {
450 init_gm (struct v3_vm_info * vm, v3_cfg_tree_t * cfg, void ** priv_data)
452 struct v3_guarded_mods * mods = &global_mods;
456 INIT_LIST_HEAD(&(mods->mod_list));
458 if (!(mods->mod_name_table = v3_create_htable(0, mod_name_hash_fn, mod_name_eq_fn))) {
459 PrintError(VM_NONE, VCORE_NONE,"GM: Could not create GM module name hashtable\n");
463 if (!(mods->mod_id_table = v3_create_htable(0, mod_id_hash_fn, mod_id_eq_fn))) {
464 PrintError(VM_NONE, VCORE_NONE,"GM: Could not create GM module ID hashtable\n");
468 if (!(mods->er_hash = v3_create_htable(0, entry_addr_hash_fn, entry_addr_eq_fn))) {
469 PrintError(VM_NONE, VCORE_NONE,"GM: Could not create GM entry point hashtable\n");
473 /* these hypercalls will be invoked with a module id (hash) in rax. */
474 if (v3_register_hypercall(vm, V3_BIN_CALL_HCALL, border_in_call, mods) < 0 ||
475 v3_register_hypercall(vm, V3_BOUT_RET_HCALL, border_out_return, mods) < 0 ||
476 v3_register_hypercall(vm, V3_BIN_RET_HCALL, border_in_return, mods) < 0 ||
477 v3_register_hypercall(vm, V3_BOUT_CALL_HCALL, border_out_call, mods) < 0 ||
478 v3_register_hypercall(vm, V3_GUARD_INIT_HCALL, guard_init, mods) < 0) {
479 PrintError(VM_NONE, VCORE_NONE,"GM: Problem registering hypercalls\n");
488 deinit_gm (struct v3_vm_info * vm, void * priv_data)
490 struct v3_guarded_mods * mods = (struct v3_guarded_mods *)priv_data;
491 struct v3_gm * mod = NULL;
492 struct v3_gm * tmp = NULL;
494 list_for_each_entry_safe(mod, tmp, &(mods->mod_list), mod_node) {
498 v3_free_htable(mods->mod_id_table, 0, 0);
499 v3_free_htable(mods->mod_name_table, 0, 0);
500 v3_free_htable(mods->er_hash, 0, 0);
502 if (v3_remove_hypercall(vm, V3_BIN_CALL_HCALL) < 0 ||
503 v3_remove_hypercall(vm, V3_BOUT_RET_HCALL) < 0 ||
504 v3_remove_hypercall(vm, V3_BIN_RET_HCALL) < 0 ||
505 v3_remove_hypercall(vm, V3_BOUT_CALL_HCALL) < 0 ||
506 v3_remove_hypercall(vm, V3_GUARD_INIT_HCALL) < 0) {
508 PrintError(VM_NONE, VCORE_NONE,"GM: Problem removing GM hypercalls\n");
516 /* returns 0 on error, module id on success */
518 v3_register_gm (void * vm,
523 unsigned int nentries,
529 struct v3_guarded_mods * mods = &global_mods;
536 PrintError(VM_NONE, VCORE_NONE,"Invalid module name\n");
540 if (v3_htable_search(mods->mod_name_table, (addr_t)name)) {
541 PrintError(VM_NONE, VCORE_NONE,"Multiple instances of guarded module (%s)\n", name);
545 /* generate a unique identifier for this module */
548 mod = (struct v3_gm *)V3_Malloc(sizeof(struct v3_gm));
550 PrintError(VM_NONE, VCORE_NONE,"Problem allocating guarded module\n");
554 memset(mod, 0, sizeof(struct v3_gm));
556 V3_Print(VM_NONE, VCORE_NONE, "V3 GM Registration: received name (%s)\n", name);
557 mod->name = V3_Malloc(strlen(name)+1);
559 PrintError(VM_NONE, VCORE_NONE,"Problem allocating space for mod name\n");
563 V3_Print(VM_NONE, VCORE_NONE, "V3 GM Registration: received hash (%s)\n", hash);
564 mod->content_hash = V3_Malloc(strlen(hash)+1);
565 if (!mod->content_hash) {
566 PrintError(VM_NONE, VCORE_NONE,"Problem allocating space for content hash\n");
569 strcpy(mod->name, name);
570 strcpy(mod->content_hash, hash);
572 mod->hcall_offset = hc_off;
573 mod->text_size = size;
574 mod->num_entries = nentries;
575 mod->private_data = private_data;
577 mod->callback_nesting = 0;
578 mod->kernel_nesting = 0;
580 INIT_LIST_HEAD(&(mod->priv_list));
581 if (add_privs_to_list(vm, &(mod->priv_list), nprivs, priv_array) == -1) {
582 PrintError(VM_NONE, VCORE_NONE,"Could not add privileges to guarded module\n");
589 /* setup the valid entry points */
590 INIT_LIST_HEAD(&(mod->er_list));
592 mod->entry_points = V3_Malloc(sizeof(struct v3_entry_point)*mod->num_entries);
594 if (!mod->entry_points) {
595 PrintError(VM_NONE, VCORE_NONE,"Problem allocating entry point array\n");
599 memcpy(mod->entry_points, entry_points, sizeof(struct v3_entry_point)*mod->num_entries);
601 /* entries are added to the list, but can't be hashed yet since we're using
602 * the load address as a key, but we haven't fixed these addresses up yet */
603 for (i = 0; i < mod->num_entries; i++) {
604 tmp = V3_Malloc(strlen(mod->entry_points[i].name)+1);
606 PrintError(VM_NONE, VCORE_NONE,"Problem allocating space for name locally\n");
609 strcpy(tmp, mod->entry_points[i].name);
610 mod->entry_points[i].name = tmp;
611 mod->entry_points[i].gm = mod;
612 list_add(&(mod->entry_points[i].er_node), &(mod->er_list));
615 //v3_htable_insert(mods->mod_id_table, mod_id, (addr_t)mod);
616 // TODO: this will change the content hash of the module code segment, fix!
617 v3_htable_insert(mods->mod_id_table, (addr_t)0xa3aeea3aeebadbad, (addr_t)mod);
618 v3_htable_insert(mods->mod_name_table, (addr_t)name, (addr_t)mod);
619 list_add(&(mod->mod_node), &(mods->mod_list));
624 static struct v3_extension_impl guard_mods_impl = {
625 .name = "guard_mods",
627 .vm_deinit = deinit_gm,
634 register_extension(&guard_mods_impl);