| 1 | /* |
|---|
| 2 | * __cpu_kentry.S - unified kernel entry point |
|---|
| 3 | * |
|---|
| 4 | * Author Ghassan Almaless (2007,2008,2009,2010,2011,2012) |
|---|
| 5 | * |
|---|
| 6 | * Copyright (c) UPMC Sorbonne Universites |
|---|
| 7 | * |
|---|
| 8 | * This file is part of ALMOS-kernel. |
|---|
| 9 | * |
|---|
| 10 | * ALMOS-kernel is free software; you can redistribute it and/or modify it |
|---|
| 11 | * under the terms of the GNU General Public License as published by |
|---|
| 12 | * the Free Software Foundation; version 2.0 of the License. |
|---|
| 13 | * |
|---|
| 14 | * ALMOS-kernel is distributed in the hope that it will be useful, but |
|---|
| 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 17 | * General Public License for more details. |
|---|
| 18 | * |
|---|
| 19 | * You should have received a copy of the GNU General Public License |
|---|
| 20 | * along with ALMOS-kernel; if not, write to the Free Software Foundation, |
|---|
| 21 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|---|
| 22 | */ |
|---|
| 23 | |
|---|
| 24 | #include <cpu-regs.h> |
|---|
| 25 | #define _ALMOS_ASM_ |
|---|
| 26 | #include <boot-info.h> |
|---|
| 27 | |
|---|
| 28 | #--------------------------------------------------------------------------------- |
|---|
| 29 | # This file defines the unique kernel entry point in case of |
|---|
| 30 | # Exception / Interrupt/Syscall for a MIPS32 processor core. |
|---|
| 31 | # The base address of the segment containing this code TODO [AG] |
|---|
| 32 | #--------------------------------------------------------------------------------- |
|---|
| 33 | |
|---|
| 34 | .section .kentry,"ax",@progbits |
|---|
| 35 | .extern cpu_do_interrupt |
|---|
| 36 | .extern cpu_do_exception |
|---|
| 37 | .extern cpu_do_syscall |
|---|
| 38 | .extern cpu_kentry |
|---|
| 39 | .extern cpu_kexit |
|---|
| 40 | .org 0x180 |
|---|
| 41 | .ent kentry |
|---|
| 42 | .global kentry |
|---|
| 43 | .global kentry_load |
|---|
| 44 | .set noat |
|---|
| 45 | .set noreorder |
|---|
| 46 | |
|---|
| 47 | #define SAVE_SIZE REGS_NR*4 |
|---|
| 48 | |
|---|
| 49 | #FIXME: PROC_WIDTH support only 4 proc clusters! |
|---|
| 50 | |
|---|
| 51 | #define PROC_WIDTH 2 |
|---|
| 52 | #define XY_WIDTH 8 |
|---|
| 53 | #define XY_MASK 0xFF |
|---|
| 54 | #define MMU_MD_MASK 0xF |
|---|
| 55 | |
|---|
| 56 | #------------------------------------------------------------------------------- |
|---|
| 57 | # Kernel Entry point |
|---|
| 58 | #------------------------------------------------------------------------------- |
|---|
| 59 | kentry: |
|---|
| 60 | mfc0 $26, $12 # read SR |
|---|
| 61 | andi $26, $26, 0x10 # KSU bitmask |
|---|
| 62 | beq $26, $0, KERNEL_MODE |
|---|
| 63 | ori $26, $0, 0x3 # MMU OFF value |
|---|
| 64 | |
|---|
| 65 | |
|---|
| 66 | LOAD_KERNEL_STACK: |
|---|
| 67 | mtc2 $26, $1 # set MMU OFF |
|---|
| 68 | nop |
|---|
| 69 | mfc0 $26, $4, # read current thread pointer ($26) |
|---|
| 70 | sw $27, (TLS_K1*4)($26) # save user value |
|---|
| 71 | |
|---|
| 72 | #$27 register can now be used |
|---|
| 73 | sw $29, (SP*4)($26) # save user stack |
|---|
| 74 | lw $29, (KSP*4)($26) # read kernel stack |
|---|
| 75 | |
|---|
| 76 | ori $27, $0, 0xF # MMU old value: assumed ON |
|---|
| 77 | sw $27, (MMU_MD*4)($26) # save MMU MODE |
|---|
| 78 | |
|---|
| 79 | j UNIFIED_MODE |
|---|
| 80 | or $27, $0, $26 # pointer to uzone ($27) |
|---|
| 81 | |
|---|
| 82 | #Use only $26 to save two values: MMU_MODE (4 bits) and XY_DATA_EXT (8bits) |
|---|
| 83 | #$26 content (in hex): 0x00000MXY (M is the value of MMU_MODE and XY is ...) |
|---|
| 84 | #N.B: MMU MODE is also modified by cpu_do_exception |
|---|
| 85 | |
|---|
| 86 | KERNEL_MODE: |
|---|
| 87 | #get old PADDR_EXT (in $26 at bits 0-7) |
|---|
| 88 | and $26, $26, $0 # $26 <= 0x00000000 |
|---|
| 89 | mfc2 $26, $24 # $26 <= 0x??????XY |
|---|
| 90 | andi $26, $26, XY_MASK # $26 <= 0x000000XY |
|---|
| 91 | #set PADDR_EXT to point to local mem |
|---|
| 92 | mfc0 $27, $15, 1 # read ebase |
|---|
| 93 | andi $27, $27, 0xFFF # extract arch cpu id |
|---|
| 94 | srl $27, $27, PROC_WIDTH # extract cluster xy |
|---|
| 95 | mtc2 $27, $24 # set XY PADDR_EXT |
|---|
| 96 | |
|---|
| 97 | #get old MMU MODE (in $26 at bits 8-11) |
|---|
| 98 | mfc2 $27, $1 # get MMU MODE old value |
|---|
| 99 | andi $27, $27, MMU_MD_MASK # $27 <= 0x0000000M |
|---|
| 100 | sll $27, $27, XY_WIDTH # $27 <= 0x00000M00 |
|---|
| 101 | or $26, $26, $27 # $26 <= 0x00000MXY <= (0x000000XY or 0x00000M00) |
|---|
| 102 | #set MMU OFF |
|---|
| 103 | ori $27, $0, 0x3 # MMU OFF value |
|---|
| 104 | mtc2 $27, $1 # set MMU OFF |
|---|
| 105 | |
|---|
| 106 | #save old stack, MMU_MODE and PADDR_EXT |
|---|
| 107 | addiu $27, $29, -(SAVE_SIZE) # allocate a region on stack ($27) |
|---|
| 108 | sw $29, (SP*4)($27) # save old stack ($29 can be used) |
|---|
| 109 | |
|---|
| 110 | srl $29, $26, XY_WIDTH # $29 <= 0x0000000M |
|---|
| 111 | sw $29, (MMU_MD*4)($27) # save MMU MODE |
|---|
| 112 | andi $26, $26, XY_MASK # $26 <= 0x000000XY |
|---|
| 113 | sw $26, (DP_EXT*4)($27) # save old DATA PADDR EXT (0x000000XY) |
|---|
| 114 | |
|---|
| 115 | or $29, $27, $0 # set new stack |
|---|
| 116 | mfc0 $26, $4, 2 # $26 <= this thread pointer |
|---|
| 117 | |
|---|
| 118 | # $27: zone for saving the cpu registers |
|---|
| 119 | # $26: current thread pointer ($this) |
|---|
| 120 | # $29: kstack |
|---|
| 121 | |
|---|
| 122 | UNIFIED_MODE: |
|---|
| 123 | sw $1, (AT*4)($27) |
|---|
| 124 | sw $2, (V0*4)($27) |
|---|
| 125 | sw $3, (V1*4)($27) |
|---|
| 126 | sw $4, (A0*4)($27) |
|---|
| 127 | sw $5, (A1*4)($27) |
|---|
| 128 | sw $6, (A2*4)($27) |
|---|
| 129 | sw $7, (A3*4)($27) |
|---|
| 130 | sw $8, (T0*4)($27) |
|---|
| 131 | sw $9, (T1*4)($27) |
|---|
| 132 | sw $10, (T2*4)($27) |
|---|
| 133 | sw $11, (T3*4)($27) |
|---|
| 134 | sw $12, (T4*4)($27) |
|---|
| 135 | sw $13, (T5*4)($27) |
|---|
| 136 | sw $14, (T6*4)($27) |
|---|
| 137 | sw $15, (T7*4)($27) |
|---|
| 138 | sw $24, (T8*4)($27) |
|---|
| 139 | sw $25, (T9*4)($27) |
|---|
| 140 | sw $16, (S0*4)($27) |
|---|
| 141 | sw $17, (S1*4)($27) |
|---|
| 142 | sw $18, (S2*4)($27) |
|---|
| 143 | sw $19, (S3*4)($27) |
|---|
| 144 | sw $20, (S4*4)($27) |
|---|
| 145 | sw $21, (S5*4)($27) |
|---|
| 146 | sw $22, (S6*4)($27) |
|---|
| 147 | sw $23, (S7*4)($27) |
|---|
| 148 | sw $30, (S8*4)($27) |
|---|
| 149 | sw $28, (GP*4)($27) |
|---|
| 150 | sw $31, (RA*4)($27) # save RA |
|---|
| 151 | |
|---|
| 152 | mfc0 $16, $14 # Read EPC |
|---|
| 153 | sw $16, (EPC*4)($27) # Save EPC |
|---|
| 154 | mflo $14 # read LO |
|---|
| 155 | sw $14, (LO*4)($27) # save LO |
|---|
| 156 | mfhi $15 # read HI |
|---|
| 157 | sw $15, (HI*4)($27) # save HI |
|---|
| 158 | |
|---|
| 159 | mfc0 $18, $12 # Read current SR |
|---|
| 160 | sw $18, (SR*4)($27) # Save SR |
|---|
| 161 | srl $3, $18, 5 # put SR in kernel mode, IRQ disabled, clear exl |
|---|
| 162 | sll $3, $3, 5 # ... |
|---|
| 163 | mtc0 $3, $12 # Set new SR |
|---|
| 164 | |
|---|
| 165 | mfc0 $17, $13 # read CR |
|---|
| 166 | sw $17, (CR*4)($27) # Save CR |
|---|
| 167 | andi $1, $17, 0x3F # $1 <= XCODE from CP0_CR |
|---|
| 168 | |
|---|
| 169 | #if CPU_IN_KERNEL |
|---|
| 170 | # First signal that we are entering the kernel |
|---|
| 171 | mfc0 $4, $15, 1 # $4 <= cpu_id |
|---|
| 172 | andi $4, $4, 0x3 # mask all but CPU_local_id |
|---|
| 173 | mfc0 $5, $4, 2 # read current thread pointer |
|---|
| 174 | jal cpu_kentry |
|---|
| 175 | addiu $29, $29, -8 |
|---|
| 176 | addiu $29, $29, 8 |
|---|
| 177 | #endif |
|---|
| 178 | |
|---|
| 179 | # Second, depending on XCODE, call the apropriate function |
|---|
| 180 | ori $8, $0, 0x20 # cause syscall |
|---|
| 181 | beq $8, $1, cause_sys |
|---|
| 182 | or $19, $0, $27 # for kentry_exit |
|---|
| 183 | mfc0 $5, $15, 1 |
|---|
| 184 | andi $5, $5, 0x1FF # $5 (arg1) <= CPU(X,Y,lid) |
|---|
| 185 | or $4, $0, $26 # $4 (arg0) <= this thread |
|---|
| 186 | beq $1, $0, cause_int |
|---|
| 187 | or $6, $0, $27 # $6 (arg2) <= regs_tbl |
|---|
| 188 | |
|---|
| 189 | # Exceptions Handler |
|---|
| 190 | # --------------------------------------------- |
|---|
| 191 | la $1, cpu_do_exception |
|---|
| 192 | jalr $1 |
|---|
| 193 | addiu $29, $29, -3*4 |
|---|
| 194 | j kentry_exit |
|---|
| 195 | addiu $29, $29, 3*4 |
|---|
| 196 | |
|---|
| 197 | # System Call Handler |
|---|
| 198 | # ------------------------------------------------ |
|---|
| 199 | cause_sys: |
|---|
| 200 | la $14, cpu_do_syscall |
|---|
| 201 | addiu $29, $29, -4 |
|---|
| 202 | jalr $14 |
|---|
| 203 | or $4, $0, $27 |
|---|
| 204 | j kentry_exit |
|---|
| 205 | or $19, $0, $2 |
|---|
| 206 | |
|---|
| 207 | # Interrupts Handler |
|---|
| 208 | # ---------------------------------------------- |
|---|
| 209 | cause_int: |
|---|
| 210 | la $1, cpu_do_interrupt |
|---|
| 211 | srl $7, $17, 10 # extract IP state, 4th arg |
|---|
| 212 | addiu $29, $29, -4*4 # cpu_interrupt has 4 arg |
|---|
| 213 | jal $1 |
|---|
| 214 | andi $7, $7, 0x3F # 6 HW IRQ LINES, 2th arg is irq_state |
|---|
| 215 | |
|---|
| 216 | # Kentry exit |
|---|
| 217 | # ---------------------------------------------- |
|---|
| 218 | kentry_exit: |
|---|
| 219 | #if CPU_IN_KERNEL |
|---|
| 220 | # First signal that we are exiting from the kernel |
|---|
| 221 | mfc0 $4, $15, 1 # arg is cpu_id |
|---|
| 222 | andi $4, $4, 0x3 # mask all but CPU_local_id |
|---|
| 223 | mfc0 $5, $4, 2 # read current thread pointer |
|---|
| 224 | jal cpu_kexit |
|---|
| 225 | addiu $29, $29, -8 |
|---|
| 226 | addiu $29, $29, 8 |
|---|
| 227 | #endif |
|---|
| 228 | # Then load context ... |
|---|
| 229 | or $27, $0, $19 |
|---|
| 230 | lw $1, (AT*4)($27) |
|---|
| 231 | lw $2, (V0*4)($27) |
|---|
| 232 | lw $3, (V1*4)($27) |
|---|
| 233 | lw $4, (A0*4)($27) |
|---|
| 234 | lw $5, (A1*4)($27) |
|---|
| 235 | lw $6, (A2*4)($27) |
|---|
| 236 | lw $7, (A3*4)($27) |
|---|
| 237 | lw $8, (T0*4)($27) |
|---|
| 238 | lw $9, (T1*4)($27) |
|---|
| 239 | lw $10, (T2*4)($27) |
|---|
| 240 | lw $11, (T3*4)($27) |
|---|
| 241 | lw $12, (T4*4)($27) |
|---|
| 242 | lw $13, (T5*4)($27) |
|---|
| 243 | lw $14, (T6*4)($27) |
|---|
| 244 | lw $15, (T7*4)($27) |
|---|
| 245 | lw $24, (T8*4)($27) |
|---|
| 246 | lw $25, (T9*4)($27) |
|---|
| 247 | lw $18, (S2*4)($27) |
|---|
| 248 | lw $19, (S3*4)($27) |
|---|
| 249 | lw $20, (S4*4)($27) |
|---|
| 250 | lw $21, (S5*4)($27) |
|---|
| 251 | lw $22, (S6*4)($27) |
|---|
| 252 | lw $23, (S7*4)($27) |
|---|
| 253 | lw $30, (S8*4)($27) |
|---|
| 254 | lw $28, (GP*4)($27) |
|---|
| 255 | lw $31, (RA*4)($27) |
|---|
| 256 | lw $16, (EPC*4)($27) # load EPC |
|---|
| 257 | lw $29, (SP*4)($27) # restore SP |
|---|
| 258 | lw $17, (SR*4)($27) # load SR |
|---|
| 259 | mfc0 $26, $12 # Read current SR |
|---|
| 260 | andi $17, $17, 0x1F |
|---|
| 261 | mtc0 $16, $14 # restore EPC |
|---|
| 262 | or $26, $26, $17 |
|---|
| 263 | lw $16, (LO*4)($27) # load LO |
|---|
| 264 | mtc0 $26, $12 # setup new SR |
|---|
| 265 | lw $17, (HI*4)($27) # load HI |
|---|
| 266 | mtlo $16 # restore LO |
|---|
| 267 | mthi $17 # restore HI |
|---|
| 268 | |
|---|
| 269 | lw $16, (S0*4)($27) |
|---|
| 270 | lw $17, (S1*4)($27) |
|---|
| 271 | |
|---|
| 272 | #TODO: optimize |
|---|
| 273 | lw $26, (MMU_MD*4)($27) # load MMU MODE |
|---|
| 274 | andi $26, $26, 0xc |
|---|
| 275 | beq $26, $0, OUT_MMU_3 # both MSB are 0 (the first two LSB are always set) |
|---|
| 276 | andi $26, $26, 0x8 |
|---|
| 277 | beq $26, $0, OUT_MMU_7 # first MSB is 0 (bit 2 is set) |
|---|
| 278 | lw $26, (DP_EXT*4)($27) # load DP_EXT |
|---|
| 279 | |
|---|
| 280 | # Possible value for MMU: |
|---|
| 281 | #In kernel mode : 0x7/0x3 |
|---|
| 282 | #In user mode : 0xF |
|---|
| 283 | |
|---|
| 284 | # DP_EXT can either be local or remote |
|---|
| 285 | #Once these register set we can no longer |
|---|
| 286 | #access global data |
|---|
| 287 | |
|---|
| 288 | #If MMU mode was 0xF |
|---|
| 289 | lw $27, (TLS_K1*4)($27) |
|---|
| 290 | mtc2 $26, $24 # restore DP EXT |
|---|
| 291 | ori $26, $0, 0xF # MMU 0xF value |
|---|
| 292 | mtc2 $26, $1 # set MMU MODE |
|---|
| 293 | j OUT_KENTRY |
|---|
| 294 | nop |
|---|
| 295 | |
|---|
| 296 | OUT_MMU_7: |
|---|
| 297 | #If MMU mode was 0x7 (uspace) |
|---|
| 298 | lw $27, (TLS_K1*4)($27) |
|---|
| 299 | mtc2 $26, $24 # restore DP EXT |
|---|
| 300 | ori $26, $0, 0x7 # MMU 0x7 value |
|---|
| 301 | mtc2 $26, $1 # set MMU MODE |
|---|
| 302 | j OUT_KENTRY |
|---|
| 303 | nop |
|---|
| 304 | |
|---|
| 305 | OUT_MMU_3: |
|---|
| 306 | #If MMU mode was 0x3 |
|---|
| 307 | lw $26, (DP_EXT*4)($27) # load DP_EXT |
|---|
| 308 | lw $27, (TLS_K1*4)($27) |
|---|
| 309 | mtc2 $26, $24 # restore DP EXT |
|---|
| 310 | ori $26, $0, 0x3 # MMU 0x3 value |
|---|
| 311 | mtc2 $26, $1 # set MMU MODE |
|---|
| 312 | nop |
|---|
| 313 | |
|---|
| 314 | OUT_KENTRY: |
|---|
| 315 | nop |
|---|
| 316 | eret |
|---|
| 317 | |
|---|
| 318 | |
|---|
| 319 | .end kentry |
|---|
| 320 | .set reorder |
|---|
| 321 | .set at |
|---|
| 322 | |
|---|
| 323 | .ent kentry_load |
|---|
| 324 | kentry_load: |
|---|
| 325 | #theses nops are required to load the eret instruction |
|---|
| 326 | #while we are in virtual mode (processor pipeline) ? |
|---|
| 327 | mtc2 $26, $1 # set MMU MODE |
|---|
| 328 | nop |
|---|
| 329 | nop |
|---|
| 330 | eret |
|---|
| 331 | .end kentry_load |
|---|
| 332 | |
|---|
| 333 | #------------------------------------------------------------------------------- |
|---|
| 334 | |
|---|