Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | /* SPDX-License-Identifier: GPL-2.0-only */ /* * Early kernel startup code for Hexagon * * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. */ #include <linux/linkage.h> #include <linux/init.h> #include <asm/asm-offsets.h> #include <asm/mem-layout.h> #include <asm/vm_mmu.h> #include <asm/page.h> #include <asm/hexagon_vm.h> #define SEGTABLE_ENTRIES #0x0e0 __INIT ENTRY(stext) /* * VMM will already have set up true vector page, MMU, etc. * To set up initial kernel identity map, we have to pass * the VMM a pointer to some canonical page tables. In * this implementation, we're assuming that we've got * them precompiled. Generate value in R24, as we'll need * it again shortly. */ r24.L = #LO(swapper_pg_dir) r24.H = #HI(swapper_pg_dir) /* * Symbol is kernel segment address, but we need * the logical/physical address. */ r25 = pc; r2.h = #0xffc0; r2.l = #0x0000; r25 = and(r2,r25); /* R25 holds PHYS_OFFSET now */ r1.h = #HI(PAGE_OFFSET); r1.l = #LO(PAGE_OFFSET); r24 = sub(r24,r1); /* swapper_pg_dir - PAGE_OFFSET */ r24 = add(r24,r25); /* + PHYS_OFFSET */ r0 = r24; /* aka __pa(swapper_pg_dir) */ /* * Initialize page dir to make the virtual and physical * addresses where the kernel was loaded be identical. * Done in 4MB chunks. */ #define PTE_BITS ( __HVM_PTE_R | __HVM_PTE_W | __HVM_PTE_X \ | __HEXAGON_C_WB_L2 << 6 \ | __HVM_PDE_S_4MB) /* * Get number of VA=PA entries; only really needed for jump * to hyperspace; gets blown away immediately after */ { r1.l = #LO(_end); r2.l = #LO(stext); r3 = #1; } { r1.h = #HI(_end); r2.h = #HI(stext); r3 = asl(r3, #22); } { r1 = sub(r1, r2); r3 = add(r3, #-1); } /* r1 = _end - stext */ r1 = add(r1, r3); /* + (4M-1) */ r26 = lsr(r1, #22); /* / 4M = # of entries */ r1 = r25; r2.h = #0xffc0; r2.l = #0x0000; /* round back down to 4MB boundary */ r1 = and(r1,r2); r2 = lsr(r1, #22) /* 4MB page number */ r2 = asl(r2, #2) /* times sizeof(PTE) (4bytes) */ r0 = add(r0,r2) /* r0 = address of correct PTE */ r2 = #PTE_BITS r1 = add(r1,r2) /* r1 = 4MB PTE for the first entry */ r2.h = #0x0040 r2.l = #0x0000 /* 4MB increments */ loop0(1f,r26); 1: memw(r0 ++ #4) = r1 { r1 = add(r1, r2); } :endloop0 /* Also need to overwrite the initial 0xc0000000 entries */ /* PAGE_OFFSET >> (4MB shift - 4 bytes per entry shift) */ R1.H = #HI(PAGE_OFFSET >> (22 - 2)) R1.L = #LO(PAGE_OFFSET >> (22 - 2)) r0 = add(r1, r24); /* advance to 0xc0000000 entry */ r1 = r25; r2.h = #0xffc0; r2.l = #0x0000; /* round back down to 4MB boundary */ r1 = and(r1,r2); /* for huge page */ r2 = #PTE_BITS r1 = add(r1,r2); r2.h = #0x0040 r2.l = #0x0000 /* 4MB increments */ loop0(1f,SEGTABLE_ENTRIES); 1: memw(r0 ++ #4) = r1; { r1 = add(r1,r2); } :endloop0 r0 = r24; /* * The subroutine wrapper around the virtual instruction touches * no memory, so we should be able to use it even here. * Note that in this version, R1 and R2 get "clobbered"; see * vm_ops.S */ r1 = #VM_TRANS_TYPE_TABLE call __vmnewmap; /* Jump into virtual address range. */ r31.h = #hi(__head_s_vaddr_target) r31.l = #lo(__head_s_vaddr_target) jumpr r31 /* Insert trippy space effects. */ __head_s_vaddr_target: /* * Tear down VA=PA translation now that we are running * in kernel virtual space. */ r0 = #__HVM_PDE_S_INVALID r1.h = #0xffc0; r1.l = #0x0000; r2 = r25; /* phys_offset */ r2 = and(r1,r2); r1.l = #lo(swapper_pg_dir) r1.h = #hi(swapper_pg_dir) r2 = lsr(r2, #22) /* 4MB page number */ r2 = asl(r2, #2) /* times sizeof(PTE) (4bytes) */ r1 = add(r1,r2); loop0(1f,r26) 1: { memw(R1 ++ #4) = R0 }:endloop0 r0 = r24 r1 = #VM_TRANS_TYPE_TABLE call __vmnewmap /* Go ahead and install the trap0 return so angel calls work */ r0.h = #hi(_K_provisional_vec) r0.l = #lo(_K_provisional_vec) call __vmsetvec /* * OK, at this point we should start to be much more careful, * we're going to enter C code and start touching memory * in all sorts of places. * This means: * SGP needs to be OK * Need to lock shared resources * A bunch of other things that will cause * all kinds of painful bugs */ /* * Stack pointer should be pointed at the init task's * thread stack, which should have been declared in arch/init_task.c. * So uhhhhh... * It's accessible via the init_thread_union, which is a union * of a thread_info struct and a stack; of course, the top * of the stack is not for you. The end of the stack * is simply init_thread_union + THREAD_SIZE. */ {r29.H = #HI(init_thread_union); r0.H = #HI(_THREAD_SIZE); } {r29.L = #LO(init_thread_union); r0.L = #LO(_THREAD_SIZE); } /* initialize the register used to point to current_thread_info */ /* Fixme: THREADINFO_REG can't be R2 because of that memset thing. */ {r29 = add(r29,r0); THREADINFO_REG = r29; } /* Hack: zero bss; */ { r0.L = #LO(__bss_start); r1 = #0; r2.l = #LO(__bss_stop); } { r0.H = #HI(__bss_start); r2.h = #HI(__bss_stop); } r2 = sub(r2,r0); call memset; /* Set PHYS_OFFSET; should be in R25 */ #ifdef CONFIG_HEXAGON_PHYS_OFFSET r0.l = #LO(__phys_offset); r0.h = #HI(__phys_offset); memw(r0) = r25; #endif /* Time to make the doughnuts. */ call start_kernel /* * Should not reach here. */ 1: jump 1b .p2align PAGE_SHIFT ENTRY(external_cmdline_buffer) .fill _PAGE_SIZE,1,0 .data .p2align PAGE_SHIFT ENTRY(empty_zero_page) .fill _PAGE_SIZE,1,0 |