1 | /****************************************************************************** |
---|
2 | * xenpatch.c |
---|
3 | * Copyright (c) 2006 Silicon Graphics Inc. |
---|
4 | * Jes Sorensen <jes@sgi.com> |
---|
5 | * |
---|
6 | * This program is free software; you can redistribute it and/or modify it |
---|
7 | * under the terms and conditions of the GNU General Public License, |
---|
8 | * version 2, as published by the Free Software Foundation. |
---|
9 | * |
---|
10 | * This program is distributed in the hope it will be useful, but WITHOUT |
---|
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
---|
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
---|
13 | * more details. |
---|
14 | * |
---|
15 | * You should have received a copy of the GNU General Public License along with |
---|
16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple |
---|
17 | * Place - Suite 330, Boston, MA 02111-1307 USA. |
---|
18 | * |
---|
19 | * Parts of this based on code from arch/ia64/kernel/patch.c |
---|
20 | */ |
---|
21 | |
---|
22 | #include <xen/config.h> |
---|
23 | #include <xen/lib.h> |
---|
24 | #include <asm/xensystem.h> |
---|
25 | #include <asm/intrinsics.h> |
---|
26 | |
---|
27 | /* |
---|
28 | * This was adapted from code written by Tony Luck: |
---|
29 | * |
---|
30 | * The 64-bit value in a "movl reg=value" is scattered between the two words of the bundle |
---|
31 | * like this: |
---|
32 | * |
---|
33 | * 6 6 5 4 3 2 1 |
---|
34 | * 3210987654321098765432109876543210987654321098765432109876543210 |
---|
35 | * ABBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCDEEEEEFFFFFFFFFGGGGGGG |
---|
36 | * |
---|
37 | * CCCCCCCCCCCCCCCCCCxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
---|
38 | * xxxxAFFFFFFFFFEEEEEDxGGGGGGGxxxxxxxxxxxxxBBBBBBBBBBBBBBBBBBBBBBB |
---|
39 | */ |
---|
40 | static u64 |
---|
41 | get_imm64 (u64 insn_addr) |
---|
42 | { |
---|
43 | u64 *p = (u64 *) (insn_addr & -16); /* mask out slot number */ |
---|
44 | |
---|
45 | return ( (p[1] & 0x0800000000000000UL) << 4) | /*A*/ |
---|
46 | ((p[1] & 0x00000000007fffffUL) << 40) | /*B*/ |
---|
47 | ((p[0] & 0xffffc00000000000UL) >> 24) | /*C*/ |
---|
48 | ((p[1] & 0x0000100000000000UL) >> 23) | /*D*/ |
---|
49 | ((p[1] & 0x0003e00000000000UL) >> 29) | /*E*/ |
---|
50 | ((p[1] & 0x07fc000000000000UL) >> 43) | /*F*/ |
---|
51 | ((p[1] & 0x000007f000000000UL) >> 36); /*G*/ |
---|
52 | } |
---|
53 | |
---|
54 | /* Patch instruction with "val" where "mask" has 1 bits. */ |
---|
55 | void |
---|
56 | ia64_patch (u64 insn_addr, u64 mask, u64 val) |
---|
57 | { |
---|
58 | u64 m0, m1, v0, v1, b0, b1, *b = (u64 *) (insn_addr & -16); |
---|
59 | #define insn_mask ((1UL << 41) - 1) |
---|
60 | unsigned long shift; |
---|
61 | |
---|
62 | b0 = b[0]; b1 = b[1]; |
---|
63 | /* 5 bits of template, then 3 x 41-bit instructions */ |
---|
64 | shift = 5 + 41 * (insn_addr % 16); |
---|
65 | if (shift >= 64) { |
---|
66 | m1 = mask << (shift - 64); |
---|
67 | v1 = val << (shift - 64); |
---|
68 | } else { |
---|
69 | m0 = mask << shift; m1 = mask >> (64 - shift); |
---|
70 | v0 = val << shift; v1 = val >> (64 - shift); |
---|
71 | b[0] = (b0 & ~m0) | (v0 & m0); |
---|
72 | } |
---|
73 | b[1] = (b1 & ~m1) | (v1 & m1); |
---|
74 | } |
---|
75 | |
---|
76 | void |
---|
77 | ia64_patch_imm64 (u64 insn_addr, u64 val) |
---|
78 | { |
---|
79 | /* The assembler may generate offset pointing to either slot 1 |
---|
80 | or slot 2 for a long (2-slot) instruction, occupying slots 1 |
---|
81 | and 2. */ |
---|
82 | insn_addr &= -16UL; |
---|
83 | ia64_patch(insn_addr + 2, 0x01fffefe000UL, |
---|
84 | (((val & 0x8000000000000000UL) >> 27) | /* bit 63 -> 36 */ |
---|
85 | ((val & 0x0000000000200000UL) << 0) | /* bit 21 -> 21 */ |
---|
86 | ((val & 0x00000000001f0000UL) << 6) | /* bit 16 -> 22 */ |
---|
87 | ((val & 0x000000000000ff80UL) << 20) | /* bit 7 -> 27 */ |
---|
88 | ((val & 0x000000000000007fUL) << 13) /* bit 0 -> 13 */)); |
---|
89 | ia64_patch(insn_addr + 1, 0x1ffffffffffUL, val >> 22); |
---|
90 | } |
---|
91 | |
---|
92 | extern char frametable_miss; |
---|
93 | extern unsigned long xen_pstart; |
---|
94 | |
---|
95 | /* |
---|
96 | * Add more patch points in seperate functions as appropriate |
---|
97 | */ |
---|
98 | |
---|
99 | static void xen_patch_frametable_miss(u64 offset) |
---|
100 | { |
---|
101 | u64 addr, val; |
---|
102 | |
---|
103 | addr = (u64)&frametable_miss; |
---|
104 | val = get_imm64(addr) + offset; |
---|
105 | ia64_patch_imm64(addr, val); |
---|
106 | } |
---|
107 | |
---|
108 | |
---|
109 | void xen_patch_kernel(void) |
---|
110 | { |
---|
111 | unsigned long patch_offset; |
---|
112 | |
---|
113 | patch_offset = xen_pstart - (KERNEL_START - PAGE_OFFSET); |
---|
114 | |
---|
115 | printk("Xen patching physical address access by offset: " |
---|
116 | "0x%lx\n", patch_offset); |
---|
117 | |
---|
118 | xen_patch_frametable_miss(patch_offset); |
---|
119 | |
---|
120 | ia64_sync_i(); |
---|
121 | ia64_srlz_i(); |
---|
122 | } |
---|