Is it possible to make a PTE point a different physical page?
Say I'm currently in Kernel mode in a context of some process A that currently has the address 400k mapped to physical page no. 5.
Can I make that address (400k) to be mapped to a physical page no. 6 ? (For example)
If so, how?
I tried using this API:
set_pte / clear_pte / mk_pte / pfn_to_page
but no luck so far.
EDIT:
Some code:
static pte_t *walk_page_table(struct mm_struct *mm, size_t addr)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *ptep;
spinlock_t *ptl;
struct vm_area_struct* vma = mm->mmap;
pgd = pgd_offset(mm, addr);
if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
return NULL;
pud = pud_offset(pgd, addr);
if (pud_none(*pud) || unlikely(pud_bad(*pud)))
return NULL;
pmd = pmd_offset(pud, addr);
if (pmd_none(*pmd))
return NULL;
ptep = pte_offset_map(pmd, addr);
return ptep;
}
bool change_pte(size_t address, size_t new_page_phys_address)
{
pte_t *p = walk_page_table(current->mm, address);
pte_t new_pte;
if (!p)
return false;
new_pte = pfn_pte(new_page_phys_address >> PAGE_SHIFT,
PAGE_KERNEL_EXEC);
set_pte(p, new_pte);
__flush_tlb_one(address);
return true;
}
Some test code:
struct pt_regs* regs = task_pt_regs(current);
hexDump("someData", regs->ip, some_size);
void * newPage = kmalloc(PAGE_SIZE,GFP_KERNEL);
memset(newPage,0,PAGE_SIZE);
change_pte(regs->ip, virtual_to_physical(newPage));
hexDump("post someData", regs->ip, some_size);
Let me share one suggestion with you. It seems that there is no evidence, that kmalloc will return page aligned address (i don't know for sure, but you can check it). Let me make an illustration:
page1 page2 remapped uspace page
| 000|000 | | 000|
| 000|000 | | 000|
^ ^
| |
newPage reg->ip
Try to use get_free_page instead of kmalloc. If this wouldn't help, i will try to repeat your experiment.
Related
I`m trying to access the GPIOs of a MT7620n via register settings. So far I can access them by using /sys/class/gpio/... but that is not fast enough for me.
In the Programming guide of the MT7620 page 84 you can see that the GPIO base address is at 0x10000600 and the single registers have an offset of 4 Bytes.
MT7620 Programming Guide
Something like:
devmem 0x10000600
from the shell works absolutely fine but I cannot access it from inside of a c Programm.
Here is my code:
#define GPIOCHIP_0_ADDDRESS 0x10000600 // base address
#define GPIO_BLOCK 4
volatile unsigned long *gpiochip_0_Address;
int gpioSetup()
{
int m_mfd;
if ((m_mfd = open("/dev/mem", O_RDWR)) < 0)
{
printf("ERROR open\n");
return -1;
}
gpiochip_0_Address = (unsigned long*)mmap(NULL, GPIO_BLOCK, PROT_READ|PROT_WRITE, MAP_SHARED, m_mfd, GPIOCHIP_0_ADDDRESS);
close(m_mfd);
if(gpiochip_0_Address == MAP_FAILED)
{
printf("mmap() failed at phsical address:%d %s\n", GPIOCHIP_0_ADDDRESS, strerror(errno));
return -2;
}
return 0;
}
The Output I get is:
mmap() failed at phsical address:268436992 Invalid argument
What do I have to take care of? Do I have to make the memory accessable before? I´m running as root.
Thanks
EDIT
Peter Cordes is right, thank you so much.
Here is my final solution, if somebody finds a bug, please tell me ;)
#define GPIOCHIP_0_ADDDRESS 0x10000600 // base address
volatile unsigned long *gpiochip_0_Address;
int gpioSetup()
{
const size_t pagesize = sysconf(_SC_PAGE_SIZE);
unsigned long gpiochip_pageAddress = GPIOCHIP_0_ADDDRESS & ~(pagesize-1); //get the closest page-sized-address
const unsigned long gpiochip_0_offset = GPIOCHIP_0_ADDDRESS - gpiochip_pageAddress; //calculate the offset between the physical address and the page-sized-address
int m_mfd;
if ((m_mfd = open("/dev/mem", O_RDWR)) < 0)
{
printf("ERROR open\n");
return -1;
}
page_virtual_start_Address = (unsigned long*)mmap(NULL, pagesize, PROT_READ|PROT_WRITE, MAP_SHARED, m_mfd, gpiochip_pageAddress);
close(m_mfd);
if(page_virtual_start_Address == MAP_FAILED)
{
printf("ERROR mmap\n");
printf("mmap() failed at phsical address:%d %d\n", GPIOCHIP_0_ADDDRESS, strerror(errno));
return -2;
}
gpiochip_0_Address = page_virtual_start_Address + (gpiochip_0_offset/sizeof(long));
return 0;
}
mmap's file offset argument has to be page-aligned, and that's one of the documented reasons for mmap to fail with EINVAL.
0x10000600 is not a multiple of 4k, or even 1k, so that's almost certainly your problem. I don't think any systems have pages as small as 512B.
mmap a whole page that includes the address you want, and access the MMIO registers at an offset within that page.
Either hard-code it, or maybe do something like GPIOCHIP_0_ADDDRESS & ~(page_size-1) to round down an address to a page-aligned boundary. You should be able to do something that gets the page size as a compile-time constant so it still compiles efficiently.
I study network-device-driver recently. But somehow not understand free_netdev this function.
I have read the following link:
Possible de-reference of private data using net_device
The answer says that when free the network device, the private data will also be free.
After checking this function, I found that it will call the following function:
void netdev_freemem(struct net_device *dev)
{
char *addr = (char *)dev - dev->padded;
kvfree(addr);
}
But I cannot understand why call this function will free all the net_device memory, and also the private data?
Or my understanding is wrong...
Just wondering if someone can guide me to understand the mechanism of free_netdev.
Thanks in advance.
Check out alloc_netdev() function definition in net/core/dev.c
alloc_size = sizeof(struct net_device);
if (sizeof_priv) {
/* ensure 32-byte alignment of private area */
alloc_size = ALIGN(alloc_size, NETDEV_ALIGN);
alloc_size += sizeof_priv;
}
/* ensure 32-byte alignment of whole construct */
alloc_size += NETDEV_ALIGN - 1;
p = kzalloc(alloc_size, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);
if (!p)
p = vzalloc(alloc_size);
if (!p)
return NULL;
dev = PTR_ALIGN(p, NETDEV_ALIGN);
dev->padded = (char *)dev - (char *)p;
It does a Kzalloc of sizeof(struct net_device) + sizeof_priv + padding_bytes.
So net_device private is memory immediately following struct net_device and hence kfree() of netdev frees even net_device_private memory.
Thanks #Nithin
After checking code of alloc_netdev_mqs, I think it will be clear to draw a diagram to answer my own question. So from the diagram, we can see (char *)dev - dev->padded is just want to find the p's location, And free this p variable will just free all the allocated memory.
------------------- [p = kzalloc(alloc_size, GFP_KERNEL)]
------------------- [dev = PTR_ALIGN(p, NETDEV_ALIGN)]
since p may not aligned to NETDEV_ALIGN * n
and the final added NETDEV_ALIGN - 1 is for the space
of dev - p
------------------- [size of net_device struct]
------------------- [do the size alignment of net_device struct]
------------------- [private data]
------------------- [add final NETDEV_ALIGN - 1 for padding]
I go throught vm_area_struct areas of the task and try to get corresponding struct page * (pages), but some pages is not present in RAM: pte_present(*pte) returns 0. I can't understand this behaviour because I have no swap area, so I suppose that all userspace virtual space maps into presented pages in RAM. Could anybody explain me this?
static struct page * get_page(unsigned long addr)
{
pgd_t *pgd;
pte_t *pte;
pud_t *pud;
pmd_t *pmd;
struct page *pg;
struct mm_struct *mm = current->mm;
pgd = pgd_offset(mm, addr);
if (pgd_none_or_clear_bad(pgd)) {
goto err;
}
pud = pud_offset(pgd, addr);
if (pud_none(*pud) || pud_bad(*pud)) {
goto err;
}
pmd = pmd_offset(pud, addr);
if (pmd_none(*pmd) || pmd_bad(*pmd)) {
goto err;
}
pte = pte_offset_map(pmd, addr);
if (!pte) {
goto err;
}
if (!pte_present(*pte)) {
PR("pte is not present\n");
goto err;
}
pg = pte_page(*pte);
if (!pg) {
pte_unmap(pte);
goto err;
}
pte_unmap(pte);
pte_none checks that there is no value in pte, pte_present check flag of presence.
#define pte_none(pte) (!pte_val(pte))
#define pte_present(pte) (pte_isset((pte), L_PTE_PRESENT))
so the condition for swapped out pages would be !pte_present && !pte_none
And in your case you interpret all empty ptes as swapped out...
It may be a minor page fault where the memory region is reserved, but not allocated with data yet.
I want to write a function that set write protection to all page in Virtual memory. I that moment, I do like that:
mm = task->mm;
spin_lock(&mm->page_table_lock);
for (vma = mm->mmap ; vma ; vma = vma->vm_next) { //loop for all vmas
vma->vm_flags, (int) vma->vm_flags & VM_WRITE);
addr = vma->vm_start;
vma->vm_flags &= ~VM_WRITE;
while(addr < vma->vm_end){ //loop for each page
pgd = pgd_offset(mm, addr);
if (pgd_none(*pgd)) { addr += PAGE_SIZE; continue; }
pud = pud_offset(pgd, addr);
if (pud_none(*pud)) { addr += PAGE_SIZE; continue; }
pmd = pmd_offset(pud, addr);
if (pmd_none(*pmd)) { addr += PAGE_SIZE; continue; }
pte = pte_offset_map(pmd, addr);
if (pte_present(*pte)){
*pte = pte_wrprotect(*pte);
}
spin_unlock(&mm->page_table_lock);
...........
I work on Ubuntu 14.04 (Kernel 3.13). My program work in kernel level.
But it just set write protection to a part of memory, it does't set write protection to all page that process is using.
Somebody can help me, please!
I have one question about how to build routines for 1Byte/2Byte access based on SetPhysLong() in WinIo v3.0.
SetPhysLong() is used to access memory in unit of DWORD(32bit) and my objective is to build my own routines:
SetPhysBYTE() to access memory in unit of BYTE(8bit unsigned), and
SetPhysWORD() to access memory in unit of WORD(16bit unsigned)
Someone told me below way can solve this problem:
Use GetPhysLong to retrieve the current DWORD contents
Put the new BYTE / WORD into the correct part of the DWORD
Then use SetPhysLong to write that back
But I think above way failed if below situation occurs:
Assume 4-byte register is mapped to memory 0x12345678 (because it is memory-mapped IO)
Assume 2nd byte of that register is the Status register and the attribute is "Write 1 to clear" and 2nd byte is 0x40 now
Assume we want to write 0xA5 to memory 0x12345678. Follow your way and we got:
use GetPhysLong then returns 0x00004000
put 0xA5 to LSB and we got 0x000040A5
use SetPhysLong to set 0x000040A5 to memory address 0x12345678
This is NOT correct because value 0x40 will be written to 2nd byte and clear it to 0x00 !!!
Thus my objective is to use SetPhysBYTE(0x12345678, 0xA5) to achieve my goal and do not influence other bytes based on below SetPhysLong()....
bool _stdcall SetPhysLong(PBYTE pbPhysAddr, DWORD dwPhysVal)
{
PDWORD pdwLinAddr;
tagPhysStruct PhysStruct;
if (!IsWinIoInitialized)
return false;
if (g_Is64BitOS)
{
PhysStruct.pvPhysAddress = (DWORD64)pbPhysAddr;
}
else
{
// Avoid sign extension issues
PhysStruct.pvPhysAddress = (DWORD64)(DWORD32)pbPhysAddr;
}
PhysStruct.dwPhysMemSizeInBytes = 4;
pdwLinAddr = (PDWORD)MapPhysToLin(PhysStruct);
if (pdwLinAddr == NULL)
return false;
*pdwLinAddr = dwPhysVal;
UnmapPhysicalMemory(PhysStruct);
return true;
}
[EDIT] I have solved this and please reference below code if need be...
bool _stdcall SetPhysBYTE(PBYTE pbPhysAddr, BYTE bPhysVal)
{
PDWORD pdwLinAddr;
tagPhysStruct PhysStruct;
if (!IsWinIoInitialized)
return false;
if (g_Is64BitOS)
{
PhysStruct.pvPhysAddress = (DWORD64)pbPhysAddr;
}
else
{
// Avoid sign extension issues
PhysStruct.pvPhysAddress = (DWORD64)(DWORD32)pbPhysAddr;
}
PhysStruct.dwPhysMemSizeInBytes = 1;
pdwLinAddr = (PDWORD)MapPhysToLin(PhysStruct);
if (pdwLinAddr == NULL)
return false;
*(((PBYTE)((DWORD)pdwLinAddr))) = bPhysVal;
UnmapPhysicalMemory(PhysStruct);
return true;
}
bool _stdcall SetPhysWORD(PBYTE pbPhysAddr, WORD wPhysVal)
{
PDWORD pdwLinAddr;
tagPhysStruct PhysStruct;
if (!IsWinIoInitialized)
return false;
if (g_Is64BitOS)
{
PhysStruct.pvPhysAddress = (DWORD64)pbPhysAddr;
}
else
{
// Avoid sign extension issues
PhysStruct.pvPhysAddress = (DWORD64)(DWORD32)pbPhysAddr;
}
PhysStruct.dwPhysMemSizeInBytes = 2;
pdwLinAddr = (PDWORD)MapPhysToLin(PhysStruct);
if (pdwLinAddr == NULL)
return false;
*(((PWORD)((DWORD)pdwLinAddr))) = wPhysVal;
UnmapPhysicalMemory(PhysStruct);
return true;
}
See the [EDIT] part of this question !