1 /* 2 * spu_restore.c 3 * 4 * (C) Copyright IBM Corp. 2005 5 * 6 * SPU-side context restore sequence outlined in 7 * Synergistic Processor Element Book IV 8 * 9 * Author: Mark Nutter <mnutter@us.ibm.com> 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation; either version 2, or (at your option) 14 * any later version. 15 * 16 * This program is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU General Public License for more details. 20 * 21 * You should have received a copy of the GNU General Public License 22 * along with this program; if not, write to the Free Software 23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 24 * 25 */ 26 27 28 #ifndef LS_SIZE 29 #define LS_SIZE 0x40000 /* 256K (in bytes) */ 30 #endif 31 32 typedef unsigned int u32; 33 typedef unsigned long long u64; 34 35 #include <spu_intrinsics.h> 36 #include <asm/spu_csa.h> 37 #include "spu_utils.h" 38 39 #define BR_INSTR 0x327fff80 /* br -4 */ 40 #define NOP_INSTR 0x40200000 /* nop */ 41 #define HEQ_INSTR 0x7b000000 /* heq $0, $0 */ 42 #define STOP_INSTR 0x00000000 /* stop 0x0 */ 43 #define ILLEGAL_INSTR 0x00800000 /* illegal instr */ 44 #define RESTORE_COMPLETE 0x00003ffc /* stop 0x3ffc */ 45 46 static inline void fetch_regs_from_mem(addr64 lscsa_ea) 47 { 48 unsigned int ls = (unsigned int)®s_spill[0]; 49 unsigned int size = sizeof(regs_spill); 50 unsigned int tag_id = 0; 51 unsigned int cmd = 0x40; /* GET */ 52 53 spu_writech(MFC_LSA, ls); 54 spu_writech(MFC_EAH, lscsa_ea.ui[0]); 55 spu_writech(MFC_EAL, lscsa_ea.ui[1]); 56 spu_writech(MFC_Size, size); 57 spu_writech(MFC_TagID, tag_id); 58 spu_writech(MFC_Cmd, cmd); 59 } 60 61 static inline void restore_upper_240kb(addr64 lscsa_ea) 62 { 63 unsigned int ls = 16384; 64 unsigned int list = (unsigned int)&dma_list[0]; 65 unsigned int size = sizeof(dma_list); 66 unsigned int tag_id = 0; 67 unsigned int cmd = 0x44; /* GETL */ 68 69 /* Restore, Step 4: 70 * Enqueue the GETL command (tag 0) to the MFC SPU command 71 * queue to transfer the upper 240 kb of LS from CSA. 72 */ 73 spu_writech(MFC_LSA, ls); 74 spu_writech(MFC_EAH, lscsa_ea.ui[0]); 75 spu_writech(MFC_EAL, list); 76 spu_writech(MFC_Size, size); 77 spu_writech(MFC_TagID, tag_id); 78 spu_writech(MFC_Cmd, cmd); 79 } 80 81 static inline void restore_decr(void) 82 { 83 unsigned int offset; 84 unsigned int decr_running; 85 unsigned int decr; 86 87 /* Restore, Step 6(moved): 88 * If the LSCSA "decrementer running" flag is set 89 * then write the SPU_WrDec channel with the 90 * decrementer value from LSCSA. 91 */ 92 offset = LSCSA_QW_OFFSET(decr_status); 93 decr_running = regs_spill[offset].slot[0] & SPU_DECR_STATUS_RUNNING; 94 if (decr_running) { 95 offset = LSCSA_QW_OFFSET(decr); 96 decr = regs_spill[offset].slot[0]; 97 spu_writech(SPU_WrDec, decr); 98 } 99 } 100 101 static inline void write_ppu_mb(void) 102 { 103 unsigned int offset; 104 unsigned int data; 105 106 /* Restore, Step 11: 107 * Write the MFC_WrOut_MB channel with the PPU_MB 108 * data from LSCSA. 109 */ 110 offset = LSCSA_QW_OFFSET(ppu_mb); 111 data = regs_spill[offset].slot[0]; 112 spu_writech(SPU_WrOutMbox, data); 113 } 114 115 static inline void write_ppuint_mb(void) 116 { 117 unsigned int offset; 118 unsigned int data; 119 120 /* Restore, Step 12: 121 * Write the MFC_WrInt_MB channel with the PPUINT_MB 122 * data from LSCSA. 123 */ 124 offset = LSCSA_QW_OFFSET(ppuint_mb); 125 data = regs_spill[offset].slot[0]; 126 spu_writech(SPU_WrOutIntrMbox, data); 127 } 128 129 static inline void restore_fpcr(void) 130 { 131 unsigned int offset; 132 vector unsigned int fpcr; 133 134 /* Restore, Step 13: 135 * Restore the floating-point status and control 136 * register from the LSCSA. 137 */ 138 offset = LSCSA_QW_OFFSET(fpcr); 139 fpcr = regs_spill[offset].v; 140 spu_mtfpscr(fpcr); 141 } 142 143 static inline void restore_srr0(void) 144 { 145 unsigned int offset; 146 unsigned int srr0; 147 148 /* Restore, Step 14: 149 * Restore the SPU SRR0 data from the LSCSA. 150 */ 151 offset = LSCSA_QW_OFFSET(srr0); 152 srr0 = regs_spill[offset].slot[0]; 153 spu_writech(SPU_WrSRR0, srr0); 154 } 155 156 static inline void restore_event_mask(void) 157 { 158 unsigned int offset; 159 unsigned int event_mask; 160 161 /* Restore, Step 15: 162 * Restore the SPU_RdEventMsk data from the LSCSA. 163 */ 164 offset = LSCSA_QW_OFFSET(event_mask); 165 event_mask = regs_spill[offset].slot[0]; 166 spu_writech(SPU_WrEventMask, event_mask); 167 } 168 169 static inline void restore_tag_mask(void) 170 { 171 unsigned int offset; 172 unsigned int tag_mask; 173 174 /* Restore, Step 16: 175 * Restore the SPU_RdTagMsk data from the LSCSA. 176 */ 177 offset = LSCSA_QW_OFFSET(tag_mask); 178 tag_mask = regs_spill[offset].slot[0]; 179 spu_writech(MFC_WrTagMask, tag_mask); 180 } 181 182 static inline void restore_complete(void) 183 { 184 extern void exit_fini(void); 185 unsigned int *exit_instrs = (unsigned int *)exit_fini; 186 unsigned int offset; 187 unsigned int stopped_status; 188 unsigned int stopped_code; 189 190 /* Restore, Step 18: 191 * Issue a stop-and-signal instruction with 192 * "good context restore" signal value. 193 * 194 * Restore, Step 19: 195 * There may be additional instructions placed 196 * here by the PPE Sequence for SPU Context 197 * Restore in order to restore the correct 198 * "stopped state". 199 * 200 * This step is handled here by analyzing the 201 * LSCSA.stopped_status and then modifying the 202 * exit() function to behave appropriately. 203 */ 204 205 offset = LSCSA_QW_OFFSET(stopped_status); 206 stopped_status = regs_spill[offset].slot[0]; 207 stopped_code = regs_spill[offset].slot[1]; 208 209 switch (stopped_status) { 210 case SPU_STOPPED_STATUS_P_I: 211 /* SPU_Status[P,I]=1. Add illegal instruction 212 * followed by stop-and-signal instruction after 213 * end of restore code. 214 */ 215 exit_instrs[0] = RESTORE_COMPLETE; 216 exit_instrs[1] = ILLEGAL_INSTR; 217 exit_instrs[2] = STOP_INSTR | stopped_code; 218 break; 219 case SPU_STOPPED_STATUS_P_H: 220 /* SPU_Status[P,H]=1. Add 'heq $0, $0' followed 221 * by stop-and-signal instruction after end of 222 * restore code. 223 */ 224 exit_instrs[0] = RESTORE_COMPLETE; 225 exit_instrs[1] = HEQ_INSTR; 226 exit_instrs[2] = STOP_INSTR | stopped_code; 227 break; 228 case SPU_STOPPED_STATUS_S_P: 229 /* SPU_Status[S,P]=1. Add nop instruction 230 * followed by 'br -4' after end of restore 231 * code. 232 */ 233 exit_instrs[0] = RESTORE_COMPLETE; 234 exit_instrs[1] = STOP_INSTR | stopped_code; 235 exit_instrs[2] = NOP_INSTR; 236 exit_instrs[3] = BR_INSTR; 237 break; 238 case SPU_STOPPED_STATUS_S_I: 239 /* SPU_Status[S,I]=1. Add illegal instruction 240 * followed by 'br -4' after end of restore code. 241 */ 242 exit_instrs[0] = RESTORE_COMPLETE; 243 exit_instrs[1] = ILLEGAL_INSTR; 244 exit_instrs[2] = NOP_INSTR; 245 exit_instrs[3] = BR_INSTR; 246 break; 247 case SPU_STOPPED_STATUS_I: 248 /* SPU_Status[I]=1. Add illegal instruction followed 249 * by infinite loop after end of restore sequence. 250 */ 251 exit_instrs[0] = RESTORE_COMPLETE; 252 exit_instrs[1] = ILLEGAL_INSTR; 253 exit_instrs[2] = NOP_INSTR; 254 exit_instrs[3] = BR_INSTR; 255 break; 256 case SPU_STOPPED_STATUS_S: 257 /* SPU_Status[S]=1. Add two 'nop' instructions. */ 258 exit_instrs[0] = RESTORE_COMPLETE; 259 exit_instrs[1] = NOP_INSTR; 260 exit_instrs[2] = NOP_INSTR; 261 exit_instrs[3] = BR_INSTR; 262 break; 263 case SPU_STOPPED_STATUS_H: 264 /* SPU_Status[H]=1. Add 'heq $0, $0' instruction 265 * after end of restore code. 266 */ 267 exit_instrs[0] = RESTORE_COMPLETE; 268 exit_instrs[1] = HEQ_INSTR; 269 exit_instrs[2] = NOP_INSTR; 270 exit_instrs[3] = BR_INSTR; 271 break; 272 case SPU_STOPPED_STATUS_P: 273 /* SPU_Status[P]=1. Add stop-and-signal instruction 274 * after end of restore code. 275 */ 276 exit_instrs[0] = RESTORE_COMPLETE; 277 exit_instrs[1] = STOP_INSTR | stopped_code; 278 break; 279 case SPU_STOPPED_STATUS_R: 280 /* SPU_Status[I,S,H,P,R]=0. Add infinite loop. */ 281 exit_instrs[0] = RESTORE_COMPLETE; 282 exit_instrs[1] = NOP_INSTR; 283 exit_instrs[2] = NOP_INSTR; 284 exit_instrs[3] = BR_INSTR; 285 break; 286 default: 287 /* SPU_Status[R]=1. No additional instructions. */ 288 break; 289 } 290 spu_sync(); 291 } 292 293 /** 294 * main - entry point for SPU-side context restore. 295 * 296 * This code deviates from the documented sequence in the 297 * following aspects: 298 * 299 * 1. The EA for LSCSA is passed from PPE in the 300 * signal notification channels. 301 * 2. The register spill area is pulled by SPU 302 * into LS, rather than pushed by PPE. 303 * 3. All 128 registers are restored by exit(). 304 * 4. The exit() function is modified at run 305 * time in order to properly restore the 306 * SPU_Status register. 307 */ 308 int main() 309 { 310 addr64 lscsa_ea; 311 312 lscsa_ea.ui[0] = spu_readch(SPU_RdSigNotify1); 313 lscsa_ea.ui[1] = spu_readch(SPU_RdSigNotify2); 314 fetch_regs_from_mem(lscsa_ea); 315 316 set_event_mask(); /* Step 1. */ 317 set_tag_mask(); /* Step 2. */ 318 build_dma_list(lscsa_ea); /* Step 3. */ 319 restore_upper_240kb(lscsa_ea); /* Step 4. */ 320 /* Step 5: done by 'exit'. */ 321 enqueue_putllc(lscsa_ea); /* Step 7. */ 322 set_tag_update(); /* Step 8. */ 323 read_tag_status(); /* Step 9. */ 324 restore_decr(); /* moved Step 6. */ 325 read_llar_status(); /* Step 10. */ 326 write_ppu_mb(); /* Step 11. */ 327 write_ppuint_mb(); /* Step 12. */ 328 restore_fpcr(); /* Step 13. */ 329 restore_srr0(); /* Step 14. */ 330 restore_event_mask(); /* Step 15. */ 331 restore_tag_mask(); /* Step 16. */ 332 /* Step 17. done by 'exit'. */ 333 restore_complete(); /* Step 18. */ 334 335 return 0; 336 } 337