cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

spu_restore.c (8601B)


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