status_t socket_control(net_socket* socket, int32 op, void* data, size_t length) { switch (op) { case FIONBIO: { if (data == NULL) return B_BAD_VALUE; int value; if (is_syscall()) { if (!IS_USER_ADDRESS(data) || user_memcpy(&value, data, sizeof(int)) != B_OK) { return B_BAD_ADDRESS; } } else value = *(int*)data; return socket_setsockopt(socket, SOL_SOCKET, SO_NONBLOCK, &value, sizeof(int)); } case FIONREAD: { if (data == NULL) return B_BAD_VALUE; ssize_t available = socket_read_avail(socket); if (available < B_OK) return available; if (is_syscall()) { if (!IS_USER_ADDRESS(data) || user_memcpy(data, &available, sizeof(ssize_t)) != B_OK) { return B_BAD_ADDRESS; } } else *(ssize_t *)data = available; return B_OK; } case B_SET_BLOCKING_IO: case B_SET_NONBLOCKING_IO: { int value = op == B_SET_NONBLOCKING_IO; return socket_setsockopt(socket, SOL_SOCKET, SO_NONBLOCK, &value, sizeof(int)); } } return socket->first_info->control(socket->first_protocol, LEVEL_DRIVER_IOCTL, op, data, &length); }
static int kern_do_signal(struct pt_regs *regs) { struct k_sigaction ka_copy; siginfo_t info; sigset_t *oldset; int sig, handled_sig = 0; if (test_thread_flag(TIF_RESTORE_SIGMASK)) oldset = ¤t->saved_sigmask; else oldset = ¤t->blocked; while ((sig = get_signal_to_deliver(&info, &ka_copy, regs, NULL)) > 0) { handled_sig = 1; /* Whee! Actually deliver the signal. */ handle_signal(regs, sig, &ka_copy, &info); } /* Did we come from a system call? */ if (!handled_sig && (PT_REGS_SYSCALL_NR(regs) >= 0)) { /* Restart the system call - no handlers present */ switch (PT_REGS_SYSCALL_RET(regs)) { case -ERESTARTNOHAND: case -ERESTARTSYS: case -ERESTARTNOINTR: PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs); PT_REGS_RESTART_SYSCALL(regs); break; case -ERESTART_RESTARTBLOCK: PT_REGS_ORIG_SYSCALL(regs) = __NR_restart_syscall; PT_REGS_RESTART_SYSCALL(regs); break; } } /* * This closes a way to execute a system call on the host. If * you set a breakpoint on a system call instruction and singlestep * from it, the tracing thread used to PTRACE_SINGLESTEP the process * rather than PTRACE_SYSCALL it, allowing the system call to execute * on the host. The tracing thread will check this flag and * PTRACE_SYSCALL if necessary. */ if (current->ptrace & PT_DTRACE) current->thread.singlestep_syscall = is_syscall(PT_REGS_IP(¤t->thread.regs)); /* * if there's no signal to deliver, we just put the saved sigmask * back */ if (!handled_sig) restore_saved_sigmask(); return handled_sig; }
static int kern_do_signal(struct pt_regs *regs) { struct k_sigaction ka_copy; siginfo_t info; sigset_t *oldset; int sig, handled_sig = 0; if (test_thread_flag(TIF_RESTORE_SIGMASK)) oldset = ¤t->saved_sigmask; else oldset = ¤t->blocked; while ((sig = get_signal_to_deliver(&info, &ka_copy, regs, NULL)) > 0) { handled_sig = 1; if (!handle_signal(regs, sig, &ka_copy, &info, oldset)) { if (test_thread_flag(TIF_RESTORE_SIGMASK)) clear_thread_flag(TIF_RESTORE_SIGMASK); break; } } if (!handled_sig && (PT_REGS_SYSCALL_NR(regs) >= 0)) { switch (PT_REGS_SYSCALL_RET(regs)) { case -ERESTARTNOHAND: case -ERESTARTSYS: case -ERESTARTNOINTR: PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs); PT_REGS_RESTART_SYSCALL(regs); break; case -ERESTART_RESTARTBLOCK: PT_REGS_ORIG_SYSCALL(regs) = __NR_restart_syscall; PT_REGS_RESTART_SYSCALL(regs); break; } } if (current->ptrace & PT_DTRACE) current->thread.singlestep_syscall = is_syscall(PT_REGS_IP(¤t->thread.regs)); if (!handled_sig && test_thread_flag(TIF_RESTORE_SIGMASK)) { clear_thread_flag(TIF_RESTORE_SIGMASK); sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); } return handled_sig; }
static int kern_do_signal(struct pt_regs *regs, sigset_t *oldset, int error) { siginfo_t info; struct k_sigaction *ka; int err, sig; if (!oldset) oldset = ¤t->blocked; sig = get_signal_to_deliver(&info, regs, NULL); if(sig == 0) return(0); /* Whee! Actually deliver the signal. */ ka = ¤t->sig->action[sig -1 ]; err = handle_signal(regs, sig, ka, &info, oldset, error); if(!err) return(1); /* Did we come from a system call? */ if(PT_REGS_SYSCALL_NR(regs) >= 0){ /* Restart the system call - no handlers present */ if(PT_REGS_SYSCALL_RET(regs) == -ERESTARTNOHAND || PT_REGS_SYSCALL_RET(regs) == -ERESTARTSYS || PT_REGS_SYSCALL_RET(regs) == -ERESTARTNOINTR){ PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs); PT_REGS_RESTART_SYSCALL(regs); } else if(PT_REGS_SYSCALL_RET(regs) == -ERESTART_RESTARTBLOCK){ PT_REGS_SYSCALL_RET(regs) = __NR_restart_syscall; PT_REGS_RESTART_SYSCALL(regs); } } /* This closes a way to execute a system call on the host. If * you set a breakpoint on a system call instruction and singlestep * from it, the tracing thread used to PTRACE_SINGLESTEP the process * rather than PTRACE_SYSCALL it, allowing the system call to execute * on the host. The tracing thread will check this flag and * PTRACE_SYSCALL if necessary. */ if((current->ptrace & PT_DTRACE) && is_syscall(PT_REGS_IP(¤t->thread.regs))) (void) CHOOSE_MODE(current->thread.mode.tt.singlestep_syscall = 1, 0); return(0); }
void main_loop(int return_on_sigret) { /* main_loop returns only if return_on_sigret == 1 && rt_sigreturn is invoked. see also: rt_sigsuspend */ while (task_run() == 0) { /* dump_instr(); */ /* print_regs(); */ uint64_t exit_reason; vmm_read_vmcs(VMCS_RO_EXIT_REASON, &exit_reason); switch (exit_reason) { case VMX_REASON_VMCALL: printk("reason: vmcall\n"); assert(false); break; case VMX_REASON_EXC_NMI: { /* References: * - Intel SDM 27.2.2, Table 24-15: Information for VM Exits Due to Vectored Events */ uint64_t exc_info; vmm_read_vmcs(VMCS_RO_VMEXIT_IRQ_INFO, &exc_info); int int_type = (exc_info & 0x700) >> 8; switch (int_type) { default: assert(false); case VMCS_EXCTYPE_EXTERNAL_INTERRUPT: case VMCS_EXCTYPE_NONMASKTABLE_INTERRUPT: /* nothing we can do, host os handles it */ continue; case VMCS_EXCTYPE_HARDWARE_EXCEPTION: /* including invalid opcode */ case VMCS_EXCTYPE_SOFTWARE_EXCEPTION: /* including break points, overflows */ break; } int exc_vec = exc_info & 0xff; switch (exc_vec) { case X86_VEC_PF: { /* FIXME */ uint64_t gladdr; vmm_read_vmcs(VMCS_RO_EXIT_QUALIFIC, &gladdr); printk("page fault: caused by guest linear address 0x%llx\n", gladdr); send_signal(getpid(), LINUX_SIGSEGV); } case X86_VEC_UD: { uint64_t instlen, rip; vmm_read_vmcs(VMCS_RO_VMEXIT_INSTR_LEN, &instlen); vmm_read_register(HV_X86_RIP, &rip); if (is_syscall(instlen, rip)) { int r = handle_syscall(); vmm_read_register(HV_X86_RIP, &rip); /* reload rip for execve */ vmm_write_register(HV_X86_RIP, rip + 2); if (return_on_sigret && r < 0) { return; } continue; } else if (is_avx(instlen, rip)) { uint64_t xcr0; vmm_read_register(HV_X86_XCR0, &xcr0); if ((xcr0 & XCR0_AVX_STATE) == 0) { unsigned int eax, ebx, ecx, edx; get_cpuid_count(0x0d, 0x0, &eax, &ebx, &ecx, &edx); if (eax & XCR0_AVX_STATE) { vmm_write_register(HV_X86_XCR0, xcr0 | XCR0_AVX_STATE); continue; } } } /* FIXME */ warnk("invalid opcode! (rip = %p): ", (void *) rip); unsigned char inst[instlen]; if (copy_from_user(inst, rip, instlen)) assert(false); for (uint64_t i = 0; i < instlen; ++i) fprintf(stderr, "%02x ", inst[i] & 0xff); fprintf(stderr, "\n"); send_signal(getpid(), LINUX_SIGILL); } case X86_VEC_DE: case X86_VEC_DB: case X86_VEC_BP: case X86_VEC_OF: case X86_VEC_BR: case X86_VEC_NM: case X86_VEC_DF: case X86_VEC_TS: case X86_VEC_NP: case X86_VEC_SS: case X86_VEC_GP: case X86_VEC_MF: case X86_VEC_AC: case X86_VEC_MC: case X86_VEC_XM: case X86_VEC_VE: case X86_VEC_SX: default: /* FIXME */ warnk("exception thrown: %d\n", exc_vec); uint64_t instlen, rip; vmm_read_vmcs(VMCS_RO_VMEXIT_INSTR_LEN, &instlen); vmm_read_register(HV_X86_RIP, &rip); fprintf(stderr, "inst: \n"); unsigned char inst[instlen]; if (copy_from_user(inst, rip, instlen)) assert(false); for (uint64_t i = 0; i < instlen; ++i) fprintf(stderr, "%02x ", inst[i] & 0xff); fprintf(stderr, "\n"); exit(1); /* TODO */ } break; } case VMX_REASON_EPT_VIOLATION: printk("reason: ept_violation\n"); uint64_t gpaddr; vmm_read_vmcs(VMCS_GUEST_PHYSICAL_ADDRESS, &gpaddr); printk("guest-physical address = 0x%llx\n", gpaddr); uint64_t qual; vmm_read_vmcs(VMCS_RO_EXIT_QUALIFIC, &qual); printk("exit qualification = 0x%llx\n", qual); if (qual & (1 << 7)) { uint64_t gladdr; vmm_read_vmcs(VMCS_RO_GUEST_LIN_ADDR, &gladdr); printk("guest linear address = 0x%llx\n", gladdr); int verify = 0; if (qual & (1 << 0)) { verify = VERIFY_READ; } else if (qual & (1 << 1)) { verify = VERIFY_WRITE; } else if (qual & (1 << 2)) { verify = VERIFY_EXEC; } if (!addr_ok(gladdr, verify)) { printk("page fault: caused by guest linear address 0x%llx\n", gladdr); send_signal(getpid(), LINUX_SIGSEGV); } } else { printk("guest linear address = (unavailable)\n"); } break; case VMX_REASON_CPUID: { uint64_t rax; vmm_read_register(HV_X86_RAX, &rax); unsigned eax, ebx, ecx, edx; __get_cpuid(rax, &eax, &ebx, &ecx, &edx); vmm_write_register(HV_X86_RAX, eax); vmm_write_register(HV_X86_RBX, ebx); vmm_write_register(HV_X86_RCX, ecx); vmm_write_register(HV_X86_RDX, edx); uint64_t rip; vmm_read_register(HV_X86_RIP, &rip); vmm_write_register(HV_X86_RIP, rip + 2); break; } case VMX_REASON_IRQ: { break; } case VMX_REASON_HLT: { break; } default: // See: Intel® 64 and IA-32 Architectures Software Developer’s Manual // Volume 3B: System Programming Guide, Part 2 // Order Number: 253669-033US // December 2009 // Section 21.9 VM-EXIT INFORMATION FIELDS // 21.9.1 Basic VM-Exit information // Exit reason if (exit_reason & (1<<31)) { exit_reason ^= (1<<31); printk("VM-entry failure exit reason: %llx\n", exit_reason); } else printk("other exit reason: %llx\n", exit_reason); if (exit_reason & VMX_REASON_VMENTRY_GUEST) check_vm_entry(); vmm_read_vmcs(VMCS_RO_EXIT_QUALIFIC, &qual); printk("exit qualification: %llx\n", qual); } } __builtin_unreachable(); }