微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在禁用 SMP 支持的情况下运行“invd”指令?

如何解决如何在禁用 SMP 支持的情况下运行“invd”指令?

我正在尝试从内核模块执行“invd”指令。我之前问过一个类似的问题 How to execute “invd” instruction? 并且从@Peter Cordes 的回答中,我知道系统启动后我无法在 SMP 系统上安全地运行此指令。那么,我不应该在没有 SMP 支持的情况下在启动后运行这条指令吗?因为没有其他核心在运行,所以内存不一致没有变化?我使用 -o0 标志编译了以下内核模块,

static int __init deviceDriver_init(void){

unsigned long flags;
int LEN=10;
int STEP=1;
int VALUE=1;
int arr[LEN];
int i;
unsigned long dummy;

printk(KERN_INFO "invd Driver loaded\n");

//wbinvd();
//asm volatile("cpuid\n":::);

local_irq_disable();

__asm__ __volatile__(

    "wbinvd\n"
    "loop:"
    "movq %%rdx,(%%rbx);"
    "leaq (%%rbx,%%rcx,8),%%rbx;"
    "cmpq %%rbx,%%rax;"
    "jg loop;"

    "invd\n"
    : "=b"(dummy) // output
    : "b" (arr),"a" (arr+LEN),"c" (STEP),"d" (VALUE)
    : "cc","memory"
);

local_irq_enable();

//asm volatile("invd\n":::);

printk(KERN_INFO "invd execute\n"); 

return 0; 
}

在插入模块时我仍然收到以下错误,我在终端中得到 Segmentation fault (core dumped) 并且 dmesg 显示

[ 2590.518614] invd Driver loaded
[ 2590.518840] general protection fault: 0000 [#5] SMP PTI

我用 nosmp 启动内核,但我不明白为什么 dmesg 仍然显示 SMP PTI

$cat /proc/cmdline 
BOOT_IMAGE=/boot/vmlinuz-4.15.0-136-generic root=UUID=dbe747ff-a6a5-45cb-8553-c6db6d445d3d ro quiet splash nosmp vt.handoff=7

更新帖子:

正如我在评论部分提到的,在从 BIOS 禁用 SGX 后,我能够毫无错误地运行此 invd。但是,当我尝试在具有相同内核版本的不同机器上运行相同的代码时,我仍然收到相同的错误消息。这很奇怪,我无法解释为什么会发生这种情况。在评论部分,@prl 提到错误可能来自 invd 后面的指令。我开始认为这也许是真的。因为 dmesg 中倒数第二行在 RED [ 153.527386] RIP: loop+0xc/0xf22 [noSmp8] RSP: ffffb8d9450a7be0 中突出显示。因此,错误似乎来自 loop 内部。我已经根据建议更新了 __init 函数代码。我不擅长汇编代码,谁能告诉我内联汇编代码是否正确?如果这个内联汇编代码不正确如何修复代码?我的整个 dmesg 跟踪是,

[  153.514293] invd Driver loaded
[  153.514547] general protection fault: 0000 [#1] SMP PTI
[  153.514656] Modules linked in: noSmp8(OE+) xt_CHECKSUM iptable_mangle ipt_MASQUERADE nf_nat_masquerade_ipv4 iptable_nat nf_nat_ipv4 nf_nat nf_conntrack_ipv4 nf_defrag_ipv4 xt_conntrack nf_conntrack libcrc32c ipt_REJECT nf_reject_ipv4 xt_tcpudp bridge stp llc ebtable_filter ebtables ip6table_filter ip6_tables iptable_filter ip_tables x_tables ccm arc4 intel_rapl rt2800usb rt2x00usb x86_pkg_temp_thermal intel_powerclamp rt2800lib coretemp rt2x00lib mac80211 cfg80211 kvm_intel kvm irqbypass snd_hda_codec_realtek crct10dif_pclmul crc32_pclmul ghash_clmulni_intel snd_hda_codec_hdmi pcbc aesni_intel aes_x86_64 crypto_simd glue_helper cryptd intel_cstate intel_rapl_perf dell_smm_hwmon dell_wmi dell_smbios dcdbas intel_wmi_thunderbolt snd_hda_codec_generic dell_wmi_descriptor wmi_bmof snd_seq_midi snd_seq_midi_event
[  153.515454]  serio_raw snd_hda_intel snd_hda_codec snd_hda_core sparse_keymap snd_hwdep snd_rawmidi joydev input_leds snd_seq snd_pcm snd_seq_device snd_timer snd soundcore mei_me mei shpchp intel_pch_thermal mac_hid acpi_pad parport_pc ppdev lp parport autofs4 hid_generic usbhid hid nouveau mxm_wmi ttm drm_kms_helper psmouse syscopyarea sysfillrect sysimgblt igb e1000e dca i2c_algo_bit ptp pps_core ahci libahci fb_sys_fops drm wmi video
[  153.516038] cpu: 0 PID: 4024 Comm: insmod Tainted: G           OE    4.15.0-136-generic #140~16.04.1-Ubuntu
[  153.516331] Hardware name: Dell Inc. BIOS 1.3.2 01/25/2016
[  153.516626] RIP: 0010:loop+0xc/0xf22 [noSmp8]
[  153.516917] RSP: 0018:ffffb8d9450a7be0 EFLAGS: 00010046
[  153.517213] RAX: ffffb8d9450a7c08 RBX: ffffb8d9450a7c08 RCX: 0000000000000001
[  153.517513] RDX: 0000000000000001 RSI: ffffb8d9450a7be0 RDI: ffff8edaadc16490
[  153.517814] RBP: ffffb8d9450a7c60 R08: 0000000000012c40 R09: ffffffffb39624c4
[  153.518119] R10: ffffb8d9450a7c78 R11: 000000000000038c R12: ffffb8d9450a7c10
[  153.518427] R13: 0000000000000000 R14: 0000000000000001 R15: ffff8eda4c6bd660
[  153.518730] FS:  00007fd7f09cf700(0000) GS:ffff8edaadc00000(0000) knlGS:0000000000000000
[  153.519036] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  153.519346] CR2: 00005634f95fde50 CR3: 000000040dd2c001 CR4: 00000000003606f0
[  153.519656] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[  153.519980] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[  153.520289] Call Trace:
[  153.520597]  ? 0xffffffffc050d000
[  153.520899]  do_one_initcall+0x55/0x1ac
[  153.521201]  ? do_one_initcall+0x55/0x1ac
[  153.521504]  ? do_init_module+0x27/0x223
[  153.521808]  ? _cond_resched+0x32/0x50
[  153.522107]  ? kmem_cache_alloc_trace+0x165/0x1c0
[  153.522408]  do_init_module+0x5f/0x223
[  153.522710]  load_module+0x188c/0x1ea0
[  153.523016]  ? ima_post_read_file+0x83/0xa0
[  153.523320]  SYSC_finit_module+0xe5/0x120
[  153.523623]  ? SYSC_finit_module+0xe5/0x120
[  153.523927]  SyS_finit_module+0xe/0x10
[  153.524231]  do_syscall_64+0x73/0x130
[  153.524534]  entry_SYSCALL_64_after_hwframe+0x41/0xa6
[  153.524838] RIP: 0033:0x7fd7f04fd599
[  153.525144] RSP: 002b:00007ffda61c2968 EFLAGS: 00000202 ORIG_RAX: 0000000000000139
[  153.525455] RAX: ffffffffffffffda RBX: 00005643631d7210 RCX: 00007fd7f04fd599
[  153.525768] RDX: 0000000000000000 RSI: 0000564361c3226b RDI: 0000000000000003
[  153.526084] RBP: 0000564361c3226b R08: 0000000000000000 R09: 00007fd7f07c2ea0
[  153.526403] R10: 0000000000000003 R11: 0000000000000202 R12: 0000000000000000
[  153.526722] R13: 00005643631d7ca0 R14: 0000000000000000 R15: 0000000000000000
[  153.527040] Code: 00 48 8b 75 c8 48 8b 45 c8 8b 55 b8 48 63 d2 48 c1 e2 02 48 01 d0 8b 4d b4 8b 55 bc 48 89 f3 48 89 13 48 8d 1c cb 48 39 d8 7f f4 <0f> 08 48 89 d8 48 89 45 d0 e8 40 ef 73 00 48 c7 c7 c7 d0 c4 c0 
[  153.527386] RIP: loop+0xc/0xf22 [noSmp8] RSP: ffffb8d9450a7be0
[  153.530228] ---[ end trace cc9ea64985c9fe34 ]---

那么,即使没有 SMP 也无法运行 invd

解决方法

这里有两个问题:

a) 如何执行 INVD(不安全)

