| @@ -0,0 +1,260 @@ |
| | 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 | * copy new kernel into place and then call hv_reexec |
| | 15 | * |
| | 16 | */ |
| | 17 | |
| | 18 | #include <linux/linkage.h> |
| | 19 | #include <arch/chip.h> |
| | 20 | #include <asm/page.h> |
| | 21 | #include <hv/hypervisor.h> |
| | 22 | |
| | 23 | #undef RELOCATE_NEW_KERNEL_VERBOSE |
| | 24 | |
| | 25 | STD_ENTRY(relocate_new_kernel) |
| | 26 | |
| | 27 | move r30, r0 /* page list */ |
| | 28 | move r31, r1 /* address of page we are on */ |
| | 29 | move r32, r2 /* start address of new kernel */ |
| | 30 | |
| | 31 | shrui r1, r1, PAGE_SHIFT |
| | 32 | addi r1, r1, 1 |
| | 33 | shli sp, r1, PAGE_SHIFT |
| | 34 | addi sp, sp, -8 |
| | 35 | /* we now have a stack (whether we need one or not) */ |
| | 36 | |
| | 37 | moveli r40, hw2_last(hv_console_putc) |
| | 38 | shl16insli r40, r40, hw1(hv_console_putc) |
| | 39 | shl16insli r40, r40, hw0(hv_console_putc) |
| | 40 | |
| | 41 | #ifdef RELOCATE_NEW_KERNEL_VERBOSE |
| | 42 | moveli r0, 'r' |
| | 43 | jalr r40 |
| | 44 | |
| | 45 | moveli r0, '_' |
| | 46 | jalr r40 |
| | 47 | |
| | 48 | moveli r0, 'n' |
| | 49 | jalr r40 |
| | 50 | |
| | 51 | moveli r0, '_' |
| | 52 | jalr r40 |
| | 53 | |
| | 54 | moveli r0, 'k' |
| | 55 | jalr r40 |
| | 56 | |
| | 57 | moveli r0, '\n' |
| | 58 | jalr r40 |
| | 59 | #endif |
| | 60 | |
| | 61 | /* |
| | 62 | * Throughout this code r30 is pointer to the element of page |
| | 63 | * list we are working on. |
| | 64 | * |
| | 65 | * Normally we get to the next element of the page list by |
| | 66 | * incrementing r30 by eight. The exception is if the element |
| | 67 | * on the page list is an IND_INDIRECTION in which case we use |
| | 68 | * the element with the low bits masked off as the new value |
| | 69 | * of r30. |
| | 70 | * |
| | 71 | * To get this started, we need the value passed to us (which |
| | 72 | * will always be an IND_INDIRECTION) in memory somewhere with |
| | 73 | * r30 pointing at it. To do that, we push the value passed |
| | 74 | * to us on the stack and make r30 point to it. |
| | 75 | */ |
| | 76 | |
| | 77 | st sp, r30 |
| | 78 | move r30, sp |
| | 79 | addi sp, sp, -16 |
| | 80 | |
| | 81 | #if CHIP_HAS_CBOX_HOME_MAP() |
| | 82 | /* |
| | 83 | * On TILE-GX, we need to flush all tiles' caches, since we may |
| | 84 | * have been doing hash-for-home caching there. Note that we |
| | 85 | * must do this _after_ we're completely done modifying any memory |
| | 86 | * other than our output buffer (which we know is locally cached). |
| | 87 | * We want the caches to be fully clean when we do the reexec, |
| | 88 | * because the hypervisor is going to do this flush again at that |
| | 89 | * point, and we don't want that second flush to overwrite any memory. |
| | 90 | */ |
| | 91 | { |
| | 92 | move r0, zero /* cache_pa */ |
| | 93 | moveli r1, hw2_last(HV_FLUSH_EVICT_L2) |
| | 94 | } |
| | 95 | { |
| | 96 | shl16insli r1, r1, hw1(HV_FLUSH_EVICT_L2) |
| | 97 | movei r2, -1 /* cache_cpumask; -1 means all client tiles */ |
| | 98 | } |
| | 99 | { |
| | 100 | shl16insli r1, r1, hw0(HV_FLUSH_EVICT_L2) /* cache_control */ |
| | 101 | move r3, zero /* tlb_va */ |
| | 102 | } |
| | 103 | { |
| | 104 | move r4, zero /* tlb_length */ |
| | 105 | move r5, zero /* tlb_pgsize */ |
| | 106 | } |
| | 107 | { |
| | 108 | move r6, zero /* tlb_cpumask */ |
| | 109 | move r7, zero /* asids */ |
| | 110 | } |
| | 111 | { |
| | 112 | moveli r20, hw2_last(hv_flush_remote) |
| | 113 | move r8, zero /* asidcount */ |
| | 114 | } |
| | 115 | shl16insli r20, r20, hw1(hv_flush_remote) |
| | 116 | shl16insli r20, r20, hw0(hv_flush_remote) |
| | 117 | |
| | 118 | jalr r20 |
| | 119 | #endif |
| | 120 | |
| | 121 | /* r33 is destination pointer, default to zero */ |
| | 122 | |
| | 123 | moveli r33, 0 |
| | 124 | |
| | 125 | .Lloop: ld r10, r30 |
| | 126 | |
| | 127 | andi r9, r10, 0xf /* low 4 bits tell us what type it is */ |
| | 128 | xor r10, r10, r9 /* r10 is now value with low 4 bits stripped */ |
| | 129 | |
| | 130 | cmpeqi r0, r9, 0x1 /* IND_DESTINATION */ |
| | 131 | beqzt r0, .Ltry2 |
| | 132 | |
| | 133 | move r33, r10 |
| | 134 | |
| | 135 | #ifdef RELOCATE_NEW_KERNEL_VERBOSE |
| | 136 | moveli r0, 'd' |
| | 137 | jalr r40 |
| | 138 | #endif |
| | 139 | |
| | 140 | addi r30, r30, 8 |
| | 141 | j .Lloop |
| | 142 | |
| | 143 | .Ltry2: |
| | 144 | cmpeqi r0, r9, 0x2 /* IND_INDIRECTION */ |
| | 145 | beqzt r0, .Ltry4 |
| | 146 | |
| | 147 | move r30, r10 |
| | 148 | |
| | 149 | #ifdef RELOCATE_NEW_KERNEL_VERBOSE |
| | 150 | moveli r0, 'i' |
| | 151 | jalr r40 |
| | 152 | #endif |
| | 153 | |
| | 154 | j .Lloop |
| | 155 | |
| | 156 | .Ltry4: |
| | 157 | cmpeqi r0, r9, 0x4 /* IND_DONE */ |
| | 158 | beqzt r0, .Ltry8 |
| | 159 | |
| | 160 | mf |
| | 161 | |
| | 162 | #ifdef RELOCATE_NEW_KERNEL_VERBOSE |
| | 163 | moveli r0, 'D' |
| | 164 | jalr r40 |
| | 165 | moveli r0, '\n' |
| | 166 | jalr r40 |
| | 167 | #endif |
| | 168 | |
| | 169 | move r0, r32 |
| | 170 | |
| | 171 | moveli r41, hw2_last(hv_reexec) |
| | 172 | shl16insli r41, r41, hw1(hv_reexec) |
| | 173 | shl16insli r41, r41, hw0(hv_reexec) |
| | 174 | |
| | 175 | jalr r41 |
| | 176 | |
| | 177 | /* we should not get here */ |
| | 178 | |
| | 179 | moveli r0, '?' |
| | 180 | jalr r40 |
| | 181 | moveli r0, '\n' |
| | 182 | jalr r40 |
| | 183 | |
| | 184 | j .Lhalt |
| | 185 | |
| | 186 | .Ltry8: cmpeqi r0, r9, 0x8 /* IND_SOURCE */ |
| | 187 | beqz r0, .Lerr /* unknown type */ |
| | 188 | |
| | 189 | /* copy page at r10 to page at r33 */ |
| | 190 | |
| | 191 | move r11, r33 |
| | 192 | |
| | 193 | moveli r0, hw2_last(PAGE_SIZE) |
| | 194 | shl16insli r0, r0, hw1(PAGE_SIZE) |
| | 195 | shl16insli r0, r0, hw0(PAGE_SIZE) |
| | 196 | add r33, r33, r0 |
| | 197 | |
| | 198 | /* copy word at r10 to word at r11 until r11 equals r33 */ |
| | 199 | |
| | 200 | /* We know page size must be multiple of 8, so we can unroll |
| | 201 | * 8 times safely without any edge case checking. |
| | 202 | * |
| | 203 | * Issue a flush of the destination every 8 words to avoid |
| | 204 | * incoherence when starting the new kernel. (Now this is |
| | 205 | * just good paranoia because the hv_reexec call will also |
| | 206 | * take care of this.) |
| | 207 | */ |
| | 208 | |
| | 209 | 1: |
| | 210 | { ld r0, r10; addi r10, r10, 8 } |
| | 211 | { st r11, r0; addi r11, r11, 8 } |
| | 212 | { ld r0, r10; addi r10, r10, 8 } |
| | 213 | { st r11, r0; addi r11, r11, 8 } |
| | 214 | { ld r0, r10; addi r10, r10, 8 } |
| | 215 | { st r11, r0; addi r11, r11, 8 } |
| | 216 | { ld r0, r10; addi r10, r10, 8 } |
| | 217 | { st r11, r0; addi r11, r11, 8 } |
| | 218 | { ld r0, r10; addi r10, r10, 8 } |
| | 219 | { st r11, r0; addi r11, r11, 8 } |
| | 220 | { ld r0, r10; addi r10, r10, 8 } |
| | 221 | { st r11, r0; addi r11, r11, 8 } |
| | 222 | { ld r0, r10; addi r10, r10, 8 } |
| | 223 | { st r11, r0; addi r11, r11, 8 } |
| | 224 | { ld r0, r10; addi r10, r10, 8 } |
| | 225 | { st r11, r0 } |
| | 226 | { flush r11 ; addi r11, r11, 8 } |
| | 227 | |
| | 228 | cmpeq r0, r33, r11 |
| | 229 | beqzt r0, 1b |
| | 230 | |
| | 231 | #ifdef RELOCATE_NEW_KERNEL_VERBOSE |
| | 232 | moveli r0, 's' |
| | 233 | jalr r40 |
| | 234 | #endif |
| | 235 | |
| | 236 | addi r30, r30, 8 |
| | 237 | j .Lloop |
| | 238 | |
| | 239 | |
| | 240 | .Lerr: moveli r0, 'e' |
| | 241 | jalr r40 |
| | 242 | moveli r0, 'r' |
| | 243 | jalr r40 |
| | 244 | moveli r0, 'r' |
| | 245 | jalr r40 |
| | 246 | moveli r0, '\n' |
| | 247 | jalr r40 |
| | 248 | .Lhalt: |
| | 249 | moveli r41, hw2_last(hv_halt) |
| | 250 | shl16insli r41, r41, hw1(hv_halt) |
| | 251 | shl16insli r41, r41, hw0(hv_halt) |
| | 252 | |
| | 253 | jalr r41 |
| | 254 | STD_ENDPROC(relocate_new_kernel) |
| | 255 | |
| | 256 | .section .rodata,"a" |
| | 257 | |
| | 258 | .globl relocate_new_kernel_size |
| | 259 | relocate_new_kernel_size: |
| | 260 | .long .Lend_relocate_new_kernel - relocate_new_kernel |