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

cyrix.c (5914B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <linux/init.h>
      3#include <linux/io.h>
      4#include <linux/mm.h>
      5
      6#include <asm/processor-cyrix.h>
      7#include <asm/processor-flags.h>
      8#include <asm/mtrr.h>
      9#include <asm/msr.h>
     10
     11#include "mtrr.h"
     12
     13static void
     14cyrix_get_arr(unsigned int reg, unsigned long *base,
     15	      unsigned long *size, mtrr_type * type)
     16{
     17	unsigned char arr, ccr3, rcr, shift;
     18	unsigned long flags;
     19
     20	arr = CX86_ARR_BASE + (reg << 1) + reg;	/* avoid multiplication by 3 */
     21
     22	local_irq_save(flags);
     23
     24	ccr3 = getCx86(CX86_CCR3);
     25	setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10);	/* enable MAPEN */
     26	((unsigned char *)base)[3] = getCx86(arr);
     27	((unsigned char *)base)[2] = getCx86(arr + 1);
     28	((unsigned char *)base)[1] = getCx86(arr + 2);
     29	rcr = getCx86(CX86_RCR_BASE + reg);
     30	setCx86(CX86_CCR3, ccr3);			/* disable MAPEN */
     31
     32	local_irq_restore(flags);
     33
     34	shift = ((unsigned char *) base)[1] & 0x0f;
     35	*base >>= PAGE_SHIFT;
     36
     37	/*
     38	 * Power of two, at least 4K on ARR0-ARR6, 256K on ARR7
     39	 * Note: shift==0xf means 4G, this is unsupported.
     40	 */
     41	if (shift)
     42		*size = (reg < 7 ? 0x1UL : 0x40UL) << (shift - 1);
     43	else
     44		*size = 0;
     45
     46	/* Bit 0 is Cache Enable on ARR7, Cache Disable on ARR0-ARR6 */
     47	if (reg < 7) {
     48		switch (rcr) {
     49		case 1:
     50			*type = MTRR_TYPE_UNCACHABLE;
     51			break;
     52		case 8:
     53			*type = MTRR_TYPE_WRBACK;
     54			break;
     55		case 9:
     56			*type = MTRR_TYPE_WRCOMB;
     57			break;
     58		case 24:
     59		default:
     60			*type = MTRR_TYPE_WRTHROUGH;
     61			break;
     62		}
     63	} else {
     64		switch (rcr) {
     65		case 0:
     66			*type = MTRR_TYPE_UNCACHABLE;
     67			break;
     68		case 8:
     69			*type = MTRR_TYPE_WRCOMB;
     70			break;
     71		case 9:
     72			*type = MTRR_TYPE_WRBACK;
     73			break;
     74		case 25:
     75		default:
     76			*type = MTRR_TYPE_WRTHROUGH;
     77			break;
     78		}
     79	}
     80}
     81
     82/*
     83 * cyrix_get_free_region - get a free ARR.
     84 *
     85 * @base: the starting (base) address of the region.
     86 * @size: the size (in bytes) of the region.
     87 *
     88 * Returns: the index of the region on success, else -1 on error.
     89*/
     90static int
     91cyrix_get_free_region(unsigned long base, unsigned long size, int replace_reg)
     92{
     93	unsigned long lbase, lsize;
     94	mtrr_type ltype;
     95	int i;
     96
     97	switch (replace_reg) {
     98	case 7:
     99		if (size < 0x40)
    100			break;
    101		fallthrough;
    102	case 6:
    103	case 5:
    104	case 4:
    105		return replace_reg;
    106	case 3:
    107	case 2:
    108	case 1:
    109	case 0:
    110		return replace_reg;
    111	}
    112	/* If we are to set up a region >32M then look at ARR7 immediately */
    113	if (size > 0x2000) {
    114		cyrix_get_arr(7, &lbase, &lsize, &ltype);
    115		if (lsize == 0)
    116			return 7;
    117		/* Else try ARR0-ARR6 first  */
    118	} else {
    119		for (i = 0; i < 7; i++) {
    120			cyrix_get_arr(i, &lbase, &lsize, &ltype);
    121			if (lsize == 0)
    122				return i;
    123		}
    124		/*
    125		 * ARR0-ARR6 isn't free
    126		 * try ARR7 but its size must be at least 256K
    127		 */
    128		cyrix_get_arr(i, &lbase, &lsize, &ltype);
    129		if ((lsize == 0) && (size >= 0x40))
    130			return i;
    131	}
    132	return -ENOSPC;
    133}
    134
    135static u32 cr4, ccr3;
    136
    137static void prepare_set(void)
    138{
    139	u32 cr0;
    140
    141	/*  Save value of CR4 and clear Page Global Enable (bit 7)  */
    142	if (boot_cpu_has(X86_FEATURE_PGE)) {
    143		cr4 = __read_cr4();
    144		__write_cr4(cr4 & ~X86_CR4_PGE);
    145	}
    146
    147	/*
    148	 * Disable and flush caches.
    149	 * Note that wbinvd flushes the TLBs as a side-effect
    150	 */
    151	cr0 = read_cr0() | X86_CR0_CD;
    152	wbinvd();
    153	write_cr0(cr0);
    154	wbinvd();
    155
    156	/* Cyrix ARRs - everything else was excluded at the top */
    157	ccr3 = getCx86(CX86_CCR3);
    158
    159	/* Cyrix ARRs - everything else was excluded at the top */
    160	setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10);
    161}
    162
    163static void post_set(void)
    164{
    165	/* Flush caches and TLBs */
    166	wbinvd();
    167
    168	/* Cyrix ARRs - everything else was excluded at the top */
    169	setCx86(CX86_CCR3, ccr3);
    170
    171	/* Enable caches */
    172	write_cr0(read_cr0() & ~X86_CR0_CD);
    173
    174	/* Restore value of CR4 */
    175	if (boot_cpu_has(X86_FEATURE_PGE))
    176		__write_cr4(cr4);
    177}
    178
    179static void cyrix_set_arr(unsigned int reg, unsigned long base,
    180			  unsigned long size, mtrr_type type)
    181{
    182	unsigned char arr, arr_type, arr_size;
    183
    184	arr = CX86_ARR_BASE + (reg << 1) + reg;	/* avoid multiplication by 3 */
    185
    186	/* count down from 32M (ARR0-ARR6) or from 2G (ARR7) */
    187	if (reg >= 7)
    188		size >>= 6;
    189
    190	size &= 0x7fff;		/* make sure arr_size <= 14 */
    191	for (arr_size = 0; size; arr_size++, size >>= 1)
    192		;
    193
    194	if (reg < 7) {
    195		switch (type) {
    196		case MTRR_TYPE_UNCACHABLE:
    197			arr_type = 1;
    198			break;
    199		case MTRR_TYPE_WRCOMB:
    200			arr_type = 9;
    201			break;
    202		case MTRR_TYPE_WRTHROUGH:
    203			arr_type = 24;
    204			break;
    205		default:
    206			arr_type = 8;
    207			break;
    208		}
    209	} else {
    210		switch (type) {
    211		case MTRR_TYPE_UNCACHABLE:
    212			arr_type = 0;
    213			break;
    214		case MTRR_TYPE_WRCOMB:
    215			arr_type = 8;
    216			break;
    217		case MTRR_TYPE_WRTHROUGH:
    218			arr_type = 25;
    219			break;
    220		default:
    221			arr_type = 9;
    222			break;
    223		}
    224	}
    225
    226	prepare_set();
    227
    228	base <<= PAGE_SHIFT;
    229	setCx86(arr + 0,  ((unsigned char *)&base)[3]);
    230	setCx86(arr + 1,  ((unsigned char *)&base)[2]);
    231	setCx86(arr + 2, (((unsigned char *)&base)[1]) | arr_size);
    232	setCx86(CX86_RCR_BASE + reg, arr_type);
    233
    234	post_set();
    235}
    236
    237typedef struct {
    238	unsigned long	base;
    239	unsigned long	size;
    240	mtrr_type	type;
    241} arr_state_t;
    242
    243static arr_state_t arr_state[8] = {
    244	{0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL},
    245	{0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}
    246};
    247
    248static unsigned char ccr_state[7] = { 0, 0, 0, 0, 0, 0, 0 };
    249
    250static void cyrix_set_all(void)
    251{
    252	int i;
    253
    254	prepare_set();
    255
    256	/* the CCRs are not contiguous */
    257	for (i = 0; i < 4; i++)
    258		setCx86(CX86_CCR0 + i, ccr_state[i]);
    259	for (; i < 7; i++)
    260		setCx86(CX86_CCR4 + i, ccr_state[i]);
    261
    262	for (i = 0; i < 8; i++) {
    263		cyrix_set_arr(i, arr_state[i].base,
    264			      arr_state[i].size, arr_state[i].type);
    265	}
    266
    267	post_set();
    268}
    269
    270static const struct mtrr_ops cyrix_mtrr_ops = {
    271	.vendor            = X86_VENDOR_CYRIX,
    272	.set_all	   = cyrix_set_all,
    273	.set               = cyrix_set_arr,
    274	.get               = cyrix_get_arr,
    275	.get_free_region   = cyrix_get_free_region,
    276	.validate_add_page = generic_validate_add_page,
    277	.have_wrcomb       = positive_have_wrcomb,
    278};
    279
    280int __init cyrix_init_mtrr(void)
    281{
    282	set_mtrr_ops(&cyrix_mtrr_ops);
    283	return 0;
    284}