Skip to content

Commit 2f6c9bf

Browse files
committed
sparc: Improve VDSO instruction patching.
The current VDSO patch mechanism has several problems: 1) It assumes how gcc will emit a function, with a register window, an initial save instruction and then immediately the %tick read when compiling vread_tick(). There is no such guarantees, code generation could change at any time, gcc could put a nop between the save and the %tick read, etc. So this is extremely fragile and would fail some day. 2) It disallows us to properly inline vread_tick() into the callers and thus get the best possible code sequences. So fix this to patch properly, with location based annotations. We have to be careful because we cannot do it the way we do patches elsewhere in the kernel. Those use a sequence like: 1: insn .section .whatever_patch, "ax" .word 1b replacement_insn .previous This is a dynamic shared object, so that .word cannot be resolved at build time, and thus cannot be used to execute the patches when the kernel initializes the images. Even trying to use label difference equations doesn't work in the above kind of scheme: 1: insn .section .whatever_patch, "ax" .word . - 1b replacement_insn .previous The assembler complains that it cannot resolve that computation. The issue is that this is contained in an executable section. Borrow the sequence used by x86 alternatives, which is: 1: insn .pushsection .whatever_patch, "a" .word . - 1b, . - 1f .popsection .pushsection .whatever_patch_replacements, "ax" 1: replacement_insn .previous This works, allows us to inline vread_tick() as much as we like, and can be used for arbitrary kinds of VDSO patching in the future. Also, reverse the condition for patching. Most systems are %stick based, so if we only patch on %tick systems the patching code will get little or no testing. Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 46b8306 commit 2f6c9bf

File tree

7 files changed

+68
-52
lines changed

7 files changed

+68
-52
lines changed

arch/sparc/include/asm/vdso.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
struct vdso_image {
99
void *data;
1010
unsigned long size; /* Always a multiple of PAGE_SIZE */
11+
12+
unsigned long tick_patch, tick_patch_len;
13+
1114
long sym_vvar_start; /* Negative offset to the vvar area */
12-
long sym_vread_tick; /* Start of vread_tick section */
13-
long sym_vread_tick_patch_start; /* Start of tick read */
14-
long sym_vread_tick_patch_end; /* End of tick read */
1515
};
1616

1717
#ifdef CONFIG_SPARC64

arch/sparc/kernel/time_64.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,6 @@
5353

5454
DEFINE_SPINLOCK(rtc_lock);
5555

