cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
mutt1
Observer
Observer
5,289 Views
Registered: ‎09-01-2017

Ultrascale+ fails to correctly mmap /dev/mem

Jump to solution

could someone tell me why mmaping /dev/mem address of a register doesn't work, in fact it causes the kernel to crash.

 

devmem::devmem(off_t address, size_t len):virt_addr(0), offset(0)
{

// Truncate offset to a multiple of the page size, or mmap will fail.

size_t pagesize = sysconf(_SC_PAGE_SIZE);
off_t page_base = (address / pagesize) * pagesize;
off_t page_offset = address - page_base;
mapped_size = page_offset + len;

fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd < 1) {
std::cerr << "ERROR: cannot open /dev/mem " << std::endl ;
perror("open /dev/mem");
throw(std::exception());
}
else
{ std::cout << "mmap" << std::endl;
void *mem = mmap(NULL, mapped_size, PROT_READ | PROT_WRITE , MAP_SHARED, fd, page_base);

if (mem == MAP_FAILED) {
std::cerr << "ERROR: cannot map memory at " << std::hex << address << std::dec << std::endl ;
perror("mmap ");
throw(std::exception());
}
else
{
std::cout << "Successfully mapped page_base:" << std::hex << page_base << " to virt_addr:" << mem << std::dec << std::endl;
virt_addr = (uint32_t*)mem;

offset = page_offset;
}
}
}

 

with test code as follows.

CPPUNIT_TEST_SUITE_REGISTRATION( DevMemTestCase );

void DevMemTestCase::mmap_test()
{

try
{
devmem mapping = devmem((off_t)(0x80040000), 1000);
volatile uint32_t* virt_addr=mapping.get_virtual_mem_addr();
*virt_addr=0x1;
CPPUNIT_ASSERT(*virt_addr==0x1);
}
catch(std::exception ex)
{
CPPUNIT_FAIL("DevMem CTOR threw exception");
}
}

 

*virt_addr=0x1; causes a crash. Reading from virt_addr in gdb causes Permisson Denied even when logged in as root.

 

finally the device tree identifying the register address is

 

abt_top_hier_0_axi_fifo_mm_s_0: axi_fifo_mm_s@80040000 {
compatible = "xlnx,axi-fifo-mm-s-4.1";
interrupt-parent = <&gic>;
interrupts = <0 92 4>;
reg = <0x0 0x80040000 0x0 0x10000>;
xlnx,axi-str-rxd-protocol = "XIL_AXI_STREAM_ETH_DATA";
xlnx,axi-str-rxd-tdata-width = <0x20>;
xlnx,axi-str-txc-protocol = "XIL_AXI_STREAM_ETH_CTRL";
xlnx,axi-str-txc-tdata-width = <0x20>;
xlnx,axi-str-txd-protocol = "XIL_AXI_STREAM_ETH_DATA";
xlnx,axi-str-txd-tdata-width = <0x20>;
xlnx,axis-tdest-width = <0x4>;
xlnx,axis-tid-width = <0x4>;
xlnx,axis-tuser-width = <0x4>;
xlnx,data-interface-type = <0x0>;
xlnx,has-axis-tdest = <0x0>;
xlnx,has-axis-tid = <0x0>;
xlnx,has-axis-tkeep = <0x0>;
xlnx,has-axis-tstrb = <0x0>;
xlnx,has-axis-tuser = <0x0>;
xlnx,rx-fifo-depth = <0x200>;
xlnx,rx-fifo-pe-threshold = <0x2>;
xlnx,rx-fifo-pf-threshold = <0x1fb>;
xlnx,s-axi-id-width = <0x4>;
xlnx,s-axi4-data-width = <0x20>;
xlnx,select-xpm = <0x0>;
xlnx,tx-fifo-depth = <0x200>;
xlnx,tx-fifo-pe-threshold = <0x2>;
xlnx,tx-fifo-pf-threshold = <0x1fb>;
xlnx,use-rx-cut-through = <0x0>;
xlnx,use-rx-data = <0x1>;
xlnx,use-tx-ctrl = <0x0>;
xlnx,use-tx-cut-through = <0x0>;
xlnx,use-tx-data = <0x0>;
};

 

mmap succeededs and returns a user address, so I'm guessing its mapped a virtual address rather than a physical address?

 

any assistance is much appreciated.

 

Matt

0 Kudos
1 Solution

Accepted Solutions
joe4702
Contributor
Contributor
5,746 Views
Registered: ‎08-21-2012

Your code looks the same as mine (which works).

Are you sure that is a valid physical address?

Just because it's in the device tree, doesn't mean that core necessarily exists in the PL.

Can you access that address using the Linux 'devmem' command?

 

 

View solution in original post

6 Replies
smcnutt
Contributor
Contributor
5,259 Views
Registered: ‎04-04-2018

Hi Matt,

 

I'm confused ...

 

> mmaping /dev/mem address of a register doesn't work,

> in fact it causes the kernel to crash.

...

> mmap succeededs and returns a user address

 

Does mmap succeed or crash the kernel?

 

> so I'm guessing its mapped a virtual address rather

> than a physical address?

 

mmap() maps a physical address to a virtual address. IIRC, the va ends up in the application page tables.

 

I use mmap() routinely and haven't had observed any issues.

 

Regards,

--Scott

 

5,255 Views
Registered: ‎04-20-2017
Also for mmap xilinx IP cores page alignment is never an issue, I could not verify your code for that by having a quick look, so you could try to get rid of that if mapping up cores is all you want to do

