]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/powerpc/lib/feature-fixups.c
powerpc: Add logic to patch alternative feature sections
[linux-2.6-omap-h63xx.git] / arch / powerpc / lib / feature-fixups.c
1 /*
2  *  Copyright (C) 2001 Ben. Herrenschmidt (benh@kernel.crashing.org)
3  *
4  *  Modifications for ppc64:
5  *      Copyright (C) 2003 Dave Engebretsen <engebret@us.ibm.com>
6  *
7  *  Copyright 2008 Michael Ellerman, IBM Corporation.
8  *
9  *  This program is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU General Public License
11  *  as published by the Free Software Foundation; either version
12  *  2 of the License, or (at your option) any later version.
13  */
14
15 #include <linux/kernel.h>
16 #include <asm/cputable.h>
17 #include <asm/code-patching.h>
18
19
20 struct fixup_entry {
21         unsigned long   mask;
22         unsigned long   value;
23         long            start_off;
24         long            end_off;
25         long            alt_start_off;
26         long            alt_end_off;
27 };
28
29 static unsigned int *calc_addr(struct fixup_entry *fcur, long offset)
30 {
31         /*
32          * We store the offset to the code as a negative offset from
33          * the start of the alt_entry, to support the VDSO. This
34          * routine converts that back into an actual address.
35          */
36         return (unsigned int *)((unsigned long)fcur + offset);
37 }
38
39 static int patch_alt_instruction(unsigned int *src, unsigned int *dest,
40                                  unsigned int *alt_start, unsigned int *alt_end)
41 {
42         unsigned int instr;
43
44         instr = *src;
45
46         if (instr_is_relative_branch(*src)) {
47                 unsigned int *target = (unsigned int *)branch_target(src);
48
49                 /* Branch within the section doesn't need translating */
50                 if (target < alt_start || target >= alt_end) {
51                         instr = translate_branch(dest, src);
52                         if (!instr)
53                                 return 1;
54                 }
55         }
56
57         patch_instruction(dest, instr);
58
59         return 0;
60 }
61
62 static int patch_feature_section(unsigned long value, struct fixup_entry *fcur)
63 {
64         unsigned int *start, *end, *alt_start, *alt_end, *src, *dest;
65
66         start = calc_addr(fcur, fcur->start_off);
67         end = calc_addr(fcur, fcur->end_off);
68         alt_start = calc_addr(fcur, fcur->alt_start_off);
69         alt_end = calc_addr(fcur, fcur->alt_end_off);
70
71         if ((alt_end - alt_start) > (end - start))
72                 return 1;
73
74         if ((value & fcur->mask) == fcur->value)
75                 return 0;
76
77         src = alt_start;
78         dest = start;
79
80         for (; src < alt_end; src++, dest++) {
81                 if (patch_alt_instruction(src, dest, alt_start, alt_end))
82                         return 1;
83         }
84
85         for (; dest < end; dest++)
86                 patch_instruction(dest, PPC_NOP_INSTR);
87
88         return 0;
89 }
90
91 void do_feature_fixups(unsigned long value, void *fixup_start, void *fixup_end)
92 {
93         struct fixup_entry *fcur, *fend;
94
95         fcur = fixup_start;
96         fend = fixup_end;
97
98         for (; fcur < fend; fcur++) {
99                 if (patch_feature_section(value, fcur)) {
100                         __WARN();
101                         printk("Unable to patch feature section at %p - %p" \
102                                 " with %p - %p\n",
103                                 calc_addr(fcur, fcur->start_off),
104                                 calc_addr(fcur, fcur->end_off),
105                                 calc_addr(fcur, fcur->alt_start_off),
106                                 calc_addr(fcur, fcur->alt_end_off));
107                 }
108         }
109 }