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

How to run "invd" instruction with disabled SMP support?

我正在尝试从内核模块执行“invd”指令。我之前问过一个类似的问题 How to execute “invd” instruction? 和@Peter Cordes 的回答,我知道我不能在系统启动后安全地 运行 SMP 系统上的这条指令。那么,在没有 SMP 支持的情况下,我不能在启动后 运行 这条指令吗?因为没有其他核心运行ning,所以内存不一致没有变化?我用 -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

更新post:

正如我在评论部分提到的,从 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

这里有 2 个问题:

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

为此,您需要 运行 在 CPL=0,并且您必须确保 CPU 没有使用英特尔的任何“处理器保留内存保护” Software Guard Extensions(允许程序拥有 OS 无法篡改的 shielded/private/encrypted space 的扩展,通常用于数字版权管理方案,但可能用于增强 security/confidentiality 其他东西)。

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

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

b) 如何安全地执行 INVD

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

  • IRQ。这些可以被禁用。

  • NMI 和机器检查异常。对于 运行 OS 来说,几乎不可能 stop/disable 这些,如果你可以禁用它们,那么它就像祈祷你的手指同时忽略关键的硬件故障(一个非常糟糕的主意)。

  • 固件的系统管理模式。这是一种特殊的 CPU 模式,固件用于各种不受 OS/kernel 控制的事情(例如 ECC 清理、一些电源管理、遗留设备的模拟)。无法禁用。

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

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

猜测

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