Есть ли какой-либо API для определения физического адреса из виртуального адреса в Linux?

В C# интервал просто псевдоним для System.Int32, строка для System.String, дважды для System.Double и т.д.

Лично я предпочитаю интервал, строку, дважды, и т.д. потому что они не требуют using System; оператор:) Глупая причина, я знаю...

27
задан Ciro Santilli 新疆改造中心法轮功六四事件 4 August 2017 в 07:39
поделиться

1 ответ

/proc/<pid>/pagemap Пример пользовательского минимального запуска

virt_to_phys_user.c:

#define _XOPEN_SOURCE 700
#include <fcntl.h> /* open */
#include <stdint.h> /* uint64_t  */
#include <stdio.h> /* printf */
#include <stdlib.h> /* size_t */
#include <unistd.h> /* pread, sysconf */

typedef struct {
    uint64_t pfn : 54;
    unsigned int soft_dirty : 1;
    unsigned int file_page : 1;
    unsigned int swapped : 1;
    unsigned int present : 1;
} PagemapEntry;

/* Parse the pagemap entry for the given virtual address.
 *
 * @param[out] entry      the parsed entry
 * @param[in]  pagemap_fd file descriptor to an open /proc/pid/pagemap file
 * @param[in]  vaddr      virtual address to get entry for
 * @return 0 for success, 1 for failure
 */
int pagemap_get_entry(PagemapEntry *entry, int pagemap_fd, uintptr_t vaddr)
{
    size_t nread;
    ssize_t ret;
    uint64_t data;
    uintptr_t vpn;

    vpn = vaddr / sysconf(_SC_PAGE_SIZE);
    nread = 0;
    while (nread < sizeof(data)) {
        ret = pread(pagemap_fd, &data, sizeof(data) - nread,
                vpn * sizeof(data) + nread);
        nread += ret;
        if (ret <= 0) {
            return 1;
        }
    }
    entry->pfn = data & (((uint64_t)1 << 54) - 1);
    entry->soft_dirty = (data >> 54) & 1;
    entry->file_page = (data >> 61) & 1;
    entry->swapped = (data >> 62) & 1;
    entry->present = (data >> 63) & 1;
    return 0;
}

/* Convert the given virtual address to physical using /proc/PID/pagemap.
 *
 * @param[out] paddr physical address
 * @param[in]  pid   process to convert for
 * @param[in] vaddr virtual address to get entry for
 * @return 0 for success, 1 for failure
 */
int virt_to_phys_user(uintptr_t *paddr, pid_t pid, uintptr_t vaddr)
{
    char pagemap_file[BUFSIZ];
    int pagemap_fd;

    snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)pid);
    pagemap_fd = open(pagemap_file, O_RDONLY);
    if (pagemap_fd < 0) {
        return 1;
    }
    PagemapEntry entry;
    if (pagemap_get_entry(&entry, pagemap_fd, vaddr)) {
        return 1;
    }
    close(pagemap_fd);
    *paddr = (entry.pfn * sysconf(_SC_PAGE_SIZE)) + (vaddr % sysconf(_SC_PAGE_SIZE));
    return 0;
}

int main(int argc, char **argv)
{
    pid_t pid;
    uintptr_t vaddr, paddr = 0;

    if (argc < 3) {
        printf("Usage: %s pid vaddr\n", argv[0]);
        return EXIT_FAILURE;
    }
    pid = strtoull(argv[1], NULL, 0);
    vaddr = strtoull(argv[2], NULL, 0);
    if (virt_to_phys_user(&paddr, pid, vaddr)) {
        fprintf(stderr, "error: virt_to_phys_user\n");
        return EXIT_FAILURE;
    };
    printf("0x%jx\n", (uintmax_t)paddr);
    return EXIT_SUCCESS;
}

GitHub upstream .

Использование:

sudo ./virt_to_phys_user.out <pid> <physical-address>

sudo требуется для чтения /proc/<pid>/pagemap, даже если у вас есть разрешения на доступ к файлам, как описано по адресу: https://unix.stackexchange.com/questions/345915 / how-to-change-access-of-proc-self-pagemap-file / 383838 # 383838

Как уже упоминалось на: https://stackoverflow.com/a/46247716/ 895245 Linux распределяет таблицы страниц лениво, поэтому убедитесь, что вы прочитали и записали байт по этому адресу из тестовой программы перед использованием virt_to_phys_user.

Как проверить это

Тестовая программа:

#define _XOPEN_SOURCE 700
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

enum { I0 = 0x12345678 };

static volatile uint32_t i = I0;

int main(void) {
    printf("vaddr %p\n", (void *)&i);
    printf("pid %ju\n", (uintmax_t)getpid());
    while (i == I0) {
        sleep(1);
    }
    printf("i %jx\n", (uintmax_t)i);
    return EXIT_SUCCESS;
}

Тестовая программа выводит адрес принадлежащей ей переменной и ее PID, например:

vaddr 0x600800
pid 110

и затем вы можете передать преобразование виртуального адреса с помощью:

sudo ./virt_to_phys_user.out 110 0x600800

Наконец, преобразование может быть проверено с помощью /dev/mem для наблюдения / изменения памяти, но вы это невозможно сделать в Ubuntu 17.04 без перекомпиляции ядра, поскольку для этого требуется: CONFIG_STRICT_DEVMEM=n, см. также: Как получить доступ к физическим адресам из пространства пользователя в Linux? Buildroot - это простой способ ] чтобы преодолеть это, однако.

В качестве альтернативы, вы можете использовать Виртуальную машину, например, команду xp монитора QEMU: Как декодировать записи / proc / pid / pagemap в Linux?

Смотрите это, чтобы вывести все страницы : Как декодировать записи / proc / pid / pagemap в Linux?

Подмножество пользовательских областей этого вопроса: Как найти физический адрес переменной из пространства пользователя в Linux ?

Дамп всех страниц процесса с /proc/<pid>/maps

/proc/<pid>/maps перечисляет все диапазоны адресов процесса, поэтому мы можем пройтись по нему, чтобы перевести все страницы: / proc / [pid] / pagemaps и / proc / [pid] / maps | linux

Kerneland virt_to_phys работает только для kmalloc адресов

Из модуля ядра, virt_to_phys, было упомянуто.

Однако важно подчеркнуть, что оно имеет это ограничение.

[1 151] например. это терпит неудачу для переменных модуля. arc/x86/include/asm/io.h документация:

Возвращенный физический адрес - это физическое (CPU) отображение для заданного адреса памяти. Допустимо использовать эту функцию только для адресов, напрямую сопоставленных или распределенных через kmalloc.

Вот модуль ядра, который иллюстрирует это вместе с тестом пользовательского пространства .

Так что это не очень общая возможность. См. Как получить физический адрес из логического адреса в модуле ядра Linux? только для методов модуля ядра.

12
ответ дан 28 November 2019 в 04:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: