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