Amumu
Amumu

Reputation: 18552

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?

Upvotes: 1

Views: 4260

Answers (3)

verdery
verdery

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

coder.liss
coder.liss

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

unwind
unwind

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

Related Questions