Ich habe den Geode-Patch für den Nehemiah angepasst und verzichte aber bewusst auf ein patchfile, da mit jedem neuen Kernel wieder eine Anpassung erforderlich ist und auch nicht so viel zu ändern ist.
Es funktioniert ab Kernel 2.6.34 - notwendige Anpassungen sind:
- Kernelsources besorgen
- Datei "/arch/x86/kernel/Makefile" editieren - folgende Zeile einfügen:
...
obj-$(CONFIG_APB_TIMER) += apb_timer.o
---- HIER FOLGENDE ZEILE EINFUEGEN ----
obj-$(CONFIG_MVIAC3_2) += nopl_emu.o
obj-$(CONFIG_K8_NB) += k8.o
...
- In Datei "/arch/x86/kernel/entry_32.S" die Funktion "invalid_op" folgendermassen ändern:
ENTRY(invalid_op)
RING0_INT_FRAME
pushl $0
CFI_ADJUST_CFA_OFFSET 4
pushl $do_nopl_emu
/* pushl $do_invalid_op */
CFI_ADJUST_CFA_OFFSET 4
jmp error_code
CFI_ENDPROC
END(invalid_op)
- Neue Datei mit folgendem Inhalt erzeugen und als "/arch/x86/kernel/nopl_emu.c" speichern:
/*
* linux/arch/x86/kernel/nopl_emu.c
*
* Copyright (C) 2002 Willy Tarreau
* Copyright (C) 2009 Matteo Croce
*
* Manfred Miederer did some minimal changes
* to use it for Kernel 2.6.34 and a Via Nehemiah cpu
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/linkage.h>
#include <asm/math_emu.h>
#include <asm/traps.h>
/* This code can be used to allow the AMD Geode / Via Nehemiah 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 <willy@xxxxxxxxxx>
* Matteo Croce <technoboy85@xxxxxxxxx>
*/
static inline int do_1f(u8 *ip) {
int length = 3;
switch (*ip) {
case 0x84:
if (!ip[5]) length++;
else return 0;
case 0x80:
if (!ip[4] && !ip[3]) length += 2;
else return 0;
case 0x44:
if (!ip[2]) length++;
else return 0;
case 0x40:
if (!ip[1]) length++;
else return 0;
case 0x00:
return length;
}
return 0;
}
static inline int do_0f(u8 *ip) {
if (*ip == 0x1f) return do_1f(ip + 1);
return 0;
}
static inline int do_66(u8 *ip) {
if (*ip == 0x90) return 2;
if (*ip == 0x0f) {
int res = do_0f(ip + 1);
if (res) return res + 1;
else return 0;
}
return 0;
}
static inline int do_start(u8 *ip) {
if (*ip == 0x0f) return do_0f(ip + 1);
if (*ip == 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) {
int res = do_start((u8 *)instruction_pointer(regs));
if (res) regs->ip += res;
else do_invalid_op(regs, error_code);
}
Nach einigen Wochen Einsatz: es funktioniert gut!
VG, LW.
P.S.: Falls Du ein patchfile benötigst, kann ich es auf die Schnelle leider nicht erzeugen, da ich keinen archlinux Kernel auf diesem System verwende.