/*1* Copyright 2011 Tilera Corporation. All Rights Reserved.2*3* This program is free software; you can redistribute it and/or4* modify it under the terms of the GNU General Public License5* as published by the Free Software Foundation, version 2.6*7* This program is distributed in the hope that it will be useful, but8* WITHOUT ANY WARRANTY; without even the implied warranty of9* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or10* NON INFRINGEMENT. See the GNU General Public License for11* more details.12*13* TILE startup code.14*/1516#include <linux/linkage.h>17#include <linux/init.h>18#include <asm/page.h>19#include <asm/pgtable.h>20#include <asm/thread_info.h>21#include <asm/processor.h>22#include <asm/asm-offsets.h>23#include <hv/hypervisor.h>24#include <arch/chip.h>25#include <arch/spr_def.h>2627/*28* This module contains the entry code for kernel images. It performs the29* minimal setup needed to call the generic C routines.30*/3132__HEAD33ENTRY(_start)34/* Notify the hypervisor of what version of the API we want */35{36movei r1, TILE_CHIP37movei r2, TILE_CHIP_REV38}39{40moveli r0, _HV_VERSION41jal hv_init42}43/* Get a reasonable default ASID in r0 */44{45move r0, zero46jal hv_inquire_asid47}4849/*50* Install the default page table. The relocation required to51* statically define the table is a bit too complex, so we have52* to plug in the pointer from the L0 to the L1 table by hand.53* We only do this on the first cpu to boot, though, since the54* other CPUs should see a properly-constructed page table.55*/56{57v4int_l r2, zero, r0 /* ASID for hv_install_context */58moveli r4, hw1_last(swapper_pgprot - PAGE_OFFSET)59}60{61shl16insli r4, r4, hw0(swapper_pgprot - PAGE_OFFSET)62}63{64ld r1, r4 /* access_pte for hv_install_context */65}66{67moveli r0, hw1_last(.Lsv_data_pmd - PAGE_OFFSET)68moveli r6, hw1_last(temp_data_pmd - PAGE_OFFSET)69}70{71/* After initializing swapper_pgprot, HV_PTE_GLOBAL is set. */72bfextu r7, r1, HV_PTE_INDEX_GLOBAL, HV_PTE_INDEX_GLOBAL73inv r474}75bnez r7, .Lno_write76{77shl16insli r0, r0, hw0(.Lsv_data_pmd - PAGE_OFFSET)78shl16insli r6, r6, hw0(temp_data_pmd - PAGE_OFFSET)79}80{81/* Cut off the low bits of the PT address. */82shrui r6, r6, HV_LOG2_PAGE_TABLE_ALIGN83/* Start with our access pte. */84move r5, r185}86{87/* Stuff the address into the page table pointer slot of the PTE. */88bfins r5, r6, HV_PTE_INDEX_PTFN, \89HV_PTE_INDEX_PTFN + HV_PTE_PTFN_BITS - 190}91{92/* Store the L0 data PTE. */93st r0, r594addli r6, r6, (temp_code_pmd - temp_data_pmd) >> \95HV_LOG2_PAGE_TABLE_ALIGN96}97{98addli r0, r0, .Lsv_code_pmd - .Lsv_data_pmd99bfins r5, r6, HV_PTE_INDEX_PTFN, \100HV_PTE_INDEX_PTFN + HV_PTE_PTFN_BITS - 1101}102/* Store the L0 code PTE. */103st r0, r5104105.Lno_write:106moveli lr, hw2_last(1f)107{108shl16insli lr, lr, hw1(1f)109moveli r0, hw1_last(swapper_pg_dir - PAGE_OFFSET)110}111{112shl16insli lr, lr, hw0(1f)113shl16insli r0, r0, hw0(swapper_pg_dir - PAGE_OFFSET)114}115{116move r3, zero117j hv_install_context118}1191:120121/* Install the interrupt base. */122moveli r0, hw2_last(MEM_SV_START)123shl16insli r0, r0, hw1(MEM_SV_START)124shl16insli r0, r0, hw0(MEM_SV_START)125mtspr SPR_INTERRUPT_VECTOR_BASE_K, r0126127/*128* Get our processor number and save it away in SAVE_K_0.129* Extract stuff from the topology structure: r4 = y, r6 = x,130* r5 = width. FIXME: consider whether we want to just make these131* 64-bit values (and if so fix smp_topology write below, too).132*/133jal hv_inquire_topology134{135v4int_l r5, zero, r1 /* r5 = width */136shrui r4, r0, 32 /* r4 = y */137}138{139v4int_l r6, zero, r0 /* r6 = x */140mul_lu_lu r4, r4, r5141}142{143add r4, r4, r6 /* r4 == cpu == y*width + x */144}145146#ifdef CONFIG_SMP147/*148* Load up our per-cpu offset. When the first (master) tile149* boots, this value is still zero, so we will load boot_pc150* with start_kernel, and boot_sp with init_stack + THREAD_SIZE.151* The master tile initializes the per-cpu offset array, so that152* when subsequent (secondary) tiles boot, they will instead load153* from their per-cpu versions of boot_sp and boot_pc.154*/155moveli r5, hw2_last(__per_cpu_offset)156shl16insli r5, r5, hw1(__per_cpu_offset)157shl16insli r5, r5, hw0(__per_cpu_offset)158shl3add r5, r4, r5159ld r5, r5160bnez r5, 1f161162/*163* Save the width and height to the smp_topology variable164* for later use.165*/166moveli r0, hw2_last(smp_topology + HV_TOPOLOGY_WIDTH_OFFSET)167shl16insli r0, r0, hw1(smp_topology + HV_TOPOLOGY_WIDTH_OFFSET)168shl16insli r0, r0, hw0(smp_topology + HV_TOPOLOGY_WIDTH_OFFSET)169st r0, r11701:171#else172move r5, zero173#endif174175/* Load and go with the correct pc and sp. */176{177moveli r1, hw2_last(boot_sp)178moveli r0, hw2_last(boot_pc)179}180{181shl16insli r1, r1, hw1(boot_sp)182shl16insli r0, r0, hw1(boot_pc)183}184{185shl16insli r1, r1, hw0(boot_sp)186shl16insli r0, r0, hw0(boot_pc)187}188{189add r1, r1, r5190add r0, r0, r5191}192ld r0, r0193ld sp, r1194or r4, sp, r4195mtspr SPR_SYSTEM_SAVE_K_0, r4 /* save ksp0 + cpu */196addi sp, sp, -STACK_TOP_DELTA197{198move lr, zero /* stop backtraces in the called function */199jr r0200}201ENDPROC(_start)202203__PAGE_ALIGNED_BSS204.align PAGE_SIZE205ENTRY(empty_zero_page)206.fill PAGE_SIZE,1,0207END(empty_zero_page)208209.macro PTE cpa, bits1210.quad HV_PTE_PAGE | HV_PTE_DIRTY | HV_PTE_PRESENT | HV_PTE_ACCESSED |\211HV_PTE_GLOBAL | (HV_PTE_MODE_CACHE_NO_L3 << HV_PTE_INDEX_MODE) |\212(\bits1) | (HV_CPA_TO_PFN(\cpa) << HV_PTE_INDEX_PFN)213.endm214215__PAGE_ALIGNED_DATA216.align PAGE_SIZE217ENTRY(swapper_pg_dir)218.org swapper_pg_dir + HV_L0_INDEX(PAGE_OFFSET) * HV_PTE_SIZE219.Lsv_data_pmd:220.quad 0 /* PTE temp_data_pmd - PAGE_OFFSET, 0 */221.org swapper_pg_dir + HV_L0_INDEX(MEM_SV_START) * HV_PTE_SIZE222.Lsv_code_pmd:223.quad 0 /* PTE temp_code_pmd - PAGE_OFFSET, 0 */224.org swapper_pg_dir + HV_L0_SIZE225END(swapper_pg_dir)226227.align HV_PAGE_TABLE_ALIGN228ENTRY(temp_data_pmd)229/*230* We fill the PAGE_OFFSET pmd with huge pages with231* VA = PA + PAGE_OFFSET. We remap things with more precise access232* permissions later.233*/234.set addr, 0235.rept HV_L1_ENTRIES236PTE addr, HV_PTE_READABLE | HV_PTE_WRITABLE237.set addr, addr + HV_PAGE_SIZE_LARGE238.endr239.org temp_data_pmd + HV_L1_SIZE240END(temp_data_pmd)241242.align HV_PAGE_TABLE_ALIGN243ENTRY(temp_code_pmd)244/*245* We fill the MEM_SV_START pmd with huge pages with246* VA = PA + PAGE_OFFSET. We remap things with more precise access247* permissions later.248*/249.set addr, 0250.rept HV_L1_ENTRIES251PTE addr, HV_PTE_READABLE | HV_PTE_EXECUTABLE252.set addr, addr + HV_PAGE_SIZE_LARGE253.endr254.org temp_code_pmd + HV_L1_SIZE255END(temp_code_pmd)256257/*258* Isolate swapper_pgprot to its own cache line, since each cpu259* starting up will read it using VA-is-PA and local homing.260* This would otherwise likely conflict with other data on the cache261* line, once we have set its permanent home in the page tables.262*/263__INITDATA264.align CHIP_L2_LINE_SIZE()265ENTRY(swapper_pgprot)266.quad HV_PTE_PRESENT | (HV_PTE_MODE_CACHE_NO_L3 << HV_PTE_INDEX_MODE)267.align CHIP_L2_LINE_SIZE()268END(swapper_pgprot)269270271