Нелегальная инструкция - модули ядра
Я пытаюсь следовать этим инструкциям:
Моя цель здесь состоит в том, чтобы разрешить доступ пользователей к PMU. Вот некоторые из инструкций модуля, который я использую.
static void enable_cpu_counters(void* data)
{
asm volatile("msr pmuserenr_el0, %0" :: "r"(0xf));
armv8pmu_pmcr_write (ARMV8_PMCR_LC|ARMV8_PMCR_E);
asm volatile("msr PMCNTENSET_EL0, %0" :: "r" ((u32)(1<<31)));
armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMCR_E|ARMV8_PMCR_LC);
printk("\nCPU:%d \n", smp_processor_id());
}
static void disable_cpu_counters (void* data)
{
printk(KERN_INFO "\ndisabling user-mode PMU access on CPU #%d \n",
smp_processor_id());
/* Program PMU and disable all counters */
armv8pmu_pmcr_write(armv8pmu_pmcr_read() |~ARMV8_PMCR_E);
asm volatile("msr pmuserenr_el0, %0" ::"r"((u64)0));
}
static int __init init(void)
{
unsigned int reguser=0;
isb();
asm volatile("mrs %0, pmcr_el0" : "=r" (reguser));
printk(KERN_INFO "\nPMCR_EL0 register before : %x\n", reguser);
asm volatile("mrs %0, pmuserenr_el0" : "=r" (reguser));
printk(KERN_INFO "\nPMUSERENR register before : %x\n", reguser);
on_each_cpu (enable_cpu_counters, NULL, 1);
printk(KERN_INFO "\nEnable Access PMU Initialized\n");
/* Enable counters */
asm volatile("mrs %0, pmcr_el0" : "=r" (reguser));
printk(KERN_INFO "\nPMCR_EL0 register after : %x\n", reguser);
asm volatile("mrs %0, pmuserenr_el0" : "=r" (reguser));
printk(KERN_INFO "\nPMUSERENR register after : %x\n", reguser);
return 0;
}
static void __exit fini(void)
{
on_each_cpu(disable_cpu_counters, NULL, 1);
printk(KERN_INFO "\nAccess PMU Disabled\n");
}
module_init (init);
module_exit (fini);
Выход (dmesg):
[79371.445026] \x0aPMCR_EL0 register before : 41023040
[79371.450997] \x0aPMUSERENR register before : 0
[79371.456511] \x0aCPU:3
[79371.456546] \x0aCPU:2
[79371.456614] \x0aCPU:0
[79371.456631] \x0aCPU:4
[79371.456652] \x0aCPU:5
[79371.473784] \x0aCPU:1
[79371.477254] \x0aEnable Access PMU Initialized
[79371.482693] \x0aPMCR_EL0 register after : 41023001
[79371.488560] \x0aPMUSERENR register after : f
Затем следует второй модуль, на этот раз считывающий только значения из регистров.
Модуль (проверка, хранит ли он значения в регистрах):
static int __init init(void)
{
unsigned int reguser=0;
isb();
/* Enable counters */
asm volatile("mrs %0, pmcr_el0" : "=r" (reguser));
printk(KERN_INFO "\nPMCR_EL0 register : %x\n", reguser);
asm volatile("mrs %0, pmuserenr_el0" : "=r" (reguser));
printk(KERN_INFO "\nPMUSERENR register : %x\n", reguser);
return 0;
}
static void __exit fini(void)
{
printk(KERN_INFO "\nDisabling read_arm_pmu.ko\n");
}
module_init (init);
module_exit (fini);
Выход (dmesg):
[79385.429198] \x0aPMCR_EL0 register : 41023040
[79385.434584] \x0aPMUSERENR register : 0
Тестирование функциональности в пользовательском пространстве с помощью простой программы дает:
juno:/data/data/papi/workplace # ./monitoring 2 2 2 3
[1359092.706711] monitoring[11095]: undefined instruction: pc=000000000040065c
[1359092.713652] Code: 00000000 00000000 00000000 d10043ff (d53b9c00)
Illegal instruction
ПРИМЕЧАНИЕ: я знаю, что это, вероятно, потому, что первый бит PMCR_EL0 равен 0, а PMUSERENR не равен f. Не знаю, как сделать так, чтобы модуль не возвращал значения из регистров...
Полезные команды, которые могут помочь при отладке:
juno: / # cat / proc / modules
read_arm_pmu 16384 0 - Live 0x0000000000000000 (PO)
enable_arm_pmu 16384 0 - Live 0x0000000000000000 (PO)
juno: / # cat / proc / devices
Символьные устройства: 1 mem 5 /dev/tty 5 /dev/console 5 /dev/ptmx 10 misc 13 input 14 sound 29 fb 90 mtd 108 ppp 116 alsa 128 ptm 136 pts 180 usb 189 usb_device 204 ttyAMA 226 drm 249 roccat 250 hidraw 251 bsg 252 тройник 253 rtc 254 gpiochip
Блочные устройства: 1 рамдиск 259 blkext 7 петля 8 SD 31 MtdBlock 65 SD 66 SD 67 SD 68 SD 69 SD 70 SD 71 SD 128 SD 129 SD 130 SD 130 SD 131 132 133 SD 134 SD 135 SD 179 ммК 253 устройство-картограф 254 virtblk
cat / proc / sys / kernel / tainted
4097
juno: / # cat / proc / interrupts
CPU0 CPU1 CPU2 CPU3 CPU4 CPU5
2: 1844410 1197983 1353732 16779 3199 5419 GIC v2 92 Уровень arch_mem_timer
3: 0 0 0 0 0 0 GIC v2 29 Уровень arch_timer
4: 894927 272142 288023 18616 39333 15390 GIC v2 30 Level arch_timer
7:00 0 0 0 0 0 GIC v2 198 Таймер уровня
14: 419017 0 0 0 0 0 GIC v2 68 Уровень mhu_link
15:00 0 0 0 0 0 GIC v2 67 Уровень mhu_link
16:00 0 0 0 0 0 GIC v2 120 Уровень 7ff00000.dma
17:00 0 0 0 0 0 GIC v2 121 Уровень 7ff00000.dma
18:00 0 0 0 0 0 GIC v2 122 Уровень 7ff00000.dma
19:00 0 0 0 0 0 GIC v2 123 Уровень 7ff00000.dma
20:00 0 0 0 0 0 GIC v2 124 Уровень 7ff00000.dma
21:00 0 0 0 0 0 GIC v2 140 Уровень 7ff00000.dma
22:00 0 0 0 0 0 GIC v2 141 Уровень 7ff00000.dma
23:00 0 0 0 0 0 GIC v2 142 Уровень 7ff00000.dma
24:00 0 0 0 0 0 GIC v2 143 Уровень 7ff00000.dma
25:00 0 0 0 0 0 GIC v2 125 Уровень hdlcd
26:00 0 0 0 0 0 GIC v2 117 Уровень hdlcd
27: 6336 0 0 0 0 0 GIC v2 115 Уровень uart-pl011
28: 53666 0 0 0 0 0 GIC v2 136 Уровень 7ffa0000.i2c
30: 47345 0 0 0 0 0 GIC v2 149 Уровень ehci_hcd: usb1
33: 14370 0 0 0 0 0 GIC v2 65 Уровень 2d000000.gpu
34:00 0 0 0 0 0 GIC v2 66 Уровень 2d000000.gpu
35: 39222 0 0 0 0 0 GIC v2 64 Уровень 2d000000.gpu
37: 52 0 0 0 0 0 GIC v2 194 Уровень mmci-pl18x (cmd)
40:00 0 0 0 0 0 GIC v2 100 Уровень rtc-pl031
43: 0 0 0 0 0 0 GIC v2 169 Уровень sata_sil24 [0000: 03: 00.0]
45:00 0 0 0 0 0 M SI 0 Edge PCIe PME, aerdrv
52: 1049234 0 0 0 0 0 M SI 4194304 Edge eth0
IPI0: 62569 1835646 1888285 43688 29194 29798 Перераспределение прерываний
IPI1: 315 1276 725 386 307 207 F вызовы прерывания вызова
IPI2: 0 0 0 0 0 0 C прерывания остановки PU
IPI3: 829962 24938 62500 1274 498 1029 Прерывания широковещательной передачи
IPI4: 800250 925302 1266671 10821 9264 7192 I RQ прерывания работы
IPI5: 0 0 0 0 0 0 C прерывания пробуждения PU
Ошибка: 0
Я надеюсь, что вы, ребята, можете мне помочь, мне кажется, что я трачу много времени на то, что может быть простым.
TLDR - модуль ядра должен разрешать доступ в пространство пользователя, и он не работает. Нужна помощь, чтобы узнать почему.
Спасибо Луис
РЕДАКТИРОВАТЬ: форматирование, делая мою проблему яснее.
0 ответов
FWIW, я успешно использовал трюк модуля ядра на старых девбордах, но на новом у меня точно такая же проблема.
В https://devtalk.nvidia.com/default/topic/955554/jetson-tx1/performance-counters-reset-itself/ было предложено отключить CONFIG_CPU_IDLE
может помочь, и это помогает мне. С отключенным, я могу установить биты в pmuserenr_el0
через модуль ядра и включите / используйте счетчик через пользовательское пространство.
Тем не менее, я успешно использовал модуль ядра на Dragonboard 410c, на котором был запущен образ Debian 83, на котором запущено ядро debian-qcom-dragonboard410c-16.04. Это конкретное ядро также имеетCONFIG_CPU_IDLE
включен, так что, видимо, что-то изменилось в какой-то момент, делаяCONFIG_CPU_IDLE
прервать доступ к PMU.
Из любопытства я также проверил настройкуreset_pmuserenr_el0
макрос для установки pmuserenr_el0
в 0xf
вместо того, чтобы очистить его, чтобы посмотреть, смогу ли я сохранить CONFIG_CPU_IDLE
включен. Это позволяет мне получать доступ к регистрам PMU из пользовательского пространства, но любой сравнительный анализ невозможен, поскольку счетчики постоянно останавливаются.
К сожалению, отключение CONFIG_CPU_IDLE
требует пересобрать ядро - недостаточно просто загрузить новый модуль.