1/* 2 * Copyright 2011 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 * TILE startup code. 15 */ 16 17#include <linux/linkage.h> 18#include <linux/init.h> 19#include <asm/page.h> 20#include <asm/pgtable.h> 21#include <asm/thread_info.h> 22#include <asm/processor.h> 23#include <asm/asm-offsets.h> 24#include <hv/hypervisor.h> 25#include <arch/chip.h> 26#include <arch/spr_def.h> 27 28/* Extract two 32-bit bit values that were read into one register. */ 29#ifdef __BIG_ENDIAN__ 30#define GET_FIRST_INT(rd, rs) shrsi rd, rs, 32 31#define GET_SECOND_INT(rd, rs) addxi rd, rs, 0 32#else 33#define GET_FIRST_INT(rd, rs) addxi rd, rs, 0 34#define GET_SECOND_INT(rd, rs) shrsi rd, rs, 32 35#endif 36 37/* 38 * This module contains the entry code for kernel images. It performs the 39 * minimal setup needed to call the generic C routines. 40 */ 41 42 __HEAD 43ENTRY(_start) 44 /* Notify the hypervisor of what version of the API we want */ 45 { 46#if KERNEL_PL == 1 && _HV_VERSION == 13 47 /* Support older hypervisors by asking for API version 12. */ 48 movei r0, _HV_VERSION_OLD_HV_INIT 49#else 50 movei r0, _HV_VERSION 51#endif 52 movei r1, TILE_CHIP 53 } 54 { 55 movei r2, TILE_CHIP_REV 56 movei r3, KERNEL_PL 57 } 58 jal _hv_init 59 /* Get a reasonable default ASID in r0 */ 60 { 61 move r0, zero 62 jal _hv_inquire_asid 63 } 64 65 /* 66 * Install the default page table. The relocation required to 67 * statically define the table is a bit too complex, so we have 68 * to plug in the pointer from the L0 to the L1 table by hand. 69 * We only do this on the first cpu to boot, though, since the 70 * other CPUs should see a properly-constructed page table. 71 */ 72 { 73 GET_FIRST_INT(r2, r0) /* ASID for hv_install_context */ 74 moveli r4, hw1_last(swapper_pgprot - PAGE_OFFSET) 75 } 76 { 77 shl16insli r4, r4, hw0(swapper_pgprot - PAGE_OFFSET) 78 } 79 { 80 ld r1, r4 /* access_pte for hv_install_context */ 81 } 82 { 83 moveli r0, hw1_last(.Lsv_data_pmd - PAGE_OFFSET) 84 moveli r6, hw1_last(temp_data_pmd - PAGE_OFFSET) 85 } 86 { 87 /* After initializing swapper_pgprot, HV_PTE_GLOBAL is set. */ 88 bfextu r7, r1, HV_PTE_INDEX_GLOBAL, HV_PTE_INDEX_GLOBAL 89 finv r4 90 } 91 bnez r7, .Lno_write 92 { 93 shl16insli r0, r0, hw0(.Lsv_data_pmd - PAGE_OFFSET) 94 shl16insli r6, r6, hw0(temp_data_pmd - PAGE_OFFSET) 95 } 96 { 97 /* Cut off the low bits of the PT address. */ 98 shrui r6, r6, HV_LOG2_PAGE_TABLE_ALIGN 99 /* Start with our access pte. */ 100 move r5, r1 101 } 102 { 103 /* Stuff the address into the page table pointer slot of the PTE. */ 104 bfins r5, r6, HV_PTE_INDEX_PTFN, \ 105 HV_PTE_INDEX_PTFN + HV_PTE_PTFN_BITS - 1 106 } 107 { 108 /* Store the L0 data PTE. */ 109 st r0, r5 110 addli r6, r6, (temp_code_pmd - temp_data_pmd) >> \ 111 HV_LOG2_PAGE_TABLE_ALIGN 112 } 113 { 114 addli r0, r0, .Lsv_code_pmd - .Lsv_data_pmd 115 bfins r5, r6, HV_PTE_INDEX_PTFN, \ 116 HV_PTE_INDEX_PTFN + HV_PTE_PTFN_BITS - 1 117 } 118 /* Store the L0 code PTE. */ 119 st r0, r5 120 121.Lno_write: 122 moveli lr, hw2_last(1f) 123 { 124 shl16insli lr, lr, hw1(1f) 125 moveli r0, hw1_last(swapper_pg_dir - PAGE_OFFSET) 126 } 127 { 128 shl16insli lr, lr, hw0(1f) 129 shl16insli r0, r0, hw0(swapper_pg_dir - PAGE_OFFSET) 130 } 131 { 132 moveli r3, CTX_PAGE_FLAG 133 j _hv_install_context 134 } 1351: 136 137 /* Install the interrupt base. */ 138 moveli r0, hw2_last(intrpt_start) 139 shl16insli r0, r0, hw1(intrpt_start) 140 shl16insli r0, r0, hw0(intrpt_start) 141 mtspr SPR_INTERRUPT_VECTOR_BASE_K, r0 142 143 /* Get our processor number and save it away in SAVE_K_0. */ 144 jal _hv_inquire_topology 145 { 146 GET_FIRST_INT(r5, r1) /* r5 = width */ 147 GET_SECOND_INT(r4, r0) /* r4 = y */ 148 } 149 { 150 GET_FIRST_INT(r6, r0) /* r6 = x */ 151 mul_lu_lu r4, r4, r5 152 } 153 { 154 add r4, r4, r6 /* r4 == cpu == y*width + x */ 155 } 156 157#ifdef CONFIG_SMP 158 /* 159 * Load up our per-cpu offset. When the first (master) tile 160 * boots, this value is still zero, so we will load boot_pc 161 * with start_kernel, and boot_sp with at the top of init_stack. 162 * The master tile initializes the per-cpu offset array, so that 163 * when subsequent (secondary) tiles boot, they will instead load 164 * from their per-cpu versions of boot_sp and boot_pc. 165 */ 166 moveli r5, hw2_last(__per_cpu_offset) 167 shl16insli r5, r5, hw1(__per_cpu_offset) 168 shl16insli r5, r5, hw0(__per_cpu_offset) 169 shl3add r5, r4, r5 170 ld r5, r5 171 bnez r5, 1f 172 173 /* 174 * Save the width and height to the smp_topology variable 175 * for later use. 176 */ 177 moveli r0, hw2_last(smp_topology + HV_TOPOLOGY_WIDTH_OFFSET) 178 shl16insli r0, r0, hw1(smp_topology + HV_TOPOLOGY_WIDTH_OFFSET) 179 shl16insli r0, r0, hw0(smp_topology + HV_TOPOLOGY_WIDTH_OFFSET) 180 st r0, r1 1811: 182#else 183 move r5, zero 184#endif 185 186 /* Load and go with the correct pc and sp. */ 187 { 188 moveli r1, hw2_last(boot_sp) 189 moveli r0, hw2_last(boot_pc) 190 } 191 { 192 shl16insli r1, r1, hw1(boot_sp) 193 shl16insli r0, r0, hw1(boot_pc) 194 } 195 { 196 shl16insli r1, r1, hw0(boot_sp) 197 shl16insli r0, r0, hw0(boot_pc) 198 } 199 { 200 add r1, r1, r5 201 add r0, r0, r5 202 } 203 ld r0, r0 204 ld sp, r1 205 shli r4, r4, CPU_SHIFT 206 bfins r4, sp, 0, CPU_SHIFT-1 207 mtspr SPR_SYSTEM_SAVE_K_0, r4 /* save ksp0 + cpu */ 208 { 209 move lr, zero /* stop backtraces in the called function */ 210 jr r0 211 } 212 ENDPROC(_start) 213 214__PAGE_ALIGNED_BSS 215 .align PAGE_SIZE 216ENTRY(empty_zero_page) 217 .fill PAGE_SIZE,1,0 218 END(empty_zero_page) 219 220 .macro PTE cpa, bits1 221 .quad HV_PTE_PAGE | HV_PTE_DIRTY | HV_PTE_PRESENT | HV_PTE_ACCESSED |\ 222 HV_PTE_GLOBAL | (HV_PTE_MODE_CACHE_NO_L3 << HV_PTE_INDEX_MODE) |\ 223 (\bits1) | (HV_CPA_TO_PTFN(\cpa) << HV_PTE_INDEX_PTFN) 224 .endm 225 226__PAGE_ALIGNED_DATA 227 .align PAGE_SIZE 228ENTRY(swapper_pg_dir) 229 .org swapper_pg_dir + PGD_INDEX(PAGE_OFFSET) * HV_PTE_SIZE 230.Lsv_data_pmd: 231 .quad 0 /* PTE temp_data_pmd - PAGE_OFFSET, 0 */ 232 .org swapper_pg_dir + PGD_INDEX(MEM_SV_START) * HV_PTE_SIZE 233.Lsv_code_pmd: 234 .quad 0 /* PTE temp_code_pmd - PAGE_OFFSET, 0 */ 235 .org swapper_pg_dir + SIZEOF_PGD 236 END(swapper_pg_dir) 237 238 .align HV_PAGE_TABLE_ALIGN 239ENTRY(temp_data_pmd) 240 /* 241 * We fill the PAGE_OFFSET pmd with huge pages with 242 * VA = PA + PAGE_OFFSET. We remap things with more precise access 243 * permissions later. 244 */ 245 .set addr, 0 246 .rept PTRS_PER_PMD 247 PTE addr, HV_PTE_READABLE | HV_PTE_WRITABLE 248 .set addr, addr + HPAGE_SIZE 249 .endr 250 .org temp_data_pmd + SIZEOF_PMD 251 END(temp_data_pmd) 252 253 .align HV_PAGE_TABLE_ALIGN 254ENTRY(temp_code_pmd) 255 /* 256 * We fill the MEM_SV_START pmd with huge pages with 257 * VA = PA + PAGE_OFFSET. We remap things with more precise access 258 * permissions later. 259 */ 260 .set addr, 0 261 .rept PTRS_PER_PMD 262 PTE addr, HV_PTE_READABLE | HV_PTE_EXECUTABLE 263 .set addr, addr + HPAGE_SIZE 264 .endr 265 .org temp_code_pmd + SIZEOF_PMD 266 END(temp_code_pmd) 267 268 /* 269 * Isolate swapper_pgprot to its own cache line, since each cpu 270 * starting up will read it using VA-is-PA and local homing. 271 * This would otherwise likely conflict with other data on the cache 272 * line, once we have set its permanent home in the page tables. 273 */ 274 __INITDATA 275 .align CHIP_L2_LINE_SIZE() 276ENTRY(swapper_pgprot) 277 .quad HV_PTE_PRESENT | (HV_PTE_MODE_CACHE_NO_L3 << HV_PTE_INDEX_MODE) 278 .align CHIP_L2_LINE_SIZE() 279 END(swapper_pgprot) 280