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

unaligned_32.c (7200B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * unaligned.c: Unaligned load/store trap handling with special
      4 *              cases for the kernel to do them more quickly.
      5 *
      6 * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
      7 * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
      8 */
      9
     10
     11#include <linux/kernel.h>
     12#include <linux/sched/signal.h>
     13#include <linux/mm.h>
     14#include <asm/ptrace.h>
     15#include <asm/processor.h>
     16#include <linux/uaccess.h>
     17#include <linux/smp.h>
     18#include <linux/perf_event.h>
     19#include <linux/extable.h>
     20
     21#include <asm/setup.h>
     22
     23#include "kernel.h"
     24
     25enum direction {
     26	load,    /* ld, ldd, ldh, ldsh */
     27	store,   /* st, std, sth, stsh */
     28	both,    /* Swap, ldstub, etc. */
     29	fpload,
     30	fpstore,
     31	invalid,
     32};
     33
     34static inline enum direction decode_direction(unsigned int insn)
     35{
     36	unsigned long tmp = (insn >> 21) & 1;
     37
     38	if(!tmp)
     39		return load;
     40	else {
     41		if(((insn>>19)&0x3f) == 15)
     42			return both;
     43		else
     44			return store;
     45	}
     46}
     47
     48/* 8 = double-word, 4 = word, 2 = half-word */
     49static inline int decode_access_size(unsigned int insn)
     50{
     51	insn = (insn >> 19) & 3;
     52
     53	if(!insn)
     54		return 4;
     55	else if(insn == 3)
     56		return 8;
     57	else if(insn == 2)
     58		return 2;
     59	else {
     60		printk("Impossible unaligned trap. insn=%08x\n", insn);
     61		die_if_kernel("Byte sized unaligned access?!?!", current->thread.kregs);
     62		return 4; /* just to keep gcc happy. */
     63	}
     64}
     65
     66/* 0x400000 = signed, 0 = unsigned */
     67static inline int decode_signedness(unsigned int insn)
     68{
     69	return (insn & 0x400000);
     70}
     71
     72static inline void maybe_flush_windows(unsigned int rs1, unsigned int rs2,
     73				       unsigned int rd)
     74{
     75	if(rs2 >= 16 || rs1 >= 16 || rd >= 16) {
     76		/* Wheee... */
     77		__asm__ __volatile__("save %sp, -0x40, %sp\n\t"
     78				     "save %sp, -0x40, %sp\n\t"
     79				     "save %sp, -0x40, %sp\n\t"
     80				     "save %sp, -0x40, %sp\n\t"
     81				     "save %sp, -0x40, %sp\n\t"
     82				     "save %sp, -0x40, %sp\n\t"
     83				     "save %sp, -0x40, %sp\n\t"
     84				     "restore; restore; restore; restore;\n\t"
     85				     "restore; restore; restore;\n\t");
     86	}
     87}
     88
     89static inline int sign_extend_imm13(int imm)
     90{
     91	return imm << 19 >> 19;
     92}
     93
     94static inline unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)
     95{
     96	struct reg_window32 *win;
     97
     98	if(reg < 16)
     99		return (!reg ? 0 : regs->u_regs[reg]);
    100
    101	/* Ho hum, the slightly complicated case. */
    102	win = (struct reg_window32 *) regs->u_regs[UREG_FP];
    103	return win->locals[reg - 16]; /* yes, I know what this does... */
    104}
    105
    106static inline unsigned long safe_fetch_reg(unsigned int reg, struct pt_regs *regs)
    107{
    108	struct reg_window32 __user *win;
    109	unsigned long ret;
    110
    111	if (reg < 16)
    112		return (!reg ? 0 : regs->u_regs[reg]);
    113
    114	/* Ho hum, the slightly complicated case. */
    115	win = (struct reg_window32 __user *) regs->u_regs[UREG_FP];
    116
    117	if ((unsigned long)win & 3)
    118		return -1;
    119
    120	if (get_user(ret, &win->locals[reg - 16]))
    121		return -1;
    122
    123	return ret;
    124}
    125
    126static inline unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs)
    127{
    128	struct reg_window32 *win;
    129
    130	if(reg < 16)
    131		return &regs->u_regs[reg];
    132	win = (struct reg_window32 *) regs->u_regs[UREG_FP];
    133	return &win->locals[reg - 16];
    134}
    135
    136static unsigned long compute_effective_address(struct pt_regs *regs,
    137					       unsigned int insn)
    138{
    139	unsigned int rs1 = (insn >> 14) & 0x1f;
    140	unsigned int rs2 = insn & 0x1f;
    141	unsigned int rd = (insn >> 25) & 0x1f;
    142
    143	if(insn & 0x2000) {
    144		maybe_flush_windows(rs1, 0, rd);
    145		return (fetch_reg(rs1, regs) + sign_extend_imm13(insn));
    146	} else {
    147		maybe_flush_windows(rs1, rs2, rd);
    148		return (fetch_reg(rs1, regs) + fetch_reg(rs2, regs));
    149	}
    150}
    151
    152unsigned long safe_compute_effective_address(struct pt_regs *regs,
    153					     unsigned int insn)
    154{
    155	unsigned int rs1 = (insn >> 14) & 0x1f;
    156	unsigned int rs2 = insn & 0x1f;
    157	unsigned int rd = (insn >> 25) & 0x1f;
    158
    159	if(insn & 0x2000) {
    160		maybe_flush_windows(rs1, 0, rd);
    161		return (safe_fetch_reg(rs1, regs) + sign_extend_imm13(insn));
    162	} else {
    163		maybe_flush_windows(rs1, rs2, rd);
    164		return (safe_fetch_reg(rs1, regs) + safe_fetch_reg(rs2, regs));
    165	}
    166}
    167
    168/* This is just to make gcc think panic does return... */
    169static void unaligned_panic(char *str)
    170{
    171	panic("%s", str);
    172}
    173
    174/* una_asm.S */
    175extern int do_int_load(unsigned long *dest_reg, int size,
    176		       unsigned long *saddr, int is_signed);
    177extern int __do_int_store(unsigned long *dst_addr, int size,
    178			  unsigned long *src_val);
    179
    180static int do_int_store(int reg_num, int size, unsigned long *dst_addr,
    181			struct pt_regs *regs)
    182{
    183	unsigned long zero[2] = { 0, 0 };
    184	unsigned long *src_val;
    185
    186	if (reg_num)
    187		src_val = fetch_reg_addr(reg_num, regs);
    188	else {
    189		src_val = &zero[0];
    190		if (size == 8)
    191			zero[1] = fetch_reg(1, regs);
    192	}
    193	return __do_int_store(dst_addr, size, src_val);
    194}
    195
    196extern void smp_capture(void);
    197extern void smp_release(void);
    198
    199static inline void advance(struct pt_regs *regs)
    200{
    201	regs->pc   = regs->npc;
    202	regs->npc += 4;
    203}
    204
    205static inline int floating_point_load_or_store_p(unsigned int insn)
    206{
    207	return (insn >> 24) & 1;
    208}
    209
    210static inline int ok_for_kernel(unsigned int insn)
    211{
    212	return !floating_point_load_or_store_p(insn);
    213}
    214
    215static void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn)
    216{
    217	const struct exception_table_entry *entry;
    218
    219	entry = search_exception_tables(regs->pc);
    220	if (!entry) {
    221		unsigned long address = compute_effective_address(regs, insn);
    222        	if(address < PAGE_SIZE) {
    223                	printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference in mna handler");
    224        	} else
    225                	printk(KERN_ALERT "Unable to handle kernel paging request in mna handler");
    226	        printk(KERN_ALERT " at virtual address %08lx\n",address);
    227		printk(KERN_ALERT "current->{mm,active_mm}->context = %08lx\n",
    228			(current->mm ? current->mm->context :
    229			current->active_mm->context));
    230		printk(KERN_ALERT "current->{mm,active_mm}->pgd = %08lx\n",
    231			(current->mm ? (unsigned long) current->mm->pgd :
    232			(unsigned long) current->active_mm->pgd));
    233	        die_if_kernel("Oops", regs);
    234		/* Not reached */
    235	}
    236	regs->pc = entry->fixup;
    237	regs->npc = regs->pc + 4;
    238}
    239
    240asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn)
    241{
    242	enum direction dir = decode_direction(insn);
    243	int size = decode_access_size(insn);
    244
    245	if(!ok_for_kernel(insn) || dir == both) {
    246		printk("Unsupported unaligned load/store trap for kernel at <%08lx>.\n",
    247		       regs->pc);
    248		unaligned_panic("Wheee. Kernel does fpu/atomic unaligned load/store.");
    249	} else {
    250		unsigned long addr = compute_effective_address(regs, insn);
    251		int err;
    252
    253		perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, addr);
    254		switch (dir) {
    255		case load:
    256			err = do_int_load(fetch_reg_addr(((insn>>25)&0x1f),
    257							 regs),
    258					  size, (unsigned long *) addr,
    259					  decode_signedness(insn));
    260			break;
    261
    262		case store:
    263			err = do_int_store(((insn>>25)&0x1f), size,
    264					   (unsigned long *) addr, regs);
    265			break;
    266		default:
    267			panic("Impossible kernel unaligned trap.");
    268			/* Not reached... */
    269		}
    270		if (err)
    271			kernel_mna_trap_fault(regs, insn);
    272		else
    273			advance(regs);
    274	}
    275}
    276
    277asmlinkage void user_unaligned_trap(struct pt_regs *regs, unsigned int insn)
    278{
    279	send_sig_fault(SIGBUS, BUS_ADRALN,
    280		       (void __user *)safe_compute_effective_address(regs, insn),
    281		       current);
    282}