I'm trying to get the physical address of a kernel symbol (because I need it to be read by a system that only accepts phys addr). So I'm using things like __pa(vaddr) and virt_to_phys(vaddr).
Problem is that the conversion is not correct everytime. Sometimes it is, sometimes it's not. How do I know that? Because I'm reading the value on the kernel module as well and comparing to value read using fmem and using my other system.
Ex:
my kernel symbol's address is 0xffffffffb3400000, so when I do
printk(KERN_INFO "Addr: 0x%lx , Value: 0x%lx\n", _stext, *_stext) I get => Addr: 0xffffffffb3400000 , Value: 0x4801e03f51258d48.
When it reads right I use fmem and the other system to read the physical address given by __pa and the value matches 0x4801e03f51258d48 in both cases. But sometimes it doesn't work, so the value is different. And how do I know the value itself hasn't changed? Because dereferencing the virtual address still gives me the result 0x4801e03f51258d48.
I know kernel code and data changes on every boot (including kernel symbols addresses), that's not a problem since I'm getting the kernel symbol's addr everytime. But I expected the virtual to phys mapping to be the same every time, shouldn't it be? I mean, what can I do to get the physical address of my kernel symbol correctly?
So right now I have
u64 _stext_pa = __pa(_stext_va)
printk(KERN_INFO "Value of _stext (va: 0x%lx | pa: 0x%x) = 0x%lx\n", _stext_va, _stext_pa, *_stext_va)
Addresses (virtual and physical) change every boot but the value is always the same. That's what I need.
I need a physical address paddr so that I can perform int value = *paddr; on my other system and get the same result I'm getting with the virtual address
Edit: this "other system" I'm refering to is an SMM driver, but that's not relevant, just in case you might be wondering "what is 'other system'"
Related
I am trying to read/write a memory in EL2, but it doesn't return what I want.
I use kzalloc to get initialized space, then use str to write a number (0x12345678) in this space.
Next, I use __pa() to get the physical address(PA) of this space. I found PA=VA-0x80000000. I will send this PA to EL2 for reading, so I put it into one register(r1)
Third step is call hvc, after calling hvc it is in EL2. I have created a branch in hyp_stub_vectors (in arch/arm/kernel/hyp-stub.S, I am sure this file will handler hvc ), and used ldr to read this memory space to get my number.
But it failed.
I guess possible reasons are
I got a wrong physical address with __pa(). But I have walked the aarch32 stage-1 translation and got the same address, actually this space is a block, so it's OK to delete an offset to get the physical address.
in EL2 it still has address translation. But I checked some related system register and found the MMU in EL2 is disable. Possibly I checked a wrong register?
My device is Raspberry Pi 3B+, Cortex-A53
The problem may be related to cache incoherence. Given that your EL2 is running with MMU disabled, it also has data cache disabled, as stated in this paper. This means that to access a memory location in EL2 you need to get the value into RAM.
To achieve this, you can use the dc civac, x0 instruction, with x0 being a virtual address of the variable. This will flush the cache line with your variable and write the value into RAM.
P.S. To verify whether your PA is correct, read the value at __va(__pa(addr)) and make sure that it's the same.
I have a "Hello World" program to which I've attached lldb. I'm trying to answer a few questions for myself about the results I get when I try to get the address of library functions:
(lldb) image lookup -n printf
1 match found in /usr/lib/system/libsystem_c.dylib:
Address: libsystem_c.dylib[0x000000000003f550] (libsystem_c.dylib.__TEXT.__text + 253892)
Summary: libsystem_c.dylib`printf
(lldb) image lookup -n scanf
1 match found in /usr/lib/system/libsystem_c.dylib:
Address: libsystem_c.dylib[0x000000000003fc69] (libsystem_c.dylib.__TEXT.__text + 255709)
Summary: libsystem_c.dylib`scanf
(lldb) expr &printf
(int (*)(const char *__restrict, ...)) $2 = 0x00007fff6f8c5550 (libsystem_c.dylib`printf)
(lldb) expr &scanf
error: unsupported expression with unknown type
I have three questions here:
What kind of address is 0x00007fff6f8c5550? I assume it is the function pointer to printf. Is this a virtual address that exists only in the mapped space of the current process? If yes, why does another program return the same address for printf?
Assuming it's some global shared address that is the same for every process, would modifying the contents of the data at this address (which I haven't been able to do yet) create a copy of the modified memory page and will the address change? (i'm on Mac OS and I assume one process cannot change shared memory for another process)
Why does expr &scanf not work, but expr &printf does?
What kind of address is 0x00007fff6f8c5550? I assume it is the function pointer to printf.
Yes, that's correct.
Is this a virtual address that exists only
in the mapped space of the current process?
Well, yes and no. It is a virtual address specific to your process and you should not assume it's valid in another process. But:
If yes, why does another
program return the same address for printf?
As an optimization, macOS uses a shared mapping for a lot of the system libraries. They are loaded once at boot and used by all processes. For a given boot, the address is constant across all such processes. However, the address is randomized each boot for security.
Assuming it's some global shared address that is the same for every process, would modifying the contents of the data at this address
(which I haven't been able to do yet) create a copy of the modified
memory page and will the address change?
Well, it is mapped copy-on-write. So, modifying it would create a copy. However, that wouldn't change its address. The OS would simply modify the mapping so that the memory around that address is private to your process.
(i'm on Mac OS and I assume
one process cannot change shared memory for another process)
Well, processes can cooperate to have writable shared memory. But, in general, you're correct that security precautions prevent unwanted modifications to a process's memory.
Why does expr &scanf not work, but expr &printf does?
Your program (presumably) doesn't use scanf, so there's no debugging information regarding it. The main thing lldb is missing is the type of scanf. If you use a cast expression, it can work:
(lldb) p scanf
error: 'scanf' has unknown type; cast it to its declared type to use it
(lldb) p &scanf
error: unsupported expression with unknown type
(lldb) p (int(*)(const char * __restrict, ...))scanf
(int (*)(const char *__restrict, ...)) $3 = 0x00007fffd7e958d4 (libsystem_c.dylib`scanf)
Conversely, it works for printf because your program does use it.
I would like to get the PFN associated with a memory block allocated with dma_alloc_coherent for use with a PCIe device as shown below:
unsigned long pfn;
buffer = dma_alloc_coherent(&pcie->dev, size, &bus_addr, GFP_KERNEL);
// Get PFN?
virt_to_phys(buffer) >> PAGE_SHIFT;
I'm aware that this is probably not the correct method, but it seems to work... I'm just looking for the right solution to translate the potential bus address (since I do not know if there is an IOMMU) to a PFN. Thanks in advance.
Note: There seems to be an ARM function in the kernel called dma_to_pfn, which seems to be exactly what I need, but for x86.
What you're doing is indeed wrong. From the man page for virt_to_phys():
This function does not give bus mappings for DMA transfers. In almost all conceivable cases a device driver should not be using this function.
The equivalent function for DMA addresses is dma_to_phys(), defined in include/linux/dma-direct.h as follows:
phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr);
Therefore you can do:
dma_to_phys(&pcie->dev, bus_addr) >> PAGE_SHIFT;
Notice that I am using the bus_addr returned by dma_alloc_coherent(), not buffer, since you obviously need to pass a DMA address (dma_addr_t) to this function, not a virtual address.
There also seems to be a macro PHYS_PFN() defined in include/linux/pfn.h to get the PFN for a given physical address, if you prefer to use that:
PHYS_PFN(dma_to_phys(&pcie->dev, bus_addr));
I was wondering if there is an existing system call/API for accessing getting the physical address of the virtual address?
If there is none then some direction on how to get that working ?
Also, how to get the physical address of MMIO which is non-pageable physical memory ?
The answer lies in IOMemoryDescriptor and IODMACommand objects.
If the memory in question is kernel-allocated, it should be allocated by creating an IOBufferMemoryDescriptor in the first place. If that's not possible, or if it's a buffer allocated in user space, you can wrap the relevant pointer using IOMemoryDescriptor::withAddressRange(address, length, options, task) or one of the other factory functions. In the case of withAddressRange, the address passed in must be virtual, in the address space of task.
You can directly grab physical address ranges from an IOMemoryDescriptor by calling the getPhysicalSegment() function (only valid between prepare()…complete() calls). However, normally you would do this for creating scatter-gather lists (DMA), and for this purpose Apple strongly recommends the IODMACommand. You can create these using IODMACommand::withSpecification(). Then use the genIOVMSegments() function to generate the scatter-gather list.
Modern Macs, and also some old PPC G5s contain an IOMMU (Intel calls this VT-d), so the system memory addresses you pass to PCI/Thunderbolt devices are not in fact physical, but IO-Mapped. IODMACommand will do this for you, as long as you use the "system mapper" (the default) and set mappingOptions to kMapped. If you're preparing addresses for the CPU, not a device, you will want to turn off mapping - use kIOMemoryMapperNone in your IOMemoryDescriptor options. Depending on what exactly you're trying to do, you probably don't need IODMACommand in this case either.
Note: it's often wise to pool and reuse your IODMACommand objects, rather than freeing and reallocating them.
Regarding MMIO, I assume you mean PCI BARs and similar - for IOPCIDevice, you can grab an IOMemoryDescriptor representing the memory-mapped device range using getDeviceMemoryWithRegister() and similar functions.
Example:
If all you want are pure CPU-space physical addresses for a given virtual memory range in some task, you can do something like this (untested as a complete kext that uses it would be rather large):
// INPUTS:
mach_vm_address_t virtual_range_start = …; // start address of virtual memory
mach_vm_size_t virtual_range_size_bytes = …; // number of bytes in range
task_t task = …; // Task object of process in which the virtual memory address is mapped
IOOptionBits direction = kIODirectionInOut; // whether the memory will be written or read, or both during the operation
IOOptionBits options =
kIOMemoryMapperNone // we want raw physical addresses, not IO-mapped
| direction;
// Process for getting physical addresses:
IOMemoryDescriptor* md = IOMemoryDescriptor::withAddressRange(
virtual_range_start, virtual_range_size_bytes, direction, task);
// TODO: check for md == nullptr
// Wire down virtual range to specific physical pages
IOReturn result = md->prepare(direction);
// TODO: do error handling
IOByteCount offset = 0;
while (offset < virtual_range_size_bytes)
{
IOByteCount segment_len = 0;
addr64_t phys_addr = md->getPhysicalSegment(offset, &len, kIOMemoryMapperNone);
// TODO: do something with physical range of segment_len bytes at address phys_addr here
offset += segment_len;
}
/* Unwire. Call this only once you're done with the physical ranges
* as the pager can change the physical-virtual mapping outside of
* prepare…complete blocks. */
md->complete(direction);
md->release();
As explained above, this is not suitable for generating DMA scatter-gather lists for device I/O. Note also this code is only valid for 64-bit kernels. You'll need to be careful if you still need to support ancient 32-bit kernels (OS X 10.7 and earlier) because virtual and physical addresses can still be 64-bit (64-bit user processes and PAE, respectively), but not all memory descriptor functions are set up for that. There are 64-bit-safe variants available to be used for 32-bit kexts.
arif#khost:~/src/linux$ global -x ip_rcv_finish
ip_rcv_finish 319 net/ipv4/ip_input.c static int ip_rcv_finish(struct sk_buff *skb)
Now if i want to use this function i need to initialize a pointer to this function.
To be able to do that i need the address of the function.
I've seen that from user space i can read /proc/kallsyms to get an address of a symbol. Is their any similar mechanism exist where i can read the symbol table to extract a symbol's address from kernel space?
Depending on your kernel version, you can use kallsyms_lookup_name and/or kallsyms_on_each_symbol to obtain the addresses of the symbols from code running in the kernel space.
This only works if CONFIG_KALLSYMS is set in the kernel configuration.
Note that I would not recommend looking up the addresses of the functions to be called though unless there is no better way (kernel API) to do what you would like to. Still, if nothing else helps, kallsyms_*() API may be the way to go.