Reputation: 17
I am trying to get physical address from /proc/[pid]/pagemap using virtual address and I thought it was working fine until I tried with a simple test program. This is my code which gets physical address using virtual address:
#include "addresstranslation.h"
#include <stdio.h>
#define PAGEMAP_ENTRY 8
#define GET_BIT(X,Y) (X & ((uint64_t)1<<Y)) >> Y
#define GET_PFN(X) X & 0x7FFFFFFFFFFFFF
#define page_mapping_file "/proc/self/pagemap"
const int __endian_bit = 1;
#define is_bigendian() ( (*(char*)&__endian_bit) == 0 )
uintptr_t virtual_to_physical_address(uintptr_t virt_addr)
{
uintptr_t file_offset = 0;
uintptr_t read_val = 0;
uintptr_t page_number = 0;
int i = 0;
int c = 0;
int pid = 0;
int status = 0;
unsigned char c_buf[PAGEMAP_ENTRY];
FILE *f = fopen(page_mapping_file, "rb");
if(!f)
{
// if this happens run as root
printf("Error! Cannot open %s. Please, run as root.\n", page_mapping_file);
return 0;
}
file_offset = virt_addr / getpagesize() * PAGEMAP_ENTRY;
status = fseek(f, file_offset, SEEK_SET);
if(status)
{
printf("Error! Cannot seek in %s.\n", page_mapping_file);
perror("Failed to do fseek!");
fclose(f);
return 0;
}
for(i = 0; i < PAGEMAP_ENTRY; i++)
{
c = getc(f);
if(c == EOF)
{
fclose(f);
return 0;
}
if(is_bigendian())
{
c_buf[i] = c;
}
else
{
c_buf[PAGEMAP_ENTRY - i - 1] = c;
}
}
for(i=0; i < PAGEMAP_ENTRY; i++)
{
read_val = (read_val << 8) + c_buf[i];
}
/*
if(GET_BIT(read_val, 63))
{
page_number = GET_PFN(read_val);
printf("%d \n", page_number);
}
else
{
printf("Page not present\n");
}
if(GET_BIT(read_val, 62))
{
printf("Page swapped\n");
}
*/
fclose(f);
return read_val;
}
addresstranslation.h:
/*
* addresstranslation.h
*
* Translates virtual to physical address.
*/
#ifndef __ADDRESS_TRANSLATION_H
#define __ADDRESS_TRANSLATION_H
#include <inttypes.h>
#include <stdint.h>
uintptr_t virtual_to_physical_address(uintptr_t virt_addr);
#endif
And this is the simple test that I tried.
#include <stdlib.h>
#include <stdio.h>
#include "addresstranslation.h"
int main(int argc, char* argv[])
{
int *a1, *a2, *b1,*c1, *b2 = NULL;
int N = 4096;
uintptr_t ap1, ap2, bp1, bp2 = 0;
printf("Test virtual to physical address translation.\n");
a1 = (int*)malloc(sizeof(int) * N);
if (!a1)
{
printf("Error: cannot allocate memory for a\n");
return 1;
}
b1 = (int*)malloc(sizeof(int) * N);
if (!b1)
{
printf("Error: cannot allocate memory for b\n");
return 1;
}
ap1 = virtual_to_physical_address((uintptr_t)a1);
bp1 = virtual_to_physical_address((uintptr_t)b1);
printf("a1_virt= %p: a1_phys= %" PRIxPTR "\n", a1, ap1);
printf("b1_virt= %p b1_phys= %" PRIxPTR "\n", b1, bp1);
a2 = a1 + 1000;
b2 = b1 + 1;
ap2 = virtual_to_physical_address((uintptr_t)a2);
bp2 = virtual_to_physical_address((uintptr_t)b2);
printf("a2_virt= %p a2_phys= %" PRIxPTR "\n", a2, ap2);
printf("b2_virt= %p b2_phys= %" PRIxPTR "\n", b2, bp2);
printf("Done\n");
}
Which prints something like this:
Test virtual to physical address translation.
a1_virt= 0x958d008: a1_phys= 4f8ce
b1_virt= 0x9591010 b1_phys= 4d40b
a2_virt= 0x958dfa8 a2_phys= 4f8ce
b2_virt= 0x9591014 b2_phys= 4d40b
Done
As you can see a1 and a2 have different virtual addresses but have same physical address, my question is; Is my virtual-physical address conversion faulty or is it about Linux memory management and it is possible one physical address can be mapped to two different virtual addresses ?
Upvotes: 0
Views: 2205
Reputation: 16540
the C language is case sensitive.
so this macro:
#DEFINE PAGE_MAPPING_FILE "/PROC/self/pagemap"
is not invoked when using:
FILE *f = fopen(page_mapping_file, "rb");
and #DEFINE
means nothing to the C compiler.
The correct spelling is: #define
I.E. all lower case
Upvotes: 1