1/* 2 * Early kernel startup code for Hexagon 3 * 4 * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved. 5 * 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 and 9 * only version 2 as published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 * 02110-1301, USA. 20 */ 21 22#include <linux/linkage.h> 23#include <linux/init.h> 24#include <asm/asm-offsets.h> 25#include <asm/mem-layout.h> 26#include <asm/vm_mmu.h> 27#include <asm/page.h> 28 29 __INIT 30ENTRY(stext) 31 /* 32 * VMM will already have set up true vector page, MMU, etc. 33 * To set up initial kernel identity map, we have to pass 34 * the VMM a pointer to some canonical page tables. In 35 * this implementation, we're assuming that we've got 36 * them precompiled. Generate value in R24, as we'll need 37 * it again shortly. 38 */ 39 r24.L = #LO(swapper_pg_dir) 40 r24.H = #HI(swapper_pg_dir) 41 42 /* 43 * Symbol is kernel segment address, but we need 44 * the logical/physical address. 45 */ 46 r25 = pc; 47 r2.h = #0xffc0; 48 r2.l = #0x0000; 49 r25 = and(r2,r25); /* R25 holds PHYS_OFFSET now */ 50 r1.h = #HI(PAGE_OFFSET); 51 r1.l = #LO(PAGE_OFFSET); 52 r24 = sub(r24,r1); /* swapper_pg_dir - PAGE_OFFSET */ 53 r24 = add(r24,r25); /* + PHYS_OFFSET */ 54 55 r0 = r24; /* aka __pa(swapper_pg_dir) */ 56 57 /* 58 * Initialize page dir to make the virtual and physical 59 * addresses where the kernel was loaded be identical. 60 * Done in 4MB chunks. 61 */ 62#define PTE_BITS ( __HVM_PTE_R | __HVM_PTE_W | __HVM_PTE_X \ 63 | __HEXAGON_C_WB_L2 << 6 \ 64 | __HVM_PDE_S_4MB) 65 66 r1 = pc 67 r2.H = #0xffc0 68 r2.L = #0x0000 69 r1 = and(r1,r2) /* round PC to 4MB boundary */ 70 r2 = lsr(r1, #22) /* 4MB page number */ 71 r2 = asl(r2, #2) /* times sizeof(PTE) (4bytes) */ 72 r0 = add(r0,r2) /* r0 = address of correct PTE */ 73 r2 = #PTE_BITS 74 r1 = add(r1,r2) /* r1 = 4MB PTE for the first entry */ 75 r2.h = #0x0040 76 r2.l = #0x0000 /* 4MB */ 77 memw(r0 ++ #4) = r1 78 r1 = add(r1, r2) 79 memw(r0 ++ #4) = r1 80 81 r0 = r24 82 83 /* 84 * The subroutine wrapper around the virtual instruction touches 85 * no memory, so we should be able to use it even here. 86 */ 87 call __vmnewmap; 88 89 /* Jump into virtual address range. */ 90 91 r31.h = #hi(__head_s_vaddr_target) 92 r31.l = #lo(__head_s_vaddr_target) 93 jumpr r31 94 95 /* Insert trippy space effects. */ 96 97__head_s_vaddr_target: 98 /* 99 * Tear down VA=PA translation now that we are running 100 * in the desgnated kernel segments. 101 */ 102 r0 = #__HVM_PDE_S_INVALID 103 r1 = r24 104 loop0(1f,#0x100) 1051: 106 { 107 memw(R1 ++ #4) = R0 108 }:endloop0 109 110 r0 = r24 111 call __vmnewmap 112 113 /* Go ahead and install the trap0 return so angel calls work */ 114 r0.h = #hi(_K_provisional_vec) 115 r0.l = #lo(_K_provisional_vec) 116 call __vmsetvec 117 118 /* 119 * OK, at this point we should start to be much more careful, 120 * we're going to enter C code and start touching memory 121 * in all sorts of places. 122 * This means: 123 * SGP needs to be OK 124 * Need to lock shared resources 125 * A bunch of other things that will cause 126 * all kinds of painful bugs 127 */ 128 129 /* 130 * Stack pointer should be pointed at the init task's 131 * thread stack, which should have been declared in arch/init_task.c. 132 * So uhhhhh... 133 * It's accessible via the init_thread_union, which is a union 134 * of a thread_info struct and a stack; of course, the top 135 * of the stack is not for you. The end of the stack 136 * is simply init_thread_union + THREAD_SIZE. 137 */ 138 139 {r29.H = #HI(init_thread_union); r0.H = #HI(_THREAD_SIZE); } 140 {r29.L = #LO(init_thread_union); r0.L = #LO(_THREAD_SIZE); } 141 142 /* initialize the register used to point to current_thread_info */ 143 /* Fixme: THREADINFO_REG can't be R2 because of that memset thing. */ 144 {r29 = add(r29,r0); THREADINFO_REG = r29; } 145 146 /* Hack: zero bss; */ 147 { r0.L = #LO(__bss_start); r1 = #0; r2.l = #LO(__bss_stop); } 148 { r0.H = #HI(__bss_start); r2.h = #HI(__bss_stop); } 149 150 r2 = sub(r2,r0); 151 call memset; 152 153 /* Set PHYS_OFFSET; should still be in R25 */ 154#ifdef CONFIG_HEXAGON_PHYS_OFFSET 155 r0.l = #LO(__phys_offset); 156 r0.h = #HI(__phys_offset); 157 memw(r0) = r25; 158#endif 159 160 /* Time to make the doughnuts. */ 161 call start_kernel 162 163 /* 164 * Should not reach here. 165 */ 1661: 167 jump 1b 168 169.p2align PAGE_SHIFT 170ENTRY(external_cmdline_buffer) 171 .fill _PAGE_SIZE,1,0 172 173.data 174.p2align PAGE_SHIFT 175ENTRY(empty_zero_page) 176 .fill _PAGE_SIZE,1,0 177