Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / SLOF / lib / libhvcall / brokensc1.c
1 #include <stdint.h>
2 #include <stddef.h>
3 #include <cpu.h>
4 #include "libhvcall.h"
5 #include "byteorder.h"
6
7 // #define DEBUG_PATCHERY
8
9 #define H_SET_DABR      0x28
10 #define INS_SC1         0x44000022
11 #define INS_SC1_REPLACE 0x7c000268
12
13 extern volatile uint32_t sc1ins;
14
15 static unsigned long hcall(uint32_t inst, unsigned long arg0, unsigned long arg1)
16 {
17         register unsigned long r3 asm("r3") = arg0;
18         register unsigned long r4 asm("r4") = arg1;
19         register unsigned long r5 asm("r5") = inst;
20         asm volatile("bl 1f             \n"
21                      "1:                \n"
22                      "li 11, 2f - 1b    \n"
23                      "mflr 12           \n"
24                      "add 11, 11, 12    \n"
25                      "stw 5, 0(11)      \n"
26                      "dcbst 0, 11       \n"
27                      "sync              \n"
28                      "icbi 0, 11        \n"
29                      "isync             \n"
30                      "2:                \n"
31                      ".long 0           \n"
32                      : "=r" (r3)
33                      : "r" (r3), "r" (r4), "r" (r5)
34                      : "ctr", "r0", "r6", "r7", "r8", "r9", "r10", "r11",
35                        "r12", "r13", "r31", "lr", "cc");
36         return r3;
37 }
38
39 static int check_broken_sc1(void)
40 {
41         long r;
42
43         /*
44          * Check if we can do a simple hcall. If it works, we are running in
45          * a sane environment and everything's fine. If it doesn't, we need
46          * to patch the hypercall instruction to something that traps into
47          * supervisor mode.
48          */
49         r = hcall(INS_SC1, H_SET_DABR, 0);
50         if (r == H_SUCCESS || r == H_HARDWARE) {
51                 /* All is fine */
52                 return 0;
53         }
54
55         /* We found a broken sc1 host! */
56         return 1;
57 }
58
59 int patch_broken_sc1(void *start, void *end, uint32_t *test_ins)
60 {
61         uint32_t *p;
62         /* The sc 1 instruction */
63         uint32_t sc1 = INS_SC1;
64         /* An illegal instruction that KVM interprets as sc 1 */
65         uint32_t sc1_replacement = INS_SC1_REPLACE;
66         int is_le = (test_ins && *test_ins == 0x48000008);
67 #ifdef DEBUG_PATCHERY
68         int cnt = 0;
69 #endif
70
71         /* The host is sane, get out of here */
72         if (!check_broken_sc1())
73                 return 0;
74
75         /* We only get here with a broken sc1 implementation */
76
77         /* Trim the range we scan to not cover the data section */
78         if (test_ins) {
79                 /* This is the cpu table matcher for 970FX */
80                 uint32_t end_bytes[] = { 0xffff0000, 0x3c0000 };
81                 /*
82                  * The .__start symbol contains a trap instruction followed
83                  * by lots of zeros.
84                  */
85                 uint32_t start_bytes[] = { 0x7fe00008, 0, 0, 0, 0 };
86
87                 if (is_le) {
88                         end_bytes[0] = bswap_32(end_bytes[0]);
89                         end_bytes[1] = bswap_32(end_bytes[1]);
90                         start_bytes[1] = bswap_32(start_bytes[1]);
91                 }
92
93                 /* Find the start of the text section */
94                 for (p = test_ins; (long)p > (long)start; p--) {
95                         if (p[0] == start_bytes[0] &&
96                             p[1] == start_bytes[1] &&
97                             p[2] == start_bytes[2] &&
98                             p[3] == start_bytes[3] &&
99                             p[4] == start_bytes[4]) {
100                                 /*
101                                  * We found a match of the instruction sequence
102                                  *     trap
103                                  *     .long 0
104                                  *     .long 0
105                                  *     .long 0
106                                  *     .long 0
107                                  * which marks the beginning of the .text
108                                  * section on all Linux kernels I've checked.
109                                  */
110 #ifdef DEBUG_PATCHERY
111                                 printf("Shortened start from %p to %p\n", end, p);
112 #endif
113                                 start = p;
114                                 break;
115                         }
116                 }
117
118                 /* Find the end of the text section */
119                 for (p = start; (long)p < (long)end; p++) {
120                         if (p[0] == end_bytes[0] && p[1] == end_bytes[1]) {
121                                 /*
122                                  * We found a match of the PPC970FX entry in the
123                                  * guest kernel's CPU table. That table is
124                                  * usually found early in the .data section and
125                                  * thus marks the end of the .text section for
126                                  * us which we need to patch.
127                                  */
128 #ifdef DEBUG_PATCHERY
129                                 printf("Shortened end from %p to %p\n", end, p);
130 #endif
131                                 end = p;
132                                 break;
133                         }
134                 }
135         }
136
137         if (is_le) {
138                 /*
139                  * The kernel was built for LE mode, so our sc1 and replacement
140                  * opcodes are in the wrong byte order. Reverse them.
141                  */
142                 sc1 = bswap_32(sc1);
143                 sc1_replacement = bswap_32(sc1_replacement);
144         }
145
146         /* Patch all sc 1 instructions to reserved instruction 31/308 */
147         for (p = start; (long)p < (long)end; p++) {
148                 if (*p == sc1) {
149                         *p = sc1_replacement;
150                         flush_cache(p, sizeof(*p));
151 #ifdef DEBUG_PATCHERY
152                         cnt++;
153 #endif
154                 }
155         }
156
157 #ifdef DEBUG_PATCHERY
158         printf("Patched %d instructions (%p - %p)\n", cnt, start, end);
159 #endif
160
161         return 1;
162 }