diff -Naur linux-3.0.68.i686/arch/x86/Kconfig.cpu kernel-lt-3.0.68/linux-3.0.68.i686/arch/x86/Kconfig.cpu --- linux-3.0.68.i686/arch/x86/Kconfig.cpu 2013-03-03 17:09:28.000000000 -0500 +++ kernel-lt-3.0.68/linux-3.0.68.i686/arch/x86/Kconfig.cpu 2013-03-13 10:53:28.000000000 -0400 @@ -508,3 +508,15 @@ CPU might render the kernel unbootable. If unsure, say N. + +config GEODE_NOPL + default n + bool "NOPL emulation for Geode LX CPU to promote it to i686" + depends on M686 && !MGEODE_LX && !MGEODEGX1 + ---help--- + This enables NOPL instruction emulation for Geode LX CPUs + Using this emulation, the Geode LX can be promoted to full i686. + Do NOT select a GEODE CPU type, but select i686 instead. + + If unsure, say N. + diff -Naur linux-3.0.68.i686/arch/x86/kernel/cpu/amd.c kernel-lt-3.0.68/linux-3.0.68.i686/arch/x86/kernel/cpu/amd.c --- linux-3.0.68.i686/arch/x86/kernel/cpu/amd.c 2013-03-03 17:09:28.000000000 -0500 +++ kernel-lt-3.0.68/linux-3.0.68.i686/arch/x86/kernel/cpu/amd.c 2013-03-13 10:46:37.000000000 -0400 @@ -137,11 +137,15 @@ return; } +#ifdef CONFIG_GEODE_NOPL if (c->x86_model == 10) { - /* AMD Geode LX is model 10 */ - /* placeholder for any needed mods */ + /* Geode only lacks the NOPL instruction to be i686, + but we can promote it to a i686 class cpu + and emulate NOPLs in the exception handler*/ + boot_cpu_data.x86 = 6; return; } +#endif } static void __cpuinit amd_k7_smp_check(struct cpuinfo_x86 *c) diff -Naur linux-3.0.68.i686/arch/x86/kernel/entry_32.S kernel-lt-3.0.68/linux-3.0.68.i686/arch/x86/kernel/entry_32.S --- linux-3.0.68.i686/arch/x86/kernel/entry_32.S 2013-03-03 17:09:28.000000000 -0500 +++ kernel-lt-3.0.68/linux-3.0.68.i686/arch/x86/kernel/entry_32.S 2013-03-13 10:46:37.000000000 -0400 @@ -936,7 +936,11 @@ ENTRY(invalid_op) RING0_INT_FRAME pushl_cfi $0 +#ifdef CONFIG_GEODE_NOPL + pushl_cfi $do_nopl_emu +#else pushl_cfi $do_invalid_op +#endif jmp error_code CFI_ENDPROC END(invalid_op) diff -Naur linux-3.0.68.i686/arch/x86/kernel/Makefile kernel-lt-3.0.68/linux-3.0.68.i686/arch/x86/kernel/Makefile --- linux-3.0.68.i686/arch/x86/kernel/Makefile 2013-03-03 17:09:28.000000000 -0500 +++ kernel-lt-3.0.68/linux-3.0.68.i686/arch/x86/kernel/Makefile 2013-03-13 10:46:37.000000000 -0400 @@ -94,6 +94,8 @@ obj-$(CONFIG_HPET_TIMER) += hpet.o obj-$(CONFIG_APB_TIMER) += apb_timer.o +obj-$(CONFIG_GEODE_NOPL) += nopl_emu.o + obj-$(CONFIG_AMD_NB) += amd_nb.o obj-$(CONFIG_DEBUG_RODATA_TEST) += test_rodata.o obj-$(CONFIG_DEBUG_NX_TEST) += test_nx.o diff -Naur linux-3.0.68.i686/arch/x86/kernel/nopl_emu.c kernel-lt-3.0.68/linux-3.0.68.i686/arch/x86/kernel/nopl_emu.c --- linux-3.0.68.i686/arch/x86/kernel/nopl_emu.c 1969-12-31 19:00:00.000000000 -0500 +++ kernel-lt-3.0.68/linux-3.0.68.i686/arch/x86/kernel/nopl_emu.c 2013-03-13 10:46:37.000000000 -0400 @@ -0,0 +1,128 @@ +/* + * linux/arch/x86/kernel/nopl_emu.c + * + * Copyright (C) 2002 Willy Tarreau + * Copyright (C) 2010 Matteo Croce + */ + +#include +#include +#include + +/* This code can be used to allow the AMD Geode to hopefully correctly execute + * some code which was originally compiled for an i686, by emulating NOPL, + * the only missing i686 instruction in the CPU + * + * Willy Tarreau + * Matteo Croce + */ + +static inline int do_1f(u8 *ip) +{ + u8 val1, val2; + int length = 3; + if (get_user(val1, ip)) + return 0; + switch (val1) { + case 0x84: + get_user(val1, ip + 5); + if (!val1) + length++; + else + return 0; + case 0x80: + get_user(val1, ip + 4); + get_user(val2, ip + 3); + if (!val1 && !val2) + length += 2; + else + return 0; + case 0x44: + get_user(val1, ip + 2); + if (!val1) + length++; + else + return 0; + case 0x40: + get_user(val1, ip + 1); + if (!val1) + length++; + else + return 0; + case 0x00: + return length; + } + return 0; +} + +static inline int do_0f(u8 *ip) +{ + u8 val; + if (get_user(val, ip)) + return 0; + if (val == 0x1f) + return do_1f(ip + 1); + return 0; +} + +static inline int do_66(u8 *ip) +{ + u8 val; + if (get_user(val, ip)) + return 0; + if (val == 0x90) + return 2; + if (val == 0x0f) { + int res = do_0f(ip + 1); + if (res) + return res + 1; + else + return 0; + } + return 0; +} + +static inline int do_start(u8 *ip) +{ + u8 val; + if (get_user(val, ip)) + return 0; + if (val == 0x0f) + return do_0f(ip + 1); + if (val == 0x66) + return do_66(ip + 1); + return 0; +} + +/* [do_nopl_emu] is called by exception 6 after an invalid opcode has been + * encountered. It will try to emulate it by doing nothing, + * and will send a SIGILL or SIGSEGV to the process if not possible. + * the NOPL can have variable length opcodes: + +bytes number opcode + 2 66 90 + 3 0f 1f 00 + 4 0f 1f 40 00 + 5 0f 1f 44 00 00 + 6 66 0f 1f 44 00 00 + 7 0f 1f 80 00 00 00 00 + 8 0f 1f 84 00 00 00 00 00 + 9 66 0f 1f 84 00 00 00 00 00 +*/ +void do_nopl_emu(struct pt_regs *regs, long error_code) +{ + u8 *ip = (u8 *)instruction_pointer(regs); + int res = do_start(ip); + + if (res) { + int i = 0; + do { + ip += res; + i++; + res = do_start(ip); + } while(res); + printk(KERN_DEBUG "geode_nopl: emulated %d instructions\n", i); + regs->ip = (typeof(regs->ip))ip; + } else + do_invalid_op(regs, error_code); +}