Mmap is not working for high address memory mapping? - linux

I am trying to do
memory = (char *)mmap((void *)0X0000100000000000,(size_t)0xffffffff/8,PROT_READ | PROT_WRITE , MAP_SHARED|MAP_ANONYMOUS,4,0);
but its not mapping anything and returning 0. I need to map memory at high address in 64-bit machine.

This is not meant as a complete answer - more of a possible explanation:
0X0000100000000000 is 281474976710656. Do you have that high a virtual memory address available? Or stated another way: is that address valid in your OS? I would guess the answer is no.
Is mmap actually returning MAP_FAILED ( (void *) -1 )? Usually when you give mmap an address it does not like, you get MAP_FAILED and errno == EINVAL. Did you check errno?
Note: 4 bytes is not the word length in a 64 bit OS, usually it is 8. A 4 byte word cannot address all of memory, for example.

Related

How to increase the size of memory region allocated with mmap()

I'm allocating memory using mmap Linux syscall.
mov x5, 0 ; offset is zero
mov x4, -1 ; no file descriptor
mov x3, 0x22 ; MAP_PRIVATE + MAP_ANONYMOUS
mov x2, 3 ; PROT_READ + PROT_WRITE
mov x1, 4096 ; initial region size in bytes
mov x0, 0 ; let Linux choose region address
mov x8, 222 ; mmap
svc 0
Is it possible to increase the size of allocated memory region preserving its start address and contents? How to do it properly?
On Linux, use the mremap(2) Linux-specific system call without MREMAP_MAYMOVE to extend the existing mapping, without considering the option of remapping those physical pages to a different virtual address where there's enough room for the larger mapping.
It will return an error if some other mapping already exists for the pages you want to grow into. (Unlike mmap(MAP_FIXED) which will silently replace those mappings.)
If you're writing in asm, portability to non-Linux is barely relevant; other OSes will have different call numbers and maybe ABIs, so just look up __NR_mremap in asm/unistd.h, and get the flags patterns from sys/mman.h.
With just portable POSIX calls, mmap() with a non-NULL hint address = right after you existing mapping, but without MAP_FIXED; it will pick that address if the pages are free (and as #datenwolf says, merge with the earlier mapping into one long extent). Otherwise it will pick somewhere else. (Then you have to munmap that mapping that ended up not where you wanted it.)
There is a Linux-specific mmap option: MAP_FIXED_NOREPLACE will return an error instead of mapping at an address different from the hint. Kernels older than 4.17 don't know about that flag and will typically treat it as if you used no other flags besides MAP_ANONYMOUS, so you should check the return value against the hint.
Do not use MAP_FIXED_NOREPLACE | MAP_FIXED; that would act as MAP_FIXED on old kernels, and maybe also on new kernels that do know about MAP_FIXED_NOREPLACE.
Assuming you know the start of the mapping you want to extend, and the desired new total size, mremap is a better choice than mmap(MAP_FIXED_NOREPLACE). It's been supported since at least Linux 2.4, i.e. decades, and keeps the existing mapping flags and permissions automatically (e.g. MAP_PRIVATE, PROT_READ|PROT_WRITE)
If you only knew the end address of the existing mapping, mmap(MAP_FIXED_NOREPLACE) might be a good choice.
If there's free virtual address space behind your original region, just create an additional mmap-ed region right behind the original one, using the MAP_FIXED | MAP_FIXED_NOREPLACE flags and identical permissions. If the page size of both regions are identical, they'll be coalesced into single mapping.

mmap CMA area on /dev/mem

I need reserve 256-512 Mb of continuous physical memory and have access to this memory from the user space.
I decided to use CMA for memory reserving.
Here are the steps on my idea that must be performed:
Reservation required amount of memory by CMA during system booting.
Parsing of CMA patch output which looks like for example: "CMA: reserved 256 MiB at 27400000" and saving two parameters: size of CMA area = 256*1024*1024 bytes and phys address of CMA area = 0x27400000.
Mapping of CMA area at /dev/mem file with offset = 0x27400000 using mmap(). (Of course, CONFIG_STRICT_DEVMEM is disabled)
It would let me to read data directly from phys memory from user space.
But the next code make segmentation fault(there size = 1Mb):
int file;
void* start;
file=open("/dev/mem", O_RDWR | O_SYNC);
if ( (start = mmap(0, 1024*1024, PROT_READ | PROT_WRITE, MAP_SHARED, file, 0x27400000)) == MAP_FAILED ){
perror("mmap");
}
for (int offs = 0; offs<50; offs++){
cout<<((char *)start)[offs];
}
Output of this code: "mmap: Invalid argument".
When I changed offset = 0x27400000 on 0, this code worked fine and program displayed trash. It also work for alot of offsets which I looked at /proc/iomem.
According to information from /proc/iomem, phys addr of CMA area (0x27400000 on my system) always situated in System RAM.
Does anyone have any ideas, how to mmap CMA area on /dev/mem? What am I doing wrong?
Thanks alot for any help!
Solution to this problem was suggested to me by Jeff Haran in kernelnewbies mailing list.
It was necessary to disable CONFIG_x86_PAT in .config and mmap() has started to work!
If CONFIG_X86_PAT is configured you will have problems mapping memory to user space. It basically implements the same restrictions as CONFIG_STRICT_DEVMEM.
Jeff Haran
Now I can mmap /dev/mem on any physical address I want.
But necessary to be careful:
Word of caution. CONFIG_X86_PAT was likely introduced for a reason. There may be some performance penalties for turning this off, though in my testing so far turning if off doesn’t seem to break anything.
Jeff Haran

How does this way of writing data to a specific physical memory address work?

I want to write data to an arbitrary physical memory address to test the error detection and correction feature of my system. One code segment in an existing kernel module is written like this:
u32 addr;
struct page *page;
void *mem;
pci_read_config_dword(priv->mc, I5100_MEMEINJADDRMAT, &addr);
/* Inject error by writing to address */
page = pfn_to_page(addr >> PAGE_SHIFT);
mem = kmap(page) + (addr & (~PAGE_MASK));
*((volatile u32*) (mem)) = 0x01010101;
kunmap(page);
I5100_MEMEINJADDRMAT is the register address of a register in i5100 memory controller. Basically, the memory address is retrieved in that register. I don't understand the remaining code, starting from retrieving a page then perform bitwise operations.
As far as I understand, pfn_to_page is used to get a page that includes a particular physical address by passing in a page frame number as argument. The addr >> PAGE_SHIFT part is to translate from a given address to its corresponding page frame number. But, I don't understand how to use PAGE_SHIFT correctly? What should be the correct data type to use with PAGE_SHIFT?
kmap() returns the appropriate virtual page address then add the offset to get the correct pointer to a virtual memory address. What does (addr & (~PAGE_MASK)) actually do?
My task is to write error injection to a physical address? But the above code seems to write to a virtual address. Is there any other way?
This:
(addr & (~PAGE_MASK))
will clear the bits in addr that are set in PAGE_MASK. Assuming a page size of 4 KB, the PAGE_MASK will likely have its 12 least significant bits set, since 212 = 4096.
So, PAGE_MASK is 0x00000fff. Then, the bit-wise inverse ~PAGE_MASK is simply 0xfffff000, so when addr is bitwise-and:ed with this, the lowest 12 bits of addr are cleared.
/* PAGE_SHIFT determines the page size */
#define PAGE_SHIFT 12
#define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT)
#define PAGE_MASK (~(PAGE_SIZE-1))
I found these definition in linux-source-3.2.0.
but I get PAGE_MASK is 0xfffff000
so I think this operator is clearing the highest 20bit, or try to get the value of lower 12 bit.
page = pfn_to_page(addr >> PAGE_SHIFT);
It gets the page address using page frame number (page frame number (pfn) is obtained shifting to right the address). Because least significant bits represent offset in the page (12 bits for x86). Page frame number is equal to 20 most significant bits of the physical address for x86.
You can apply shifting on pointer or integer data types. It depends on the situation.
(addr & (~PAGE_MASK)) gets inner offset of the frame. To access correct byte you should add offset to related page.
Virtual address represents physical address. As far as I know, there is no way other than using virtual address. Also, I should say this is kernel virtual space, it is different from userspace virtual addresses.Do not mix userspace with kernel space.

Manual Virtual Address Translation

I've looked at a few different articles related to this already but none of them explain the solution in a way that I can understand and replicate. I need to know how to translate a physical address to a virtual address in memory based on the following:
A simple virtual memory system has 32KB physical memory with 16-bit virtual address, of which 12 bits are used as offset. The following is the current content of the page table of one of the processes:
So basically I think the page size of this virtual memory system is 1024KB. I need a process to find the corresponding PA of VA B2A0. If you can give me the process I can go from there, you don't have to give me the final solution :)
Thanks in advance guys. Also, if you know of an article that does this already and I've just missed it, feel free to just link me to that.
Cheers.
32 KB is 2^15.
so there are 15 bits for every physical address, lower 12 of them are used as offset, higher 3 as a number of pageframe.
What virtual page does 0xb2a0 resides in? To determine this, we need to take bits of the address, higher than 2^12. The size of a page is 2^12, that is 4096 or 0x1000, so it is a virtual page number 0xb = 11 (floor of 0xb2a0 / 0x1000). Offset inside the page is 0xb2a0 modulo 0x1000, it's 0x2a0.
Then use the table to translate the virtual page number 11 to a physical pageframe. The virtual page is present (1), and it corresponds to the physical frame number with higher bits 111, that is 111 + twelve 0 in binary, => 0x7000 - it is the address of the start of the physical frame.
Our physical address resides at offset 0x2a0, so, the sought physical address is 0x7000 + 0x2a0 = 0x72a0.
Please, follow this flow and make it clear for you. If you have questions, read the Wikipedia first and if something is still not clear, ask :)
I was trying to do my examination review and study, and I couldn't find a solid answer to this same question. I consolidated what I have learnt, and I hope that whatever I summed up here will help those like me. :)
I find the explanation in the answer above a little hard to understand for my little brain.
I think this link below gives a better overview than Wikipedia's explanation:
http://williams.comp.ncat.edu/addrtrans.htm
This youtube video also offers an excellent guide in explaining the process of virtual address translation:
https://www.youtube.com/watch?v=6neHHkI0Z0o
Back to the question ->>>
The first question is - what is the 'page size' of this virtual memory system?
based on the definition here - https://en.wikipedia.org/wiki/Page_(computer_memory)
I was initially confused between 'pages' and 'page size' but I kinda figured it out now. Pages determines the number of pages available (like in a book), and page size is like (A4,A5,A6 pages in the book!).
As such, since the virtual memory and physical memory offset is the same and is mapped accordingly, we can determine the page size via the offset size. If the offset size is given as 12-bit, then 2^12 = 4,096 Byte a.k.a 4-KB.
Side question for curious minds, how many virtual memory pages are there?
- 16-bit of virtual address space minus 12-bit of offset = 4-bit
- which equals to 2^4 = 16-pages available (thus the table we see!)
Another side question for other curious minds, how many PHYSICAL memory pages are there?
- 32KB of physical Memory = 32 x 1024bytes = 32,768 bytes
- Log(32768) / Log(2) = 15-bits which also means 2^15 for total physical MEMORY
- minus the offset of 12-bit that we already know...
- 15-bit (total physical memory) minus 12-bit (offset) = 3-bit for physical address space
Going to the next question, what is the corresponding physical address of virtual address 0xb2a0 (that is currently set in hex notation)?
#Dmytro Sirenko answer above explains it quite well, I will help to rephrase it here.
We need to remember that our virtual address is - 16-bit, and that address space now contain is value = b2a0 (ignoring the 0x).
My short-cut (please correct me if am wrong), is that since the ratio of the address : offset (page size) is 4:12 = 1:3...
b | 2 a 0
^
page number | offset
When converting hex value b to decimal = 11.
I look into the table, and I found Page Frame = 111 in the table entry number 11.
111 is noted in binary and it correlates to the physical memory frame.
Remember, we were looking at 15-bit of Physical Memory Address space, as such, we can determine that:
1 1 1 | 0 0 0 0 0 0 0 0 0 0 0 0
Address | offset
As Offset are mapped directly from virtual memory to physical memory, we bring the value of (2a0) right into the physical memory. Unfortunately, we can't represent it right away in here because it's in hexadecimal format while my above address space is set in binary.
Considering that I am going to be tested in an examination and I won't be allowed to bring in a calculator... I will do a reverse and answer in Hexadecimal instead. :)
When we convert 111 into decimal (I go by 001 = 1, 010 = 2, 100 = 4, 101 = 5, 110 = 6, 111 = 7).
Now I need to convert from decimal to Hex! = 7 (dec) = 7
As such, the corresponding Physical Memory location of this virtual memory address is.... (loud drums and curtain open....)
7 2 a 0
which is notated in this manner 0x72a0.

