Linux Audio

Check our new training course

Loading...
  1/*
  2 * Copyright 2010 Tilera Corporation. All Rights Reserved.
  3 *
  4 *   This program is free software; you can redistribute it and/or
  5 *   modify it under the terms of the GNU General Public License
  6 *   as published by the Free Software Foundation, version 2.
  7 *
  8 *   This program is distributed in the hope that it will be useful, but
  9 *   WITHOUT ANY WARRANTY; without even the implied warranty of
 10 *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
 11 *   NON INFRINGEMENT.  See the GNU General Public License for
 12 *   more details.
 13 *
 14 * Based on i386 version, copyright (C) 2001 Rusty Russell.
 15 */
 16
 17#include <linux/moduleloader.h>
 18#include <linux/elf.h>
 19#include <linux/vmalloc.h>
 20#include <linux/fs.h>
 21#include <linux/string.h>
 22#include <linux/kernel.h>
 23#include <asm/pgtable.h>
 24#include <asm/homecache.h>
 25#include <arch/opcode.h>
 26
 27#ifdef __tilegx__
 28# define Elf_Rela Elf64_Rela
 29# define ELF_R_SYM ELF64_R_SYM
 30# define ELF_R_TYPE ELF64_R_TYPE
 31#else
 32# define Elf_Rela Elf32_Rela
 33# define ELF_R_SYM ELF32_R_SYM
 34# define ELF_R_TYPE ELF32_R_TYPE
 35#endif
 36
 37#ifdef MODULE_DEBUG
 38#define DEBUGP printk
 39#else
 40#define DEBUGP(fmt...)
 41#endif
 42
 43/*
 44 * Allocate some address space in the range MEM_MODULE_START to
 45 * MEM_MODULE_END and populate it with memory.
 46 */
 47void *module_alloc(unsigned long size)
 48{
 49	struct page **pages;
 50	pgprot_t prot_rwx = __pgprot(_PAGE_KERNEL | _PAGE_KERNEL_EXEC);
 51	struct vm_struct *area;
 52	int i = 0;
 53	int npages;
 54
 55	if (size == 0)
 56		return NULL;
 57	npages = (size + PAGE_SIZE - 1) / PAGE_SIZE;
 58	pages = kmalloc(npages * sizeof(struct page *), GFP_KERNEL);
 59	if (pages == NULL)
 60		return NULL;
 61	for (; i < npages; ++i) {
 62		pages[i] = alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
 63		if (!pages[i])
 64			goto error;
 65	}
 66
 67	area = __get_vm_area(size, VM_ALLOC, MEM_MODULE_START, MEM_MODULE_END);
 68	if (!area)
 69		goto error;
 70	area->nr_pages = npages;
 71	area->pages = pages;
 72
 73	if (map_vm_area(area, prot_rwx, &pages)) {
 74		vunmap(area->addr);
 75		goto error;
 76	}
 77
 78	return area->addr;
 79
 80error:
 81	while (--i >= 0)
 82		__free_page(pages[i]);
 83	kfree(pages);
 84	return NULL;
 85}
 86
 87
 88/* Free memory returned from module_alloc */
 89void module_free(struct module *mod, void *module_region)
 90{
 91	vfree(module_region);
 92
 93	/* Globally flush the L1 icache. */
 94	flush_remote(0, HV_FLUSH_EVICT_L1I, cpu_online_mask,
 95		     0, 0, 0, NULL, NULL, 0);
 96
 97	/*
 98	 * FIXME: If module_region == mod->module_init, trim exception
 99	 * table entries.
100	 */
101}
102
103#ifdef __tilegx__
104/*
105 * Validate that the high 16 bits of "value" is just the sign-extension of
106 * the low 48 bits.
107 */
108static int validate_hw2_last(long value, struct module *me)
109{
110	if (((value << 16) >> 16) != value) {
111		pr_warning("module %s: Out of range HW2_LAST value %#lx\n",
112			   me->name, value);
113		return 0;
114	}
115	return 1;
116}
117
118/*
119 * Validate that "value" isn't too big to hold in a JumpOff relocation.
120 */
121static int validate_jumpoff(long value)
122{
123	/* Determine size of jump offset. */
124	int shift = __builtin_clzl(get_JumpOff_X1(create_JumpOff_X1(-1)));
125
126	/* Check to see if it fits into the relocation slot. */
127	long f = get_JumpOff_X1(create_JumpOff_X1(value));
128	f = (f << shift) >> shift;
129
130	return f == value;
131}
132#endif
133
134int apply_relocate_add(Elf_Shdr *sechdrs,
135		       const char *strtab,
136		       unsigned int symindex,
137		       unsigned int relsec,
138		       struct module *me)
139{
140	unsigned int i;
141	Elf_Rela *rel = (void *)sechdrs[relsec].sh_addr;
142	Elf_Sym *sym;
143	u64 *location;
144	unsigned long value;
145
146	DEBUGP("Applying relocate section %u to %u\n", relsec,
147	       sechdrs[relsec].sh_info);
148	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
149		/* This is where to make the change */
150		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
151			+ rel[i].r_offset;
152		/*
153		 * This is the symbol it is referring to.
154		 * Note that all undefined symbols have been resolved.
155		 */
156		sym = (Elf_Sym *)sechdrs[symindex].sh_addr
157			+ ELF_R_SYM(rel[i].r_info);
158		value = sym->st_value + rel[i].r_addend;
159
160		switch (ELF_R_TYPE(rel[i].r_info)) {
161
162#ifdef __LITTLE_ENDIAN
163# define MUNGE(func) \
164	(*location = ((*location & ~func(-1)) | func(value)))
165#else
166/*
167 * Instructions are always little-endian, so when we read them as data,
168 * we have to swap them around before and after modifying them.
169 */
170# define MUNGE(func) \
171	(*location = swab64((swab64(*location) & ~func(-1)) | func(value)))
172#endif
173
174#ifndef __tilegx__
175		case R_TILE_32:
176			*(uint32_t *)location = value;
177			break;
178		case R_TILE_IMM16_X0_HA:
179			value = (value + 0x8000) >> 16;
180			/*FALLTHROUGH*/
181		case R_TILE_IMM16_X0_LO:
182			MUNGE(create_Imm16_X0);
183			break;
184		case R_TILE_IMM16_X1_HA:
185			value = (value + 0x8000) >> 16;
186			/*FALLTHROUGH*/
187		case R_TILE_IMM16_X1_LO:
188			MUNGE(create_Imm16_X1);
189			break;
190		case R_TILE_JOFFLONG_X1:
191			value -= (unsigned long) location;  /* pc-relative */
192			value = (long) value >> 3;     /* count by instrs */
193			MUNGE(create_JOffLong_X1);
194			break;
195#else
196		case R_TILEGX_64:
197			*location = value;
198			break;
199		case R_TILEGX_IMM16_X0_HW2_LAST:
200			if (!validate_hw2_last(value, me))
201				return -ENOEXEC;
202			value >>= 16;
203			/*FALLTHROUGH*/
204		case R_TILEGX_IMM16_X0_HW1:
205			value >>= 16;
206			/*FALLTHROUGH*/
207		case R_TILEGX_IMM16_X0_HW0:
208			MUNGE(create_Imm16_X0);
209			break;
210		case R_TILEGX_IMM16_X1_HW2_LAST:
211			if (!validate_hw2_last(value, me))
212				return -ENOEXEC;
213			value >>= 16;
214			/*FALLTHROUGH*/
215		case R_TILEGX_IMM16_X1_HW1:
216			value >>= 16;
217			/*FALLTHROUGH*/
218		case R_TILEGX_IMM16_X1_HW0:
219			MUNGE(create_Imm16_X1);
220			break;
221		case R_TILEGX_JUMPOFF_X1:
222			value -= (unsigned long) location;  /* pc-relative */
223			value = (long) value >> 3;     /* count by instrs */
224			if (!validate_jumpoff(value)) {
225				pr_warning("module %s: Out of range jump to"
226					   " %#llx at %#llx (%p)\n", me->name,
227					   sym->st_value + rel[i].r_addend,
228					   rel[i].r_offset, location);
229				return -ENOEXEC;
230			}
231			MUNGE(create_JumpOff_X1);
232			break;
233#endif
234
235#undef MUNGE
236
237		default:
238			pr_err("module %s: Unknown relocation: %d\n",
239			       me->name, (int) ELF_R_TYPE(rel[i].r_info));
240			return -ENOEXEC;
241		}
242	}
243	return 0;
244}