/* delete a context */ void context_free(context_t *cont) { /* we don't need to lock because this is called only when we free the context anyway */ while (cont->funcs.next != NULL) { context_free_function(LIST_TO_STRUCT(function_t, list, cont->funcs.next)); } while (cont->anonym.next != NULL) { context_free_function(LIST_TO_STRUCT(function_t, list, cont->anonym.next)); } memory_free(cont); }
/* write callback */ static ssize_t kplugs_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos) { kplugs_command_t *cmd = NULL; context_t *file_cont = NULL; context_t *cont = NULL; bytecode_t *code = NULL; function_t *func = NULL; exception_t excep; stack_t stack; word iter, arg; word args; byte little_endian; byte func_name[MAX_FUNC_NAME + 1]; int err = 0; #ifdef __LITTLE_ENDIAN little_endian = 1; #else little_endian = 0; #endif /* get the user's command */ cmd = (kplugs_command_t *)buf; file_cont = (context_t *)filp->private_data; if (count < sizeof(byte) * 3) { /* three bytes of header */ return create_error(file_cont, -ERROR_PARAM); } if (cmd->word_size != sizeof(word) || cmd->l_endian != little_endian) { return create_error(file_cont, -ERROR_ARCH); } if (cmd->version_major != VERSION_MAJOR || cmd->version_minor != VERSION_MINOR) { return create_error(file_cont, -ERROR_VERSION); } if (count != sizeof(kplugs_command_t)) { return create_error(file_cont, -ERROR_PARAM); } cont = cmd->is_global ? GLOBAL_CONTEXT : file_cont; switch (cmd->type) { case KPLUGS_LOAD: /* load a new function */ if (cmd->len2 != 0 || cmd->ptr2 != NULL) { return create_error(file_cont, -ERROR_PARAM); } code = memory_alloc(cmd->len1); if (NULL == code) { ERROR(create_error(file_cont, -ERROR_MEM)); } err = memory_copy_from_outside(code, cmd->uptr1, cmd->len1); if (err < 0) { err = create_error(file_cont, err); goto clean; } /* create the function */ err = function_create(code, cmd->len1, &func); if (err < 0) { err = create_error(file_cont, err); goto clean; } err = context_add_function(cont, func); if (err < 0) { err = create_error(file_cont, err); goto clean; } /* return the function's address */ context_create_reply(file_cont, (word)&func->func_code, NULL); return count; case KPLUGS_UNLOAD: /* unload a function with a name */ if (NULL != cmd->uptr2) { return create_error(file_cont, -ERROR_PARAM); } case KPLUGS_EXECUTE: /* execute (and unload) a function with a name */ if (cmd->len1 > MAX_FUNC_NAME || (cmd->len2 % sizeof(word)) != 0) { return create_error(file_cont, -ERROR_PARAM); } err = memory_copy_from_outside(func_name, cmd->uptr1, cmd->len1); if (err < 0) { return create_error(file_cont, err); } func_name[cmd->len1] = '\0'; /* find the function */ if (cmd->type == KPLUGS_UNLOAD) { func = context_find_function(cont, func_name); if (NULL == func) { return create_error(file_cont, -ERROR_UFUNC); } /* delete the function */ context_free_function(func); err = (int)count; goto clean; } if (!cmd->is_global) { func = context_find_function(file_cont, func_name); } if (NULL == func) { func = context_find_function(GLOBAL_CONTEXT, func_name); if (NULL == func) { return create_error(file_cont, -ERROR_UFUNC); } } goto execute_func; case KPLUGS_UNLOAD_ANONYMOUS: /* unload an anonymous function */ if (NULL != cmd->uptr2 || cmd->len1 || cmd->len2) { return create_error(file_cont, -ERROR_PARAM); } func = context_find_anonymous(cont, cmd->ptr1); if (NULL == func) { return create_error(file_cont, -ERROR_UFUNC); } /* delete the function */ context_free_function(func); err = (int)count; goto clean; break; case KPLUGS_EXECUTE_ANONYMOUS: /* execute (and unload) an anonymous function */ if (cmd->len1 || (cmd->len2 % sizeof(word)) != 0) { return create_error(file_cont, -ERROR_PARAM); } /* find the function */ if (!cmd->is_global) { func = context_find_anonymous(file_cont, cmd->ptr1); } if (NULL == func) { func = context_find_anonymous(GLOBAL_CONTEXT, cmd->ptr1); if (NULL == func) { return create_error(file_cont, -ERROR_UFUNC); } } execute_func: /* do the execution of a function: */ args = cmd->len2 / sizeof(word); if (args > func->num_maxargs || args < func->num_minargs) { ERROR_CLEAN(create_error(file_cont, -ERROR_ARGS)); } err = stack_alloc(&stack, sizeof(word), CALL_STACK_SIZE); if (err < 0) { err = create_error(file_cont, err); goto clean; } /* push the arguments to a stack */ for (iter = 0; iter < args; ++iter) { err = memory_copy_from_outside(&arg, cmd->ptr2 + (iter * sizeof(word)), sizeof(arg)); if (err < 0) { stack_free(&stack); err = create_error(file_cont, err); goto clean; } if (NULL == stack_push(&stack, &arg)) { stack_free(&stack); ERROR_CLEAN(create_error(file_cont, -ERROR_MEM)); } } /* execute the function and create an answer */ arg = vm_run_function(func, &stack, &excep); stack_free(&stack); if (excep.had_exception) { err = -EINVAL; /* it dosen't really matter which error. the value of the error will be taken from the answer */ arg = excep.value; } else { err = (int)count; } context_create_reply(file_cont, arg, &excep); goto clean; case KPLUGS_GET_LAST_EXCEPTION: if (NULL != cmd->uptr2 || cmd->len1 < sizeof(exception_t) || cmd->len2) { ERROR(create_error(file_cont, -ERROR_PARAM)); } err = context_get_last_exception(file_cont, (exception_t *)cmd->ptr1); if (err < 0) { return create_error(file_cont, err); } return count; default: return create_error(file_cont, -ERROR_PARAM); } clean: if (NULL != func) { function_put(func); } else if (NULL != code) { memory_free(code); } return err; }