0

As in title I need to allocate at least 32MB block of physical memory. I have a hardware that saves it's trace data with 32MB blocks at given physical address. So I thought of allocating enough space with virtual address and translating it's address but I've forgot that pages at physical memory are not stored next to the other.

Right now I have this code.

#define MEGABYTE 1048576
#include <fcntl.h> /* open */
#include <stdint.h> /* uint64_t  */
#include <stdio.h> /* printf */
#include <stdlib.h> /* size_t */
#include <unistd.h> /* pread, sysconf */
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>


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;


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 is_changed(uint8_t *pBuffer, uint64_t size)
{
    printf("size = %ld\n", size);
    for (uint64_t i = 0; i < size; i++)
    {
        if(pBuffer[i] != 'A')
        {
            printf("Found it!!!\n");
            printf("Index = %ld\n",i);
            return 1;
        }
    }
    printf("Nothing found!\n");
    return 0;
}

uint8_t *virtual_mem_addr = NULL;
uint8_t *physical_mem_addr = NULL;
uint64_t mem_size = 0;
pid_t pid = 0;
char* vaddr = NULL;
uintptr_t paddr = 0;
FILE *f = NULL;

int main(int argc, char const *argv[])
{
    uint64_t pFile;
    struct stat statbuf;
    unsigned char *mmapPointer;
    uint32_t file_size = 0;
    pid = getpid();
    if (argc < 2)
    {
        printf("Usage %s <filename>\n", argv[0]);
        return 1;
    }
    pFile = open(argv[1], O_RDWR | O_SYNC);
    if(pFile <= 0)
    {
        printf("Error opening %s.\n", argv[1]);
        return 1;
    }
    if(fstat(pFile,&statbuf) < 0)
    {
        printf("fstat error\n");
        return 1;
    }
    mmapPointer = mmap(NULL,statbuf.st_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, pFile,0);
    printf("mmapPointer = %p\n",mmapPointer);
    printf("pid %ju\n", (uintmax_t)pid);

    printf("%c\n",mmapPointer[0]);
    printf("mmapPointer After loop = %p\n",mmapPointer);

    if (virt_to_phys_user(&paddr, pid,(uintptr_t)mmapPointer))
    {
        fprintf(stderr, "error: virt_to_phys_user\n");
        return EXIT_FAILURE;
    };
    printf("paddr = 0x%jx\n", (uintmax_t)paddr);

    //WAITING FOR CHANGE
    printf("Press key to continue.\n");
    getchar();
    // //CHECK IF SOMETHING HAS CHANGED

    is_changed(mmapPointer, 32*MEGABYTE);

    if(munmap(mmapPointer,statbuf.st_size) < 0)
    {
        perror("Error un-mapping the file\n");
        close(pFile);
        return 1;
    }
    close(pFile);
    return EXIT_SUCCESS;
}

It allocates virtual memory for buffer file(64MB file full of 'A' bytes) with mmap() and with virt_to_phys() it translates it's virtual address to physical address. Next when there's "WAITING FOR CHANGE" comment I'm trying to save some bytes to this address with another program. I've tested it with busybox devmem right now, and when reading first 16KB of memory I've read what's inside my buffer file, but after that it reads some gibberish data, so that confirms, that physical memory allocated next page somewhere else in the memory.

So my question is how can I allocate 32MB block of memory that is next to the other in physical memory space. Better question is: Is it even possible?

Kankarollo
  • 15
  • 5
  • I don't think I understand what your code is trying to do. But if you want to retrieve data from a particular physical address, AFAIK the usual approach on Linux would be to just read or `mmap` the device `/dev/mem`. If you read, then the "second buffer" is the one you read into and you don't care where it's located in either virtual or physical memory. If you mmap, you can process the data in place or copy it wherever you like; again, physical proximity is irrelevant. – Nate Eldredge May 11 '20 at 15:32
  • At a glance, your post looks like a serious XY problem. – Nate Eldredge May 11 '20 at 15:34
  • @MarcoBonelli Yes it is. Thanks! – Kankarollo May 11 '20 at 19:01

0 Answers0