162 lines
4 KiB
C
162 lines
4 KiB
C
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <cpu.h>
|
|
#include "libhvcall.h"
|
|
#include "byteorder.h"
|
|
|
|
// #define DEBUG_PATCHERY
|
|
|
|
#define H_SET_DABR 0x28
|
|
#define INS_SC1 0x44000022
|
|
#define INS_SC1_REPLACE 0x7c000268
|
|
|
|
extern volatile uint32_t sc1ins;
|
|
|
|
static unsigned long hcall(uint32_t inst, unsigned long arg0, unsigned long arg1)
|
|
{
|
|
register unsigned long r3 asm("r3") = arg0;
|
|
register unsigned long r4 asm("r4") = arg1;
|
|
register unsigned long r5 asm("r5") = inst;
|
|
asm volatile("bl 1f \n"
|
|
"1: \n"
|
|
"li 11, 2f - 1b \n"
|
|
"mflr 12 \n"
|
|
"add 11, 11, 12 \n"
|
|
"stw 5, 0(11) \n"
|
|
"dcbst 0, 11 \n"
|
|
"sync \n"
|
|
"icbi 0, 11 \n"
|
|
"isync \n"
|
|
"2: \n"
|
|
".long 0 \n"
|
|
: "=r" (r3)
|
|
: "r" (r3), "r" (r4), "r" (r5)
|
|
: "ctr", "r0", "r6", "r7", "r8", "r9", "r10", "r11",
|
|
"r12", "r13", "r31", "lr", "cc");
|
|
return r3;
|
|
}
|
|
|
|
int check_broken_sc1(void)
|
|
{
|
|
long r;
|
|
|
|
/*
|
|
* Check if we can do a simple hcall. If it works, we are running in
|
|
* a sane environment and everything's fine. If it doesn't, we need
|
|
* to patch the hypercall instruction to something that traps into
|
|
* supervisor mode.
|
|
*/
|
|
r = hcall(INS_SC1, H_SET_DABR, 0);
|
|
if (r == H_PRIVILEGE) {
|
|
/* We found a broken sc1 host! */
|
|
return 1;
|
|
}
|
|
|
|
/* All is fine */
|
|
return 0;
|
|
}
|
|
|
|
int patch_broken_sc1(void *start, void *end, uint32_t *test_ins)
|
|
{
|
|
uint32_t *p;
|
|
/* The sc 1 instruction */
|
|
uint32_t sc1 = INS_SC1;
|
|
/* An illegal instruction that KVM interprets as sc 1 */
|
|
uint32_t sc1_replacement = INS_SC1_REPLACE;
|
|
int is_le = (test_ins && *test_ins == 0x48000008);
|
|
#ifdef DEBUG_PATCHERY
|
|
int cnt = 0;
|
|
#endif
|
|
|
|
/* The host is sane, get out of here */
|
|
if (!check_broken_sc1())
|
|
return 0;
|
|
|
|
/* We only get here with a broken sc1 implementation */
|
|
|
|
/* Trim the range we scan to not cover the data section */
|
|
if (test_ins) {
|
|
/* This is the cpu table matcher for 970FX */
|
|
uint32_t end_bytes[] = { 0xffff0000, 0x3c0000 };
|
|
/*
|
|
* The .__start symbol contains a trap instruction followed
|
|
* by lots of zeros.
|
|
*/
|
|
uint32_t start_bytes[] = { 0x7fe00008, 0, 0, 0, 0 };
|
|
|
|
if (is_le) {
|
|
end_bytes[0] = bswap_32(end_bytes[0]);
|
|
end_bytes[1] = bswap_32(end_bytes[1]);
|
|
start_bytes[1] = bswap_32(start_bytes[1]);
|
|
}
|
|
|
|
/* Find the start of the text section */
|
|
for (p = test_ins; (long)p > (long)start; p--) {
|
|
if (p[0] == start_bytes[0] &&
|
|
p[1] == start_bytes[1] &&
|
|
p[2] == start_bytes[2] &&
|
|
p[3] == start_bytes[3] &&
|
|
p[4] == start_bytes[4]) {
|
|
/*
|
|
* We found a match of the instruction sequence
|
|
* trap
|
|
* .long 0
|
|
* .long 0
|
|
* .long 0
|
|
* .long 0
|
|
* which marks the beginning of the .text
|
|
* section on all Linux kernels I've checked.
|
|
*/
|
|
#ifdef DEBUG_PATCHERY
|
|
printf("Shortened start from %p to %p\n", end, p);
|
|
#endif
|
|
start = p;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Find the end of the text section */
|
|
for (p = start; (long)p < (long)end; p++) {
|
|
if (p[0] == end_bytes[0] && p[1] == end_bytes[1]) {
|
|
/*
|
|
* We found a match of the PPC970FX entry in the
|
|
* guest kernel's CPU table. That table is
|
|
* usually found early in the .data section and
|
|
* thus marks the end of the .text section for
|
|
* us which we need to patch.
|
|
*/
|
|
#ifdef DEBUG_PATCHERY
|
|
printf("Shortened end from %p to %p\n", end, p);
|
|
#endif
|
|
end = p;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (is_le) {
|
|
/*
|
|
* The kernel was built for LE mode, so our sc1 and replacement
|
|
* opcodes are in the wrong byte order. Reverse them.
|
|
*/
|
|
sc1 = bswap_32(sc1);
|
|
sc1_replacement = bswap_32(sc1_replacement);
|
|
}
|
|
|
|
/* Patch all sc 1 instructions to reserved instruction 31/308 */
|
|
for (p = start; (long)p < (long)end; p++) {
|
|
if (*p == sc1) {
|
|
*p = sc1_replacement;
|
|
flush_cache(p, sizeof(*p));
|
|
#ifdef DEBUG_PATCHERY
|
|
cnt++;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_PATCHERY
|
|
printf("Patched %d instructions (%p - %p)\n", cnt, start, end);
|
|
#endif
|
|
|
|
return 1;
|
|
}
|