В системе на базе ARM, работающей под управлением Linux, у меня есть устройство, в котором память отображается на физический адрес. Из программы пространства пользователя, где все адреса являются виртуальными, как я могу читать контент с этого адреса?
Как получить доступ к физическим адресам из пользовательского пространства в Linux?
Ответ 1
Вы можете сопоставить файл устройства в пользовательской памяти процесса с помощью системного вызова mmap(2)
. Обычно файлы устройств представляют собой сопоставления физической памяти с файловой системой.
В противном случае вам необходимо написать модуль ядра, который создает такой файл, или предоставляет способ сопоставить необходимую память с пользовательским процессом.
Другой способ - переназначение частей /dev/mem в пользовательскую память.
Изменить: Пример mmaping/dev/mem (эта программа должна иметь доступ к /dev/mem, например, иметь права root):
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("Usage: %s <phys_addr> <offset>\n", argv[0]);
return 0;
}
off_t offset = strtoul(argv[1], NULL, 0);
size_t len = strtoul(argv[2], NULL, 0);
// Truncate offset to a multiple of the page size, or mmap will fail.
size_t pagesize = sysconf(_SC_PAGE_SIZE);
off_t page_base = (offset / pagesize) * pagesize;
off_t page_offset = offset - page_base;
int fd = open("/dev/mem", O_SYNC);
unsigned char *mem = mmap(NULL, page_offset + len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, page_base);
if (mem == MAP_FAILED) {
perror("Can't map memory");
return -1;
}
size_t i;
for (i = 0; i < len; ++i)
printf("%02x ", (int)mem[page_offset + i]);
return 0;
}
Ответ 2
busybox devmem
busybox devmem
- это крошечная утилита CLI, которая называется mmaps /dev/mem
.
Вы можете получить его в Ubuntu с помощью: sudo apt-get install busybox
Использование: чтение 4 байтов с физического адреса 0x12345678
:
sudo busybox devmem 0x12345678
Напишите 0x9abcdef0
по этому адресу:
sudo busybox devmem 0x12345678 w 0x9abcdef0
Источник: https://github.com/mirror/busybox/blob/1_27_2/miscutils/devmem.c#L85
mmap MAP_SHARED
При mmapping /dev/mem
вы, вероятно, захотите использовать:
open("/dev/mem", O_RDWR | O_SYNC);
mmap(..., PROT_READ | PROT_WRITE, MAP_SHARED, ...)
MAP_SHARED
заставляет записи немедленно отправляться в физическую память, что облегчает наблюдение и имеет больше смысла для MAP_SHARED
аппаратного регистра.
CONFIG_STRICT_DEVMEM
и nopat
Чтобы использовать /dev/mem
для просмотра и изменения обычной оперативной памяти в ядре v4.9, вы должны:
- отключить
CONFIG_STRICT_DEVMEM
(устанавливается по умолчанию в Ubuntu 17.04) - передать
nopat
командной строки ядраnopat
для x86
Порты ввода-вывода по-прежнему работают без них.
Сброс кеша
Если вы попытаетесь выполнить запись в ОЗУ, а не в регистр, память может быть кэширована ЦП. Как очистить кэш ЦП для области адресного пространства в Linux? и я не вижу очень переносимого/простого способа очистить его или пометить регион как недоступный для кэширования:
- Как записать память пространства ядра (физический адрес) в файл, используя O_DIRECT?
- Как очистить кэш процессора для области адресного пространства в Linux?
- Можно ли выделить в пользовательском пространстве не кешируемый блок памяти в Linux?
Так, может быть, /dev/mem
нельзя надежно использовать для передачи буферов памяти на устройства?
К сожалению, в QEMU этого не наблюдается, поскольку QEMU не моделирует кэши.
Как это проверить
Теперь самое интересное. Вот несколько классных настроек:
- Пользовательская память
- выделение
volatile
переменную процесса на UserLand - получить физический адрес с помощью
/proc/<pid>/maps
+/proc/<pid>/pagemap
- измените значение по физическому адресу с помощью
devmem
и наблюдайте заdevmem
пользовательского процесса
- выделение
- Ядро памяти
- выделить память ядра с помощью
kmalloc
- получить физический адрес с помощью
virt_to_phys
и передать его обратно пользователю - изменить физический адрес с помощью
devmem
- запросить значение из модуля ядра
- выделить память ядра с помощью
- IO mem и устройство виртуальной платформы QEMU
- создать платформенное устройство с известными физическими адресами регистра
- используйте
devmem
для записи в реестр - смотреть, как
printf
выходит из виртуального устройства в ответ
Ответ 3
Я хочу читать и записывать данные на FPGA, выступая в качестве физической памяти. Начальный адрес: 0xFFFDF0000 Конечный адрес: 0xFFFDF007F
так что я должен ввести для "Смещение"? потому что я получаю ошибку шины при указании физического адреса в качестве значения смещения. пожалуйста помоги
Спасибо