diff -up ./arch/x86/Kconfig.cpu.fisttp ./arch/x86/Kconfig.cpu --- ./arch/x86/Kconfig.cpu.fisttp 2026-05-30 10:42:41.248743008 +0900 +++ ./arch/x86/Kconfig.cpu 2026-05-31 04:23:09.512785339 +0900 @@ -437,6 +437,17 @@ config CPU_EMU_SSE2 If you are not sure, say N. +config CPU_EMU_SSE3 + bool "SSE3 instruction emulation" + depends on X86_32 && CPU_EMU486 + help + This attemps to emulate certain SSE3 codes + (fisttpll, movddup, lddqu). + This will allow programs to run which is compiled by -msse3. + The opcode is used unconditionally in WebEngine. + + If you are not sure, say N. + config CPU_PROC_EMULATED_OPS bool "/proc/emulated_ops support" depends on X86_32 && CPU_EMU486 diff -up ./arch/x86/configs/i386_defconfig.fisttp ./arch/x86/configs/i386_defconfig --- ./arch/x86/configs/i386_defconfig.fisttp 2026-05-30 10:42:41.248743008 +0900 +++ ./arch/x86/configs/i386_defconfig 2026-05-30 10:43:13.697730199 +0900 @@ -36,6 +36,7 @@ CONFIG_SMP=y # CONFIG_IGNORE_SSP_OPCODE is not set # CONFIG_CPU_IGNORE_PREFETCH is not set # CONFIG_CPU_EMU_FUCOMI is not set +# CONFIG_CPU_EMU_SSE3 is not set # CONFIG_CPU_PROC_EMULATED_OPS is not set # CONFIG_CPU_PROC_EMULATED_FILES is not set CONFIG_HYPERVISOR_GUEST=y diff -up ./arch/x86/kernel/traps.c.fisttp ./arch/x86/kernel/traps.c --- ./arch/x86/kernel/traps.c.fisttp 2026-05-30 10:42:41.249743008 +0900 +++ ./arch/x86/kernel/traps.c 2026-05-31 15:23:59.292422593 +0900 @@ -826,6 +826,7 @@ static struct { unsigned long fcmov; unsigned long sse; unsigned long sse2; + unsigned long sse3; unsigned long endbr32; } emulated_ops_counter = { 0, 0, 0, 0, 0, 0, 0, 0 @@ -849,6 +850,10 @@ static int emulated_ops_proc_show(struct emulated_ops_counter.sse, emulated_ops_counter.sse2); #endif +#ifdef CONFIG_CPU_EMU_SSE3 + seq_printf(m, "sse3: %lu\n", + emulated_ops_counter.sse3); +#endif #ifdef CONFIG_IGNORE_SSP_OPCODE seq_printf(m, "endbr32: %lu\n", emulated_ops_counter.endbr32); #endif @@ -878,6 +883,7 @@ static ssize_t emulated_ops_proc_write(s emulated_ops_counter.fcmov = 0; emulated_ops_counter.sse = 0; emulated_ops_counter.sse2 = 0; + emulated_ops_counter.sse3 = 0; emulated_ops_counter.endbr32 = 0; } return count; @@ -910,15 +916,17 @@ struct { unsigned long cmov; unsigned long sse; unsigned long sse2; + unsigned long sse3; unsigned long endbr32; } emulated_files_list[EMU_OPS_FILES_MAX] = { - { {'\0'}, 0,0,0,0 }, + { {'\0'}, 0,0,0,0,0 }, }; enum emulated_op_type { OP_NONE = 0, OP_CMOV, OP_SSE, OP_SSE2, + OP_SSE3, OP_ENDBR32, }; static int emulated_files_running = 0; @@ -933,6 +941,7 @@ static void emulated_files_clear(void) emulated_files_list[i].cmov = 0; emulated_files_list[i].sse = 0; emulated_files_list[i].sse2 = 0; + emulated_files_list[i].sse3 = 0; emulated_files_list[i].endbr32 = 0; } } @@ -988,6 +997,7 @@ static void __emulated_files_byip(unsign emulated_files_list[i].cmov=0; emulated_files_list[i].sse=0; emulated_files_list[i].sse2=0; + emulated_files_list[i].sse3=0; emulated_files_list[i].endbr32=0; break; } @@ -1004,6 +1014,8 @@ static void __emulated_files_byip(unsign emulated_files_list[i].sse++; break; case OP_SSE2: emulated_files_list[i].sse2++; break; + case OP_SSE3: + emulated_files_list[i].sse3++; break; case OP_ENDBR32: emulated_files_list[i].endbr32++; break; case OP_NONE: @@ -1022,29 +1034,31 @@ static int emulated_files_proc_show(stru { int i; #ifdef CONFIG_IGNORE_SSP_OPCODE - seq_printf(m, "%10s %10s %10s %10s %s%s\n", "cmov","sse","sse2","endbr32", "filename", + seq_printf(m, "%10s %10s %10s %10s %10s %s%s\n", "cmov","sse","sse2","sse3","endbr32", "filename", (!emulated_files_running)?" (stopped; write \"1\" to start)":"" ); #else - seq_printf(m, "%10s %10s %10s %s%s\n", "cmov","sse","sse2","filename", + seq_printf(m, "%10s %10s %10s 10%s %s%s\n", "cmov","sse","sse2","sse3","filename", (!emulated_files_running)?" (stopped; write \"1\" to start)":"" ); #endif for (i=0;iip = (u32)eip; + goto sse_return; + } /* DD /1 fisttpll */ + if (op1 == 0xF2 && op2 == 0x0F && (op3 == 0x10 || op3 == 0x11)) { /* movsd: move 64bit double from XMM/mem to XMM */ u8 xdst, modrm; @@ -4793,6 +4846,102 @@ sse_again: goto sse_return; } /* F2 0F 10|11 movsd */ + if (op1 == 0xF2 && op2 == 0x0F && op3 == 0x12) { + /* movddup: double copy 64bit XMM/mem to XMM (SSE3) */ + u8 xdst, modrm; + modrm = op4; + EMU_COUNT_SSE3(eip); + eip += 4; /* skips all the opcodes */ + + xdst = (modrm >> 3) & 7; + dst = XMMREG_ADDR(xdst); + if ((modrm & 0xC0) == 0xC0) { /* register to register */ + src = XMMREG_ADDR(modrm & 7); + + /* for sse machines, we fetch first from the real %xmm */ + if (static_cpu_has(X86_FEATURE_XMM)) { +#define get_from_real_xmm(src,xsrc) \ + __asm__ __volatile__( \ + "movdqu %%xmm" #xsrc ", (%0)\n\t" \ + : /*output*/ \ + : "r" (src) /*input*/ \ + : "memory" \ + ) + switch(modrm & 7) { + case 0: get_from_real_xmm(src, 0); break; + case 1: get_from_real_xmm(src, 1); break; + case 2: get_from_real_xmm(src, 2); break; + case 3: get_from_real_xmm(src, 3); break; + case 4: get_from_real_xmm(src, 4); break; + case 5: get_from_real_xmm(src, 5); break; + case 6: get_from_real_xmm(src, 6); break; + case 7: get_from_real_xmm(src, 7); break; + } +#undef get_from_real_xmm + } /* real %xmm */ + + /* dup copy */ + *(u64*)dst = *(u64*)src; + *(((u64*)dst)+1) = *(u64*)src; + + /* shove it back to real %xmm */ + if (static_cpu_has(X86_FEATURE_XMM)) { +#define put_to_real_xmm(dst,xdst) \ + __asm__ __volatile__( \ + "movdqu (%0), %%xmm" #xdst "\n\t" \ + : : "r" (dst) \ + ) + switch(xdst) { + case 0: put_to_real_xmm(dst, 0); break; + case 1: put_to_real_xmm(dst, 1); break; + case 2: put_to_real_xmm(dst, 2); break; + case 3: put_to_real_xmm(dst, 3); break; + case 4: put_to_real_xmm(dst, 4); break; + case 5: put_to_real_xmm(dst, 5); break; + case 6: put_to_real_xmm(dst, 6); break; + case 7: put_to_real_xmm(dst, 7); break; + } +#undef put_to_real_xmm + } /* real %xmm */ + regs->ip = (u32)eip; + goto sse_return; + } + src = modrm_address(regs, &eip, 1, modrm); + /* we must verify that src is valid for this task */ + if (!access_ok_read((void *)src, 4)) { + exc_general_protection(regs, 0); + return; + } + + get_user(dst[0], src+0); + get_user(dst[1], src+1); + get_user(dst[2], src+0); /*copy*/ + get_user(dst[3], src+1); + + /* shove it back to real %xmm */ + if (static_cpu_has(X86_FEATURE_XMM)) { +#define put_to_real_xmm(dst,xdst) \ + __asm__ __volatile__( \ + "movdqu %0, %%xmm" #xdst "\n\t" \ + : : "m" (dst) \ + ) + switch(xdst) { + case 0: put_to_real_xmm(dst, 0); break; + case 1: put_to_real_xmm(dst, 1); break; + case 2: put_to_real_xmm(dst, 2); break; + case 3: put_to_real_xmm(dst, 3); break; + case 4: put_to_real_xmm(dst, 4); break; + case 5: put_to_real_xmm(dst, 5); break; + case 6: put_to_real_xmm(dst, 6); break; + case 7: put_to_real_xmm(dst, 7); break; + } +#undef put_to_real_xmm + } + + regs->ip = (u32)eip; + goto sse_return; + } /* F2 0F 12 movddup */ + if (op1 == 0xF2 && op2 == 0x0F && op3 == 0x2A) { /* cvtsi2sd: convert 32bit int from reg/mem to XMM double */ u8 xdst, modrm; @@ -5364,6 +5513,54 @@ sse_again: goto sse_return; } /* F2 0F C2 cmpsd */ + if (op1 == 0xF2 && op2 == 0x0F && op3 == 0xF0) { + /* lddqu: load 128bit mem to xmm (SSE3) */ + u8 xdst, modrm; + modrm = op4; + EMU_COUNT_SSE3(eip); + eip += 4; /* skips all the opcodes */ + + xdst = (modrm >> 3) & 7; + dst = XMMREG_ADDR(xdst); + if ((modrm & 0xC0) == 0xC0) { /* register to register */ + goto invalid_opcode; + } + src = modrm_address(regs, &eip, 1, modrm); + /* we must verify that src is valid for this task */ + if (!access_ok_read((void *)src, 8)) { + exc_general_protection(regs, 0); + return; + } + + get_user(dst[0], src+0); + get_user(dst[1], src+1); + get_user(dst[2], src+2); + get_user(dst[3], src+3); + + /* shove it back to real %xmm */ + if (static_cpu_has(X86_FEATURE_XMM)) { +#define put_to_real_xmm(dst,xdst) \ + __asm__ __volatile__( \ + "movdqu (%0), %%xmm" #xdst "\n\t" \ + : : "r" (dst) \ + ) + switch(xdst) { + case 0: put_to_real_xmm(dst, 0); break; + case 1: put_to_real_xmm(dst, 1); break; + case 2: put_to_real_xmm(dst, 2); break; + case 3: put_to_real_xmm(dst, 3); break; + case 4: put_to_real_xmm(dst, 4); break; + case 5: put_to_real_xmm(dst, 5); break; + case 6: put_to_real_xmm(dst, 6); break; + case 7: put_to_real_xmm(dst, 7); break; + } +#undef put_to_real_xmm + } + + regs->ip = (u32)eip; + goto sse_return; + } /* F2 0F F0 lddqu */ + if (op1 == 0xF3 && op2 == 0x0F && (op3 == 0x10 || op3 == 0x11)) { if (!do_movss(regs)) goto invalid_opcode; goto sse_return;