vmalloc_to_pfn returns 32 bit address on Linux 32 system. Why does it chop off higher bits of PAE physical address?

I'm using vmalloc_to_pfn() to get the physical address on a 32-bit PAE Linux system. It looks like vmalloc_to_pfn() returns "unsigned long" which means it is 32 bit on a 32 bit system, 64 bit on a 64-bit system. On 64-bit Linux, unsigned long is 64 bit and I've no issues.
Problem:
Using this function to convert virtual to physical:
VA: 0xf8ab87fc
PA using vmalloc_to_pfn: 0x36f7f7fc. But I'm actually expecting: 0x136f7f7fc.
The physical address falls between 4 to 5 GB. But I can't get the exact physical address, I only get the chopped off 32-bit address. Is there another way to get true physical address?
I am myself studying this, and am on 32 bit - so this is not exactly an answer. But digging through the same stuff, I can see the source for vmalloc_to_pfn says:
/*
* Map a vmalloc()-space virtual address to the physical page frame number.
*/
unsigned long vmalloc_to_pfn(const void *vmalloc_addr)
{
return page_to_pfn(vmalloc_to_page(vmalloc_addr));
}
EXPORT_SYMBOL(vmalloc_to_pfn);
So, it should not actually return an address - it should return a "page frame number" (PFN). In relation to that:
http://www.tldp.org/LDP/tlk/mm/memory.html
Using the above example again, process Y's virtual page frame number 1 is mapped to physical page frame number 4 which starts at 0x8000 (4 x 0x2000). Adding in the 0x194 byte offset gives us a final physical address of 0x8194.
So apparently, one should multiply the PFN by PAGE_SIZE to get an actual address - which then makes it strange, how come you got "returns 32 bit address on Linux 32 system" to work at all (but then again, I'm no expert - maybe PFN is equivalent to an address for 32 bit?). Probably a minimal working example of a module in the question OP, and output on both platforms for comparison, would have been in order.
In any case, I just have noticed what you have - that Physical Address Extension (PAE) may make a difference in paging; apparently, the value stored as a PFN in Page Global Directory (PGD) is architecture-specific, and is defined differently depending on it:
typedef unsigned long pgdval_t; // arch/x86/include/asm/pgtable-2level_types.h
typedef u64 pgdval_t; // arch/x86/include/asm/pgtable-3level_types.h
typedef unsigned long pgdval_t; // arch/x86/include/asm/pgtable_64_types.h
To summarize - just using vmalloc_to_pfn() is probably not the whole story in getting the physical address.

Resources