1e6b04fe0SSam Ravnborg /* Kernel module help for sparc64. 2e6b04fe0SSam Ravnborg * 3e6b04fe0SSam Ravnborg * Copyright (C) 2001 Rusty Russell. 4e6b04fe0SSam Ravnborg * Copyright (C) 2002 David S. Miller. 5e6b04fe0SSam Ravnborg */ 6e6b04fe0SSam Ravnborg 7e6b04fe0SSam Ravnborg #include <linux/moduleloader.h> 8e6b04fe0SSam Ravnborg #include <linux/kernel.h> 9e6b04fe0SSam Ravnborg #include <linux/elf.h> 10e6b04fe0SSam Ravnborg #include <linux/vmalloc.h> 11e6b04fe0SSam Ravnborg #include <linux/fs.h> 125a0e3ad6STejun Heo #include <linux/gfp.h> 13e6b04fe0SSam Ravnborg #include <linux/string.h> 14e6b04fe0SSam Ravnborg #include <linux/ctype.h> 15e6b04fe0SSam Ravnborg #include <linux/mm.h> 16e6b04fe0SSam Ravnborg 17e6b04fe0SSam Ravnborg #include <asm/processor.h> 18e6b04fe0SSam Ravnborg #include <asm/spitfire.h> 19d550bbd4SDavid Howells #include <asm/cacheflush.h> 20e6b04fe0SSam Ravnborg 210b64120cSDavid S. Miller #include "entry.h" 220b64120cSDavid S. Miller 23e6b04fe0SSam Ravnborg #ifdef CONFIG_SPARC64 24dff9d3c2SDavid S. Miller 25dff9d3c2SDavid S. Miller #include <linux/jump_label.h> 26dff9d3c2SDavid S. Miller 27e6b04fe0SSam Ravnborg static void *module_map(unsigned long size) 28e6b04fe0SSam Ravnborg { 29d0a21265SDavid Rientjes if (PAGE_ALIGN(size) > MODULES_LEN) 30e6b04fe0SSam Ravnborg return NULL; 31d0a21265SDavid Rientjes return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END, 32cb9e3c29SAndrey Ryabinin GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE, 33d0a21265SDavid Rientjes __builtin_return_address(0)); 34e6b04fe0SSam Ravnborg } 35e6b04fe0SSam Ravnborg #else 36e6b04fe0SSam Ravnborg static void *module_map(unsigned long size) 37e6b04fe0SSam Ravnborg { 38e6b04fe0SSam Ravnborg return vmalloc(size); 39e6b04fe0SSam Ravnborg } 40e6b04fe0SSam Ravnborg #endif /* CONFIG_SPARC64 */ 41e6b04fe0SSam Ravnborg 42e6b04fe0SSam Ravnborg void *module_alloc(unsigned long size) 43e6b04fe0SSam Ravnborg { 44e6b04fe0SSam Ravnborg void *ret; 45e6b04fe0SSam Ravnborg 46e6b04fe0SSam Ravnborg ret = module_map(size); 47a9e8d1a6SWei Yongjun if (ret) 48e6b04fe0SSam Ravnborg memset(ret, 0, size); 49e6b04fe0SSam Ravnborg 50e6b04fe0SSam Ravnborg return ret; 51e6b04fe0SSam Ravnborg } 52e6b04fe0SSam Ravnborg 53e6b04fe0SSam Ravnborg /* Make generic code ignore STT_REGISTER dummy undefined symbols. */ 54e6b04fe0SSam Ravnborg int module_frob_arch_sections(Elf_Ehdr *hdr, 55e6b04fe0SSam Ravnborg Elf_Shdr *sechdrs, 56e6b04fe0SSam Ravnborg char *secstrings, 57e6b04fe0SSam Ravnborg struct module *mod) 58e6b04fe0SSam Ravnborg { 59e6b04fe0SSam Ravnborg unsigned int symidx; 60e6b04fe0SSam Ravnborg Elf_Sym *sym; 61e6b04fe0SSam Ravnborg char *strtab; 62e6b04fe0SSam Ravnborg int i; 63e6b04fe0SSam Ravnborg 64e6b04fe0SSam Ravnborg for (symidx = 0; sechdrs[symidx].sh_type != SHT_SYMTAB; symidx++) { 65e6b04fe0SSam Ravnborg if (symidx == hdr->e_shnum-1) { 66e6b04fe0SSam Ravnborg printk("%s: no symtab found.\n", mod->name); 67e6b04fe0SSam Ravnborg return -ENOEXEC; 68e6b04fe0SSam Ravnborg } 69e6b04fe0SSam Ravnborg } 70e6b04fe0SSam Ravnborg sym = (Elf_Sym *)sechdrs[symidx].sh_addr; 71e6b04fe0SSam Ravnborg strtab = (char *)sechdrs[sechdrs[symidx].sh_link].sh_addr; 72e6b04fe0SSam Ravnborg 73e6b04fe0SSam Ravnborg for (i = 1; i < sechdrs[symidx].sh_size / sizeof(Elf_Sym); i++) { 74e6b04fe0SSam Ravnborg if (sym[i].st_shndx == SHN_UNDEF) { 751b35a57bSDavid S. Miller if (ELF_ST_TYPE(sym[i].st_info) == STT_REGISTER) 76e6b04fe0SSam Ravnborg sym[i].st_shndx = SHN_ABS; 77e6b04fe0SSam Ravnborg } 78e6b04fe0SSam Ravnborg } 79e6b04fe0SSam Ravnborg return 0; 80e6b04fe0SSam Ravnborg } 81e6b04fe0SSam Ravnborg 82e6b04fe0SSam Ravnborg int apply_relocate_add(Elf_Shdr *sechdrs, 83e6b04fe0SSam Ravnborg const char *strtab, 84e6b04fe0SSam Ravnborg unsigned int symindex, 85e6b04fe0SSam Ravnborg unsigned int relsec, 86e6b04fe0SSam Ravnborg struct module *me) 87e6b04fe0SSam Ravnborg { 88e6b04fe0SSam Ravnborg unsigned int i; 89e6b04fe0SSam Ravnborg Elf_Rela *rel = (void *)sechdrs[relsec].sh_addr; 90e6b04fe0SSam Ravnborg Elf_Sym *sym; 91e6b04fe0SSam Ravnborg u8 *location; 92e6b04fe0SSam Ravnborg u32 *loc32; 93e6b04fe0SSam Ravnborg 94e6b04fe0SSam Ravnborg for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { 95e6b04fe0SSam Ravnborg Elf_Addr v; 96e6b04fe0SSam Ravnborg 97e6b04fe0SSam Ravnborg /* This is where to make the change */ 98e6b04fe0SSam Ravnborg location = (u8 *)sechdrs[sechdrs[relsec].sh_info].sh_addr 99e6b04fe0SSam Ravnborg + rel[i].r_offset; 100e6b04fe0SSam Ravnborg loc32 = (u32 *) location; 101e6b04fe0SSam Ravnborg 102e6b04fe0SSam Ravnborg #ifdef CONFIG_SPARC64 103e6b04fe0SSam Ravnborg BUG_ON(((u64)location >> (u64)32) != (u64)0); 104e6b04fe0SSam Ravnborg #endif /* CONFIG_SPARC64 */ 105e6b04fe0SSam Ravnborg 106e6b04fe0SSam Ravnborg /* This is the symbol it is referring to. Note that all 107e6b04fe0SSam Ravnborg undefined symbols have been resolved. */ 108e6b04fe0SSam Ravnborg sym = (Elf_Sym *)sechdrs[symindex].sh_addr 109e6b04fe0SSam Ravnborg + ELF_R_SYM(rel[i].r_info); 110e6b04fe0SSam Ravnborg v = sym->st_value + rel[i].r_addend; 111e6b04fe0SSam Ravnborg 112e6b04fe0SSam Ravnborg switch (ELF_R_TYPE(rel[i].r_info) & 0xff) { 113dfa520afSAndreas Larsson case R_SPARC_DISP32: 114dfa520afSAndreas Larsson v -= (Elf_Addr) location; 115dfa520afSAndreas Larsson *loc32 = v; 116dfa520afSAndreas Larsson break; 117e6b04fe0SSam Ravnborg #ifdef CONFIG_SPARC64 118e6b04fe0SSam Ravnborg case R_SPARC_64: 119e6b04fe0SSam Ravnborg location[0] = v >> 56; 120e6b04fe0SSam Ravnborg location[1] = v >> 48; 121e6b04fe0SSam Ravnborg location[2] = v >> 40; 122e6b04fe0SSam Ravnborg location[3] = v >> 32; 123e6b04fe0SSam Ravnborg location[4] = v >> 24; 124e6b04fe0SSam Ravnborg location[5] = v >> 16; 125e6b04fe0SSam Ravnborg location[6] = v >> 8; 126e6b04fe0SSam Ravnborg location[7] = v >> 0; 127e6b04fe0SSam Ravnborg break; 128e6b04fe0SSam Ravnborg 129e6b04fe0SSam Ravnborg case R_SPARC_WDISP19: 130e6b04fe0SSam Ravnborg v -= (Elf_Addr) location; 131e6b04fe0SSam Ravnborg *loc32 = (*loc32 & ~0x7ffff) | 132e6b04fe0SSam Ravnborg ((v >> 2) & 0x7ffff); 133e6b04fe0SSam Ravnborg break; 134e6b04fe0SSam Ravnborg 135e6b04fe0SSam Ravnborg case R_SPARC_OLO10: 136e6b04fe0SSam Ravnborg *loc32 = (*loc32 & ~0x1fff) | 137e6b04fe0SSam Ravnborg (((v & 0x3ff) + 138e6b04fe0SSam Ravnborg (ELF_R_TYPE(rel[i].r_info) >> 8)) 139e6b04fe0SSam Ravnborg & 0x1fff); 140e6b04fe0SSam Ravnborg break; 141e6b04fe0SSam Ravnborg #endif /* CONFIG_SPARC64 */ 142e6b04fe0SSam Ravnborg 143e6b04fe0SSam Ravnborg case R_SPARC_32: 144e6b04fe0SSam Ravnborg case R_SPARC_UA32: 145e6b04fe0SSam Ravnborg location[0] = v >> 24; 146e6b04fe0SSam Ravnborg location[1] = v >> 16; 147e6b04fe0SSam Ravnborg location[2] = v >> 8; 148e6b04fe0SSam Ravnborg location[3] = v >> 0; 149e6b04fe0SSam Ravnborg break; 150e6b04fe0SSam Ravnborg 151e6b04fe0SSam Ravnborg case R_SPARC_WDISP30: 152e6b04fe0SSam Ravnborg v -= (Elf_Addr) location; 153e6b04fe0SSam Ravnborg *loc32 = (*loc32 & ~0x3fffffff) | 154e6b04fe0SSam Ravnborg ((v >> 2) & 0x3fffffff); 155e6b04fe0SSam Ravnborg break; 156e6b04fe0SSam Ravnborg 157e6b04fe0SSam Ravnborg case R_SPARC_WDISP22: 158e6b04fe0SSam Ravnborg v -= (Elf_Addr) location; 159e6b04fe0SSam Ravnborg *loc32 = (*loc32 & ~0x3fffff) | 160e6b04fe0SSam Ravnborg ((v >> 2) & 0x3fffff); 161e6b04fe0SSam Ravnborg break; 162e6b04fe0SSam Ravnborg 163e6b04fe0SSam Ravnborg case R_SPARC_LO10: 164e6b04fe0SSam Ravnborg *loc32 = (*loc32 & ~0x3ff) | (v & 0x3ff); 165e6b04fe0SSam Ravnborg break; 166e6b04fe0SSam Ravnborg 167e6b04fe0SSam Ravnborg case R_SPARC_HI22: 168e6b04fe0SSam Ravnborg *loc32 = (*loc32 & ~0x3fffff) | 169e6b04fe0SSam Ravnborg ((v >> 10) & 0x3fffff); 170e6b04fe0SSam Ravnborg break; 171e6b04fe0SSam Ravnborg 172e6b04fe0SSam Ravnborg default: 173e6b04fe0SSam Ravnborg printk(KERN_ERR "module %s: Unknown relocation: %x\n", 174e6b04fe0SSam Ravnborg me->name, 175e6b04fe0SSam Ravnborg (int) (ELF_R_TYPE(rel[i].r_info) & 0xff)); 176e6b04fe0SSam Ravnborg return -ENOEXEC; 1776cb79b3fSJoe Perches } 178e6b04fe0SSam Ravnborg } 179e6b04fe0SSam Ravnborg return 0; 180e6b04fe0SSam Ravnborg } 181e6b04fe0SSam Ravnborg 182e6b04fe0SSam Ravnborg #ifdef CONFIG_SPARC64 1830b64120cSDavid S. Miller static void do_patch_sections(const Elf_Ehdr *hdr, 1840b64120cSDavid S. Miller const Elf_Shdr *sechdrs) 1850b64120cSDavid S. Miller { 1860b64120cSDavid S. Miller const Elf_Shdr *s, *sun4v_1insn = NULL, *sun4v_2insn = NULL; 1870b64120cSDavid S. Miller char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; 1880b64120cSDavid S. Miller 1890b64120cSDavid S. Miller for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { 1900b64120cSDavid S. Miller if (!strcmp(".sun4v_1insn_patch", secstrings + s->sh_name)) 1910b64120cSDavid S. Miller sun4v_1insn = s; 1920b64120cSDavid S. Miller if (!strcmp(".sun4v_2insn_patch", secstrings + s->sh_name)) 1930b64120cSDavid S. Miller sun4v_2insn = s; 1940b64120cSDavid S. Miller } 1950b64120cSDavid S. Miller 1960b64120cSDavid S. Miller if (sun4v_1insn && tlb_type == hypervisor) { 1970b64120cSDavid S. Miller void *p = (void *) sun4v_1insn->sh_addr; 1980b64120cSDavid S. Miller sun4v_patch_1insn_range(p, p + sun4v_1insn->sh_size); 1990b64120cSDavid S. Miller } 2000b64120cSDavid S. Miller if (sun4v_2insn && tlb_type == hypervisor) { 2010b64120cSDavid S. Miller void *p = (void *) sun4v_2insn->sh_addr; 2020b64120cSDavid S. Miller sun4v_patch_2insn_range(p, p + sun4v_2insn->sh_size); 2030b64120cSDavid S. Miller } 2040b64120cSDavid S. Miller } 2050b64120cSDavid S. Miller 206e6b04fe0SSam Ravnborg int module_finalize(const Elf_Ehdr *hdr, 207e6b04fe0SSam Ravnborg const Elf_Shdr *sechdrs, 208e6b04fe0SSam Ravnborg struct module *me) 209e6b04fe0SSam Ravnborg { 210dff9d3c2SDavid S. Miller /* make jump label nops */ 211dff9d3c2SDavid S. Miller jump_label_apply_nops(me); 212dff9d3c2SDavid S. Miller 2130b64120cSDavid S. Miller do_patch_sections(hdr, sechdrs); 2140b64120cSDavid S. Miller 215e6b04fe0SSam Ravnborg /* Cheetah's I-cache is fully coherent. */ 216e6b04fe0SSam Ravnborg if (tlb_type == spitfire) { 217e6b04fe0SSam Ravnborg unsigned long va; 218e6b04fe0SSam Ravnborg 219e6b04fe0SSam Ravnborg flushw_all(); 220e6b04fe0SSam Ravnborg for (va = 0; va < (PAGE_SIZE << 1); va += 32) 221e6b04fe0SSam Ravnborg spitfire_put_icache_tag(va, 0x0); 222e6b04fe0SSam Ravnborg __asm__ __volatile__("flush %g6"); 223e6b04fe0SSam Ravnborg } 224e6b04fe0SSam Ravnborg 225e6b04fe0SSam Ravnborg return 0; 226e6b04fe0SSam Ravnborg } 227e6b04fe0SSam Ravnborg #endif /* CONFIG_SPARC64 */ 228