为此,您需要在 CPL=0 下运行,并且您必须确保 CPU 没有使用任何“处理器保留内存保护”,这是英特尔软件保护扩展(允许程序运行的扩展)的一部分具有操作系统无法篡改的屏蔽/私有/加密空间,通常用于数字权限管理方案,但也可能用于增强其他事物的安全性/机密性)。

请注意,最新版本的 Linux 支持 SGX,但我不确定何时引入支持或您的内核有多旧,或者是否启用/禁用。

如果其中任何一个不正确(例如,您处于 CPL=3 或存在“处理器保留内存保护”),您将收到一般保护错误异常。

b) 如何安全地执行 INVD

为此,您必须确保缓存(包括“外部缓存”——例如可能包括 eDRAM 和内置于非易失性 RAM 中的缓存之类的东西)不包含任何会在丢失时导致问题的修改数据.这包括来自以下方面的数据:

  • IRQ。这些可以被禁用。

  • NMI 和机器检查异常。对于正在运行的操作系统,几乎不可能停止/禁用这些,如果您可以禁用它们,那么就像交叉手指而忽略严重的硬件故障(一个非常糟糕的主意)。

  • 固件的系统管理模式。这是一种特殊的 CPU 模式,固件用于各种不受操作系统/内核控制的事情(例如 ECC 清理、某些电源管理、旧设备的仿真)。它不能被禁用。

  • 写入由 CPU 本身完成。这包括更新页表中的访问/脏标志(无法禁用),以及将数据存储在内存中的任何性能监控或调试功能(可以“未启用”)。

由于这些限制(并且不要忘记性能问题),只有两种情况下 INVD 可能是正常的 - 需要确定 RAM 芯片大小和配置内存控制器的早期固件代码(它很可能有用/正常) ,以及计算机关闭前的瞬间(这可能毫无意义)。

猜测

我猜测(基于我无法想到任何其他合理的原因)您想要构建临时屏蔽/私有内存区域(以增强安全性 - 例如,这样您放入该区域的数据不会/ 不能泄漏到 RAM 中)。在这种情况下(具有讽刺意味的是),专为此工作 (SGX) 设计的工具可能会阻止您做得不好。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。