Existe alguma API para determinar o endereço físico do endereço virtual no Linux?

Existe alguma API para determinar o endereço físico do endereço virtual no sistema operacional Linux?

O kernel e o espaço do usuário funcionam com endereços virtuais (também chamados de endereços lineares) que são mapeados para endereços físicos pelo hardware de gerenciamento de memory. Esse mapeamento é definido por tabelas de páginas, configuradas pelo sistema operacional.

Dispositivos DMA usam endereços de barramento. Em um PC i386, os endereços de barramento são os mesmos dos endereços físicos, mas outras arquiteturas podem ter hardware de mapeamento de endereço especial para converter endereços de barramento em endereços físicos.

No Linux, você pode usar essas funções em asm/io.h :

  • virt_to_phys (virt_addr);
  • phys_to_virt (phys_addr);
  • virt_to_bus (virt_addr);
  • bus_to_virt (bus_addr);

Tudo isso é sobre como acessar a memory comum. Há também “memory compartilhada” no barramento PCI ou ISA. Ele pode ser mapeado dentro de um espaço de endereço de 32 bits usando ioremap () e, em seguida, usado através das funções readb (), writeb () (etc.).

A vida é complicada pelo fato de que existem vários caches ao redor, de modo que diferentes maneiras de acessar o mesmo endereço físico não precisam dar o mesmo resultado.

Além disso, o endereço físico real por trás do endereço virtual pode mudar. Ainda mais do que isso – não pode haver endereço associado a um endereço virtual até que você acesse essa memory.

Quanto à API de usuário, não há nenhuma que eu saiba.

Como foi respondido anteriormente, os programas normais não precisam se preocupar com endereços físicos enquanto são executados em um espaço de endereço virtual com todas as suas conveniências. Além disso, nem todos os endereços virtuais têm um endereço físico, eles podem pertencer a arquivos mapeados ou a páginas trocadas. No entanto, às vezes, pode ser interessante ver esse mapeamento, mesmo no território do usuário.

Para este propósito, o kernel do Linux expõe seu mapeamento para userland através de um conjunto de arquivos no /proc . A documentação pode ser encontrada aqui . Pequeno resumo:

  1. /proc/$pid/maps fornece uma lista de mapeamentos de endereços virtuais junto com informações adicionais, como o arquivo correspondente para arquivos mapeados.
  2. /proc/$pid/pagemap fornece mais informações sobre cada página mapeada, incluindo o endereço físico, se existir.

Este site fornece um programa em C que despeja os mapeamentos de todos os processos em execução usando essa interface e uma explicação do que ela faz.

Exemplo mínimo de userland /proc//pagemap

virt_to_phys_user.c :

 #define _XOPEN_SOURCE 700 #include  /* open */ #include  /* uint64_t */ #include  /* printf */ #include  /* size_t */ #include  /* 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; nread = 0; while (nread < sizeof(data)) { ret = pread(pagemap_fd, &data, sizeof(data), (vaddr / sysconf(_SC_PAGE_SIZE)) * 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; } 

Uso:

 sudo ./virt_to_phys_user.out   

sudo é necessário para ler /proc//pagemap mesmo se você tiver permissions de arquivo conforme explicado em: https://unix.stackexchange.com/questions/345915/how-to-change-permission-of-proc-self -pagemap-arquivo / 383838 # 383838

Como mencionado em: https://stackoverflow.com/a/46247716/895245 O Linux aloca tabelas de páginas preguiçosamente, portanto, certifique-se de ler e gravar um byte para esse endereço do programa de teste antes de usar o virt_to_phys_user .

Como testar isso

Programa de teste:

 #define _XOPEN_SOURCE 700 #include  #include  #include  #include  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; } 

O programa de teste gera o endereço de uma variável que possui e seu PID, por exemplo:

 vaddr 0x600800 pid 110 

e então você pode passar o endereço virtual com:

 sudo ./virt_to_phys_user.out 110 0x600800 

Finalmente, a conversão pode ser testada usando /dev/mem para observar / modificar a memory, mas você não pode fazer isso no Ubuntu 17.04 sem recompilar o kernel, pois requer: CONFIG_STRICT_DEVMEM=n , veja também: Como acessar endereços físicos do espaço do usuário no Linux? Buildroot é uma maneira fácil de superar isso, no entanto.

Alternativamente, você pode usar uma máquina virtual como o comando xp do monitor QEMU: Como decodificar as inputs / proc / pid / pagemap no Linux?

Veja isto para descarregar todas as páginas: Como decodificar inputs / proc / pid / pagemap no Linux?

Subconjunto Userland desta questão: Como encontrar o endereço físico de uma variável do espaço do usuário no Linux?

Descarregar todas as páginas do processo com /proc//maps

/proc//maps lista todos os intervalos de endereços do processo, para que possamos percorrê-lo para traduzir todas as páginas: / proc / [pid] / pagemaps e / proc / [pid] / maps | linux

Kerneland virt_to_phys só funciona para endereços kmalloc

De um módulo do kernel, virt_to_phys foi mencionado.

No entanto, é importante destacar que ela tem essa limitação.

Por exemplo, falha para variables ​​do módulo. arc/x86/include/asm/io.h documentação:

O endereço físico retornado é o mapeamento físico (CPU) para o endereço de memory fornecido. Só é válido usar esta function em endereços diretamente mapeados ou alocados via kmalloc.

Aqui está um módulo do kernel que ilustra isso junto com um teste userland .

Portanto, esta não é uma possibilidade muito geral. Veja: Como obter o endereço físico do lógico em um módulo do kernel do Linux? para methods do módulo do kernel exclusivamente.

Eu me pergunto por que não há API de usuário.

Porque o endereço físico da memory do usuário é desconhecido.

O Linux usa a paginação por demanda para a memory terrestre do usuário. Seu object de terreno do usuário não terá memory física até que seja acessado. Quando o sistema está com falta de memory, seu object de terra do usuário pode ser trocado e perder memory física, a menos que a página esteja bloqueada para o processo. Quando você acessa o object novamente, ele é trocado e recebe memory física, mas é provável que haja memory física diferente da anterior. Você pode tirar um instantâneo do mapeamento de página, mas não é garantido que seja o mesmo no próximo momento.

Portanto, procurar o endereço físico de um object de destino do usuário geralmente não tem sentido.

O programa C sugerido acima geralmente funciona, mas pode retornar resultados enganosos em (pelo menos) duas maneiras:

  1. A página não está presente (mas o endereço virtual é mapeado para uma página!). Isso acontece devido ao mapeamento preguiçoso pelo sistema operacional: ele mapeia os endereços somente quando eles são realmente acessados.
  2. O PFN retornado aponta para alguma página física possivelmente temporária que pode ser alterada logo após devido à cópia na gravação. Por exemplo: para arquivos mapeados na memory, o PFN pode apontar para a cópia somente leitura. Para mapeamentos anônimos, o PFN de todas as páginas no mapeamento pode ser uma página específica somente para leitura, cheia de 0s (a partir da qual todas as páginas anônimas são geradas quando gravadas).

A linha inferior é, para garantir um resultado mais confiável: para mapeamentos somente leitura, leia de todas as páginas pelo menos uma vez antes de consultar seu PFN. Para páginas ativadas para gravação, escreva em cada página pelo menos uma vez antes de consultar seu PFN.

Obviamente, teoricamente, mesmo depois de obter um PFN “estável”, os mapeamentos poderiam sempre mudar arbitrariamente em tempo de execução (por exemplo, ao mover páginas para dentro e para fora da troca) e não deveriam ser confiáveis.