From 3f9d0c77893034de85f27e2869e926f99b04a660 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Mon, 29 Jan 2024 14:26:33 -0500 Subject: [PATCH] DevTools: Remove UserspaceEmulator It hasn't been built since we dropped i386 support. When we want to bring it back, we can get it back from source control. No behavior change. --- .../DevTools/UserspaceEmulator/CMakeLists.txt | 26 - .../DevTools/UserspaceEmulator/Emulator.cpp | 789 ---- .../DevTools/UserspaceEmulator/Emulator.h | 307 -- .../UserspaceEmulator/EmulatorControl.h | 25 - .../UserspaceEmulator/Emulator_syscalls.cpp | 1657 --------- .../UserspaceEmulator/MallocTracer.cpp | 430 --- .../DevTools/UserspaceEmulator/MallocTracer.h | 110 - .../DevTools/UserspaceEmulator/MmapRegion.cpp | 327 -- .../DevTools/UserspaceEmulator/MmapRegion.h | 81 - Userland/DevTools/UserspaceEmulator/Range.cpp | 25 - Userland/DevTools/UserspaceEmulator/Range.h | 75 - .../UserspaceEmulator/RangeAllocator.cpp | 191 - .../UserspaceEmulator/RangeAllocator.h | 38 - .../DevTools/UserspaceEmulator/Region.cpp | 19 - Userland/DevTools/UserspaceEmulator/Region.h | 88 - Userland/DevTools/UserspaceEmulator/Report.h | 22 - .../UserspaceEmulator/SimpleRegion.cpp | 129 - .../DevTools/UserspaceEmulator/SimpleRegion.h | 42 - .../DevTools/UserspaceEmulator/SoftCPU.cpp | 3234 ----------------- Userland/DevTools/UserspaceEmulator/SoftCPU.h | 1416 -------- .../DevTools/UserspaceEmulator/SoftFPU.cpp | 1770 --------- Userland/DevTools/UserspaceEmulator/SoftFPU.h | 587 --- .../DevTools/UserspaceEmulator/SoftMMU.cpp | 384 -- Userland/DevTools/UserspaceEmulator/SoftMMU.h | 149 - .../DevTools/UserspaceEmulator/SoftVPU.cpp | 799 ---- Userland/DevTools/UserspaceEmulator/SoftVPU.h | 154 - .../UserspaceEmulator/ValueWithShadow.h | 194 - Userland/DevTools/UserspaceEmulator/main.cpp | 121 - 28 files changed, 13189 deletions(-) delete mode 100644 Userland/DevTools/UserspaceEmulator/CMakeLists.txt delete mode 100644 Userland/DevTools/UserspaceEmulator/Emulator.cpp delete mode 100644 Userland/DevTools/UserspaceEmulator/Emulator.h delete mode 100644 Userland/DevTools/UserspaceEmulator/EmulatorControl.h delete mode 100644 Userland/DevTools/UserspaceEmulator/Emulator_syscalls.cpp delete mode 100644 Userland/DevTools/UserspaceEmulator/MallocTracer.cpp delete mode 100644 Userland/DevTools/UserspaceEmulator/MallocTracer.h delete mode 100644 Userland/DevTools/UserspaceEmulator/MmapRegion.cpp delete mode 100644 Userland/DevTools/UserspaceEmulator/MmapRegion.h delete mode 100644 Userland/DevTools/UserspaceEmulator/Range.cpp delete mode 100644 Userland/DevTools/UserspaceEmulator/Range.h delete mode 100644 Userland/DevTools/UserspaceEmulator/RangeAllocator.cpp delete mode 100644 Userland/DevTools/UserspaceEmulator/RangeAllocator.h delete mode 100644 Userland/DevTools/UserspaceEmulator/Region.cpp delete mode 100644 Userland/DevTools/UserspaceEmulator/Region.h delete mode 100644 Userland/DevTools/UserspaceEmulator/Report.h delete mode 100644 Userland/DevTools/UserspaceEmulator/SimpleRegion.cpp delete mode 100644 Userland/DevTools/UserspaceEmulator/SimpleRegion.h delete mode 100644 Userland/DevTools/UserspaceEmulator/SoftCPU.cpp delete mode 100644 Userland/DevTools/UserspaceEmulator/SoftCPU.h delete mode 100644 Userland/DevTools/UserspaceEmulator/SoftFPU.cpp delete mode 100644 Userland/DevTools/UserspaceEmulator/SoftFPU.h delete mode 100644 Userland/DevTools/UserspaceEmulator/SoftMMU.cpp delete mode 100644 Userland/DevTools/UserspaceEmulator/SoftMMU.h delete mode 100644 Userland/DevTools/UserspaceEmulator/SoftVPU.cpp delete mode 100644 Userland/DevTools/UserspaceEmulator/SoftVPU.h delete mode 100644 Userland/DevTools/UserspaceEmulator/ValueWithShadow.h delete mode 100644 Userland/DevTools/UserspaceEmulator/main.cpp diff --git a/Userland/DevTools/UserspaceEmulator/CMakeLists.txt b/Userland/DevTools/UserspaceEmulator/CMakeLists.txt deleted file mode 100644 index 7135ba09af..0000000000 --- a/Userland/DevTools/UserspaceEmulator/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -serenity_component( - UserspaceEmulator - RECOMMENDED - TARGETS UserspaceEmulator -) - -set(SOURCES - Emulator.cpp - Emulator_syscalls.cpp - MallocTracer.cpp - MmapRegion.cpp - Range.cpp - RangeAllocator.cpp - Region.cpp - SimpleRegion.cpp - SoftCPU.cpp - SoftFPU.cpp - SoftMMU.cpp - SoftVPU.cpp - main.cpp -) - -add_compile_options(-mmmx -Wno-psabi -frounding-math) - -serenity_bin(UserspaceEmulator) -target_link_libraries(UserspaceEmulator PRIVATE LibX86 LibDebug LibCore LibFileSystem LibLine LibSystem) diff --git a/Userland/DevTools/UserspaceEmulator/Emulator.cpp b/Userland/DevTools/UserspaceEmulator/Emulator.cpp deleted file mode 100644 index b6746965a4..0000000000 --- a/Userland/DevTools/UserspaceEmulator/Emulator.cpp +++ /dev/null @@ -1,789 +0,0 @@ -/* - * Copyright (c) 2020-2021, Andreas Kling - * Copyright (c) 2021, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Emulator.h" -#include "MmapRegion.h" -#include "SimpleRegion.h" -#include "SoftCPU.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(AK_COMPILER_GCC) -# pragma GCC optimize("O3") -#endif - -namespace UserspaceEmulator { - -static constexpr u32 stack_location = 0x10000000; -static constexpr size_t stack_size = 1 * MiB; - -static constexpr u32 signal_trampoline_location = 0xb0000000; - -static Emulator* s_the; - -Emulator& Emulator::the() -{ - VERIFY(s_the); - return *s_the; -} - -Emulator::Emulator(ByteString const& executable_path, Vector const& arguments, Vector const& environment) - : m_executable_path(executable_path) - , m_arguments(arguments) - , m_environment(environment) - , m_mmu(*this) - , m_cpu(make(*this)) - , m_editor(Line::Editor::construct()) -{ - m_malloc_tracer = make(*this); - - static constexpr FlatPtr userspace_range_ceiling = 0xbe000000; -#ifdef UE_ASLR - static constexpr FlatPtr page_mask = 0xfffff000u; - size_t random_offset = (get_random() % 32 * MiB) & page_mask; - FlatPtr base = userspace_range_base + random_offset; -#else - FlatPtr base = userspace_range_base; -#endif - - m_range_allocator.initialize_with_range(VirtualAddress(base), userspace_range_ceiling - base); - - VERIFY(!s_the); - s_the = this; - // setup_stack(arguments, environment); - register_signal_handlers(); - setup_signal_trampoline(); -} - -Vector Emulator::generate_auxiliary_vector(FlatPtr load_base, FlatPtr entry_eip, ByteString const& executable_path, int executable_fd) const -{ - // FIXME: This is not fully compatible with the auxiliary vector the kernel generates, this is just the bare - // minimum to get the loader going. - Vector auxv; - // PHDR/EXECFD - // PH* - auxv.append({ ELF::AuxiliaryValue::PageSize, PAGE_SIZE }); - auxv.append({ ELF::AuxiliaryValue::BaseAddress, (void*)load_base }); - - auxv.append({ ELF::AuxiliaryValue::Entry, (void*)entry_eip }); - - // FIXME: Don't hard code this? We might support other platforms later.. (e.g. x86_64) - auxv.append({ ELF::AuxiliaryValue::Platform, "i386"sv }); - - auxv.append({ ELF::AuxiliaryValue::ExecFilename, executable_path }); - - auxv.append({ ELF::AuxiliaryValue::ExecFileDescriptor, executable_fd }); - - auxv.append({ ELF::AuxiliaryValue::Null, 0L }); - return auxv; -} - -void Emulator::setup_stack(Vector aux_vector) -{ - m_range_allocator.reserve_user_range(VirtualAddress(stack_location), stack_size); - auto stack_region = make(stack_location, stack_size); - stack_region->set_stack(true); - m_mmu.add_region(move(stack_region)); - m_cpu->set_esp(shadow_wrap_as_initialized(stack_location + stack_size)); - - Vector argv_entries; - - for (auto const& argument : m_arguments) { - m_cpu->push_string(argument); - argv_entries.append(m_cpu->esp().value()); - } - - Vector env_entries; - - for (auto const& variable : m_environment) { - m_cpu->push_string(variable.view()); - env_entries.append(m_cpu->esp().value()); - } - - for (auto& auxv : aux_vector) { - if (!auxv.optional_string.is_empty()) { - m_cpu->push_string(auxv.optional_string); - auxv.auxv.a_un.a_ptr = (void*)m_cpu->esp().value(); - } - } - - for (ssize_t i = aux_vector.size() - 1; i >= 0; --i) { - auto& value = aux_vector[i].auxv; - m_cpu->push_buffer((u8 const*)&value, sizeof(value)); - } - - m_cpu->push32(shadow_wrap_as_initialized(0)); // char** envp = { envv_entries..., nullptr } - for (ssize_t i = env_entries.size() - 1; i >= 0; --i) - m_cpu->push32(shadow_wrap_as_initialized(env_entries[i])); - u32 envp = m_cpu->esp().value(); - - m_cpu->push32(shadow_wrap_as_initialized(0)); // char** argv = { argv_entries..., nullptr } - for (ssize_t i = argv_entries.size() - 1; i >= 0; --i) - m_cpu->push32(shadow_wrap_as_initialized(argv_entries[i])); - u32 argv = m_cpu->esp().value(); - - while ((m_cpu->esp().value() + 4) % 16 != 0) - m_cpu->push32(shadow_wrap_as_initialized(0)); // (alignment) - - u32 argc = argv_entries.size(); - m_cpu->push32(shadow_wrap_as_initialized(envp)); - m_cpu->push32(shadow_wrap_as_initialized(argv)); - m_cpu->push32(shadow_wrap_as_initialized(argc)); - - VERIFY(m_cpu->esp().value() % 16 == 0); -} - -bool Emulator::load_elf() -{ - auto file_or_error = Core::MappedFile::map(m_executable_path); - if (file_or_error.is_error()) { - reportln("Unable to map {}: {}"sv, m_executable_path, file_or_error.error()); - return false; - } - - auto elf_image_data = file_or_error.value()->bytes(); - ELF::Image executable_elf(elf_image_data); - - if (!executable_elf.is_dynamic()) { - // FIXME: Support static objects - VERIFY_NOT_REACHED(); - } - - StringBuilder interpreter_path_builder; - auto result_or_error = ELF::validate_program_headers(*(Elf32_Ehdr const*)elf_image_data.data(), elf_image_data.size(), elf_image_data, &interpreter_path_builder); - if (result_or_error.is_error() || !result_or_error.value()) { - reportln("failed to validate ELF file"sv); - return false; - } - auto interpreter_path = interpreter_path_builder.string_view(); - - VERIFY(!interpreter_path.is_null()); - dbgln("interpreter: {}", interpreter_path); - - auto interpreter_file_or_error = Core::MappedFile::map(interpreter_path); - VERIFY(!interpreter_file_or_error.is_error()); - auto interpreter_image_data = interpreter_file_or_error.value()->bytes(); - ELF::Image interpreter_image(interpreter_image_data); - - constexpr FlatPtr interpreter_load_offset = 0x08000000; - interpreter_image.for_each_program_header([&](ELF::Image::ProgramHeader const& program_header) { - // Loader is not allowed to have its own TLS regions - VERIFY(program_header.type() != PT_TLS); - - if (program_header.type() == PT_LOAD) { - auto start_address = program_header.vaddr().offset(interpreter_load_offset); - m_range_allocator.reserve_user_range(start_address, program_header.size_in_memory()); - auto region = make(start_address.get(), program_header.size_in_memory()); - if (program_header.is_executable() && !program_header.is_writable()) - region->set_text(true); - memcpy(region->data(), program_header.raw_data(), program_header.size_in_image()); - memset(region->shadow_data(), 0x01, program_header.size_in_memory()); - if (program_header.is_executable()) { - m_loader_text_base = region->base(); - m_loader_text_size = region->size(); - } - mmu().add_region(move(region)); - return IterationDecision::Continue; - } - - return IterationDecision::Continue; - }); - - auto entry_point = interpreter_image.entry().offset(interpreter_load_offset).get(); - m_cpu->set_eip(entry_point); - - // executable_fd will be used by the loader - int executable_fd = open(m_executable_path.characters(), O_RDONLY); - if (executable_fd < 0) - return false; - - auto aux_vector = generate_auxiliary_vector(interpreter_load_offset, entry_point, m_executable_path, executable_fd); - setup_stack(move(aux_vector)); - - return true; -} - -int Emulator::exec() -{ - // X86::ELFSymbolProvider symbol_provider(*m_elf); - X86::ELFSymbolProvider* symbol_provider = nullptr; - - constexpr bool trace = false; - - size_t instructions_until_next_profile_dump = profile_instruction_interval(); - if (is_profiling() && m_loader_text_size.has_value()) - emit_profile_event(profile_stream(), "mmap"sv, ByteString::formatted(R"("ptr": {}, "size": {}, "name": "/usr/lib/Loader.so")", *m_loader_text_base, *m_loader_text_size)); - - while (!m_shutdown) { - if (m_steps_til_pause) [[likely]] { - m_cpu->save_base_eip(); - auto insn = X86::Instruction::from_stream(*m_cpu, X86::ProcessorMode::Protected); - // Exec cycle - if constexpr (trace) { - outln("{:p} \033[33;1m{}\033[0m", m_cpu->base_eip(), insn.to_byte_string(m_cpu->base_eip(), symbol_provider)); - } - - (m_cpu->*insn.handler())(insn); - - if (is_profiling()) { - if (instructions_until_next_profile_dump == 0) { - instructions_until_next_profile_dump = profile_instruction_interval(); - emit_profile_sample(profile_stream()); - } else { - --instructions_until_next_profile_dump; - } - } - - if constexpr (trace) { - m_cpu->dump(); - } - - if (m_pending_signals) [[unlikely]] { - dispatch_one_pending_signal(); - } - if (m_steps_til_pause > 0) - m_steps_til_pause--; - - } else { - handle_repl(); - } - } - - if (auto* tracer = malloc_tracer()) - tracer->dump_leak_report(); - - return m_exit_status; -} - -void Emulator::send_signal(int signal) -{ - SignalInfo info { - // FIXME: Fill this in somehow - .signal_info = { - .si_signo = signal, - .si_code = SI_USER, - .si_errno = 0, - .si_pid = getpid(), - .si_uid = geteuid(), - .si_addr = 0, - .si_status = 0, - .si_band = 0, - .si_value = { - .sival_int = 0, - }, - }, - .context = {}, - }; - did_receive_signal(signal, info, true); -} - -void Emulator::handle_repl() -{ - // Console interface - // FIXME: Previous Instruction**s** - // FIXME: Function names (base, call, jump) - auto saved_eip = m_cpu->eip(); - m_cpu->save_base_eip(); - auto insn = X86::Instruction::from_stream(*m_cpu, X86::ProcessorMode::Protected); - // FIXME: This does not respect inlining - // another way of getting the current function is at need - if (auto symbol = symbol_at(m_cpu->base_eip()); symbol.has_value()) { - outln("[{}]: {}", symbol->lib_name, symbol->symbol); - } - - outln("==> {}", create_instruction_line(m_cpu->base_eip(), insn)); - for (int i = 0; i < 7; ++i) { - m_cpu->save_base_eip(); - insn = X86::Instruction::from_stream(*m_cpu, X86::ProcessorMode::Protected); - outln(" {}", create_instruction_line(m_cpu->base_eip(), insn)); - } - // We don't want to increase EIP here, we just want the instructions - m_cpu->set_eip(saved_eip); - - outln(); - m_cpu->dump(); - outln(); - - auto line_or_error = m_editor->get_line(">> "); - if (line_or_error.is_error()) - return; - - // FIXME: find a way to find a global symbol-address for run-until-call - auto help = [] { - outln("Available commands:"); - outln("continue, c: Continue the execution"); - outln("quit, q: Quit the execution (this will \"kill\" the program and run checks)"); - outln("ret, r: Run until function returns"); - outln("step, s [count]: Execute [count] instructions and then halt"); - outln("signal, sig [number:int], send signal to emulated program (default: sigint:2)"); - }; - auto line = line_or_error.release_value(); - if (line.is_empty()) { - if (m_editor->history().is_empty()) { - help(); - return; - } - line = m_editor->history().last().entry; - } - - auto parts = line.split_view(' '); - m_editor->add_to_history(line); - - if (parts[0].is_one_of("s"sv, "step"sv)) { - if (parts.size() == 1) { - m_steps_til_pause = 1; - return; - } - auto number = AK::StringUtils::convert_to_int(parts[1]); - if (!number.has_value()) { - outln("usage \"step [count]\"\n\tcount can't be less than 1"); - return; - } - m_steps_til_pause = number.value(); - } else if (parts[0].is_one_of("c"sv, "continue"sv)) { - m_steps_til_pause = -1; - } else if (parts[0].is_one_of("r"sv, "ret"sv)) { - m_run_til_return = true; - // FIXME: This may be uninitialized - m_watched_addr = m_mmu.read32({ 0x23, m_cpu->ebp().value() + 4 }).value(); - m_steps_til_pause = -1; - } else if (parts[0].is_one_of("q"sv, "quit"sv)) { - m_shutdown = true; - } else if (parts[0].is_one_of("sig"sv, "signal"sv)) { - if (parts.size() == 1) { - send_signal(SIGINT); - return; - } - if (parts.size() == 2) { - auto number = AK::StringUtils::convert_to_int(parts[1]); - if (number.has_value()) { - send_signal(*number); - return; - } - } - outln("Usage: sig [signal:int], default: SINGINT:2"); - } else { - help(); - } -} - -Vector Emulator::raw_backtrace() -{ - Vector backtrace; - backtrace.append(m_cpu->base_eip()); - - // FIXME: Maybe do something if the backtrace has uninitialized data in the frame chain. - - u32 frame_ptr = m_cpu->ebp().value(); - while (frame_ptr) { - u32 ret_ptr = m_mmu.read32({ 0x23, frame_ptr + 4 }).value(); - if (!ret_ptr) - break; - backtrace.append(ret_ptr); - frame_ptr = m_mmu.read32({ 0x23, frame_ptr }).value(); - } - return backtrace; -} - -MmapRegion const* Emulator::find_text_region(FlatPtr address) -{ - MmapRegion const* matching_region = nullptr; - mmu().for_each_region_of_type([&](auto& region) { - if (!(region.is_executable() && address >= region.base() && address < region.base() + region.size())) - return IterationDecision::Continue; - matching_region = ®ion; - return IterationDecision::Break; - }); - return matching_region; -} - -// FIXME: This interface isn't the nicest -MmapRegion const* Emulator::load_library_from_address(FlatPtr address) -{ - auto const* region = find_text_region(address); - if (!region) - return {}; - - ByteString lib_name = region->lib_name(); - if (lib_name.is_null()) - return {}; - - ByteString lib_path = lib_name; - if (FileSystem::looks_like_shared_library(lib_name)) - lib_path = ByteString::formatted("/usr/lib/{}", lib_path); - - if (!m_dynamic_library_cache.contains(lib_path)) { - auto file_or_error = Core::MappedFile::map(lib_path); - if (file_or_error.is_error()) - return {}; - - auto image = make(file_or_error.value()->bytes()); - auto debug_info = make(*image); - m_dynamic_library_cache.set(lib_path, CachedELF { file_or_error.release_value(), move(debug_info), move(image) }); - } - return region; -} - -MmapRegion const* Emulator::first_region_for_object(StringView name) -{ - MmapRegion* ret = nullptr; - mmu().for_each_region_of_type([&](auto& region) { - if (region.lib_name() == name) { - ret = ®ion; - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - return ret; -} - -// FIXME: This disregards function inlining. -Optional Emulator::symbol_at(FlatPtr address) -{ - auto const* address_region = load_library_from_address(address); - if (!address_region) - return {}; - auto lib_name = address_region->lib_name(); - auto const* first_region = (lib_name.is_null() || lib_name.is_empty()) ? address_region : first_region_for_object(lib_name); - VERIFY(first_region); - auto lib_path = lib_name; - if (FileSystem::looks_like_shared_library(lib_name)) { - lib_path = ByteString::formatted("/usr/lib/{}", lib_name); - } - - auto it = m_dynamic_library_cache.find(lib_path); - auto const& elf = it->value.debug_info->elf(); - auto symbol = elf.symbolicate(address - first_region->base()); - - auto source_position = it->value.debug_info->get_source_position(address - first_region->base()); - return { { lib_name, symbol, source_position } }; -} - -ByteString Emulator::create_backtrace_line(FlatPtr address) -{ - auto maybe_symbol = symbol_at(address); - if (!maybe_symbol.has_value()) { - return ByteString::formatted("=={}== {:p}", getpid(), address); - } - if (!maybe_symbol->source_position.has_value()) { - return ByteString::formatted("=={}== {:p} [{}]: {}", getpid(), address, maybe_symbol->lib_name, maybe_symbol->symbol); - } - - auto const& source_position = maybe_symbol->source_position.value(); - return ByteString::formatted("=={}== {:p} [{}]: {} (\e[34;1m{}\e[0m:{})", getpid(), address, maybe_symbol->lib_name, maybe_symbol->symbol, LexicalPath::basename(source_position.file_path), source_position.line_number); -} - -void Emulator::dump_backtrace(Vector const& backtrace) -{ - for (auto const& address : backtrace) { - reportln("{}"sv, create_backtrace_line(address)); - } -} - -void Emulator::dump_backtrace() -{ - dump_backtrace(raw_backtrace()); -} - -void Emulator::emit_profile_sample(Stream& output) -{ - if (!is_in_region_of_interest()) - return; - StringBuilder builder; - timeval tv {}; - gettimeofday(&tv, nullptr); - builder.appendff(R"~(, {{"type": "sample", "pid": {}, "tid": {}, "timestamp": {}, "lost_samples": 0, "stack": [)~", getpid(), gettid(), tv.tv_sec * 1000 + tv.tv_usec / 1000); - builder.join(',', raw_backtrace()); - builder.append("]}\n"sv); - output.write_until_depleted(builder.string_view().bytes()).release_value_but_fixme_should_propagate_errors(); -} - -void Emulator::emit_profile_event(Stream& output, StringView event_name, ByteString const& contents) -{ - StringBuilder builder; - timeval tv {}; - gettimeofday(&tv, nullptr); - builder.appendff(R"~(, {{"type": "{}", "pid": {}, "tid": {}, "timestamp": {}, "lost_samples": 0, "stack": [], {}}})~", event_name, getpid(), gettid(), tv.tv_sec * 1000 + tv.tv_usec / 1000, contents); - builder.append('\n'); - output.write_until_depleted(builder.string_view().bytes()).release_value_but_fixme_should_propagate_errors(); -} - -ByteString Emulator::create_instruction_line(FlatPtr address, X86::Instruction const& insn) -{ - auto symbol = symbol_at(address); - if (!symbol.has_value() || !symbol->source_position.has_value()) - return ByteString::formatted("{:p}: {}", address, insn.to_byte_string(address)); - - return ByteString::formatted("{:p}: {} \e[34;1m{}\e[0m:{}", address, insn.to_byte_string(address), LexicalPath::basename(symbol->source_position->file_path), symbol->source_position.value().line_number); -} - -static void emulator_signal_handler(int signum, siginfo_t* signal_info, void* context) -{ - Emulator::the().did_receive_signal(signum, { *signal_info, *reinterpret_cast(context) }); -} - -void Emulator::register_signal_handlers() -{ - struct sigaction action { - .sa_sigaction = emulator_signal_handler, - .sa_mask = 0, - .sa_flags = SA_SIGINFO, - }; - sigemptyset(&action.sa_mask); - - for (int signum = 0; signum < NSIG; ++signum) - sigaction(signum, &action, nullptr); -} - -enum class DefaultSignalAction { - Terminate, - Ignore, - DumpCore, - Stop, - Continue, -}; - -static DefaultSignalAction default_signal_action(int signal) -{ - VERIFY(signal && signal < NSIG); - - switch (signal) { - case SIGHUP: - case SIGINT: - case SIGKILL: - case SIGPIPE: - case SIGALRM: - case SIGUSR1: - case SIGUSR2: - case SIGVTALRM: - case SIGSTKFLT: - case SIGIO: - case SIGPROF: - case SIGTERM: - return DefaultSignalAction::Terminate; - case SIGCHLD: - case SIGURG: - case SIGWINCH: - case SIGINFO: - return DefaultSignalAction::Ignore; - case SIGQUIT: - case SIGILL: - case SIGTRAP: - case SIGABRT: - case SIGBUS: - case SIGFPE: - case SIGSEGV: - case SIGXCPU: - case SIGXFSZ: - case SIGSYS: - return DefaultSignalAction::DumpCore; - case SIGCONT: - return DefaultSignalAction::Continue; - case SIGSTOP: - case SIGTSTP: - case SIGTTIN: - case SIGTTOU: - return DefaultSignalAction::Stop; - } - VERIFY_NOT_REACHED(); -} - -void Emulator::dispatch_one_pending_signal() -{ - int signum = -1; - for (signum = 1; signum < NSIG; ++signum) { - int mask = 1 << signum; - if (m_pending_signals & mask) - break; - } - VERIFY(signum != -1); - m_pending_signals &= ~(1 << signum); - - if (((1 << (signum - 1)) & m_signal_mask) != 0) - return; - - auto& handler = m_signal_handler[signum]; - - if (handler.handler == 0) { - // SIG_DFL - auto action = default_signal_action(signum); - if (action == DefaultSignalAction::Ignore) - return; - reportln("\n=={}== Got signal {} ({}), no handler registered"sv, getpid(), signum, strsignal(signum)); - dump_backtrace(); - m_shutdown = true; - return; - } - - if (handler.handler == 1) { - // SIG_IGN - return; - } - - reportln("\n=={}== Got signal {} ({}), handler at {:p}"sv, getpid(), signum, strsignal(signum), handler.handler); - - auto old_esp = m_cpu->esp().value(); - - auto signal_info = m_signal_data[signum]; - signal_info.context.uc_sigmask = m_signal_mask; - signal_info.context.uc_stack = { - .ss_sp = bit_cast(old_esp), - .ss_flags = 0, - .ss_size = 0, - }; - signal_info.context.uc_mcontext = __mcontext { - .eax = m_cpu->eax().value(), - .ecx = m_cpu->ecx().value(), - .edx = m_cpu->edx().value(), - .ebx = m_cpu->ebx().value(), - .esp = m_cpu->esp().value(), - .ebp = m_cpu->ebp().value(), - .esi = m_cpu->esi().value(), - .edi = m_cpu->edi().value(), - .eip = m_cpu->eip(), - .eflags = m_cpu->eflags(), - .cs = m_cpu->cs(), - .ss = m_cpu->ss(), - .ds = m_cpu->ds(), - .es = m_cpu->es(), - // ??? - .fs = 0, - .gs = 0, - }; - - // Align the stack to 16 bytes. - // Note that we push some elements on to the stack before the return address, - // so we need to account for this here. - constexpr static FlatPtr elements_pushed_on_stack_before_handler_address = 1; // one slot for a saved register - FlatPtr const extra_bytes_pushed_on_stack_before_handler_address = sizeof(ucontext_t) + sizeof(siginfo_t); - FlatPtr stack_alignment = (old_esp - elements_pushed_on_stack_before_handler_address * sizeof(FlatPtr) + extra_bytes_pushed_on_stack_before_handler_address) % 16; - // Also note that we have to skip the thread red-zone (if needed), so do that here. - old_esp -= stack_alignment; - - m_cpu->set_esp(shadow_wrap_with_taint_from(old_esp, m_cpu->esp())); - - m_cpu->push32(shadow_wrap_as_initialized(0u)); // syscall return value slot - - m_cpu->push_buffer(bit_cast(&signal_info.context), sizeof(ucontext_t)); - auto pointer_to_ucontext = m_cpu->esp().value(); - - m_cpu->push_buffer(bit_cast(&signal_info.signal_info), sizeof(siginfo_t)); - auto pointer_to_signal_info = m_cpu->esp().value(); - - // FPU state, leave a 512-byte gap. FIXME: Fill this in. - m_cpu->set_esp({ m_cpu->esp().value() - 512, m_cpu->esp().shadow() }); - - // Leave one empty slot to align the stack for a handler call. - m_cpu->push32(shadow_wrap_as_initialized(0u)); - m_cpu->push32(shadow_wrap_as_initialized(pointer_to_ucontext)); - m_cpu->push32(shadow_wrap_as_initialized(pointer_to_signal_info)); - m_cpu->push32(shadow_wrap_as_initialized(static_cast(signum))); - - m_cpu->push32(shadow_wrap_as_initialized(handler.handler)); - - m_cpu->set_eip(m_signal_trampoline); -} - -// Make sure the compiler doesn't "optimize away" this function: -static void signal_trampoline_dummy() __attribute__((used)); -NEVER_INLINE void signal_trampoline_dummy() -{ - // The trampoline preserves the current eax, pushes the signal code and - // then calls the signal handler. We do this because, when interrupting a - // blocking syscall, that syscall may return some special error code in eax; - // This error code would likely be overwritten by the signal handler, so it's - // necessary to preserve it here. - constexpr static auto offset_to_first_register_slot = sizeof(__ucontext) + sizeof(siginfo) + 512 + 4 * sizeof(FlatPtr); - asm( - ".intel_syntax noprefix\n" - ".globl asm_signal_trampoline\n" - "asm_signal_trampoline:\n" - // stack state: 0, ucontext, signal_info, (alignment = 16), fpu_state (alignment = 16), 0, ucontext*, siginfo*, signal, (alignment = 16), handler - - // Pop the handler into ecx - "pop ecx\n" // save handler - // we have to save eax 'cause it might be the return value from a syscall - "mov [esp+%P2], eax\n" - // Note that the stack is currently aligned to 16 bytes as we popped the extra entries above. - // and it's already setup to call the handler with the expected values on the stack. - // call the signal handler - "call ecx\n" - // drop the 4 arguments - "add esp, 16\n" - // Current stack state is just saved_eax, ucontext, signal_info, fpu_state?. - // syscall SC_sigreturn - "mov eax, %P0\n" - "int 0x82\n" - ".globl asm_signal_trampoline_end\n" - "asm_signal_trampoline_end:\n" - ".att_syntax" - : - : "i"(Syscall::SC_sigreturn), - "i"(offset_to_first_register_slot), - "i"(offset_to_first_register_slot - sizeof(FlatPtr))); -} - -extern "C" void asm_signal_trampoline(void); -extern "C" void asm_signal_trampoline_end(void); - -void Emulator::setup_signal_trampoline() -{ - m_range_allocator.reserve_user_range(VirtualAddress(signal_trampoline_location), 4096); - auto trampoline_region = make(signal_trampoline_location, 4096); - - u8* trampoline = (u8*)asm_signal_trampoline; - u8* trampoline_end = (u8*)asm_signal_trampoline_end; - size_t trampoline_size = trampoline_end - trampoline; - - u8* code_ptr = trampoline_region->data(); - memcpy(code_ptr, trampoline, trampoline_size); - - m_signal_trampoline = trampoline_region->base(); - mmu().add_region(move(trampoline_region)); -} - -void Emulator::dump_regions() const -{ - const_cast(m_mmu).for_each_region([&](Region const& region) { - reportln("{:p}-{:p} {:c}{:c}{:c} {} {}{}{} "sv, - region.base(), - region.end() - 1, - region.is_readable() ? 'R' : '-', - region.is_writable() ? 'W' : '-', - region.is_executable() ? 'X' : '-', - is(region) ? static_cast(region).name() : "", - is(region) ? "(mmap) " : "", - region.is_stack() ? "(stack) " : "", - region.is_text() ? "(text) " : ""); - return IterationDecision::Continue; - }); -} - -bool Emulator::is_in_libsystem() const -{ - return m_cpu->base_eip() >= m_libsystem_start && m_cpu->base_eip() < m_libsystem_end; -} - -bool Emulator::is_in_loader_code() const -{ - if (!m_loader_text_base.has_value() || !m_loader_text_size.has_value()) - return false; - return (m_cpu->base_eip() >= m_loader_text_base.value() && m_cpu->base_eip() < m_loader_text_base.value() + m_loader_text_size.value()); -} - -} diff --git a/Userland/DevTools/UserspaceEmulator/Emulator.h b/Userland/DevTools/UserspaceEmulator/Emulator.h deleted file mode 100644 index f295508a4a..0000000000 --- a/Userland/DevTools/UserspaceEmulator/Emulator.h +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (c) 2020-2021, Andreas Kling - * Copyright (c) 2021, sin-ack - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "MallocTracer.h" -#include "RangeAllocator.h" -#include "Report.h" -#include "SoftMMU.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace UserspaceEmulator { - -class MallocTracer; -class SoftCPU; - -class Emulator { -public: - static Emulator& the(); - - Emulator(ByteString const& executable_path, Vector const& arguments, Vector const& environment); - - void set_profiling_details(bool should_dump_profile, size_t instruction_interval, Stream* profile_stream, Vector>* profiler_strings, Vector* profiler_string_id_map) - { - m_is_profiling = should_dump_profile; - m_profile_instruction_interval = instruction_interval; - m_profile_stream = profile_stream; - m_profiler_strings = profiler_strings; - m_profiler_string_id_map = profiler_string_id_map; - } - - void set_in_region_of_interest(bool value) - { - m_is_in_region_of_interest = value; - } - - Stream& profile_stream() { return *m_profile_stream; } - Vector>& profiler_strings() { return *m_profiler_strings; } - Vector& profiler_string_id_map() { return *m_profiler_string_id_map; } - - bool is_profiling() const { return m_is_profiling; } - bool is_in_region_of_interest() const { return m_is_in_region_of_interest; } - size_t profile_instruction_interval() const { return m_profile_instruction_interval; } - bool is_memory_auditing_suppressed() const { return m_is_memory_auditing_suppressed; } - - bool load_elf(); - void dump_backtrace(); - void dump_backtrace(Vector const&); - Vector raw_backtrace(); - - int exec(); - void handle_repl(); - u32 virt_syscall(u32 function, u32 arg1, u32 arg2, u32 arg3); - - SoftMMU& mmu() { return m_mmu; } - - MallocTracer* malloc_tracer() { return m_malloc_tracer; } - - bool is_in_loader_code() const; - bool is_in_libsystem() const; - - void pause() - { - m_steps_til_pause = 0; - m_run_til_return = false; - } - ALWAYS_INLINE void return_callback(FlatPtr addr) - { - if (m_run_til_return) [[unlikely]] { - if (addr == m_watched_addr) - pause(); - } - } - ALWAYS_INLINE void call_callback(FlatPtr addr) - { - if (m_run_til_call) [[unlikely]] { - if (addr == m_watched_addr) - pause(); - } - } - - struct SignalInfo { - siginfo_t signal_info; - ucontext_t context; - }; - void did_receive_signal(int signum, SignalInfo info, bool from_emulator = false) - { - if (!from_emulator && signum == SIGINT) - return did_receive_sigint(signum); - - m_pending_signals |= (1 << signum); - m_signal_data[signum] = info; - } - - void did_receive_sigint(int) - { - if (m_steps_til_pause == 0) - m_shutdown = true; - else - pause(); - } - - struct SymbolInfo { - ByteString lib_name; - ByteString symbol; - Optional source_position; - }; - - Optional symbol_at(FlatPtr address); - - void dump_regions() const; - -private: - const ByteString m_executable_path; - Vector const m_arguments; - Vector const m_environment; - - SoftMMU m_mmu; - NonnullOwnPtr m_cpu; - - OwnPtr m_malloc_tracer; - - void setup_stack(Vector); - Vector generate_auxiliary_vector(FlatPtr load_base, FlatPtr entry_eip, ByteString const& executable_path, int executable_fd) const; - void register_signal_handlers(); - void setup_signal_trampoline(); - - void send_signal(int); - - void emit_profile_sample(Stream&); - void emit_profile_event(Stream&, StringView event_name, ByteString const& contents); - - int virt$accept4(FlatPtr); - u32 virt$allocate_tls(FlatPtr, size_t); - int virt$anon_create(size_t, int); - int virt$bind(int sockfd, FlatPtr address, socklen_t address_length); - u32 virt$bindmount(u32 params_addr); - int virt$chdir(FlatPtr, size_t); - int virt$chmod(FlatPtr); - int virt$chown(FlatPtr); - int virt$clock_gettime(int, FlatPtr); - int virt$clock_nanosleep(FlatPtr); - int virt$clock_settime(uint32_t clock_id, FlatPtr user_ts); - int virt$close(int); - int virt$connect(int sockfd, FlatPtr address, socklen_t address_size); - int virt$create_inode_watcher(unsigned); - int virt$dbgputstr(FlatPtr characters, int length); - int virt$disown(pid_t); - int virt$dup2(int, int); - int virt$emuctl(FlatPtr, FlatPtr, FlatPtr); - int virt$execve(FlatPtr); - void virt$exit(int); - int virt$faccessat(FlatPtr); - int virt$fchmod(int, mode_t); - int virt$fchown(int, uid_t, gid_t); - u32 virt$fcntl(int fd, int, u32); - int virt$fork(); - u32 virt$fsopen(u32); - u32 virt$fsmount(u32); - int virt$fstat(int, FlatPtr); - int virt$ftruncate(int fd, FlatPtr length_addr); - int virt$futex(FlatPtr); - int virt$get_dir_entries(int fd, FlatPtr buffer, ssize_t); - int virt$get_stack_bounds(FlatPtr, FlatPtr); - int virt$getcwd(FlatPtr buffer, size_t buffer_size); - gid_t virt$getegid(); - uid_t virt$geteuid(); - gid_t virt$getgid(); - int virt$getgroups(ssize_t count, FlatPtr); - int virt$gethostname(FlatPtr, ssize_t); - int virt$getpeername(FlatPtr); - int virt$getpgid(pid_t); - int virt$getpgrp(); - u32 virt$getpid(); - pid_t virt$getppid(); - ssize_t virt$getrandom(FlatPtr buffer, size_t buffer_size, unsigned int flags); - int virt$getsid(pid_t); - int virt$getsockname(FlatPtr); - int virt$getsockopt(FlatPtr); - u32 virt$gettid(); - uid_t virt$getuid(); - int virt$inode_watcher_add_watch(FlatPtr); - int virt$inode_watcher_remove_watch(int, int); - int virt$ioctl(int fd, unsigned, FlatPtr); - int virt$kill(pid_t, int); - int virt$killpg(int pgrp, int sig); - int virt$listen(int, int); - int virt$lseek(int fd, FlatPtr offset_addr, int whence); - u32 virt$madvise(FlatPtr, size_t, int); - int virt$mkdir(FlatPtr path, size_t path_length, mode_t mode); - u32 virt$mmap(u32); - u32 virt$mprotect(FlatPtr, size_t, int); - FlatPtr virt$mremap(FlatPtr); - int virt$annotate_mapping(FlatPtr); - u32 virt$munmap(FlatPtr address, size_t size); - u32 virt$open(u32); - FlatPtr virt$perf_event(int type, FlatPtr arg1, FlatPtr arg2); - FlatPtr virt$perf_register_string(FlatPtr, size_t); - int virt$pipe(FlatPtr pipefd, int flags); - u32 virt$pledge(u32); - int virt$poll(FlatPtr); - int virt$profiling_disable(pid_t); - int virt$profiling_enable(pid_t, u64); - int virt$purge(int mode); - u32 virt$read(int, FlatPtr, ssize_t); - int virt$readlink(FlatPtr); - int virt$realpath(FlatPtr); - int virt$recvfd(int, int); - int virt$recvmsg(int sockfd, FlatPtr msg_addr, int flags); - int virt$rename(FlatPtr address); - u32 virt$remount(u32); - int virt$rmdir(FlatPtr path, size_t path_length); - int virt$scheduler_get_parameters(FlatPtr); - int virt$scheduler_set_parameters(FlatPtr); - int virt$sendfd(int, int); - int virt$sendmsg(int sockfd, FlatPtr msg_addr, int flags); - int virt$set_mmap_name(FlatPtr); - int virt$set_process_name(FlatPtr buffer, int size); - int virt$setgid(gid_t); - int virt$setgroups(ssize_t count, FlatPtr); - int virt$setpgid(pid_t pid, pid_t pgid); - pid_t virt$setsid(); - int virt$setsockopt(FlatPtr); - int virt$setuid(uid_t); - int virt$shutdown(int sockfd, int how); - int virt$sigaction(int, FlatPtr, FlatPtr); - int virt$sigprocmask(int how, FlatPtr set, FlatPtr old_set); - int virt$sigreturn(); - int virt$socket(int, int, int); - int virt$stat(FlatPtr); - int virt$symlink(FlatPtr address); - void virt$sync(); - u32 virt$sysconf(u32 name); - mode_t virt$umask(mode_t); - int virt$uname(FlatPtr params_addr); - int virt$unlink(FlatPtr path, size_t path_length); - u32 virt$unveil(u32); - int virt$waitid(FlatPtr); - u32 virt$write(int, FlatPtr, ssize_t); - - void dispatch_one_pending_signal(); - MmapRegion const* find_text_region(FlatPtr address); - MmapRegion const* load_library_from_address(FlatPtr address); - MmapRegion const* first_region_for_object(StringView name); - ByteString create_backtrace_line(FlatPtr address); - ByteString create_instruction_line(FlatPtr address, X86::Instruction const& insn); - - bool m_shutdown { false }; - int m_exit_status { 0 }; - - i64 m_steps_til_pause { -1 }; - bool m_run_til_return { false }; - bool m_run_til_call { false }; - FlatPtr m_watched_addr { 0 }; - RefPtr m_editor; - - FlatPtr m_libsystem_start { 0 }; - FlatPtr m_libsystem_end { 0 }; - - sigset_t m_pending_signals { 0 }; - sigset_t m_signal_mask { 0 }; - Array m_signal_data; - - struct SignalHandlerInfo { - FlatPtr handler { 0 }; - sigset_t mask { 0 }; - int flags { 0 }; - }; - SignalHandlerInfo m_signal_handler[NSIG]; - - FlatPtr m_signal_trampoline { 0 }; - Optional m_loader_text_base; - Optional m_loader_text_size; - - struct CachedELF { - NonnullOwnPtr mapped_file; - NonnullOwnPtr debug_info; - NonnullOwnPtr image; - }; - - HashMap m_dynamic_library_cache; - - RangeAllocator m_range_allocator; - - Stream* m_profile_stream { nullptr }; - Vector* m_profiler_string_id_map { nullptr }; - Vector>* m_profiler_strings { nullptr }; - - bool m_is_profiling { false }; - size_t m_profile_instruction_interval { 0 }; - bool m_is_in_region_of_interest { false }; - bool m_is_memory_auditing_suppressed { false }; -}; - -} diff --git a/Userland/DevTools/UserspaceEmulator/EmulatorControl.h b/Userland/DevTools/UserspaceEmulator/EmulatorControl.h deleted file mode 100644 index 38f038707d..0000000000 --- a/Userland/DevTools/UserspaceEmulator/EmulatorControl.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2021, Ali Mohammad Pur - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace UserspaceEmulator { - -enum class Command { - MarkROIStart = 5, - MarkROIEnd = 6, -}; - -inline void control(Command command) -{ - emuctl(to_underlying(command), 0, 0); -} - -} diff --git a/Userland/DevTools/UserspaceEmulator/Emulator_syscalls.cpp b/Userland/DevTools/UserspaceEmulator/Emulator_syscalls.cpp deleted file mode 100644 index 6658d75b49..0000000000 --- a/Userland/DevTools/UserspaceEmulator/Emulator_syscalls.cpp +++ /dev/null @@ -1,1657 +0,0 @@ -/* - * Copyright (c) 2020-2021, Andreas Kling - * Copyright (c) 2022, Rummskartoffel - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Emulator.h" -#include "MmapRegion.h" -#include "SimpleRegion.h" -#include "SoftCPU.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(AK_COMPILER_GCC) -# pragma GCC optimize("O3") -#endif - -namespace UserspaceEmulator { - -u32 Emulator::virt_syscall(u32 function, u32 arg1, u32 arg2, u32 arg3) -{ - if constexpr (SPAM_DEBUG) - reportln("Syscall: {} ({:x})"sv, Syscall::to_string((Syscall::Function)function), function); - switch (function) { - case SC_accept4: - return virt$accept4(arg1); - case SC_allocate_tls: - return virt$allocate_tls(arg1, arg2); - case SC_anon_create: - return virt$anon_create(arg1, arg2); - case SC_annotate_mapping: - return virt$annotate_mapping(arg1); - case SC_bind: - return virt$bind(arg1, arg2, arg3); - case SC_bindmount: - return virt$bindmount(arg1); - case SC_chdir: - return virt$chdir(arg1, arg2); - case SC_chmod: - return virt$chmod(arg1); - case SC_chown: - return virt$chown(arg1); - case SC_clock_gettime: - return virt$clock_gettime(arg1, arg2); - case SC_clock_nanosleep: - return virt$clock_nanosleep(arg1); - case SC_clock_settime: - return virt$clock_settime(arg1, arg2); - case SC_close: - return virt$close(arg1); - case SC_connect: - return virt$connect(arg1, arg2, arg3); - case SC_create_inode_watcher: - return virt$create_inode_watcher(arg1); - case SC_dbgputstr: - return virt$dbgputstr(arg1, arg2); - case SC_disown: - return virt$disown(arg1); - case SC_dup2: - return virt$dup2(arg1, arg2); - case SC_emuctl: - return virt$emuctl(arg1, arg2, arg3); - case SC_execve: - return virt$execve(arg1); - case SC_exit: - virt$exit((int)arg1); - return 0; - case SC_faccessat: - return virt$faccessat(arg1); - case SC_fchmod: - return virt$fchmod(arg1, arg2); - case SC_fchown: - return virt$fchown(arg1, arg2, arg3); - case SC_fcntl: - return virt$fcntl(arg1, arg2, arg3); - case SC_fork: - return virt$fork(); - case SC_fstat: - return virt$fstat(arg1, arg2); - case SC_fsmount: - return virt$fsopen(arg1); - case SC_fsopen: - return virt$fsopen(arg1); - case SC_ftruncate: - return virt$ftruncate(arg1, arg2); - case SC_futex: - return virt$futex(arg1); - case SC_get_dir_entries: - return virt$get_dir_entries(arg1, arg2, arg3); - case SC_get_stack_bounds: - return virt$get_stack_bounds(arg1, arg2); - case SC_getcwd: - return virt$getcwd(arg1, arg2); - case SC_getegid: - return virt$getegid(); - case SC_geteuid: - return virt$geteuid(); - case SC_getgid: - return virt$getgid(); - case SC_getgroups: - return virt$getgroups(arg1, arg2); - case SC_gethostname: - return virt$gethostname(arg1, arg2); - case SC_getpeername: - return virt$getpeername(arg1); - case SC_getpgid: - return virt$getpgid(arg1); - case SC_getpgrp: - return virt$getpgrp(); - case SC_getpid: - return virt$getpid(); - case SC_getppid: - return virt$getppid(); - case SC_getrandom: - return virt$getrandom(arg1, arg2, arg3); - case SC_getsid: - return virt$getsid(arg1); - case SC_getsockname: - return virt$getsockname(arg1); - case SC_getsockopt: - return virt$getsockopt(arg1); - case SC_gettid: - return virt$gettid(); - case SC_getuid: - return virt$getuid(); - case SC_inode_watcher_add_watch: - return virt$inode_watcher_add_watch(arg1); - case SC_inode_watcher_remove_watch: - return virt$inode_watcher_remove_watch(arg1, arg2); - case SC_ioctl: - return virt$ioctl(arg1, arg2, arg3); - case SC_kill: - return virt$kill(arg1, arg2); - case SC_killpg: - return virt$killpg(arg1, arg2); - case SC_listen: - return virt$listen(arg1, arg2); - case SC_lseek: - return virt$lseek(arg1, arg2, arg3); - case SC_madvise: - return virt$madvise(arg1, arg2, arg3); - case SC_map_time_page: - return -ENOSYS; - case SC_mkdir: - return virt$mkdir(arg1, arg2, arg3); - case SC_mmap: - return virt$mmap(arg1); - case SC_mprotect: - return virt$mprotect(arg1, arg2, arg3); - case SC_mremap: - return virt$mremap(arg1); - case SC_munmap: - return virt$munmap(arg1, arg2); - case SC_open: - return virt$open(arg1); - case SC_perf_event: - return virt$perf_event((int)arg1, arg2, arg3); - case SC_perf_register_string: - return virt$perf_register_string(arg1, arg2); - case SC_pipe: - return virt$pipe(arg1, arg2); - case SC_pledge: - return virt$pledge(arg1); - case SC_poll: - return virt$poll(arg1); - case SC_profiling_disable: - return virt$profiling_disable(arg1); - case SC_profiling_enable: - return virt$profiling_enable(arg1, arg2); - case SC_purge: - return virt$purge(arg1); - case SC_read: - return virt$read(arg1, arg2, arg3); - case SC_readlink: - return virt$readlink(arg1); - case SC_realpath: - return virt$realpath(arg1); - case SC_recvfd: - return virt$recvfd(arg1, arg2); - case SC_recvmsg: - return virt$recvmsg(arg1, arg2, arg3); - case SC_rename: - return virt$rename(arg1); - case SC_remount: - return virt$remount(arg1); - case SC_rmdir: - return virt$rmdir(arg1, arg2); - case SC_scheduler_get_parameters: - return virt$scheduler_get_parameters(arg1); - case SC_scheduler_set_parameters: - return virt$scheduler_set_parameters(arg1); - case SC_sendfd: - return virt$sendfd(arg1, arg2); - case SC_sendmsg: - return virt$sendmsg(arg1, arg2, arg3); - case SC_set_mmap_name: - return virt$set_mmap_name(arg1); - case SC_setgid: - return virt$setgid(arg2); - case SC_setgroups: - return virt$setgroups(arg1, arg2); - case SC_setpgid: - return virt$setpgid(arg1, arg2); - case SC_setsid: - return virt$setsid(); - case SC_setsockopt: - return virt$setsockopt(arg1); - case SC_setuid: - return virt$setuid(arg1); - case SC_shutdown: - return virt$shutdown(arg1, arg2); - case SC_sigaction: - return virt$sigaction(arg1, arg2, arg3); - case SC_sigprocmask: - return virt$sigprocmask(arg1, arg2, arg3); - case SC_sigreturn: - return virt$sigreturn(); - case SC_socket: - return virt$socket(arg1, arg2, arg3); - case SC_stat: - return virt$stat(arg1); - case SC_symlink: - return virt$symlink(arg1); - case SC_sync: - virt$sync(); - return 0; - case SC_sysconf: - return virt$sysconf(arg1); - case SC_umask: - return virt$umask(arg1); - case SC_uname: - return virt$uname(arg1); - case SC_unlink: - return virt$unlink(arg1, arg2); - case SC_unveil: - return virt$unveil(arg1); - case SC_waitid: - return virt$waitid(arg1); - case SC_write: - return virt$write(arg1, arg2, arg3); - default: - reportln("\n=={}== \033[31;1mUnimplemented syscall: {}\033[0m, {:p}"sv, getpid(), Syscall::to_string((Syscall::Function)function), function); - dump_backtrace(); - TODO(); - } -} - -int Emulator::virt$anon_create(size_t size, int options) -{ - return syscall(SC_anon_create, size, options); -} - -int Emulator::virt$sendfd(int socket, int fd) -{ - return syscall(SC_sendfd, socket, fd); -} - -int Emulator::virt$recvfd(int socket, int options) -{ - return syscall(SC_recvfd, socket, options); -} - -int Emulator::virt$profiling_enable(pid_t pid, u64 mask) -{ - return syscall(SC_profiling_enable, pid, mask); -} - -int Emulator::virt$profiling_disable(pid_t pid) -{ - return syscall(SC_profiling_disable, pid); -} - -FlatPtr Emulator::virt$perf_event(int event, FlatPtr arg1, FlatPtr arg2) -{ - if (event == PERF_EVENT_SIGNPOST) { - if (is_profiling()) { - if (profiler_string_id_map().size() > arg1) - emit_profile_event(profile_stream(), "signpost"sv, ByteString::formatted("\"arg1\": {}, \"arg2\": {}", arg1, arg2)); - syscall(SC_perf_event, PERF_EVENT_SIGNPOST, profiler_string_id_map().at(arg1), arg2); - } else { - syscall(SC_perf_event, PERF_EVENT_SIGNPOST, arg1, arg2); - } - return 0; - } - return -ENOSYS; -} - -FlatPtr Emulator::virt$perf_register_string(FlatPtr string, size_t size) -{ - char* buffer = (char*)alloca(size + 4); - // FIXME: not nice, but works - __builtin_memcpy(buffer, "UE: ", 4); - mmu().copy_from_vm((buffer + 4), string, size); - auto ret = (int)syscall(SC_perf_register_string, buffer, size + 4); - - if (ret >= 0 && is_profiling()) { - profiler_strings().append(make(StringView { buffer + 4, size })); - profiler_string_id_map().append(ret); - ret = profiler_string_id_map().size() - 1; - } - return ret; -} - -int Emulator::virt$disown(pid_t pid) -{ - return syscall(SC_disown, pid); -} - -int Emulator::virt$purge(int mode) -{ - return syscall(SC_purge, mode); -} - -int Emulator::virt$fstat(int fd, FlatPtr statbuf) -{ - struct stat local_statbuf; - int rc = syscall(SC_fstat, fd, &local_statbuf); - if (rc < 0) - return rc; - mmu().copy_to_vm(statbuf, &local_statbuf, sizeof(local_statbuf)); - return rc; -} - -int Emulator::virt$close(int fd) -{ - return syscall(SC_close, fd); -} - -int Emulator::virt$mkdir(FlatPtr path, size_t path_length, mode_t mode) -{ - auto buffer = mmu().copy_buffer_from_vm(path, path_length); - return syscall(SC_mkdir, buffer.data(), buffer.size(), mode); -} - -int Emulator::virt$rmdir(FlatPtr path, size_t path_length) -{ - auto buffer = mmu().copy_buffer_from_vm(path, path_length); - return syscall(SC_rmdir, buffer.data(), buffer.size()); -} - -int Emulator::virt$unlink(FlatPtr path, size_t path_length) -{ - auto buffer = mmu().copy_buffer_from_vm(path, path_length); - return syscall(SC_unlink, AT_FDCWD, buffer.data(), buffer.size(), 0); -} - -int Emulator::virt$symlink(FlatPtr params_addr) -{ - Syscall::SC_symlink_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - - auto target = mmu().copy_buffer_from_vm((FlatPtr)params.target.characters, params.target.length); - params.target.characters = (char const*)target.data(); - params.target.length = target.size(); - - auto link = mmu().copy_buffer_from_vm((FlatPtr)params.linkpath.characters, params.linkpath.length); - params.linkpath.characters = (char const*)link.data(); - params.linkpath.length = link.size(); - - return syscall(SC_symlink, ¶ms); -} - -int Emulator::virt$rename(FlatPtr params_addr) -{ - Syscall::SC_rename_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - - auto new_path = mmu().copy_buffer_from_vm((FlatPtr)params.new_path.characters, params.new_path.length); - params.new_path.characters = (char const*)new_path.data(); - params.new_path.length = new_path.size(); - - auto old_path = mmu().copy_buffer_from_vm((FlatPtr)params.old_path.characters, params.old_path.length); - params.old_path.characters = (char const*)old_path.data(); - params.old_path.length = old_path.size(); - - return syscall(SC_rename, ¶ms); -} - -int Emulator::virt$dbgputstr(FlatPtr characters, int length) -{ - auto buffer = mmu().copy_buffer_from_vm(characters, length); - dbgputstr((char const*)buffer.data(), buffer.size()); - return 0; -} - -int Emulator::virt$chmod(FlatPtr params_addr) -{ - Syscall::SC_chmod_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - - auto path = mmu().copy_buffer_from_vm((FlatPtr)params.path.characters, params.path.length); - params.path.characters = (char const*)path.data(); - params.path.length = path.size(); - return syscall(SC_chmod, ¶ms); -} - -int Emulator::virt$chown(FlatPtr params_addr) -{ - Syscall::SC_chown_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - - auto path = mmu().copy_buffer_from_vm((FlatPtr)params.path.characters, params.path.length); - params.path.characters = (char const*)path.data(); - params.path.length = path.size(); - - return syscall(SC_chown, ¶ms); -} - -int Emulator::virt$fchmod(int fd, mode_t mode) -{ - return syscall(SC_fchmod, fd, mode); -} - -int Emulator::virt$fchown(int fd, uid_t uid, gid_t gid) -{ - return syscall(SC_fchown, fd, uid, gid); -} - -int Emulator::virt$setsockopt(FlatPtr params_addr) -{ - Syscall::SC_setsockopt_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - - if (params.option == SO_RCVTIMEO || params.option == SO_TIMESTAMP) { - auto host_value_buffer_result = ByteBuffer::create_zeroed(params.value_size); - if (host_value_buffer_result.is_error()) - return -ENOMEM; - auto& host_value_buffer = host_value_buffer_result.value(); - mmu().copy_from_vm(host_value_buffer.data(), (FlatPtr)params.value, params.value_size); - int rc = setsockopt(params.sockfd, params.level, params.option, host_value_buffer.data(), host_value_buffer.size()); - if (rc < 0) - return -errno; - return rc; - } - - if (params.option == SO_BINDTODEVICE) { - auto ifname = mmu().copy_buffer_from_vm((FlatPtr)params.value, params.value_size); - params.value = ifname.data(); - params.value_size = ifname.size(); - return syscall(SC_setsockopt, ¶ms); - } - - TODO(); -} - -int Emulator::virt$get_stack_bounds(FlatPtr base, FlatPtr size) -{ - auto* region = mmu().find_region({ m_cpu->ss(), m_cpu->esp().value() }); - FlatPtr b = region->base(); - size_t s = region->size(); - mmu().copy_to_vm(base, &b, sizeof(b)); - mmu().copy_to_vm(size, &s, sizeof(s)); - return 0; -} - -int Emulator::virt$ftruncate(int fd, off_t length) -{ - off_t length; - return syscall(SC_ftruncate, fd, length); -} - -int Emulator::virt$uname(FlatPtr params_addr) -{ - struct utsname local_uname; - auto rc = syscall(SC_uname, &local_uname); - mmu().copy_to_vm(params_addr, &local_uname, sizeof(local_uname)); - return rc; -} - -mode_t Emulator::virt$umask(mode_t mask) -{ - return syscall(SC_umask, mask); -} - -int Emulator::virt$accept4(FlatPtr params_addr) -{ - Syscall::SC_accept4_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - sockaddr_storage addr = {}; - socklen_t addrlen; - mmu().copy_from_vm(&addrlen, (FlatPtr)params.addrlen, sizeof(socklen_t)); - VERIFY(addrlen <= sizeof(addr)); - int rc = accept4(params.sockfd, (sockaddr*)&addr, &addrlen, params.flags); - if (rc == 0) { - mmu().copy_to_vm((FlatPtr)params.addr, &addr, addrlen); - mmu().copy_to_vm((FlatPtr)params.addrlen, &addrlen, sizeof(socklen_t)); - } - return rc < 0 ? -errno : rc; -} - -int Emulator::virt$bind(int sockfd, FlatPtr address, socklen_t address_length) -{ - auto buffer = mmu().copy_buffer_from_vm(address, address_length); - return syscall(SC_bind, sockfd, buffer.data(), buffer.size()); -} - -int Emulator::virt$connect(int sockfd, FlatPtr address, socklen_t address_size) -{ - auto buffer = mmu().copy_buffer_from_vm(address, address_size); - return syscall(SC_connect, sockfd, buffer.data(), buffer.size()); -} - -int Emulator::virt$shutdown(int sockfd, int how) -{ - return syscall(SC_shutdown, sockfd, how); -} - -int Emulator::virt$listen(int fd, int backlog) -{ - return syscall(SC_listen, fd, backlog); -} - -int Emulator::virt$kill(pid_t pid, int signal) -{ - return syscall(SC_kill, pid, signal); -} - -int Emulator::virt$killpg(int pgrp, int sig) -{ - return syscall(SC_killpg, pgrp, sig); -} - -int Emulator::virt$clock_gettime(int clockid, FlatPtr timespec) -{ - struct timespec host_timespec; - int rc = syscall(SC_clock_gettime, clockid, &host_timespec); - if (rc < 0) - return rc; - mmu().copy_to_vm(timespec, &host_timespec, sizeof(host_timespec)); - return rc; -} - -int Emulator::virt$clock_settime(uint32_t clock_id, FlatPtr user_ts) -{ - struct timespec user_timespec; - mmu().copy_from_vm(&user_timespec, user_ts, sizeof(user_timespec)); - int rc = syscall(SC_clock_settime, clock_id, &user_timespec); - return rc; -} - -int Emulator::virt$set_mmap_name(FlatPtr params_addr) -{ - Syscall::SC_set_mmap_name_params params {}; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - auto name = mmu().copy_buffer_from_vm((FlatPtr)params.name.characters, params.name.length); - - auto* region = mmu().find_region({ 0x23, (FlatPtr)params.addr }); - if (!region || !is(*region)) - return -EINVAL; - static_cast(*region).set_name(ByteString::copy(name)); - return 0; -} - -int Emulator::virt$lseek(int fd, FlatPtr offset_addr, int whence) -{ - off_t offset; - mmu().copy_from_vm(&offset, offset_addr, sizeof(off_t)); - auto rc = syscall(SC_lseek, fd, &offset, whence); - mmu().copy_to_vm(offset_addr, &offset, sizeof(off_t)); - return rc; -} - -int Emulator::virt$socket(int domain, int type, int protocol) -{ - return syscall(SC_socket, domain, type, protocol); -} - -int Emulator::virt$recvmsg(int sockfd, FlatPtr msg_addr, int flags) -{ - msghdr mmu_msg; - mmu().copy_from_vm(&mmu_msg, msg_addr, sizeof(mmu_msg)); - - Vector mmu_iovs; - mmu_iovs.resize(mmu_msg.msg_iovlen); - mmu().copy_from_vm(mmu_iovs.data(), (FlatPtr)mmu_msg.msg_iov, mmu_msg.msg_iovlen * sizeof(iovec)); - Vector buffers; - Vector iovs; - for (auto const& iov : mmu_iovs) { - auto buffer_result = ByteBuffer::create_uninitialized(iov.iov_len); - if (buffer_result.is_error()) - return -ENOMEM; - buffers.append(buffer_result.release_value()); - iovs.append({ buffers.last().data(), buffers.last().size() }); - } - - ByteBuffer control_buffer; - if (mmu_msg.msg_control) { - auto buffer_result = ByteBuffer::create_uninitialized(mmu_msg.msg_controllen); - if (buffer_result.is_error()) - return -ENOMEM; - control_buffer = buffer_result.release_value(); - } - - sockaddr_storage addr; - msghdr msg = { &addr, sizeof(addr), iovs.data(), (int)iovs.size(), mmu_msg.msg_control ? control_buffer.data() : nullptr, mmu_msg.msg_controllen, mmu_msg.msg_flags }; - int rc = recvmsg(sockfd, &msg, flags); - if (rc < 0) - return -errno; - - for (size_t i = 0; i < buffers.size(); ++i) - mmu().copy_to_vm((FlatPtr)mmu_iovs[i].iov_base, buffers[i].data(), mmu_iovs[i].iov_len); - - if (mmu_msg.msg_name) - mmu().copy_to_vm((FlatPtr)mmu_msg.msg_name, &addr, min(sizeof(addr), (size_t)mmu_msg.msg_namelen)); - if (mmu_msg.msg_control) - mmu().copy_to_vm((FlatPtr)mmu_msg.msg_control, control_buffer.data(), min(mmu_msg.msg_controllen, msg.msg_controllen)); - mmu_msg.msg_namelen = msg.msg_namelen; - mmu_msg.msg_controllen = msg.msg_controllen; - mmu_msg.msg_flags = msg.msg_flags; - mmu().copy_to_vm(msg_addr, &mmu_msg, sizeof(mmu_msg)); - return rc; -} - -int Emulator::virt$sendmsg(int sockfd, FlatPtr msg_addr, int flags) -{ - msghdr mmu_msg; - mmu().copy_from_vm(&mmu_msg, msg_addr, sizeof(mmu_msg)); - - Vector iovs; - iovs.resize(mmu_msg.msg_iovlen); - mmu().copy_from_vm(iovs.data(), (FlatPtr)mmu_msg.msg_iov, mmu_msg.msg_iovlen * sizeof(iovec)); - Vector buffers; - for (auto& iov : iovs) { - buffers.append(mmu().copy_buffer_from_vm((FlatPtr)iov.iov_base, iov.iov_len)); - iov = { buffers.last().data(), buffers.last().size() }; - } - - ByteBuffer control_buffer; - if (mmu_msg.msg_control) { - auto buffer_result = ByteBuffer::create_uninitialized(mmu_msg.msg_controllen); - if (buffer_result.is_error()) - return -ENOMEM; - control_buffer = buffer_result.release_value(); - } - - sockaddr_storage address; - socklen_t address_length = 0; - if (mmu_msg.msg_name) { - address_length = min(sizeof(address), (size_t)mmu_msg.msg_namelen); - mmu().copy_from_vm(&address, (FlatPtr)mmu_msg.msg_name, address_length); - } - - msghdr msg = { mmu_msg.msg_name ? &address : nullptr, address_length, iovs.data(), (int)iovs.size(), mmu_msg.msg_control ? control_buffer.data() : nullptr, mmu_msg.msg_controllen, mmu_msg.msg_flags }; - return sendmsg(sockfd, &msg, flags); -} - -int Emulator::virt$getsockopt(FlatPtr params_addr) -{ - Syscall::SC_getsockopt_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - - if (params.option == SO_PEERCRED) { - struct ucred creds = {}; - socklen_t creds_size = sizeof(creds); - int rc = getsockopt(params.sockfd, params.level, SO_PEERCRED, &creds, &creds_size); - if (rc < 0) - return -errno; - // FIXME: Check params.value_size - mmu().copy_to_vm((FlatPtr)params.value, &creds, sizeof(creds)); - return rc; - } - if (params.option == SO_ERROR) { - int so_error; - socklen_t so_error_len = sizeof(so_error); - int rc = getsockopt(params.sockfd, params.level, SO_ERROR, &so_error, &so_error_len); - if (rc < 0) - return -errno; - // FIXME: Check params.value_size - mmu().copy_to_vm((FlatPtr)params.value, &so_error, sizeof(so_error)); - return rc; - } - - dbgln("Not implemented socket param: {}", params.option); - TODO(); -} - -int Emulator::virt$getsockname(FlatPtr params_addr) -{ - Syscall::SC_getsockname_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - sockaddr_storage addr = {}; - socklen_t addrlen; - mmu().copy_from_vm(&addrlen, (FlatPtr)params.addrlen, sizeof(socklen_t)); - VERIFY(addrlen <= sizeof(addr)); - auto rc = getsockname(params.sockfd, (sockaddr*)&addr, &addrlen); - if (rc == 0) { - mmu().copy_to_vm((FlatPtr)params.addr, &addr, sizeof(addr)); - mmu().copy_to_vm((FlatPtr)params.addrlen, &addrlen, sizeof(addrlen)); - } - return rc < 0 ? -errno : rc; -} - -int Emulator::virt$getpeername(FlatPtr params_addr) -{ - Syscall::SC_getpeername_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - sockaddr_storage addr = {}; - socklen_t addrlen; - mmu().copy_from_vm(&addrlen, (FlatPtr)params.addrlen, sizeof(socklen_t)); - VERIFY(addrlen <= sizeof(addr)); - auto rc = getpeername(params.sockfd, (sockaddr*)&addr, &addrlen); - if (rc == 0) { - mmu().copy_to_vm((FlatPtr)params.addr, &addr, sizeof(addr)); - mmu().copy_to_vm((FlatPtr)params.addrlen, &addrlen, sizeof(addrlen)); - } - return rc < 0 ? -errno : rc; -} - -int Emulator::virt$getgroups(ssize_t count, FlatPtr groups) -{ - if (!count) - return syscall(SC_getgroups, 0, nullptr); - - auto buffer_result = ByteBuffer::create_uninitialized(count * sizeof(gid_t)); - if (buffer_result.is_error()) - return -ENOMEM; - auto& buffer = buffer_result.value(); - int rc = syscall(SC_getgroups, count, buffer.data()); - if (rc < 0) - return rc; - mmu().copy_to_vm(groups, buffer.data(), buffer.size()); - return 0; -} - -int Emulator::virt$setgroups(ssize_t count, FlatPtr groups) -{ - if (!count) - return syscall(SC_setgroups, 0, nullptr); - - auto buffer = mmu().copy_buffer_from_vm(groups, count * sizeof(gid_t)); - return syscall(SC_setgroups, count, buffer.data()); -} - -u32 Emulator::virt$fcntl(int fd, int cmd, u32 arg) -{ - switch (cmd) { - case F_DUPFD: - case F_GETFD: - case F_SETFD: - case F_GETFL: - case F_SETFL: - case F_ISTTY: - break; - default: - dbgln("Invalid fcntl cmd: {}", cmd); - } - - return syscall(SC_fcntl, fd, cmd, arg); -} - -u32 Emulator::virt$open(u32 params_addr) -{ - Syscall::SC_open_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - - auto path = mmu().copy_buffer_from_vm((FlatPtr)params.path.characters, params.path.length); - - Syscall::SC_open_params host_params {}; - host_params.dirfd = params.dirfd; - host_params.mode = params.mode; - host_params.options = params.options; - host_params.path.characters = (char const*)path.data(); - host_params.path.length = path.size(); - - return syscall(SC_open, &host_params); -} - -int Emulator::virt$pipe(FlatPtr vm_pipefd, int flags) -{ - int pipefd[2]; - int rc = syscall(SC_pipe, pipefd, flags); - if (rc < 0) - return rc; - mmu().copy_to_vm(vm_pipefd, pipefd, sizeof(pipefd)); - return rc; -} - -static void round_to_page_size(FlatPtr& address, size_t& size) -{ - auto new_end = round_up_to_power_of_two(address + size, PAGE_SIZE); - address &= ~(PAGE_SIZE - 1); - size = new_end - address; -} - -u32 Emulator::virt$munmap(FlatPtr address, size_t size) -{ - if (is_profiling()) - emit_profile_event(profile_stream(), "munmap"sv, ByteString::formatted("\"ptr\": {}, \"size\": {}", address, size)); - round_to_page_size(address, size); - Vector marked_for_deletion; - bool has_non_mmap_region = false; - mmu().for_regions_in({ 0x23, address }, size, [&](Region* region) { - if (region) { - if (!is(*region)) { - has_non_mmap_region = true; - return IterationDecision::Break; - } - marked_for_deletion.append(region); - } - return IterationDecision::Continue; - }); - if (has_non_mmap_region) - return -EINVAL; - - for (Region* region : marked_for_deletion) { - m_range_allocator.deallocate(region->range()); - mmu().remove_region(*region); - } - return 0; -} - -u32 Emulator::virt$mmap(u32 params_addr) -{ - Syscall::SC_mmap_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - params.alignment = params.alignment ? params.alignment : PAGE_SIZE; - - if (params.size == 0) - return -EINVAL; - - u32 requested_size = round_up_to_power_of_two(params.size, PAGE_SIZE); - FlatPtr final_address; - - Optional result; - if (params.flags & MAP_RANDOMIZED) { - result = m_range_allocator.allocate_randomized(requested_size, params.alignment); - } else if (params.flags & MAP_FIXED || params.flags & MAP_FIXED_NOREPLACE) { - if (params.addr) { - // If MAP_FIXED is specified, existing mappings that intersect the requested range are removed. - if (params.flags & MAP_FIXED) - virt$munmap((FlatPtr)params.addr, requested_size); - result = m_range_allocator.allocate_specific(VirtualAddress { params.addr }, requested_size); - } else { - // mmap(nullptr, …, MAP_FIXED) is technically okay, but tends to be a bug. - // Therefore, refuse to be helpful. - reportln("\n=={}== \033[31;1mTried to mmap at nullptr with MAP_FIXED.\033[0m, {:#x} bytes."sv, getpid(), params.size); - dump_backtrace(); - } - } else { - result = m_range_allocator.allocate_anywhere(requested_size, params.alignment); - } - if (!result.has_value()) - return -ENOMEM; - final_address = result.value().base().get(); - auto final_size = result.value().size(); - - ByteString name_str; - if (params.name.characters) { - auto buffer_result = ByteBuffer::create_uninitialized(params.name.length); - if (buffer_result.is_error()) - return -ENOMEM; - auto& name = buffer_result.value(); - mmu().copy_from_vm(name.data(), (FlatPtr)params.name.characters, params.name.length); - name_str = { name.data(), name.size() }; - } - - if (is_profiling()) - emit_profile_event(profile_stream(), "mmap"sv, ByteString::formatted(R"("ptr": {}, "size": {}, "name": "{}")", final_address, final_size, name_str)); - - if (params.flags & MAP_ANONYMOUS) { - mmu().add_region(MmapRegion::create_anonymous(final_address, final_size, params.prot, move(name_str))); - } else { - auto region = MmapRegion::create_file_backed(final_address, final_size, params.prot, params.flags, params.fd, params.offset, move(name_str)); - if (region->name() == "libsystem.so: .text" && !m_libsystem_start) { - m_libsystem_start = final_address; - m_libsystem_end = final_address + final_size; - } - mmu().add_region(move(region)); - } - - return final_address; -} - -FlatPtr Emulator::virt$mremap(FlatPtr params_addr) -{ - Syscall::SC_mremap_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - - // FIXME: Support regions that have been split in the past (e.g. due to mprotect or munmap). - if (auto* region = mmu().find_region({ m_cpu->ds(), (FlatPtr)params.old_address })) { - if (!is(*region)) - return -EINVAL; - VERIFY(region->size() == params.old_size); - auto& mmap_region = *(MmapRegion*)region; - auto* ptr = mremap(mmap_region.data(), mmap_region.size(), mmap_region.size(), params.flags); - if (ptr == MAP_FAILED) - return -errno; - return (FlatPtr)ptr; - } - return -EINVAL; -} - -u32 Emulator::virt$bindmount(u32 params_addr) -{ - Syscall::SC_bindmount_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - auto target = mmu().copy_buffer_from_vm((FlatPtr)params.target.characters, params.target.length); - params.target.characters = (char*)target.data(); - params.target.length = target.size(); - - return syscall(SC_bindmount, ¶ms); -} - -u32 Emulator::virt$remount(u32 params_addr) -{ - Syscall::SC_remount_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - auto target = mmu().copy_buffer_from_vm((FlatPtr)params.target.characters, params.target.length); - params.target.characters = (char*)target.data(); - params.target.length = target.size(); - - return syscall(SC_remount, ¶ms); -} - -u32 Emulator::virt$fsopen(u32 params_addr) -{ - Syscall::SC_fsopen_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - auto fs_type = mmu().copy_buffer_from_vm((FlatPtr)params.fs_type.characters, params.fs_type.length); - params.fs_type.characters = (char*)fs_type.data(); - params.fs_type.length = fs_type.size(); - - return syscall(SC_fsopen, ¶ms); -} - -u32 Emulator::virt$fsmount(u32 params_addr) -{ - Syscall::SC_fsmount_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - auto target = mmu().copy_buffer_from_vm((FlatPtr)params.target.characters, params.target.length); - params.target.characters = (char*)target.data(); - params.target.length = target.size(); - - return syscall(SC_fsmount, ¶ms); -} - -u32 Emulator::virt$gettid() -{ - return gettid(); -} - -u32 Emulator::virt$getpid() -{ - return getpid(); -} - -pid_t Emulator::virt$getppid() -{ - return getppid(); -} - -u32 Emulator::virt$pledge(u32) -{ - return 0; -} - -u32 Emulator::virt$unveil(u32) -{ - return 0; -} - -u32 Emulator::virt$mprotect(FlatPtr base, size_t size, int prot) -{ - round_to_page_size(base, size); - bool has_non_mmapped_region = false; - - mmu().for_regions_in({ 0x23, base }, size, [&](Region* region) { - if (region) { - if (!is(*region)) { - has_non_mmapped_region = true; - return IterationDecision::Break; - } - auto& mmap_region = *(MmapRegion*)region; - mmap_region.set_prot(prot); - } - return IterationDecision::Continue; - }); - if (has_non_mmapped_region) - return -EINVAL; - - return 0; -} - -u32 Emulator::virt$madvise(FlatPtr, size_t, int) -{ - return 0; -} - -uid_t Emulator::virt$getuid() -{ - return getuid(); -} - -uid_t Emulator::virt$geteuid() -{ - return geteuid(); -} - -gid_t Emulator::virt$getgid() -{ - return getgid(); -} - -gid_t Emulator::virt$getegid() -{ - return getegid(); -} - -int Emulator::virt$setuid(uid_t uid) -{ - return syscall(SC_setuid, uid); -} - -int Emulator::virt$setgid(gid_t gid) -{ - return syscall(SC_setgid, gid); -} - -u32 Emulator::virt$write(int fd, FlatPtr data, ssize_t size) -{ - if (size < 0) - return -EINVAL; - auto buffer = mmu().copy_buffer_from_vm(data, size); - return syscall(SC_write, fd, buffer.data(), buffer.size()); -} - -u32 Emulator::virt$read(int fd, FlatPtr buffer, ssize_t size) -{ - if (size < 0) - return -EINVAL; - auto buffer_result = ByteBuffer::create_uninitialized(size); - if (buffer_result.is_error()) - return -ENOMEM; - auto& local_buffer = buffer_result.value(); - int nread = syscall(SC_read, fd, local_buffer.data(), local_buffer.size()); - if (nread < 0) { - if (nread == -EPERM) { - dump_backtrace(); - TODO(); - } - return nread; - } - mmu().copy_to_vm(buffer, local_buffer.data(), local_buffer.size()); - return nread; -} - -void Emulator::virt$sync() -{ - syscall(SC_sync); -} - -void Emulator::virt$exit(int status) -{ - reportln("\n=={}== \033[33;1mSyscall: exit({})\033[0m, shutting down!"sv, getpid(), status); - m_exit_status = status; - m_shutdown = true; -} - -ssize_t Emulator::virt$getrandom(FlatPtr buffer, size_t buffer_size, unsigned int flags) -{ - auto buffer_result = ByteBuffer::create_uninitialized(buffer_size); - if (buffer_result.is_error()) - return -ENOMEM; - auto& host_buffer = buffer_result.value(); - int rc = syscall(SC_getrandom, host_buffer.data(), host_buffer.size(), flags); - if (rc < 0) - return rc; - mmu().copy_to_vm(buffer, host_buffer.data(), host_buffer.size()); - return rc; -} - -int Emulator::virt$get_dir_entries(int fd, FlatPtr buffer, ssize_t size) -{ - auto buffer_result = ByteBuffer::create_uninitialized(size); - if (buffer_result.is_error()) - return -ENOMEM; - auto& host_buffer = buffer_result.value(); - int rc = syscall(SC_get_dir_entries, fd, host_buffer.data(), host_buffer.size()); - if (rc < 0) - return rc; - mmu().copy_to_vm(buffer, host_buffer.data(), host_buffer.size()); - return rc; -} - -int Emulator::virt$ioctl([[maybe_unused]] int fd, unsigned request, [[maybe_unused]] FlatPtr arg) -{ - switch (request) { - case TIOCGWINSZ: { - struct winsize ws; - int rc = syscall(SC_ioctl, fd, TIOCGWINSZ, &ws); - if (rc < 0) - return rc; - mmu().copy_to_vm(arg, &ws, sizeof(winsize)); - return 0; - } - case TIOCSWINSZ: { - struct winsize ws; - mmu().copy_from_vm(&ws, arg, sizeof(winsize)); - return syscall(SC_ioctl, fd, request, &ws); - } - case TIOCGPGRP: { - pid_t pgid; - auto rc = syscall(SC_ioctl, fd, request, &pgid); - mmu().copy_to_vm(arg, &pgid, sizeof(pgid)); - return rc; - } - case TIOCSPGRP: - return syscall(SC_ioctl, fd, request, arg); - case TCGETS: { - struct termios termios; - int rc = syscall(SC_ioctl, fd, request, &termios); - if (rc < 0) - return rc; - mmu().copy_to_vm(arg, &termios, sizeof(termios)); - return rc; - } - case TCSETS: - case TCSETSF: - case TCSETSW: { - struct termios termios; - mmu().copy_from_vm(&termios, arg, sizeof(termios)); - return syscall(SC_ioctl, fd, request, &termios); - } - case TCFLSH: - return syscall(SC_ioctl, fd, request, arg); - case TIOCNOTTY: - case TIOCSCTTY: - return syscall(SC_ioctl, fd, request, 0); - case TIOCSTI: - return -EIO; - case GRAPHICS_IOCTL_GET_PROPERTIES: { - size_t size = 0; - auto rc = syscall(SC_ioctl, fd, request, &size); - mmu().copy_to_vm(arg, &size, sizeof(size)); - return rc; - } - case GRAPHICS_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER: - return syscall(SC_ioctl, fd, request, arg); - case FIONBIO: { - int enabled; - mmu().copy_from_vm(&enabled, arg, sizeof(int)); - return syscall(SC_ioctl, fd, request, &enabled); - } - default: - reportln("Unsupported ioctl: {}"sv, request); - dump_backtrace(); - TODO(); - } - VERIFY_NOT_REACHED(); -} - -int Emulator::virt$emuctl(FlatPtr arg1, FlatPtr arg2, FlatPtr arg3) -{ - auto* tracer = malloc_tracer(); - if (arg1 <= 4 && !tracer) - return 0; - switch (arg1) { - case 1: - tracer->target_did_malloc({}, arg3, arg2); - return 0; - case 2: - tracer->target_did_free({}, arg2); - return 0; - case 3: - tracer->target_did_realloc({}, arg3, arg2); - return 0; - case 4: - tracer->target_did_change_chunk_size({}, arg3, arg2); - return 0; - case 5: // mark ROI start - if (is_in_region_of_interest()) - return -EINVAL; - m_is_in_region_of_interest = true; - return 0; - case 6: // mark ROI end - m_is_in_region_of_interest = false; - return 0; - case 7: - m_is_memory_auditing_suppressed = true; - return 0; - case 8: - m_is_memory_auditing_suppressed = false; - return 0; - default: - return -EINVAL; - } -} - -int Emulator::virt$fork() -{ - int rc = fork(); - if (rc < 0) - return -errno; - return rc; -} - -int Emulator::virt$execve(FlatPtr params_addr) -{ - Syscall::SC_execve_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - - auto path = ByteString::copy(mmu().copy_buffer_from_vm((FlatPtr)params.path.characters, params.path.length)); - Vector arguments; - Vector environment; - - auto copy_string_list = [this](auto& output_vector, auto& string_list) { - for (size_t i = 0; i < string_list.length; ++i) { - Syscall::StringArgument string; - mmu().copy_from_vm(&string, (FlatPtr)&string_list.strings[i], sizeof(string)); - output_vector.append(ByteString::copy(mmu().copy_buffer_from_vm((FlatPtr)string.characters, string.length))); - } - }; - - copy_string_list(arguments, params.arguments); - copy_string_list(environment, params.environment); - - reportln("\n=={}== \033[33;1mSyscall:\033[0m execve"sv, getpid()); - reportln("=={}== @ {}"sv, getpid(), path); - for (auto& argument : arguments) - reportln("=={}== - {}"sv, getpid(), argument); - - if (access(path.characters(), X_OK) < 0) { - if (errno == ENOENT || errno == EACCES) - return -errno; - } - - Vector argv; - Vector envp; - - argv.append(const_cast("/bin/UserspaceEmulator")); - if (g_report_to_debug) - argv.append(const_cast("--report-to-debug")); - argv.append(const_cast("--")); - argv.append(const_cast(path.characters())); - - auto create_string_vector = [](auto& output_vector, auto& input_vector) { - for (auto& string : input_vector) - output_vector.append(const_cast(string.characters())); - output_vector.append(nullptr); - }; - - create_string_vector(argv, arguments); - create_string_vector(envp, environment); - - // Yoink duplicated program name. - argv.remove(3 + (g_report_to_debug ? 1 : 0)); - - return execve(argv[0], (char* const*)argv.data(), (char* const*)envp.data()); -} - -int Emulator::virt$stat(FlatPtr params_addr) -{ - Syscall::SC_stat_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - - auto path = ByteString::copy(mmu().copy_buffer_from_vm((FlatPtr)params.path.characters, params.path.length)); - struct stat host_statbuf; - int rc; - if (params.follow_symlinks) - rc = stat(path.characters(), &host_statbuf); - else - rc = lstat(path.characters(), &host_statbuf); - if (rc < 0) - return -errno; - mmu().copy_to_vm((FlatPtr)params.statbuf, &host_statbuf, sizeof(host_statbuf)); - return rc; -} - -int Emulator::virt$realpath(FlatPtr params_addr) -{ - Syscall::SC_realpath_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - - auto path = mmu().copy_buffer_from_vm((FlatPtr)params.path.characters, params.path.length); - auto buffer_result = ByteBuffer::create_zeroed(params.buffer.size); - if (buffer_result.is_error()) - return -ENOMEM; - auto& host_buffer = buffer_result.value(); - - Syscall::SC_realpath_params host_params; - host_params.path = { (char const*)path.data(), path.size() }; - host_params.buffer = { (char*)host_buffer.data(), host_buffer.size() }; - int rc = syscall(SC_realpath, &host_params); - if (rc < 0) - return rc; - mmu().copy_to_vm((FlatPtr)params.buffer.data, host_buffer.data(), host_buffer.size()); - return rc; -} - -int Emulator::virt$gethostname(FlatPtr buffer, ssize_t buffer_size) -{ - if (buffer_size < 0) - return -EINVAL; - auto buffer_result = ByteBuffer::create_zeroed(buffer_size); - if (buffer_result.is_error()) - return -ENOMEM; - auto& host_buffer = buffer_result.value(); - int rc = syscall(SC_gethostname, host_buffer.data(), host_buffer.size()); - if (rc < 0) - return rc; - mmu().copy_to_vm(buffer, host_buffer.data(), host_buffer.size()); - return rc; -} - -int Emulator::virt$sigaction(int signum, FlatPtr act, FlatPtr oldact) -{ - if (signum == SIGKILL) { - reportln("Attempted to sigaction() with SIGKILL"sv); - return -EINVAL; - } - - if (signum <= 0 || signum >= NSIG) - return -EINVAL; - - struct sigaction host_act; - mmu().copy_from_vm(&host_act, act, sizeof(host_act)); - - auto& handler = m_signal_handler[signum]; - handler.handler = (FlatPtr)host_act.sa_handler; - handler.mask = host_act.sa_mask; - handler.flags = host_act.sa_flags; - - if (oldact) { - struct sigaction host_oldact; - auto& old_handler = m_signal_handler[signum]; - host_oldact.sa_handler = (void (*)(int))(old_handler.handler); - host_oldact.sa_mask = old_handler.mask; - host_oldact.sa_flags = old_handler.flags; - mmu().copy_to_vm(oldact, &host_oldact, sizeof(host_oldact)); - } - return 0; -} - -int Emulator::virt$sigprocmask(int how, FlatPtr set, FlatPtr old_set) -{ - if (old_set) { - mmu().copy_to_vm(old_set, &m_signal_mask, sizeof(sigset_t)); - } - if (set) { - sigset_t set_value; - mmu().copy_from_vm(&set_value, set, sizeof(sigset_t)); - switch (how) { - case SIG_BLOCK: - m_signal_mask |= set_value; - break; - case SIG_SETMASK: - m_signal_mask = set_value; - break; - case SIG_UNBLOCK: - m_signal_mask &= ~set_value; - break; - default: - return -EINVAL; - } - } - return 0; -} - -int Emulator::virt$sigreturn() -{ - u32 stack_ptr = m_cpu->esp().value(); - auto local_pop = [&]() { - auto value = m_cpu->read_memory({ m_cpu->ss(), stack_ptr }); - stack_ptr += sizeof(T); - return value; - }; - - // State from signal trampoline (note that we're assuming i386 here): - // saved_ax, ucontext, signal_info, fpu_state. - - // Drop the FPU state - // FIXME: Read and restore from this. - stack_ptr += 512; - - // Drop the signal info - stack_ptr += sizeof(siginfo_t); - - auto ucontext = local_pop.operator()(); - - auto eax = local_pop.operator()(); - - m_signal_mask = ucontext.value().uc_sigmask; - - auto mcontext_slice = ucontext.slice<&ucontext_t::uc_mcontext>(); - - m_cpu->set_edi(mcontext_slice.slice<&__mcontext::edi>()); - m_cpu->set_esi(mcontext_slice.slice<&__mcontext::esi>()); - m_cpu->set_ebp(mcontext_slice.slice<&__mcontext::ebp>()); - m_cpu->set_esp(mcontext_slice.slice<&__mcontext::esp>()); - m_cpu->set_ebx(mcontext_slice.slice<&__mcontext::ebx>()); - m_cpu->set_edx(mcontext_slice.slice<&__mcontext::edx>()); - m_cpu->set_ecx(mcontext_slice.slice<&__mcontext::ecx>()); - m_cpu->set_eax(mcontext_slice.slice<&__mcontext::eax>()); - m_cpu->set_eip(mcontext_slice.value().eip); - m_cpu->set_eflags(mcontext_slice.slice<&__mcontext::eflags>()); - - // FIXME: We're dropping the shadow bits here. - return eax.value(); -} - -int Emulator::virt$getpgrp() -{ - return syscall(SC_getpgrp); -} - -int Emulator::virt$getpgid(pid_t pid) -{ - return syscall(SC_getpgid, pid); -} - -int Emulator::virt$setpgid(pid_t pid, pid_t pgid) -{ - return syscall(SC_setpgid, pid, pgid); -} - -int Emulator::virt$getcwd(FlatPtr buffer, size_t buffer_size) -{ - auto buffer_result = ByteBuffer::create_zeroed(buffer_size); - if (buffer_result.is_error()) - return -ENOMEM; - auto& host_buffer = buffer_result.value(); - int rc = syscall(SC_getcwd, host_buffer.data(), host_buffer.size()); - if (rc < 0) - return rc; - mmu().copy_to_vm(buffer, host_buffer.data(), host_buffer.size()); - return rc; -} - -int Emulator::virt$getsid(pid_t pid) -{ - return syscall(SC_getsid, pid); -} - -int Emulator::virt$faccessat(FlatPtr params_addr) -{ - Syscall::SC_faccessat_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - - auto host_path = mmu().copy_buffer_from_vm(reinterpret_cast(params.pathname.characters), params.pathname.length); - Syscall::SC_faccessat_params host_params = params; - host_params.pathname = { reinterpret_cast(host_path.data()), host_path.size() }; - - return syscall(SC_faccessat, &host_params); -} - -int Emulator::virt$waitid(FlatPtr params_addr) -{ - Syscall::SC_waitid_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - - Syscall::SC_waitid_params host_params = params; - siginfo info {}; - host_params.infop = &info; - - int rc = syscall(SC_waitid, &host_params); - if (rc < 0) - return rc; - - if (info.si_addr) { - // FIXME: Translate this somehow once we actually start setting it in the kernel. - dbgln("si_addr is set to {:p}, I did not expect this!", info.si_addr); - TODO(); - } - - if (params.infop) - mmu().copy_to_vm((FlatPtr)params.infop, &info, sizeof(info)); - - return rc; -} - -int Emulator::virt$chdir(FlatPtr path, size_t path_length) -{ - auto host_path = mmu().copy_buffer_from_vm(path, path_length); - return syscall(SC_chdir, host_path.data(), host_path.size()); -} - -int Emulator::virt$dup2(int old_fd, int new_fd) -{ - return syscall(SC_dup2, old_fd, new_fd); -} - -int Emulator::virt$scheduler_get_parameters(FlatPtr user_addr) -{ - Syscall::SC_scheduler_parameters_params user_param; - mmu().copy_from_vm(&user_param, user_addr, sizeof(user_param)); - auto rc = syscall(SC_scheduler_get_parameters, &user_param); - mmu().copy_to_vm(user_addr, &user_param, sizeof(user_param)); - return rc; -} - -int Emulator::virt$scheduler_set_parameters(FlatPtr user_addr) -{ - Syscall::SC_scheduler_parameters_params user_param; - mmu().copy_from_vm(&user_param, user_addr, sizeof(user_param)); - return syscall(SC_scheduler_set_parameters, &user_param); -} - -pid_t Emulator::virt$setsid() -{ - return syscall(SC_setsid); -} - -int Emulator::virt$create_inode_watcher(unsigned flags) -{ - return syscall(SC_create_inode_watcher, flags); -} - -int Emulator::virt$inode_watcher_add_watch(FlatPtr params_addr) -{ - Syscall::SC_inode_watcher_add_watch_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - return syscall(SC_inode_watcher_add_watch, ¶ms); -} - -int Emulator::virt$inode_watcher_remove_watch(int fd, int wd) -{ - return syscall(SC_inode_watcher_add_watch, fd, wd); -} - -int Emulator::virt$clock_nanosleep(FlatPtr params_addr) -{ - Syscall::SC_clock_nanosleep_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - - timespec requested_sleep; - mmu().copy_from_vm(&requested_sleep, (FlatPtr)params.requested_sleep, sizeof(timespec)); - params.requested_sleep = &requested_sleep; - - auto* remaining_vm_addr = params.remaining_sleep; - timespec remaining { 0, 0 }; - params.remaining_sleep = &remaining; - - int rc = syscall(SC_clock_nanosleep, ¶ms); - if (remaining_vm_addr) - mmu().copy_to_vm((FlatPtr)remaining_vm_addr, &remaining, sizeof(timespec)); - - return rc; -} - -int Emulator::virt$readlink(FlatPtr params_addr) -{ - Syscall::SC_readlink_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - - auto path = mmu().copy_buffer_from_vm((FlatPtr)params.path.characters, params.path.length); - auto buffer_result = ByteBuffer::create_zeroed(params.buffer.size); - if (buffer_result.is_error()) - return -ENOMEM; - auto& host_buffer = buffer_result.value(); - - Syscall::SC_readlink_params host_params; - host_params.path = { (char const*)path.data(), path.size() }; - host_params.buffer = { (char*)host_buffer.data(), host_buffer.size() }; - int rc = syscall(SC_readlink, &host_params); - if (rc < 0) - return rc; - mmu().copy_to_vm((FlatPtr)params.buffer.data, host_buffer.data(), host_buffer.size()); - return rc; -} - -u32 Emulator::virt$allocate_tls(FlatPtr initial_data, size_t size) -{ - // TODO: This matches what Thread::make_thread_specific_region does. The kernel - // ends up allocating one more page. Figure out if this is intentional. - auto region_size = align_up_to(size, PAGE_SIZE) + PAGE_SIZE; - constexpr auto tls_location = VirtualAddress(0x20000000); - m_range_allocator.reserve_user_range(tls_location, region_size); - auto tcb_region = make(tls_location.get(), region_size); - - size_t offset = 0; - while (size - offset > 0) { - u8 buffer[512]; - size_t read_bytes = min(sizeof(buffer), size - offset); - mmu().copy_from_vm(buffer, initial_data + offset, read_bytes); - memcpy(tcb_region->data() + offset, buffer, read_bytes); - offset += read_bytes; - } - memset(tcb_region->shadow_data(), 0x01, size); - - auto tls_region = make(0, 4); - tls_region->write32(0, shadow_wrap_as_initialized(tcb_region->base() + (u32)size)); - memset(tls_region->shadow_data(), 0x01, 4); - - u32 tls_base = tcb_region->base(); - mmu().add_region(move(tcb_region)); - mmu().set_tls_region(move(tls_region)); - return tls_base; -} - -u32 Emulator::virt$sysconf(u32 name) -{ - return syscall(SC_sysconf, name); -} - -int Emulator::virt$annotate_mapping(FlatPtr) -{ - // FIXME: Implement this. - return 0; -} - -int Emulator::virt$futex(FlatPtr params_addr) -{ - Syscall::SC_futex_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - - // FIXME: Implement this. - return 0; -} - -int Emulator::virt$poll(FlatPtr params_addr) -{ - Syscall::SC_poll_params params; - mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); - - if (params.nfds >= FD_SETSIZE) - return EINVAL; - - Vector fds; - struct timespec timeout; - u32 sigmask; - - if (params.fds) - mmu().copy_from_vm(fds.data(), (FlatPtr)params.fds, sizeof(pollfd) * params.nfds); - if (params.timeout) - mmu().copy_from_vm(&timeout, (FlatPtr)params.timeout, sizeof(timeout)); - if (params.sigmask) - mmu().copy_from_vm(&sigmask, (FlatPtr)params.sigmask, sizeof(sigmask)); - - int rc = ppoll(params.fds ? fds.data() : nullptr, params.nfds, params.timeout ? &timeout : nullptr, params.sigmask ? &sigmask : nullptr); - if (rc < 0) - return -errno; - - if (params.fds) - mmu().copy_to_vm((FlatPtr)params.fds, fds.data(), sizeof(pollfd) * params.nfds); - if (params.timeout) - mmu().copy_to_vm((FlatPtr)params.timeout, &timeout, sizeof(timeout)); - - return rc; -} -} diff --git a/Userland/DevTools/UserspaceEmulator/MallocTracer.cpp b/Userland/DevTools/UserspaceEmulator/MallocTracer.cpp deleted file mode 100644 index a31c8a6ba7..0000000000 --- a/Userland/DevTools/UserspaceEmulator/MallocTracer.cpp +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2021, Tobias Christiansen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "MallocTracer.h" -#include "Emulator.h" -#include "MmapRegion.h" -#include -#include -#include -#include -#include - -namespace UserspaceEmulator { - -MallocTracer::MallocTracer(Emulator& emulator) - : m_emulator(emulator) -{ -} - -template -inline void MallocTracer::for_each_mallocation(Callback callback) const -{ - m_emulator.mmu().for_each_region([&](auto& region) { - if (is(region) && static_cast(region).is_malloc_block()) { - auto* malloc_data = static_cast(region).malloc_metadata(); - for (auto& mallocation : malloc_data->mallocations) { - if (mallocation.used && callback(mallocation) == IterationDecision::Break) - return IterationDecision::Break; - } - } - return IterationDecision::Continue; - }); -} - -void MallocTracer::update_metadata(MmapRegion& mmap_region, size_t chunk_size) -{ - mmap_region.set_malloc_metadata({}, - adopt_own(*new MallocRegionMetadata { - .region = mmap_region, - .address = mmap_region.base(), - .chunk_size = chunk_size, - .mallocations = {}, - })); - auto& malloc_data = *mmap_region.malloc_metadata(); - - bool is_chunked_block = malloc_data.chunk_size <= size_classes[num_size_classes - 1]; - if (is_chunked_block) - malloc_data.mallocations.resize((ChunkedBlock::block_size - sizeof(ChunkedBlock)) / malloc_data.chunk_size); - else - malloc_data.mallocations.resize(1); - - // Mark the containing mmap region as a malloc block! - mmap_region.set_malloc(true); -} - -void MallocTracer::target_did_malloc(Badge, FlatPtr address, size_t size) -{ - if (m_emulator.is_in_loader_code()) - return; - auto* region = m_emulator.mmu().find_region({ 0x23, address }); - VERIFY(region); - auto& mmap_region = verify_cast(*region); - - auto* shadow_bits = mmap_region.shadow_data() + address - mmap_region.base(); - memset(shadow_bits, 0, size); - - if (auto* existing_mallocation = find_mallocation(address)) { - VERIFY(existing_mallocation->freed); - existing_mallocation->size = size; - existing_mallocation->freed = false; - existing_mallocation->malloc_backtrace = m_emulator.raw_backtrace(); - existing_mallocation->free_backtrace.clear(); - return; - } - - if (!mmap_region.is_malloc_block()) { - auto chunk_size = mmap_region.read32(offsetof(CommonHeader, m_size)).value(); - update_metadata(mmap_region, chunk_size); - } - auto* mallocation = mmap_region.malloc_metadata()->mallocation_for_address(address); - VERIFY(mallocation); - *mallocation = { address, size, true, false, m_emulator.raw_backtrace(), Vector() }; -} - -void MallocTracer::target_did_change_chunk_size(Badge, FlatPtr block, size_t chunk_size) -{ - if (m_emulator.is_in_loader_code()) - return; - auto* region = m_emulator.mmu().find_region({ 0x23, block }); - VERIFY(region); - auto& mmap_region = verify_cast(*region); - update_metadata(mmap_region, chunk_size); -} - -ALWAYS_INLINE Mallocation* MallocRegionMetadata::mallocation_for_address(FlatPtr address) const -{ - auto index = chunk_index_for_address(address); - if (!index.has_value()) - return nullptr; - return &const_cast(this->mallocations[index.value()]); -} - -ALWAYS_INLINE Optional MallocRegionMetadata::chunk_index_for_address(FlatPtr address) const -{ - bool is_chunked_block = chunk_size <= size_classes[num_size_classes - 1]; - if (!is_chunked_block) { - // This is a BigAllocationBlock - return 0; - } - auto offset_into_block = address - this->address; - if (offset_into_block < sizeof(ChunkedBlock)) - return 0; - auto chunk_offset = offset_into_block - sizeof(ChunkedBlock); - auto chunk_index = chunk_offset / this->chunk_size; - if (chunk_index >= mallocations.size()) - return {}; - return chunk_index; -} - -void MallocTracer::target_did_free(Badge, FlatPtr address) -{ - if (!address) - return; - if (m_emulator.is_in_loader_code()) - return; - - if (auto* mallocation = find_mallocation(address)) { - if (mallocation->freed) { - reportln("\n=={}== \033[31;1mDouble free()\033[0m, {:p}"sv, getpid(), address); - reportln("=={}== Address {} has already been passed to free()"sv, getpid(), address); - m_emulator.dump_backtrace(); - } else { - mallocation->freed = true; - mallocation->free_backtrace = m_emulator.raw_backtrace(); - } - return; - } - - reportln("\n=={}== \033[31;1mInvalid free()\033[0m, {:p}"sv, getpid(), address); - reportln("=={}== Address {} has never been returned by malloc()"sv, getpid(), address); - m_emulator.dump_backtrace(); -} - -void MallocTracer::target_did_realloc(Badge, FlatPtr address, size_t size) -{ - if (m_emulator.is_in_loader_code()) - return; - auto* region = m_emulator.mmu().find_region({ 0x23, address }); - VERIFY(region); - auto& mmap_region = verify_cast(*region); - - VERIFY(mmap_region.is_malloc_block()); - - auto* existing_mallocation = find_mallocation(address); - VERIFY(existing_mallocation); - VERIFY(!existing_mallocation->freed); - - size_t old_size = existing_mallocation->size; - - auto* shadow_bits = mmap_region.shadow_data() + address - mmap_region.base(); - - if (size > old_size) { - memset(shadow_bits + old_size, 1, size - old_size); - } else { - memset(shadow_bits + size, 1, old_size - size); - } - - existing_mallocation->size = size; - // FIXME: Should we track malloc/realloc backtrace separately perhaps? - existing_mallocation->malloc_backtrace = m_emulator.raw_backtrace(); -} - -Mallocation* MallocTracer::find_mallocation(FlatPtr address) -{ - auto* region = m_emulator.mmu().find_region({ 0x23, address }); - if (!region) - return nullptr; - return find_mallocation(*region, address); -} - -Mallocation* MallocTracer::find_mallocation_before(FlatPtr address) -{ - Mallocation* found_mallocation = nullptr; - for_each_mallocation([&](auto& mallocation) { - if (mallocation.address >= address) - return IterationDecision::Continue; - if (!found_mallocation || (mallocation.address > found_mallocation->address)) - found_mallocation = const_cast(&mallocation); - return IterationDecision::Continue; - }); - return found_mallocation; -} - -Mallocation* MallocTracer::find_mallocation_after(FlatPtr address) -{ - Mallocation* found_mallocation = nullptr; - for_each_mallocation([&](auto& mallocation) { - if (mallocation.address <= address) - return IterationDecision::Continue; - if (!found_mallocation || (mallocation.address < found_mallocation->address)) - found_mallocation = const_cast(&mallocation); - return IterationDecision::Continue; - }); - return found_mallocation; -} - -void MallocTracer::audit_read(Region const& region, FlatPtr address, size_t size) -{ - if (!m_auditing_enabled) - return; - - if (m_emulator.is_memory_auditing_suppressed()) { - return; - } - - if (m_emulator.is_in_libsystem()) { - return; - } - - if (m_emulator.is_in_loader_code()) { - return; - } - - auto* mallocation = find_mallocation(region, address); - - if (!mallocation) { - reportln("\n=={}== \033[31;1mHeap buffer overflow\033[0m, invalid {}-byte read at address {:p}"sv, getpid(), size, address); - m_emulator.dump_backtrace(); - auto* mallocation_before = find_mallocation_before(address); - auto* mallocation_after = find_mallocation_after(address); - size_t distance_to_mallocation_before = mallocation_before ? (address - mallocation_before->address - mallocation_before->size) : 0; - size_t distance_to_mallocation_after = mallocation_after ? (mallocation_after->address - address) : 0; - if (mallocation_before && (!mallocation_after || distance_to_mallocation_before < distance_to_mallocation_after)) { - reportln("=={}== Address is {} byte(s) after block of size {}, identity {:p}, allocated at:"sv, getpid(), distance_to_mallocation_before, mallocation_before->size, mallocation_before->address); - m_emulator.dump_backtrace(mallocation_before->malloc_backtrace); - return; - } - if (mallocation_after && (!mallocation_before || distance_to_mallocation_after < distance_to_mallocation_before)) { - reportln("=={}== Address is {} byte(s) before block of size {}, identity {:p}, allocated at:"sv, getpid(), distance_to_mallocation_after, mallocation_after->size, mallocation_after->address); - m_emulator.dump_backtrace(mallocation_after->malloc_backtrace); - } - return; - } - - size_t offset_into_mallocation = address - mallocation->address; - - if (mallocation->freed) { - reportln("\n=={}== \033[31;1mUse-after-free\033[0m, invalid {}-byte read at address {:p}"sv, getpid(), size, address); - m_emulator.dump_backtrace(); - reportln("=={}== Address is {} byte(s) into block of size {}, allocated at:"sv, getpid(), offset_into_mallocation, mallocation->size); - m_emulator.dump_backtrace(mallocation->malloc_backtrace); - reportln("=={}== Later freed at:"sv, getpid()); - m_emulator.dump_backtrace(mallocation->free_backtrace); - return; - } -} - -void MallocTracer::audit_write(Region const& region, FlatPtr address, size_t size) -{ - if (!m_auditing_enabled) - return; - - if (m_emulator.is_memory_auditing_suppressed()) { - return; - } - - if (m_emulator.is_in_loader_code()) { - return; - } - - auto* mallocation = find_mallocation(region, address); - if (!mallocation) { - reportln("\n=={}== \033[31;1mHeap buffer overflow\033[0m, invalid {}-byte write at address {:p}"sv, getpid(), size, address); - m_emulator.dump_backtrace(); - auto* mallocation_before = find_mallocation_before(address); - auto* mallocation_after = find_mallocation_after(address); - size_t distance_to_mallocation_before = mallocation_before ? (address - mallocation_before->address - mallocation_before->size) : 0; - size_t distance_to_mallocation_after = mallocation_after ? (mallocation_after->address - address) : 0; - if (mallocation_before && (!mallocation_after || distance_to_mallocation_before < distance_to_mallocation_after)) { - reportln("=={}== Address is {} byte(s) after block of size {}, identity {:p}, allocated at:"sv, getpid(), distance_to_mallocation_before, mallocation_before->size, mallocation_before->address); - m_emulator.dump_backtrace(mallocation_before->malloc_backtrace); - return; - } - if (mallocation_after && (!mallocation_before || distance_to_mallocation_after < distance_to_mallocation_before)) { - reportln("=={}== Address is {} byte(s) before block of size {}, identity {:p}, allocated at:"sv, getpid(), distance_to_mallocation_after, mallocation_after->size, mallocation_after->address); - m_emulator.dump_backtrace(mallocation_after->malloc_backtrace); - } - return; - } - - size_t offset_into_mallocation = address - mallocation->address; - - if (mallocation->freed) { - reportln("\n=={}== \033[31;1mUse-after-free\033[0m, invalid {}-byte write at address {:p}"sv, getpid(), size, address); - m_emulator.dump_backtrace(); - reportln("=={}== Address is {} byte(s) into block of size {}, allocated at:"sv, getpid(), offset_into_mallocation, mallocation->size); - m_emulator.dump_backtrace(mallocation->malloc_backtrace); - reportln("=={}== Later freed at:"sv, getpid()); - m_emulator.dump_backtrace(mallocation->free_backtrace); - return; - } -} - -void MallocTracer::populate_memory_graph() -{ - // Create Node for each live Mallocation - for_each_mallocation([&](auto& mallocation) { - if (mallocation.freed) - return IterationDecision::Continue; - m_memory_graph.set(mallocation.address, {}); - return IterationDecision::Continue; - }); - - // Find pointers from each memory region to another - for_each_mallocation([&](auto& mallocation) { - if (mallocation.freed) - return IterationDecision::Continue; - - size_t pointers_in_mallocation = mallocation.size / sizeof(u32); - - auto& edges_from_mallocation = m_memory_graph.find(mallocation.address)->value; - - for (size_t i = 0; i < pointers_in_mallocation; ++i) { - auto value = m_emulator.mmu().read32({ 0x23, mallocation.address + i * sizeof(u32) }); - auto other_address = value.value(); - if (!value.is_uninitialized() && m_memory_graph.contains(value.value())) { - if constexpr (REACHABLE_DEBUG) - reportln("region/mallocation {:p} is reachable from other mallocation {:p}"sv, other_address, mallocation.address); - edges_from_mallocation.edges_from_node.append(other_address); - } - } - return IterationDecision::Continue; - }); - - // Find mallocations that are pointed to by other regions - Vector reachable_mallocations = {}; - m_emulator.mmu().for_each_region([&](auto& region) { - // Skip the stack - if (region.is_stack()) - return IterationDecision::Continue; - if (region.is_text()) - return IterationDecision::Continue; - if (!region.is_readable()) - return IterationDecision::Continue; - // Skip malloc blocks - if (is(region) && static_cast(region).is_malloc_block()) - return IterationDecision::Continue; - - size_t pointers_in_region = region.size() / sizeof(u32); - - for (size_t i = 0; i < pointers_in_region; ++i) { - auto value = region.read32(i * sizeof(u32)); - auto other_address = value.value(); - if (!value.is_uninitialized() && m_memory_graph.contains(value.value())) { - if constexpr (REACHABLE_DEBUG) - reportln("region/mallocation {:p} is reachable from region {:p}-{:p}"sv, other_address, region.base(), region.end() - 1); - m_memory_graph.find(other_address)->value.is_reachable = true; - reachable_mallocations.append(other_address); - } - } - return IterationDecision::Continue; - }); - - // Propagate reachability - // There are probably better ways to do that - Vector visited = {}; - for (size_t i = 0; i < reachable_mallocations.size(); ++i) { - auto reachable = reachable_mallocations.at(i); - if (visited.contains_slow(reachable)) - continue; - visited.append(reachable); - auto& mallocation_node = m_memory_graph.find(reachable)->value; - - if (!mallocation_node.is_reachable) - mallocation_node.is_reachable = true; - - for (auto& edge : mallocation_node.edges_from_node) { - reachable_mallocations.append(edge); - } - } -} - -void MallocTracer::dump_memory_graph() -{ - for (auto& key : m_memory_graph.keys()) { - auto value = m_memory_graph.find(key)->value; - dbgln("Block {:p} [{}reachable] ({} edges)", key, !value.is_reachable ? "not " : "", value.edges_from_node.size()); - for (auto& edge : value.edges_from_node) { - dbgln(" -> {:p}", edge); - } - } -} - -void MallocTracer::dump_leak_report() -{ - TemporaryChange change(m_auditing_enabled, false); - - size_t bytes_leaked = 0; - size_t leaks_found = 0; - - populate_memory_graph(); - - if constexpr (REACHABLE_DEBUG) - dump_memory_graph(); - - for_each_mallocation([&](auto& mallocation) { - if (mallocation.freed) - return IterationDecision::Continue; - - auto& value = m_memory_graph.find(mallocation.address)->value; - - if (value.is_reachable) - return IterationDecision::Continue; - ++leaks_found; - bytes_leaked += mallocation.size; - reportln("\n=={}== \033[31;1mLeak\033[0m, {}-byte allocation at address {:p}"sv, getpid(), mallocation.size, mallocation.address); - m_emulator.dump_backtrace(mallocation.malloc_backtrace); - return IterationDecision::Continue; - }); - - if (!leaks_found) - reportln("\n=={}== \033[32;1mNo leaks found!\033[0m"sv, getpid()); - else - reportln("\n=={}== \033[31;1m{} leak(s) found: {} byte(s) leaked\033[0m"sv, getpid(), leaks_found, bytes_leaked); -} -} diff --git a/Userland/DevTools/UserspaceEmulator/MallocTracer.h b/Userland/DevTools/UserspaceEmulator/MallocTracer.h deleted file mode 100644 index fe43d79987..0000000000 --- a/Userland/DevTools/UserspaceEmulator/MallocTracer.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "MmapRegion.h" -#include "SoftMMU.h" -#include -#include -#include -#include -#include - -namespace UserspaceEmulator { - -class Emulator; -class SoftCPU; - -struct GraphNode { - Vector edges_from_node {}; - - bool is_reachable { false }; -}; - -using MemoryGraph = HashMap; - -struct Mallocation { - bool contains(FlatPtr a) const - { - return a >= address && a < (address + size); - } - - FlatPtr address { 0 }; - size_t size { 0 }; - bool used { false }; - bool freed { false }; - - Vector malloc_backtrace; - Vector free_backtrace; -}; - -class MallocRegionMetadata { -public: - MmapRegion& region; - FlatPtr address { 0 }; - size_t chunk_size { 0 }; - - Optional chunk_index_for_address(FlatPtr) const; - Mallocation* mallocation_for_address(FlatPtr) const; - - Vector mallocations; -}; - -class MallocTracer { -public: - explicit MallocTracer(Emulator&); - - void target_did_malloc(Badge, FlatPtr address, size_t); - void target_did_free(Badge, FlatPtr address); - void target_did_realloc(Badge, FlatPtr address, size_t); - void target_did_change_chunk_size(Badge, FlatPtr, size_t); - - void audit_read(Region const&, FlatPtr address, size_t); - void audit_write(Region const&, FlatPtr address, size_t); - - void dump_leak_report(); - -private: - template - void for_each_mallocation(Callback callback) const; - - Mallocation* find_mallocation(Region const&, FlatPtr); - Mallocation* find_mallocation(FlatPtr); - Mallocation* find_mallocation_before(FlatPtr); - Mallocation* find_mallocation_after(FlatPtr); - - void dump_memory_graph(); - void populate_memory_graph(); - - void update_metadata(MmapRegion& mmap_region, size_t chunk_size); - - Emulator& m_emulator; - - MemoryGraph m_memory_graph {}; - - bool m_auditing_enabled { true }; -}; - -ALWAYS_INLINE Mallocation* MallocTracer::find_mallocation(Region const& region, FlatPtr address) -{ - if (!is(region)) - return nullptr; - if (!static_cast(region).is_malloc_block()) - return nullptr; - auto* malloc_data = static_cast(const_cast(region)).malloc_metadata(); - if (!malloc_data) - return nullptr; - auto* mallocation = malloc_data->mallocation_for_address(address); - if (!mallocation) - return nullptr; - if (!mallocation->used) - return nullptr; - if (!mallocation->contains(address)) - return nullptr; - return mallocation; -} -} diff --git a/Userland/DevTools/UserspaceEmulator/MmapRegion.cpp b/Userland/DevTools/UserspaceEmulator/MmapRegion.cpp deleted file mode 100644 index 9373862589..0000000000 --- a/Userland/DevTools/UserspaceEmulator/MmapRegion.cpp +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "MmapRegion.h" -#include "Emulator.h" -#include -#include -#include - -namespace UserspaceEmulator { - -static void* mmap_initialized(size_t bytes, char initial_value, char const* name) -{ - auto* ptr = mmap_with_name(nullptr, bytes, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0, name); - VERIFY(ptr != MAP_FAILED); - memset(ptr, initial_value, bytes); - return ptr; -} - -static void free_pages(void* ptr, size_t bytes) -{ - int rc = munmap(ptr, bytes); - VERIFY(rc == 0); -} - -NonnullOwnPtr MmapRegion::create_anonymous(u32 base, u32 size, u32 prot, ByteString name) -{ - auto* data = (u8*)mmap_initialized(size, 0, ByteString::formatted("(UE) {}", name).characters()); - auto* shadow_data = (u8*)mmap_initialized(size, 1, "MmapRegion ShadowData"); - auto region = adopt_own(*new MmapRegion(base, size, prot, data, shadow_data)); - region->m_name = move(name); - return region; -} - -NonnullOwnPtr MmapRegion::create_file_backed(u32 base, u32 size, u32 prot, int flags, int fd, off_t offset, ByteString name) -{ - // Since we put the memory to an arbitrary location, do not pass MAP_FIXED and MAP_FIXED_NOREPLACE to the Kernel. - auto real_flags = flags & ~(MAP_FIXED | MAP_FIXED_NOREPLACE); - auto* data = (u8*)mmap_with_name(nullptr, size, prot, real_flags, fd, offset, name.is_empty() ? nullptr : ByteString::formatted("(UE) {}", name).characters()); - VERIFY(data != MAP_FAILED); - auto* shadow_data = (u8*)mmap_initialized(size, 1, "MmapRegion ShadowData"); - auto region = adopt_own(*new MmapRegion(base, size, prot, data, shadow_data)); - region->m_file_backed = true; - region->m_name = move(name); - return region; -} - -MmapRegion::MmapRegion(u32 base, u32 size, int prot, u8* data, u8* shadow_data) - : Region(base, size, true) - , m_data(data) - , m_shadow_data(shadow_data) -{ - set_prot(prot); -} - -MmapRegion::~MmapRegion() -{ - free_pages(m_data, size()); - free_pages(m_shadow_data, size()); -} - -ValueWithShadow MmapRegion::read8(FlatPtr offset) -{ - if (!is_readable()) { - reportln("8-bit read from unreadable MmapRegion @ {:p}"sv, base() + offset); - emulator().dump_backtrace(); - TODO(); - } - - if (is_malloc_block()) { - if (auto* tracer = emulator().malloc_tracer()) - tracer->audit_read(*this, base() + offset, 1); - } - - VERIFY(offset < size()); - return { m_data[offset], m_shadow_data[offset] }; -} - -ValueWithShadow MmapRegion::read16(u32 offset) -{ - if (!is_readable()) { - reportln("16-bit read from unreadable MmapRegion @ {:p}"sv, base() + offset); - emulator().dump_backtrace(); - TODO(); - } - - if (is_malloc_block()) { - if (auto* tracer = emulator().malloc_tracer()) - tracer->audit_read(*this, base() + offset, 2); - } - - VERIFY(offset + 1 < size()); - u16 value, shadow; - ByteReader::load(m_data + offset, value); - ByteReader::load(m_shadow_data + offset, shadow); - - return { value, shadow }; -} - -ValueWithShadow MmapRegion::read32(u32 offset) -{ - if (!is_readable()) { - reportln("32-bit read from unreadable MmapRegion @ {:p}"sv, base() + offset); - emulator().dump_backtrace(); - TODO(); - } - - if (is_malloc_block()) { - if (auto* tracer = emulator().malloc_tracer()) - tracer->audit_read(*this, base() + offset, 4); - } - - VERIFY(offset + 3 < size()); - u32 value, shadow; - ByteReader::load(m_data + offset, value); - ByteReader::load(m_shadow_data + offset, shadow); - - return { value, shadow }; -} - -ValueWithShadow MmapRegion::read64(u32 offset) -{ - if (!is_readable()) { - reportln("64-bit read from unreadable MmapRegion @ {:p}"sv, base() + offset); - emulator().dump_backtrace(); - TODO(); - } - - if (is_malloc_block()) { - if (auto* tracer = emulator().malloc_tracer()) - tracer->audit_read(*this, base() + offset, 8); - } - - VERIFY(offset + 7 < size()); - u64 value, shadow; - ByteReader::load(m_data + offset, value); - ByteReader::load(m_shadow_data + offset, shadow); - - return { value, shadow }; -} - -ValueWithShadow MmapRegion::read128(u32 offset) -{ - if (!is_readable()) { - reportln("128-bit read from unreadable MmapRegion @ {:p}"sv, base() + offset); - emulator().dump_backtrace(); - TODO(); - } - - if (is_malloc_block()) { - if (auto* tracer = emulator().malloc_tracer()) - tracer->audit_read(*this, base() + offset, 16); - } - - VERIFY(offset + 15 < size()); - u128 value, shadow; - ByteReader::load(m_data + offset, value); - ByteReader::load(m_shadow_data + offset, shadow); - return { value, shadow }; -} - -ValueWithShadow MmapRegion::read256(u32 offset) -{ - if (!is_readable()) { - reportln("256-bit read from unreadable MmapRegion @ {:p}"sv, base() + offset); - emulator().dump_backtrace(); - TODO(); - } - - if (is_malloc_block()) { - if (auto* tracer = emulator().malloc_tracer()) - tracer->audit_read(*this, base() + offset, 32); - } - - VERIFY(offset + 31 < size()); - u256 value, shadow; - ByteReader::load(m_data + offset, value); - ByteReader::load(m_shadow_data + offset, shadow); - return { value, shadow }; -} - -void MmapRegion::write8(u32 offset, ValueWithShadow value) -{ - if (!is_writable()) { - reportln("8-bit write from unwritable MmapRegion @ {:p}"sv, base() + offset); - emulator().dump_backtrace(); - TODO(); - } - - if (is_malloc_block()) { - if (auto* tracer = emulator().malloc_tracer()) - tracer->audit_write(*this, base() + offset, 1); - } - - VERIFY(offset < size()); - m_data[offset] = value.value(); - m_shadow_data[offset] = value.shadow()[0]; -} - -void MmapRegion::write16(u32 offset, ValueWithShadow value) -{ - if (!is_writable()) { - reportln("16-bit write from unwritable MmapRegion @ {:p}"sv, base() + offset); - emulator().dump_backtrace(); - TODO(); - } - - if (is_malloc_block()) { - if (auto* tracer = emulator().malloc_tracer()) - tracer->audit_write(*this, base() + offset, 2); - } - - VERIFY(offset + 1 < size()); - ByteReader::store(m_data + offset, value.value()); - ByteReader::store(m_shadow_data + offset, value.shadow()); -} - -void MmapRegion::write32(u32 offset, ValueWithShadow value) -{ - if (!is_writable()) { - reportln("32-bit write from unwritable MmapRegion @ {:p}"sv, base() + offset); - emulator().dump_backtrace(); - TODO(); - } - - if (is_malloc_block()) { - if (auto* tracer = emulator().malloc_tracer()) - tracer->audit_write(*this, base() + offset, 4); - } - - VERIFY(offset + 3 < size()); - VERIFY(m_data != m_shadow_data); - ByteReader::store(m_data + offset, value.value()); - ByteReader::store(m_shadow_data + offset, value.shadow()); -} - -void MmapRegion::write64(u32 offset, ValueWithShadow value) -{ - if (!is_writable()) { - reportln("64-bit write from unwritable MmapRegion @ {:p}"sv, base() + offset); - emulator().dump_backtrace(); - TODO(); - } - - if (is_malloc_block()) { - if (auto* tracer = emulator().malloc_tracer()) - tracer->audit_write(*this, base() + offset, 8); - } - - VERIFY(offset + 7 < size()); - VERIFY(m_data != m_shadow_data); - ByteReader::store(m_data + offset, value.value()); - ByteReader::store(m_shadow_data + offset, value.shadow()); -} - -void MmapRegion::write128(u32 offset, ValueWithShadow value) -{ - if (!is_writable()) { - reportln("128-bit write from unwritable MmapRegion @ {:p}"sv, base() + offset); - emulator().dump_backtrace(); - TODO(); - } - - if (is_malloc_block()) { - if (auto* tracer = emulator().malloc_tracer()) - tracer->audit_write(*this, base() + offset, 16); - } - VERIFY(offset + 15 < size()); - VERIFY(m_data != m_shadow_data); - ByteReader::store(m_data + offset, value.value()); - ByteReader::store(m_shadow_data + offset, value.shadow()); -} - -void MmapRegion::write256(u32 offset, ValueWithShadow value) -{ - if (!is_writable()) { - reportln("256-bit write from unwritable MmapRegion @ {:p}"sv, base() + offset); - emulator().dump_backtrace(); - TODO(); - } - - if (is_malloc_block()) { - if (auto* tracer = emulator().malloc_tracer()) - tracer->audit_write(*this, base() + offset, 32); - } - VERIFY(offset + 31 < size()); - VERIFY(m_data != m_shadow_data); - ByteReader::store(m_data + offset, value.value()); - ByteReader::store(m_shadow_data + offset, value.shadow()); -} - -NonnullOwnPtr MmapRegion::split_at(VirtualAddress offset) -{ - VERIFY(!m_malloc); - VERIFY(!m_malloc_metadata); - Range new_range = range(); - Range other_range = new_range.split_at(offset); - auto other_region = adopt_own(*new MmapRegion(other_range.base().get(), other_range.size(), prot(), data() + new_range.size(), shadow_data() + new_range.size())); - other_region->m_file_backed = m_file_backed; - other_region->m_name = m_name; - set_range(new_range); - return other_region; -} - -void MmapRegion::set_prot(int prot) -{ - set_readable(prot & PROT_READ); - set_writable(prot & PROT_WRITE); - set_executable(prot & PROT_EXEC); - if (m_file_backed) { - if (mprotect(m_data, size(), prot & ~PROT_EXEC) < 0) { - perror("MmapRegion::set_prot: mprotect"); - exit(1); - } - } -} - -void MmapRegion::set_name(ByteString name) -{ - m_name = move(name); - set_mmap_name(range().base().as_ptr(), range().size(), ByteString::formatted("(UE) {}", m_name).characters()); -} - -} diff --git a/Userland/DevTools/UserspaceEmulator/MmapRegion.h b/Userland/DevTools/UserspaceEmulator/MmapRegion.h deleted file mode 100644 index e4593ea04d..0000000000 --- a/Userland/DevTools/UserspaceEmulator/MmapRegion.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2020-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "SoftMMU.h" -#include - -namespace UserspaceEmulator { - -class MallocRegionMetadata; -class MallocTracer; - -class MmapRegion final : public Region { -public: - static NonnullOwnPtr create_anonymous(u32 base, u32 size, u32 prot, ByteString name); - static NonnullOwnPtr create_file_backed(u32 base, u32 size, u32 prot, int flags, int fd, off_t offset, ByteString name); - virtual ~MmapRegion() override; - - virtual ValueWithShadow read8(u32 offset) override; - virtual ValueWithShadow read16(u32 offset) override; - virtual ValueWithShadow read32(u32 offset) override; - virtual ValueWithShadow read64(u32 offset) override; - virtual ValueWithShadow read128(u32 offset) override; - virtual ValueWithShadow read256(u32 offset) override; - - virtual void write8(u32 offset, ValueWithShadow) override; - virtual void write16(u32 offset, ValueWithShadow) override; - virtual void write32(u32 offset, ValueWithShadow) override; - virtual void write64(u32 offset, ValueWithShadow) override; - virtual void write128(u32 offset, ValueWithShadow) override; - virtual void write256(u32 offset, ValueWithShadow) override; - - virtual u8* data() override { return m_data; } - virtual u8* shadow_data() override { return m_shadow_data; } - - bool is_malloc_block() const { return m_malloc; } - void set_malloc(bool b) { m_malloc = b; } - - NonnullOwnPtr split_at(VirtualAddress); - - int prot() const - { - return (is_readable() ? PROT_READ : 0) | (is_writable() ? PROT_WRITE : 0) | (is_executable() ? PROT_EXEC : 0); - } - void set_prot(int prot); - - MallocRegionMetadata* malloc_metadata() { return m_malloc_metadata; } - void set_malloc_metadata(Badge, NonnullOwnPtr metadata) { m_malloc_metadata = move(metadata); } - - ByteString const& name() const { return m_name; } - ByteString lib_name() const - { - if (m_name.contains("Loader.so"sv)) - return "Loader.so"; - auto const maybe_separator = m_name.find(':'); - if (!maybe_separator.has_value()) - return {}; - return m_name.substring(0, *maybe_separator); - } - void set_name(ByteString name); - -private: - MmapRegion(u32 base, u32 size, int prot, u8* data, u8* shadow_data); - - u8* m_data { nullptr }; - u8* m_shadow_data { nullptr }; - bool m_file_backed { false }; - bool m_malloc { false }; - - OwnPtr m_malloc_metadata; - ByteString m_name; -}; - -template<> -inline bool Region::fast_is() const { return m_mmap; } - -} diff --git a/Userland/DevTools/UserspaceEmulator/Range.cpp b/Userland/DevTools/UserspaceEmulator/Range.cpp deleted file mode 100644 index ca1e13b4f8..0000000000 --- a/Userland/DevTools/UserspaceEmulator/Range.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Range.h" -#include - -namespace UserspaceEmulator { - -Vector Range::carve(Range const& taken) const -{ - VERIFY((taken.size() % PAGE_SIZE) == 0); - Vector parts; - if (taken == *this) - return {}; - if (taken.base() > base()) - parts.append({ base(), taken.base().get() - base().get() }); - if (taken.end() < end()) - parts.append({ taken.end(), end().get() - taken.end().get() }); - return parts; -} - -} diff --git a/Userland/DevTools/UserspaceEmulator/Range.h b/Userland/DevTools/UserspaceEmulator/Range.h deleted file mode 100644 index 7bde305e24..0000000000 --- a/Userland/DevTools/UserspaceEmulator/Range.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace UserspaceEmulator { - -class Range { - friend class RangeAllocator; - -public: - Range() = delete; - Range(VirtualAddress base, size_t size) - : m_base(base) - , m_size(size) - { - } - - VirtualAddress base() const { return m_base; } - size_t size() const { return m_size; } - bool is_valid() const { return !m_base.is_null(); } - - bool contains(VirtualAddress vaddr) const { return vaddr >= base() && vaddr < end(); } - - VirtualAddress end() const { return m_base.offset(m_size); } - - bool operator==(Range const& other) const - { - return m_base == other.m_base && m_size == other.m_size; - } - - bool contains(VirtualAddress base, size_t size) const - { - if (base.offset(size) < base) - return false; - return base >= m_base && base.offset(size) <= end(); - } - - bool contains(Range const& other) const - { - return contains(other.base(), other.size()); - } - - Vector carve(Range const&) const; - - Range split_at(VirtualAddress address) - { - VERIFY(address.is_page_aligned()); - VERIFY(m_base < address); - size_t new_size = (address - m_base).get(); - VERIFY(new_size < m_size); - size_t other_size = m_size - new_size; - m_size = new_size; - return { address, other_size }; - } - -private: - VirtualAddress m_base; - size_t m_size { 0 }; -}; - -} - -namespace AK { -template<> -struct Traits : public DefaultTraits { - static constexpr bool is_trivial() { return true; } -}; -} diff --git a/Userland/DevTools/UserspaceEmulator/RangeAllocator.cpp b/Userland/DevTools/UserspaceEmulator/RangeAllocator.cpp deleted file mode 100644 index d673d7febd..0000000000 --- a/Userland/DevTools/UserspaceEmulator/RangeAllocator.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "RangeAllocator.h" -#include -#include -#include - -#define VM_GUARD_PAGES -#define PAGE_MASK ((FlatPtr)0xfffff000u) - -namespace UserspaceEmulator { - -RangeAllocator::RangeAllocator() - : m_total_range({}, 0) -{ -} - -void RangeAllocator::initialize_with_range(VirtualAddress base, size_t size) -{ - m_total_range = { base, size }; - m_available_ranges.append({ base, size }); -} - -void RangeAllocator::dump() const -{ - dbgln("RangeAllocator({})", this); - for (auto const& range : m_available_ranges) { - dbgln(" {:x} -> {:x}", range.base().get(), range.end().get() - 1); - } -} - -void RangeAllocator::carve_at_index(int index, Range const& range) -{ - auto remaining_parts = m_available_ranges[index].carve(range); - VERIFY(remaining_parts.size() >= 1); - VERIFY(m_total_range.contains(remaining_parts[0])); - m_available_ranges[index] = remaining_parts[0]; - if (remaining_parts.size() == 2) { - VERIFY(m_total_range.contains(remaining_parts[1])); - m_available_ranges.insert(index + 1, move(remaining_parts[1])); - } -} - -Optional RangeAllocator::allocate_randomized(size_t size, size_t alignment) -{ - if (!size) - return {}; - - VERIFY((size % PAGE_SIZE) == 0); - VERIFY((alignment % PAGE_SIZE) == 0); - - // FIXME: I'm sure there's a smarter way to do this. - static constexpr size_t maximum_randomization_attempts = 1000; - for (size_t i = 0; i < maximum_randomization_attempts; ++i) { - VirtualAddress random_address { round_up_to_power_of_two(get_random(), alignment) }; - - if (!m_total_range.contains(random_address, size)) - continue; - - auto range = allocate_specific(random_address, size); - if (range.has_value()) - return range; - } - - return allocate_anywhere(size, alignment); -} - -Optional RangeAllocator::allocate_anywhere(size_t size, size_t alignment) -{ - if (!size) - return {}; - - VERIFY((size % PAGE_SIZE) == 0); - VERIFY((alignment % PAGE_SIZE) == 0); - -#ifdef VM_GUARD_PAGES - // NOTE: We pad VM allocations with a guard page on each side. - if (Checked::addition_would_overflow(size, PAGE_SIZE * 2)) - return {}; - - size_t effective_size = size + PAGE_SIZE * 2; - size_t offset_from_effective_base = PAGE_SIZE; -#else - size_t effective_size = size; - size_t offset_from_effective_base = 0; -#endif - - if (Checked::addition_would_overflow(effective_size, alignment)) - return {}; - - for (size_t i = 0; i < m_available_ranges.size(); ++i) { - auto& available_range = m_available_ranges[i]; - // FIXME: This check is probably excluding some valid candidates when using a large alignment. - if (available_range.size() < (effective_size + alignment)) - continue; - - FlatPtr initial_base = available_range.base().offset(offset_from_effective_base).get(); - FlatPtr aligned_base = round_up_to_power_of_two(initial_base, alignment); - - Range allocated_range(VirtualAddress(aligned_base), size); - VERIFY(m_total_range.contains(allocated_range)); - - if (available_range == allocated_range) { - m_available_ranges.remove(i); - return allocated_range; - } - carve_at_index(i, allocated_range); - return allocated_range; - } - dbgln("RangeAllocator: Failed to allocate anywhere: size={}, alignment={}", size, alignment); - return {}; -} - -Optional RangeAllocator::allocate_specific(VirtualAddress base, size_t size) -{ - if (!size) - return {}; - - VERIFY(base.is_page_aligned()); - VERIFY((size % PAGE_SIZE) == 0); - - Range allocated_range(base, size); - if (!m_total_range.contains(allocated_range)) { - dbgln("Unallocatable mmap request?! {:p}+{:p}", base.get(), size); - return {}; - } - for (size_t i = 0; i < m_available_ranges.size(); ++i) { - auto& available_range = m_available_ranges[i]; - if (!available_range.contains(base, size)) - continue; - if (available_range == allocated_range) { - m_available_ranges.remove(i); - return allocated_range; - } - carve_at_index(i, allocated_range); - return allocated_range; - } - return {}; -} - -void RangeAllocator::deallocate(Range const& range) -{ - VERIFY(m_total_range.contains(range)); - VERIFY(range.size()); - VERIFY((range.size() % PAGE_SIZE) == 0); - VERIFY(range.base() < range.end()); - VERIFY(!m_available_ranges.is_empty()); - - size_t nearby_index = 0; - auto* existing_range = binary_search( - m_available_ranges.span(), - range, - &nearby_index, - [](auto& a, auto& b) { return a.base().get() - b.end().get(); }); - - size_t inserted_index = 0; - if (existing_range) { - existing_range->m_size += range.size(); - inserted_index = nearby_index; - } else { - m_available_ranges.insert_before_matching( - Range(range), [&](auto& entry) { - return entry.base() >= range.end(); - }, - nearby_index, &inserted_index); - } - - if (inserted_index < (m_available_ranges.size() - 1)) { - // We already merged with previous. Try to merge with next. - auto& inserted_range = m_available_ranges[inserted_index]; - auto& next_range = m_available_ranges[inserted_index + 1]; - if (inserted_range.end() == next_range.base()) { - inserted_range.m_size += next_range.size(); - m_available_ranges.remove(inserted_index + 1); - return; - } - } -} - -void RangeAllocator::reserve_user_range(VirtualAddress begin, size_t size) -{ - auto end = round_up_to_power_of_two(begin.offset(size).get(), PAGE_SIZE); - auto allocated_range = allocate_specific(begin.page_base(), end - begin.page_base().get()); - VERIFY(allocated_range.has_value()); -} - -} diff --git a/Userland/DevTools/UserspaceEmulator/RangeAllocator.h b/Userland/DevTools/UserspaceEmulator/RangeAllocator.h deleted file mode 100644 index 63f9155d1a..0000000000 --- a/Userland/DevTools/UserspaceEmulator/RangeAllocator.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Range.h" -#include - -namespace UserspaceEmulator { - -class RangeAllocator { -public: - RangeAllocator(); - - void initialize_with_range(VirtualAddress, size_t); - - Optional allocate_anywhere(size_t, size_t alignment = PAGE_SIZE); - Optional allocate_specific(VirtualAddress, size_t); - Optional allocate_randomized(size_t, size_t alignment); - void deallocate(Range const&); - - void reserve_user_range(VirtualAddress, size_t); - - void dump() const; - - bool contains(Range const& range) const { return m_total_range.contains(range); } - -private: - void carve_at_index(int, Range const&); - - Vector m_available_ranges; - Range m_total_range; -}; - -} diff --git a/Userland/DevTools/UserspaceEmulator/Region.cpp b/Userland/DevTools/UserspaceEmulator/Region.cpp deleted file mode 100644 index a4aaf72cef..0000000000 --- a/Userland/DevTools/UserspaceEmulator/Region.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Region.h" -#include "Emulator.h" - -namespace UserspaceEmulator { - -Region::Region(u32 base, u32 size, bool mmap) - : m_emulator(Emulator::the()) - , m_range(Range { VirtualAddress { base }, size }) - , m_mmap(mmap) -{ -} - -} diff --git a/Userland/DevTools/UserspaceEmulator/Region.h b/Userland/DevTools/UserspaceEmulator/Region.h deleted file mode 100644 index 79b8458dd8..0000000000 --- a/Userland/DevTools/UserspaceEmulator/Region.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Range.h" -#include "ValueWithShadow.h" -#include -#include -#include - -namespace UserspaceEmulator { - -class Emulator; - -class Region { -public: - virtual ~Region() = default; - - Range const& range() const { return m_range; } - - u32 base() const { return m_range.base().get(); } - u32 size() const { return m_range.size(); } - u32 end() const { return m_range.end().get(); } - - bool contains(u32 address) const { return address >= base() && address < end(); } - - virtual void write8(u32 offset, ValueWithShadow) = 0; - virtual void write16(u32 offset, ValueWithShadow) = 0; - virtual void write32(u32 offset, ValueWithShadow) = 0; - virtual void write64(u32 offset, ValueWithShadow) = 0; - virtual void write128(u32 offset, ValueWithShadow) = 0; - virtual void write256(u32 offset, ValueWithShadow) = 0; - - virtual ValueWithShadow read8(u32 offset) = 0; - virtual ValueWithShadow read16(u32 offset) = 0; - virtual ValueWithShadow read32(u32 offset) = 0; - virtual ValueWithShadow read64(u32 offset) = 0; - virtual ValueWithShadow read128(u32 offset) = 0; - virtual ValueWithShadow read256(u32 offset) = 0; - - virtual u8* cacheable_ptr([[maybe_unused]] u32 offset) { return nullptr; } - - bool is_stack() const { return m_stack; } - void set_stack(bool b) { m_stack = b; } - - bool is_text() const { return m_text; } - void set_text(bool b) { m_text = b; } - - bool is_readable() const { return m_readable; } - bool is_writable() const { return m_writable; } - bool is_executable() const { return m_executable; } - - void set_readable(bool b) { m_readable = b; } - void set_writable(bool b) { m_writable = b; } - void set_executable(bool b) { m_executable = b; } - - virtual u8* data() = 0; - virtual u8* shadow_data() = 0; - - Emulator& emulator() { return m_emulator; } - Emulator const& emulator() const { return m_emulator; } - - template - bool fast_is() const = delete; - -protected: - Region(u32 base, u32 size, bool mmap = false); - void set_range(Range r) { m_range = r; } - -private: - Emulator& m_emulator; - - Range m_range; - - bool m_mmap { false }; - bool m_stack { false }; - bool m_text { false }; - bool m_readable { true }; - bool m_writable { true }; - bool m_executable { true }; -}; - -} diff --git a/Userland/DevTools/UserspaceEmulator/Report.h b/Userland/DevTools/UserspaceEmulator/Report.h deleted file mode 100644 index 91a21c36d9..0000000000 --- a/Userland/DevTools/UserspaceEmulator/Report.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -extern bool g_report_to_debug; - -template -void reportln(StringView format, Ts... args) -{ - if (g_report_to_debug) { - AK::VariadicFormatParams variadic_format_params { args... }; - AK::vdbgln(format, variadic_format_params); - } else { - warnln(format, args...); - } -} diff --git a/Userland/DevTools/UserspaceEmulator/SimpleRegion.cpp b/Userland/DevTools/UserspaceEmulator/SimpleRegion.cpp deleted file mode 100644 index 963e021c29..0000000000 --- a/Userland/DevTools/UserspaceEmulator/SimpleRegion.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SimpleRegion.h" -#include -#include - -namespace UserspaceEmulator { - -SimpleRegion::SimpleRegion(u32 base, u32 size) - : Region(base, size) -{ - m_data = (u8*)calloc(1, size); - m_shadow_data = (u8*)malloc(size); - memset(m_shadow_data, 1, size); -} - -SimpleRegion::~SimpleRegion() -{ - free(m_shadow_data); - free(m_data); -} - -ValueWithShadow SimpleRegion::read8(FlatPtr offset) -{ - VERIFY(offset < size()); - return { m_data[offset], m_shadow_data[offset] }; -} - -ValueWithShadow SimpleRegion::read16(u32 offset) -{ - VERIFY(offset + 1 < size()); - - u16 value, shadow; - ByteReader::load(m_data + offset, value); - ByteReader::load(m_shadow_data + offset, shadow); - - return { value, shadow }; -} - -ValueWithShadow SimpleRegion::read32(u32 offset) -{ - VERIFY(offset + 3 < size()); - - u32 value, shadow; - ByteReader::load(m_data + offset, value); - ByteReader::load(m_shadow_data + offset, shadow); - - return { value, shadow }; -} - -ValueWithShadow SimpleRegion::read64(u32 offset) -{ - VERIFY(offset + 7 < size()); - - u64 value, shadow; - ByteReader::load(m_data + offset, value); - ByteReader::load(m_shadow_data + offset, shadow); - - return { value, shadow }; -} - -ValueWithShadow SimpleRegion::read128(u32 offset) -{ - VERIFY(offset + 15 < size()); - u128 value, shadow; - ByteReader::load(m_data + offset, value); - ByteReader::load(m_shadow_data + offset, shadow); - return { value, shadow }; -} - -ValueWithShadow SimpleRegion::read256(u32 offset) -{ - VERIFY(offset + 31 < size()); - u256 value, shadow; - ByteReader::load(m_data + offset, value); - ByteReader::load(m_shadow_data + offset, shadow); - return { value, shadow }; -} - -void SimpleRegion::write8(u32 offset, ValueWithShadow value) -{ - VERIFY(offset < size()); - m_data[offset] = value.value(); - m_shadow_data[offset] = value.shadow()[0]; -} - -void SimpleRegion::write16(u32 offset, ValueWithShadow value) -{ - VERIFY(offset + 1 < size()); - ByteReader::store(m_data + offset, value.value()); - ByteReader::store(m_shadow_data + offset, value.shadow()); -} - -void SimpleRegion::write32(u32 offset, ValueWithShadow value) -{ - VERIFY(offset + 3 < size()); - ByteReader::store(m_data + offset, value.value()); - ByteReader::store(m_shadow_data + offset, value.shadow()); -} - -void SimpleRegion::write64(u32 offset, ValueWithShadow value) -{ - VERIFY(offset + 7 < size()); - ByteReader::store(m_data + offset, value.value()); - ByteReader::store(m_shadow_data + offset, value.shadow()); -} -void SimpleRegion::write128(u32 offset, ValueWithShadow value) -{ - VERIFY(offset + 15 < size()); - ByteReader::store(m_data + offset, value.value()); - ByteReader::store(m_shadow_data + offset, value.shadow()); -} -void SimpleRegion::write256(u32 offset, ValueWithShadow value) -{ - VERIFY(offset + 31 < size()); - ByteReader::store(m_data + offset, value.value()); - ByteReader::store(m_shadow_data + offset, value.shadow()); -} - -u8* SimpleRegion::cacheable_ptr(u32 offset) -{ - return m_data + offset; -} - -} diff --git a/Userland/DevTools/UserspaceEmulator/SimpleRegion.h b/Userland/DevTools/UserspaceEmulator/SimpleRegion.h deleted file mode 100644 index 4847d12bd4..0000000000 --- a/Userland/DevTools/UserspaceEmulator/SimpleRegion.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "SoftMMU.h" - -namespace UserspaceEmulator { - -class SimpleRegion final : public Region { -public: - SimpleRegion(u32 base, u32 size); - virtual ~SimpleRegion() override; - - virtual ValueWithShadow read8(u32 offset) override; - virtual ValueWithShadow read16(u32 offset) override; - virtual ValueWithShadow read32(u32 offset) override; - virtual ValueWithShadow read64(u32 offset) override; - virtual ValueWithShadow read128(u32 offset) override; - virtual ValueWithShadow read256(u32 offset) override; - - virtual void write8(u32 offset, ValueWithShadow) override; - virtual void write16(u32 offset, ValueWithShadow) override; - virtual void write32(u32 offset, ValueWithShadow) override; - virtual void write64(u32 offset, ValueWithShadow) override; - virtual void write128(u32 offset, ValueWithShadow) override; - virtual void write256(u32 offset, ValueWithShadow) override; - - virtual u8* data() override { return m_data; } - virtual u8* shadow_data() override { return m_shadow_data; } - - virtual u8* cacheable_ptr(u32 offset) override; - -private: - u8* m_data { nullptr }; - u8* m_shadow_data { nullptr }; -}; - -} diff --git a/Userland/DevTools/UserspaceEmulator/SoftCPU.cpp b/Userland/DevTools/UserspaceEmulator/SoftCPU.cpp deleted file mode 100644 index f6d7bda34b..0000000000 --- a/Userland/DevTools/UserspaceEmulator/SoftCPU.cpp +++ /dev/null @@ -1,3234 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2021, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SoftCPU.h" -#include "Emulator.h" -#include -#include -#include -#include -#include -#include -#include - -#if defined(AK_COMPILER_GCC) -# pragma GCC optimize("O3") -#endif - -#define TODO_INSN() \ - do { \ - reportln("\n=={}== Unimplemented instruction: {}\n"sv, getpid(), __FUNCTION__); \ - m_emulator.dump_backtrace(); \ - _exit(0); \ - } while (0) - -#define FPU_INSTRUCTION(name) \ - void SoftCPU::name(const X86::Instruction& insn) \ - { \ - m_fpu.name(insn); \ - } - -#define VPU_INSTRUCTION(name) \ - void SoftCPU::name(const X86::Instruction& insn) \ - { \ - m_vpu.name(insn); \ - } - -#define DEFINE_GENERIC_SHIFT_ROTATE_INSN_HANDLERS(mnemonic, op) \ - void SoftCPU::mnemonic##_RM8_1(const X86::Instruction& insn) \ - { \ - generic_RM8_1(op>, insn); \ - } \ - void SoftCPU::mnemonic##_RM8_CL(const X86::Instruction& insn) \ - { \ - generic_RM8_CL(op>, insn); \ - } \ - void SoftCPU::mnemonic##_RM8_imm8(const X86::Instruction& insn) \ - { \ - generic_RM8_imm8(op>, insn); \ - } \ - void SoftCPU::mnemonic##_RM16_1(const X86::Instruction& insn) \ - { \ - generic_RM16_1(op>, insn); \ - } \ - void SoftCPU::mnemonic##_RM16_CL(const X86::Instruction& insn) \ - { \ - generic_RM16_CL(op>, insn); \ - } \ - void SoftCPU::mnemonic##_RM16_imm8(const X86::Instruction& insn) \ - { \ - generic_RM16_unsigned_imm8(op>, insn); \ - } \ - void SoftCPU::mnemonic##_RM32_1(const X86::Instruction& insn) \ - { \ - generic_RM32_1(op>, insn); \ - } \ - void SoftCPU::mnemonic##_RM32_CL(const X86::Instruction& insn) \ - { \ - generic_RM32_CL(op>, insn); \ - } \ - void SoftCPU::mnemonic##_RM32_imm8(const X86::Instruction& insn) \ - { \ - generic_RM32_unsigned_imm8(op>, insn); \ - } - -namespace UserspaceEmulator { - -template -ALWAYS_INLINE void warn_if_uninitialized(T value_with_shadow, char const* message) -{ - if (value_with_shadow.is_uninitialized()) [[unlikely]] { - reportln("\033[31;1mWarning! Use of uninitialized value: {}\033[0m\n"sv, message); - Emulator::the().dump_backtrace(); - } -} - -ALWAYS_INLINE void SoftCPU::warn_if_flags_tainted(char const* message) const -{ - if (m_flags_tainted) [[unlikely]] { - reportln("\n=={}== \033[31;1mConditional depends on uninitialized data\033[0m ({})\n"sv, getpid(), message); - Emulator::the().dump_backtrace(); - } -} - -template -constexpr T sign_extended_to(U value) -{ - if (!(value & X86::TypeTrivia::sign_bit)) - return value; - return (X86::TypeTrivia::mask & ~X86::TypeTrivia::mask) | value; -} - -SoftCPU::SoftCPU(Emulator& emulator) - : m_emulator(emulator) - , m_fpu(emulator, *this) - , m_vpu(emulator, *this) -{ - PartAddressableRegister empty_reg; - explicit_bzero(&empty_reg, sizeof(empty_reg)); - for (auto& gpr : m_gpr) - gpr = ValueWithShadow::create_initialized(empty_reg); - - m_segment[(int)X86::SegmentRegister::CS] = 0x1b; - m_segment[(int)X86::SegmentRegister::DS] = 0x23; - m_segment[(int)X86::SegmentRegister::ES] = 0x23; - m_segment[(int)X86::SegmentRegister::SS] = 0x23; - m_segment[(int)X86::SegmentRegister::FS] = 0x23; - m_segment[(int)X86::SegmentRegister::GS] = 0x2b; -} - -void SoftCPU::dump() const -{ - outln(" eax={:p} ebx={:p} ecx={:p} edx={:p} ebp={:p} esp={:p} esi={:p} edi={:p} o={:d} s={:d} z={:d} a={:d} p={:d} c={:d}", - eax(), ebx(), ecx(), edx(), ebp(), esp(), esi(), edi(), of(), sf(), zf(), af(), pf(), cf()); - outln("#eax={:hex-dump} #ebx={:hex-dump} #ecx={:hex-dump} #edx={:hex-dump} #ebhex-dump={:hex-dump} #eshex-dump={:hex-dump} #esi={:hex-dump} #edi={:hex-dump} #f={}", - eax().shadow().span(), ebx().shadow().span(), ecx().shadow().span(), edx().shadow().span(), ebp().shadow().span(), esp().shadow().span(), esi().shadow().span(), edi().shadow().span(), m_flags_tainted); - fflush(stdout); -} - -void SoftCPU::update_code_cache() -{ - auto* region = m_emulator.mmu().find_region({ cs(), eip() }); - VERIFY(region); - - if (!region->is_executable()) { - reportln("SoftCPU::update_code_cache: Non-executable region @ {:p}"sv, eip()); - Emulator::the().dump_backtrace(); - TODO(); - } - - // FIXME: This cache needs to be invalidated if the code region is ever unmapped. - m_cached_code_region = region; - m_cached_code_base_ptr = region->data(); -} - -ValueWithShadow SoftCPU::read_memory8(X86::LogicalAddress address) -{ - VERIFY(address.selector() == 0x1b || address.selector() == 0x23 || address.selector() == 0x2b); - auto value = m_emulator.mmu().read8(address); - outln_if(MEMORY_DEBUG, "\033[36;1mread_memory8: @{:#04x}:{:p} -> {:#02x} ({:#02x})\033[0m", address.selector(), address.offset(), value, value.shadow_as_value()); - return value; -} - -ValueWithShadow SoftCPU::read_memory16(X86::LogicalAddress address) -{ - VERIFY(address.selector() == 0x1b || address.selector() == 0x23 || address.selector() == 0x2b); - auto value = m_emulator.mmu().read16(address); - outln_if(MEMORY_DEBUG, "\033[36;1mread_memory16: @{:#04x}:{:p} -> {:#04x} ({:#04x})\033[0m", address.selector(), address.offset(), value, value.shadow_as_value()); - return value; -} - -ValueWithShadow SoftCPU::read_memory32(X86::LogicalAddress address) -{ - VERIFY(address.selector() == 0x1b || address.selector() == 0x23 || address.selector() == 0x2b); - auto value = m_emulator.mmu().read32(address); - outln_if(MEMORY_DEBUG, "\033[36;1mread_memory32: @{:#04x}:{:p} -> {:#08x} ({:#08x})\033[0m", address.selector(), address.offset(), value, value.shadow_as_value()); - return value; -} - -ValueWithShadow SoftCPU::read_memory64(X86::LogicalAddress address) -{ - VERIFY(address.selector() == 0x1b || address.selector() == 0x23 || address.selector() == 0x2b); - auto value = m_emulator.mmu().read64(address); - outln_if(MEMORY_DEBUG, "\033[36;1mread_memory64: @{:#04x}:{:p} -> {:#016x} ({:#016x})\033[0m", address.selector(), address.offset(), value, value.shadow_as_value()); - return value; -} - -ValueWithShadow SoftCPU::read_memory128(X86::LogicalAddress address) -{ - VERIFY(address.selector() == 0x1b || address.selector() == 0x23 || address.selector() == 0x2b); - auto value = m_emulator.mmu().read128(address); - outln_if(MEMORY_DEBUG, "\033[36;1mread_memory128: @{:#04x}:{:p} -> {:#032x} ({:#032x})\033[0m", address.selector(), address.offset(), value, value.shadow_as_value()); - return value; -} -ValueWithShadow SoftCPU::read_memory256(X86::LogicalAddress address) -{ - VERIFY(address.selector() == 0x1b || address.selector() == 0x23 || address.selector() == 0x2b); - auto value = m_emulator.mmu().read256(address); - outln_if(MEMORY_DEBUG, "\033[36;1mread_memory256: @{:#04x}:{:p} -> {:#064x} ({:#064x})\033[0m", address.selector(), address.offset(), value, value.shadow_as_value()); - return value; -} - -void SoftCPU::write_memory8(X86::LogicalAddress address, ValueWithShadow value) -{ - VERIFY(address.selector() == 0x23 || address.selector() == 0x2b); - outln_if(MEMORY_DEBUG, "\033[36;1mwrite_memory8: @{:#04x}:{:p} <- {:#02x} ({:#02x})\033[0m", address.selector(), address.offset(), value, value.shadow_as_value()); - m_emulator.mmu().write8(address, value); -} - -void SoftCPU::write_memory16(X86::LogicalAddress address, ValueWithShadow value) -{ - VERIFY(address.selector() == 0x23 || address.selector() == 0x2b); - outln_if(MEMORY_DEBUG, "\033[36;1mwrite_memory16: @{:#04x}:{:p} <- {:#04x} ({:#04x})\033[0m", address.selector(), address.offset(), value, value.shadow_as_value()); - m_emulator.mmu().write16(address, value); -} - -void SoftCPU::write_memory32(X86::LogicalAddress address, ValueWithShadow value) -{ - VERIFY(address.selector() == 0x23 || address.selector() == 0x2b); - outln_if(MEMORY_DEBUG, "\033[36;1mwrite_memory32: @{:#04x}:{:p} <- {:#08x} ({:#08x})\033[0m", address.selector(), address.offset(), value, value.shadow_as_value()); - m_emulator.mmu().write32(address, value); -} - -void SoftCPU::write_memory64(X86::LogicalAddress address, ValueWithShadow value) -{ - VERIFY(address.selector() == 0x23 || address.selector() == 0x2b); - outln_if(MEMORY_DEBUG, "\033[36;1mwrite_memory64: @{:#04x}:{:p} <- {:#016x} ({:#016x})\033[0m", address.selector(), address.offset(), value, value.shadow_as_value()); - m_emulator.mmu().write64(address, value); -} - -void SoftCPU::write_memory128(X86::LogicalAddress address, ValueWithShadow value) -{ - VERIFY(address.selector() == 0x23 || address.selector() == 0x2b); - outln_if(MEMORY_DEBUG, "\033[36;1mwrite_memory128: @{:#04x}:{:p} <- {:#032x} ({:#032x})\033[0m", address.selector(), address.offset(), value, value.shadow_as_value()); - m_emulator.mmu().write128(address, value); -} - -void SoftCPU::write_memory256(X86::LogicalAddress address, ValueWithShadow value) -{ - VERIFY(address.selector() == 0x23 || address.selector() == 0x2b); - outln_if(MEMORY_DEBUG, "\033[36;1mwrite_memory256: @{:#04x}:{:p} <- {:#064x} ({:#064x})\033[0m", address.selector(), address.offset(), value, value.shadow_as_value()); - m_emulator.mmu().write256(address, value); -} - -void SoftCPU::push_string(StringView string) -{ - u32 space_to_allocate = round_up_to_power_of_two(string.length() + 1, 16); - set_esp({ esp().value() - space_to_allocate, esp().shadow() }); - m_emulator.mmu().copy_to_vm(esp().value(), string.characters_without_null_termination(), string.length()); - m_emulator.mmu().write8({ 0x23, esp().value() + string.length() }, shadow_wrap_as_initialized((u8)'\0')); -} - -void SoftCPU::push_buffer(u8 const* data, size_t size) -{ - set_esp({ esp().value() - size, esp().shadow() }); - warn_if_uninitialized(esp(), "push_buffer"); - m_emulator.mmu().copy_to_vm(esp().value(), data, size); -} - -void SoftCPU::push32(ValueWithShadow value) -{ - set_esp({ esp().value() - sizeof(u32), esp().shadow() }); - warn_if_uninitialized(esp(), "push32"); - write_memory32({ ss(), esp().value() }, value); -} - -ValueWithShadow SoftCPU::pop32() -{ - warn_if_uninitialized(esp(), "pop32"); - auto value = read_memory32({ ss(), esp().value() }); - set_esp({ esp().value() + sizeof(u32), esp().shadow() }); - return value; -} - -void SoftCPU::push16(ValueWithShadow value) -{ - warn_if_uninitialized(esp(), "push16"); - set_esp({ esp().value() - sizeof(u16), esp().shadow() }); - write_memory16({ ss(), esp().value() }, value); -} - -ValueWithShadow SoftCPU::pop16() -{ - warn_if_uninitialized(esp(), "pop16"); - auto value = read_memory16({ ss(), esp().value() }); - set_esp({ esp().value() + sizeof(u16), esp().shadow() }); - return value; -} - -template -void SoftCPU::do_once_or_repeat(const X86::Instruction& insn, Callback callback) -{ - if (!insn.has_rep_prefix()) - return callback(); - - while (loop_index(insn.address_size()).value()) { - callback(); - decrement_loop_index(insn.address_size()); - if constexpr (check_zf) { - warn_if_flags_tainted("repz/repnz"); - if (insn.rep_prefix() == X86::Prefix::REPZ && !zf()) - break; - if (insn.rep_prefix() == X86::Prefix::REPNZ && zf()) - break; - } - } -} - -template -ALWAYS_INLINE static T op_inc(SoftCPU& cpu, T data) -{ - typename T::ValueType result; - u32 new_flags = 0; - - if constexpr (sizeof(typename T::ValueType) == 4) { - asm volatile("incl %%eax\n" - : "=a"(result) - : "a"(data.value())); - } else if constexpr (sizeof(typename T::ValueType) == 2) { - asm volatile("incw %%ax\n" - : "=a"(result) - : "a"(data.value())); - } else if constexpr (sizeof(typename T::ValueType) == 1) { - asm volatile("incb %%al\n" - : "=a"(result) - : "a"(data.value())); - } - - asm volatile( - "pushf\n" - "pop %%ebx" - : "=b"(new_flags)); - - cpu.set_flags_oszap(new_flags); - cpu.taint_flags_from(data); - return shadow_wrap_with_taint_from(result, data); -} - -template -ALWAYS_INLINE static T op_dec(SoftCPU& cpu, T data) -{ - typename T::ValueType result; - u32 new_flags = 0; - - if constexpr (sizeof(typename T::ValueType) == 4) { - asm volatile("decl %%eax\n" - : "=a"(result) - : "a"(data.value())); - } else if constexpr (sizeof(typename T::ValueType) == 2) { - asm volatile("decw %%ax\n" - : "=a"(result) - : "a"(data.value())); - } else if constexpr (sizeof(typename T::ValueType) == 1) { - asm volatile("decb %%al\n" - : "=a"(result) - : "a"(data.value())); - } - - asm volatile( - "pushf\n" - "pop %%ebx" - : "=b"(new_flags)); - - cpu.set_flags_oszap(new_flags); - cpu.taint_flags_from(data); - return shadow_wrap_with_taint_from(result, data); -} - -template -ALWAYS_INLINE static T op_xor(SoftCPU& cpu, T const& dest, T const& src) -{ - typename T::ValueType result; - u32 new_flags = 0; - - if constexpr (sizeof(typename T::ValueType) == 4) { - asm volatile("xorl %%ecx, %%eax\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else if constexpr (sizeof(typename T::ValueType) == 2) { - asm volatile("xor %%cx, %%ax\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else if constexpr (sizeof(typename T::ValueType) == 1) { - asm volatile("xorb %%cl, %%al\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else { - VERIFY_NOT_REACHED(); - } - - asm volatile( - "pushf\n" - "pop %%ebx" - : "=b"(new_flags)); - - cpu.set_flags_oszpc(new_flags); - cpu.taint_flags_from(dest, src); - return shadow_wrap_with_taint_from(result, dest, src); -} - -template -ALWAYS_INLINE static T op_or(SoftCPU& cpu, T const& dest, T const& src) -{ - typename T::ValueType result = 0; - u32 new_flags = 0; - - if constexpr (sizeof(typename T::ValueType) == 4) { - asm volatile("orl %%ecx, %%eax\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else if constexpr (sizeof(typename T::ValueType) == 2) { - asm volatile("or %%cx, %%ax\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else if constexpr (sizeof(typename T::ValueType) == 1) { - asm volatile("orb %%cl, %%al\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else { - VERIFY_NOT_REACHED(); - } - - asm volatile( - "pushf\n" - "pop %%ebx" - : "=b"(new_flags)); - - cpu.set_flags_oszpc(new_flags); - cpu.taint_flags_from(dest, src); - return shadow_wrap_with_taint_from(result, dest, src); -} - -template -ALWAYS_INLINE static T op_sub(SoftCPU& cpu, T const& dest, T const& src) -{ - typename T::ValueType result = 0; - u32 new_flags = 0; - - if constexpr (sizeof(typename T::ValueType) == 4) { - asm volatile("subl %%ecx, %%eax\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else if constexpr (sizeof(typename T::ValueType) == 2) { - asm volatile("subw %%cx, %%ax\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else if constexpr (sizeof(typename T::ValueType) == 1) { - asm volatile("subb %%cl, %%al\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else { - VERIFY_NOT_REACHED(); - } - - asm volatile( - "pushf\n" - "pop %%ebx" - : "=b"(new_flags)); - - cpu.set_flags_oszapc(new_flags); - cpu.taint_flags_from(dest, src); - return shadow_wrap_with_taint_from(result, dest, src); -} - -template -ALWAYS_INLINE static T op_sbb_impl(SoftCPU& cpu, T const& dest, T const& src) -{ - typename T::ValueType result = 0; - u32 new_flags = 0; - - if constexpr (cf) - asm volatile("stc"); - else - asm volatile("clc"); - - if constexpr (sizeof(typename T::ValueType) == 4) { - asm volatile("sbbl %%ecx, %%eax\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else if constexpr (sizeof(typename T::ValueType) == 2) { - asm volatile("sbbw %%cx, %%ax\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else if constexpr (sizeof(typename T::ValueType) == 1) { - asm volatile("sbbb %%cl, %%al\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else { - VERIFY_NOT_REACHED(); - } - - asm volatile( - "pushf\n" - "pop %%ebx" - : "=b"(new_flags)); - - cpu.set_flags_oszapc(new_flags); - cpu.taint_flags_from(dest, src); - return shadow_wrap_with_taint_from(result, dest, src); -} - -template -ALWAYS_INLINE static T op_sbb(SoftCPU& cpu, T& dest, T const& src) -{ - cpu.warn_if_flags_tainted("sbb"); - if (cpu.cf()) - return op_sbb_impl(cpu, dest, src); - return op_sbb_impl(cpu, dest, src); -} - -template -ALWAYS_INLINE static T op_add(SoftCPU& cpu, T& dest, T const& src) -{ - typename T::ValueType result = 0; - u32 new_flags = 0; - - if constexpr (sizeof(typename T::ValueType) == 4) { - asm volatile("addl %%ecx, %%eax\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else if constexpr (sizeof(typename T::ValueType) == 2) { - asm volatile("addw %%cx, %%ax\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else if constexpr (sizeof(typename T::ValueType) == 1) { - asm volatile("addb %%cl, %%al\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else { - VERIFY_NOT_REACHED(); - } - - asm volatile( - "pushf\n" - "pop %%ebx" - : "=b"(new_flags)); - - cpu.set_flags_oszapc(new_flags); - cpu.taint_flags_from(dest, src); - return shadow_wrap_with_taint_from(result, dest, src); -} - -template -ALWAYS_INLINE static T op_adc_impl(SoftCPU& cpu, T& dest, T const& src) -{ - typename T::ValueType result = 0; - u32 new_flags = 0; - - if constexpr (cf) - asm volatile("stc"); - else - asm volatile("clc"); - - if constexpr (sizeof(typename T::ValueType) == 4) { - asm volatile("adcl %%ecx, %%eax\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else if constexpr (sizeof(typename T::ValueType) == 2) { - asm volatile("adcw %%cx, %%ax\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else if constexpr (sizeof(typename T::ValueType) == 1) { - asm volatile("adcb %%cl, %%al\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else { - VERIFY_NOT_REACHED(); - } - - asm volatile( - "pushf\n" - "pop %%ebx" - : "=b"(new_flags)); - - cpu.set_flags_oszapc(new_flags); - cpu.taint_flags_from(dest, src); - return shadow_wrap_with_taint_from(result, dest, src); -} - -template -ALWAYS_INLINE static T op_adc(SoftCPU& cpu, T& dest, T const& src) -{ - cpu.warn_if_flags_tainted("adc"); - if (cpu.cf()) - return op_adc_impl(cpu, dest, src); - return op_adc_impl(cpu, dest, src); -} - -template -ALWAYS_INLINE static T op_and(SoftCPU& cpu, T const& dest, T const& src) -{ - typename T::ValueType result = 0; - u32 new_flags = 0; - - if constexpr (sizeof(typename T::ValueType) == 4) { - asm volatile("andl %%ecx, %%eax\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else if constexpr (sizeof(typename T::ValueType) == 2) { - asm volatile("andw %%cx, %%ax\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else if constexpr (sizeof(typename T::ValueType) == 1) { - asm volatile("andb %%cl, %%al\n" - : "=a"(result) - : "a"(dest.value()), "c"(src.value())); - } else { - VERIFY_NOT_REACHED(); - } - - asm volatile( - "pushf\n" - "pop %%ebx" - : "=b"(new_flags)); - - cpu.set_flags_oszpc(new_flags); - cpu.taint_flags_from(dest, src); - return shadow_wrap_with_taint_from(result, dest, src); -} - -template -ALWAYS_INLINE static void op_imul(SoftCPU& cpu, T const& dest, T const& src, T& result_high, T& result_low) -{ - bool did_overflow = false; - if constexpr (sizeof(T) == 4) { - i64 result = (i64)src * (i64)dest; - result_low = result & 0xffffffff; - result_high = result >> 32; - did_overflow = (result > NumericLimits::max() || result < NumericLimits::min()); - } else if constexpr (sizeof(T) == 2) { - i32 result = (i32)src * (i32)dest; - result_low = result & 0xffff; - result_high = result >> 16; - did_overflow = (result > NumericLimits::max() || result < NumericLimits::min()); - } else if constexpr (sizeof(T) == 1) { - i16 result = (i16)src * (i16)dest; - result_low = result & 0xff; - result_high = result >> 8; - did_overflow = (result > NumericLimits::max() || result < NumericLimits::min()); - } - - if (did_overflow) { - cpu.set_cf(true); - cpu.set_of(true); - } else { - cpu.set_cf(false); - cpu.set_of(false); - } -} - -template -ALWAYS_INLINE static T op_shr(SoftCPU& cpu, T data, ValueWithShadow steps) -{ - if (steps.value() == 0) - return shadow_wrap_with_taint_from(data.value(), data, steps); - - u32 result = 0; - u32 new_flags = 0; - - if constexpr (sizeof(typename T::ValueType) == 4) { - asm volatile("shrl %%cl, %%eax\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } else if constexpr (sizeof(typename T::ValueType) == 2) { - asm volatile("shrw %%cl, %%ax\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } else if constexpr (sizeof(typename T::ValueType) == 1) { - asm volatile("shrb %%cl, %%al\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } - - asm volatile( - "pushf\n" - "pop %%ebx" - : "=b"(new_flags)); - - cpu.set_flags_oszapc(new_flags); - cpu.taint_flags_from(data, steps); - return shadow_wrap_with_taint_from(result, data, steps); -} - -template -ALWAYS_INLINE static T op_shl(SoftCPU& cpu, T data, ValueWithShadow steps) -{ - if (steps.value() == 0) - return shadow_wrap_with_taint_from(data.value(), data, steps); - - u32 result = 0; - u32 new_flags = 0; - - if constexpr (sizeof(typename T::ValueType) == 4) { - asm volatile("shll %%cl, %%eax\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } else if constexpr (sizeof(typename T::ValueType) == 2) { - asm volatile("shlw %%cl, %%ax\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } else if constexpr (sizeof(typename T::ValueType) == 1) { - asm volatile("shlb %%cl, %%al\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } - - asm volatile( - "pushf\n" - "pop %%ebx" - : "=b"(new_flags)); - - cpu.set_flags_oszapc(new_flags); - cpu.taint_flags_from(data, steps); - return shadow_wrap_with_taint_from(result, data, steps); -} - -template -ALWAYS_INLINE static T op_shrd(SoftCPU& cpu, T data, T extra_bits, ValueWithShadow steps) -{ - if (steps.value() == 0) - return shadow_wrap_with_taint_from(data.value(), data, steps); - - u32 result = 0; - u32 new_flags = 0; - - if constexpr (sizeof(typename T::ValueType) == 4) { - asm volatile("shrd %%cl, %%edx, %%eax\n" - : "=a"(result) - : "a"(data.value()), "d"(extra_bits.value()), "c"(steps.value())); - } else if constexpr (sizeof(typename T::ValueType) == 2) { - asm volatile("shrd %%cl, %%dx, %%ax\n" - : "=a"(result) - : "a"(data.value()), "d"(extra_bits.value()), "c"(steps.value())); - } - - asm volatile( - "pushf\n" - "pop %%ebx" - : "=b"(new_flags)); - - cpu.set_flags_oszapc(new_flags); - cpu.taint_flags_from(data, steps); - return shadow_wrap_with_taint_from(result, data, steps); -} - -template -ALWAYS_INLINE static T op_shld(SoftCPU& cpu, T data, T extra_bits, ValueWithShadow steps) -{ - if (steps.value() == 0) - return shadow_wrap_with_taint_from(data.value(), data, steps); - - u32 result = 0; - u32 new_flags = 0; - - if constexpr (sizeof(typename T::ValueType) == 4) { - asm volatile("shld %%cl, %%edx, %%eax\n" - : "=a"(result) - : "a"(data.value()), "d"(extra_bits.value()), "c"(steps.value())); - } else if constexpr (sizeof(typename T::ValueType) == 2) { - asm volatile("shld %%cl, %%dx, %%ax\n" - : "=a"(result) - : "a"(data.value()), "d"(extra_bits.value()), "c"(steps.value())); - } - - asm volatile( - "pushf\n" - "pop %%ebx" - : "=b"(new_flags)); - - cpu.set_flags_oszapc(new_flags); - cpu.taint_flags_from(data, steps); - return shadow_wrap_with_taint_from(result, data, steps); -} - -template -ALWAYS_INLINE void SoftCPU::generic_AL_imm8(Op op, const X86::Instruction& insn) -{ - auto dest = al(); - auto src = shadow_wrap_as_initialized(insn.imm8()); - auto result = op(*this, dest, src); - if (is_or && insn.imm8() == 0xff) - result.set_initialized(); - if (update_dest) - set_al(result); -} - -template -ALWAYS_INLINE void SoftCPU::generic_AX_imm16(Op op, const X86::Instruction& insn) -{ - auto dest = ax(); - auto src = shadow_wrap_as_initialized(insn.imm16()); - auto result = op(*this, dest, src); - if (is_or && insn.imm16() == 0xffff) - result.set_initialized(); - if (update_dest) - set_ax(result); -} - -template -ALWAYS_INLINE void SoftCPU::generic_EAX_imm32(Op op, const X86::Instruction& insn) -{ - auto dest = eax(); - auto src = shadow_wrap_as_initialized(insn.imm32()); - auto result = op(*this, dest, src); - if (is_or && insn.imm32() == 0xffffffff) - result.set_initialized(); - if (update_dest) - set_eax(result); -} - -template -ALWAYS_INLINE void SoftCPU::generic_RM16_imm16(Op op, const X86::Instruction& insn) -{ - auto dest = insn.modrm().read16(*this, insn); - auto src = shadow_wrap_as_initialized(insn.imm16()); - auto result = op(*this, dest, src); - if (is_or && insn.imm16() == 0xffff) - result.set_initialized(); - if (update_dest) - insn.modrm().write16(*this, insn, result); -} - -template -ALWAYS_INLINE void SoftCPU::generic_RM16_imm8(Op op, const X86::Instruction& insn) -{ - auto dest = insn.modrm().read16(*this, insn); - auto src = shadow_wrap_as_initialized(sign_extended_to(insn.imm8())); - auto result = op(*this, dest, src); - if (is_or && src.value() == 0xffff) - result.set_initialized(); - if (update_dest) - insn.modrm().write16(*this, insn, result); -} - -template -ALWAYS_INLINE void SoftCPU::generic_RM16_unsigned_imm8(Op op, const X86::Instruction& insn) -{ - auto dest = insn.modrm().read16(*this, insn); - auto src = shadow_wrap_as_initialized(insn.imm8()); - auto result = op(*this, dest, src); - if (update_dest) - insn.modrm().write16(*this, insn, result); -} - -template -ALWAYS_INLINE void SoftCPU::generic_RM16_reg16(Op op, const X86::Instruction& insn) -{ - auto dest = insn.modrm().read16(*this, insn); - auto src = const_gpr16(insn.reg16()); - auto result = op(*this, dest, src); - if (dont_taint_for_same_operand && insn.modrm().is_register() && insn.modrm().register_index() == insn.register_index()) { - result.set_initialized(); - m_flags_tainted = false; - } - if (update_dest) - insn.modrm().write16(*this, insn, result); -} - -template -ALWAYS_INLINE void SoftCPU::generic_RM32_imm32(Op op, const X86::Instruction& insn) -{ - auto dest = insn.modrm().read32(*this, insn); - auto src = insn.imm32(); - auto result = op(*this, dest, shadow_wrap_as_initialized(src)); - if (is_or && src == 0xffffffff) - result.set_initialized(); - if (update_dest) - insn.modrm().write32(*this, insn, result); -} - -template -ALWAYS_INLINE void SoftCPU::generic_RM32_imm8(Op op, const X86::Instruction& insn) -{ - auto dest = insn.modrm().read32(*this, insn); - auto src = sign_extended_to(insn.imm8()); - auto result = op(*this, dest, shadow_wrap_as_initialized(src)); - if (is_or && src == 0xffffffff) - result.set_initialized(); - if (update_dest) - insn.modrm().write32(*this, insn, result); -} - -template -ALWAYS_INLINE void SoftCPU::generic_RM32_unsigned_imm8(Op op, const X86::Instruction& insn) -{ - auto dest = insn.modrm().read32(*this, insn); - auto src = shadow_wrap_as_initialized(insn.imm8()); - auto result = op(*this, dest, src); - if (update_dest) - insn.modrm().write32(*this, insn, result); -} - -template -ALWAYS_INLINE void SoftCPU::generic_RM32_reg32(Op op, const X86::Instruction& insn) -{ - auto dest = insn.modrm().read32(*this, insn); - auto src = const_gpr32(insn.reg32()); - auto result = op(*this, dest, src); - if (dont_taint_for_same_operand && insn.modrm().is_register() && insn.modrm().register_index() == insn.register_index()) { - result.set_initialized(); - m_flags_tainted = false; - } - if (update_dest) - insn.modrm().write32(*this, insn, result); -} - -template -ALWAYS_INLINE void SoftCPU::generic_RM8_imm8(Op op, const X86::Instruction& insn) -{ - auto dest = insn.modrm().read8(*this, insn); - auto src = insn.imm8(); - auto result = op(*this, dest, shadow_wrap_as_initialized(src)); - if (is_or && src == 0xff) - result.set_initialized(); - if (update_dest) - insn.modrm().write8(*this, insn, result); -} - -template -ALWAYS_INLINE void SoftCPU::generic_RM8_reg8(Op op, const X86::Instruction& insn) -{ - auto dest = insn.modrm().read8(*this, insn); - auto src = const_gpr8(insn.reg8()); - auto result = op(*this, dest, src); - if (dont_taint_for_same_operand && insn.modrm().is_register() && insn.modrm().register_index() == insn.register_index()) { - result.set_initialized(); - m_flags_tainted = false; - } - if (update_dest) - insn.modrm().write8(*this, insn, result); -} - -template -ALWAYS_INLINE void SoftCPU::generic_reg16_RM16(Op op, const X86::Instruction& insn) -{ - auto dest = const_gpr16(insn.reg16()); - auto src = insn.modrm().read16(*this, insn); - auto result = op(*this, dest, src); - if (dont_taint_for_same_operand && insn.modrm().is_register() && insn.modrm().register_index() == insn.register_index()) { - result.set_initialized(); - m_flags_tainted = false; - } - if (update_dest) - gpr16(insn.reg16()) = result; -} - -template -ALWAYS_INLINE void SoftCPU::generic_reg32_RM32(Op op, const X86::Instruction& insn) -{ - auto dest = const_gpr32(insn.reg32()); - auto src = insn.modrm().read32(*this, insn); - auto result = op(*this, dest, src); - if (dont_taint_for_same_operand && insn.modrm().is_register() && insn.modrm().register_index() == insn.register_index()) { - result.set_initialized(); - m_flags_tainted = false; - } - if (update_dest) - gpr32(insn.reg32()) = result; -} - -template -ALWAYS_INLINE void SoftCPU::generic_reg8_RM8(Op op, const X86::Instruction& insn) -{ - auto dest = const_gpr8(insn.reg8()); - auto src = insn.modrm().read8(*this, insn); - auto result = op(*this, dest, src); - if (dont_taint_for_same_operand && insn.modrm().is_register() && insn.modrm().register_index() == insn.register_index()) { - result.set_initialized(); - m_flags_tainted = false; - } - if (update_dest) - gpr8(insn.reg8()) = result; -} - -template -ALWAYS_INLINE void SoftCPU::generic_RM8_1(Op op, const X86::Instruction& insn) -{ - auto data = insn.modrm().read8(*this, insn); - insn.modrm().write8(*this, insn, op(*this, data, shadow_wrap_as_initialized(1))); -} - -template -ALWAYS_INLINE void SoftCPU::generic_RM8_CL(Op op, const X86::Instruction& insn) -{ - auto data = insn.modrm().read8(*this, insn); - insn.modrm().write8(*this, insn, op(*this, data, cl())); -} - -template -ALWAYS_INLINE void SoftCPU::generic_RM16_1(Op op, const X86::Instruction& insn) -{ - auto data = insn.modrm().read16(*this, insn); - insn.modrm().write16(*this, insn, op(*this, data, shadow_wrap_as_initialized(1))); -} - -template -ALWAYS_INLINE void SoftCPU::generic_RM16_CL(Op op, const X86::Instruction& insn) -{ - auto data = insn.modrm().read16(*this, insn); - insn.modrm().write16(*this, insn, op(*this, data, cl())); -} - -template -ALWAYS_INLINE void SoftCPU::generic_RM32_1(Op op, const X86::Instruction& insn) -{ - auto data = insn.modrm().read32(*this, insn); - insn.modrm().write32(*this, insn, op(*this, data, shadow_wrap_as_initialized(1))); -} - -template -ALWAYS_INLINE void SoftCPU::generic_RM32_CL(Op op, const X86::Instruction& insn) -{ - auto data = insn.modrm().read32(*this, insn); - insn.modrm().write32(*this, insn, op(*this, data, cl())); -} - -void SoftCPU::AAA(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::AAD(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::AAM(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::AAS(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::ARPL(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::BOUND(const X86::Instruction&) { TODO_INSN(); } - -template -ALWAYS_INLINE static T op_bsf(SoftCPU&, T value) -{ - return { (typename T::ValueType)bit_scan_forward(value.value()), value.shadow() }; -} - -template -ALWAYS_INLINE static T op_bsr(SoftCPU&, T value) -{ - typename T::ValueType bit_index = 0; - if constexpr (sizeof(typename T::ValueType) == 4) { - asm volatile("bsrl %%eax, %%edx" - : "=d"(bit_index) - : "a"(value.value())); - } - if constexpr (sizeof(typename T::ValueType) == 2) { - asm volatile("bsrw %%ax, %%dx" - : "=d"(bit_index) - : "a"(value.value())); - } - return shadow_wrap_with_taint_from(bit_index, value); -} - -void SoftCPU::BSF_reg16_RM16(const X86::Instruction& insn) -{ - auto src = insn.modrm().read16(*this, insn); - set_zf(!src.value()); - if (src.value()) - gpr16(insn.reg16()) = op_bsf(*this, src); - taint_flags_from(src); -} - -void SoftCPU::BSF_reg32_RM32(const X86::Instruction& insn) -{ - auto src = insn.modrm().read32(*this, insn); - set_zf(!src.value()); - if (src.value()) { - gpr32(insn.reg32()) = op_bsf(*this, src); - taint_flags_from(src); - } -} - -void SoftCPU::BSR_reg16_RM16(const X86::Instruction& insn) -{ - auto src = insn.modrm().read16(*this, insn); - set_zf(!src.value()); - if (src.value()) { - gpr16(insn.reg16()) = op_bsr(*this, src); - taint_flags_from(src); - } -} - -void SoftCPU::BSR_reg32_RM32(const X86::Instruction& insn) -{ - auto src = insn.modrm().read32(*this, insn); - set_zf(!src.value()); - if (src.value()) { - gpr32(insn.reg32()) = op_bsr(*this, src); - taint_flags_from(src); - } -} - -void SoftCPU::BSWAP_reg32(const X86::Instruction& insn) -{ - gpr32(insn.reg32()) = { __builtin_bswap32(gpr32(insn.reg32()).value()), __builtin_bswap32(gpr32(insn.reg32()).shadow_as_value()) }; -} - -template -ALWAYS_INLINE static T op_bt(T value, T) -{ - return value; -} - -template -ALWAYS_INLINE static T op_bts(T value, T bit_mask) -{ - return value | bit_mask; -} - -template -ALWAYS_INLINE static T op_btr(T value, T bit_mask) -{ - return value & ~bit_mask; -} - -template -ALWAYS_INLINE static T op_btc(T value, T bit_mask) -{ - return value ^ bit_mask; -} - -template -ALWAYS_INLINE void BTx_RM16_reg16(SoftCPU& cpu, const X86::Instruction& insn, Op op) -{ - if (insn.modrm().is_register()) { - unsigned bit_index = cpu.const_gpr16(insn.reg16()).value() & (X86::TypeTrivia::bits - 1); - auto original = insn.modrm().read16(cpu, insn); - u16 bit_mask = 1 << bit_index; - u16 result = op(original.value(), bit_mask); - cpu.set_cf((original.value() & bit_mask) != 0); - cpu.taint_flags_from(cpu.gpr16(insn.reg16()), original); - if (should_update) - insn.modrm().write16(cpu, insn, shadow_wrap_with_taint_from(result, cpu.gpr16(insn.reg16()), original)); - return; - } - // FIXME: Is this supposed to perform a full 16-bit read/modify/write? - unsigned bit_offset_in_array = cpu.const_gpr16(insn.reg16()).value() / 8; - unsigned bit_offset_in_byte = cpu.const_gpr16(insn.reg16()).value() & 7; - auto address = insn.modrm().resolve(cpu, insn); - address.set_offset(address.offset() + bit_offset_in_array); - auto dest = cpu.read_memory8(address); - u8 bit_mask = 1 << bit_offset_in_byte; - u8 result = op(dest.value(), bit_mask); - cpu.set_cf((dest.value() & bit_mask) != 0); - cpu.taint_flags_from(cpu.gpr16(insn.reg16()), dest); - if (should_update) - cpu.write_memory8(address, shadow_wrap_with_taint_from(result, cpu.gpr16(insn.reg16()), dest)); -} - -template -ALWAYS_INLINE void BTx_RM32_reg32(SoftCPU& cpu, const X86::Instruction& insn, Op op) -{ - if (insn.modrm().is_register()) { - unsigned bit_index = cpu.const_gpr32(insn.reg32()).value() & (X86::TypeTrivia::bits - 1); - auto original = insn.modrm().read32(cpu, insn); - u32 bit_mask = 1 << bit_index; - u32 result = op(original.value(), bit_mask); - cpu.set_cf((original.value() & bit_mask) != 0); - cpu.taint_flags_from(cpu.gpr32(insn.reg32()), original); - if (should_update) - insn.modrm().write32(cpu, insn, shadow_wrap_with_taint_from(result, cpu.gpr32(insn.reg32()), original)); - return; - } - // FIXME: Is this supposed to perform a full 32-bit read/modify/write? - unsigned bit_offset_in_array = cpu.const_gpr32(insn.reg32()).value() / 8; - unsigned bit_offset_in_byte = cpu.const_gpr32(insn.reg32()).value() & 7; - auto address = insn.modrm().resolve(cpu, insn); - address.set_offset(address.offset() + bit_offset_in_array); - auto dest = cpu.read_memory8(address); - u8 bit_mask = 1 << bit_offset_in_byte; - u8 result = op(dest.value(), bit_mask); - cpu.set_cf((dest.value() & bit_mask) != 0); - cpu.taint_flags_from(cpu.gpr32(insn.reg32()), dest); - if (should_update) - cpu.write_memory8(address, shadow_wrap_with_taint_from(result, cpu.gpr32(insn.reg32()), dest)); -} - -template -ALWAYS_INLINE void BTx_RM16_imm8(SoftCPU& cpu, const X86::Instruction& insn, Op op) -{ - unsigned bit_index = insn.imm8() & (X86::TypeTrivia::mask); - - // FIXME: Support higher bit indices - VERIFY(bit_index < 16); - - auto original = insn.modrm().read16(cpu, insn); - u16 bit_mask = 1 << bit_index; - auto result = op(original.value(), bit_mask); - cpu.set_cf((original.value() & bit_mask) != 0); - cpu.taint_flags_from(original); - if (should_update) - insn.modrm().write16(cpu, insn, shadow_wrap_with_taint_from(result, original)); -} - -template -ALWAYS_INLINE void BTx_RM32_imm8(SoftCPU& cpu, const X86::Instruction& insn, Op op) -{ - unsigned bit_index = insn.imm8() & (X86::TypeTrivia::mask); - - // FIXME: Support higher bit indices - VERIFY(bit_index < 32); - - auto original = insn.modrm().read32(cpu, insn); - u32 bit_mask = 1 << bit_index; - auto result = op(original.value(), bit_mask); - cpu.set_cf((original.value() & bit_mask) != 0); - cpu.taint_flags_from(original); - if (should_update) - insn.modrm().write32(cpu, insn, shadow_wrap_with_taint_from(result, original)); -} - -#define DEFINE_GENERIC_BTx_INSN_HANDLERS(mnemonic, op, update_dest) \ - void SoftCPU::mnemonic##_RM32_reg32(const X86::Instruction& insn) \ - { \ - BTx_RM32_reg32(*this, insn, op); \ - } \ - void SoftCPU::mnemonic##_RM16_reg16(const X86::Instruction& insn) \ - { \ - BTx_RM16_reg16(*this, insn, op); \ - } \ - void SoftCPU::mnemonic##_RM32_imm8(const X86::Instruction& insn) \ - { \ - BTx_RM32_imm8(*this, insn, op); \ - } \ - void SoftCPU::mnemonic##_RM16_imm8(const X86::Instruction& insn) \ - { \ - BTx_RM16_imm8(*this, insn, op); \ - } - -DEFINE_GENERIC_BTx_INSN_HANDLERS(BTS, op_bts, true); -DEFINE_GENERIC_BTx_INSN_HANDLERS(BTR, op_btr, true); -DEFINE_GENERIC_BTx_INSN_HANDLERS(BTC, op_btc, true); -DEFINE_GENERIC_BTx_INSN_HANDLERS(BT, op_bt, false); - -void SoftCPU::CALL_FAR_mem16(const X86::Instruction&) -{ - TODO(); -} -void SoftCPU::CALL_FAR_mem32(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::CALL_RM16(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::CALL_RM32(const X86::Instruction& insn) -{ - auto address = insn.modrm().read32(*this, insn); - push32(shadow_wrap_as_initialized(eip())); - warn_if_uninitialized(address, "call rm32"); - set_eip(address.value()); - // FIXME: this won't catch at the moment due to us not having a way to set - // the watch point - m_emulator.call_callback(address.value()); -} - -void SoftCPU::CALL_imm16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::CALL_imm16_imm16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::CALL_imm16_imm32(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::CALL_imm32(const X86::Instruction& insn) -{ - push32(shadow_wrap_as_initialized(eip())); - set_eip(eip() + (i32)insn.imm32()); - // FIXME: this won't catch at the moment due to us not having a way to set - // the watch point - m_emulator.call_callback(eip() + (i32)insn.imm32()); -} - -void SoftCPU::CBW(const X86::Instruction&) -{ - set_ah(shadow_wrap_with_taint_from((al().value() & 0x80) ? 0xff : 0x00, al())); -} - -void SoftCPU::CDQ(const X86::Instruction&) -{ - if (eax().value() & 0x80000000) - set_edx(shadow_wrap_with_taint_from(0xffffffff, eax())); - else - set_edx(shadow_wrap_with_taint_from(0, eax())); -} - -void SoftCPU::CLC(const X86::Instruction&) -{ - set_cf(false); -} - -void SoftCPU::CLD(const X86::Instruction&) -{ - set_df(false); -} - -void SoftCPU::CLI(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::CLTS(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::CMC(const X86::Instruction&) -{ - set_cf(!cf()); -} - -void SoftCPU::CMOVcc_reg16_RM16(const X86::Instruction& insn) -{ - warn_if_flags_tainted("cmovcc reg16, rm16"); - if (evaluate_condition(insn.cc())) - gpr16(insn.reg16()) = insn.modrm().read16(*this, insn); -} - -void SoftCPU::CMOVcc_reg32_RM32(const X86::Instruction& insn) -{ - warn_if_flags_tainted("cmovcc reg32, rm32"); - if (evaluate_condition(insn.cc())) - gpr32(insn.reg32()) = insn.modrm().read32(*this, insn); -} - -template -ALWAYS_INLINE static void do_cmps(SoftCPU& cpu, const X86::Instruction& insn) -{ - auto src_segment = cpu.segment(insn.segment_prefix().value_or(X86::SegmentRegister::DS)); - cpu.do_once_or_repeat(insn, [&] { - auto src = cpu.read_memory({ src_segment, cpu.source_index(insn.address_size()).value() }); - auto dest = cpu.read_memory({ cpu.es(), cpu.destination_index(insn.address_size()).value() }); - op_sub(cpu, dest, src); - cpu.step_source_index(insn.address_size(), sizeof(T)); - cpu.step_destination_index(insn.address_size(), sizeof(T)); - }); -} - -void SoftCPU::CMPSB(const X86::Instruction& insn) -{ - do_cmps(*this, insn); -} - -void SoftCPU::CMPSD(const X86::Instruction& insn) -{ - do_cmps(*this, insn); -} - -void SoftCPU::CMPSW(const X86::Instruction& insn) -{ - do_cmps(*this, insn); -} - -void SoftCPU::CMPXCHG_RM16_reg16(const X86::Instruction& insn) -{ - auto current = insn.modrm().read16(*this, insn); - taint_flags_from(current, ax()); - if (current.value() == ax().value()) { - set_zf(true); - insn.modrm().write16(*this, insn, const_gpr16(insn.reg16())); - } else { - set_zf(false); - set_ax(current); - } -} - -void SoftCPU::CMPXCHG_RM32_reg32(const X86::Instruction& insn) -{ - auto current = insn.modrm().read32(*this, insn); - taint_flags_from(current, eax()); - if (current.value() == eax().value()) { - set_zf(true); - insn.modrm().write32(*this, insn, const_gpr32(insn.reg32())); - } else { - set_zf(false); - set_eax(current); - } -} - -void SoftCPU::CMPXCHG_RM8_reg8(const X86::Instruction& insn) -{ - auto current = insn.modrm().read8(*this, insn); - taint_flags_from(current, al()); - if (current.value() == al().value()) { - set_zf(true); - insn.modrm().write8(*this, insn, const_gpr8(insn.reg8())); - } else { - set_zf(false); - set_al(current); - } -} - -void SoftCPU::CPUID(const X86::Instruction&) -{ - if (eax().value() == 0) { - set_eax(shadow_wrap_as_initialized(1)); - set_ebx(shadow_wrap_as_initialized(0x6c6c6548)); - set_edx(shadow_wrap_as_initialized(0x6972466f)); - set_ecx(shadow_wrap_as_initialized(0x73646e65)); - return; - } - - if (eax().value() == 1) { - u32 stepping = 0; - u32 model = 1; - u32 family = 3; - u32 type = 0; - set_eax(shadow_wrap_as_initialized(stepping | (model << 4) | (family << 8) | (type << 12))); - set_ebx(shadow_wrap_as_initialized(0)); - set_edx(shadow_wrap_as_initialized((1 << 15))); // Features (CMOV) - set_ecx(shadow_wrap_as_initialized(0)); - return; - } - - dbgln("Unhandled CPUID with eax={:p}", eax().value()); -} - -void SoftCPU::CWD(const X86::Instruction&) -{ - set_dx(shadow_wrap_with_taint_from((ax().value() & 0x8000) ? 0xffff : 0x0000, ax())); -} - -void SoftCPU::CWDE(const X86::Instruction&) -{ - set_eax(shadow_wrap_with_taint_from(sign_extended_to(ax().value()), ax())); -} - -void SoftCPU::DAA(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::DAS(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::DEC_RM16(const X86::Instruction& insn) -{ - insn.modrm().write16(*this, insn, op_dec(*this, insn.modrm().read16(*this, insn))); -} - -void SoftCPU::DEC_RM32(const X86::Instruction& insn) -{ - insn.modrm().write32(*this, insn, op_dec(*this, insn.modrm().read32(*this, insn))); -} - -void SoftCPU::DEC_RM8(const X86::Instruction& insn) -{ - insn.modrm().write8(*this, insn, op_dec(*this, insn.modrm().read8(*this, insn))); -} - -void SoftCPU::DEC_reg16(const X86::Instruction& insn) -{ - gpr16(insn.reg16()) = op_dec(*this, const_gpr16(insn.reg16())); -} - -void SoftCPU::DEC_reg32(const X86::Instruction& insn) -{ - gpr32(insn.reg32()) = op_dec(*this, const_gpr32(insn.reg32())); -} - -void SoftCPU::DIV_RM16(const X86::Instruction& insn) -{ - auto divisor = insn.modrm().read16(*this, insn); - if (divisor.value() == 0) { - reportln("Divide by zero"sv); - TODO(); - } - u32 dividend = ((u32)dx().value() << 16) | ax().value(); - auto quotient = dividend / divisor.value(); - if (quotient > NumericLimits::max()) { - reportln("Divide overflow"sv); - TODO(); - } - - auto remainder = dividend % divisor.value(); - auto original_ax = ax(); - - set_ax(shadow_wrap_with_taint_from(quotient, original_ax, dx())); - set_dx(shadow_wrap_with_taint_from(remainder, original_ax, dx())); -} - -void SoftCPU::DIV_RM32(const X86::Instruction& insn) -{ - auto divisor = insn.modrm().read32(*this, insn); - if (divisor.value() == 0) { - reportln("Divide by zero"sv); - TODO(); - } - u64 dividend = ((u64)edx().value() << 32) | eax().value(); - auto quotient = dividend / divisor.value(); - if (quotient > NumericLimits::max()) { - reportln("Divide overflow"sv); - TODO(); - } - - auto remainder = dividend % divisor.value(); - auto original_eax = eax(); - - set_eax(shadow_wrap_with_taint_from(quotient, original_eax, edx(), divisor)); - set_edx(shadow_wrap_with_taint_from(remainder, original_eax, edx(), divisor)); -} - -void SoftCPU::DIV_RM8(const X86::Instruction& insn) -{ - auto divisor = insn.modrm().read8(*this, insn); - if (divisor.value() == 0) { - reportln("Divide by zero"sv); - TODO(); - } - u16 dividend = ax().value(); - auto quotient = dividend / divisor.value(); - if (quotient > NumericLimits::max()) { - reportln("Divide overflow"sv); - TODO(); - } - - auto remainder = dividend % divisor.value(); - auto original_ax = ax(); - set_al(shadow_wrap_with_taint_from(quotient, original_ax, divisor)); - set_ah(shadow_wrap_with_taint_from(remainder, original_ax, divisor)); -} - -void SoftCPU::ENTER16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::ENTER32(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::ESCAPE(const X86::Instruction&) -{ - reportln("FIXME: x87 floating-point support"sv); - m_emulator.dump_backtrace(); - TODO(); -} -FPU_INSTRUCTION(FADD_RM32); -FPU_INSTRUCTION(FMUL_RM32); -FPU_INSTRUCTION(FCOM_RM32); -FPU_INSTRUCTION(FCOMP_RM32); -FPU_INSTRUCTION(FSUB_RM32); -FPU_INSTRUCTION(FSUBR_RM32); -FPU_INSTRUCTION(FDIV_RM32); -FPU_INSTRUCTION(FDIVR_RM32); -FPU_INSTRUCTION(FLD_RM32); -FPU_INSTRUCTION(FXCH); -FPU_INSTRUCTION(FST_RM32); -FPU_INSTRUCTION(FNOP); -FPU_INSTRUCTION(FSTP_RM32); -FPU_INSTRUCTION(FLDENV); -FPU_INSTRUCTION(FCHS); -FPU_INSTRUCTION(FABS); -FPU_INSTRUCTION(FTST); -FPU_INSTRUCTION(FXAM); -FPU_INSTRUCTION(FLDCW); -FPU_INSTRUCTION(FLD1); -FPU_INSTRUCTION(FLDL2T); -FPU_INSTRUCTION(FLDL2E); -FPU_INSTRUCTION(FLDPI); -FPU_INSTRUCTION(FLDLG2); -FPU_INSTRUCTION(FLDLN2); -FPU_INSTRUCTION(FLDZ); -FPU_INSTRUCTION(FNSTENV); -FPU_INSTRUCTION(F2XM1); -FPU_INSTRUCTION(FYL2X); -FPU_INSTRUCTION(FPTAN); -FPU_INSTRUCTION(FPATAN); -FPU_INSTRUCTION(FXTRACT); -FPU_INSTRUCTION(FPREM1); -FPU_INSTRUCTION(FDECSTP); -FPU_INSTRUCTION(FINCSTP); -FPU_INSTRUCTION(FNSTCW); -FPU_INSTRUCTION(FPREM); -FPU_INSTRUCTION(FYL2XP1); -FPU_INSTRUCTION(FSQRT); -FPU_INSTRUCTION(FSINCOS); -FPU_INSTRUCTION(FRNDINT); -FPU_INSTRUCTION(FSCALE); -FPU_INSTRUCTION(FSIN); -FPU_INSTRUCTION(FCOS); -FPU_INSTRUCTION(FIADD_RM32); -FPU_INSTRUCTION(FCMOVB); -FPU_INSTRUCTION(FIMUL_RM32); -FPU_INSTRUCTION(FCMOVE); -FPU_INSTRUCTION(FICOM_RM32); -FPU_INSTRUCTION(FCMOVBE); -FPU_INSTRUCTION(FICOMP_RM32); -FPU_INSTRUCTION(FCMOVU); -FPU_INSTRUCTION(FISUB_RM32); -FPU_INSTRUCTION(FISUBR_RM32); -FPU_INSTRUCTION(FUCOMPP); -FPU_INSTRUCTION(FIDIV_RM32); -FPU_INSTRUCTION(FIDIVR_RM32); -FPU_INSTRUCTION(FILD_RM32); -FPU_INSTRUCTION(FCMOVNB); -FPU_INSTRUCTION(FISTTP_RM32); -FPU_INSTRUCTION(FCMOVNE); -FPU_INSTRUCTION(FIST_RM32); -FPU_INSTRUCTION(FCMOVNBE); -FPU_INSTRUCTION(FISTP_RM32); -FPU_INSTRUCTION(FCMOVNU); -FPU_INSTRUCTION(FNENI); -FPU_INSTRUCTION(FNDISI); -FPU_INSTRUCTION(FNCLEX); -FPU_INSTRUCTION(FNINIT); -FPU_INSTRUCTION(FNSETPM); -FPU_INSTRUCTION(FLD_RM80); -FPU_INSTRUCTION(FUCOMI); -FPU_INSTRUCTION(FCOMI); -FPU_INSTRUCTION(FSTP_RM80); -FPU_INSTRUCTION(FADD_RM64); -FPU_INSTRUCTION(FMUL_RM64); -FPU_INSTRUCTION(FCOM_RM64); -FPU_INSTRUCTION(FCOMP_RM64); -FPU_INSTRUCTION(FSUB_RM64); -FPU_INSTRUCTION(FSUBR_RM64); -FPU_INSTRUCTION(FDIV_RM64); -FPU_INSTRUCTION(FDIVR_RM64); -FPU_INSTRUCTION(FLD_RM64); -FPU_INSTRUCTION(FFREE); -FPU_INSTRUCTION(FISTTP_RM64); -FPU_INSTRUCTION(FST_RM64); -FPU_INSTRUCTION(FSTP_RM64); -FPU_INSTRUCTION(FRSTOR); -FPU_INSTRUCTION(FUCOM); -FPU_INSTRUCTION(FUCOMP); -FPU_INSTRUCTION(FNSAVE); -FPU_INSTRUCTION(FNSTSW); -FPU_INSTRUCTION(FIADD_RM16); -FPU_INSTRUCTION(FADDP); -FPU_INSTRUCTION(FIMUL_RM16); -FPU_INSTRUCTION(FMULP); -FPU_INSTRUCTION(FICOM_RM16); -FPU_INSTRUCTION(FICOMP_RM16); -FPU_INSTRUCTION(FCOMPP); -FPU_INSTRUCTION(FISUB_RM16); -FPU_INSTRUCTION(FSUBRP); -FPU_INSTRUCTION(FISUBR_RM16); -FPU_INSTRUCTION(FSUBP); -FPU_INSTRUCTION(FIDIV_RM16); -FPU_INSTRUCTION(FDIVRP); -FPU_INSTRUCTION(FIDIVR_RM16); -FPU_INSTRUCTION(FDIVP); -FPU_INSTRUCTION(FILD_RM16); -FPU_INSTRUCTION(FFREEP); -FPU_INSTRUCTION(FISTTP_RM16); -FPU_INSTRUCTION(FIST_RM16); -FPU_INSTRUCTION(FISTP_RM16); -FPU_INSTRUCTION(FBLD_M80); -FPU_INSTRUCTION(FNSTSW_AX); -FPU_INSTRUCTION(FILD_RM64); -FPU_INSTRUCTION(FUCOMIP); -FPU_INSTRUCTION(FBSTP_M80); -FPU_INSTRUCTION(FCOMIP); -FPU_INSTRUCTION(FISTP_RM64); - -void SoftCPU::HLT(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::IDIV_RM16(const X86::Instruction& insn) -{ - auto divisor_with_shadow = insn.modrm().read16(*this, insn); - auto divisor = (i16)divisor_with_shadow.value(); - if (divisor == 0) { - reportln("Divide by zero"sv); - TODO(); - } - i32 dividend = (i32)(((u32)dx().value() << 16) | (u32)ax().value()); - i32 result = dividend / divisor; - if (result > NumericLimits::max() || result < NumericLimits::min()) { - reportln("Divide overflow"sv); - TODO(); - } - - auto original_ax = ax(); - set_ax(shadow_wrap_with_taint_from(result, original_ax, dx(), divisor_with_shadow)); - set_dx(shadow_wrap_with_taint_from(dividend % divisor, original_ax, dx(), divisor_with_shadow)); -} - -void SoftCPU::IDIV_RM32(const X86::Instruction& insn) -{ - auto divisor_with_shadow = insn.modrm().read32(*this, insn); - auto divisor = (i32)divisor_with_shadow.value(); - if (divisor == 0) { - reportln("Divide by zero"sv); - TODO(); - } - i64 dividend = (i64)(((u64)edx().value() << 32) | (u64)eax().value()); - i64 result = dividend / divisor; - if (result > NumericLimits::max() || result < NumericLimits::min()) { - reportln("Divide overflow"sv); - TODO(); - } - - auto original_eax = eax(); - set_eax(shadow_wrap_with_taint_from(result, original_eax, edx(), divisor_with_shadow)); - set_edx(shadow_wrap_with_taint_from(dividend % divisor, original_eax, edx(), divisor_with_shadow)); -} - -void SoftCPU::IDIV_RM8(const X86::Instruction& insn) -{ - auto divisor_with_shadow = insn.modrm().read8(*this, insn); - auto divisor = (i8)divisor_with_shadow.value(); - if (divisor == 0) { - reportln("Divide by zero"sv); - TODO(); - } - i16 dividend = ax().value(); - i16 result = dividend / divisor; - if (result > NumericLimits::max() || result < NumericLimits::min()) { - reportln("Divide overflow"sv); - TODO(); - } - - auto original_ax = ax(); - set_al(shadow_wrap_with_taint_from(result, divisor_with_shadow, original_ax)); - set_ah(shadow_wrap_with_taint_from(dividend % divisor, divisor_with_shadow, original_ax)); -} - -void SoftCPU::IMUL_RM16(const X86::Instruction& insn) -{ - i16 result_high; - i16 result_low; - auto src = insn.modrm().read16(*this, insn); - op_imul(*this, src.value(), ax().value(), result_high, result_low); - gpr16(X86::RegisterDX) = shadow_wrap_with_taint_from(result_high, src, ax()); - gpr16(X86::RegisterAX) = shadow_wrap_with_taint_from(result_low, src, ax()); -} - -void SoftCPU::IMUL_RM32(const X86::Instruction& insn) -{ - i32 result_high; - i32 result_low; - auto src = insn.modrm().read32(*this, insn); - op_imul(*this, src.value(), eax().value(), result_high, result_low); - gpr32(X86::RegisterEDX) = shadow_wrap_with_taint_from(result_high, src, eax()); - gpr32(X86::RegisterEAX) = shadow_wrap_with_taint_from(result_low, src, eax()); -} - -void SoftCPU::IMUL_RM8(const X86::Instruction& insn) -{ - i8 result_high; - i8 result_low; - auto src = insn.modrm().read8(*this, insn); - op_imul(*this, src.value(), al().value(), result_high, result_low); - gpr8(X86::RegisterAH) = shadow_wrap_with_taint_from(result_high, src, al()); - gpr8(X86::RegisterAL) = shadow_wrap_with_taint_from(result_low, src, al()); -} - -void SoftCPU::IMUL_reg16_RM16(const X86::Instruction& insn) -{ - i16 result_high; - i16 result_low; - auto src = insn.modrm().read16(*this, insn); - op_imul(*this, gpr16(insn.reg16()).value(), src.value(), result_high, result_low); - gpr16(insn.reg16()) = shadow_wrap_with_taint_from(result_low, src, gpr16(insn.reg16())); -} - -void SoftCPU::IMUL_reg16_RM16_imm16(const X86::Instruction& insn) -{ - i16 result_high; - i16 result_low; - auto src = insn.modrm().read16(*this, insn); - op_imul(*this, src.value(), insn.imm16(), result_high, result_low); - gpr16(insn.reg16()) = shadow_wrap_with_taint_from(result_low, src); -} - -void SoftCPU::IMUL_reg16_RM16_imm8(const X86::Instruction& insn) -{ - i16 result_high; - i16 result_low; - auto src = insn.modrm().read16(*this, insn); - op_imul(*this, src.value(), sign_extended_to(insn.imm8()), result_high, result_low); - gpr16(insn.reg16()) = shadow_wrap_with_taint_from(result_low, src); -} - -void SoftCPU::IMUL_reg32_RM32(const X86::Instruction& insn) -{ - i32 result_high; - i32 result_low; - auto src = insn.modrm().read32(*this, insn); - op_imul(*this, gpr32(insn.reg32()).value(), src.value(), result_high, result_low); - gpr32(insn.reg32()) = shadow_wrap_with_taint_from(result_low, src, gpr32(insn.reg32())); -} - -void SoftCPU::IMUL_reg32_RM32_imm32(const X86::Instruction& insn) -{ - i32 result_high; - i32 result_low; - auto src = insn.modrm().read32(*this, insn); - op_imul(*this, src.value(), insn.imm32(), result_high, result_low); - gpr32(insn.reg32()) = shadow_wrap_with_taint_from(result_low, src); -} - -void SoftCPU::IMUL_reg32_RM32_imm8(const X86::Instruction& insn) -{ - i32 result_high; - i32 result_low; - auto src = insn.modrm().read32(*this, insn); - op_imul(*this, src.value(), sign_extended_to(insn.imm8()), result_high, result_low); - gpr32(insn.reg32()) = shadow_wrap_with_taint_from(result_low, src); -} - -void SoftCPU::INC_RM16(const X86::Instruction& insn) -{ - insn.modrm().write16(*this, insn, op_inc(*this, insn.modrm().read16(*this, insn))); -} - -void SoftCPU::INC_RM32(const X86::Instruction& insn) -{ - insn.modrm().write32(*this, insn, op_inc(*this, insn.modrm().read32(*this, insn))); -} - -void SoftCPU::INC_RM8(const X86::Instruction& insn) -{ - insn.modrm().write8(*this, insn, op_inc(*this, insn.modrm().read8(*this, insn))); -} - -void SoftCPU::INC_reg16(const X86::Instruction& insn) -{ - gpr16(insn.reg16()) = op_inc(*this, const_gpr16(insn.reg16())); -} - -void SoftCPU::INC_reg32(const X86::Instruction& insn) -{ - gpr32(insn.reg32()) = op_inc(*this, const_gpr32(insn.reg32())); -} - -void SoftCPU::INSB(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::INSD(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::INSW(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::INT1(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::INT3(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::INTO(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::INT_imm8(const X86::Instruction& insn) -{ - VERIFY(insn.imm8() == 0x82); - // FIXME: virt_syscall should take ValueWithShadow and whine about uninitialized arguments - set_eax(shadow_wrap_as_initialized(m_emulator.virt_syscall(eax().value(), edx().value(), ecx().value(), ebx().value()))); -} - -void SoftCPU::INVLPG(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::IN_AL_DX(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::IN_AL_imm8(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::IN_AX_DX(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::IN_AX_imm8(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::IN_EAX_DX(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::IN_EAX_imm8(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::IRET(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::JCXZ_imm8(const X86::Instruction& insn) -{ - switch (insn.address_size()) { - case X86::AddressSize::Size32: - warn_if_uninitialized(ecx(), "jecxz imm8"); - if (ecx().value() == 0) - set_eip(eip() + (i8)insn.imm8()); - break; - case X86::AddressSize::Size16: - warn_if_uninitialized(cx(), "jcxz imm8"); - if (cx().value() == 0) - set_eip(eip() + (i8)insn.imm8()); - break; - default: - VERIFY_NOT_REACHED(); - } -} - -void SoftCPU::JMP_FAR_mem16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::JMP_FAR_mem32(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::JMP_RM16(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::JMP_RM32(const X86::Instruction& insn) -{ - set_eip(insn.modrm().read32(*this, insn).value()); -} - -void SoftCPU::JMP_imm16(const X86::Instruction& insn) -{ - set_eip(eip() + (i16)insn.imm16()); -} - -void SoftCPU::JMP_imm16_imm16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::JMP_imm16_imm32(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::JMP_imm32(const X86::Instruction& insn) -{ - set_eip(eip() + (i32)insn.imm32()); -} - -void SoftCPU::JMP_short_imm8(const X86::Instruction& insn) -{ - set_eip(eip() + (i8)insn.imm8()); -} - -void SoftCPU::Jcc_NEAR_imm(const X86::Instruction& insn) -{ - warn_if_flags_tainted("jcc near imm32"); - if (evaluate_condition(insn.cc())) - set_eip(eip() + (i32)insn.imm32()); -} - -void SoftCPU::Jcc_imm8(const X86::Instruction& insn) -{ - warn_if_flags_tainted("jcc imm8"); - if (evaluate_condition(insn.cc())) - set_eip(eip() + (i8)insn.imm8()); -} - -void SoftCPU::LAHF(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::LAR_reg16_RM16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::LAR_reg32_RM32(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::LDS_reg16_mem16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::LDS_reg32_mem32(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::LEAVE16(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::LEAVE32(const X86::Instruction&) -{ - auto new_ebp = read_memory32({ ss(), ebp().value() }); - set_esp({ ebp().value() + 4, ebp().shadow() }); - set_ebp(new_ebp); -} - -void SoftCPU::LEA_reg16_mem16(const X86::Instruction& insn) -{ - // FIXME: Respect shadow values - gpr16(insn.reg16()) = shadow_wrap_as_initialized(insn.modrm().resolve(*this, insn).offset()); -} - -void SoftCPU::LEA_reg32_mem32(const X86::Instruction& insn) -{ - // FIXME: Respect shadow values - gpr32(insn.reg32()) = shadow_wrap_as_initialized(insn.modrm().resolve(*this, insn).offset()); -} - -void SoftCPU::LES_reg16_mem16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::LES_reg32_mem32(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::LFS_reg16_mem16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::LFS_reg32_mem32(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::LGDT(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::LGS_reg16_mem16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::LGS_reg32_mem32(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::LIDT(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::LLDT_RM16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::LMSW_RM16(const X86::Instruction&) { TODO_INSN(); } - -template -ALWAYS_INLINE static void do_lods(SoftCPU& cpu, const X86::Instruction& insn) -{ - auto src_segment = cpu.segment(insn.segment_prefix().value_or(X86::SegmentRegister::DS)); - cpu.do_once_or_repeat(insn, [&] { - auto src = cpu.read_memory({ src_segment, cpu.source_index(insn.address_size()).value() }); - cpu.gpr(X86::RegisterAL) = src; - cpu.step_source_index(insn.address_size(), sizeof(T)); - }); -} - -void SoftCPU::LODSB(const X86::Instruction& insn) -{ - do_lods(*this, insn); -} - -void SoftCPU::LODSD(const X86::Instruction& insn) -{ - do_lods(*this, insn); -} - -void SoftCPU::LODSW(const X86::Instruction& insn) -{ - do_lods(*this, insn); -} - -void SoftCPU::LOOPNZ_imm8(const X86::Instruction& insn) -{ - warn_if_flags_tainted("loopnz"); - switch (insn.address_size()) { - case X86::AddressSize::Size32: - set_ecx({ ecx().value() - 1, ecx().shadow() }); - if (ecx().value() != 0 && !zf()) - set_eip(eip() + (i8)insn.imm8()); - break; - case X86::AddressSize::Size16: - set_cx({ (u16)(cx().value() - 1), cx().shadow() }); - if (cx().value() != 0 && !zf()) - set_eip(eip() + (i8)insn.imm8()); - break; - default: - VERIFY_NOT_REACHED(); - } -} -void SoftCPU::LOOPZ_imm8(const X86::Instruction& insn) -{ - warn_if_flags_tainted("loopz"); - switch (insn.address_size()) { - case X86::AddressSize::Size32: - set_ecx({ ecx().value() - 1, ecx().shadow() }); - if (ecx().value() != 0 && zf()) - set_eip(eip() + (i8)insn.imm8()); - break; - case X86::AddressSize::Size16: - set_cx({ (u16)(cx().value() - 1), cx().shadow() }); - if (cx().value() != 0 && zf()) - set_eip(eip() + (i8)insn.imm8()); - break; - default: - VERIFY_NOT_REACHED(); - } -} - -void SoftCPU::LOOP_imm8(const X86::Instruction& insn) -{ - switch (insn.address_size()) { - case X86::AddressSize::Size32: - set_ecx({ ecx().value() - 1, ecx().shadow() }); - if (ecx().value() != 0) - set_eip(eip() + (i8)insn.imm8()); - break; - case X86::AddressSize::Size16: - set_cx({ (u16)(cx().value() - 1), cx().shadow() }); - if (cx().value() != 0) - set_eip(eip() + (i8)insn.imm8()); - break; - default: - VERIFY_NOT_REACHED(); - } -} - -void SoftCPU::LSL_reg16_RM16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::LSL_reg32_RM32(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::LSS_reg16_mem16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::LSS_reg32_mem32(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::LTR_RM16(const X86::Instruction&) { TODO_INSN(); } - -template -ALWAYS_INLINE static void do_movs(SoftCPU& cpu, const X86::Instruction& insn) -{ - auto src_segment = cpu.segment(insn.segment_prefix().value_or(X86::SegmentRegister::DS)); - cpu.do_once_or_repeat(insn, [&] { - auto src = cpu.read_memory({ src_segment, cpu.source_index(insn.address_size()).value() }); - cpu.write_memory({ cpu.es(), cpu.destination_index(insn.address_size()).value() }, src); - cpu.step_source_index(insn.address_size(), sizeof(T)); - cpu.step_destination_index(insn.address_size(), sizeof(T)); - }); -} - -void SoftCPU::MOVSB(const X86::Instruction& insn) -{ - do_movs(*this, insn); -} - -void SoftCPU::MOVSD(const X86::Instruction& insn) -{ - do_movs(*this, insn); -} - -void SoftCPU::MOVSW(const X86::Instruction& insn) -{ - do_movs(*this, insn); -} - -void SoftCPU::MOVSX_reg16_RM8(const X86::Instruction& insn) -{ - auto src = insn.modrm().read8(*this, insn); - gpr16(insn.reg16()) = shadow_wrap_with_taint_from(sign_extended_to(src.value()), src); -} - -void SoftCPU::MOVSX_reg32_RM16(const X86::Instruction& insn) -{ - auto src = insn.modrm().read16(*this, insn); - gpr32(insn.reg32()) = shadow_wrap_with_taint_from(sign_extended_to(src.value()), src); -} - -void SoftCPU::MOVSX_reg32_RM8(const X86::Instruction& insn) -{ - auto src = insn.modrm().read8(*this, insn); - gpr32(insn.reg32()) = shadow_wrap_with_taint_from(sign_extended_to(src.value()), src); -} - -void SoftCPU::MOVZX_reg16_RM8(const X86::Instruction& insn) -{ - auto src = insn.modrm().read8(*this, insn); - gpr16(insn.reg16()) = ValueWithShadow(src.value(), 0x0100 | (src.shadow_as_value() & 0xff)); -} - -void SoftCPU::MOVZX_reg32_RM16(const X86::Instruction& insn) -{ - auto src = insn.modrm().read16(*this, insn); - gpr32(insn.reg32()) = ValueWithShadow(src.value(), 0x01010000 | (src.shadow_as_value() & 0xffff)); -} - -void SoftCPU::MOVZX_reg32_RM8(const X86::Instruction& insn) -{ - auto src = insn.modrm().read8(*this, insn); - gpr32(insn.reg32()) = ValueWithShadow(src.value(), 0x01010100 | (src.shadow_as_value() & 0xff)); -} - -void SoftCPU::MOV_AL_moff8(const X86::Instruction& insn) -{ - set_al(read_memory8({ segment(insn.segment_prefix().value_or(X86::SegmentRegister::DS)), insn.imm_address() })); -} - -void SoftCPU::MOV_AX_moff16(const X86::Instruction& insn) -{ - set_ax(read_memory16({ segment(insn.segment_prefix().value_or(X86::SegmentRegister::DS)), insn.imm_address() })); -} - -void SoftCPU::MOV_CR_reg32(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::MOV_DR_reg32(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::MOV_EAX_moff32(const X86::Instruction& insn) -{ - set_eax(read_memory32({ segment(insn.segment_prefix().value_or(X86::SegmentRegister::DS)), insn.imm_address() })); -} - -void SoftCPU::MOV_RM16_imm16(const X86::Instruction& insn) -{ - insn.modrm().write16(*this, insn, shadow_wrap_as_initialized(insn.imm16())); -} - -void SoftCPU::MOV_RM16_reg16(const X86::Instruction& insn) -{ - insn.modrm().write16(*this, insn, const_gpr16(insn.reg16())); -} - -void SoftCPU::MOV_RM16_seg(X86::Instruction const& insn) -{ - insn.modrm().write16(*this, insn, shadow_wrap_as_initialized(m_segment[to_underlying(insn.segment_register())])); -} - -void SoftCPU::MOV_RM32_imm32(const X86::Instruction& insn) -{ - insn.modrm().write32(*this, insn, shadow_wrap_as_initialized(insn.imm32())); -} - -void SoftCPU::MOV_RM32_reg32(const X86::Instruction& insn) -{ - insn.modrm().write32(*this, insn, const_gpr32(insn.reg32())); -} - -void SoftCPU::MOV_RM8_imm8(const X86::Instruction& insn) -{ - insn.modrm().write8(*this, insn, shadow_wrap_as_initialized(insn.imm8())); -} - -void SoftCPU::MOV_RM8_reg8(const X86::Instruction& insn) -{ - insn.modrm().write8(*this, insn, const_gpr8(insn.reg8())); -} - -void SoftCPU::MOV_moff16_AX(const X86::Instruction& insn) -{ - write_memory16({ segment(insn.segment_prefix().value_or(X86::SegmentRegister::DS)), insn.imm_address() }, ax()); -} - -void SoftCPU::MOV_moff32_EAX(const X86::Instruction& insn) -{ - write_memory32({ segment(insn.segment_prefix().value_or(X86::SegmentRegister::DS)), insn.imm_address() }, eax()); -} - -void SoftCPU::MOV_moff8_AL(const X86::Instruction& insn) -{ - write_memory8({ segment(insn.segment_prefix().value_or(X86::SegmentRegister::DS)), insn.imm_address() }, al()); -} - -void SoftCPU::MOV_reg16_RM16(const X86::Instruction& insn) -{ - gpr16(insn.reg16()) = insn.modrm().read16(*this, insn); -} - -void SoftCPU::MOV_reg16_imm16(const X86::Instruction& insn) -{ - gpr16(insn.reg16()) = shadow_wrap_as_initialized(insn.imm16()); -} - -void SoftCPU::MOV_reg32_CR(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::MOV_reg32_DR(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::MOV_reg32_RM32(const X86::Instruction& insn) -{ - gpr32(insn.reg32()) = insn.modrm().read32(*this, insn); -} - -void SoftCPU::MOV_reg32_imm32(const X86::Instruction& insn) -{ - gpr32(insn.reg32()) = shadow_wrap_as_initialized(insn.imm32()); -} - -void SoftCPU::MOV_reg8_RM8(const X86::Instruction& insn) -{ - gpr8(insn.reg8()) = insn.modrm().read8(*this, insn); -} - -void SoftCPU::MOV_reg8_imm8(const X86::Instruction& insn) -{ - gpr8(insn.reg8()) = shadow_wrap_as_initialized(insn.imm8()); -} - -void SoftCPU::write_segment_register(X86::SegmentRegister segment_register, ValueWithShadow value) -{ - // FIXME: Validate the segment selector and raise exception if necessary. - // FIXME: Complain if uninitialized data is moved into a segment register. - m_segment[to_underlying(segment_register)] = value.value(); -} - -void SoftCPU::MOV_seg_RM16(X86::Instruction const& insn) -{ - write_segment_register(insn.segment_register(), insn.modrm().read16(*this, insn)); -} - -void SoftCPU::MOV_seg_RM32(X86::Instruction const& insn) -{ - // NOTE: This instruction performs a 32-bit read but only the bottom 16 bits are used since segment registers are 16-bit. - auto value = insn.modrm().read32(*this, insn); - write_segment_register(insn.segment_register(), ValueWithShadow(value.value(), value.shadow_as_value())); -} - -void SoftCPU::MUL_RM16(const X86::Instruction& insn) -{ - auto src = insn.modrm().read16(*this, insn); - u32 result = (u32)ax().value() * (u32)src.value(); - auto original_ax = ax(); - set_ax(shadow_wrap_with_taint_from(result & 0xffff, src, original_ax)); - set_dx(shadow_wrap_with_taint_from(result >> 16, src, original_ax)); - taint_flags_from(src, original_ax); - - set_cf(dx().value() != 0); - set_of(dx().value() != 0); -} - -void SoftCPU::MUL_RM32(const X86::Instruction& insn) -{ - auto src = insn.modrm().read32(*this, insn); - u64 result = (u64)eax().value() * (u64)src.value(); - auto original_eax = eax(); - set_eax(shadow_wrap_with_taint_from(result, src, original_eax)); - set_edx(shadow_wrap_with_taint_from(result >> 32, src, original_eax)); - taint_flags_from(src, original_eax); - - set_cf(edx().value() != 0); - set_of(edx().value() != 0); -} - -void SoftCPU::MUL_RM8(const X86::Instruction& insn) -{ - auto src = insn.modrm().read8(*this, insn); - u16 result = (u16)al().value() * src.value(); - auto original_al = al(); - set_ax(shadow_wrap_with_taint_from(result, src, original_al)); - taint_flags_from(src, original_al); - - set_cf((result & 0xff00) != 0); - set_of((result & 0xff00) != 0); -} - -void SoftCPU::NEG_RM16(const X86::Instruction& insn) -{ - insn.modrm().write16(*this, insn, op_sub>(*this, shadow_wrap_as_initialized(0), insn.modrm().read16(*this, insn))); -} - -void SoftCPU::NEG_RM32(const X86::Instruction& insn) -{ - insn.modrm().write32(*this, insn, op_sub>(*this, shadow_wrap_as_initialized(0), insn.modrm().read32(*this, insn))); -} - -void SoftCPU::NEG_RM8(const X86::Instruction& insn) -{ - insn.modrm().write8(*this, insn, op_sub>(*this, shadow_wrap_as_initialized(0), insn.modrm().read8(*this, insn))); -} - -void SoftCPU::NOP(const X86::Instruction&) -{ -} - -void SoftCPU::NOT_RM16(const X86::Instruction& insn) -{ - auto data = insn.modrm().read16(*this, insn); - insn.modrm().write16(*this, insn, ValueWithShadow(~data.value(), data.shadow())); -} - -void SoftCPU::NOT_RM32(const X86::Instruction& insn) -{ - auto data = insn.modrm().read32(*this, insn); - insn.modrm().write32(*this, insn, ValueWithShadow(~data.value(), data.shadow())); -} - -void SoftCPU::NOT_RM8(const X86::Instruction& insn) -{ - auto data = insn.modrm().read8(*this, insn); - insn.modrm().write8(*this, insn, ValueWithShadow(~data.value(), data.shadow())); -} - -void SoftCPU::OUTSB(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::OUTSD(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::OUTSW(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::OUT_DX_AL(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::OUT_DX_AX(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::OUT_DX_EAX(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::OUT_imm8_AL(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::OUT_imm8_AX(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::OUT_imm8_EAX(const X86::Instruction&) { TODO_INSN(); } - -FPU_INSTRUCTION(PACKSSDW_mm1_mm2m64); -FPU_INSTRUCTION(PACKSSWB_mm1_mm2m64); -FPU_INSTRUCTION(PACKUSWB_mm1_mm2m64); -FPU_INSTRUCTION(PADDB_mm1_mm2m64); -FPU_INSTRUCTION(PADDW_mm1_mm2m64); -FPU_INSTRUCTION(PADDD_mm1_mm2m64); -FPU_INSTRUCTION(PADDSB_mm1_mm2m64); -FPU_INSTRUCTION(PADDSW_mm1_mm2m64); -FPU_INSTRUCTION(PADDUSB_mm1_mm2m64); -FPU_INSTRUCTION(PADDUSW_mm1_mm2m64); -FPU_INSTRUCTION(PAND_mm1_mm2m64); -FPU_INSTRUCTION(PANDN_mm1_mm2m64); -FPU_INSTRUCTION(PCMPEQB_mm1_mm2m64); -FPU_INSTRUCTION(PCMPEQW_mm1_mm2m64); -FPU_INSTRUCTION(PCMPEQD_mm1_mm2m64); -FPU_INSTRUCTION(PCMPGTB_mm1_mm2m64); -FPU_INSTRUCTION(PCMPGTW_mm1_mm2m64); -FPU_INSTRUCTION(PCMPGTD_mm1_mm2m64); -FPU_INSTRUCTION(PMADDWD_mm1_mm2m64); -FPU_INSTRUCTION(PMULHW_mm1_mm2m64); -FPU_INSTRUCTION(PMULLW_mm1_mm2m64); - -void SoftCPU::POPA(const X86::Instruction&) -{ - set_di(pop16()); - set_si(pop16()); - set_bp(pop16()); - - pop16(); - - set_bx(pop16()); - set_dx(pop16()); - set_cx(pop16()); - set_ax(pop16()); -} - -void SoftCPU::POPAD(const X86::Instruction&) -{ - set_edi(pop32()); - set_esi(pop32()); - set_ebp(pop32()); - - pop32(); - - set_ebx(pop32()); - set_edx(pop32()); - set_ecx(pop32()); - set_eax(pop32()); -} - -void SoftCPU::POPF(const X86::Instruction&) -{ - auto popped_value = pop16(); - m_eflags &= ~0xffff; - m_eflags |= popped_value.value(); - taint_flags_from(popped_value); -} - -void SoftCPU::POPFD(const X86::Instruction&) -{ - auto popped_value = pop32(); - m_eflags &= ~0x00fcffff; - m_eflags |= popped_value.value() & 0x00fcffff; - taint_flags_from(popped_value); -} - -void SoftCPU::POP_DS(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::POP_ES(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::POP_FS(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::POP_GS(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::POP_RM16(const X86::Instruction& insn) -{ - insn.modrm().write16(*this, insn, pop16()); -} - -void SoftCPU::POP_RM32(const X86::Instruction& insn) -{ - insn.modrm().write32(*this, insn, pop32()); -} - -void SoftCPU::POP_SS(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::POP_reg16(const X86::Instruction& insn) -{ - gpr16(insn.reg16()) = pop16(); -} - -void SoftCPU::POP_reg32(const X86::Instruction& insn) -{ - gpr32(insn.reg32()) = pop32(); -} - -FPU_INSTRUCTION(POR_mm1_mm2m64); -FPU_INSTRUCTION(PSLLW_mm1_mm2m64); -FPU_INSTRUCTION(PSLLW_mm1_imm8); -FPU_INSTRUCTION(PSLLD_mm1_mm2m64); -FPU_INSTRUCTION(PSLLD_mm1_imm8); -FPU_INSTRUCTION(PSLLQ_mm1_mm2m64); -FPU_INSTRUCTION(PSLLQ_mm1_imm8); -FPU_INSTRUCTION(PSRAW_mm1_mm2m64); -FPU_INSTRUCTION(PSRAW_mm1_imm8); -FPU_INSTRUCTION(PSRAD_mm1_mm2m64); -FPU_INSTRUCTION(PSRAD_mm1_imm8); -FPU_INSTRUCTION(PSRLW_mm1_mm2m64); -FPU_INSTRUCTION(PSRLW_mm1_imm8); -FPU_INSTRUCTION(PSRLD_mm1_mm2m64); -FPU_INSTRUCTION(PSRLD_mm1_imm8); -FPU_INSTRUCTION(PSRLQ_mm1_mm2m64); -FPU_INSTRUCTION(PSRLQ_mm1_imm8); -FPU_INSTRUCTION(PSUBB_mm1_mm2m64); -FPU_INSTRUCTION(PSUBW_mm1_mm2m64); -FPU_INSTRUCTION(PSUBD_mm1_mm2m64); -FPU_INSTRUCTION(PSUBSB_mm1_mm2m64); -FPU_INSTRUCTION(PSUBSW_mm1_mm2m64); -FPU_INSTRUCTION(PSUBUSB_mm1_mm2m64); -FPU_INSTRUCTION(PSUBUSW_mm1_mm2m64); -FPU_INSTRUCTION(PUNPCKHBW_mm1_mm2m64); -FPU_INSTRUCTION(PUNPCKHWD_mm1_mm2m64); -FPU_INSTRUCTION(PUNPCKHDQ_mm1_mm2m64); -FPU_INSTRUCTION(PUNPCKLBW_mm1_mm2m32); -FPU_INSTRUCTION(PUNPCKLWD_mm1_mm2m32); -FPU_INSTRUCTION(PUNPCKLDQ_mm1_mm2m32); - -void SoftCPU::PUSHA(const X86::Instruction&) -{ - auto temp = sp(); - - push16(ax()); - push16(cx()); - push16(dx()); - push16(bx()); - - push16(temp); - - push16(bp()); - push16(si()); - push16(di()); -} - -void SoftCPU::PUSHAD(const X86::Instruction&) -{ - auto temp = esp(); - - push32(eax()); - push32(ecx()); - push32(edx()); - push32(ebx()); - - push32(temp); - - push32(ebp()); - push32(esi()); - push32(edi()); -} - -void SoftCPU::PUSHF(const X86::Instruction&) -{ - // FIXME: Respect shadow flags when they exist! - push16(shadow_wrap_as_initialized(m_eflags & 0xffff)); -} - -void SoftCPU::PUSHFD(const X86::Instruction&) -{ - // FIXME: Respect shadow flags when they exist! - push32(shadow_wrap_as_initialized(m_eflags & 0x00fcffff)); -} - -void SoftCPU::PUSH_CS(X86::Instruction const&) -{ - push16(shadow_wrap_as_initialized(cs())); -} - -void SoftCPU::PUSH_DS(X86::Instruction const&) -{ - push16(shadow_wrap_as_initialized(ds())); -} - -void SoftCPU::PUSH_ES(X86::Instruction const&) -{ - push16(shadow_wrap_as_initialized(es())); -} - -void SoftCPU::PUSH_FS(X86::Instruction const&) -{ - push16(shadow_wrap_as_initialized(fs())); -} - -void SoftCPU::PUSH_GS(X86::Instruction const&) -{ - push16(shadow_wrap_as_initialized(gs())); -} - -void SoftCPU::PUSH_RM16(const X86::Instruction& insn) -{ - push16(insn.modrm().read16(*this, insn)); -} - -void SoftCPU::PUSH_RM32(const X86::Instruction& insn) -{ - push32(insn.modrm().read32(*this, insn)); -} - -void SoftCPU::PUSH_SP_8086_80186(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::PUSH_SS(X86::Instruction const&) -{ - push16(shadow_wrap_as_initialized(ss())); -} - -void SoftCPU::PUSH_imm16(const X86::Instruction& insn) -{ - push16(shadow_wrap_as_initialized(insn.imm16())); -} - -void SoftCPU::PUSH_imm32(const X86::Instruction& insn) -{ - push32(shadow_wrap_as_initialized(insn.imm32())); -} - -void SoftCPU::PUSH_imm8(const X86::Instruction& insn) -{ - VERIFY(!insn.has_operand_size_override_prefix()); - push32(shadow_wrap_as_initialized(sign_extended_to(insn.imm8()))); -} - -void SoftCPU::PUSH_reg16(const X86::Instruction& insn) -{ - push16(gpr16(insn.reg16())); -} - -void SoftCPU::PUSH_reg32(const X86::Instruction& insn) -{ - push32(gpr32(insn.reg32())); -} - -FPU_INSTRUCTION(PXOR_mm1_mm2m64); - -template -ALWAYS_INLINE static T op_rcl_impl(SoftCPU& cpu, T data, ValueWithShadow steps) -{ - if (steps.value() == 0) - return shadow_wrap_with_taint_from(data.value(), data, steps); - - u32 result = 0; - u32 new_flags = 0; - - if constexpr (cf) - asm volatile("stc"); - else - asm volatile("clc"); - - if constexpr (sizeof(typename T::ValueType) == 4) { - asm volatile("rcll %%cl, %%eax\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } else if constexpr (sizeof(typename T::ValueType) == 2) { - asm volatile("rclw %%cl, %%ax\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } else if constexpr (sizeof(typename T::ValueType) == 1) { - asm volatile("rclb %%cl, %%al\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } - - asm volatile( - "pushf\n" - "pop %%ebx" - : "=b"(new_flags)); - - cpu.set_flags_oc(new_flags); - cpu.taint_flags_from(data, steps); - return shadow_wrap_with_taint_from(result, data, steps); -} - -template -ALWAYS_INLINE static T op_rcl(SoftCPU& cpu, T data, ValueWithShadow steps) -{ - cpu.warn_if_flags_tainted("rcl"); - if (cpu.cf()) - return op_rcl_impl(cpu, data, steps); - return op_rcl_impl(cpu, data, steps); -} - -DEFINE_GENERIC_SHIFT_ROTATE_INSN_HANDLERS(RCL, op_rcl) - -template -ALWAYS_INLINE static T op_rcr_impl(SoftCPU& cpu, T data, ValueWithShadow steps) -{ - if (steps.value() == 0) - return shadow_wrap_with_taint_from(data.value(), data, steps); - - u32 result = 0; - u32 new_flags = 0; - - if constexpr (cf) - asm volatile("stc"); - else - asm volatile("clc"); - - if constexpr (sizeof(typename T::ValueType) == 4) { - asm volatile("rcrl %%cl, %%eax\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } else if constexpr (sizeof(typename T::ValueType) == 2) { - asm volatile("rcrw %%cl, %%ax\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } else if constexpr (sizeof(typename T::ValueType) == 1) { - asm volatile("rcrb %%cl, %%al\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } - - asm volatile( - "pushf\n" - "pop %%ebx" - : "=b"(new_flags)); - - cpu.set_flags_oc(new_flags); - return shadow_wrap_with_taint_from(result, data, steps); -} - -template -ALWAYS_INLINE static T op_rcr(SoftCPU& cpu, T data, ValueWithShadow steps) -{ - cpu.warn_if_flags_tainted("rcr"); - if (cpu.cf()) - return op_rcr_impl(cpu, data, steps); - return op_rcr_impl(cpu, data, steps); -} - -DEFINE_GENERIC_SHIFT_ROTATE_INSN_HANDLERS(RCR, op_rcr) - -void SoftCPU::RDTSC(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::RET(const X86::Instruction& insn) -{ - VERIFY(!insn.has_operand_size_override_prefix()); - auto ret_address = pop32(); - warn_if_uninitialized(ret_address, "ret"); - set_eip(ret_address.value()); - - m_emulator.return_callback(ret_address.value()); -} - -void SoftCPU::RETF(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::RETF_imm16(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::RET_imm16(const X86::Instruction& insn) -{ - VERIFY(!insn.has_operand_size_override_prefix()); - auto ret_address = pop32(); - warn_if_uninitialized(ret_address, "ret imm16"); - set_eip(ret_address.value()); - set_esp({ esp().value() + insn.imm16(), esp().shadow() }); - - m_emulator.return_callback(ret_address.value()); -} - -template -ALWAYS_INLINE static T op_rol(SoftCPU& cpu, T data, ValueWithShadow steps) -{ - if (steps.value() == 0) - return shadow_wrap_with_taint_from(data.value(), data, steps); - - u32 result = 0; - u32 new_flags = 0; - - if constexpr (sizeof(typename T::ValueType) == 4) { - asm volatile("roll %%cl, %%eax\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } else if constexpr (sizeof(typename T::ValueType) == 2) { - asm volatile("rolw %%cl, %%ax\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } else if constexpr (sizeof(typename T::ValueType) == 1) { - asm volatile("rolb %%cl, %%al\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } - - asm volatile( - "pushf\n" - "pop %%ebx" - : "=b"(new_flags)); - - cpu.set_flags_oc(new_flags); - return shadow_wrap_with_taint_from(result, data, steps); -} - -DEFINE_GENERIC_SHIFT_ROTATE_INSN_HANDLERS(ROL, op_rol) - -template -ALWAYS_INLINE static T op_ror(SoftCPU& cpu, T data, ValueWithShadow steps) -{ - if (steps.value() == 0) - return shadow_wrap_with_taint_from(data.value(), data, steps); - - u32 result = 0; - u32 new_flags = 0; - - if constexpr (sizeof(typename T::ValueType) == 4) { - asm volatile("rorl %%cl, %%eax\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } else if constexpr (sizeof(typename T::ValueType) == 2) { - asm volatile("rorw %%cl, %%ax\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } else if constexpr (sizeof(typename T::ValueType) == 1) { - asm volatile("rorb %%cl, %%al\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } - - asm volatile( - "pushf\n" - "pop %%ebx" - : "=b"(new_flags)); - - cpu.set_flags_oc(new_flags); - return shadow_wrap_with_taint_from(result, data, steps); -} - -DEFINE_GENERIC_SHIFT_ROTATE_INSN_HANDLERS(ROR, op_ror) - -void SoftCPU::SAHF(const X86::Instruction&) -{ - // FIXME: Respect shadow flags once they exists! - set_al(shadow_wrap_as_initialized(eflags() & 0xff)); -} - -void SoftCPU::SALC(const X86::Instruction&) -{ - // FIXME: Respect shadow flags once they exists! - set_al(shadow_wrap_as_initialized(cf() ? 0xff : 0x00)); -} - -template -static T op_sar(SoftCPU& cpu, T data, ValueWithShadow steps) -{ - if (steps.value() == 0) - return shadow_wrap_with_taint_from(data.value(), data, steps); - - u32 result = 0; - u32 new_flags = 0; - - if constexpr (sizeof(typename T::ValueType) == 4) { - asm volatile("sarl %%cl, %%eax\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } else if constexpr (sizeof(typename T::ValueType) == 2) { - asm volatile("sarw %%cl, %%ax\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } else if constexpr (sizeof(typename T::ValueType) == 1) { - asm volatile("sarb %%cl, %%al\n" - : "=a"(result) - : "a"(data.value()), "c"(steps.value())); - } - - asm volatile( - "pushf\n" - "pop %%ebx" - : "=b"(new_flags)); - - cpu.set_flags_oszapc(new_flags); - return shadow_wrap_with_taint_from(result, data, steps); -} - -DEFINE_GENERIC_SHIFT_ROTATE_INSN_HANDLERS(SAR, op_sar) - -template -ALWAYS_INLINE static void do_scas(SoftCPU& cpu, const X86::Instruction& insn) -{ - cpu.do_once_or_repeat(insn, [&] { - auto src = cpu.const_gpr(X86::RegisterAL); - auto dest = cpu.read_memory({ cpu.es(), cpu.destination_index(insn.address_size()).value() }); - op_sub(cpu, dest, src); - cpu.step_destination_index(insn.address_size(), sizeof(T)); - }); -} - -void SoftCPU::SCASB(const X86::Instruction& insn) -{ - do_scas(*this, insn); -} - -void SoftCPU::SCASD(const X86::Instruction& insn) -{ - do_scas(*this, insn); -} - -void SoftCPU::SCASW(const X86::Instruction& insn) -{ - do_scas(*this, insn); -} - -void SoftCPU::SETcc_RM8(const X86::Instruction& insn) -{ - warn_if_flags_tainted("setcc"); - insn.modrm().write8(*this, insn, shadow_wrap_as_initialized(evaluate_condition(insn.cc()))); -} - -void SoftCPU::SGDT(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::SHLD_RM16_reg16_CL(const X86::Instruction& insn) -{ - insn.modrm().write16(*this, insn, op_shld(*this, insn.modrm().read16(*this, insn), const_gpr16(insn.reg16()), cl())); -} - -void SoftCPU::SHLD_RM16_reg16_imm8(const X86::Instruction& insn) -{ - insn.modrm().write16(*this, insn, op_shld(*this, insn.modrm().read16(*this, insn), const_gpr16(insn.reg16()), shadow_wrap_as_initialized(insn.imm8()))); -} - -void SoftCPU::SHLD_RM32_reg32_CL(const X86::Instruction& insn) -{ - insn.modrm().write32(*this, insn, op_shld(*this, insn.modrm().read32(*this, insn), const_gpr32(insn.reg32()), cl())); -} - -void SoftCPU::SHLD_RM32_reg32_imm8(const X86::Instruction& insn) -{ - insn.modrm().write32(*this, insn, op_shld(*this, insn.modrm().read32(*this, insn), const_gpr32(insn.reg32()), shadow_wrap_as_initialized(insn.imm8()))); -} - -DEFINE_GENERIC_SHIFT_ROTATE_INSN_HANDLERS(SHL, op_shl) - -void SoftCPU::SHRD_RM16_reg16_CL(const X86::Instruction& insn) -{ - insn.modrm().write16(*this, insn, op_shrd(*this, insn.modrm().read16(*this, insn), const_gpr16(insn.reg16()), cl())); -} - -void SoftCPU::SHRD_RM16_reg16_imm8(const X86::Instruction& insn) -{ - insn.modrm().write16(*this, insn, op_shrd(*this, insn.modrm().read16(*this, insn), const_gpr16(insn.reg16()), shadow_wrap_as_initialized(insn.imm8()))); -} - -void SoftCPU::SHRD_RM32_reg32_CL(const X86::Instruction& insn) -{ - insn.modrm().write32(*this, insn, op_shrd(*this, insn.modrm().read32(*this, insn), const_gpr32(insn.reg32()), cl())); -} - -void SoftCPU::SHRD_RM32_reg32_imm8(const X86::Instruction& insn) -{ - insn.modrm().write32(*this, insn, op_shrd(*this, insn.modrm().read32(*this, insn), const_gpr32(insn.reg32()), shadow_wrap_as_initialized(insn.imm8()))); -} - -DEFINE_GENERIC_SHIFT_ROTATE_INSN_HANDLERS(SHR, op_shr) - -void SoftCPU::SIDT(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::SLDT_RM16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::SMSW_RM16(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::STC(const X86::Instruction&) -{ - set_cf(true); -} - -void SoftCPU::STD(const X86::Instruction&) -{ - set_df(true); -} - -void SoftCPU::STI(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::STOSB(const X86::Instruction& insn) -{ - if (insn.has_rep_prefix() && !df()) { - // Fast path for 8-bit forward memory fill. - if (m_emulator.mmu().fast_fill_memory8({ es(), destination_index(insn.address_size()).value() }, ecx().value(), al())) { - switch (insn.address_size()) { - case X86::AddressSize::Size32: - // FIXME: Should an uninitialized ECX taint EDI here? - set_edi({ (u32)(edi().value() + ecx().value()), edi().shadow() }); - set_ecx(shadow_wrap_as_initialized(0)); - break; - case X86::AddressSize::Size16: - // FIXME: Should an uninitialized CX taint DI here? - set_di({ (u16)(di().value() + cx().value()), di().shadow() }); - set_cx(shadow_wrap_as_initialized(0)); - break; - default: - VERIFY_NOT_REACHED(); - } - return; - } - } - - do_once_or_repeat(insn, [&] { - write_memory8({ es(), destination_index(insn.address_size()).value() }, al()); - step_destination_index(insn.address_size(), 1); - }); -} - -void SoftCPU::STOSD(const X86::Instruction& insn) -{ - if (insn.has_rep_prefix() && !df()) { - // Fast path for 32-bit forward memory fill. - if (m_emulator.mmu().fast_fill_memory32({ es(), destination_index(insn.address_size()).value() }, ecx().value(), eax())) { - switch (insn.address_size()) { - case X86::AddressSize::Size32: - // FIXME: Should an uninitialized ECX taint EDI here? - set_edi({ (u32)(edi().value() + (ecx().value() * sizeof(u32))), edi().shadow() }); - set_ecx(shadow_wrap_as_initialized(0)); - break; - case X86::AddressSize::Size16: - // FIXME: Should an uninitialized CX taint DI here? - set_di({ (u16)(di().value() + (cx().value() * sizeof(u32))), di().shadow() }); - set_cx(shadow_wrap_as_initialized(0)); - break; - default: - VERIFY_NOT_REACHED(); - } - return; - } - } - - do_once_or_repeat(insn, [&] { - write_memory32({ es(), destination_index(insn.address_size()).value() }, eax()); - step_destination_index(insn.address_size(), 4); - }); -} - -void SoftCPU::STOSW(const X86::Instruction& insn) -{ - do_once_or_repeat(insn, [&] { - write_memory16({ es(), destination_index(insn.address_size()).value() }, ax()); - step_destination_index(insn.address_size(), 2); - }); -} - -void SoftCPU::STR_RM16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::UD0(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::UD1(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::UD2(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::VERR_RM16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::VERW_RM16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::WAIT(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::WBINVD(const X86::Instruction&) { TODO_INSN(); } - -void SoftCPU::XADD_RM16_reg16(const X86::Instruction& insn) -{ - auto dest = insn.modrm().read16(*this, insn); - auto src = const_gpr16(insn.reg16()); - auto result = op_add(*this, dest, src); - gpr16(insn.reg16()) = dest; - insn.modrm().write16(*this, insn, result); -} - -void SoftCPU::XADD_RM32_reg32(const X86::Instruction& insn) -{ - auto dest = insn.modrm().read32(*this, insn); - auto src = const_gpr32(insn.reg32()); - auto result = op_add(*this, dest, src); - gpr32(insn.reg32()) = dest; - insn.modrm().write32(*this, insn, result); -} - -void SoftCPU::XADD_RM8_reg8(const X86::Instruction& insn) -{ - auto dest = insn.modrm().read8(*this, insn); - auto src = const_gpr8(insn.reg8()); - auto result = op_add(*this, dest, src); - gpr8(insn.reg8()) = dest; - insn.modrm().write8(*this, insn, result); -} - -void SoftCPU::XCHG_AX_reg16(const X86::Instruction& insn) -{ - auto temp = gpr16(insn.reg16()); - gpr16(insn.reg16()) = ax(); - set_ax(temp); -} - -void SoftCPU::XCHG_EAX_reg32(const X86::Instruction& insn) -{ - auto temp = gpr32(insn.reg32()); - gpr32(insn.reg32()) = eax(); - set_eax(temp); -} - -void SoftCPU::XCHG_reg16_RM16(const X86::Instruction& insn) -{ - auto temp = insn.modrm().read16(*this, insn); - insn.modrm().write16(*this, insn, const_gpr16(insn.reg16())); - gpr16(insn.reg16()) = temp; -} - -void SoftCPU::XCHG_reg32_RM32(const X86::Instruction& insn) -{ - auto temp = insn.modrm().read32(*this, insn); - insn.modrm().write32(*this, insn, const_gpr32(insn.reg32())); - gpr32(insn.reg32()) = temp; -} - -void SoftCPU::XCHG_reg8_RM8(const X86::Instruction& insn) -{ - auto temp = insn.modrm().read8(*this, insn); - insn.modrm().write8(*this, insn, const_gpr8(insn.reg8())); - gpr8(insn.reg8()) = temp; -} - -void SoftCPU::XLAT(const X86::Instruction& insn) -{ - u32 offset; - switch (insn.address_size()) { - case X86::AddressSize::Size32: - warn_if_uninitialized(ebx(), "xlat ebx"); - offset = ebx().value() + al().value(); - break; - case X86::AddressSize::Size16: - warn_if_uninitialized(bx(), "xlat bx"); - offset = bx().value() + al().value(); - break; - default: - VERIFY_NOT_REACHED(); - } - warn_if_uninitialized(al(), "xlat al"); - set_al(read_memory8({ segment(insn.segment_prefix().value_or(X86::SegmentRegister::DS)), offset })); -} - -#define DEFINE_GENERIC_INSN_HANDLERS_PARTIAL(mnemonic, op, update_dest, is_zero_idiom_if_both_operands_same, is_or) \ - void SoftCPU::mnemonic##_AL_imm8(const X86::Instruction& insn) \ - { \ - generic_AL_imm8(op>, insn); \ - } \ - void SoftCPU::mnemonic##_AX_imm16(const X86::Instruction& insn) \ - { \ - generic_AX_imm16(op>, insn); \ - } \ - void SoftCPU::mnemonic##_EAX_imm32(const X86::Instruction& insn) \ - { \ - generic_EAX_imm32(op>, insn); \ - } \ - void SoftCPU::mnemonic##_RM16_imm16(const X86::Instruction& insn) \ - { \ - generic_RM16_imm16(op>, insn); \ - } \ - void SoftCPU::mnemonic##_RM16_reg16(const X86::Instruction& insn) \ - { \ - generic_RM16_reg16(op>, insn); \ - } \ - void SoftCPU::mnemonic##_RM32_imm32(const X86::Instruction& insn) \ - { \ - generic_RM32_imm32(op>, insn); \ - } \ - void SoftCPU::mnemonic##_RM32_reg32(const X86::Instruction& insn) \ - { \ - generic_RM32_reg32(op>, insn); \ - } \ - void SoftCPU::mnemonic##_RM8_imm8(const X86::Instruction& insn) \ - { \ - generic_RM8_imm8(op>, insn); \ - } \ - void SoftCPU::mnemonic##_RM8_reg8(const X86::Instruction& insn) \ - { \ - generic_RM8_reg8(op>, insn); \ - } - -#define DEFINE_GENERIC_INSN_HANDLERS(mnemonic, op, update_dest, is_zero_idiom_if_both_operands_same, is_or) \ - DEFINE_GENERIC_INSN_HANDLERS_PARTIAL(mnemonic, op, update_dest, is_zero_idiom_if_both_operands_same, is_or) \ - void SoftCPU::mnemonic##_RM16_imm8(const X86::Instruction& insn) \ - { \ - generic_RM16_imm8(op>, insn); \ - } \ - void SoftCPU::mnemonic##_RM32_imm8(const X86::Instruction& insn) \ - { \ - generic_RM32_imm8(op>, insn); \ - } \ - void SoftCPU::mnemonic##_reg16_RM16(const X86::Instruction& insn) \ - { \ - generic_reg16_RM16(op>, insn); \ - } \ - void SoftCPU::mnemonic##_reg32_RM32(const X86::Instruction& insn) \ - { \ - generic_reg32_RM32(op>, insn); \ - } \ - void SoftCPU::mnemonic##_reg8_RM8(const X86::Instruction& insn) \ - { \ - generic_reg8_RM8(op>, insn); \ - } - -DEFINE_GENERIC_INSN_HANDLERS(XOR, op_xor, true, true, false) -DEFINE_GENERIC_INSN_HANDLERS(OR, op_or, true, false, true) -DEFINE_GENERIC_INSN_HANDLERS(ADD, op_add, true, false, false) -DEFINE_GENERIC_INSN_HANDLERS(ADC, op_adc, true, false, false) -DEFINE_GENERIC_INSN_HANDLERS(SUB, op_sub, true, true, false) -DEFINE_GENERIC_INSN_HANDLERS(SBB, op_sbb, true, false, false) -DEFINE_GENERIC_INSN_HANDLERS(AND, op_and, true, false, false) -DEFINE_GENERIC_INSN_HANDLERS(CMP, op_sub, false, false, false) -DEFINE_GENERIC_INSN_HANDLERS_PARTIAL(TEST, op_and, false, false, false) - -FPU_INSTRUCTION(MOVQ_mm1_mm2m64); -FPU_INSTRUCTION(MOVQ_mm1m64_mm2); -FPU_INSTRUCTION(MOVD_mm1_rm32); -FPU_INSTRUCTION(MOVQ_mm1_rm64); // long mode -FPU_INSTRUCTION(MOVD_rm32_mm2); -FPU_INSTRUCTION(MOVQ_rm64_mm2); // long mode -FPU_INSTRUCTION(EMMS); - -void SoftCPU::CMPXCHG8B_m64(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::RDRAND_reg(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::RDSEED_reg(X86::Instruction const&) { TODO_INSN(); } - -VPU_INSTRUCTION(PREFETCHTNTA); -VPU_INSTRUCTION(PREFETCHT0); -VPU_INSTRUCTION(PREFETCHT1); -VPU_INSTRUCTION(PREFETCHT2); -VPU_INSTRUCTION(LDMXCSR); -VPU_INSTRUCTION(STMXCSR); -VPU_INSTRUCTION(MOVUPS_xmm1_xmm2m128); -VPU_INSTRUCTION(MOVSS_xmm1_xmm2m32); -VPU_INSTRUCTION(MOVUPS_xmm1m128_xmm2); -VPU_INSTRUCTION(MOVSS_xmm1m32_xmm2); -VPU_INSTRUCTION(MOVLPS_xmm1_xmm2m64); -VPU_INSTRUCTION(MOVLPS_m64_xmm2); -VPU_INSTRUCTION(UNPCKLPS_xmm1_xmm2m128); -VPU_INSTRUCTION(UNPCKHPS_xmm1_xmm2m128); -VPU_INSTRUCTION(MOVHPS_xmm1_xmm2m64); -VPU_INSTRUCTION(MOVHPS_m64_xmm2); -VPU_INSTRUCTION(MOVAPS_xmm1_xmm2m128); -VPU_INSTRUCTION(MOVAPS_xmm1m128_xmm2); -VPU_INSTRUCTION(CVTTPS2PI_mm1_xmm2m64); -VPU_INSTRUCTION(CVTTSS2SI_r32_xmm2m32); -VPU_INSTRUCTION(CVTPI2PS_xmm1_mm2m64); -VPU_INSTRUCTION(CVTSI2SS_xmm1_rm32); -VPU_INSTRUCTION(MOVNTPS_xmm1m128_xmm2); -VPU_INSTRUCTION(CVTPS2PI_xmm1_mm2m64); -VPU_INSTRUCTION(CVTSS2SI_r32_xmm2m32); -VPU_INSTRUCTION(UCOMISS_xmm1_xmm2m32); -VPU_INSTRUCTION(COMISS_xmm1_xmm2m32); -VPU_INSTRUCTION(MOVMSKPS_reg_xmm); -VPU_INSTRUCTION(SQRTPS_xmm1_xmm2m128); -VPU_INSTRUCTION(SQRTSS_xmm1_xmm2m32); -VPU_INSTRUCTION(RSQRTPS_xmm1_xmm2m128); -VPU_INSTRUCTION(RSQRTSS_xmm1_xmm2m32); -VPU_INSTRUCTION(RCPPS_xmm1_xmm2m128); -VPU_INSTRUCTION(RCPSS_xmm1_xmm2m32); -VPU_INSTRUCTION(ANDPS_xmm1_xmm2m128); -VPU_INSTRUCTION(ANDNPS_xmm1_xmm2m128); -VPU_INSTRUCTION(ORPS_xmm1_xmm2m128); -VPU_INSTRUCTION(XORPS_xmm1_xmm2m128); -VPU_INSTRUCTION(ADDPS_xmm1_xmm2m128); -VPU_INSTRUCTION(ADDSS_xmm1_xmm2m32); -VPU_INSTRUCTION(MULPS_xmm1_xmm2m128); -VPU_INSTRUCTION(MULSS_xmm1_xmm2m32); -VPU_INSTRUCTION(SUBPS_xmm1_xmm2m128); -VPU_INSTRUCTION(SUBSS_xmm1_xmm2m32); -VPU_INSTRUCTION(MINPS_xmm1_xmm2m128); -VPU_INSTRUCTION(MINSS_xmm1_xmm2m32); -VPU_INSTRUCTION(DIVPS_xmm1_xmm2m128); -VPU_INSTRUCTION(DIVSS_xmm1_xmm2m32); -VPU_INSTRUCTION(MAXPS_xmm1_xmm2m128); -VPU_INSTRUCTION(MAXSS_xmm1_xmm2m32); -VPU_INSTRUCTION(PSHUFW_mm1_mm2m64_imm8); -VPU_INSTRUCTION(CMPPS_xmm1_xmm2m128_imm8); -VPU_INSTRUCTION(CMPSS_xmm1_xmm2m32_imm8); -VPU_INSTRUCTION(PINSRW_mm1_r32m16_imm8); -VPU_INSTRUCTION(PINSRW_xmm1_r32m16_imm8); -VPU_INSTRUCTION(PEXTRW_reg_mm1_imm8); -VPU_INSTRUCTION(PEXTRW_reg_xmm1_imm8); -VPU_INSTRUCTION(SHUFPS_xmm1_xmm2m128_imm8); -VPU_INSTRUCTION(PMOVMSKB_reg_mm1); -VPU_INSTRUCTION(PMOVMSKB_reg_xmm1); -VPU_INSTRUCTION(PMINUB_mm1_mm2m64); -VPU_INSTRUCTION(PMINUB_xmm1_xmm2m128); -VPU_INSTRUCTION(PMAXUB_mm1_mm2m64); -VPU_INSTRUCTION(PMAXUB_xmm1_xmm2m128); -VPU_INSTRUCTION(PAVGB_mm1_mm2m64); -VPU_INSTRUCTION(PAVGB_xmm1_xmm2m128); -VPU_INSTRUCTION(PAVGW_mm1_mm2m64); -VPU_INSTRUCTION(PAVGW_xmm1_xmm2m128); -VPU_INSTRUCTION(PMULHUW_mm1_mm2m64); -VPU_INSTRUCTION(PMULHUW_xmm1_xmm2m64); -VPU_INSTRUCTION(MOVNTQ_m64_mm1); -VPU_INSTRUCTION(PMINSB_mm1_mm2m64); -VPU_INSTRUCTION(PMINSB_xmm1_xmm2m128); -VPU_INSTRUCTION(PMAXSB_mm1_mm2m64); -VPU_INSTRUCTION(PMAXSB_xmm1_xmm2m128); -VPU_INSTRUCTION(PSADBB_mm1_mm2m64); -VPU_INSTRUCTION(PSADBB_xmm1_xmm2m128); -VPU_INSTRUCTION(MASKMOVQ_mm1_mm2m64); - -void SoftCPU::MOVUPD_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MOVSD_xmm1_xmm2m32(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MOVUPD_xmm1m128_xmm2(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MOVSD_xmm1m32_xmm2(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MOVLPD_xmm1_m64(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MOVLPD_m64_xmm2(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::UNPCKLPD_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::UNPCKHPD_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MOVHPD_xmm1_xmm2m64(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MOVAPD_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MOVAPD_xmm1m128_xmm2(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::CVTPI2PD_xmm1_mm2m64(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::CVTSI2SD_xmm1_rm32(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::CVTTPD2PI_mm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::CVTTSS2SI_r32_xmm2m64(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::CVTPD2PI_xmm1_mm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::CVTSD2SI_xmm1_rm64(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::UCOMISD_xmm1_xmm2m64(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::COMISD_xmm1_xmm2m64(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MOVMSKPD_reg_xmm(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::SQRTPD_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::SQRTSD_xmm1_xmm2m32(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::ANDPD_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::ANDNPD_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::ORPD_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::XORPD_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::ADDPD_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::ADDSD_xmm1_xmm2m32(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MULPD_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MULSD_xmm1_xmm2m32(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::CVTPS2PD_xmm1_xmm2m64(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::CVTPD2PS_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::CVTSS2SD_xmm1_xmm2m32(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::CVTSD2SS_xmm1_xmm2m64(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::CVTDQ2PS_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::CVTPS2DQ_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::CVTTPS2DQ_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::SUBPD_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::SUBSD_xmm1_xmm2m32(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MINPD_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MINSD_xmm1_xmm2m32(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::DIVPD_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::DIVSD_xmm1_xmm2m32(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MAXPD_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MAXSD_xmm1_xmm2m32(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::PUNPCKLQDQ_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::PUNPCKHQDQ_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MOVDQA_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MOVDQU_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::PSHUFD_xmm1_xmm2m128_imm8(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::PSHUFHW_xmm1_xmm2m128_imm8(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::PSHUFLW_xmm1_xmm2m128_imm8(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::PSRLQ_xmm1_imm8(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::PSRLDQ_xmm1_imm8(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::PSLLQ_xmm1_imm8(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::PSLLDQ_xmm1_imm8(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MOVD_rm32_xmm2(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MOVQ_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MOVDQA_xmm1m128_xmm2(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MOVDQU_xmm1m128_xmm2(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::CMPPD_xmm1_xmm2m128_imm8(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::CMPSD_xmm1_xmm2m32_imm8(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::SHUFPD_xmm1_xmm2m128_imm8(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::PADDQ_mm1_mm2m64(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MOVQ_xmm1m128_xmm2(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MOVQ2DQ_xmm_mm(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::MOVDQ2Q_mm_xmm(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::CVTTPD2DQ_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::CVTPD2DQ_xmm1_xmm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::CVTDQ2PD_xmm1_xmm2m64(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::PMULUDQ_mm1_mm2m64(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::PMULUDQ_mm1_mm2m128(X86::Instruction const&) { TODO_INSN(); } -void SoftCPU::PSUBQ_mm1_mm2m64(X86::Instruction const&) { TODO_INSN(); } - -void SoftCPU::wrap_0xC0(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::wrap_0xC1_16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::wrap_0xC1_32(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::wrap_0xD0(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::wrap_0xD1_16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::wrap_0xD1_32(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::wrap_0xD2(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::wrap_0xD3_16(const X86::Instruction&) { TODO_INSN(); } -void SoftCPU::wrap_0xD3_32(const X86::Instruction&) { TODO_INSN(); } - -} diff --git a/Userland/DevTools/UserspaceEmulator/SoftCPU.h b/Userland/DevTools/UserspaceEmulator/SoftCPU.h deleted file mode 100644 index 3fbfde7681..0000000000 --- a/Userland/DevTools/UserspaceEmulator/SoftCPU.h +++ /dev/null @@ -1,1416 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Emulator.h" -#include "Region.h" -#include "SoftFPU.h" -#include "SoftVPU.h" -#include "ValueWithShadow.h" -#include -#include -#include -#include - -namespace UserspaceEmulator { - -class Emulator; -class Region; - -union PartAddressableRegister { - struct { - u32 full_u32; - }; - struct { - u16 low_u16; - u16 high_u16; - }; - struct { - u8 low_u8; - u8 high_u8; - u16 also_high_u16; - }; -}; - -class SoftCPU final - : public X86::Interpreter - , public X86::InstructionStream { - friend SoftFPU; - -public: - using ValueWithShadowType8 = ValueWithShadow; - using ValueWithShadowType16 = ValueWithShadow; - using ValueWithShadowType32 = ValueWithShadow; - using ValueWithShadowType64 = ValueWithShadow; - using ValueWithShadowType128 = ValueWithShadow; - using ValueWithShadowType256 = ValueWithShadow; - - explicit SoftCPU(Emulator&); - void dump() const; - - u32 base_eip() const { return m_base_eip; } - void save_base_eip() { m_base_eip = m_eip; } - - u32 eip() const { return m_eip; } - void set_eip(u32 eip) - { - m_eip = eip; - } - - struct Flags { - enum Flag { - CF = 0x0001, // 0b0000'0000'0000'0001 - PF = 0x0004, // 0b0000'0000'0000'0100 - AF = 0x0010, // 0b0000'0000'0001'0000 - ZF = 0x0040, // 0b0000'0000'0100'0000 - SF = 0x0080, // 0b0000'0000'1000'0000 - TF = 0x0100, // 0b0000'0001'0000'0000 - IF = 0x0200, // 0b0000'0010'0000'0000 - DF = 0x0400, // 0b0000'0100'0000'0000 - OF = 0x0800, // 0b0000'1000'0000'0000 - }; - }; - - void push32(ValueWithShadow); - ValueWithShadow pop32(); - - void push16(ValueWithShadow); - ValueWithShadow pop16(); - - void push_string(StringView); - void push_buffer(u8 const* data, size_t); - - u16 segment(X86::SegmentRegister seg) const { return m_segment[(int)seg]; } - u16& segment(X86::SegmentRegister seg) { return m_segment[(int)seg]; } - - ValueAndShadowReference gpr8(X86::RegisterIndex8 reg) - { - switch (reg) { - case X86::RegisterAL: - return m_gpr[X86::RegisterEAX].reference_to<&PartAddressableRegister::low_u8>(); - case X86::RegisterAH: - return m_gpr[X86::RegisterEAX].reference_to<&PartAddressableRegister::high_u8>(); - case X86::RegisterBL: - return m_gpr[X86::RegisterEBX].reference_to<&PartAddressableRegister::low_u8>(); - case X86::RegisterBH: - return m_gpr[X86::RegisterEBX].reference_to<&PartAddressableRegister::high_u8>(); - case X86::RegisterCL: - return m_gpr[X86::RegisterECX].reference_to<&PartAddressableRegister::low_u8>(); - case X86::RegisterCH: - return m_gpr[X86::RegisterECX].reference_to<&PartAddressableRegister::high_u8>(); - case X86::RegisterDL: - return m_gpr[X86::RegisterEDX].reference_to<&PartAddressableRegister::low_u8>(); - case X86::RegisterDH: - return m_gpr[X86::RegisterEDX].reference_to<&PartAddressableRegister::high_u8>(); - default: - VERIFY_NOT_REACHED(); - } - } - - ValueWithShadow const_gpr8(X86::RegisterIndex8 reg) const - { - switch (reg) { - case X86::RegisterAL: - return m_gpr[X86::RegisterEAX].slice<&PartAddressableRegister::low_u8>(); - case X86::RegisterAH: - return m_gpr[X86::RegisterEAX].slice<&PartAddressableRegister::high_u8>(); - case X86::RegisterBL: - return m_gpr[X86::RegisterEBX].slice<&PartAddressableRegister::low_u8>(); - case X86::RegisterBH: - return m_gpr[X86::RegisterEBX].slice<&PartAddressableRegister::high_u8>(); - case X86::RegisterCL: - return m_gpr[X86::RegisterECX].slice<&PartAddressableRegister::low_u8>(); - case X86::RegisterCH: - return m_gpr[X86::RegisterECX].slice<&PartAddressableRegister::high_u8>(); - case X86::RegisterDL: - return m_gpr[X86::RegisterEDX].slice<&PartAddressableRegister::low_u8>(); - case X86::RegisterDH: - return m_gpr[X86::RegisterEDX].slice<&PartAddressableRegister::high_u8>(); - default: - VERIFY_NOT_REACHED(); - } - } - - ValueWithShadow const_gpr16(X86::RegisterIndex16 reg) const - { - return m_gpr[reg].slice<&PartAddressableRegister::low_u16>(); - } - - ValueAndShadowReference gpr16(X86::RegisterIndex16 reg) - { - return m_gpr[reg].reference_to<&PartAddressableRegister::low_u16>(); - } - - ValueWithShadow const_gpr32(X86::RegisterIndex32 reg) const - { - return m_gpr[reg].slice<&PartAddressableRegister::full_u32>(); - } - - ValueAndShadowReference gpr32(X86::RegisterIndex32 reg) - { - return m_gpr[reg].reference_to<&PartAddressableRegister::full_u32>(); - } - - template - ValueWithShadow const_gpr(unsigned register_index) const - { - if constexpr (sizeof(T) == 1) - return const_gpr8((X86::RegisterIndex8)register_index); - if constexpr (sizeof(T) == 2) - return const_gpr16((X86::RegisterIndex16)register_index); - if constexpr (sizeof(T) == 4) - return const_gpr32((X86::RegisterIndex32)register_index); - } - - template - ValueAndShadowReference gpr(unsigned register_index) - { - if constexpr (sizeof(T) == 1) - return gpr8((X86::RegisterIndex8)register_index); - if constexpr (sizeof(T) == 2) - return gpr16((X86::RegisterIndex16)register_index); - if constexpr (sizeof(T) == 4) - return gpr32((X86::RegisterIndex32)register_index); - } - - ValueWithShadow source_index(X86::AddressSize address_size) const - { - if (address_size == X86::AddressSize::Size32) - return esi(); - if (address_size == X86::AddressSize::Size16) - return { si().value(), (u32)si().shadow_as_value() & 0xffff }; - VERIFY_NOT_REACHED(); - } - - ValueWithShadow destination_index(X86::AddressSize address_size) const - { - if (address_size == X86::AddressSize::Size32) - return edi(); - if (address_size == X86::AddressSize::Size16) - return { di().value(), (u32)di().shadow_as_value() & 0xffff }; - VERIFY_NOT_REACHED(); - } - - ValueWithShadow loop_index(X86::AddressSize address_size) const - { - if (address_size == X86::AddressSize::Size32) - return ecx(); - if (address_size == X86::AddressSize::Size16) - return { cx().value(), (u32)cx().shadow_as_value() & 0xffff }; - VERIFY_NOT_REACHED(); - } - - bool decrement_loop_index(X86::AddressSize address_size) - { - switch (address_size) { - case X86::AddressSize::Size32: - set_ecx({ ecx().value() - 1, ecx().shadow() }); - return ecx().value() == 0; - case X86::AddressSize::Size16: - set_cx(ValueWithShadow(cx().value() - 1, cx().shadow())); - return cx().value() == 0; - default: - VERIFY_NOT_REACHED(); - } - } - - ALWAYS_INLINE void step_source_index(X86::AddressSize address_size, u32 step) - { - switch (address_size) { - case X86::AddressSize::Size32: - if (df()) - set_esi({ esi().value() - step, esi().shadow() }); - else - set_esi({ esi().value() + step, esi().shadow() }); - break; - case X86::AddressSize::Size16: - if (df()) - set_si(ValueWithShadow(si().value() - step, si().shadow())); - else - set_si(ValueWithShadow(si().value() + step, si().shadow())); - break; - default: - VERIFY_NOT_REACHED(); - } - } - - ALWAYS_INLINE void step_destination_index(X86::AddressSize address_size, u32 step) - { - switch (address_size) { - case X86::AddressSize::Size32: - if (df()) - set_edi({ edi().value() - step, edi().shadow() }); - else - set_edi({ edi().value() + step, edi().shadow() }); - break; - case X86::AddressSize::Size16: - if (df()) - set_di(ValueWithShadow(di().value() - step, di().shadow())); - else - set_di(ValueWithShadow(di().value() + step, di().shadow())); - break; - default: - VERIFY_NOT_REACHED(); - } - } - - u32 eflags() const { return m_eflags; } - void set_eflags(ValueWithShadow eflags) - { - m_eflags = eflags.value(); - m_flags_tainted = eflags.is_uninitialized(); - } - - ValueWithShadow eax() const { return const_gpr32(X86::RegisterEAX); } - ValueWithShadow ebx() const { return const_gpr32(X86::RegisterEBX); } - ValueWithShadow ecx() const { return const_gpr32(X86::RegisterECX); } - ValueWithShadow edx() const { return const_gpr32(X86::RegisterEDX); } - ValueWithShadow esp() const { return const_gpr32(X86::RegisterESP); } - ValueWithShadow ebp() const { return const_gpr32(X86::RegisterEBP); } - ValueWithShadow esi() const { return const_gpr32(X86::RegisterESI); } - ValueWithShadow edi() const { return const_gpr32(X86::RegisterEDI); } - - ValueWithShadow ax() const { return const_gpr16(X86::RegisterAX); } - ValueWithShadow bx() const { return const_gpr16(X86::RegisterBX); } - ValueWithShadow cx() const { return const_gpr16(X86::RegisterCX); } - ValueWithShadow dx() const { return const_gpr16(X86::RegisterDX); } - ValueWithShadow sp() const { return const_gpr16(X86::RegisterSP); } - ValueWithShadow bp() const { return const_gpr16(X86::RegisterBP); } - ValueWithShadow si() const { return const_gpr16(X86::RegisterSI); } - ValueWithShadow di() const { return const_gpr16(X86::RegisterDI); } - - ValueWithShadow al() const { return const_gpr8(X86::RegisterAL); } - ValueWithShadow ah() const { return const_gpr8(X86::RegisterAH); } - ValueWithShadow bl() const { return const_gpr8(X86::RegisterBL); } - ValueWithShadow bh() const { return const_gpr8(X86::RegisterBH); } - ValueWithShadow cl() const { return const_gpr8(X86::RegisterCL); } - ValueWithShadow ch() const { return const_gpr8(X86::RegisterCH); } - ValueWithShadow dl() const { return const_gpr8(X86::RegisterDL); } - ValueWithShadow dh() const { return const_gpr8(X86::RegisterDH); } - - long double fpu_get(u8 index) { return m_fpu.fpu_get(index); } - long double fpu_pop() { return m_fpu.fpu_pop(); } - MMX mmx_get(u8 index) const { return m_fpu.mmx_get(index); } - - void set_eax(ValueWithShadow value) { gpr32(X86::RegisterEAX) = value; } - void set_ebx(ValueWithShadow value) { gpr32(X86::RegisterEBX) = value; } - void set_ecx(ValueWithShadow value) { gpr32(X86::RegisterECX) = value; } - void set_edx(ValueWithShadow value) { gpr32(X86::RegisterEDX) = value; } - void set_esp(ValueWithShadow value) { gpr32(X86::RegisterESP) = value; } - void set_ebp(ValueWithShadow value) { gpr32(X86::RegisterEBP) = value; } - void set_esi(ValueWithShadow value) { gpr32(X86::RegisterESI) = value; } - void set_edi(ValueWithShadow value) { gpr32(X86::RegisterEDI) = value; } - - void set_ax(ValueWithShadow value) { gpr16(X86::RegisterAX) = value; } - void set_bx(ValueWithShadow value) { gpr16(X86::RegisterBX) = value; } - void set_cx(ValueWithShadow value) { gpr16(X86::RegisterCX) = value; } - void set_dx(ValueWithShadow value) { gpr16(X86::RegisterDX) = value; } - void set_sp(ValueWithShadow value) { gpr16(X86::RegisterSP) = value; } - void set_bp(ValueWithShadow value) { gpr16(X86::RegisterBP) = value; } - void set_si(ValueWithShadow value) { gpr16(X86::RegisterSI) = value; } - void set_di(ValueWithShadow value) { gpr16(X86::RegisterDI) = value; } - - void set_al(ValueWithShadow value) { gpr8(X86::RegisterAL) = value; } - void set_ah(ValueWithShadow value) { gpr8(X86::RegisterAH) = value; } - void set_bl(ValueWithShadow value) { gpr8(X86::RegisterBL) = value; } - void set_bh(ValueWithShadow value) { gpr8(X86::RegisterBH) = value; } - void set_cl(ValueWithShadow value) { gpr8(X86::RegisterCL) = value; } - void set_ch(ValueWithShadow value) { gpr8(X86::RegisterCH) = value; } - void set_dl(ValueWithShadow value) { gpr8(X86::RegisterDL) = value; } - void set_dh(ValueWithShadow value) { gpr8(X86::RegisterDH) = value; } - - void fpu_push(long double value) { m_fpu.fpu_push(value); } - void fpu_set(u8 index, long double value) { m_fpu.fpu_set(index, value); } - void mmx_set(u8 index, MMX value) { m_fpu.mmx_set(index, value); } - - bool of() const { return m_eflags & Flags::OF; } - bool sf() const { return m_eflags & Flags::SF; } - bool zf() const { return m_eflags & Flags::ZF; } - bool af() const { return m_eflags & Flags::AF; } - bool pf() const { return m_eflags & Flags::PF; } - bool cf() const { return m_eflags & Flags::CF; } - bool df() const { return m_eflags & Flags::DF; } - - void set_flag(Flags::Flag flag, bool value) - { - if (value) - m_eflags |= flag; - else - m_eflags &= ~flag; - } - - void set_of(bool value) { set_flag(Flags::OF, value); } - void set_sf(bool value) { set_flag(Flags::SF, value); } - void set_zf(bool value) { set_flag(Flags::ZF, value); } - void set_af(bool value) { set_flag(Flags::AF, value); } - void set_pf(bool value) { set_flag(Flags::PF, value); } - void set_cf(bool value) { set_flag(Flags::CF, value); } - void set_df(bool value) { set_flag(Flags::DF, value); } - - void set_flags_with_mask(u32 new_flags, u32 mask) - { - m_eflags &= ~mask; - m_eflags |= new_flags & mask; - } - - void set_flags_oszapc(u32 new_flags) - { - set_flags_with_mask(new_flags, Flags::OF | Flags::SF | Flags::ZF | Flags::AF | Flags::PF | Flags::CF); - } - - void set_flags_oszap(u32 new_flags) - { - set_flags_with_mask(new_flags, Flags::OF | Flags::SF | Flags::ZF | Flags::AF | Flags::PF); - } - - void set_flags_oszpc(u32 new_flags) - { - set_flags_with_mask(new_flags, Flags::OF | Flags::SF | Flags::ZF | Flags::PF | Flags::CF); - } - - void set_flags_oc(u32 new_flags) - { - set_flags_with_mask(new_flags, Flags::OF | Flags::CF); - } - - u16 cs() const { return m_segment[(int)X86::SegmentRegister::CS]; } - u16 ds() const { return m_segment[(int)X86::SegmentRegister::DS]; } - u16 es() const { return m_segment[(int)X86::SegmentRegister::ES]; } - u16 ss() const { return m_segment[(int)X86::SegmentRegister::SS]; } - u16 fs() const { return m_segment[(int)X86::SegmentRegister::FS]; } - u16 gs() const { return m_segment[(int)X86::SegmentRegister::GS]; } - - ValueWithShadow read_memory8(X86::LogicalAddress); - ValueWithShadow read_memory16(X86::LogicalAddress); - ValueWithShadow read_memory32(X86::LogicalAddress); - ValueWithShadow read_memory64(X86::LogicalAddress); - ValueWithShadow read_memory128(X86::LogicalAddress); - ValueWithShadow read_memory256(X86::LogicalAddress); - - template - ValueWithShadow read_memory(X86::LogicalAddress address) - { - auto value = m_emulator.mmu().read(address); - if constexpr (AK::HasFormatter) - outln_if(MEMORY_DEBUG, "\033[36;1mread_memory: @{:#04x}:{:p} -> {:#064x} ({:hex-dump})\033[0m", address.selector(), address.offset(), value.value(), value.shadow().span()); - else - outln_if(MEMORY_DEBUG, "\033[36;1mread_memory: @{:#04x}:{:p} -> ??? ({:hex-dump})\033[0m", address.selector(), address.offset(), value.shadow().span()); - return value; - } - - void write_memory8(X86::LogicalAddress, ValueWithShadow); - void write_memory16(X86::LogicalAddress, ValueWithShadow); - void write_memory32(X86::LogicalAddress, ValueWithShadow); - void write_memory64(X86::LogicalAddress, ValueWithShadow); - void write_memory128(X86::LogicalAddress, ValueWithShadow); - void write_memory256(X86::LogicalAddress, ValueWithShadow); - - template - void write_memory(X86::LogicalAddress address, ValueWithShadow data) - { - if constexpr (sizeof(T) == 1) - return write_memory8(address, data); - if constexpr (sizeof(T) == 2) - return write_memory16(address, data); - if constexpr (sizeof(T) == 4) - return write_memory32(address, data); - if constexpr (sizeof(T) == 8) - return write_memory64(address, data); - if constexpr (sizeof(T) == 16) - return write_memory128(address, data); - if constexpr (sizeof(T) == 32) - return write_memory256(address, data); - } - - bool evaluate_condition(u8 condition) const - { - switch (condition) { - case 0: - return of(); // O - case 1: - return !of(); // NO - case 2: - return cf(); // B, C, NAE - case 3: - return !cf(); // NB, NC, AE - case 4: - return zf(); // E, Z - case 5: - return !zf(); // NE, NZ - case 6: - return cf() || zf(); // BE, NA - case 7: - return !(cf() || zf()); // NBE, A - case 8: - return sf(); // S - case 9: - return !sf(); // NS - case 10: - return pf(); // P, PE - case 11: - return !pf(); // NP, PO - case 12: - return sf() != of(); // L, NGE - case 13: - return sf() == of(); // NL, GE - case 14: - return (sf() != of()) || zf(); // LE, NG - case 15: - return !((sf() != of()) || zf()); // NLE, G - default: - VERIFY_NOT_REACHED(); - } - return 0; - } - - template - void do_once_or_repeat(const X86::Instruction& insn, Callback); - - template - void taint_flags_from(A const& a) - { - m_flags_tainted = a.is_uninitialized(); - } - - template - void taint_flags_from(A const& a, B const& b) - { - m_flags_tainted = a.is_uninitialized() || b.is_uninitialized(); - } - - template - void taint_flags_from(A const& a, B const& b, C const& c) - { - m_flags_tainted = a.is_uninitialized() || b.is_uninitialized() || c.is_uninitialized(); - } - - void warn_if_flags_tainted(char const* message) const; - - // ^X86::InstructionStream - virtual bool can_read() override { return false; } - virtual u8 read8() override; - virtual u16 read16() override; - virtual u32 read32() override; - virtual u64 read64() override; - -private: - // ^X86::Interpreter - virtual void AAA(const X86::Instruction&) override; - virtual void AAD(const X86::Instruction&) override; - virtual void AAM(const X86::Instruction&) override; - virtual void AAS(const X86::Instruction&) override; - virtual void ADC_AL_imm8(const X86::Instruction&) override; - virtual void ADC_AX_imm16(const X86::Instruction&) override; - virtual void ADC_EAX_imm32(const X86::Instruction&) override; - virtual void ADC_RM16_imm16(const X86::Instruction&) override; - virtual void ADC_RM16_imm8(const X86::Instruction&) override; - virtual void ADC_RM16_reg16(const X86::Instruction&) override; - virtual void ADC_RM32_imm32(const X86::Instruction&) override; - virtual void ADC_RM32_imm8(const X86::Instruction&) override; - virtual void ADC_RM32_reg32(const X86::Instruction&) override; - virtual void ADC_RM8_imm8(const X86::Instruction&) override; - virtual void ADC_RM8_reg8(const X86::Instruction&) override; - virtual void ADC_reg16_RM16(const X86::Instruction&) override; - virtual void ADC_reg32_RM32(const X86::Instruction&) override; - virtual void ADC_reg8_RM8(const X86::Instruction&) override; - virtual void ADD_AL_imm8(const X86::Instruction&) override; - virtual void ADD_AX_imm16(const X86::Instruction&) override; - virtual void ADD_EAX_imm32(const X86::Instruction&) override; - virtual void ADD_RM16_imm16(const X86::Instruction&) override; - virtual void ADD_RM16_imm8(const X86::Instruction&) override; - virtual void ADD_RM16_reg16(const X86::Instruction&) override; - virtual void ADD_RM32_imm32(const X86::Instruction&) override; - virtual void ADD_RM32_imm8(const X86::Instruction&) override; - virtual void ADD_RM32_reg32(const X86::Instruction&) override; - virtual void ADD_RM8_imm8(const X86::Instruction&) override; - virtual void ADD_RM8_reg8(const X86::Instruction&) override; - virtual void ADD_reg16_RM16(const X86::Instruction&) override; - virtual void ADD_reg32_RM32(const X86::Instruction&) override; - virtual void ADD_reg8_RM8(const X86::Instruction&) override; - virtual void AND_AL_imm8(const X86::Instruction&) override; - virtual void AND_AX_imm16(const X86::Instruction&) override; - virtual void AND_EAX_imm32(const X86::Instruction&) override; - virtual void AND_RM16_imm16(const X86::Instruction&) override; - virtual void AND_RM16_imm8(const X86::Instruction&) override; - virtual void AND_RM16_reg16(const X86::Instruction&) override; - virtual void AND_RM32_imm32(const X86::Instruction&) override; - virtual void AND_RM32_imm8(const X86::Instruction&) override; - virtual void AND_RM32_reg32(const X86::Instruction&) override; - virtual void AND_RM8_imm8(const X86::Instruction&) override; - virtual void AND_RM8_reg8(const X86::Instruction&) override; - virtual void AND_reg16_RM16(const X86::Instruction&) override; - virtual void AND_reg32_RM32(const X86::Instruction&) override; - virtual void AND_reg8_RM8(const X86::Instruction&) override; - virtual void ARPL(const X86::Instruction&) override; - virtual void BOUND(const X86::Instruction&) override; - virtual void BSF_reg16_RM16(const X86::Instruction&) override; - virtual void BSF_reg32_RM32(const X86::Instruction&) override; - virtual void BSR_reg16_RM16(const X86::Instruction&) override; - virtual void BSR_reg32_RM32(const X86::Instruction&) override; - virtual void BSWAP_reg32(const X86::Instruction&) override; - virtual void BTC_RM16_imm8(const X86::Instruction&) override; - virtual void BTC_RM16_reg16(const X86::Instruction&) override; - virtual void BTC_RM32_imm8(const X86::Instruction&) override; - virtual void BTC_RM32_reg32(const X86::Instruction&) override; - virtual void BTR_RM16_imm8(const X86::Instruction&) override; - virtual void BTR_RM16_reg16(const X86::Instruction&) override; - virtual void BTR_RM32_imm8(const X86::Instruction&) override; - virtual void BTR_RM32_reg32(const X86::Instruction&) override; - virtual void BTS_RM16_imm8(const X86::Instruction&) override; - virtual void BTS_RM16_reg16(const X86::Instruction&) override; - virtual void BTS_RM32_imm8(const X86::Instruction&) override; - virtual void BTS_RM32_reg32(const X86::Instruction&) override; - virtual void BT_RM16_imm8(const X86::Instruction&) override; - virtual void BT_RM16_reg16(const X86::Instruction&) override; - virtual void BT_RM32_imm8(const X86::Instruction&) override; - virtual void BT_RM32_reg32(const X86::Instruction&) override; - virtual void CALL_FAR_mem16(const X86::Instruction&) override; - virtual void CALL_FAR_mem32(const X86::Instruction&) override; - virtual void CALL_RM16(const X86::Instruction&) override; - virtual void CALL_RM32(const X86::Instruction&) override; - virtual void CALL_imm16(const X86::Instruction&) override; - virtual void CALL_imm16_imm16(const X86::Instruction&) override; - virtual void CALL_imm16_imm32(const X86::Instruction&) override; - virtual void CALL_imm32(const X86::Instruction&) override; - virtual void CBW(const X86::Instruction&) override; - virtual void CDQ(const X86::Instruction&) override; - virtual void CLC(const X86::Instruction&) override; - virtual void CLD(const X86::Instruction&) override; - virtual void CLI(const X86::Instruction&) override; - virtual void CLTS(const X86::Instruction&) override; - virtual void CMC(const X86::Instruction&) override; - virtual void CMOVcc_reg16_RM16(const X86::Instruction&) override; - virtual void CMOVcc_reg32_RM32(const X86::Instruction&) override; - virtual void CMPSB(const X86::Instruction&) override; - virtual void CMPSD(const X86::Instruction&) override; - virtual void CMPSW(const X86::Instruction&) override; - virtual void CMPXCHG_RM16_reg16(const X86::Instruction&) override; - virtual void CMPXCHG_RM32_reg32(const X86::Instruction&) override; - virtual void CMPXCHG_RM8_reg8(const X86::Instruction&) override; - virtual void CMP_AL_imm8(const X86::Instruction&) override; - virtual void CMP_AX_imm16(const X86::Instruction&) override; - virtual void CMP_EAX_imm32(const X86::Instruction&) override; - virtual void CMP_RM16_imm16(const X86::Instruction&) override; - virtual void CMP_RM16_imm8(const X86::Instruction&) override; - virtual void CMP_RM16_reg16(const X86::Instruction&) override; - virtual void CMP_RM32_imm32(const X86::Instruction&) override; - virtual void CMP_RM32_imm8(const X86::Instruction&) override; - virtual void CMP_RM32_reg32(const X86::Instruction&) override; - virtual void CMP_RM8_imm8(const X86::Instruction&) override; - virtual void CMP_RM8_reg8(const X86::Instruction&) override; - virtual void CMP_reg16_RM16(const X86::Instruction&) override; - virtual void CMP_reg32_RM32(const X86::Instruction&) override; - virtual void CMP_reg8_RM8(const X86::Instruction&) override; - virtual void CPUID(const X86::Instruction&) override; - virtual void CWD(const X86::Instruction&) override; - virtual void CWDE(const X86::Instruction&) override; - virtual void DAA(const X86::Instruction&) override; - virtual void DAS(const X86::Instruction&) override; - virtual void DEC_RM16(const X86::Instruction&) override; - virtual void DEC_RM32(const X86::Instruction&) override; - virtual void DEC_RM8(const X86::Instruction&) override; - virtual void DEC_reg16(const X86::Instruction&) override; - virtual void DEC_reg32(const X86::Instruction&) override; - virtual void DIV_RM16(const X86::Instruction&) override; - virtual void DIV_RM32(const X86::Instruction&) override; - virtual void DIV_RM8(const X86::Instruction&) override; - virtual void ENTER16(const X86::Instruction&) override; - virtual void ENTER32(const X86::Instruction&) override; - virtual void ESCAPE(const X86::Instruction&) override; - virtual void FADD_RM32(const X86::Instruction&) override; - virtual void FMUL_RM32(const X86::Instruction&) override; - virtual void FCOM_RM32(const X86::Instruction&) override; - virtual void FCOMP_RM32(const X86::Instruction&) override; - virtual void FSUB_RM32(const X86::Instruction&) override; - virtual void FSUBR_RM32(const X86::Instruction&) override; - virtual void FDIV_RM32(const X86::Instruction&) override; - virtual void FDIVR_RM32(const X86::Instruction&) override; - virtual void FLD_RM32(const X86::Instruction&) override; - virtual void FXCH(const X86::Instruction&) override; - virtual void FST_RM32(const X86::Instruction&) override; - virtual void FNOP(const X86::Instruction&) override; - virtual void FSTP_RM32(const X86::Instruction&) override; - virtual void FLDENV(const X86::Instruction&) override; - virtual void FCHS(const X86::Instruction&) override; - virtual void FABS(const X86::Instruction&) override; - virtual void FTST(const X86::Instruction&) override; - virtual void FXAM(const X86::Instruction&) override; - virtual void FLDCW(const X86::Instruction&) override; - virtual void FLD1(const X86::Instruction&) override; - virtual void FLDL2T(const X86::Instruction&) override; - virtual void FLDL2E(const X86::Instruction&) override; - virtual void FLDPI(const X86::Instruction&) override; - virtual void FLDLG2(const X86::Instruction&) override; - virtual void FLDLN2(const X86::Instruction&) override; - virtual void FLDZ(const X86::Instruction&) override; - virtual void FNSTENV(const X86::Instruction&) override; - virtual void F2XM1(const X86::Instruction&) override; - virtual void FYL2X(const X86::Instruction&) override; - virtual void FPTAN(const X86::Instruction&) override; - virtual void FPATAN(const X86::Instruction&) override; - virtual void FXTRACT(const X86::Instruction&) override; - virtual void FPREM1(const X86::Instruction&) override; - virtual void FDECSTP(const X86::Instruction&) override; - virtual void FINCSTP(const X86::Instruction&) override; - virtual void FNSTCW(const X86::Instruction&) override; - virtual void FPREM(const X86::Instruction&) override; - virtual void FYL2XP1(const X86::Instruction&) override; - virtual void FSQRT(const X86::Instruction&) override; - virtual void FSINCOS(const X86::Instruction&) override; - virtual void FRNDINT(const X86::Instruction&) override; - virtual void FSCALE(const X86::Instruction&) override; - virtual void FSIN(const X86::Instruction&) override; - virtual void FCOS(const X86::Instruction&) override; - virtual void FIADD_RM32(const X86::Instruction&) override; - virtual void FCMOVB(const X86::Instruction&) override; - virtual void FIMUL_RM32(const X86::Instruction&) override; - virtual void FCMOVE(const X86::Instruction&) override; - virtual void FICOM_RM32(const X86::Instruction&) override; - virtual void FCMOVBE(const X86::Instruction&) override; - virtual void FICOMP_RM32(const X86::Instruction&) override; - virtual void FCMOVU(const X86::Instruction&) override; - virtual void FISUB_RM32(const X86::Instruction&) override; - virtual void FISUBR_RM32(const X86::Instruction&) override; - virtual void FUCOMPP(const X86::Instruction&) override; - virtual void FIDIV_RM32(const X86::Instruction&) override; - virtual void FIDIVR_RM32(const X86::Instruction&) override; - virtual void FILD_RM32(const X86::Instruction&) override; - virtual void FCMOVNB(const X86::Instruction&) override; - virtual void FISTTP_RM32(const X86::Instruction&) override; - virtual void FCMOVNE(const X86::Instruction&) override; - virtual void FIST_RM32(const X86::Instruction&) override; - virtual void FCMOVNBE(const X86::Instruction&) override; - virtual void FISTP_RM32(const X86::Instruction&) override; - virtual void FCMOVNU(const X86::Instruction&) override; - virtual void FNENI(const X86::Instruction&) override; - virtual void FNDISI(const X86::Instruction&) override; - virtual void FNCLEX(const X86::Instruction&) override; - virtual void FNINIT(const X86::Instruction&) override; - virtual void FNSETPM(const X86::Instruction&) override; - virtual void FLD_RM80(const X86::Instruction&) override; - virtual void FUCOMI(const X86::Instruction&) override; - virtual void FCOMI(const X86::Instruction&) override; - virtual void FSTP_RM80(const X86::Instruction&) override; - virtual void FADD_RM64(const X86::Instruction&) override; - virtual void FMUL_RM64(const X86::Instruction&) override; - virtual void FCOM_RM64(const X86::Instruction&) override; - virtual void FCOMP_RM64(const X86::Instruction&) override; - virtual void FSUB_RM64(const X86::Instruction&) override; - virtual void FSUBR_RM64(const X86::Instruction&) override; - virtual void FDIV_RM64(const X86::Instruction&) override; - virtual void FDIVR_RM64(const X86::Instruction&) override; - virtual void FLD_RM64(const X86::Instruction&) override; - virtual void FFREE(const X86::Instruction&) override; - virtual void FISTTP_RM64(const X86::Instruction&) override; - virtual void FST_RM64(const X86::Instruction&) override; - virtual void FSTP_RM64(const X86::Instruction&) override; - virtual void FRSTOR(const X86::Instruction&) override; - virtual void FUCOM(const X86::Instruction&) override; - virtual void FUCOMP(const X86::Instruction&) override; - virtual void FNSAVE(const X86::Instruction&) override; - virtual void FNSTSW(const X86::Instruction&) override; - virtual void FIADD_RM16(const X86::Instruction&) override; - virtual void FADDP(const X86::Instruction&) override; - virtual void FIMUL_RM16(const X86::Instruction&) override; - virtual void FMULP(const X86::Instruction&) override; - virtual void FICOM_RM16(const X86::Instruction&) override; - virtual void FICOMP_RM16(const X86::Instruction&) override; - virtual void FCOMPP(const X86::Instruction&) override; - virtual void FISUB_RM16(const X86::Instruction&) override; - virtual void FSUBRP(const X86::Instruction&) override; - virtual void FISUBR_RM16(const X86::Instruction&) override; - virtual void FSUBP(const X86::Instruction&) override; - virtual void FIDIV_RM16(const X86::Instruction&) override; - virtual void FDIVRP(const X86::Instruction&) override; - virtual void FIDIVR_RM16(const X86::Instruction&) override; - virtual void FDIVP(const X86::Instruction&) override; - virtual void FILD_RM16(const X86::Instruction&) override; - virtual void FFREEP(const X86::Instruction&) override; - virtual void FISTTP_RM16(const X86::Instruction&) override; - virtual void FIST_RM16(const X86::Instruction&) override; - virtual void FISTP_RM16(const X86::Instruction&) override; - virtual void FBLD_M80(const X86::Instruction&) override; - virtual void FNSTSW_AX(const X86::Instruction&) override; - virtual void FILD_RM64(const X86::Instruction&) override; - virtual void FUCOMIP(const X86::Instruction&) override; - virtual void FBSTP_M80(const X86::Instruction&) override; - virtual void FCOMIP(const X86::Instruction&) override; - virtual void FISTP_RM64(const X86::Instruction&) override; - virtual void HLT(const X86::Instruction&) override; - virtual void IDIV_RM16(const X86::Instruction&) override; - virtual void IDIV_RM32(const X86::Instruction&) override; - virtual void IDIV_RM8(const X86::Instruction&) override; - virtual void IMUL_RM16(const X86::Instruction&) override; - virtual void IMUL_RM32(const X86::Instruction&) override; - virtual void IMUL_RM8(const X86::Instruction&) override; - virtual void IMUL_reg16_RM16(const X86::Instruction&) override; - virtual void IMUL_reg16_RM16_imm16(const X86::Instruction&) override; - virtual void IMUL_reg16_RM16_imm8(const X86::Instruction&) override; - virtual void IMUL_reg32_RM32(const X86::Instruction&) override; - virtual void IMUL_reg32_RM32_imm32(const X86::Instruction&) override; - virtual void IMUL_reg32_RM32_imm8(const X86::Instruction&) override; - virtual void INC_RM16(const X86::Instruction&) override; - virtual void INC_RM32(const X86::Instruction&) override; - virtual void INC_RM8(const X86::Instruction&) override; - virtual void INC_reg16(const X86::Instruction&) override; - virtual void INC_reg32(const X86::Instruction&) override; - virtual void INSB(const X86::Instruction&) override; - virtual void INSD(const X86::Instruction&) override; - virtual void INSW(const X86::Instruction&) override; - virtual void INT1(const X86::Instruction&) override; - virtual void INT3(const X86::Instruction&) override; - virtual void INTO(const X86::Instruction&) override; - virtual void INT_imm8(const X86::Instruction&) override; - virtual void INVLPG(const X86::Instruction&) override; - virtual void IN_AL_DX(const X86::Instruction&) override; - virtual void IN_AL_imm8(const X86::Instruction&) override; - virtual void IN_AX_DX(const X86::Instruction&) override; - virtual void IN_AX_imm8(const X86::Instruction&) override; - virtual void IN_EAX_DX(const X86::Instruction&) override; - virtual void IN_EAX_imm8(const X86::Instruction&) override; - virtual void IRET(const X86::Instruction&) override; - virtual void JCXZ_imm8(const X86::Instruction&) override; - virtual void JMP_FAR_mem16(const X86::Instruction&) override; - virtual void JMP_FAR_mem32(const X86::Instruction&) override; - virtual void JMP_RM16(const X86::Instruction&) override; - virtual void JMP_RM32(const X86::Instruction&) override; - virtual void JMP_imm16(const X86::Instruction&) override; - virtual void JMP_imm16_imm16(const X86::Instruction&) override; - virtual void JMP_imm16_imm32(const X86::Instruction&) override; - virtual void JMP_imm32(const X86::Instruction&) override; - virtual void JMP_short_imm8(const X86::Instruction&) override; - virtual void Jcc_NEAR_imm(const X86::Instruction&) override; - virtual void Jcc_imm8(const X86::Instruction&) override; - virtual void LAHF(const X86::Instruction&) override; - virtual void LAR_reg16_RM16(const X86::Instruction&) override; - virtual void LAR_reg32_RM32(const X86::Instruction&) override; - virtual void LDS_reg16_mem16(const X86::Instruction&) override; - virtual void LDS_reg32_mem32(const X86::Instruction&) override; - virtual void LEAVE16(const X86::Instruction&) override; - virtual void LEAVE32(const X86::Instruction&) override; - virtual void LEA_reg16_mem16(const X86::Instruction&) override; - virtual void LEA_reg32_mem32(const X86::Instruction&) override; - virtual void LES_reg16_mem16(const X86::Instruction&) override; - virtual void LES_reg32_mem32(const X86::Instruction&) override; - virtual void LFS_reg16_mem16(const X86::Instruction&) override; - virtual void LFS_reg32_mem32(const X86::Instruction&) override; - virtual void LGDT(const X86::Instruction&) override; - virtual void LGS_reg16_mem16(const X86::Instruction&) override; - virtual void LGS_reg32_mem32(const X86::Instruction&) override; - virtual void LIDT(const X86::Instruction&) override; - virtual void LLDT_RM16(const X86::Instruction&) override; - virtual void LMSW_RM16(const X86::Instruction&) override; - virtual void LODSB(const X86::Instruction&) override; - virtual void LODSD(const X86::Instruction&) override; - virtual void LODSW(const X86::Instruction&) override; - virtual void LOOPNZ_imm8(const X86::Instruction&) override; - virtual void LOOPZ_imm8(const X86::Instruction&) override; - virtual void LOOP_imm8(const X86::Instruction&) override; - virtual void LSL_reg16_RM16(const X86::Instruction&) override; - virtual void LSL_reg32_RM32(const X86::Instruction&) override; - virtual void LSS_reg16_mem16(const X86::Instruction&) override; - virtual void LSS_reg32_mem32(const X86::Instruction&) override; - virtual void LTR_RM16(const X86::Instruction&) override; - virtual void MOVSB(const X86::Instruction&) override; - virtual void MOVSD(const X86::Instruction&) override; - virtual void MOVSW(const X86::Instruction&) override; - virtual void MOVSX_reg16_RM8(const X86::Instruction&) override; - virtual void MOVSX_reg32_RM16(const X86::Instruction&) override; - virtual void MOVSX_reg32_RM8(const X86::Instruction&) override; - virtual void MOVZX_reg16_RM8(const X86::Instruction&) override; - virtual void MOVZX_reg32_RM16(const X86::Instruction&) override; - virtual void MOVZX_reg32_RM8(const X86::Instruction&) override; - virtual void MOV_AL_moff8(const X86::Instruction&) override; - virtual void MOV_AX_moff16(const X86::Instruction&) override; - virtual void MOV_CR_reg32(const X86::Instruction&) override; - virtual void MOV_DR_reg32(const X86::Instruction&) override; - virtual void MOV_EAX_moff32(const X86::Instruction&) override; - virtual void MOV_RM16_imm16(const X86::Instruction&) override; - virtual void MOV_RM16_reg16(const X86::Instruction&) override; - virtual void MOV_RM16_seg(const X86::Instruction&) override; - virtual void MOV_RM32_imm32(const X86::Instruction&) override; - virtual void MOV_RM32_reg32(const X86::Instruction&) override; - virtual void MOV_RM8_imm8(const X86::Instruction&) override; - virtual void MOV_RM8_reg8(const X86::Instruction&) override; - virtual void MOV_moff16_AX(const X86::Instruction&) override; - virtual void MOV_moff32_EAX(const X86::Instruction&) override; - virtual void MOV_moff8_AL(const X86::Instruction&) override; - virtual void MOV_reg16_RM16(const X86::Instruction&) override; - virtual void MOV_reg16_imm16(const X86::Instruction&) override; - virtual void MOV_reg32_CR(const X86::Instruction&) override; - virtual void MOV_reg32_DR(const X86::Instruction&) override; - virtual void MOV_reg32_RM32(const X86::Instruction&) override; - virtual void MOV_reg32_imm32(const X86::Instruction&) override; - virtual void MOV_reg8_RM8(const X86::Instruction&) override; - virtual void MOV_reg8_imm8(const X86::Instruction&) override; - virtual void MOV_seg_RM16(const X86::Instruction&) override; - virtual void MOV_seg_RM32(const X86::Instruction&) override; - virtual void MUL_RM16(const X86::Instruction&) override; - virtual void MUL_RM32(const X86::Instruction&) override; - virtual void MUL_RM8(const X86::Instruction&) override; - virtual void NEG_RM16(const X86::Instruction&) override; - virtual void NEG_RM32(const X86::Instruction&) override; - virtual void NEG_RM8(const X86::Instruction&) override; - virtual void NOP(const X86::Instruction&) override; - virtual void NOT_RM16(const X86::Instruction&) override; - virtual void NOT_RM32(const X86::Instruction&) override; - virtual void NOT_RM8(const X86::Instruction&) override; - virtual void OR_AL_imm8(const X86::Instruction&) override; - virtual void OR_AX_imm16(const X86::Instruction&) override; - virtual void OR_EAX_imm32(const X86::Instruction&) override; - virtual void OR_RM16_imm16(const X86::Instruction&) override; - virtual void OR_RM16_imm8(const X86::Instruction&) override; - virtual void OR_RM16_reg16(const X86::Instruction&) override; - virtual void OR_RM32_imm32(const X86::Instruction&) override; - virtual void OR_RM32_imm8(const X86::Instruction&) override; - virtual void OR_RM32_reg32(const X86::Instruction&) override; - virtual void OR_RM8_imm8(const X86::Instruction&) override; - virtual void OR_RM8_reg8(const X86::Instruction&) override; - virtual void OR_reg16_RM16(const X86::Instruction&) override; - virtual void OR_reg32_RM32(const X86::Instruction&) override; - virtual void OR_reg8_RM8(const X86::Instruction&) override; - virtual void OUTSB(const X86::Instruction&) override; - virtual void OUTSD(const X86::Instruction&) override; - virtual void OUTSW(const X86::Instruction&) override; - virtual void OUT_DX_AL(const X86::Instruction&) override; - virtual void OUT_DX_AX(const X86::Instruction&) override; - virtual void OUT_DX_EAX(const X86::Instruction&) override; - virtual void OUT_imm8_AL(const X86::Instruction&) override; - virtual void OUT_imm8_AX(const X86::Instruction&) override; - virtual void OUT_imm8_EAX(const X86::Instruction&) override; - virtual void PACKSSDW_mm1_mm2m64(const X86::Instruction&) override; - virtual void PACKSSWB_mm1_mm2m64(const X86::Instruction&) override; - virtual void PACKUSWB_mm1_mm2m64(const X86::Instruction&) override; - virtual void PADDB_mm1_mm2m64(const X86::Instruction&) override; - virtual void PADDW_mm1_mm2m64(const X86::Instruction&) override; - virtual void PADDD_mm1_mm2m64(const X86::Instruction&) override; - virtual void PADDSB_mm1_mm2m64(const X86::Instruction&) override; - virtual void PADDSW_mm1_mm2m64(const X86::Instruction&) override; - virtual void PADDUSB_mm1_mm2m64(const X86::Instruction&) override; - virtual void PADDUSW_mm1_mm2m64(const X86::Instruction&) override; - virtual void PAND_mm1_mm2m64(const X86::Instruction&) override; - virtual void PANDN_mm1_mm2m64(const X86::Instruction&) override; - virtual void PCMPEQB_mm1_mm2m64(const X86::Instruction&) override; - virtual void PCMPEQW_mm1_mm2m64(const X86::Instruction&) override; - virtual void PCMPEQD_mm1_mm2m64(const X86::Instruction&) override; - virtual void PCMPGTB_mm1_mm2m64(const X86::Instruction&) override; - virtual void PCMPGTW_mm1_mm2m64(const X86::Instruction&) override; - virtual void PCMPGTD_mm1_mm2m64(const X86::Instruction&) override; - virtual void PMADDWD_mm1_mm2m64(const X86::Instruction&) override; - virtual void PMULHW_mm1_mm2m64(const X86::Instruction&) override; - virtual void PMULLW_mm1_mm2m64(const X86::Instruction&) override; - virtual void POPA(const X86::Instruction&) override; - virtual void POPAD(const X86::Instruction&) override; - virtual void POPF(const X86::Instruction&) override; - virtual void POPFD(const X86::Instruction&) override; - virtual void POP_DS(const X86::Instruction&) override; - virtual void POP_ES(const X86::Instruction&) override; - virtual void POP_FS(const X86::Instruction&) override; - virtual void POP_GS(const X86::Instruction&) override; - virtual void POP_RM16(const X86::Instruction&) override; - virtual void POP_RM32(const X86::Instruction&) override; - virtual void POP_SS(const X86::Instruction&) override; - virtual void POP_reg16(const X86::Instruction&) override; - virtual void POP_reg32(const X86::Instruction&) override; - virtual void POR_mm1_mm2m64(const X86::Instruction&) override; - virtual void PSLLW_mm1_mm2m64(const X86::Instruction&) override; - virtual void PSLLW_mm1_imm8(const X86::Instruction&) override; - virtual void PSLLD_mm1_mm2m64(const X86::Instruction&) override; - virtual void PSLLD_mm1_imm8(const X86::Instruction&) override; - virtual void PSLLQ_mm1_mm2m64(const X86::Instruction&) override; - virtual void PSLLQ_mm1_imm8(const X86::Instruction&) override; - virtual void PSRAW_mm1_mm2m64(const X86::Instruction&) override; - virtual void PSRAW_mm1_imm8(const X86::Instruction&) override; - virtual void PSRAD_mm1_mm2m64(const X86::Instruction&) override; - virtual void PSRAD_mm1_imm8(const X86::Instruction&) override; - virtual void PSRLW_mm1_mm2m64(const X86::Instruction&) override; - virtual void PSRLW_mm1_imm8(const X86::Instruction&) override; - virtual void PSRLD_mm1_mm2m64(const X86::Instruction&) override; - virtual void PSRLD_mm1_imm8(const X86::Instruction&) override; - virtual void PSRLQ_mm1_mm2m64(const X86::Instruction&) override; - virtual void PSRLQ_mm1_imm8(const X86::Instruction&) override; - virtual void PSUBB_mm1_mm2m64(const X86::Instruction&) override; - virtual void PSUBW_mm1_mm2m64(const X86::Instruction&) override; - virtual void PSUBD_mm1_mm2m64(const X86::Instruction&) override; - virtual void PSUBSB_mm1_mm2m64(const X86::Instruction&) override; - virtual void PSUBSW_mm1_mm2m64(const X86::Instruction&) override; - virtual void PSUBUSB_mm1_mm2m64(const X86::Instruction&) override; - virtual void PSUBUSW_mm1_mm2m64(const X86::Instruction&) override; - virtual void PUNPCKHBW_mm1_mm2m64(const X86::Instruction&) override; - virtual void PUNPCKHWD_mm1_mm2m64(const X86::Instruction&) override; - virtual void PUNPCKHDQ_mm1_mm2m64(const X86::Instruction&) override; - virtual void PUNPCKLBW_mm1_mm2m32(const X86::Instruction&) override; - virtual void PUNPCKLWD_mm1_mm2m32(const X86::Instruction&) override; - virtual void PUNPCKLDQ_mm1_mm2m32(const X86::Instruction&) override; - virtual void PUSHA(const X86::Instruction&) override; - virtual void PUSHAD(const X86::Instruction&) override; - virtual void PUSHF(const X86::Instruction&) override; - virtual void PUSHFD(const X86::Instruction&) override; - virtual void PUSH_CS(const X86::Instruction&) override; - virtual void PUSH_DS(const X86::Instruction&) override; - virtual void PUSH_ES(const X86::Instruction&) override; - virtual void PUSH_FS(const X86::Instruction&) override; - virtual void PUSH_GS(const X86::Instruction&) override; - virtual void PUSH_RM16(const X86::Instruction&) override; - virtual void PUSH_RM32(const X86::Instruction&) override; - virtual void PUSH_SP_8086_80186(const X86::Instruction&) override; - virtual void PUSH_SS(const X86::Instruction&) override; - virtual void PUSH_imm16(const X86::Instruction&) override; - virtual void PUSH_imm32(const X86::Instruction&) override; - virtual void PUSH_imm8(const X86::Instruction&) override; - virtual void PUSH_reg16(const X86::Instruction&) override; - virtual void PUSH_reg32(const X86::Instruction&) override; - virtual void PXOR_mm1_mm2m64(const X86::Instruction&) override; - virtual void RCL_RM16_1(const X86::Instruction&) override; - virtual void RCL_RM16_CL(const X86::Instruction&) override; - virtual void RCL_RM16_imm8(const X86::Instruction&) override; - virtual void RCL_RM32_1(const X86::Instruction&) override; - virtual void RCL_RM32_CL(const X86::Instruction&) override; - virtual void RCL_RM32_imm8(const X86::Instruction&) override; - virtual void RCL_RM8_1(const X86::Instruction&) override; - virtual void RCL_RM8_CL(const X86::Instruction&) override; - virtual void RCL_RM8_imm8(const X86::Instruction&) override; - virtual void RCR_RM16_1(const X86::Instruction&) override; - virtual void RCR_RM16_CL(const X86::Instruction&) override; - virtual void RCR_RM16_imm8(const X86::Instruction&) override; - virtual void RCR_RM32_1(const X86::Instruction&) override; - virtual void RCR_RM32_CL(const X86::Instruction&) override; - virtual void RCR_RM32_imm8(const X86::Instruction&) override; - virtual void RCR_RM8_1(const X86::Instruction&) override; - virtual void RCR_RM8_CL(const X86::Instruction&) override; - virtual void RCR_RM8_imm8(const X86::Instruction&) override; - virtual void RDTSC(const X86::Instruction&) override; - virtual void RET(const X86::Instruction&) override; - virtual void RETF(const X86::Instruction&) override; - virtual void RETF_imm16(const X86::Instruction&) override; - virtual void RET_imm16(const X86::Instruction&) override; - virtual void ROL_RM16_1(const X86::Instruction&) override; - virtual void ROL_RM16_CL(const X86::Instruction&) override; - virtual void ROL_RM16_imm8(const X86::Instruction&) override; - virtual void ROL_RM32_1(const X86::Instruction&) override; - virtual void ROL_RM32_CL(const X86::Instruction&) override; - virtual void ROL_RM32_imm8(const X86::Instruction&) override; - virtual void ROL_RM8_1(const X86::Instruction&) override; - virtual void ROL_RM8_CL(const X86::Instruction&) override; - virtual void ROL_RM8_imm8(const X86::Instruction&) override; - virtual void ROR_RM16_1(const X86::Instruction&) override; - virtual void ROR_RM16_CL(const X86::Instruction&) override; - virtual void ROR_RM16_imm8(const X86::Instruction&) override; - virtual void ROR_RM32_1(const X86::Instruction&) override; - virtual void ROR_RM32_CL(const X86::Instruction&) override; - virtual void ROR_RM32_imm8(const X86::Instruction&) override; - virtual void ROR_RM8_1(const X86::Instruction&) override; - virtual void ROR_RM8_CL(const X86::Instruction&) override; - virtual void ROR_RM8_imm8(const X86::Instruction&) override; - virtual void SAHF(const X86::Instruction&) override; - virtual void SALC(const X86::Instruction&) override; - virtual void SAR_RM16_1(const X86::Instruction&) override; - virtual void SAR_RM16_CL(const X86::Instruction&) override; - virtual void SAR_RM16_imm8(const X86::Instruction&) override; - virtual void SAR_RM32_1(const X86::Instruction&) override; - virtual void SAR_RM32_CL(const X86::Instruction&) override; - virtual void SAR_RM32_imm8(const X86::Instruction&) override; - virtual void SAR_RM8_1(const X86::Instruction&) override; - virtual void SAR_RM8_CL(const X86::Instruction&) override; - virtual void SAR_RM8_imm8(const X86::Instruction&) override; - virtual void SBB_AL_imm8(const X86::Instruction&) override; - virtual void SBB_AX_imm16(const X86::Instruction&) override; - virtual void SBB_EAX_imm32(const X86::Instruction&) override; - virtual void SBB_RM16_imm16(const X86::Instruction&) override; - virtual void SBB_RM16_imm8(const X86::Instruction&) override; - virtual void SBB_RM16_reg16(const X86::Instruction&) override; - virtual void SBB_RM32_imm32(const X86::Instruction&) override; - virtual void SBB_RM32_imm8(const X86::Instruction&) override; - virtual void SBB_RM32_reg32(const X86::Instruction&) override; - virtual void SBB_RM8_imm8(const X86::Instruction&) override; - virtual void SBB_RM8_reg8(const X86::Instruction&) override; - virtual void SBB_reg16_RM16(const X86::Instruction&) override; - virtual void SBB_reg32_RM32(const X86::Instruction&) override; - virtual void SBB_reg8_RM8(const X86::Instruction&) override; - virtual void SCASB(const X86::Instruction&) override; - virtual void SCASD(const X86::Instruction&) override; - virtual void SCASW(const X86::Instruction&) override; - virtual void SETcc_RM8(const X86::Instruction&) override; - virtual void SGDT(const X86::Instruction&) override; - virtual void SHLD_RM16_reg16_CL(const X86::Instruction&) override; - virtual void SHLD_RM16_reg16_imm8(const X86::Instruction&) override; - virtual void SHLD_RM32_reg32_CL(const X86::Instruction&) override; - virtual void SHLD_RM32_reg32_imm8(const X86::Instruction&) override; - virtual void SHL_RM16_1(const X86::Instruction&) override; - virtual void SHL_RM16_CL(const X86::Instruction&) override; - virtual void SHL_RM16_imm8(const X86::Instruction&) override; - virtual void SHL_RM32_1(const X86::Instruction&) override; - virtual void SHL_RM32_CL(const X86::Instruction&) override; - virtual void SHL_RM32_imm8(const X86::Instruction&) override; - virtual void SHL_RM8_1(const X86::Instruction&) override; - virtual void SHL_RM8_CL(const X86::Instruction&) override; - virtual void SHL_RM8_imm8(const X86::Instruction&) override; - virtual void SHRD_RM16_reg16_CL(const X86::Instruction&) override; - virtual void SHRD_RM16_reg16_imm8(const X86::Instruction&) override; - virtual void SHRD_RM32_reg32_CL(const X86::Instruction&) override; - virtual void SHRD_RM32_reg32_imm8(const X86::Instruction&) override; - virtual void SHR_RM16_1(const X86::Instruction&) override; - virtual void SHR_RM16_CL(const X86::Instruction&) override; - virtual void SHR_RM16_imm8(const X86::Instruction&) override; - virtual void SHR_RM32_1(const X86::Instruction&) override; - virtual void SHR_RM32_CL(const X86::Instruction&) override; - virtual void SHR_RM32_imm8(const X86::Instruction&) override; - virtual void SHR_RM8_1(const X86::Instruction&) override; - virtual void SHR_RM8_CL(const X86::Instruction&) override; - virtual void SHR_RM8_imm8(const X86::Instruction&) override; - virtual void SIDT(const X86::Instruction&) override; - virtual void SLDT_RM16(const X86::Instruction&) override; - virtual void SMSW_RM16(const X86::Instruction&) override; - virtual void STC(const X86::Instruction&) override; - virtual void STD(const X86::Instruction&) override; - virtual void STI(const X86::Instruction&) override; - virtual void STOSB(const X86::Instruction&) override; - virtual void STOSD(const X86::Instruction&) override; - virtual void STOSW(const X86::Instruction&) override; - virtual void STR_RM16(const X86::Instruction&) override; - virtual void SUB_AL_imm8(const X86::Instruction&) override; - virtual void SUB_AX_imm16(const X86::Instruction&) override; - virtual void SUB_EAX_imm32(const X86::Instruction&) override; - virtual void SUB_RM16_imm16(const X86::Instruction&) override; - virtual void SUB_RM16_imm8(const X86::Instruction&) override; - virtual void SUB_RM16_reg16(const X86::Instruction&) override; - virtual void SUB_RM32_imm32(const X86::Instruction&) override; - virtual void SUB_RM32_imm8(const X86::Instruction&) override; - virtual void SUB_RM32_reg32(const X86::Instruction&) override; - virtual void SUB_RM8_imm8(const X86::Instruction&) override; - virtual void SUB_RM8_reg8(const X86::Instruction&) override; - virtual void SUB_reg16_RM16(const X86::Instruction&) override; - virtual void SUB_reg32_RM32(const X86::Instruction&) override; - virtual void SUB_reg8_RM8(const X86::Instruction&) override; - virtual void TEST_AL_imm8(const X86::Instruction&) override; - virtual void TEST_AX_imm16(const X86::Instruction&) override; - virtual void TEST_EAX_imm32(const X86::Instruction&) override; - virtual void TEST_RM16_imm16(const X86::Instruction&) override; - virtual void TEST_RM16_reg16(const X86::Instruction&) override; - virtual void TEST_RM32_imm32(const X86::Instruction&) override; - virtual void TEST_RM32_reg32(const X86::Instruction&) override; - virtual void TEST_RM8_imm8(const X86::Instruction&) override; - virtual void TEST_RM8_reg8(const X86::Instruction&) override; - virtual void UD0(const X86::Instruction&) override; - virtual void UD1(const X86::Instruction&) override; - virtual void UD2(const X86::Instruction&) override; - virtual void VERR_RM16(const X86::Instruction&) override; - virtual void VERW_RM16(const X86::Instruction&) override; - virtual void WAIT(const X86::Instruction&) override; - virtual void WBINVD(const X86::Instruction&) override; - virtual void XADD_RM16_reg16(const X86::Instruction&) override; - virtual void XADD_RM32_reg32(const X86::Instruction&) override; - virtual void XADD_RM8_reg8(const X86::Instruction&) override; - virtual void XCHG_AX_reg16(const X86::Instruction&) override; - virtual void XCHG_EAX_reg32(const X86::Instruction&) override; - virtual void XCHG_reg16_RM16(const X86::Instruction&) override; - virtual void XCHG_reg32_RM32(const X86::Instruction&) override; - virtual void XCHG_reg8_RM8(const X86::Instruction&) override; - virtual void XLAT(const X86::Instruction&) override; - virtual void XOR_AL_imm8(const X86::Instruction&) override; - virtual void XOR_AX_imm16(const X86::Instruction&) override; - virtual void XOR_EAX_imm32(const X86::Instruction&) override; - virtual void XOR_RM16_imm16(const X86::Instruction&) override; - virtual void XOR_RM16_imm8(const X86::Instruction&) override; - virtual void XOR_RM16_reg16(const X86::Instruction&) override; - virtual void XOR_RM32_imm32(const X86::Instruction&) override; - virtual void XOR_RM32_imm8(const X86::Instruction&) override; - virtual void XOR_RM32_reg32(const X86::Instruction&) override; - virtual void XOR_RM8_imm8(const X86::Instruction&) override; - virtual void XOR_RM8_reg8(const X86::Instruction&) override; - virtual void XOR_reg16_RM16(const X86::Instruction&) override; - virtual void XOR_reg32_RM32(const X86::Instruction&) override; - virtual void XOR_reg8_RM8(const X86::Instruction&) override; - virtual void MOVQ_mm1_mm2m64(const X86::Instruction&) override; - virtual void MOVQ_mm1m64_mm2(const X86::Instruction&) override; - virtual void MOVD_mm1_rm32(const X86::Instruction&) override; - virtual void MOVQ_mm1_rm64(const X86::Instruction&) override; // long mode - virtual void MOVD_rm32_mm2(const X86::Instruction&) override; - virtual void MOVQ_rm64_mm2(const X86::Instruction&) override; // long mode - virtual void EMMS(const X86::Instruction&) override; - - virtual void CMPXCHG8B_m64(X86::Instruction const&) override; - virtual void RDRAND_reg(X86::Instruction const&) override; - virtual void RDSEED_reg(X86::Instruction const&) override; - - virtual void PREFETCHTNTA(X86::Instruction const&) override; - virtual void PREFETCHT0(X86::Instruction const&) override; - virtual void PREFETCHT1(X86::Instruction const&) override; - virtual void PREFETCHT2(X86::Instruction const&) override; - virtual void LDMXCSR(X86::Instruction const&) override; - virtual void STMXCSR(X86::Instruction const&) override; - virtual void MOVUPS_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void MOVSS_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void MOVUPS_xmm1m128_xmm2(X86::Instruction const&) override; - virtual void MOVSS_xmm1m32_xmm2(X86::Instruction const&) override; - virtual void MOVLPS_xmm1_xmm2m64(X86::Instruction const&) override; - virtual void MOVLPS_m64_xmm2(X86::Instruction const&) override; - virtual void UNPCKLPS_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void UNPCKHPS_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void MOVHPS_xmm1_xmm2m64(X86::Instruction const&) override; - virtual void MOVHPS_m64_xmm2(X86::Instruction const&) override; - virtual void MOVAPS_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void MOVAPS_xmm1m128_xmm2(X86::Instruction const&) override; - virtual void CVTPI2PS_xmm1_mm2m64(X86::Instruction const&) override; - virtual void CVTSI2SS_xmm1_rm32(X86::Instruction const&) override; - virtual void MOVNTPS_xmm1m128_xmm2(X86::Instruction const&) override; - virtual void CVTTPS2PI_mm1_xmm2m64(X86::Instruction const&) override; - virtual void CVTTSS2SI_r32_xmm2m32(X86::Instruction const&) override; - virtual void CVTPS2PI_xmm1_mm2m64(X86::Instruction const&) override; - virtual void CVTSS2SI_r32_xmm2m32(X86::Instruction const&) override; - virtual void UCOMISS_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void COMISS_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void MOVMSKPS_reg_xmm(X86::Instruction const&) override; - virtual void SQRTPS_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void SQRTSS_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void RSQRTPS_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void RSQRTSS_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void RCPPS_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void RCPSS_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void ANDPS_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void ANDNPS_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void ORPS_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void XORPS_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void ADDPS_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void ADDSS_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void MULPS_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void MULSS_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void SUBPS_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void SUBSS_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void MINPS_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void MINSS_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void DIVPS_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void DIVSS_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void MAXPS_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void MAXSS_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void PSHUFW_mm1_mm2m64_imm8(X86::Instruction const&) override; - virtual void CMPPS_xmm1_xmm2m128_imm8(X86::Instruction const&) override; - virtual void CMPSS_xmm1_xmm2m32_imm8(X86::Instruction const&) override; - virtual void PINSRW_mm1_r32m16_imm8(X86::Instruction const&) override; - virtual void PINSRW_xmm1_r32m16_imm8(X86::Instruction const&) override; - virtual void PEXTRW_reg_mm1_imm8(X86::Instruction const&) override; - virtual void PEXTRW_reg_xmm1_imm8(X86::Instruction const&) override; - virtual void SHUFPS_xmm1_xmm2m128_imm8(X86::Instruction const&) override; - virtual void PMOVMSKB_reg_mm1(X86::Instruction const&) override; - virtual void PMOVMSKB_reg_xmm1(X86::Instruction const&) override; - virtual void PMINUB_mm1_mm2m64(X86::Instruction const&) override; - virtual void PMINUB_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void PMAXUB_mm1_mm2m64(X86::Instruction const&) override; - virtual void PMAXUB_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void PAVGB_mm1_mm2m64(X86::Instruction const&) override; - virtual void PAVGB_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void PAVGW_mm1_mm2m64(X86::Instruction const&) override; - virtual void PAVGW_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void PMULHUW_mm1_mm2m64(X86::Instruction const&) override; - virtual void PMULHUW_xmm1_xmm2m64(X86::Instruction const&) override; - virtual void MOVNTQ_m64_mm1(X86::Instruction const&) override; - virtual void PMINSB_mm1_mm2m64(X86::Instruction const&) override; - virtual void PMINSB_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void PMAXSB_mm1_mm2m64(X86::Instruction const&) override; - virtual void PMAXSB_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void PSADBB_mm1_mm2m64(X86::Instruction const&) override; - virtual void PSADBB_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void MASKMOVQ_mm1_mm2m64(X86::Instruction const&) override; - - virtual void MOVUPD_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void MOVSD_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void MOVUPD_xmm1m128_xmm2(X86::Instruction const&) override; - virtual void MOVSD_xmm1m32_xmm2(X86::Instruction const&) override; - virtual void MOVLPD_xmm1_m64(X86::Instruction const&) override; - virtual void MOVLPD_m64_xmm2(X86::Instruction const&) override; - virtual void UNPCKLPD_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void UNPCKHPD_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void MOVHPD_xmm1_xmm2m64(X86::Instruction const&) override; - virtual void MOVAPD_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void MOVAPD_xmm1m128_xmm2(X86::Instruction const&) override; - virtual void CVTPI2PD_xmm1_mm2m64(X86::Instruction const&) override; - virtual void CVTSI2SD_xmm1_rm32(X86::Instruction const&) override; - virtual void CVTTPD2PI_mm1_xmm2m128(X86::Instruction const&) override; - virtual void CVTTSS2SI_r32_xmm2m64(X86::Instruction const&) override; - virtual void CVTPD2PI_xmm1_mm2m128(X86::Instruction const&) override; - virtual void CVTSD2SI_xmm1_rm64(X86::Instruction const&) override; - virtual void UCOMISD_xmm1_xmm2m64(X86::Instruction const&) override; - virtual void COMISD_xmm1_xmm2m64(X86::Instruction const&) override; - virtual void MOVMSKPD_reg_xmm(X86::Instruction const&) override; - virtual void SQRTPD_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void SQRTSD_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void ANDPD_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void ANDNPD_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void ORPD_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void XORPD_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void ADDPD_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void ADDSD_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void MULPD_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void MULSD_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void CVTPS2PD_xmm1_xmm2m64(X86::Instruction const&) override; - virtual void CVTPD2PS_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void CVTSS2SD_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void CVTSD2SS_xmm1_xmm2m64(X86::Instruction const&) override; - virtual void CVTDQ2PS_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void CVTPS2DQ_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void CVTTPS2DQ_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void SUBPD_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void SUBSD_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void MINPD_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void MINSD_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void DIVPD_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void DIVSD_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void MAXPD_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void MAXSD_xmm1_xmm2m32(X86::Instruction const&) override; - virtual void PUNPCKLQDQ_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void PUNPCKHQDQ_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void MOVDQA_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void MOVDQU_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void PSHUFD_xmm1_xmm2m128_imm8(X86::Instruction const&) override; - virtual void PSHUFHW_xmm1_xmm2m128_imm8(X86::Instruction const&) override; - virtual void PSHUFLW_xmm1_xmm2m128_imm8(X86::Instruction const&) override; - virtual void PSRLQ_xmm1_imm8(X86::Instruction const&) override; - virtual void PSRLDQ_xmm1_imm8(X86::Instruction const&) override; - virtual void PSLLQ_xmm1_imm8(X86::Instruction const&) override; - virtual void PSLLDQ_xmm1_imm8(X86::Instruction const&) override; - virtual void MOVD_rm32_xmm2(X86::Instruction const&) override; - virtual void MOVQ_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void MOVDQA_xmm1m128_xmm2(X86::Instruction const&) override; - virtual void MOVDQU_xmm1m128_xmm2(X86::Instruction const&) override; - virtual void CMPPD_xmm1_xmm2m128_imm8(X86::Instruction const&) override; - virtual void CMPSD_xmm1_xmm2m32_imm8(X86::Instruction const&) override; - virtual void SHUFPD_xmm1_xmm2m128_imm8(X86::Instruction const&) override; - virtual void PADDQ_mm1_mm2m64(X86::Instruction const&) override; - virtual void MOVQ_xmm1m128_xmm2(X86::Instruction const&) override; - virtual void MOVQ2DQ_xmm_mm(X86::Instruction const&) override; - virtual void MOVDQ2Q_mm_xmm(X86::Instruction const&) override; - virtual void CVTTPD2DQ_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void CVTPD2DQ_xmm1_xmm2m128(X86::Instruction const&) override; - virtual void CVTDQ2PD_xmm1_xmm2m64(X86::Instruction const&) override; - virtual void PMULUDQ_mm1_mm2m64(X86::Instruction const&) override; - virtual void PMULUDQ_mm1_mm2m128(X86::Instruction const&) override; - virtual void PSUBQ_mm1_mm2m64(X86::Instruction const&) override; - - virtual void wrap_0xC0(const X86::Instruction&) override; - virtual void wrap_0xC1_16(const X86::Instruction&) override; - virtual void wrap_0xC1_32(const X86::Instruction&) override; - virtual void wrap_0xD0(const X86::Instruction&) override; - virtual void wrap_0xD1_16(const X86::Instruction&) override; - virtual void wrap_0xD1_32(const X86::Instruction&) override; - virtual void wrap_0xD2(const X86::Instruction&) override; - virtual void wrap_0xD3_16(const X86::Instruction&) override; - virtual void wrap_0xD3_32(const X86::Instruction&) override; - - template - void generic_AL_imm8(Op, const X86::Instruction&); - template - void generic_AX_imm16(Op, const X86::Instruction&); - template - void generic_EAX_imm32(Op, const X86::Instruction&); - template - void generic_RM16_imm16(Op, const X86::Instruction&); - template - void generic_RM16_imm8(Op, const X86::Instruction&); - template - void generic_RM16_unsigned_imm8(Op, const X86::Instruction&); - template - void generic_RM16_reg16(Op, const X86::Instruction&); - template - void generic_RM32_imm32(Op, const X86::Instruction&); - template - void generic_RM32_imm8(Op, const X86::Instruction&); - template - void generic_RM32_unsigned_imm8(Op, const X86::Instruction&); - template - void generic_RM32_reg32(Op, const X86::Instruction&); - template - void generic_RM8_imm8(Op, const X86::Instruction&); - template - void generic_RM8_reg8(Op, const X86::Instruction&); - template - void generic_reg16_RM16(Op, const X86::Instruction&); - template - void generic_reg32_RM32(Op, const X86::Instruction&); - template - void generic_reg8_RM8(Op, const X86::Instruction&); - - template - void generic_RM8_1(Op, const X86::Instruction&); - template - void generic_RM8_CL(Op, const X86::Instruction&); - template - void generic_RM16_1(Op, const X86::Instruction&); - template - void generic_RM16_CL(Op, const X86::Instruction&); - template - void generic_RM32_1(Op, const X86::Instruction&); - template - void generic_RM32_CL(Op, const X86::Instruction&); - - void update_code_cache(); - - void write_segment_register(X86::SegmentRegister, ValueWithShadow); - - Emulator& m_emulator; - SoftFPU m_fpu; - SoftVPU m_vpu; - - ValueWithShadow m_gpr[8]; - - u16 m_segment[8] { 0 }; - u32 m_eflags { 0 }; - - bool m_flags_tainted { false }; - - u32 m_eip { 0 }; - u32 m_base_eip { 0 }; - - Region* m_cached_code_region { nullptr }; - u8* m_cached_code_base_ptr { nullptr }; -}; - -ALWAYS_INLINE u8 SoftCPU::read8() -{ - if (!m_cached_code_region || !m_cached_code_region->contains(m_eip)) - update_code_cache(); - - u8 value = m_cached_code_base_ptr[m_eip - m_cached_code_region->base()]; - m_eip += 1; - return value; -} - -ALWAYS_INLINE u16 SoftCPU::read16() -{ - if (!m_cached_code_region || !m_cached_code_region->contains(m_eip)) - update_code_cache(); - - u16 value; - ByteReader::load(&m_cached_code_base_ptr[m_eip - m_cached_code_region->base()], value); - m_eip += 2; - return value; -} - -ALWAYS_INLINE u32 SoftCPU::read32() -{ - if (!m_cached_code_region || !m_cached_code_region->contains(m_eip)) - update_code_cache(); - - u32 value; - ByteReader::load(&m_cached_code_base_ptr[m_eip - m_cached_code_region->base()], value); - - m_eip += 4; - return value; -} - -ALWAYS_INLINE u64 SoftCPU::read64() -{ - if (!m_cached_code_region || !m_cached_code_region->contains(m_eip)) - update_code_cache(); - - u64 value; - ByteReader::load(&m_cached_code_base_ptr[m_eip - m_cached_code_region->base()], value); - - m_eip += 8; - return value; -} - -} diff --git a/Userland/DevTools/UserspaceEmulator/SoftFPU.cpp b/Userland/DevTools/UserspaceEmulator/SoftFPU.cpp deleted file mode 100644 index e0b74eee54..0000000000 --- a/Userland/DevTools/UserspaceEmulator/SoftFPU.cpp +++ /dev/null @@ -1,1770 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2021, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SoftFPU.h" -#include "Emulator.h" -#include "SoftCPU.h" -#include "ValueWithShadow.h" - -#include -#include -#include - -#include - -#if defined(AK_COMPILER_GCC) -# pragma GCC optimize("O3") -#endif - -#define TODO_INSN() \ - do { \ - reportln("\n=={}== Unimplemented instruction: {}\n"sv, getpid(), __FUNCTION__); \ - m_emulator.dump_backtrace(); \ - _exit(0); \ - } while (0) - -template -ALWAYS_INLINE void warn_if_uninitialized(T value_with_shadow, char const* message) -{ - if (value_with_shadow.is_uninitialized()) [[unlikely]] { - reportln("\033[31;1mWarning! Use of uninitialized value: {}\033[0m\n"sv, message); - UserspaceEmulator::Emulator::the().dump_backtrace(); - } -} - -namespace UserspaceEmulator { // NOLINT(readability-implicit-bool-conversion) 0/1 to follow spec closer - -ALWAYS_INLINE void SoftFPU::warn_if_mmx_absolute(u8 index) const -{ - if (m_reg_is_mmx[index]) [[unlikely]] { - reportln("\033[31;1mWarning! Use of an MMX register as an FPU value ({} abs)\033[0m\n"sv, index); - m_emulator.dump_backtrace(); - } -} -ALWAYS_INLINE void SoftFPU::warn_if_fpu_absolute(u8 index) const -{ - if (!m_reg_is_mmx[index]) [[unlikely]] { - reportln("\033[31;1mWarning! Use of an FPU value ({} abs) as an MMX register\033[0m\n"sv, index); - m_emulator.dump_backtrace(); - } -} - -ALWAYS_INLINE long double SoftFPU::fpu_get(u8 index) -{ - VERIFY(index < 8); - if (!fpu_is_set(index)) - fpu_set_stack_underflow(); - warn_if_mmx_absolute(index); - - u8 effective_index = (m_fpu_stack_top + index) % 8; - - return m_storage[effective_index].fp; -} -ALWAYS_INLINE void SoftFPU::fpu_set_absolute(u8 index, long double value) -{ - VERIFY(index < 8); - set_tag_from_value_absolute(index, value); - m_storage[index].fp = value; - m_reg_is_mmx[index] = false; -} -ALWAYS_INLINE void SoftFPU::fpu_set(u8 index, long double value) -{ - VERIFY(index < 8); - fpu_set_absolute((m_fpu_stack_top + index) % 8, value); -} -MMX SoftFPU::mmx_get(u8 index) const -{ - VERIFY(index < 8); - warn_if_fpu_absolute(index); - return m_storage[index].mmx; -} -void SoftFPU::mmx_set(u8 index, MMX value) -{ - m_storage[index].mmx = value; - // The high bytes are set to 0b11... to make the floating-point value NaN. - // This way we are technically able to find out if we are reading the wrong - // type, but this is still difficult, so we use our own lookup for that - m_storage[index].__high = 0xFFFFU; - m_reg_is_mmx[index] = true; -} - -ALWAYS_INLINE void SoftFPU::fpu_push(long double value) -{ - if (fpu_is_set(7)) - fpu_set_stack_overflow(); - m_fpu_stack_top = (m_fpu_stack_top - 1u) % 8; - - fpu_set(0, value); -} - -ALWAYS_INLINE long double SoftFPU::fpu_pop() -{ - warn_if_mmx_absolute(m_fpu_stack_top); - - if (!fpu_is_set(0)) - fpu_set_stack_underflow(); - - auto ret = fpu_get(0); - fpu_set_tag(0, FPU_Tag::Empty); - m_fpu_stack_top = (m_fpu_stack_top + 1u) % 8; - return ret; -} - -ALWAYS_INLINE void SoftFPU::fpu_set_exception(FPU_Exception ex) -{ - switch (ex) { - case FPU_Exception::StackFault: - m_fpu_error_stackfault = 1; - m_fpu_error_invalid = 1; // Implies InvalidOperation - break; - case FPU_Exception::InvalidOperation: - m_fpu_error_invalid = 1; - if (!m_fpu_cw.mask_invalid) - break; - return; - case FPU_Exception::DenormalizedOperand: - m_fpu_error_denorm = 1; - if (!m_fpu_cw.mask_denorm) - break; - return; - case FPU_Exception::ZeroDivide: - m_fpu_error_zero_div = 1; - if (!m_fpu_cw.mask_zero_div) - break; - return; - case FPU_Exception::Overflow: - m_fpu_error_overflow = 1; - if (!m_fpu_cw.mask_overflow) - break; - return; - case FPU_Exception::Underflow: - m_fpu_error_underflow = 1; - if (!m_fpu_cw.mask_underflow) - break; - return; - case FPU_Exception::Precision: - m_fpu_error_precision = 1; - if (!m_fpu_cw.mask_precision) - break; - return; - } - - // set exception bit - m_fpu_error_summary = 1; - - // FIXME: set traceback - // For that we need to get the currently executing instruction and - // the previous eip - - // FIXME: Call FPU Exception handler - reportln("Trying to call Exception handler from {}"sv, fpu_exception_string(ex)); - fpu_dump_env(); - m_emulator.dump_backtrace(); - TODO(); -} - -template -ALWAYS_INLINE T SoftFPU::round_checked(long double value) -{ - T result = static_cast(rintl(value)); - if (result != value) - fpu_set_exception(FPU_Exception::Precision); - if (result > value) - set_c1(1); - else - set_c1(0); - return result; -} - -template -ALWAYS_INLINE T SoftFPU::convert_checked(long double value) -{ - T result = static_cast(value); - if (auto rnd = value - result) { - if (rnd > 0) - set_c1(1); - else - set_c1(0); - fpu_set_exception(FPU_Exception::Precision); - } - return result; -} - -// Instructions - -// DATA TRANSFER -void SoftFPU::FLD_RM32(const X86::Instruction& insn) -{ - if (insn.modrm().is_register()) { - fpu_push(fpu_get(insn.modrm().register_index())); - } else { - auto new_f32 = insn.modrm().read32(m_cpu, insn); - // FIXME: Respect shadow values - fpu_push(bit_cast(new_f32.value())); - } -} -void SoftFPU::FLD_RM64(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - auto new_f64 = insn.modrm().read64(m_cpu, insn); - // FIXME: Respect shadow values - fpu_push(bit_cast(new_f64.value())); -} -void SoftFPU::FLD_RM80(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - - // long doubles can be up to 128 bits wide in memory for reasons (alignment) and only uses 80 bits of precision - // GCC uses 12 bytes in 32 bit and 16 bytes in 64 bit mode - // so in the 32 bit case we read a bit to much, but that shouldn't be an issue. - // FIXME: Respect shadow values - u128 new_f80 = insn.modrm().read128(m_cpu, insn).value(); - - fpu_push(*(long double*)new_f80.bytes().data()); -} - -void SoftFPU::FST_RM32(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - float f32 = convert_checked(fpu_get(0)); - - if (fpu_is_set(0)) - insn.modrm().write32(m_cpu, insn, shadow_wrap_as_initialized(bit_cast(f32))); - else - insn.modrm().write32(m_cpu, insn, ValueWithShadow(bit_cast(f32), 0u)); -} -void SoftFPU::FST_RM64(const X86::Instruction& insn) -{ - if (insn.modrm().is_register()) { - fpu_set(insn.modrm().register_index(), fpu_get(0)); - } else { - double f64 = convert_checked(fpu_get(0)); - if (fpu_is_set(0)) - insn.modrm().write64(m_cpu, insn, shadow_wrap_as_initialized(bit_cast(f64))); - else - insn.modrm().write64(m_cpu, insn, ValueWithShadow(bit_cast(f64), 0ULL)); - } -} - -void SoftFPU::FSTP_RM32(const X86::Instruction& insn) -{ - FST_RM32(insn); - fpu_pop(); -} -void SoftFPU::FSTP_RM64(const X86::Instruction& insn) -{ - FST_RM64(insn); - fpu_pop(); -} -void SoftFPU::FSTP_RM80(const X86::Instruction& insn) -{ - if (insn.modrm().is_register()) { - fpu_set(insn.modrm().register_index(), fpu_get(0)); - fpu_pop(); - } else { - // FIXME: Respect more shadow values - // long doubles can be up to 128 bits wide in memory for reasons (alignment) and only uses 80 bits of precision - // gcc uses 12 byte in 32 bit and 16 byte in 64 bit mode - // due to only 10 bytes being used, we just write these 10 into memory - // We have to do .bytes().data() to get around static type analysis - ValueWithShadow f80 { 0u, 0u }; - u128 value {}; - f80 = insn.modrm().read128(m_cpu, insn); - *(long double*)value.bytes().data() = fpu_pop(); - memcpy(f80.value().bytes().data(), &value, 10); // copy - f80.set_initialized(); - insn.modrm().write128(m_cpu, insn, f80); - } -} - -void SoftFPU::FILD_RM16(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - auto m16int = insn.modrm().read16(m_cpu, insn); - warn_if_uninitialized(m16int, "int16 loaded as float"); - - fpu_push(static_cast(static_cast(m16int.value()))); -} -void SoftFPU::FILD_RM32(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - auto m32int = insn.modrm().read32(m_cpu, insn); - warn_if_uninitialized(m32int, "int32 loaded as float"); - - fpu_push(static_cast(static_cast(m32int.value()))); -} -void SoftFPU::FILD_RM64(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - auto m64int = insn.modrm().read64(m_cpu, insn); - warn_if_uninitialized(m64int, "int64 loaded as float"); - - fpu_push(static_cast(static_cast(m64int.value()))); -} - -void SoftFPU::FIST_RM16(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - auto f = fpu_get(0); - set_c1(0); - auto int16 = round_checked(f); - - // FIXME: Respect shadow values - insn.modrm().write16(m_cpu, insn, shadow_wrap_as_initialized(bit_cast(int16))); -} -void SoftFPU::FIST_RM32(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - auto f = fpu_get(0); - set_c1(0); - auto int32 = round_checked(f); - // FIXME: Respect shadow values - insn.modrm().write32(m_cpu, insn, shadow_wrap_as_initialized(bit_cast(int32))); -} - -void SoftFPU::FISTP_RM16(const X86::Instruction& insn) -{ - FIST_RM16(insn); - fpu_pop(); -} -void SoftFPU::FISTP_RM32(const X86::Instruction& insn) -{ - FIST_RM32(insn); - fpu_pop(); -} -void SoftFPU::FISTP_RM64(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - auto f = fpu_pop(); - set_c1(0); - auto i64 = round_checked(f); - // FIXME: Respect shadow values - insn.modrm().write64(m_cpu, insn, shadow_wrap_as_initialized(bit_cast(i64))); -} - -void SoftFPU::FISTTP_RM16(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - set_c1(0); - i16 value = static_cast(fpu_pop()); - // FIXME: Respect shadow values - insn.modrm().write16(m_cpu, insn, shadow_wrap_as_initialized(bit_cast(value))); -} -void SoftFPU::FISTTP_RM32(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - i32 value = static_cast(fpu_pop()); - set_c1(0); - // FIXME: Respect shadow values - insn.modrm().write32(m_cpu, insn, shadow_wrap_as_initialized(bit_cast(value))); -} -void SoftFPU::FISTTP_RM64(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - set_c1(0); - i64 value = static_cast(fpu_pop()); - // FIXME: Respect shadow values - insn.modrm().write64(m_cpu, insn, shadow_wrap_as_initialized(bit_cast(value))); -} - -void SoftFPU::FBLD_M80(const X86::Instruction&) { TODO_INSN(); } -void SoftFPU::FBSTP_M80(const X86::Instruction&) { TODO_INSN(); } - -void SoftFPU::FXCH(const X86::Instruction& insn) -{ - VERIFY(insn.modrm().is_register()); - set_c1(0); - auto tmp = fpu_get(0); - fpu_set(0, fpu_get(insn.modrm().register_index())); - fpu_set(insn.modrm().register_index(), tmp); -} - -void SoftFPU::FCMOVE(const X86::Instruction& insn) -{ - VERIFY(insn.modrm().is_register()); - if (m_cpu.zf()) - fpu_set(0, fpu_get(insn.modrm().rm())); -} -void SoftFPU::FCMOVNE(const X86::Instruction& insn) -{ - VERIFY(insn.modrm().is_register()); - if (!m_cpu.zf()) - fpu_set(0, fpu_get((insn.modrm().reg_fpu()))); -} - -void SoftFPU::FCMOVB(const X86::Instruction& insn) -{ - VERIFY(insn.modrm().is_register()); - if (m_cpu.cf()) - fpu_set(0, fpu_get(insn.modrm().rm())); -} -void SoftFPU::FCMOVNB(const X86::Instruction& insn) -{ - VERIFY(insn.modrm().is_register()); - if (!m_cpu.cf()) - fpu_set(0, fpu_get(insn.modrm().rm())); -} -void SoftFPU::FCMOVBE(const X86::Instruction& insn) -{ - VERIFY(insn.modrm().is_register()); - if (m_cpu.cf() || m_cpu.zf()) - fpu_set(0, fpu_get(insn.modrm().rm())); -} -void SoftFPU::FCMOVNBE(const X86::Instruction& insn) -{ - VERIFY(insn.modrm().is_register()); - if (!(m_cpu.cf() || m_cpu.zf())) - fpu_set(0, fpu_get(insn.modrm().rm())); -} - -void SoftFPU::FCMOVU(const X86::Instruction& insn) -{ - VERIFY(insn.modrm().is_register()); - if (m_cpu.pf()) - fpu_set(0, fpu_get((insn.modrm().reg_fpu()))); -} -void SoftFPU::FCMOVNU(const X86::Instruction& insn) -{ - VERIFY(insn.modrm().is_register()); - if (!m_cpu.pf()) - fpu_set(0, fpu_get((insn.modrm().reg_fpu()))); -} - -// BASIC ARITHMETIC -void SoftFPU::FADD_RM32(const X86::Instruction& insn) -{ - // FIXME look at ::INC_foo for how mem/reg stuff is handled, and use that here too to make sure this is only called for mem32 ops - if (insn.modrm().is_register()) { - fpu_set(0, fpu_get(insn.modrm().register_index()) + fpu_get(0)); - } else { - auto new_f32 = insn.modrm().read32(m_cpu, insn); - // FIXME: Respect shadow values - auto f32 = bit_cast(new_f32.value()); - fpu_set(0, fpu_get(0) + f32); - } -} -void SoftFPU::FADD_RM64(const X86::Instruction& insn) -{ - // FIXME look at ::INC_foo for how mem/reg stuff is handled, and use that here too to make sure this is only called for mem64 ops - if (insn.modrm().is_register()) { - fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) + fpu_get(0)); - } else { - auto new_f64 = insn.modrm().read64(m_cpu, insn); - // FIXME: Respect shadow values - auto f64 = bit_cast(new_f64.value()); - fpu_set(0, fpu_get(0) + f64); - } -} -void SoftFPU::FADDP(const X86::Instruction& insn) -{ - VERIFY(insn.modrm().is_register()); - fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) + fpu_get(0)); - fpu_pop(); -} - -void SoftFPU::FIADD_RM32(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - auto m32int = (i32)insn.modrm().read32(m_cpu, insn).value(); - // FIXME: Respect shadow values - fpu_set(0, fpu_get(0) + (long double)m32int); -} -void SoftFPU::FIADD_RM16(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - auto m16int = (i16)insn.modrm().read16(m_cpu, insn).value(); - // FIXME: Respect shadow values - fpu_set(0, fpu_get(0) + (long double)m16int); -} - -void SoftFPU::FSUB_RM32(const X86::Instruction& insn) -{ - if (insn.modrm().is_register()) { - fpu_set(0, fpu_get(0) - fpu_get(insn.modrm().register_index())); - } else { - auto new_f32 = insn.modrm().read32(m_cpu, insn); - // FIXME: Respect shadow values - auto f32 = bit_cast(new_f32.value()); - fpu_set(0, fpu_get(0) - f32); - } -} -void SoftFPU::FSUB_RM64(const X86::Instruction& insn) -{ - if (insn.modrm().is_register()) { - // Note: This is FSUBR (DC E8+i FSUBR st(i) st(0)) in the spec - fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) - fpu_get(0)); - } else { - auto new_f64 = insn.modrm().read64(m_cpu, insn); - // FIXME: Respect shadow values - auto f64 = bit_cast(new_f64.value()); - fpu_set(0, fpu_get(0) - f64); - } -} -void SoftFPU::FSUBP(const X86::Instruction& insn) -{ - VERIFY(insn.modrm().is_register()); - fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) - fpu_get(0)); - fpu_pop(); -} - -void SoftFPU::FSUBR_RM32(const X86::Instruction& insn) -{ - if (insn.modrm().is_register()) { - fpu_set(0, fpu_get(insn.modrm().register_index()) - fpu_get(0)); - } else { - auto new_f32 = insn.modrm().read32(m_cpu, insn); - // FIXME: Respect shadow values - auto f32 = bit_cast(new_f32.value()); - fpu_set(0, f32 - fpu_get(0)); - } -} -void SoftFPU::FSUBR_RM64(const X86::Instruction& insn) -{ - if (insn.modrm().is_register()) { - // Note: This is FSUB (DC E0+i FSUB st(i) st(0)) in the spec - fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) - fpu_get(0)); - } else { - auto new_f64 = insn.modrm().read64(m_cpu, insn); - // FIXME: Respect shadow values - auto f64 = bit_cast(new_f64.value()); - fpu_set(0, f64 - fpu_get(0)); - } -} -void SoftFPU::FSUBRP(const X86::Instruction& insn) -{ - VERIFY(insn.modrm().is_register()); - fpu_set(insn.modrm().register_index(), fpu_get(0) - fpu_get(insn.modrm().register_index())); - fpu_pop(); -} - -void SoftFPU::FISUB_RM32(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - auto m32int = (i32)insn.modrm().read32(m_cpu, insn).value(); - // FIXME: Respect shadow values - fpu_set(0, fpu_get(0) - (long double)m32int); -} -void SoftFPU::FISUB_RM16(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - auto m16int = (i16)insn.modrm().read16(m_cpu, insn).value(); - // FIXME: Respect shadow values - fpu_set(0, fpu_get(0) - (long double)m16int); -} - -void SoftFPU::FISUBR_RM16(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - auto m16int = (i16)insn.modrm().read16(m_cpu, insn).value(); - // FIXME: Respect shadow values - fpu_set(0, (long double)m16int - fpu_get(0)); -} -void SoftFPU::FISUBR_RM32(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - auto m32int = (i32)insn.modrm().read32(m_cpu, insn).value(); - // FIXME: Respect shadow values - fpu_set(0, (long double)m32int - fpu_get(0)); -} - -void SoftFPU::FMUL_RM32(const X86::Instruction& insn) -{ - // FIXME look at ::INC_foo for how mem/reg stuff is handled, and use that here too to make sure this is only called for mem32 ops - if (insn.modrm().is_register()) { - fpu_set(0, fpu_get(0) * fpu_get(insn.modrm().register_index())); - } else { - auto new_f32 = insn.modrm().read32(m_cpu, insn); - // FIXME: Respect shadow values - auto f32 = bit_cast(new_f32.value()); - fpu_set(0, fpu_get(0) * f32); - } -} -void SoftFPU::FMUL_RM64(const X86::Instruction& insn) -{ - // FIXME look at ::INC_foo for how mem/reg stuff is handled, and use that here too to make sure this is only called for mem64 ops - if (insn.modrm().is_register()) { - fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) * fpu_get(0)); - } else { - auto new_f64 = insn.modrm().read64(m_cpu, insn); - // FIXME: Respect shadow values - auto f64 = bit_cast(new_f64.value()); - fpu_set(0, fpu_get(0) * f64); - } -} -void SoftFPU::FMULP(const X86::Instruction& insn) -{ - VERIFY(insn.modrm().is_register()); - fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) * fpu_get(0)); - fpu_pop(); -} - -void SoftFPU::FIMUL_RM32(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - auto m32int = (i32)insn.modrm().read32(m_cpu, insn).value(); - // FIXME: Respect shadow values - fpu_set(0, fpu_get(0) * m32int); -} -void SoftFPU::FIMUL_RM16(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - auto m16int = (i16)insn.modrm().read16(m_cpu, insn).value(); - // FIXME: Respect shadow values - fpu_set(0, fpu_get(0) * m16int); -} - -void SoftFPU::FDIV_RM32(const X86::Instruction& insn) -{ - if (insn.modrm().is_register()) { - fpu_set(0, fpu_get(0) / fpu_get(insn.modrm().register_index())); - } else { - auto new_f32 = insn.modrm().read32(m_cpu, insn); - // FIXME: Respect shadow values - auto f32 = bit_cast(new_f32.value()); - // FIXME: Raise IA on + infinity / +-infinity, +-0 / +-0, raise Z on finite / +-0 - fpu_set(0, fpu_get(0) / f32); - } -} -void SoftFPU::FDIV_RM64(const X86::Instruction& insn) -{ - if (insn.modrm().is_register()) { - // Note: This is FDIVR (DC F0+i FDIVR st(i) st(0)) in the spec - fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) / fpu_get(0)); - } else { - auto new_f64 = insn.modrm().read64(m_cpu, insn); - // FIXME: Respect shadow values - auto f64 = bit_cast(new_f64.value()); - // FIXME: Raise IA on + infinity / +-infinity, +-0 / +-0, raise Z on finite / +-0 - fpu_set(0, fpu_get(0) / f64); - } -} -void SoftFPU::FDIVP(const X86::Instruction& insn) -{ - VERIFY(insn.modrm().is_register()); - // FIXME: Raise IA on + infinity / +-infinity, +-0 / +-0, raise Z on finite / +-0 - fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) / fpu_get(0)); - fpu_pop(); -} - -void SoftFPU::FDIVR_RM32(const X86::Instruction& insn) -{ - if (insn.modrm().is_register()) { - fpu_set(0, fpu_get(insn.modrm().register_index()) / fpu_get(0)); - } else { - auto new_f32 = insn.modrm().read32(m_cpu, insn); - // FIXME: Respect shadow values - auto f32 = bit_cast(new_f32.value()); - // FIXME: Raise IA on + infinity / +-infinity, +-0 / +-0, raise Z on finite / +-0 - fpu_set(0, f32 / fpu_get(0)); - } -} -void SoftFPU::FDIVR_RM64(const X86::Instruction& insn) -{ - if (insn.modrm().is_register()) { - // Note: This is FDIV (DC F8+i FDIV st(i) st(0)) in the spec - fpu_set(insn.modrm().register_index(), fpu_get(insn.modrm().register_index()) / fpu_get(0)); - } else { - auto new_f64 = insn.modrm().read64(m_cpu, insn); - // FIXME: Respect shadow values - auto f64 = bit_cast(new_f64.value()); - // FIXME: Raise IA on + infinity / +-infinity, +-0 / +-0, raise Z on finite / +-0 - fpu_set(0, f64 / fpu_get(0)); - } -} -void SoftFPU::FDIVRP(const X86::Instruction& insn) -{ - VERIFY(insn.modrm().is_register()); - // FIXME: Raise IA on + infinity / +-infinity, +-0 / +-0, raise Z on finite / +-0 - fpu_set(insn.modrm().register_index(), fpu_get(0) / fpu_get(insn.modrm().register_index())); - fpu_pop(); -} - -void SoftFPU::FIDIV_RM16(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - auto m16int = (i16)insn.modrm().read16(m_cpu, insn).value(); - // FIXME: Respect shadow values - // FIXME: Raise IA on 0 / _=0, raise Z on finite / +-0 - fpu_set(0, fpu_get(0) / m16int); -} -void SoftFPU::FIDIV_RM32(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - auto m32int = (i32)insn.modrm().read32(m_cpu, insn).value(); - // FIXME: Respect shadow values - // FIXME: Raise IA on 0 / _=0, raise Z on finite / +-0 - fpu_set(0, fpu_get(0) / m32int); -} - -void SoftFPU::FIDIVR_RM16(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - auto m16int = (i16)insn.modrm().read16(m_cpu, insn).value(); - // FIXME: Respect shadow values - // FIXME: Raise IA on 0 / _=0, raise Z on finite / +-0 - fpu_set(0, m16int / fpu_get(0)); -} -void SoftFPU::FIDIVR_RM32(const X86::Instruction& insn) -{ - VERIFY(!insn.modrm().is_register()); - auto m32int = (i32)insn.modrm().read32(m_cpu, insn).value(); - // FIXME: Respect shadow values - // FIXME: Raise IA on 0 / _=0, raise Z on finite / +-0 - fpu_set(0, m32int / fpu_get(0)); -} - -void SoftFPU::FPREM(const X86::Instruction&) -{ - // FIXME: FPREM should only be able to reduce top's exponent by a maximum - // amount of 32-63 (impl-specific) - long double top = fpu_get(0); - long double one = fpu_get(1); - - int Q = static_cast(truncl(top / one)); - top = top - (one * Q); - set_c2(0); - set_c1(Q & 1); - set_c3((Q >> 1) & 1); - set_c0((Q >> 2) & 1); - - fpu_set(0, top); -} -void SoftFPU::FPREM1(const X86::Instruction&) -{ - // FIXME: FPREM1 should only be able to reduce top's exponent by a maximum - // amount of 32-63 (impl-specific) - long double top = fpu_get(0); - long double one = fpu_get(1); - - int Q = static_cast(roundl(top / one)); - top = top - (one * Q); - set_c2(0); - set_c1(Q & 1); - set_c3((Q >> 1) & 1); - set_c0((Q >> 2) & 1); - - fpu_set(0, top); -} -void SoftFPU::FABS(const X86::Instruction&) -{ - set_c1(0); - fpu_set(0, __builtin_fabsl(fpu_get(0))); -} -void SoftFPU::FCHS(const X86::Instruction&) -{ - set_c1(0); - fpu_set(0, -fpu_get(0)); -} - -void SoftFPU::FRNDINT(const X86::Instruction&) -{ - // FIXME: Raise #IA #D - auto res = round_checked(fpu_get(0)); - fpu_set(0, res); -} - -void SoftFPU::FSCALE(const X86::Instruction&) -{ - // FIXME: Raise #IA #D #U #O #P - fpu_set(0, fpu_get(0) * exp2l(truncl(fpu_get(1)))); -} - -void SoftFPU::FSQRT(const X86::Instruction&) -{ - // FIXME: Raise #IA #D #P - if (fpu_get(0) < 0) - fpu_set_exception(FPU_Exception::InvalidOperation); - fpu_set(0, sqrtl(fpu_get(0))); -} - -void SoftFPU::FXTRACT(const X86::Instruction&) { TODO_INSN(); } - -// COMPARISON - -// FIXME: there may be an implicit argument, how is this conveyed by the insn -void SoftFPU::FCOM_RM32(const X86::Instruction&) { TODO_INSN(); } -void SoftFPU::FCOM_RM64(const X86::Instruction&) { TODO_INSN(); } -void SoftFPU::FCOMP_RM32(const X86::Instruction&) { TODO_INSN(); } -void SoftFPU::FCOMP_RM64(const X86::Instruction&) { TODO_INSN(); } -void SoftFPU::FCOMPP(const X86::Instruction&) -{ - if (fpu_isnan(0) || fpu_isnan(1)) { - fpu_set_exception(FPU_Exception::InvalidOperation); - if (m_fpu_cw.mask_invalid) - fpu_set_unordered(); - } else { - set_c2(0); - set_c0(fpu_get(0) < fpu_get(1)); - set_c3(fpu_get(0) == fpu_get(1)); - } - fpu_pop(); - fpu_pop(); -} - -void SoftFPU::FUCOM(const X86::Instruction&) { TODO_INSN(); } // Needs QNaN detection -void SoftFPU::FUCOMP(const X86::Instruction&) { TODO_INSN(); } -void SoftFPU::FUCOMPP(const X86::Instruction&) { TODO_INSN(); } - -void SoftFPU::FICOM_RM16(const X86::Instruction& insn) -{ - // FIXME: Check for denormals - VERIFY(insn.modrm().is_register()); - auto val_shd = insn.modrm().read16(m_cpu, insn); - warn_if_uninitialized(val_shd, "int16 compare to float"); - auto val = static_cast(val_shd.value()); - if (fpu_isnan(0)) { - fpu_set_unordered(); - } else { - set_c0(fpu_get(0) < val); - set_c2(0); - set_c3(fpu_get(0) == val); - } - set_c1(0); -} -void SoftFPU::FICOM_RM32(const X86::Instruction& insn) -{ - // FIXME: Check for denormals - VERIFY(insn.modrm().is_register()); - auto val_shd = insn.modrm().read32(m_cpu, insn); - warn_if_uninitialized(val_shd, "int32 compare to float"); - auto val = static_cast(val_shd.value()); - if (fpu_isnan(0)) { - fpu_set_unordered(); - } else { - set_c0(fpu_get(0) < val); - set_c2(0); - set_c3(fpu_get(0) == val); - } - set_c1(0); -} -void SoftFPU::FICOMP_RM16(const X86::Instruction& insn) -{ - FICOM_RM16(insn); - fpu_pop(); -} -void SoftFPU::FICOMP_RM32(const X86::Instruction& insn) -{ - FICOM_RM32(insn); - fpu_pop(); -} - -void SoftFPU::FCOMI(const X86::Instruction& insn) -{ - auto i = insn.modrm().rm(); - // FIXME: QNaN / exception handling. - set_c0(0); - if (isnan(fpu_get(0)) || isnan(fpu_get(1))) { - fpu_set_exception(FPU_Exception::InvalidOperation); - m_cpu.set_zf(1); - m_cpu.set_pf(1); - m_cpu.set_cf(1); - } else { - m_cpu.set_zf(fpu_get(0) == fpu_get(i)); - m_cpu.set_pf(false); - m_cpu.set_cf(fpu_get(0) < fpu_get(i)); - } - if (!fpu_is_set(1)) - fpu_set_exception(FPU_Exception::Underflow); - - m_cpu.set_of(false); - m_cpu.set_af(false); - m_cpu.set_sf(false); - - // FIXME: Taint should be based on ST(0) and ST(i) - m_cpu.m_flags_tainted = false; -} -void SoftFPU::FCOMIP(const X86::Instruction& insn) -{ - FCOMI(insn); - fpu_pop(); -} - -void SoftFPU::FUCOMI(const X86::Instruction& insn) -{ - auto i = insn.modrm().rm(); - // FIXME: Unordered comparison checks. - // FIXME: QNaN / exception handling. - set_c1(0); - if (fpu_isnan(0) || fpu_isnan(i)) { - m_cpu.set_zf(true); - m_cpu.set_pf(true); - m_cpu.set_cf(true); - } else { - m_cpu.set_zf(fpu_get(0) == fpu_get(i)); - m_cpu.set_pf(false); - m_cpu.set_cf(fpu_get(0) < fpu_get(i)); - } - m_cpu.set_of(false); - m_cpu.set_af(false); - m_cpu.set_sf(false); - - // FIXME: Taint should be based on ST(0) and ST(i) - m_cpu.m_flags_tainted = false; -} -void SoftFPU::FUCOMIP(const X86::Instruction& insn) -{ - FUCOMI(insn); - fpu_pop(); -} - -void SoftFPU::FTST(const X86::Instruction&) -{ - // FIXME: maybe check for denormal - set_c1(0); - if (fpu_isnan(0)) - // raise #IA? - fpu_set_unordered(); - else { - set_c0(fpu_get(0) < 0.); - set_c2(0); - set_c3(fpu_get(0) == 0.); - } -} -void SoftFPU::FXAM(const X86::Instruction&) -{ - if (m_reg_is_mmx[m_fpu_stack_top]) { - // technically a subset of NaN/INF, with the Tag set to valid, - // but we have our own helper for this - set_c0(0); - set_c2(0); - set_c3(0); - } else { - switch (fpu_get_tag(0)) { - case FPU_Tag::Valid: - set_c0(0); - set_c2(1); - set_c3(0); - break; - case FPU_Tag::Zero: - set_c0(1); - set_c2(0); - set_c3(0); - break; - case FPU_Tag::Special: - if (isinf(fpu_get(0))) { - set_c0(1); - set_c2(1); - set_c3(0); - } else if (isnan(fpu_get(0))) { - set_c0(1); - set_c2(0); - set_c3(0); - } else { - // denormalized - set_c0(0); - set_c2(1); - set_c3(1); - } - break; - case FPU_Tag::Empty: - set_c0(1); - set_c2(0); - set_c3(1); - break; - default: - VERIFY_NOT_REACHED(); - } - } - set_c1(signbit(fpu_get(0))); -} - -// TRANSCENDENTAL -void SoftFPU::FSIN(const X86::Instruction&) -{ - // FIXME: Raise #IA #D #P - // FIXME: Set C1 on when result was rounded up, cleared otherwise - // FIXME: Set C2 to 1 if ST(0) is outside range of -2^63 to +2^63; else set to 0 - // ST(0) shall remain unchanged in this case - fpu_set(0, sinl(fpu_get(0))); -} -void SoftFPU::FCOS(const X86::Instruction&) -{ - // FIXME: Raise #IA #D #P - // FIXME: Set C1 on when result was rounded up, cleared otherwise - // FIXME: Set C2 to 1 if ST(0) is outside range of -2^63 to +2^63; else set to 0 - // ST(0) shall remain unchanged in this case - fpu_set(0, cosl(fpu_get(0))); -} -void SoftFPU::FSINCOS(const X86::Instruction&) -{ - // FIXME: Raise #IA #D #P - // FIXME: Set C1 on when result was rounded up, cleared otherwise - // FIXME: Set C2 to 1 if ST(0) is outside range of -2^63 to +2^63; else set to 0 - // ST(0) shall remain unchanged in this case - long double sin = sinl(fpu_get(0)); - long double cos = cosl(fpu_get(0)); - fpu_set(0, sin); - fpu_push(cos); -} - -void SoftFPU::FPTAN(const X86::Instruction&) -{ - // FIXME: Raise #IA #D #U #P - // FIXME: Set C1 on when result was rounded up, cleared otherwise - // FIXME: Set C2 to 1 if ST(0) is outside range of -2^63 to +2^63; else set to 0 - // ST(0) shall remain unchanged in this case - fpu_set(0, tanl(fpu_get(0))); - fpu_push(1.0f); -} -void SoftFPU::FPATAN(const X86::Instruction&) -{ - // FIXME: Raise #IA #D #U #P - // FIXME: Set C1 on when result was rounded up, cleared otherwise - // Note: Not implemented 80287 quirk: - // Restriction to 0 ≤ |ST(1)| < |ST(0)| < +∞ - fpu_set(1, atan2l(fpu_get(1), fpu_get(0))); - fpu_pop(); -} - -void SoftFPU::F2XM1(const X86::Instruction&) -{ - // FIXME: Raise #IA #D #U #P - // FIXME: Set C1 on when result was rounded up, cleared otherwise - // FIXME: Validate ST(0) is in range –1.0 to +1.0 - auto val = fpu_get(0); - fpu_set(0, exp2(val) - 1.0l); -} -void SoftFPU::FYL2X(const X86::Instruction&) -{ - // FIXME: Set C1 on when result was rounded up, cleared otherwise - // FIXME: Raise #IA #D #U #O #P - auto x = fpu_get(0); - auto y = fpu_get(1); - if (x < 0. && !isinf(x)) { - fpu_set_exception(FPU_Exception::InvalidOperation); - // FIXME: Spec does not say what to do here.... - // So lets just ask libm.... - fpu_set(1, y * log2l(x)); - } else if (x == 0.) { - if (y == 0) - fpu_set_exception(FPU_Exception::InvalidOperation); - fpu_set_exception(FPU_Exception::ZeroDivide); - fpu_set(1, INFINITY * (signbit(y) ? 1 : -1)); - } else { - fpu_set(1, y * log2l(x)); - } - fpu_pop(); -} -void SoftFPU::FYL2XP1(const X86::Instruction&) -{ - // FIXME: Raise #IA #O #U #P #D - auto x = fpu_get(0); - auto y = fpu_get(1); - if (x == 0 && isinf(y)) - fpu_set_exception(FPU_Exception::InvalidOperation); - - fpu_set(1, (y * log2l(x + 1.0l))); - fpu_pop(); -} - -// LOAD CONSTANT -void SoftFPU::FLD1(const X86::Instruction&) -{ - set_c1(0); - fpu_push(1.0l); -} -void SoftFPU::FLDZ(const X86::Instruction&) -{ - set_c1(0); - fpu_push(0.0l); -} -void SoftFPU::FLDPI(const X86::Instruction&) -{ - set_c1(0); - fpu_push(M_PIl); -} -void SoftFPU::FLDL2E(const X86::Instruction&) -{ - set_c1(0); - fpu_push(M_LOG2El); -} -void SoftFPU::FLDLN2(const X86::Instruction&) -{ - set_c1(0); - fpu_push(M_LN2l); -} -void SoftFPU::FLDL2T(const X86::Instruction&) -{ - set_c1(0); - fpu_push(log2l(10.0l)); -} -void SoftFPU::FLDLG2(const X86::Instruction&) -{ - set_c1(0); - fpu_push(log10l(2.0l)); -} - -// CONTROL -void SoftFPU::FINCSTP(const X86::Instruction&) -{ - m_fpu_stack_top = (m_fpu_stack_top + 1u) % 8u; - set_c1(0); -} -void SoftFPU::FDECSTP(const X86::Instruction&) -{ - m_fpu_stack_top = (m_fpu_stack_top - 1u) % 8u; - set_c1(0); -} - -void SoftFPU::FFREE(const X86::Instruction& insn) -{ - fpu_set_tag(insn.modrm().reg_fpu(), FPU_Tag::Empty); -} -void SoftFPU::FFREEP(const X86::Instruction& insn) -{ - FFREE(insn); - fpu_pop(); -} - -void SoftFPU::FNINIT(const X86::Instruction&) -{ - m_fpu_cw.cw = 0x037F; - m_fpu_sw = 0; - m_fpu_tw = 0xFFFF; - - m_fpu_ip = 0; - m_fpu_cs = 0; - - m_fpu_dp = 0; - m_fpu_ds = 0; - - m_fpu_iop = 0; -} -void SoftFPU::FNCLEX(const X86::Instruction&) -{ - m_fpu_error_invalid = 0; - m_fpu_error_denorm = 0; - m_fpu_error_zero_div = 0; - m_fpu_error_overflow = 0; - m_fpu_error_underflow = 0; - m_fpu_error_precision = 0; - m_fpu_error_stackfault = 0; - m_fpu_busy = 0; -} - -void SoftFPU::FNSTCW(const X86::Instruction& insn) -{ - insn.modrm().write16(m_cpu, insn, shadow_wrap_as_initialized(m_fpu_cw.cw)); -} -void SoftFPU::FLDCW(const X86::Instruction& insn) -{ - m_fpu_cw.cw = insn.modrm().read16(m_cpu, insn).value(); - - // Just let the host's x87 handle the rounding for us - // We do not want to accedentally raise an FP-Exception on the host, so we - // mask all exceptions - AK::X87ControlWord temp = m_fpu_cw; - temp.mask_invalid = 1; - temp.mask_denorm = 1; - temp.mask_zero_div = 1; - temp.mask_overflow = 1; - temp.mask_underflow = 1; - temp.mask_precision = 1; - AK::set_cw_x87(temp); -} - -void SoftFPU::FNSTENV(const X86::Instruction& insn) -{ - // Assuming we are always in Protected mode - // FIXME: 16-bit Format - - // 32-bit Format - /* 31--------------16---------------0 - * | | CW | 0 - * +----------------+---------------+ - * | | SW | 4 - * +----------------+---------------+ - * | | TW | 8 - * +----------------+---------------+ - * | FIP | 12 - * +----+-----------+---------------+ - * |0000|fpuOp[10:0]| FIP_sel | 16 - * +----+-----------+---------------+ - * | FDP | 20 - * +----------------+---------------+ - * | | FDP_ds | 24 - * +----------------|---------------+ - * */ - - auto address = insn.modrm().resolve(m_cpu, insn); - - m_cpu.write_memory16(address, shadow_wrap_as_initialized(m_fpu_cw.cw)); - address.set_offset(address.offset() + 4); - m_cpu.write_memory16(address, shadow_wrap_as_initialized(m_fpu_sw)); - address.set_offset(address.offset() + 4); - m_cpu.write_memory16(address, shadow_wrap_as_initialized(m_fpu_tw)); - address.set_offset(address.offset() + 4); - m_cpu.write_memory32(address, shadow_wrap_as_initialized(m_fpu_ip)); - address.set_offset(address.offset() + 4); - m_cpu.write_memory16(address, shadow_wrap_as_initialized(m_fpu_cs)); - address.set_offset(address.offset() + 2); - m_cpu.write_memory16(address, shadow_wrap_as_initialized(m_fpu_iop & 0x3FFU)); - address.set_offset(address.offset() + 2); - m_cpu.write_memory32(address, shadow_wrap_as_initialized(m_fpu_dp)); - address.set_offset(address.offset() + 4); - m_cpu.write_memory16(address, shadow_wrap_as_initialized(m_fpu_ds)); -} -void SoftFPU::FLDENV(const X86::Instruction& insn) -{ - // Assuming we are always in Protected mode - // FIXME: 16-bit Format - auto address = insn.modrm().resolve(m_cpu, insn); - - // FIXME: Shadow Values - m_fpu_cw.cw = m_cpu.read_memory16(address).value(); - // See note in FLDCW - AK::X87ControlWord temp = m_fpu_cw; - temp.mask_invalid = 1; - temp.mask_denorm = 1; - temp.mask_zero_div = 1; - temp.mask_overflow = 1; - temp.mask_underflow = 1; - temp.mask_precision = 1; - AK::set_cw_x87(temp); - - address.set_offset(address.offset() + 4); - m_fpu_sw = m_cpu.read_memory16(address).value(); - address.set_offset(address.offset() + 4); - m_fpu_tw = m_cpu.read_memory16(address).value(); - address.set_offset(address.offset() + 4); - m_fpu_ip = m_cpu.read_memory32(address).value(); - address.set_offset(address.offset() + 4); - m_fpu_cs = m_cpu.read_memory16(address).value(); - address.set_offset(address.offset() + 2); - m_fpu_iop = m_cpu.read_memory16(address).value(); - address.set_offset(address.offset() + 2); - m_fpu_dp = m_cpu.read_memory32(address).value(); - address.set_offset(address.offset() + 4); - m_fpu_ds = m_cpu.read_memory16(address).value(); -} - -void SoftFPU::FNSAVE(const X86::Instruction& insn) -{ - FNSTENV(insn); - - auto address = insn.modrm().resolve(m_cpu, insn); - address.set_offset(address.offset() + 28); // size of the ENV - - // write fpu-stack to memory - u8 raw_data[80]; - for (int i = 0; i < 8; ++i) { - memcpy(raw_data + 10 * i, &m_storage[i], 10); - } - for (int i = 0; i < 5; ++i) { - // FIXME: Shadow Value - m_cpu.write_memory128(address, shadow_wrap_as_initialized(((u128*)raw_data)[i])); - address.set_offset(address.offset() + 16); - } - - FNINIT(insn); -} -void SoftFPU::FRSTOR(const X86::Instruction& insn) -{ - FLDENV(insn); - - auto address = insn.modrm().resolve(m_cpu, insn); - address.set_offset(address.offset() + 28); // size of the ENV - - // read fpu-stack from memory - u8 raw_data[80]; - for (int i = 0; i < 5; ++i) { - // FIXME: Shadow Value - ((u128*)raw_data)[i] = m_cpu.read_memory128(address).value(); - address.set_offset(address.offset() + 16); - } - for (int i = 0; i < 8; ++i) { - memcpy(&m_storage[i], raw_data + 10 * i, 10); - } - - memset(m_reg_is_mmx, 0, sizeof(m_reg_is_mmx)); -} - -void SoftFPU::FNSTSW(const X86::Instruction& insn) -{ - insn.modrm().write16(m_cpu, insn, shadow_wrap_as_initialized(m_fpu_sw)); -} -void SoftFPU::FNSTSW_AX(const X86::Instruction&) -{ - m_cpu.set_ax(shadow_wrap_as_initialized(m_fpu_sw)); -} -// FIXME: FWAIT -void SoftFPU::FNOP(const X86::Instruction&) { } - -// DO NOTHING? -void SoftFPU::FNENI(const X86::Instruction&) { TODO_INSN(); } -void SoftFPU::FNDISI(const X86::Instruction&) { TODO_INSN(); } -void SoftFPU::FNSETPM(const X86::Instruction&) { TODO_INSN(); } - -// MMX -// helpers -#define LOAD_MM_MM64M() \ - VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */ \ - MMX mm; \ - MMX mm64m; \ - if (insn.modrm().mod() == 0b11) { /* 0b11 signals a register */ \ - mm64m = mmx_get(insn.modrm().rm()); \ - } else { \ - auto temp = insn.modrm().read64(m_cpu, insn); \ - warn_if_uninitialized(temp, "Read of uninitialized Memory as Packed integer"); \ - mm64m.raw = temp.value(); \ - } \ - mm = mmx_get(insn.modrm().reg()) - -#define MMX_intrinsic(intrinsic, res_type, actor_type) \ - LOAD_MM_MM64M(); \ - mm.res_type = __builtin_ia32_##intrinsic(mm.actor_type, mm64m.actor_type); \ - mmx_set(insn.modrm().reg(), mm); \ - mmx_common(); - -// ARITHMETIC -void SoftFPU::PADDB_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - mm.v8 += mm64m.v8; - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PADDW_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - mm.v16 += mm64m.v16; - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PADDD_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - mm.v32 += mm64m.v32; - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PADDSB_mm1_mm2m64(const X86::Instruction& insn) -{ - MMX_intrinsic(paddsb, v8, v8); -} -void SoftFPU::PADDSW_mm1_mm2m64(const X86::Instruction& insn) -{ - MMX_intrinsic(paddsw, v16, v16); -} -void SoftFPU::PADDUSB_mm1_mm2m64(const X86::Instruction& insn) -{ - MMX_intrinsic(paddusb, v8, v8); -} -void SoftFPU::PADDUSW_mm1_mm2m64(const X86::Instruction& insn) -{ - MMX_intrinsic(paddusw, v16, v16); -} - -void SoftFPU::PSUBB_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - mm.v8 -= mm64m.v8; - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PSUBW_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - mm.v16 -= mm64m.v16; - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PSUBD_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - mm.v32 -= mm64m.v32; - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PSUBSB_mm1_mm2m64(const X86::Instruction& insn) -{ - MMX_intrinsic(psubsb, v8, v8); -} -void SoftFPU::PSUBSW_mm1_mm2m64(const X86::Instruction& insn) -{ - MMX_intrinsic(psubsw, v16, v16); -} -void SoftFPU::PSUBUSB_mm1_mm2m64(const X86::Instruction& insn) -{ - MMX_intrinsic(psubusb, v8, v8); -} -void SoftFPU::PSUBUSW_mm1_mm2m64(const X86::Instruction& insn) -{ - MMX_intrinsic(psubusw, v16, v16); -} - -void SoftFPU::PMULHW_mm1_mm2m64(const X86::Instruction& insn) -{ - MMX_intrinsic(pmulhw, v16, v16); -} -void SoftFPU::PMULLW_mm1_mm2m64(const X86::Instruction& insn) -{ - MMX_intrinsic(pmullw, v16, v16); -} - -void SoftFPU::PMADDWD_mm1_mm2m64(const X86::Instruction& insn) -{ - MMX_intrinsic(pmaddwd, v32, v16); -} - -// COMPARISON -void SoftFPU::PCMPEQB_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - - mm.v8 = mm.v8 == mm64m.v8; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PCMPEQW_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - - mm.v16 = mm.v16 == mm64m.v16; - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PCMPEQD_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - - mm.v32 = mm.v32 == mm64m.v32; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} - -void SoftFPU::PCMPGTB_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - - mm.v8 = mm.v8 > mm64m.v8; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PCMPGTW_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - - mm.v16 = mm.v16 > mm64m.v16; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PCMPGTD_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - - mm.v32 = mm.v32 > mm64m.v32; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} - -// CONVERSION -void SoftFPU::PACKSSDW_mm1_mm2m64(const X86::Instruction& insn) -{ - MMX_intrinsic(packssdw, v16, v32); -} -void SoftFPU::PACKSSWB_mm1_mm2m64(const X86::Instruction& insn) -{ - MMX_intrinsic(packsswb, v8, v16); -} -void SoftFPU::PACKUSWB_mm1_mm2m64(const X86::Instruction& insn) -{ - MMX_intrinsic(packuswb, v8, v16); -} - -// UNPACK -void SoftFPU::PUNPCKHBW_mm1_mm2m64(const X86::Instruction& insn) -{ - MMX_intrinsic(punpckhbw, v8, v8); -} - -void SoftFPU::PUNPCKHWD_mm1_mm2m64(const X86::Instruction& insn) -{ - MMX_intrinsic(punpckhwd, v16, v16); -} -void SoftFPU::PUNPCKHDQ_mm1_mm2m64(const X86::Instruction& insn) -{ - MMX_intrinsic(punpckhdq, v32, v32); -} - -void SoftFPU::PUNPCKLBW_mm1_mm2m32(const X86::Instruction& insn) -{ - MMX_intrinsic(punpcklbw, v8, v8); -} -void SoftFPU::PUNPCKLWD_mm1_mm2m32(const X86::Instruction& insn) -{ - MMX_intrinsic(punpcklwd, v16, v16); -} -void SoftFPU::PUNPCKLDQ_mm1_mm2m32(const X86::Instruction& insn) -{ - MMX_intrinsic(punpckldq, v32, v32); -} - -// LOGICAL -void SoftFPU::PAND_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - - mm.raw &= mm64m.raw; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PANDN_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - - mm.raw &= ~mm64m.raw; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::POR_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - - mm.raw |= mm64m.raw; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PXOR_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - - mm.raw ^= mm64m.raw; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} - -// SHIFT -void SoftFPU::PSLLW_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - - mm.v16 <<= mm64m.v16; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PSLLW_mm1_imm8(const X86::Instruction& insn) -{ - VERIFY(!insn.has_operand_size_override_prefix()); // SSE2 - u8 imm = insn.imm8(); - MMX mm = mmx_get(insn.modrm().reg()); - - mm.v16 <<= imm; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PSLLD_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - - mm.v32 <<= mm64m.v32; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PSLLD_mm1_imm8(const X86::Instruction& insn) -{ - VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */ - u8 imm = insn.imm8(); - MMX mm = mmx_get(insn.modrm().reg()); - - mm.v32 <<= imm; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PSLLQ_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - - mm.raw <<= mm64m.raw; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PSLLQ_mm1_imm8(const X86::Instruction& insn) -{ - VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */ - u8 imm = insn.imm8(); - MMX mm = mmx_get(insn.modrm().reg()); - - mm.raw <<= imm; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PSRAW_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - - mm.v16 >>= mm64m.v16; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PSRAW_mm1_imm8(const X86::Instruction& insn) -{ - VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */ - u8 imm = insn.imm8(); - MMX mm = mmx_get(insn.modrm().reg()); - - mm.v16 >>= imm; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PSRAD_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - - mm.v32 >>= mm64m.v32; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PSRAD_mm1_imm8(const X86::Instruction& insn) -{ - VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */ - u8 imm = insn.imm8(); - MMX mm = mmx_get(insn.modrm().reg()); - - mm.v32 >>= imm; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PSRLW_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - - mm.v16u >>= mm64m.v16u; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PSRLW_mm1_imm8(const X86::Instruction& insn) -{ - VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */ - u8 imm = insn.imm8(); - MMX mm = mmx_get(insn.modrm().reg()); - - mm.v16u >>= imm; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PSRLD_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - - mm.v32u >>= mm64m.v32u; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PSRLD_mm1_imm8(const X86::Instruction& insn) -{ - VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */ - u8 imm = insn.imm8(); - MMX mm = mmx_get(insn.modrm().reg()); - - mm.v32u >>= imm; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PSRLQ_mm1_mm2m64(const X86::Instruction& insn) -{ - LOAD_MM_MM64M(); - - mm.raw >>= mm64m.raw; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} -void SoftFPU::PSRLQ_mm1_imm8(const X86::Instruction& insn) -{ - VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */ - u8 imm = insn.imm8(); - MMX mm = mmx_get(insn.modrm().reg()); - - mm.raw >>= imm; - - mmx_set(insn.modrm().reg(), mm); - mmx_common(); -} - -// DATA TRANSFER -void SoftFPU::MOVD_mm1_rm32(const X86::Instruction& insn) -{ - VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */ - u8 mmx_index = insn.modrm().reg(); - // FIXME:: Shadow Value - // upper half is zeroed out - mmx_set(mmx_index, { .raw = insn.modrm().read32(m_cpu, insn).value() }); - mmx_common(); -} -void SoftFPU::MOVD_rm32_mm2(const X86::Instruction& insn) -{ - VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */ - u8 mmx_index = insn.modrm().reg(); - // FIXME:: Shadow Value - insn.modrm().write32(m_cpu, insn, - shadow_wrap_as_initialized(static_cast(mmx_get(mmx_index).raw))); - mmx_common(); -} - -void SoftFPU::MOVQ_mm1_mm2m64(const X86::Instruction& insn) -{ - VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */ - // FIXME: Shadow Value - if (insn.modrm().mod() == 0b11) { - // instruction - mmx_set(insn.modrm().reg(), - mmx_get(insn.modrm().rm())); - } else { - mmx_set(insn.modrm().reg(), - { .raw = insn.modrm().read64(m_cpu, insn).value() }); - } - mmx_common(); -} -void SoftFPU::MOVQ_mm1m64_mm2(const X86::Instruction& insn) -{ - VERIFY(!insn.has_operand_size_override_prefix()); /* SSE2 */ - if (insn.modrm().mod() == 0b11) { - // instruction - mmx_set(insn.modrm().rm(), - mmx_get(insn.modrm().reg())); - } else { - // FIXME: Shadow Value - insn.modrm().write64(m_cpu, insn, - shadow_wrap_as_initialized(mmx_get(insn.modrm().reg()).raw)); - } - mmx_common(); -} -void SoftFPU::MOVQ_mm1_rm64(const X86::Instruction&) { TODO_INSN(); } // long mode -void SoftFPU::MOVQ_rm64_mm2(const X86::Instruction&) { TODO_INSN(); } // long mode - -// EMPTY MMX STATE -void SoftFPU::EMMS(const X86::Instruction&) -{ - // clear tagword - m_fpu_tw = 0xFFFF; -} - -} diff --git a/Userland/DevTools/UserspaceEmulator/SoftFPU.h b/Userland/DevTools/UserspaceEmulator/SoftFPU.h deleted file mode 100644 index 2abdcbaa93..0000000000 --- a/Userland/DevTools/UserspaceEmulator/SoftFPU.h +++ /dev/null @@ -1,587 +0,0 @@ -/* - * Copyright (c) 2021, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Report.h" -#include -#include -#include -#include -#include - -#include -#include - -namespace UserspaceEmulator { -using namespace AK::SIMD; -using AK::RoundingMode; - -class Emulator; -class SoftCPU; - -union MMX { - u64 raw; - c8x8 v8; - i16x4 v16; - i32x2 v32; - u16x4 v16u; - u32x2 v32u; -}; -static_assert(AssertSize()); - -class SoftFPU final { -public: - SoftFPU(Emulator& emulator, SoftCPU& cpu) - : m_emulator(emulator) - , m_cpu(cpu) - , m_fpu_cw { 0x037F } - { - } - - ALWAYS_INLINE bool c0() const { return m_fpu_c0; } - ALWAYS_INLINE bool c1() const { return m_fpu_c1; } - ALWAYS_INLINE bool c2() const { return m_fpu_c2; } - ALWAYS_INLINE bool c3() const { return m_fpu_c3; } - - ALWAYS_INLINE void set_c0(bool val) { m_fpu_c0 = val; } - ALWAYS_INLINE void set_c1(bool val) { m_fpu_c1 = val; } - ALWAYS_INLINE void set_c2(bool val) { m_fpu_c2 = val; } - ALWAYS_INLINE void set_c3(bool val) { m_fpu_c3 = val; } - - long double fpu_get(u8 index); - - void fpu_push(long double value); - long double fpu_pop(); - void fpu_set_absolute(u8 index, long double value); - void fpu_set(u8 index, long double value); - - MMX mmx_get(u8 index) const; - void mmx_set(u8 index, MMX value); - -private: - friend class SoftCPU; - - Emulator& m_emulator; - SoftCPU& m_cpu; - - enum class FPU_Exception : u8 { - InvalidOperation, - DenormalizedOperand, - ZeroDivide, - Overflow, - Underflow, - Precision, - StackFault, - }; - - enum class FPU_Tag : u8 { - Valid = 0b00, - Zero = 0b01, - Special = 0b10, - Empty = 0b11 - }; - - void fpu_dump_env() - { - reportln("Exceptions: #I:{} #D:{} #Z:{} #O:{} #U:{} #P:{} #SF:{} Summary:{}"sv, - m_fpu_error_invalid, - m_fpu_error_denorm, - m_fpu_error_zero_div, - m_fpu_error_overflow, - m_fpu_error_underflow, - m_fpu_error_precision, - m_fpu_error_stackfault, - m_fpu_error_summary); - reportln("Masks: #I:{} #D:{} #Z:{} #O:{} #U:{} #P:{}"sv, - m_fpu_cw.mask_invalid, - m_fpu_cw.mask_denorm, - m_fpu_cw.mask_zero_div, - m_fpu_cw.mask_overflow, - m_fpu_cw.mask_underflow, - m_fpu_cw.mask_precision); - reportln("C0:{} C1:{} C2:{} C3:{}"sv, c0(), c1(), c2(), c3()); - reportln("fpu-stacktop: {}"sv, m_fpu_stack_top); - reportln("fpu-stack /w stacktop (real):"sv); - for (u8 i = 0; i < 8; ++i) { - reportln("\t{} ({}): fp {} ({}), mmx {:016x}"sv, - i, (u8)((m_fpu_stack_top + i) % 8), - m_storage[(m_fpu_stack_top + i) % 8].fp, fpu_is_set(i) ? "set" : "free", - m_storage[(m_fpu_stack_top + i) % 8].mmx.raw); - } - } - - ByteString fpu_exception_string(FPU_Exception ex) - { - switch (ex) { - case FPU_Exception::StackFault: - return "Stackfault"; - case FPU_Exception::InvalidOperation: - return "Invalid Operation"; - case FPU_Exception::DenormalizedOperand: - return "Denormalized Operand"; - case FPU_Exception::ZeroDivide: - return "Divide by Zero"; - case FPU_Exception::Overflow: - return "Overflow"; - case FPU_Exception::Underflow: - return "Underflow"; - case FPU_Exception::Precision: - return "Precision"; - } - VERIFY_NOT_REACHED(); - } - - // FIXME: Technically we should check for exceptions after each insn, too, - // this might be important for FLDENV, but otherwise it should - // be fine this way - void fpu_set_exception(FPU_Exception ex); - - ALWAYS_INLINE void fpu_set_stack_overflow() - { - reportln("Stack Overflow"sv); - set_c1(1); - fpu_set_exception(FPU_Exception::StackFault); - } - - ALWAYS_INLINE void fpu_set_stack_underflow() - { - reportln("Stack Underflow"sv); - set_c1(0); - fpu_set_exception(FPU_Exception::StackFault); - } - - constexpr FPU_Tag fpu_get_tag_absolute(u8 index) const - { - switch (index) { - case 0: - return FPU_Tag(m_fpu_status_0); - case 1: - return FPU_Tag(m_fpu_status_1); - case 2: - return FPU_Tag(m_fpu_status_2); - case 3: - return FPU_Tag(m_fpu_status_3); - case 4: - return FPU_Tag(m_fpu_status_4); - case 5: - return FPU_Tag(m_fpu_status_5); - case 6: - return FPU_Tag(m_fpu_status_6); - case 7: - return FPU_Tag(m_fpu_status_7); - default: - VERIFY_NOT_REACHED(); - } - } - - constexpr FPU_Tag fpu_get_tag(u8 index) const - { - VERIFY(index < 8); - return fpu_get_tag_absolute((m_fpu_stack_top + index) % 8); - } - - ALWAYS_INLINE void fpu_set_tag_absolute(u8 index, FPU_Tag tag) - { - switch (index) { - case 0: - m_fpu_status_0 = (u8)tag; - break; - case 1: - m_fpu_status_1 = (u8)tag; - break; - case 2: - m_fpu_status_2 = (u8)tag; - break; - case 3: - m_fpu_status_3 = (u8)tag; - break; - case 4: - m_fpu_status_4 = (u8)tag; - break; - case 5: - m_fpu_status_5 = (u8)tag; - break; - case 6: - m_fpu_status_6 = (u8)tag; - break; - case 7: - m_fpu_status_7 = (u8)tag; - break; - default: - VERIFY_NOT_REACHED(); - } - } - - ALWAYS_INLINE void fpu_set_tag(u8 index, FPU_Tag tag) - { - VERIFY(index < 8); - fpu_set_tag_absolute((m_fpu_stack_top + index) % 8, tag); - } - - ALWAYS_INLINE void set_tag_from_value_absolute(u8 index, long double val) - { - switch (fpclassify(val)) { - case FP_ZERO: - fpu_set_tag_absolute(index, FPU_Tag::Zero); - break; - case FP_NAN: - case FP_INFINITE: - case FP_SUBNORMAL: - fpu_set_tag_absolute(index, FPU_Tag::Special); - break; - case FP_NORMAL: - fpu_set_tag_absolute(index, FPU_Tag::Valid); - break; - default: - VERIFY_NOT_REACHED(); - } - } - - ALWAYS_INLINE void set_tag_from_value(u8 index, long double val) - { - set_tag_from_value_absolute((m_fpu_stack_top + index) % 8, val); - } - - ALWAYS_INLINE bool fpu_isnan(u8 index) - { - return isnan(fpu_get(index)); - } - - ALWAYS_INLINE bool fpu_is_set(u8 index) const - { - return fpu_get_tag_absolute((m_fpu_stack_top + index) % 8) != FPU_Tag::Empty; - } - - ALWAYS_INLINE RoundingMode fpu_get_round_mode() const - { - return m_fpu_cw.rounding_control; - } - - template - T round_checked(long double); - - template - T convert_checked(long double); - - ALWAYS_INLINE void fpu_set_unordered() - { - set_c0(1); - set_c2(1); - set_c3(1); - } - void warn_if_mmx_absolute(u8 index) const; - void warn_if_fpu_absolute(u8 index) const; - - void mmx_common() { m_fpu_tw = 0; } - - bool m_reg_is_mmx[8] { false }; - - union { - long double fp; - struct { - MMX mmx; - u16 __high; - }; - } m_storage[8]; - - AK::X87ControlWord m_fpu_cw; - - union { - u16 m_fpu_sw { 0 }; - struct { - u16 m_fpu_error_invalid : 1; // pre | IE -> #I (#IS, #IA) - u16 m_fpu_error_denorm : 1; // pre | DE -> #D - u16 m_fpu_error_zero_div : 1; // pre | ZE -> #Z - u16 m_fpu_error_overflow : 1; // post| OE -> #O - u16 m_fpu_error_underflow : 1; // post| UE -> #U - u16 m_fpu_error_precision : 1; // post| PE -> #P - u16 m_fpu_error_stackfault : 1; // SF - u16 m_fpu_error_summary : 1; - u16 m_fpu_c0 : 1; - u16 m_fpu_c1 : 1; - u16 m_fpu_c2 : 1; - u16 m_fpu_stack_top : 3; - u16 m_fpu_c3 : 1; - u16 m_fpu_busy : 1; - }; - }; - - union { - u16 m_fpu_tw { 0xFFFF }; - struct { - u16 m_fpu_status_0 : 2; - u16 m_fpu_status_1 : 2; - u16 m_fpu_status_2 : 2; - u16 m_fpu_status_3 : 2; - u16 m_fpu_status_4 : 2; - u16 m_fpu_status_5 : 2; - u16 m_fpu_status_6 : 2; - u16 m_fpu_status_7 : 2; - }; - }; - - u32 m_fpu_ip { 0 }; - u16 m_fpu_cs { 0 }; - - u32 m_fpu_dp { 0 }; - u16 m_fpu_ds { 0 }; - - u16 m_fpu_iop { 0 }; - - // Instructions - - // DATA TRANSFER - void FLD_RM32(const X86::Instruction&); - void FLD_RM64(const X86::Instruction&); - void FLD_RM80(const X86::Instruction&); - - void FST_RM32(const X86::Instruction&); - void FST_RM64(const X86::Instruction&); - void FSTP_RM32(const X86::Instruction&); - void FSTP_RM64(const X86::Instruction&); - void FSTP_RM80(const X86::Instruction&); - - void FILD_RM32(const X86::Instruction&); - void FILD_RM16(const X86::Instruction&); - void FILD_RM64(const X86::Instruction&); - - void FIST_RM16(const X86::Instruction&); - void FIST_RM32(const X86::Instruction&); - void FISTP_RM16(const X86::Instruction&); - void FISTP_RM32(const X86::Instruction&); - void FISTP_RM64(const X86::Instruction&); - void FISTTP_RM16(const X86::Instruction&); - void FISTTP_RM32(const X86::Instruction&); - void FISTTP_RM64(const X86::Instruction&); - - void FBLD_M80(const X86::Instruction&); - void FBSTP_M80(const X86::Instruction&); - - void FXCH(const X86::Instruction&); - - void FCMOVE(const X86::Instruction&); - void FCMOVNE(const X86::Instruction&); - void FCMOVB(const X86::Instruction&); - void FCMOVBE(const X86::Instruction&); - void FCMOVNB(const X86::Instruction&); - void FCMOVNBE(const X86::Instruction&); - void FCMOVU(const X86::Instruction&); - void FCMOVNU(const X86::Instruction&); - - // BASIC ARITHMETIC - void FADD_RM32(const X86::Instruction&); - void FADD_RM64(const X86::Instruction&); - void FADDP(const X86::Instruction&); - - void FIADD_RM16(const X86::Instruction&); - void FIADD_RM32(const X86::Instruction&); - - void FSUB_RM32(const X86::Instruction&); - void FSUB_RM64(const X86::Instruction&); - void FSUBP(const X86::Instruction&); - void FSUBR_RM32(const X86::Instruction&); - void FSUBR_RM64(const X86::Instruction&); - void FSUBRP(const X86::Instruction&); - - void FISUB_RM16(const X86::Instruction&); - void FISUB_RM32(const X86::Instruction&); - void FISUBR_RM16(const X86::Instruction&); - void FISUBR_RM32(const X86::Instruction&); - - void FMUL_RM32(const X86::Instruction&); - void FMUL_RM64(const X86::Instruction&); - void FMULP(const X86::Instruction&); - - void FIMUL_RM16(const X86::Instruction&); - void FIMUL_RM32(const X86::Instruction&); - - void FDIV_RM32(const X86::Instruction&); - void FDIV_RM64(const X86::Instruction&); - void FDIVP(const X86::Instruction&); - void FDIVR_RM32(const X86::Instruction&); - void FDIVR_RM64(const X86::Instruction&); - void FDIVRP(const X86::Instruction&); - - void FIDIV_RM16(const X86::Instruction&); - void FIDIV_RM32(const X86::Instruction&); - void FIDIVR_RM16(const X86::Instruction&); - void FIDIVR_RM32(const X86::Instruction&); - - void FPREM(const X86::Instruction&); - void FPREM1(const X86::Instruction&); - - void FABS(const X86::Instruction&); - void FCHS(const X86::Instruction&); - - void FRNDINT(const X86::Instruction&); - - void FSCALE(const X86::Instruction&); - - void FSQRT(const X86::Instruction&); - - void FXTRACT(const X86::Instruction&); - - // COMPARISON - void FCOM_RM32(const X86::Instruction&); - void FCOM_RM64(const X86::Instruction&); - void FCOMP_RM32(const X86::Instruction&); - void FCOMP_RM64(const X86::Instruction&); - void FCOMPP(const X86::Instruction&); - void FCOMI(const X86::Instruction&); - void FCOMIP(const X86::Instruction&); - - void FUCOM(const X86::Instruction&); - void FUCOMP(const X86::Instruction&); - void FUCOMPP(const X86::Instruction&); - void FUCOMI(const X86::Instruction&); - void FUCOMIP(const X86::Instruction&); - - void FICOM_RM16(const X86::Instruction&); - void FICOM_RM32(const X86::Instruction&); - void FICOMP_RM16(const X86::Instruction&); - void FICOMP_RM32(const X86::Instruction&); - - void FTST(const X86::Instruction&); - void FXAM(const X86::Instruction&); - - // TRANSCENDENTAL - void FSIN(const X86::Instruction&); - void FCOS(const X86::Instruction&); - void FSINCOS(const X86::Instruction&); - void FPTAN(const X86::Instruction&); - void FPATAN(const X86::Instruction&); - - void F2XM1(const X86::Instruction&); - void FYL2X(const X86::Instruction&); - void FYL2XP1(const X86::Instruction&); - - // CONSTANT LOAD - void FLD1(const X86::Instruction&); - void FLDZ(const X86::Instruction&); - void FLDPI(const X86::Instruction&); - void FLDL2E(const X86::Instruction&); - void FLDLN2(const X86::Instruction&); - void FLDL2T(const X86::Instruction&); - void FLDLG2(const X86::Instruction&); - - // CONTROL - void FINCSTP(const X86::Instruction&); - void FDECSTP(const X86::Instruction&); - void FFREE(const X86::Instruction&); - void FFREEP(const X86::Instruction&); // undocumented - - // FIXME: Non N- versions? - void FNINIT(const X86::Instruction&); - void FNCLEX(const X86::Instruction&); - - void FNSTCW(const X86::Instruction&); - void FLDCW(const X86::Instruction&); - - void FNSTENV(const X86::Instruction&); - void FLDENV(const X86::Instruction&); - - void FNSAVE(const X86::Instruction&); - void FRSTOR(const X86::Instruction&); - - void FNSTSW(const X86::Instruction&); - void FNSTSW_AX(const X86::Instruction&); - - // FIXME: WAIT && FWAIT - void FNOP(const X86::Instruction&); - - // FPU & SIMD MANAGEMENT - // FIXME: FXSAVE && FXRSTOR - - // DO NOTHING? - // FIXME: FENI, FDISI, FSETPM - void FNENI(const X86::Instruction&); - void FNDISI(const X86::Instruction&); - void FNSETPM(const X86::Instruction&); - - // MMX - // ARITHMETIC - void PADDB_mm1_mm2m64(const X86::Instruction&); - void PADDW_mm1_mm2m64(const X86::Instruction&); - void PADDD_mm1_mm2m64(const X86::Instruction&); - void PADDSB_mm1_mm2m64(const X86::Instruction&); - void PADDSW_mm1_mm2m64(const X86::Instruction&); - void PADDUSB_mm1_mm2m64(const X86::Instruction&); - void PADDUSW_mm1_mm2m64(const X86::Instruction&); - - void PSUBB_mm1_mm2m64(const X86::Instruction&); - void PSUBW_mm1_mm2m64(const X86::Instruction&); - void PSUBD_mm1_mm2m64(const X86::Instruction&); - void PSUBSB_mm1_mm2m64(const X86::Instruction&); - void PSUBSW_mm1_mm2m64(const X86::Instruction&); - void PSUBUSB_mm1_mm2m64(const X86::Instruction&); - void PSUBUSW_mm1_mm2m64(const X86::Instruction&); - - void PMULHW_mm1_mm2m64(const X86::Instruction&); - void PMULLW_mm1_mm2m64(const X86::Instruction&); - - void PMADDWD_mm1_mm2m64(const X86::Instruction&); - - // COMPARISON - void PCMPEQB_mm1_mm2m64(const X86::Instruction&); - void PCMPEQW_mm1_mm2m64(const X86::Instruction&); - void PCMPEQD_mm1_mm2m64(const X86::Instruction&); - - void PCMPGTB_mm1_mm2m64(const X86::Instruction&); - void PCMPGTW_mm1_mm2m64(const X86::Instruction&); - void PCMPGTD_mm1_mm2m64(const X86::Instruction&); - - // CONVERSION - void PACKSSDW_mm1_mm2m64(const X86::Instruction&); - void PACKSSWB_mm1_mm2m64(const X86::Instruction&); - void PACKUSWB_mm1_mm2m64(const X86::Instruction&); - - // UNPACK - void PUNPCKHBW_mm1_mm2m64(const X86::Instruction&); - void PUNPCKHWD_mm1_mm2m64(const X86::Instruction&); - void PUNPCKHDQ_mm1_mm2m64(const X86::Instruction&); - void PUNPCKLBW_mm1_mm2m32(const X86::Instruction&); - void PUNPCKLWD_mm1_mm2m32(const X86::Instruction&); - void PUNPCKLDQ_mm1_mm2m32(const X86::Instruction&); - - // LOGICAL - void PAND_mm1_mm2m64(const X86::Instruction&); - void PANDN_mm1_mm2m64(const X86::Instruction&); - void POR_mm1_mm2m64(const X86::Instruction&); - void PXOR_mm1_mm2m64(const X86::Instruction&); - - // SHIFT - void PSLLW_mm1_mm2m64(const X86::Instruction&); - void PSLLW_mm1_imm8(const X86::Instruction&); - void PSLLD_mm1_mm2m64(const X86::Instruction&); - void PSLLD_mm1_imm8(const X86::Instruction&); - void PSLLQ_mm1_mm2m64(const X86::Instruction&); - void PSLLQ_mm1_imm8(const X86::Instruction&); - void PSRAW_mm1_mm2m64(const X86::Instruction&); - void PSRAW_mm1_imm8(const X86::Instruction&); - void PSRAD_mm1_mm2m64(const X86::Instruction&); - void PSRAD_mm1_imm8(const X86::Instruction&); - void PSRLW_mm1_mm2m64(const X86::Instruction&); - void PSRLW_mm1_imm8(const X86::Instruction&); - void PSRLD_mm1_mm2m64(const X86::Instruction&); - void PSRLD_mm1_imm8(const X86::Instruction&); - void PSRLQ_mm1_mm2m64(const X86::Instruction&); - void PSRLQ_mm1_imm8(const X86::Instruction&); - - // DATA TRANSFER - void MOVD_mm1_rm32(const X86::Instruction&); - void MOVD_rm32_mm2(const X86::Instruction&); - - void MOVQ_mm1_mm2m64(const X86::Instruction&); - void MOVQ_mm1m64_mm2(const X86::Instruction&); - void MOVQ_mm1_rm64(const X86::Instruction&); // long mode - void MOVQ_rm64_mm2(const X86::Instruction&); // long mode - - // EMPTY MMX STATE - void EMMS(const X86::Instruction&); -}; - -} diff --git a/Userland/DevTools/UserspaceEmulator/SoftMMU.cpp b/Userland/DevTools/UserspaceEmulator/SoftMMU.cpp deleted file mode 100644 index d17c62b568..0000000000 --- a/Userland/DevTools/UserspaceEmulator/SoftMMU.cpp +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SoftMMU.h" -#include "Emulator.h" -#include "MmapRegion.h" -#include "Report.h" -#include -#include -#include - -namespace UserspaceEmulator { - -SoftMMU::SoftMMU(Emulator& emulator) - : m_emulator(emulator) -{ -} - -void SoftMMU::add_region(NonnullOwnPtr region) -{ - VERIFY(!find_region({ 0x23, region->base() })); - - size_t first_page_in_region = region->base() / PAGE_SIZE; - size_t last_page_in_region = (region->base() + region->size() - 1) / PAGE_SIZE; - for (size_t page = first_page_in_region; page <= last_page_in_region; ++page) { - m_page_to_region_map[page] = region.ptr(); - } - - m_regions.append(move(region)); - quick_sort((Vector>&)m_regions, [](auto& a, auto& b) { return a->base() < b->base(); }); -} - -void SoftMMU::remove_region(Region& region) -{ - size_t first_page_in_region = region.base() / PAGE_SIZE; - for (size_t i = 0; i < ceil_div(region.size(), PAGE_SIZE); ++i) { - m_page_to_region_map[first_page_in_region + i] = nullptr; - } - - m_regions.remove_first_matching([&](auto& entry) { return entry.ptr() == ®ion; }); -} - -void SoftMMU::ensure_split_at(X86::LogicalAddress address) -{ - // FIXME: If this fails, call Emulator::dump_backtrace - VERIFY(address.selector() != 0x2b); - - u32 offset = address.offset(); - VERIFY((offset & (PAGE_SIZE - 1)) == 0); - size_t page_index = address.offset() / PAGE_SIZE; - - if (!page_index) - return; - if (m_page_to_region_map[page_index - 1] != m_page_to_region_map[page_index]) - return; - if (!m_page_to_region_map[page_index]) - return; - - // If we get here, we know that the page exists and belongs to a region, that there is - // a previous page, and that it belongs to the same region. - auto* old_region = verify_cast(m_page_to_region_map[page_index]); - - // dbgln("splitting at {:p}", address.offset()); - // dbgln(" old region: {:p}-{:p}", old_region->base(), old_region->end() - 1); - - NonnullOwnPtr new_region = old_region->split_at(VirtualAddress(offset)); - // dbgln(" new region: {:p}-{:p}", new_region->base(), new_region->end() - 1); - // dbgln(" up old region: {:p}-{:p}", old_region->base(), old_region->end() - 1); - - size_t first_page_in_region = new_region->base() / PAGE_SIZE; - size_t last_page_in_region = (new_region->base() + new_region->size() - 1) / PAGE_SIZE; - - // dbgln(" @ remapping pages {} thru {}", first_page_in_region, last_page_in_region); - - for (size_t page = first_page_in_region; page <= last_page_in_region; ++page) { - VERIFY(m_page_to_region_map[page] == old_region); - m_page_to_region_map[page] = new_region.ptr(); - } - - m_regions.append(move(new_region)); - quick_sort((Vector>&)m_regions, [](auto& a, auto& b) { return a->base() < b->base(); }); -} - -void SoftMMU::set_tls_region(NonnullOwnPtr region) -{ - VERIFY(!m_tls_region); - m_tls_region = move(region); -} - -ValueWithShadow SoftMMU::read8(X86::LogicalAddress address) -{ - auto* region = find_region(address); - if (!region) { - reportln("SoftMMU::read8: No region for @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - if (!region->is_readable()) { - reportln("SoftMMU::read8: Non-readable region @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - return region->read8(address.offset() - region->base()); -} - -ValueWithShadow SoftMMU::read16(X86::LogicalAddress address) -{ - auto* region = find_region(address); - if (!region) { - reportln("SoftMMU::read16: No region for @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - if (!region->is_readable()) { - reportln("SoftMMU::read16: Non-readable region @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - return region->read16(address.offset() - region->base()); -} - -ValueWithShadow SoftMMU::read32(X86::LogicalAddress address) -{ - auto* region = find_region(address); - if (!region) { - reportln("SoftMMU::read32: No region for @ {:04x}:{:p}"sv, address.selector(), address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - if (!region->is_readable()) { - reportln("SoftMMU::read32: Non-readable region @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - return region->read32(address.offset() - region->base()); -} - -ValueWithShadow SoftMMU::read64(X86::LogicalAddress address) -{ - auto* region = find_region(address); - if (!region) { - reportln("SoftMMU::read64: No region for @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - if (!region->is_readable()) { - reportln("SoftMMU::read64: Non-readable region @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - return region->read64(address.offset() - region->base()); -} - -ValueWithShadow SoftMMU::read128(X86::LogicalAddress address) -{ - auto* region = find_region(address); - if (!region) { - reportln("SoftMMU::read128: No region for @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - if (!region->is_readable()) { - reportln("SoftMMU::read128: Non-readable region @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - return region->read128(address.offset() - region->base()); -} - -ValueWithShadow SoftMMU::read256(X86::LogicalAddress address) -{ - auto* region = find_region(address); - if (!region) { - reportln("SoftMMU::read256: No region for @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - if (!region->is_readable()) { - reportln("SoftMMU::read256: Non-readable region @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - return region->read256(address.offset() - region->base()); -} - -void SoftMMU::write8(X86::LogicalAddress address, ValueWithShadow value) -{ - auto* region = find_region(address); - if (!region) { - reportln("SoftMMU::write8: No region for @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - if (!region->is_writable()) { - reportln("SoftMMU::write8: Non-writable region @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - region->write8(address.offset() - region->base(), value); -} - -void SoftMMU::write16(X86::LogicalAddress address, ValueWithShadow value) -{ - auto* region = find_region(address); - if (!region) { - reportln("SoftMMU::write16: No region for @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - if (!region->is_writable()) { - reportln("SoftMMU::write16: Non-writable region @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - region->write16(address.offset() - region->base(), value); -} - -void SoftMMU::write32(X86::LogicalAddress address, ValueWithShadow value) -{ - auto* region = find_region(address); - if (!region) { - reportln("SoftMMU::write32: No region for @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - if (!region->is_writable()) { - reportln("SoftMMU::write32: Non-writable region @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - region->write32(address.offset() - region->base(), value); -} - -void SoftMMU::write64(X86::LogicalAddress address, ValueWithShadow value) -{ - auto* region = find_region(address); - if (!region) { - reportln("SoftMMU::write64: No region for @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - if (!region->is_writable()) { - reportln("SoftMMU::write64: Non-writable region @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - region->write64(address.offset() - region->base(), value); -} - -void SoftMMU::write128(X86::LogicalAddress address, ValueWithShadow value) -{ - auto* region = find_region(address); - if (!region) { - reportln("SoftMMU::write128: No region for @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - if (!region->is_writable()) { - reportln("SoftMMU::write128: Non-writable region @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - region->write128(address.offset() - region->base(), value); -} - -void SoftMMU::write256(X86::LogicalAddress address, ValueWithShadow value) -{ - auto* region = find_region(address); - if (!region) { - reportln("SoftMMU::write256: No region for @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - if (!region->is_writable()) { - reportln("SoftMMU::write256: Non-writable region @ {:p}"sv, address.offset()); - m_emulator.dump_backtrace(); - TODO(); - } - - region->write256(address.offset() - region->base(), value); -} - -void SoftMMU::copy_to_vm(FlatPtr destination, void const* source, size_t size) -{ - // FIXME: We should have a way to preserve the shadow data here as well. - for (size_t i = 0; i < size; ++i) - write8({ 0x23, destination + i }, shadow_wrap_as_initialized(((u8 const*)source)[i])); -} - -void SoftMMU::copy_from_vm(void* destination, const FlatPtr source, size_t size) -{ - // FIXME: We should have a way to preserve the shadow data here as well. - for (size_t i = 0; i < size; ++i) - ((u8*)destination)[i] = read8({ 0x23, source + i }).value(); -} - -ByteBuffer SoftMMU::copy_buffer_from_vm(const FlatPtr source, size_t size) -{ - auto buffer = ByteBuffer::create_uninitialized(size).release_value_but_fixme_should_propagate_errors(); // FIXME: Handle possible OOM situation. - copy_from_vm(buffer.data(), source, size); - return buffer; -} - -bool SoftMMU::fast_fill_memory8(X86::LogicalAddress address, size_t size, ValueWithShadow value) -{ - if (!size) - return true; - auto* region = find_region(address); - if (!region) - return false; - if (!region->contains(address.offset() + size - 1)) - return false; - - if (is(*region) && static_cast(*region).is_malloc_block()) { - if (auto* tracer = m_emulator.malloc_tracer()) { - // FIXME: Add a way to audit an entire range of memory instead of looping here! - for (size_t i = 0; i < size; ++i) { - tracer->audit_write(*region, address.offset() + (i * sizeof(u8)), sizeof(u8)); - } - } - } - - size_t offset_in_region = address.offset() - region->base(); - memset(region->data() + offset_in_region, value.value(), size); - memset(region->shadow_data() + offset_in_region, value.shadow()[0], size); - return true; -} - -bool SoftMMU::fast_fill_memory32(X86::LogicalAddress address, size_t count, ValueWithShadow value) -{ - if (!count) - return true; - auto* region = find_region(address); - if (!region) - return false; - if (!region->contains(address.offset() + (count * sizeof(u32)) - 1)) - return false; - - if (is(*region) && static_cast(*region).is_malloc_block()) { - if (auto* tracer = m_emulator.malloc_tracer()) { - // FIXME: Add a way to audit an entire range of memory instead of looping here! - for (size_t i = 0; i < count; ++i) { - tracer->audit_write(*region, address.offset() + (i * sizeof(u32)), sizeof(u32)); - } - } - } - - size_t offset_in_region = address.offset() - region->base(); - fast_u32_fill((u32*)(region->data() + offset_in_region), value.value(), count); - fast_u32_fill((u32*)(region->shadow_data() + offset_in_region), value.shadow_as_value(), count); - return true; -} - -void SoftMMU::dump_backtrace() -{ - m_emulator.dump_backtrace(); -} - -} diff --git a/Userland/DevTools/UserspaceEmulator/SoftMMU.h b/Userland/DevTools/UserspaceEmulator/SoftMMU.h deleted file mode 100644 index ccd6684b59..0000000000 --- a/Userland/DevTools/UserspaceEmulator/SoftMMU.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Region.h" -#include "Report.h" -#include "ValueWithShadow.h" -#include -#include -#include -#include - -namespace UserspaceEmulator { - -class Emulator; - -class SoftMMU { -public: - explicit SoftMMU(Emulator&); - - ValueWithShadow read8(X86::LogicalAddress); - ValueWithShadow read16(X86::LogicalAddress); - ValueWithShadow read32(X86::LogicalAddress); - ValueWithShadow read64(X86::LogicalAddress); - ValueWithShadow read128(X86::LogicalAddress); - ValueWithShadow read256(X86::LogicalAddress); - - void dump_backtrace(); - - template - ValueWithShadow read(X86::LogicalAddress address) - requires(IsTriviallyConstructible) - { - auto* region = find_region(address); - if (!region) { - reportln("SoftMMU::read256: No region for @ {:p}"sv, address.offset()); - dump_backtrace(); - TODO(); - } - - if (!region->is_readable()) { - reportln("SoftMMU::read256: Non-readable region @ {:p}"sv, address.offset()); - dump_backtrace(); - TODO(); - } - - alignas(alignof(T)) u8 data[sizeof(T)]; - Array shadow; - - for (auto i = 0u; i < sizeof(T); ++i) { - auto result = region->read8(address.offset() - region->base() + i); - data[i] = result.value(); - shadow[i] = result.shadow()[0]; - } - - return { - *bit_cast(&data[0]), - shadow, - }; - } - - void write8(X86::LogicalAddress, ValueWithShadow); - void write16(X86::LogicalAddress, ValueWithShadow); - void write32(X86::LogicalAddress, ValueWithShadow); - void write64(X86::LogicalAddress, ValueWithShadow); - void write128(X86::LogicalAddress, ValueWithShadow); - void write256(X86::LogicalAddress, ValueWithShadow); - - ALWAYS_INLINE Region* find_region(X86::LogicalAddress address) - { - if (address.selector() == 0x2b) - return m_tls_region.ptr(); - - size_t page_index = address.offset() / PAGE_SIZE; - return m_page_to_region_map[page_index]; - } - - void add_region(NonnullOwnPtr); - void remove_region(Region&); - void ensure_split_at(X86::LogicalAddress); - - void set_tls_region(NonnullOwnPtr); - - bool fast_fill_memory8(X86::LogicalAddress, size_t size, ValueWithShadow); - bool fast_fill_memory32(X86::LogicalAddress, size_t size, ValueWithShadow); - - void copy_to_vm(FlatPtr destination, void const* source, size_t); - void copy_from_vm(void* destination, const FlatPtr source, size_t); - ByteBuffer copy_buffer_from_vm(const FlatPtr source, size_t); - - template - void for_each_region(Callback callback) - { - if (m_tls_region) { - if (callback(*m_tls_region) == IterationDecision::Break) - return; - } - for (auto& region : m_regions) { - if (callback(region) == IterationDecision::Break) - return; - } - } - - template - void for_each_region_of_type(Callback callback) - { - return for_each_region([callback](auto& region) { - if (!is(region)) - return IterationDecision::Continue; - return callback(static_cast(region)); - }); - } - - template - void for_regions_in(X86::LogicalAddress address, size_t size, Callback callback) - { - VERIFY(size > 0); - X86::LogicalAddress address_end = address; - address_end.set_offset(address_end.offset() + size); - ensure_split_at(address); - ensure_split_at(address_end); - - size_t first_page = address.offset() / PAGE_SIZE; - size_t last_page = (address_end.offset() - 1) / PAGE_SIZE; - Region* last_reported = nullptr; - for (size_t page = first_page; page <= last_page; ++page) { - Region* current_region = m_page_to_region_map[page]; - if (page != first_page && current_region == last_reported) - continue; - if (callback(current_region) == IterationDecision::Break) - return; - last_reported = current_region; - } - } - -private: - Emulator& m_emulator; - - Region* m_page_to_region_map[786432] = { nullptr }; - - OwnPtr m_tls_region; - Vector> m_regions; -}; - -} diff --git a/Userland/DevTools/UserspaceEmulator/SoftVPU.cpp b/Userland/DevTools/UserspaceEmulator/SoftVPU.cpp deleted file mode 100644 index 6ee75d5bb7..0000000000 --- a/Userland/DevTools/UserspaceEmulator/SoftVPU.cpp +++ /dev/null @@ -1,799 +0,0 @@ -/* - * Copyright (c) 2022, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "SoftVPU.h" -#include "SoftCPU.h" -#include - -namespace UserspaceEmulator { - -void SoftVPU::PREFETCHTNTA(X86::Instruction const&) { TODO(); } -void SoftVPU::PREFETCHT0(X86::Instruction const&) { TODO(); } -void SoftVPU::PREFETCHT1(X86::Instruction const&) { TODO(); } -void SoftVPU::PREFETCHT2(X86::Instruction const&) { TODO(); } - -void SoftVPU::LDMXCSR(X86::Instruction const& insn) -{ - // FIXME: Shadows - m_mxcsr.mxcsr = insn.modrm().read32(m_cpu, insn).value(); - - // #GP - General Protection Fault - VERIFY((m_mxcsr.mxcsr & 0xFFFF'0000) == 0); - - // Just let the host's SSE (or if not available x87) handle the rounding for us - // We do not want to accidentally raise an FP-Exception on the host, so we - // mask all exceptions -#ifdef __SSE__ - AK::MXCSR temp = m_mxcsr; - temp.invalid_operation_mask = 1; - temp.denormal_operation_mask = 1; - temp.divide_by_zero_mask = 1; - temp.overflow_mask = 1; - temp.underflow_mask = 1; - temp.precision_mask = 1; - AK::set_mxcsr(temp); -#else - // FIXME: This will mess with x87-land, because it uses the same trick, and - // Does not know of us doing this - AK::X87ControlWord cw { 0x037F }; - cw.rounding_control = m_mxcsr.rounding_control; - AK::set_cw_x87(cw); -#endif -} -void SoftVPU::STMXCSR(X86::Instruction const& insn) -{ - // FIXME: Shadows - insn.modrm().write32(m_cpu, insn, ValueWithShadow::create_initialized(m_mxcsr.mxcsr)); -} - -void SoftVPU::MOVUPS_xmm1_xmm2m128(X86::Instruction const& insn) -{ - u8 xmm1 = insn.modrm().reg(); - if (insn.modrm().is_register()) { - m_xmm[xmm1] = m_xmm[insn.modrm().rm()]; - } else { - // FIXME: Shadows - m_xmm[xmm1].ps = bit_cast(insn.modrm().read128(m_cpu, insn).value()); - } -} -void SoftVPU::MOVSS_xmm1_xmm2m32(X86::Instruction const& insn) -{ - u8 xmm1 = insn.modrm().reg(); - if (insn.modrm().is_register()) { - m_xmm[xmm1].ps[0] = m_xmm[insn.modrm().rm()].ps[0]; - } else { - // FIXME: Shadows - m_xmm[xmm1].ps[0] = bit_cast(insn.modrm().read32(m_cpu, insn).value()); - } -} -void SoftVPU::MOVUPS_xmm1m128_xmm2(X86::Instruction const& insn) -{ - u8 xmm2 = insn.modrm().reg(); - if (insn.modrm().is_register()) { - m_xmm[insn.modrm().rm()] = m_xmm[xmm2]; - } else { - // FIXME: Shadows - u128 temp = bit_cast(m_xmm[xmm2]); - insn.modrm().write128(m_cpu, insn, ValueWithShadow::create_initialized(temp)); - } -} -void SoftVPU::MOVSS_xmm1m32_xmm2(X86::Instruction const& insn) -{ - u8 xmm2 = insn.modrm().reg(); - if (insn.modrm().is_register()) { - m_xmm[insn.modrm().rm()].ps[0] = m_xmm[xmm2].ps[0]; - } else { - // FIXME: Shadows - u32 temp = bit_cast(m_xmm[xmm2].ps[0]); - insn.modrm().write32(m_cpu, insn, ValueWithShadow::create_initialized(temp)); - } -} -void SoftVPU::MOVLPS_xmm1_xmm2m64(X86::Instruction const& insn) -{ - u8 xmm1 = insn.modrm().reg(); - if (insn.modrm().is_register()) { - // Note: MOVHLPS - m_xmm[xmm1].puqw[0] = m_xmm[insn.modrm().rm()].puqw[1]; - } else { - // FIXME: Shadows - // Note: Technically we are transferring two packed floats not a quad word - m_xmm[xmm1].puqw[0] = insn.modrm().read64(m_cpu, insn).value(); - } -} -void SoftVPU::MOVLPS_m64_xmm2(X86::Instruction const& insn) -{ - u8 xmm2 = insn.modrm().reg(); - // FIXME: This might not hold true for SSE2 or later - VERIFY(!insn.modrm().is_register()); - // Note: Technically we are transferring two packed floats not a quad word - insn.modrm().write64(m_cpu, insn, ValueWithShadow::create_initialized(m_xmm[xmm2].puqw[0])); -} - -void SoftVPU::UNPCKLPS_xmm1_xmm2m128(X86::Instruction const& insn) -{ - f32x4& xmm1 = m_xmm[insn.modrm().reg()].ps; - f32x4 xmm2m128; - - if (insn.modrm().is_register()) { - xmm2m128 = m_xmm[insn.modrm().rm()].ps; - } else { - // FIXME: Shadows - xmm2m128 = bit_cast(insn.modrm().read128(m_cpu, insn).value()); - } - f32x4 dest; - if (insn.modrm().is_register()) { - xmm2m128 = m_xmm[insn.modrm().rm()].ps; - } else { - // FIXME: Shadows - xmm2m128 = bit_cast(insn.modrm().read128(m_cpu, insn).value()); - } - - dest[0] = xmm1[0]; - dest[1] = xmm2m128[0]; - dest[2] = xmm1[1]; - dest[3] = xmm2m128[1]; - - m_xmm[insn.modrm().reg()].ps = dest; -} -void SoftVPU::UNPCKHPS_xmm1_xmm2m128(X86::Instruction const& insn) -{ - f32x4 xmm1 = m_xmm[insn.modrm().reg()].ps; - f32x4 xmm2m128; - - if (insn.modrm().is_register()) { - xmm2m128 = m_xmm[insn.modrm().rm()].ps; - } else { - // FIXME: Shadows - xmm2m128 = bit_cast(insn.modrm().read128(m_cpu, insn).value()); - } - f32x4 dest; - dest[0] = xmm1[2]; - dest[1] = xmm2m128[2]; - dest[2] = xmm1[3]; - dest[3] = xmm2m128[3]; - - m_xmm[insn.modrm().reg()].ps = dest; -} - -void SoftVPU::MOVHPS_xmm1_xmm2m64(X86::Instruction const& insn) -{ - u8 xmm1 = insn.modrm().reg(); - if (insn.modrm().is_register()) { - // Note: MOVLHPS - m_xmm[xmm1].puqw[1] = m_xmm[insn.modrm().rm()].puqw[0]; - } else { - // FIXME: Shadows - // Note: Technically we are transferring two packed floats not a quad word - m_xmm[xmm1].puqw[1] = insn.modrm().read64(m_cpu, insn).value(); - } -} -void SoftVPU::MOVHPS_m64_xmm2(X86::Instruction const& insn) -{ - u8 xmm1 = insn.modrm().reg(); - VERIFY(!insn.modrm().is_register()); - // Note: Technically we are transferring two packed floats not a quad word - insn.modrm().write64(m_cpu, insn, ValueWithShadow::create_initialized(m_xmm[xmm1].puqw[1])); -} -void SoftVPU::MOVAPS_xmm1_xmm2m128(X86::Instruction const& insn) -{ - u8 xmm1 = insn.modrm().reg(); - if (insn.modrm().is_register()) { - m_xmm[xmm1] = m_xmm[insn.modrm().rm()]; - } else { - // FIXME: Alignment-check 16 - auto temp = insn.modrm().read128(m_cpu, insn); - m_xmm[xmm1].ps = bit_cast(temp.value()); - } -} -void SoftVPU::MOVAPS_xmm1m128_xmm2(X86::Instruction const& insn) -{ - u8 xmm2 = insn.modrm().reg(); - if (insn.modrm().is_register()) { - m_xmm[insn.modrm().rm()] = m_xmm[xmm2]; - } else { - // FIXME: Alignment-check 16 - u128 temp = bit_cast(m_xmm[xmm2]); - insn.modrm().write128(m_cpu, insn, ValueWithShadow::create_initialized(temp)); - } -} - -void SoftVPU::CVTPI2PS_xmm1_mm2m64(X86::Instruction const& insn) -{ - // FIXME: Raise Precision - // FIXME: Honor Rounding control - u8 xmm1 = insn.modrm().reg(); - if (insn.modrm().is_register()) { - i32x2 mm = m_cpu.mmx_get(insn.modrm().rm()).v32; - m_xmm[xmm1].ps[0] = mm[0]; - m_xmm[xmm1].ps[1] = mm[1]; - } else { - // FIXME: Shadows - i32x2 m64 = bit_cast(insn.modrm().read64(m_cpu, insn).value()); - m_xmm[xmm1].ps[0] = m64[0]; - m_xmm[xmm1].ps[1] = m64[1]; - } -} -void SoftVPU::CVTSI2SS_xmm1_rm32(X86::Instruction const& insn) -{ - // FIXME: Raise Precision - // FIXME: Shadows - // FIXME: Honor Rounding Control - m_xmm[insn.modrm().reg()].ps[0] = (i32)insn.modrm().read32(m_cpu, insn).value(); -} - -void SoftVPU::MOVNTPS_xmm1m128_xmm2(X86::Instruction const&) { TODO(); } - -void SoftVPU::CVTTPS2PI_mm1_xmm2m64(X86::Instruction const&) { TODO(); } -void SoftVPU::CVTTSS2SI_r32_xmm2m32(X86::Instruction const& insn) -{ - // FIXME: Raise Invalid, Precision - float value; - if (insn.modrm().is_register()) - value = m_xmm[insn.modrm().rm()].ps[0]; - else - value = bit_cast(insn.modrm().read32(m_cpu, insn).value()); - - m_cpu.gpr32(insn.reg32()) = ValueWithShadow::create_initialized((u32)(i32)truncf(value)); -} -void SoftVPU::CVTPS2PI_xmm1_mm2m64(X86::Instruction const&) { TODO(); } -void SoftVPU::CVTSS2SI_r32_xmm2m32(X86::Instruction const& insn) -{ - // FIXME: Raise Invalid, Precision - insn.modrm().write32(m_cpu, insn, - ValueWithShadow::create_initialized(static_cast(m_xmm[insn.modrm().reg()].ps[0]))); -} - -void SoftVPU::UCOMISS_xmm1_xmm2m32(X86::Instruction const& insn) -{ - float xmm1 = m_xmm[insn.modrm().reg()].ps[0]; - float xmm2m32; - if (insn.modrm().is_register()) - xmm2m32 = m_xmm[insn.modrm().rm()].ps[0]; - else - xmm2m32 = bit_cast(insn.modrm().read32(m_cpu, insn).value()); - // FIXME: Raise Invalid on SNaN - if (isnan(xmm1) || isnan(xmm2m32)) { - m_cpu.set_zf(true); - m_cpu.set_pf(true); - m_cpu.set_cf(true); - } else { - m_cpu.set_zf(xmm1 == xmm2m32); - m_cpu.set_pf(false); - m_cpu.set_cf(xmm1 < xmm2m32); - } - m_cpu.set_of(false); - m_cpu.set_af(false); - m_cpu.set_sf(false); -} -void SoftVPU::COMISS_xmm1_xmm2m32(X86::Instruction const& insn) -{ - // FIXME: Raise on QNaN - UCOMISS_xmm1_xmm2m32(insn); -} - -void SoftVPU::MOVMSKPS_reg_xmm(X86::Instruction const& insn) -{ - VERIFY(insn.modrm().is_register()); - u8 mask = 0; - f32x4 xmm = m_xmm[insn.modrm().rm()].ps; - mask |= signbit(xmm[0]) << 0; - mask |= signbit(xmm[1]) << 1; - mask |= signbit(xmm[2]) << 2; - mask |= signbit(xmm[3]) << 3; - - m_cpu.gpr32(insn.reg32()) = ValueWithShadow::create_initialized(mask); -} - -void SoftVPU::SQRTPS_xmm1_xmm2m128(X86::Instruction const& insn) -{ - // FIXME: Raise Invalid, Precision, Denormal - f32x4 xmm2m128; - - if (insn.modrm().is_register()) { - xmm2m128 = m_xmm[insn.modrm().rm()].ps; - } else { - // FIXME: Shadows - xmm2m128 = bit_cast(insn.modrm().read128(m_cpu, insn).value()); - } - - m_xmm[insn.modrm().reg()].ps = sqrt(xmm2m128); -} -void SoftVPU::SQRTSS_xmm1_xmm2m32(X86::Instruction const& insn) -{ - // FIXME: Raise Invalid, Precision, Denormal - float xmm2m32; - - if (insn.modrm().is_register()) { - xmm2m32 = m_xmm[insn.modrm().rm()].ps[0]; - } else { - // FIXME: Shadows - xmm2m32 = bit_cast(insn.modrm().read32(m_cpu, insn).value()); - } - - m_xmm[insn.modrm().reg()].ps[0] = AK::sqrt(xmm2m32); -} -void SoftVPU::RSQRTPS_xmm1_xmm2m128(X86::Instruction const& insn) -{ - f32x4 xmm2m128; - if (insn.modrm().is_register()) { - xmm2m128 = m_xmm[insn.modrm().rm()].ps; - } else { - // FIXME: Shadows - xmm2m128 = bit_cast(insn.modrm().read128(m_cpu, insn).value()); - } - - m_xmm[insn.modrm().reg()].ps = rsqrt(xmm2m128); -} -void SoftVPU::RSQRTSS_xmm1_xmm2m32(X86::Instruction const& insn) -{ - float xmm2m32; - if (insn.modrm().is_register()) { - xmm2m32 = m_xmm[insn.modrm().rm()].ps[0]; - } else { - // FIXME: Shadows - xmm2m32 = bit_cast(insn.modrm().read32(m_cpu, insn).value()); - } - - m_xmm[insn.modrm().reg()].ps[0] = AK::rsqrt(xmm2m32); -} - -void SoftVPU::RCPPS_xmm1_xmm2m128(X86::Instruction const& insn) -{ - f32x4 xmm2m128; - if (insn.modrm().is_register()) { - xmm2m128 = m_xmm[insn.modrm().rm()].ps; - } else { - // FIXME: Shadows - xmm2m128 = bit_cast(insn.modrm().read128(m_cpu, insn).value()); - } - - m_xmm[insn.modrm().reg()].ps = 1.f / xmm2m128; -} -void SoftVPU::RCPSS_xmm1_xmm2m32(X86::Instruction const& insn) -{ - float xmm2m32; - if (insn.modrm().is_register()) { - xmm2m32 = m_xmm[insn.modrm().rm()].ps[0]; - } else { - // FIXME: Shadows - xmm2m32 = bit_cast(insn.modrm().read32(m_cpu, insn).value()); - } - - m_xmm[insn.modrm().reg()].ps[0] = 1.f / xmm2m32; -} - -void SoftVPU::ANDPS_xmm1_xmm2m128(X86::Instruction const& insn) -{ - u32x4 xmm2m128; - if (insn.modrm().is_register()) { - xmm2m128 = m_xmm[insn.modrm().rm()].pudw; - } else { - // FIXME: Shadows - xmm2m128 = bit_cast(insn.modrm().read128(m_cpu, insn).value()); - } - - m_xmm[insn.modrm().reg()].pudw &= xmm2m128; -} -void SoftVPU::ANDNPS_xmm1_xmm2m128(X86::Instruction const& insn) -{ - u32x4 xmm2m128; - if (insn.modrm().is_register()) { - xmm2m128 = m_xmm[insn.modrm().rm()].pudw; - } else { - // FIXME: Shadows - xmm2m128 = bit_cast(insn.modrm().read128(m_cpu, insn).value()); - } - - u32x4& xmm1 = m_xmm[insn.modrm().reg()].pudw; - xmm1 = ~xmm1 & xmm2m128; -} -void SoftVPU::ORPS_xmm1_xmm2m128(X86::Instruction const& insn) -{ - u32x4 xmm2m128; - if (insn.modrm().is_register()) { - xmm2m128 = m_xmm[insn.modrm().rm()].pudw; - } else { - // FIXME: Shadows - xmm2m128 = bit_cast(insn.modrm().read128(m_cpu, insn).value()); - } - - m_xmm[insn.modrm().reg()].pudw |= xmm2m128; -} -void SoftVPU::XORPS_xmm1_xmm2m128(X86::Instruction const& insn) -{ - u32x4 xmm2m128; - if (insn.modrm().is_register()) { - xmm2m128 = m_xmm[insn.modrm().rm()].pudw; - } else { - // FIXME: Shadows - xmm2m128 = bit_cast(insn.modrm().read128(m_cpu, insn).value()); - } - - m_xmm[insn.modrm().reg()].pudw ^= xmm2m128; -} - -void SoftVPU::ADDPS_xmm1_xmm2m128(X86::Instruction const& insn) -{ - // Raise Overflow, Underflow, Invalid, Precision, Denormal - f32x4 xmm2m128; - if (insn.modrm().is_register()) { - xmm2m128 = m_xmm[insn.modrm().rm()].ps; - } else { - // FIXME: Shadows - xmm2m128 = bit_cast(insn.modrm().read128(m_cpu, insn).value()); - } - - m_xmm[insn.modrm().reg()].ps += xmm2m128; -} -void SoftVPU::ADDSS_xmm1_xmm2m32(X86::Instruction const& insn) -{ - // Raise Overflow, Underflow, Invalid, Precision, Denormal - float xmm2m32; - if (insn.modrm().is_register()) { - xmm2m32 = m_xmm[insn.modrm().rm()].ps[0]; - } else { - // FIXME: Shadows - xmm2m32 = bit_cast(insn.modrm().read32(m_cpu, insn).value()); - } - - m_xmm[insn.modrm().reg()].ps[0] += xmm2m32; -} - -void SoftVPU::MULPS_xmm1_xmm2m128(X86::Instruction const& insn) -{ - // Raise Overflow, Underflow, Invalid, Precision, Denormal - f32x4 xmm2m128; - if (insn.modrm().is_register()) { - xmm2m128 = m_xmm[insn.modrm().rm()].ps; - } else { - // FIXME: Shadows - xmm2m128 = bit_cast(insn.modrm().read128(m_cpu, insn).value()); - } - - m_xmm[insn.modrm().reg()].ps *= xmm2m128; -} -void SoftVPU::MULSS_xmm1_xmm2m32(X86::Instruction const& insn) -{ - // Raise Overflow, Underflow, Invalid, Precision, Denormal - float xmm1 = m_xmm[insn.modrm().reg()].ps[0]; - float xmm2m32; - - if (insn.modrm().is_register()) { - xmm2m32 = m_xmm[insn.modrm().rm()].ps[0]; - } else { - // FIXME: Shadows - xmm2m32 = bit_cast(insn.modrm().read32(m_cpu, insn).value()); - } - xmm1 *= xmm2m32; - - m_xmm[insn.modrm().reg()].ps[0] *= xmm1; -} - -void SoftVPU::SUBPS_xmm1_xmm2m128(X86::Instruction const& insn) -{ - // Raise Overflow, Underflow, Invalid, Precision, Denormal - f32x4 xmm2m128; - - if (insn.modrm().is_register()) { - xmm2m128 = m_xmm[insn.modrm().rm()].ps; - } else { - // FIXME: Shadows - xmm2m128 = bit_cast(insn.modrm().read128(m_cpu, insn).value()); - } - - m_xmm[insn.modrm().reg()].ps -= xmm2m128; -} -void SoftVPU::SUBSS_xmm1_xmm2m32(X86::Instruction const& insn) -{ - // Raise Overflow, Underflow, Invalid, Precision, Denormal - float xmm2m32; - - if (insn.modrm().is_register()) { - xmm2m32 = m_xmm[insn.modrm().rm()].ps[0]; - } else { - // FIXME: Shadows - xmm2m32 = bit_cast(insn.modrm().read32(m_cpu, insn).value()); - } - - m_xmm[insn.modrm().reg()].ps[0] -= xmm2m32; -} - -void SoftVPU::MINPS_xmm1_xmm2m128(X86::Instruction const& insn) -{ - // FIXME: Raise Invalid (including QNaN Source Operand), Denormal - f32x4 xmm1 = m_xmm[insn.modrm().reg()].ps; - f32x4 xmm2m128; - - if (insn.modrm().is_register()) { - xmm2m128 = m_xmm[insn.modrm().rm()].ps; - } else { - // FIXME: Shadows - xmm2m128 = bit_cast(insn.modrm().read128(m_cpu, insn).value()); - } - - for (auto i = 0; i < 4; ++i) { - // When only one is NaN or both are 0.0s (of either sign), or - // FIXME: xmm2m32 is SNaN - // xmm2m32 is returned unchanged - if (isnan(xmm1[i]) || isnan(xmm2m128[i]) || xmm1[i] == xmm2m128[i]) - xmm1[i] = xmm2m128[i]; - else - xmm1[i] = min(xmm1[i], xmm2m128[i]); - } - - m_xmm[insn.modrm().reg()].ps = xmm1; -} -void SoftVPU::MINSS_xmm1_xmm2m32(X86::Instruction const& insn) -{ - // FIXME: Raise Invalid (Including QNaN Source Operand), Denormal - float xmm1 = m_xmm[insn.modrm().reg()].ps[0]; - float xmm2m32; - - if (insn.modrm().is_register()) { - xmm2m32 = m_xmm[insn.modrm().rm()].ps[0]; - } else { - // FIXME: Shadows - xmm2m32 = bit_cast(insn.modrm().read32(m_cpu, insn).value()); - } - // When only one is NaN or both are 0.0s (of either sign), or - // FIXME: xmm2m32 is SNaN - // xmm2m32 is returned unchanged - if (isnan(xmm1) || isnan(xmm2m32) || xmm1 == xmm2m32) - xmm1 = xmm2m32; - else - xmm1 = min(xmm1, xmm2m32); - - m_xmm[insn.modrm().reg()].ps[0] = xmm1; -} - -void SoftVPU::DIVPS_xmm1_xmm2m128(X86::Instruction const& insn) -{ - // Raise Overflow, Underflow, Invalid, Divide-by-Zero, Precision, Denormal - f32x4 xmm2m128; - if (insn.modrm().is_register()) { - xmm2m128 = m_xmm[insn.modrm().rm()].ps; - } else { - // FIXME: Shadows - xmm2m128 = bit_cast(insn.modrm().read128(m_cpu, insn).value()); - } - - m_xmm[insn.modrm().reg()].ps /= xmm2m128; -} -void SoftVPU::DIVSS_xmm1_xmm2m32(X86::Instruction const& insn) -{ - // Raise Overflow, Underflow, Invalid, Divide-by-Zero, Precision, Denormal - float xmm2m32; - if (insn.modrm().is_register()) { - xmm2m32 = m_xmm[insn.modrm().rm()].ps[0]; - } else { - // FIXME: Shadows - xmm2m32 = bit_cast(insn.modrm().read32(m_cpu, insn).value()); - } - - m_xmm[insn.modrm().reg()].ps[0] /= xmm2m32; -} - -void SoftVPU::MAXPS_xmm1_xmm2m128(X86::Instruction const& insn) -{ - // FIXME: Raise Invalid (including QNaN Source Operand), Denormal - f32x4 xmm1 = m_xmm[insn.modrm().reg()].ps; - f32x4 xmm2m128; - - if (insn.modrm().is_register()) { - xmm2m128 = m_xmm[insn.modrm().rm()].ps; - } else { - // FIXME: Shadows - xmm2m128 = bit_cast(insn.modrm().read128(m_cpu, insn).value()); - } - - for (auto i = 0; i < 4; ++i) { - // When only one is NaN or both are 0.0s (of either sign), or - // FIXME: xmm2m32 is SNaN - // xmm2m32 is returned unchanged - if (isnan(xmm1[i]) || isnan(xmm2m128[i]) || xmm1[i] == xmm2m128[i]) - xmm1[i] = xmm2m128[i]; - else - xmm1[i] = max(xmm1[i], xmm2m128[i]); - } - - m_xmm[insn.modrm().reg()].ps = xmm1; -} -void SoftVPU::MAXSS_xmm1_xmm2m32(X86::Instruction const& insn) -{ - // FIXME: Raise Invalid (Including QNaN Source Operand), Denormal - float xmm1 = m_xmm[insn.modrm().reg()].ps[0]; - float xmm2m32; - - if (insn.modrm().is_register()) { - xmm2m32 = m_xmm[insn.modrm().rm()].ps[0]; - } else { - // FIXME: Shadows - xmm2m32 = bit_cast(insn.modrm().read32(m_cpu, insn).value()); - } - // When only one is NaN or both are 0.0s (of either sign), or - // FIXME: xmm2m32 is SNaN - // xmm2m32 is returned unchanged - if (isnan(xmm1) || isnan(xmm2m32) || xmm1 == xmm2m32) - xmm1 = xmm2m32; - else - xmm1 = max(xmm1, xmm2m32); - - m_xmm[insn.modrm().reg()].ps[0] = xmm1; -} - -void SoftVPU::PSHUFW_mm1_mm2m64_imm8(X86::Instruction const& insn) -{ - MMX src; - if (insn.modrm().is_register()) { - src = m_cpu.mmx_get(insn.modrm().rm()); - } else { - // FIXME: Shadows - src = bit_cast(insn.modrm().read64(m_cpu, insn).value()); - } - - u8 order = insn.imm8(); - MMX dest; - - dest.v16u[0] = src.v16u[(order >> 0) & 0b11]; - dest.v16u[1] = src.v16u[(order >> 2) & 0b11]; - dest.v16u[2] = src.v16u[(order >> 4) & 0b11]; - dest.v16u[3] = src.v16u[(order >> 6) & 0b11]; - - m_cpu.mmx_set(insn.modrm().reg(), dest); -} - -void SoftVPU::CMPPS_xmm1_xmm2m128_imm8(X86::Instruction const& insn) -{ - // FIXME: Raise Denormal, Invalid Operation (QNaN dependent on imm8) - XMM& xmm1 = m_xmm[insn.modrm().reg()]; - f32x4 xmm2m128; - - if (insn.modrm().is_register()) { - xmm2m128 = m_xmm[insn.modrm().rm()].ps; - } else { - // FIXME: Shadows - xmm2m128 = bit_cast(insn.modrm().read128(m_cpu, insn).value()); - } - using enum ComparePredicate; - switch ((ComparePredicate)insn.imm8()) { - case EQ: - xmm1.ps = xmm1.ps == xmm2m128; - break; - case LT: - xmm1.ps = xmm1.ps < xmm2m128; - break; - case LE: - xmm1.ps = xmm1.ps <= xmm2m128; - break; - case UNORD: - for (auto i = 0; i < 4; ++i) - xmm1.pudw[i] = 0xFFFF'FFFF * (isnan(xmm1.ps[i]) || isnan(xmm2m128[i])); - break; - case NEQ: - xmm1.ps = xmm1.ps != xmm2m128; - break; - case NLT: - xmm1.ps = xmm1.ps >= xmm2m128; - break; - case NLE: - xmm1.ps = xmm1.ps > xmm2m128; - break; - case ORD: - for (auto i = 0; i < 4; ++i) - xmm1.pudw[i] = 0xFFFF'FFFF * (!isnan(xmm1.ps[i]) && !isnan(xmm2m128[i])); - break; - } -} -void SoftVPU::CMPSS_xmm1_xmm2m32_imm8(X86::Instruction const& insn) -{ - // FIXME: Raise Denormal, Invalid Operation (QNaN dependent on imm8) - float xmm1 = m_xmm[insn.modrm().reg()].ps[0]; - float xmm2m128; - bool res; - - if (insn.modrm().is_register()) { - xmm2m128 = m_xmm[insn.modrm().rm()].ps[0]; - } else { - // FIXME: Shadows - xmm2m128 = bit_cast(insn.modrm().read32(m_cpu, insn).value()); - } - using enum ComparePredicate; - switch ((ComparePredicate)insn.imm8()) { - case EQ: - res = xmm1 == xmm2m128; - break; - case LT: - res = xmm1 < xmm2m128; - break; - case LE: - res = xmm1 <= xmm2m128; - break; - case UNORD: - res = isnan(xmm1) || isnan(xmm2m128); - break; - case NEQ: - res = xmm1 != xmm2m128; - break; - case NLT: - res = xmm1 >= xmm2m128; - break; - case NLE: - res = xmm1 > xmm2m128; - break; - case ORD: - res = !isnan(xmm1) && !isnan(xmm2m128); - break; - } - - m_xmm[insn.modrm().reg()].pudw[0] = 0xFFFF'FFFF * res; -} - -void SoftVPU::PINSRW_mm1_r32m16_imm8(X86::Instruction const&) { TODO(); } -void SoftVPU::PINSRW_xmm1_r32m16_imm8(X86::Instruction const&) { TODO(); } -void SoftVPU::PEXTRW_reg_mm1_imm8(X86::Instruction const&) { TODO(); } -void SoftVPU::PEXTRW_reg_xmm1_imm8(X86::Instruction const&) { TODO(); } - -void SoftVPU::SHUFPS_xmm1_xmm2m128_imm8(X86::Instruction const& insn) -{ - f32x4 src; - if (insn.modrm().is_register()) { - src = m_xmm[insn.modrm().rm()].ps; - } else { - // FIXME: Shadows - src = bit_cast(insn.modrm().read128(m_cpu, insn).value()); - } - - u8 order = insn.imm8(); - f32x4 dest; - dest[0] = src[(order >> 0) & 0b11]; - dest[1] = src[(order >> 2) & 0b11]; - dest[2] = src[(order >> 4) & 0b11]; - dest[3] = src[(order >> 6) & 0b11]; - - m_xmm[insn.modrm().reg()].ps = dest; -} - -void SoftVPU::PMOVMSKB_reg_mm1(X86::Instruction const&) { TODO(); } -void SoftVPU::PMOVMSKB_reg_xmm1(X86::Instruction const& insn) -{ - VERIFY(insn.modrm().is_register()); - XMM src = m_xmm[insn.modrm().rm()]; - - u32 dest = 0; - for (int i = 0; i < 16; ++i) - dest |= (src.pub[i] >> 7) << i; - - m_cpu.gpr32(insn.reg32()) = ValueWithShadow::create_initialized(dest); -} - -void SoftVPU::PMINUB_mm1_mm2m64(X86::Instruction const&) { TODO(); } -void SoftVPU::PMINUB_xmm1_xmm2m128(X86::Instruction const&) { TODO(); } - -void SoftVPU::PMAXUB_mm1_mm2m64(X86::Instruction const&) { TODO(); } -void SoftVPU::PMAXUB_xmm1_xmm2m128(X86::Instruction const&) { TODO(); } - -void SoftVPU::PAVGB_mm1_mm2m64(X86::Instruction const&) { TODO(); } -void SoftVPU::PAVGB_xmm1_xmm2m128(X86::Instruction const&) { TODO(); } - -void SoftVPU::PAVGW_mm1_mm2m64(X86::Instruction const&) { TODO(); } -void SoftVPU::PAVGW_xmm1_xmm2m128(X86::Instruction const&) { TODO(); } - -void SoftVPU::PMULHUW_mm1_mm2m64(X86::Instruction const&) { TODO(); } -void SoftVPU::PMULHUW_xmm1_xmm2m64(X86::Instruction const&) { TODO(); } - -void SoftVPU::MOVNTQ_m64_mm1(X86::Instruction const&) { TODO(); } - -void SoftVPU::PMINSB_mm1_mm2m64(X86::Instruction const&) { TODO(); } -void SoftVPU::PMINSB_xmm1_xmm2m128(X86::Instruction const&) { TODO(); } - -void SoftVPU::PMAXSB_mm1_mm2m64(X86::Instruction const&) { TODO(); } -void SoftVPU::PMAXSB_xmm1_xmm2m128(X86::Instruction const&) { TODO(); } - -void SoftVPU::PSADBB_mm1_mm2m64(X86::Instruction const&) { TODO(); } -void SoftVPU::PSADBB_xmm1_xmm2m128(X86::Instruction const&) { TODO(); } - -void SoftVPU::MASKMOVQ_mm1_mm2m64(X86::Instruction const&) { TODO(); } -} diff --git a/Userland/DevTools/UserspaceEmulator/SoftVPU.h b/Userland/DevTools/UserspaceEmulator/SoftVPU.h deleted file mode 100644 index 2b7614774e..0000000000 --- a/Userland/DevTools/UserspaceEmulator/SoftVPU.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (c) 2022, Leon Albrecht - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace UserspaceEmulator { -using AK::RoundingMode; -using namespace AK::SIMD; -class Emulator; -class SoftCPU; - -union XMM { - f32x4 ps; - f64x2 pd; - i8x16 psb; - u8x16 pub; - i16x8 psw; - u16x8 puw; - u32x4 pudw; - u64x2 puqw; -}; - -class SoftVPU { -public: - SoftVPU(Emulator& emulator, SoftCPU& cpu) - : m_emulator(emulator) - , m_cpu(cpu) - , m_mxcsr { 0x1F80 } - { - } - - XMM& operator[](u8 index) { return m_xmm[index]; } - - enum class RoundingMode : u8 { - NEAREST = 0b00, - DOWN = 0b01, - UP = 0b10, - TRUNC = 0b11 - }; - - enum class ComparePredicate : u8 { - EQ = 0, - LT = 1, - LE = 2, - UNORD = 3, - NEQ = 4, - NLT = 5, - NLE = 6, - ORD = 7 - // FIXME: More with VEX prefix - }; - -private: - friend SoftCPU; - Emulator& m_emulator; - SoftCPU& m_cpu; - - XMM m_xmm[8]; - - // FIXME: Maybe unimplemented features: - // * DAZ - // * FTZ - AK::MXCSR m_mxcsr; - - void PREFETCHTNTA(X86::Instruction const&); - void PREFETCHT0(X86::Instruction const&); - void PREFETCHT1(X86::Instruction const&); - void PREFETCHT2(X86::Instruction const&); - void LDMXCSR(X86::Instruction const&); - void STMXCSR(X86::Instruction const&); - void MOVUPS_xmm1_xmm2m128(X86::Instruction const&); - void MOVSS_xmm1_xmm2m32(X86::Instruction const&); - void MOVUPS_xmm1m128_xmm2(X86::Instruction const&); - void MOVSS_xmm1m32_xmm2(X86::Instruction const&); - void MOVLPS_xmm1_xmm2m64(X86::Instruction const&); - void MOVLPS_m64_xmm2(X86::Instruction const&); - void UNPCKLPS_xmm1_xmm2m128(X86::Instruction const&); - void UNPCKHPS_xmm1_xmm2m128(X86::Instruction const&); - void MOVHPS_xmm1_xmm2m64(X86::Instruction const&); - void MOVHPS_m64_xmm2(X86::Instruction const&); - void MOVAPS_xmm1_xmm2m128(X86::Instruction const&); - void MOVAPS_xmm1m128_xmm2(X86::Instruction const&); - void CVTPI2PS_xmm1_mm2m64(X86::Instruction const&); - void CVTSI2SS_xmm1_rm32(X86::Instruction const&); - void MOVNTPS_xmm1m128_xmm2(X86::Instruction const&); - void CVTTPS2PI_mm1_xmm2m64(X86::Instruction const&); - void CVTTSS2SI_r32_xmm2m32(X86::Instruction const&); - void CVTPS2PI_xmm1_mm2m64(X86::Instruction const&); - void CVTSS2SI_r32_xmm2m32(X86::Instruction const&); - void UCOMISS_xmm1_xmm2m32(X86::Instruction const&); - void COMISS_xmm1_xmm2m32(X86::Instruction const&); - void MOVMSKPS_reg_xmm(X86::Instruction const&); - void SQRTPS_xmm1_xmm2m128(X86::Instruction const&); - void SQRTSS_xmm1_xmm2m32(X86::Instruction const&); - void RSQRTPS_xmm1_xmm2m128(X86::Instruction const&); - void RSQRTSS_xmm1_xmm2m32(X86::Instruction const&); - void RCPPS_xmm1_xmm2m128(X86::Instruction const&); - void RCPSS_xmm1_xmm2m32(X86::Instruction const&); - void ANDPS_xmm1_xmm2m128(X86::Instruction const&); - void ANDNPS_xmm1_xmm2m128(X86::Instruction const&); - void ORPS_xmm1_xmm2m128(X86::Instruction const&); - void XORPS_xmm1_xmm2m128(X86::Instruction const&); - void ADDPS_xmm1_xmm2m128(X86::Instruction const&); - void ADDSS_xmm1_xmm2m32(X86::Instruction const&); - void MULPS_xmm1_xmm2m128(X86::Instruction const&); - void MULSS_xmm1_xmm2m32(X86::Instruction const&); - void SUBPS_xmm1_xmm2m128(X86::Instruction const&); - void SUBSS_xmm1_xmm2m32(X86::Instruction const&); - void MINPS_xmm1_xmm2m128(X86::Instruction const&); - void MINSS_xmm1_xmm2m32(X86::Instruction const&); - void DIVPS_xmm1_xmm2m128(X86::Instruction const&); - void DIVSS_xmm1_xmm2m32(X86::Instruction const&); - void MAXPS_xmm1_xmm2m128(X86::Instruction const&); - void MAXSS_xmm1_xmm2m32(X86::Instruction const&); - void PSHUFW_mm1_mm2m64_imm8(X86::Instruction const&); - void CMPPS_xmm1_xmm2m128_imm8(X86::Instruction const&); - void CMPSS_xmm1_xmm2m32_imm8(X86::Instruction const&); - void PINSRW_mm1_r32m16_imm8(X86::Instruction const&); - void PINSRW_xmm1_r32m16_imm8(X86::Instruction const&); - void PEXTRW_reg_mm1_imm8(X86::Instruction const&); - void PEXTRW_reg_xmm1_imm8(X86::Instruction const&); - void SHUFPS_xmm1_xmm2m128_imm8(X86::Instruction const&); - void PMOVMSKB_reg_mm1(X86::Instruction const&); - void PMOVMSKB_reg_xmm1(X86::Instruction const&); - void PMINUB_mm1_mm2m64(X86::Instruction const&); - void PMINUB_xmm1_xmm2m128(X86::Instruction const&); - void PMAXUB_mm1_mm2m64(X86::Instruction const&); - void PMAXUB_xmm1_xmm2m128(X86::Instruction const&); - void PAVGB_mm1_mm2m64(X86::Instruction const&); - void PAVGB_xmm1_xmm2m128(X86::Instruction const&); - void PAVGW_mm1_mm2m64(X86::Instruction const&); - void PAVGW_xmm1_xmm2m128(X86::Instruction const&); - void PMULHUW_mm1_mm2m64(X86::Instruction const&); - void PMULHUW_xmm1_xmm2m64(X86::Instruction const&); - void MOVNTQ_m64_mm1(X86::Instruction const&); - void PMINSB_mm1_mm2m64(X86::Instruction const&); - void PMINSB_xmm1_xmm2m128(X86::Instruction const&); - void PMAXSB_mm1_mm2m64(X86::Instruction const&); - void PMAXSB_xmm1_xmm2m128(X86::Instruction const&); - void PSADBB_mm1_mm2m64(X86::Instruction const&); - void PSADBB_xmm1_xmm2m128(X86::Instruction const&); - void MASKMOVQ_mm1_mm2m64(X86::Instruction const&); -}; - -} diff --git a/Userland/DevTools/UserspaceEmulator/ValueWithShadow.h b/Userland/DevTools/UserspaceEmulator/ValueWithShadow.h deleted file mode 100644 index d5233c936e..0000000000 --- a/Userland/DevTools/UserspaceEmulator/ValueWithShadow.h +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace UserspaceEmulator { - -template -class ValueAndShadowReference; - -template -class ValueWithShadow { -public: - using ValueType = T; - using ShadowType = Array; - - ValueWithShadow() = default; - - ValueWithShadow(T value, T shadow) - : m_value(value) - { - ReadonlyBytes { &shadow, sizeof(shadow) }.copy_to(m_shadow); - } - - ValueWithShadow(T value, ShadowType shadow) - : m_value(value) - , m_shadow(shadow) - { - } - - static ValueWithShadow create_initialized(T value) - { - ShadowType shadow; - shadow.fill(0x01); - return { - value, - shadow, - }; - } - - ValueWithShadow(ValueAndShadowReference const&); - - T value() const { return m_value; } - ShadowType const& shadow() const { return m_shadow; } - - T shadow_as_value() const - requires(IsTriviallyConstructible) - { - return *bit_cast(m_shadow.data()); - } - - template - auto reference_to() - requires(IsClass || IsUnion) - { - using ResultType = ValueAndShadowReference().*member)>>; - return ResultType { - m_value.*member, - *bit_cast(m_shadow.span().offset_pointer(bit_cast(member) - bit_cast(nullptr))), - }; - } - - template - auto slice() const - requires(IsClass || IsUnion) - { - using ResultType = ValueWithShadow().*member)>>; - return ResultType { - m_value.*member, - *bit_cast(m_shadow.span().offset_pointer(bit_cast(member) - bit_cast(nullptr))), - }; - } - - bool is_uninitialized() const - { - for (size_t i = 0; i < sizeof(ShadowType); ++i) { - if ((m_shadow[i] & 0x01) != 0x01) - return true; - } - return false; - } - - void set_initialized() - { - m_shadow.fill(0x01); - } - -private: - T m_value {}; - ShadowType m_shadow {}; -}; - -template -class ValueAndShadowReference { -public: - using ValueType = T; - using ShadowType = Array; - - ValueAndShadowReference(T& value, ShadowType& shadow) - : m_value(value) - , m_shadow(shadow) - { - } - - bool is_uninitialized() const - { - for (size_t i = 0; i < sizeof(ShadowType); ++i) { - if ((m_shadow[i] & 0x01) != 0x01) - return true; - } - return false; - } - - ValueAndShadowReference& operator=(ValueWithShadow const&); - - T shadow_as_value() const - requires(IsTriviallyConstructible) - { - return *bit_cast(m_shadow.data()); - } - - T& value() { return m_value; } - ShadowType& shadow() { return m_shadow; } - - T const& value() const { return m_value; } - ShadowType const& shadow() const { return m_shadow; } - -private: - T& m_value; - ShadowType& m_shadow; -}; - -template -ALWAYS_INLINE ValueWithShadow shadow_wrap_as_initialized(T value) -{ - return ValueWithShadow::create_initialized(value); -} - -template -ALWAYS_INLINE ValueWithShadow shadow_wrap_with_taint_from(T value, U const& taint_a) -{ - if (taint_a.is_uninitialized()) - return { value, 0 }; - return shadow_wrap_as_initialized(value); -} - -template -ALWAYS_INLINE ValueWithShadow shadow_wrap_with_taint_from(T value, U const& taint_a, V const& taint_b) -{ - if (taint_a.is_uninitialized() || taint_b.is_uninitialized()) - return { value, 0 }; - return shadow_wrap_as_initialized(value); -} - -template -ALWAYS_INLINE ValueWithShadow shadow_wrap_with_taint_from(T value, U const& taint_a, V const& taint_b, X const& taint_c) -{ - if (taint_a.is_uninitialized() || taint_b.is_uninitialized() || taint_c.is_uninitialized()) - return { value, 0 }; - return shadow_wrap_as_initialized(value); -} - -template -inline ValueWithShadow::ValueWithShadow(ValueAndShadowReference const& other) - : m_value(other.value()) - , m_shadow(other.shadow()) -{ -} - -template -inline ValueAndShadowReference& ValueAndShadowReference::operator=(ValueWithShadow const& other) -{ - m_value = other.value(); - m_shadow = other.shadow(); - return *this; -} - -} - -template -struct AK::Formatter> : AK::Formatter { - ErrorOr format(FormatBuilder& builder, UserspaceEmulator::ValueWithShadow value) - { - return Formatter::format(builder, value.value()); - } -}; diff --git a/Userland/DevTools/UserspaceEmulator/main.cpp b/Userland/DevTools/UserspaceEmulator/main.cpp deleted file mode 100644 index 25993a7d0c..0000000000 --- a/Userland/DevTools/UserspaceEmulator/main.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Emulator.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -bool g_report_to_debug = false; - -int main(int argc, char** argv, char** env) -{ - Vector arguments; - bool pause_on_startup { false }; - ByteString profile_dump_path; - bool enable_roi_mode { false }; - bool dump_profile { false }; - unsigned profile_instruction_interval { 0 }; - - Core::ArgsParser parser; - parser.set_stop_on_first_non_option(true); - parser.add_option(g_report_to_debug, "Write reports to the debug log", "report-to-debug", 0); - parser.add_option(pause_on_startup, "Pause on startup", "pause", 'p'); - parser.add_option(dump_profile, "Generate a ProfileViewer-compatible profile", "profile", 0); - parser.add_option(profile_instruction_interval, "Set the profile instruction capture interval, 128 by default", "profile-interval", 'i', "num_instructions"); - parser.add_option(profile_dump_path, "File path for profile dump", "profile-file", 0, "path"); - parser.add_option(enable_roi_mode, "Enable Region-of-Interest mode for profiling", "roi", 0); - - parser.add_positional_argument(arguments, "Command to emulate", "command"); - - parser.parse(argc, argv); - - if (dump_profile && profile_instruction_interval == 0) - profile_instruction_interval = 128; - - auto executable_path_or_error = arguments[0].contains('/') - ? FileSystem::real_path(arguments[0]) - : Core::System::resolve_executable_from_environment(arguments[0]); - if (executable_path_or_error.is_error()) { - reportln("Cannot find executable for '{}'."sv, arguments[0]); - return 1; - } - auto executable_path = executable_path_or_error.release_value().to_byte_string(); - - if (dump_profile && profile_dump_path.is_empty()) - profile_dump_path = ByteString::formatted("{}.{}.profile", LexicalPath(executable_path).basename(), getpid()); - - OwnPtr profile_stream; - OwnPtr>> profile_strings; - OwnPtr> profile_string_id_map; - - if (dump_profile) { - auto profile_stream_or_error = Core::File::open(profile_dump_path, Core::File::OpenMode::Write); - if (profile_stream_or_error.is_error()) { - warnln("Failed to open '{}' for writing: {}", profile_dump_path, profile_stream_or_error.error()); - return 1; - } - profile_stream = profile_stream_or_error.release_value(); - profile_strings = make>>(); - profile_string_id_map = make>(); - - profile_stream->write_until_depleted(R"({"events":[)"sv.bytes()).release_value_but_fixme_should_propagate_errors(); - timeval tv {}; - gettimeofday(&tv, nullptr); - profile_stream->write_until_depleted( - ByteString::formatted( - R"~({{"type": "process_create", "parent_pid": 1, "executable": "{}", "pid": {}, "tid": {}, "timestamp": {}, "lost_samples": 0, "stack": []}})~", - executable_path, getpid(), gettid(), tv.tv_sec * 1000 + tv.tv_usec / 1000) - .bytes()) - .release_value_but_fixme_should_propagate_errors(); - } - - Vector environment; - for (int i = 0; env[i]; ++i) { - environment.append(env[i]); - } - - // FIXME: It might be nice to tear down the emulator properly. - auto& emulator = *new UserspaceEmulator::Emulator(executable_path, arguments, environment); - - emulator.set_profiling_details(dump_profile, profile_instruction_interval, profile_stream, profile_strings, profile_string_id_map); - emulator.set_in_region_of_interest(!enable_roi_mode); - - if (!emulator.load_elf()) - return 1; - - StringBuilder builder; - builder.append("(UE) "sv); - builder.append(LexicalPath::basename(arguments[0])); - if (auto result = Core::Process::set_name(builder.string_view(), Core::Process::SetThreadName::Yes); result.is_error()) { - reportln("Core::Process::set_name: {}"sv, result.error()); - return 1; - } - - if (pause_on_startup) - emulator.pause(); - - int rc = emulator.exec(); - - if (dump_profile) { - emulator.profile_stream().write_until_depleted("], \"strings\": ["sv.bytes()).release_value_but_fixme_should_propagate_errors(); - if (emulator.profiler_strings().size()) { - for (size_t i = 0; i < emulator.profiler_strings().size() - 1; ++i) - emulator.profile_stream().write_until_depleted(ByteString::formatted("\"{}\", ", emulator.profiler_strings().at(i)).bytes()).release_value_but_fixme_should_propagate_errors(); - emulator.profile_stream().write_until_depleted(ByteString::formatted("\"{}\"", emulator.profiler_strings().last()).bytes()).release_value_but_fixme_should_propagate_errors(); - } - emulator.profile_stream().write_until_depleted("]}"sv.bytes()).release_value_but_fixme_should_propagate_errors(); - } - return rc; -}