56-
unsigned int __read_mostly vdso_fix_stick;
57-
5856
#ifdef CONFIG_SMP
5957
unsigned long profile_pc(struct pt_regs *regs)
6058
{
@@ -838,7 +836,6 @@ void __init time_init_early(void)
838836
} else {
839837
init_tick_ops(&tick_operations);
840838
clocksource_tick.archdata.vclock_mode = VCLOCK_TICK;
841-
vdso_fix_stick = 1;
842839
}
843840
} else {
844841
init_tick_ops(&stick_operations);

arch/sparc/vdso/vclock_gettime.c

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -105,29 +105,36 @@ static notrace noinline u64
105105
vread_tick(void) {
106106
u64 ret;
107107

108-
__asm__ __volatile__("rd %%asr24, %0 \n"
109-
".section .vread_tick_patch, \"ax\" \n"
110-
"rd %%tick, %0 \n"
111-
".previous \n"
112-
: "=&r" (ret));
108+
__asm__ __volatile__("1:\n\t"
109+
"rd %%tick, %0\n\t"
110+
".pushsection .tick_patch, \"a\"\n\t"
111+
".word 1b - ., 1f - .\n\t"
112+
".popsection\n\t"
113+
".pushsection .tick_patch_replacement, \"ax\"\n\t"
114+
"1:\n\t"
115+
"rd %%asr24, %0\n\t"
116+
".popsection\n"
117+
: "=r" (ret));
113118
return ret & ~TICK_PRIV_BIT;
114119
}
115120
#else
116121
static notrace noinline u64
117122
vread_tick(void)
118123
{
119-
unsigned int lo, hi;
120-
121-
__asm__ __volatile__("rd %%asr24, %%g1\n\t"
122-
"srlx %%g1, 32, %1\n\t"
123-
"srl %%g1, 0, %0\n"
124-
".section .vread_tick_patch, \"ax\" \n"
125-
"rd %%tick, %%g1\n"
126-
".previous \n"
127-
: "=&r" (lo), "=&r" (hi)
128-
:
129-
: "g1");
130-
return lo | ((u64)hi << 32);
124+
register unsigned long long ret asm("o4");
125+
126+
__asm__ __volatile__("1:\n\t"
127+
"rd %%tick, %L0\n\t"
128+
"srlx %L0, 32, %H0\n\t"
129+
".pushsection .tick_patch, \"a\"\n\t"
130+
".word 1b - ., 1f - .\n\t"
131+
".popsection\n\t"
132+
".pushsection .tick_patch_replacement, \"ax\"\n\t"
133+
"1:\n\t"
134+
"rd %%asr24, %L0\n\t"
135+
".popsection\n"
136+
: "=r" (ret));
137+
return ret;
131138
}
132139
#endif
133140

arch/sparc/vdso/vdso-layout.lds.S

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,8 @@ SECTIONS
7373

7474
.text : { *(.text*) } :text =0x90909090,
7575

76-
.vread_tick_patch : {
77-
vread_tick_patch_start = .;
78-
*(.vread_tick_patch)
79-
vread_tick_patch_end = .;
80-
}
76+
.tick_patch : { *(.tick_patch) } :text
77+
.tick_patch_insns : { *(.tick_patch_insns) } :text
8178

8279
/DISCARD/ : {
8380
*(.discard)

arch/sparc/vdso/vdso2c.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,6 @@ enum {
6363
sym_vvar_start,
6464
sym_VDSO_FAKE_SECTION_TABLE_START,
6565
sym_VDSO_FAKE_SECTION_TABLE_END,
66-
sym_vread_tick,
67-
sym_vread_tick_patch_start,
68-
sym_vread_tick_patch_end
6966
};
7067

7168
struct vdso_sym {
@@ -81,9 +78,6 @@ struct vdso_sym required_syms[] = {
8178
[sym_VDSO_FAKE_SECTION_TABLE_END] = {
8279
"VDSO_FAKE_SECTION_TABLE_END", 0
8380
},
84-
[sym_vread_tick] = {"vread_tick", 1},
85-
[sym_vread_tick_patch_start] = {"vread_tick_patch_start", 1},
86-
[sym_vread_tick_patch_end] = {"vread_tick_patch_end", 1}
8781
};
8882

8983
__attribute__((format(printf, 1, 2))) __attribute__((noreturn))

arch/sparc/vdso/vdso2c.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
1717
unsigned long mapping_size;
1818
int i;
1919
unsigned long j;
20-
21-
ELF(Shdr) *symtab_hdr = NULL, *strtab_hdr;
20+
ELF(Shdr) *symtab_hdr = NULL, *strtab_hdr, *secstrings_hdr,
21+
*patch_sec = NULL;
2222
ELF(Ehdr) *hdr = (ELF(Ehdr) *)raw_addr;
2323
ELF(Dyn) *dyn = 0, *dyn_end = 0;
24+
const char *secstrings;
2425
INT_BITS syms[NSYMS] = {};
2526

2627
ELF(Phdr) *pt = (ELF(Phdr) *)(raw_addr + GET_BE(&hdr->e_phoff));
@@ -63,11 +64,18 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
6364
}
6465

6566
/* Walk the section table */
67+
secstrings_hdr = raw_addr + GET_BE(&hdr->e_shoff) +
68+
GET_BE(&hdr->e_shentsize)*GET_BE(&hdr->e_shstrndx);
69+
secstrings = raw_addr + GET_BE(&secstrings_hdr->sh_offset);
6670
for (i = 0; i < GET_BE(&hdr->e_shnum); i++) {
6771
ELF(Shdr) *sh = raw_addr + GET_BE(&hdr->e_shoff) +
6872
GET_BE(&hdr->e_shentsize) * i;
6973
if (GET_BE(&sh->sh_type) == SHT_SYMTAB)
7074
symtab_hdr = sh;
75+
76+
if (!strcmp(secstrings + GET_BE(&sh->sh_name),
77+
".tick_patch"))
78+
patch_sec = sh;
7179
}
7280

7381
if (!symtab_hdr)
@@ -134,6 +142,12 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
134142
fprintf(outfile, "const struct vdso_image %s_builtin = {\n", name);
135143
fprintf(outfile, "\t.data = raw_data,\n");
136144
fprintf(outfile, "\t.size = %lu,\n", mapping_size);
145+
if (patch_sec) {
146+
fprintf(outfile, "\t.tick_patch = %lu,\n",
147+
(unsigned long)GET_BE(&patch_sec->sh_offset));
148+
fprintf(outfile, "\t.tick_patch_len = %lu,\n",
149+
(unsigned long)GET_BE(&patch_sec->sh_size));
150+
}
137151
for (i = 0; i < NSYMS; i++) {
138152
if (required_syms[i].export && syms[i])
139153
fprintf(outfile, "\t.sym_%s = %" PRIi64 ",\n",

arch/sparc/vdso/vma.c

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#include <linux/linkage.h>
1717
#include <linux/random.h>
1818
#include <linux/elf.h>
19+
#include <asm/cacheflush.h>
20+
#include <asm/spitfire.h>
1921
#include <asm/vdso.h>
2022
#include <asm/vvar.h>
2123
#include <asm/page.h>
@@ -40,7 +42,25 @@ static struct vm_special_mapping vdso_mapping32 = {
4042

4143
struct vvar_data *vvar_data;
4244

43-
#define SAVE_INSTR_SIZE 4
45+
struct tick_patch_entry {
46+
s32 orig, repl;
47+
};
48+
49+
static void stick_patch(const struct vdso_image *image)
50+
{
51+
struct tick_patch_entry *p, *p_end;
52+
53+
p = image->data + image->tick_patch;
54+
p_end = (void *)p + image->tick_patch_len;
55+
while (p < p_end) {
56+
u32 *instr = (void *)&p->orig + p->orig;
57+
u32 *repl = (void *)&p->repl + p->repl;
58+
59+
*instr = *repl;
60+
flushi(instr);
61+
p++;
62+
}
63+
}
4464

4565
/*
4666
* Allocate pages for the vdso and vvar, and copy in the vdso text from the
@@ -68,21 +88,8 @@ int __init init_vdso_image(const struct vdso_image *image,
6888
if (!cpp)
6989
goto oom;
7090

71-
if (vdso_fix_stick) {
72-
/*
73-
* If the system uses %tick instead of %stick, patch the VDSO
74-
* with instruction reading %tick instead of %stick.
75-
*/
76-
unsigned int j, k = SAVE_INSTR_SIZE;
77-
unsigned char *data = image->data;
78-
79-
for (j = image->sym_vread_tick_patch_start;
80-
j < image->sym_vread_tick_patch_end; j++) {
81-
82-
data[image->sym_vread_tick + k] = data[j];
83-
k++;
84-
}
85-
}
91+
if (tlb_type != spitfire)
92+
stick_patch(image);
8693

8794
for (i = 0; i < cnpages; i++) {
8895
cp = alloc_page(GFP_KERNEL);

0 commit comments

Comments
 (0)