Reputation: 18552
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?
Upvotes: 1
Views: 4260
Reputation: 531
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.
Upvotes: 0
Reputation: 11
/* 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.
Upvotes: 1
Reputation: 399703
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.
Upvotes: 4