Assembly 有没有办法从EL0刷新ARM缓存?

Assembly 有没有办法从EL0刷新ARM缓存?,assembly,caching,arm64,cpu-cache,armv8,Assembly,Caching,Arm64,Cpu Cache,Armv8,我一直在尝试在ARMV8上实现Spectre攻击的PoC代码(我知道大多数ARMV8处理器不易受到攻击,但无论如何都在尝试实现它)。我使用的是asm volatile(“DC-CIVAC,%[ad]”::[ad]“r”(addr))指令刷新缓存,然后尝试测量再次读取刷新地址的时间。但是在清除缓存之前和之后读取地址时,我没有发现任何差异。这让我意识到我用于flush的指令不起作用 在阅读ARMV8参考手册()时,我意识到要使DC CIVAC指令工作,应将SCTLR_EL1.UCI设置为1。我不确定

我一直在尝试在ARMV8上实现Spectre攻击的PoC代码(我知道大多数ARMV8处理器不易受到攻击,但无论如何都在尝试实现它)。我使用的是asm volatile(“DC-CIVAC,%[ad]”::[ad]“r”(addr))指令刷新缓存,然后尝试测量再次读取刷新地址的时间。但是在清除缓存之前和之后读取地址时,我没有发现任何差异。这让我意识到我用于flush的指令不起作用


在阅读ARMV8参考手册()时,我意识到要使
DC CIVAC
指令工作,应将
SCTLR_EL1.UCI
设置为1。我不确定如何在不需要
sudo
特权的情况下检查
SCTLR\u EL1
寄存器的值并将其更改为1

下面的过程提供了从Linux用户模式读取/写入
SCTLR\u EL1
的方法。 未回答在运行的Linux系统上读/写SCTLR_EL1是否能够/足以刷新缓存

这显然是一种不安全的做法,因为它显然破坏了
Armv8-a
安全方案-不要在家尝试,而是为了学习