The address of the ip cores in the device tree are always physical addresses. OP core register maps are mapped physical memory and has nothing to do with Linux virtual memory space (in which you get an address by mmap)

Mapping other regions of memory that is managed by Linux is also a dangerous game... If you need to do it for some buffer for example you need either a continuous memory allocation in kernel or exclude some DDR from petalinux that you want to manage by yourself
mutt1
Observer
Observer
5,243 Views
Registered: ‎09-01-2017

@smcnuttwrote:

Hi Matt,

 

I'm confused ...

 

> mmaping /dev/mem address of a register doesn't work,

> in fact it causes the kernel to crash.

...

> mmap succeededs and returns a user address

 

Does mmap succeed or crash the kernel?

 

> so I'm guessing its mapped a virtual address rather

> than a physical address?

 

mmap() maps a physical address to a virtual address. IIRC, the va ends up in the application page tables.

 

I use mmap() routinely and haven't had observed any issues.

 

Regards,

--Scott

 


Simplifying the code it boils down to the following

fd = open("/dev/mem", O_RDWR | O_SYNC);

void *virt_addr = mmap(NULL, 1000, PROT_READ | PROT_WRITE , MAP_SHARED, fd, 0x80040000);
*virt_addr=0x1;

 

mmap succeeds and returns a virtual memory address that (if I have understood correctly) maps onto the physical memory of the AXI_FIFO control and status registers(i.e. 0x80040000 as given in the device tree)

 

Should I then be able to read from that virtual address? I can't.

 

Should I be able to write that address? I can't because *virt_addr=0x1; segment faults.

 

My understanding is that mmapping /dev/mem to a physical memory address  should allow a (suid) user application to write to that physical memory via the virtual memory. Am I wrong?

 

 

0 Kudos
joe4702
Contributor
Contributor
5,747 Views
Registered: ‎08-21-2012

Your code looks the same as mine (which works).

Are you sure that is a valid physical address?

Just because it's in the device tree, doesn't mean that core necessarily exists in the PL.

Can you access that address using the Linux 'devmem' command?

 

 

View solution in original post

mutt1
Observer
Observer
5,232 Views
Registered: ‎09-01-2017

 


@joe4702wrote:

Your code looks the same as mine (which works).

Are you sure that is a valid physical address?

Just because it's in the device tree, doesn't mean that core necessarily exists in the PL.

Can you access that address using the Linux 'devmem' command?

 

 

good idea!

 

busybox devmem 0x80040000 does not return

busybox devmem 0x80040000 w 0x1 crashes the kernel.

 

I think you might be right!

 



 

0 Kudos
smcnutt
Contributor
Contributor
5,225 Views
Registered: ‎04-04-2018

 

> Just because it's in the device tree, doesn't mean

> that core necessarily exists in the PL.

 

Good point, Joe. Although, I would expect a bus error v. hang/crash.

 

> busybox devmem 0x80040000 does not return

> busybox devmem 0x80040000 w 0x1 crashes the kernel.

 

Check your clocks.

 

Here's a simple util I've used for years without issue ... FWIW.

 

Regards,

--Scott

 

 

#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>


int main(int argc, const char *argv[])
{
	void *ptr;
	int fd;
	uint32_t *addr;
	off_t ba;
	uint32_t oft;
	uint32_t *p;
	uint32_t i;
	uint32_t cnt;
	int pgsize = getpagesize();
	int oftmask = pgsize - 1;
	int pgmask = ~oftmask;

	/* Get the memory offset (physical address) */
	if (argc != 3) {
		printf("USAGE md phys-addr word-cnt\n");
		return -1;
	}
	if (sscanf(argv[1], "%p", &addr) != 1) {
		printf("invalid offset: '%s'\n", argv[1]);
		return -1;
	}

	/* Get the size (in words as hex) */
	if (sscanf(argv[2], "%x", &cnt) != 1) {
		printf("invalid count: '%s'\n", argv[2]);
		return -1;
	}

	/* Force 32-bit alignment */
	if ((unsigned long)addr & 0x03) {
		printf("address must be 32-bit aligned\n");
		return -1;
	}

	/* Adjust address for page boundary */
	oft = (off_t)addr & oftmask;
	ba = (off_t)addr & pgmask;

	/* We only map a single page so make sure the count
	 * won't exceed a page boundary.
	 */
	if ((oft + (cnt << 2)) > pgsize) {
		printf("Count too large.\n");
		return -1;
	}

	/* Use /dev/mem */
	fd = open("/dev/mem", O_RDWR | O_SYNC);
	if (fd == -1) {
		printf("/dev/mem: %s (%d)\n", strerror(errno), errno);
		return -1;
	}

	/* Map it! */
	ptr = mmap(NULL, pgsize, PROT_READ | PROT_WRITE | PROT_EXEC,
		MAP_SHARED, fd, ba );
	if (ptr == MAP_FAILED) {
		printf("mmap: %s (%d)\n", strerror(errno), errno);
		close(fd);
		return -1;
	}

	/* Display contents */
	p = (uint32_t *)(ptr + oft);
	for (i = 0; i < cnt; i++) {
		if ((i & 0x03) == 0)
			printf("\n%08lx :", (unsigned long)(addr + i));
		printf(" %08lx", (unsigned long)p[i]);
	}
	printf("\n");

	/* All done -- unmap and exit */
	munmap(ptr, pgsize);
	close(fd);

	return 0;
}

 

0 Kudos