该过程是在运行的
OrangePi PC2
上执行的(内核版本是
LinuxOrangePic25.4.28-sunxi64#20.02.7 SMP-Sat Mar 28 17:25:10 CET 2020 aarch64 GNU/Linux

安装最新的内核/linux标头:

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install linux-headers-current-sunxi64
sudo shutdown -r now
使用
procfs
支持编译最小可加载内核模块:

Makefile

obj-m+=/sctlr_el1.o
KBUILD_DIR=/usr/src/linux-headers-5.4.28-sunxi64
all:
        make -C $(KBUILD_DIR) M=$(PWD) modules
# Kernel module clean dependency
clean:
        make -C $(KBUILD_DIR) M=$(PWD) clean

install:
        insmod sctlr_el1.ko
remove:
        rmmod sctlr_el1
sctlr\u el1.c

// Credits:
//
// minimal kernel module:    https://docs.legato.io/17_07/yoctoOutofTreeKernelModule.html
// procfs example:           https://gist.github.com/BrotherJing/c9c5ffdc9954d998d1336711fa3a6480

#include<linux/module.h>
#include<linux/init.h>
#include<linux/proc_fs.h>
#include<linux/sched.h>
#include<linux/uaccess.h>
#include<linux/fs.h>
#include<linux/seq_file.h>
#include<linux/slab.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Frant");
MODULE_DESCRIPTION("Minimal kernel module suitable for reading/writing SCTLR_EL1");
MODULE_VERSION("0.0.1");

// write system register  SCTLR_EL1 (s3_0_c1_c0_0) with specified value.
static inline void system_write_SCTLR_EL1(uint64_t val)
{
    asm volatile("msr s3_0_c1_c0_0 , %0" : : "r" (val));
    asm volatile("isb" : : : "memory"); 
}

// read system register value SCTLR_EL1 (s3_0_c1_c0_0).
static inline uint64_t system_read_SCTLR_EL1(void)
{
    uint64_t val;
    asm volatile("mrs %0, s3_0_c1_c0_0" : "=r" (val));
    return val;
}

static int my_proc_show(struct seq_file *m,void *v) {
    uint64_t value = system_read_SCTLR_EL1();
    printk(KERN_INFO "Read value <0x%016llx> from SCTLR_EL1.", value);
    seq_printf(m,"0x%016llx\n",value);
    return 0;
}

static ssize_t my_proc_write(struct file* file,const char __user *buffer,size_t count,loff_t *f_pos){
    int rc = -1;
    uint64_t value = 0;

    char *tmp = kzalloc((count+1),GFP_KERNEL);
    if(!tmp)return -ENOMEM;
    if(copy_from_user(tmp,buffer,count)){
        kfree(tmp);
        return EFAULT;
    }

    rc = kstrtoul (tmp, 0, (unsigned long*) &value);
    if (rc != 0) {
       printk(KERN_ERR "Value <%s> is invalid.", tmp);
       kfree(tmp);
       return EFAULT;
    }

    kfree(tmp);
    system_write_SCTLR_EL1(value);
    printk(KERN_INFO "Wrote value <0x%016llx> to SCTLR_EL1.", value);

    return count;
}

static int my_proc_open(struct inode *inode,struct file *file){
    return single_open(file,my_proc_show,NULL);
}

static struct file_operations my_fops={
    .owner = THIS_MODULE,
    .open = my_proc_open,
    .release = single_release,
    .read = seq_read,
    .llseek = seq_lseek,
    .write = my_proc_write
};

static int __init sctlr_el1_init(void) {
    struct proc_dir_entry *entry;
    entry = proc_create("SCTLR_EL1",0777,NULL,&my_fops);
    if(!entry){
        return -1;  
    } else {
        printk(KERN_INFO "Entering sctlr_el1\n");
    }

 return 0;
}

static void __exit sctlr_el1_exit(void) {
    remove_proc_entry("SCTLR_EL1",NULL);
    printk(KERN_INFO "Exiting sctlr_el1\n");
}

module_init(sctlr_el1_init);
module_exit(sctlr_el1_exit);
阅读
SCTLR\u EL1

cat /proc/SCTLR_EL1
0x0000000034d4d91d
SCTLR\u EL1.UCI
在我的系统上

重新写入相同的值:

echo 0x0000000034d4d91d > /proc/SCTLR_EL1
dmesg
[ 1130.967798] Wrote value <0x0000000034d4d91d> to SCTLR_EL1.

下面的过程提供了从Linux用户模式读取/写入
SCTLR\u EL1
的方法。 未回答在运行的Linux系统上读/写SCTLR_EL1是否能够/足以刷新缓存

这显然是一种不安全的做法,因为它显然破坏了
Armv8-a
安全方案-不要在家尝试,而是为了学习

该过程是在运行的
OrangePi PC2
上执行的(内核版本是
LinuxOrangePic25.4.28-sunxi64#20.02.7 SMP-Sat Mar 28 17:25:10 CET 2020 aarch64 GNU/Linux

安装最新的内核/linux标头:

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install linux-headers-current-sunxi64
sudo shutdown -r now
使用
procfs
支持编译最小可加载内核模块:

Makefile

obj-m+=/sctlr_el1.o
KBUILD_DIR=/usr/src/linux-headers-5.4.28-sunxi64
all:
        make -C $(KBUILD_DIR) M=$(PWD) modules
# Kernel module clean dependency
clean:
        make -C $(KBUILD_DIR) M=$(PWD) clean

install:
        insmod sctlr_el1.ko
remove:
        rmmod sctlr_el1
sctlr\u el1.c

// Credits:
//
// minimal kernel module:    https://docs.legato.io/17_07/yoctoOutofTreeKernelModule.html
// procfs example:           https://gist.github.com/BrotherJing/c9c5ffdc9954d998d1336711fa3a6480

#include<linux/module.h>
#include<linux/init.h>
#include<linux/proc_fs.h>
#include<linux/sched.h>
#include<linux/uaccess.h>
#include<linux/fs.h>
#include<linux/seq_file.h>
#include<linux/slab.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Frant");
MODULE_DESCRIPTION("Minimal kernel module suitable for reading/writing SCTLR_EL1");
MODULE_VERSION("0.0.1");

// write system register  SCTLR_EL1 (s3_0_c1_c0_0) with specified value.
static inline void system_write_SCTLR_EL1(uint64_t val)
{
    asm volatile("msr s3_0_c1_c0_0 , %0" : : "r" (val));
    asm volatile("isb" : : : "memory"); 
}

// read system register value SCTLR_EL1 (s3_0_c1_c0_0).
static inline uint64_t system_read_SCTLR_EL1(void)
{
    uint64_t val;
    asm volatile("mrs %0, s3_0_c1_c0_0" : "=r" (val));
    return val;
}

static int my_proc_show(struct seq_file *m,void *v) {
    uint64_t value = system_read_SCTLR_EL1();
    printk(KERN_INFO "Read value <0x%016llx> from SCTLR_EL1.", value);
    seq_printf(m,"0x%016llx\n",value);
    return 0;
}

static ssize_t my_proc_write(struct file* file,const char __user *buffer,size_t count,loff_t *f_pos){
    int rc = -1;
    uint64_t value = 0;

    char *tmp = kzalloc((count+1),GFP_KERNEL);
    if(!tmp)return -ENOMEM;
    if(copy_from_user(tmp,buffer,count)){
        kfree(tmp);
        return EFAULT;
    }

    rc = kstrtoul (tmp, 0, (unsigned long*) &value);
    if (rc != 0) {
       printk(KERN_ERR "Value <%s> is invalid.", tmp);
       kfree(tmp);
       return EFAULT;
    }

    kfree(tmp);
    system_write_SCTLR_EL1(value);
    printk(KERN_INFO "Wrote value <0x%016llx> to SCTLR_EL1.", value);

    return count;
}

static int my_proc_open(struct inode *inode,struct file *file){
    return single_open(file,my_proc_show,NULL);
}

static struct file_operations my_fops={
    .owner = THIS_MODULE,
    .open = my_proc_open,
    .release = single_release,
    .read = seq_read,
    .llseek = seq_lseek,
    .write = my_proc_write
};

static int __init sctlr_el1_init(void) {
    struct proc_dir_entry *entry;
    entry = proc_create("SCTLR_EL1",0777,NULL,&my_fops);
    if(!entry){
        return -1;  
    } else {
        printk(KERN_INFO "Entering sctlr_el1\n");
    }

 return 0;
}

static void __exit sctlr_el1_exit(void) {
    remove_proc_entry("SCTLR_EL1",NULL);
    printk(KERN_INFO "Exiting sctlr_el1\n");
}

module_init(sctlr_el1_init);
module_exit(sctlr_el1_exit);
阅读
SCTLR\u EL1

cat /proc/SCTLR_EL1
0x0000000034d4d91d
SCTLR\u EL1.UCI
在我的系统上

重新写入相同的值:

echo 0x0000000034d4d91d > /proc/SCTLR_EL1
dmesg
[ 1130.967798] Wrote value <0x0000000034d4d91d> to SCTLR_EL1.
Linux默认为所有arm64处理器设置该位(),除了少数Cortex-A53版本需要解决勘误表(,)的问题。但是,即使在未设置
UCI
的情况下,内核中的a也会以内核模式为您执行它们,因此它们仍然可以工作,但速度较慢

总之,您可以期望、、和缓存维护指令在用户空间中始终起作用,至少在最新版本的Linux上是如此。

对于所有arm64处理器,该位由Linux默认设置(),除了少数Cortex-A53版本需要解决勘误表(,)的问题。但是,即使在未设置
UCI
的情况下,内核中的a也会以内核模式为您执行它们,因此它们仍然可以工作,但只是速度较慢


总之,您可以期望、、和缓存维护指令在用户空间中始终起作用,至少在最新版本的Linux上是如此。

您遗漏了一个
“内存”
clobber在您的内联asm中。编译器可以在编译时使用加载或存储对该asm进行重新排序,例如,如果愿意,在刷新后进行存储。您是否检查了编译器生成的asm以确保它正在执行您想要的操作?@Balvansh Heerekar:
sudo
仍将在Linux用户模式下执行任何命令,即在
EL0
您需要的是一个最小的可加载内核模块,能够获取/设置
SCTLR\u EL1.UCI
@Frant有没有一种方法可以在不实现简单内核模块的情况下获取
SCTLR\u EL1.UCI
从运行在
EL1
的应用程序中获取驱动程序,而Linux应用程序总是在
EL0
执行。请注意,如果从一个工作示例开始编写这样的驱动程序并不困难。你会在网上找到很多驱动程序。@Frant:我确实通过添加
内存进行了检查
在我的内联asm中使用clobber,但这似乎也没有帮助。我想我必须检查我的
SCTLR\u EL1.UCI
值。你漏掉了一个
内存
clobber在您的内联asm中。编译器可以在编译时使用加载或存储对该asm进行重新排序,例如,如果愿意,在刷新后进行存储。您是否检查了编译器生成的asm以确保它正在执行您想要的操作?@Balvansh Heerekar:
sudo
仍将在Linux用户模式下执行任何命令,即在
EL0
您需要的是一个最小的可加载内核模块,能够获取/设置
SCTLR\u EL1.UCI
@Frant有没有一种方法可以在不实现简单内核模块的情况下获取
SCTLR\u EL1.UCI
从运行在
EL1
的应用程序中获取驱动程序,而Linux应用程序总是在
EL0
执行。请注意,如果从一个工作示例开始编写这样的驱动程序并不困难。你会在网上找到很多驱动程序。@Frant:我确实通过添加
内存进行了检查
clobber在我的内联asm中,但这似乎也没有帮助。我想我