Qemu fw_cfg DMA aarch64

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
foreverska
Posts: 4
Joined: Sat Apr 13, 2019 10:55 pm

Qemu fw_cfg DMA aarch64

Post by foreverska »

I'm attempting DMA access of a fw_cfg file. I have accessed the directory, compared file names and found the file i want. I have compared the reported size to make sure it is correct for what I was expecting. All of these things are true and then I enter fwcfg_dma_access via

Code: Select all

    #define FWCFG_DMA_READ 	2
    fwcfg_dma_access(file.select << 16 | FWCFG_DMA_READ, ramfb_file.size, (vaddr_t) &cfg);

Code: Select all

#if __ORDER_LITTLE_ENDIAN__ 
#define HTOBE(x)  __builtin_bswap32(x)
#define HTOBE64(x) __builtin_bswap64(x)
#else 
#define HTOBE(x) x 
#define HTOBE64(x) x
#endif

#define FW_CFG_BASE 		0x09020000

#define FW_CFG_CTL_OFF 		0x08
#define FW_CFG_DATA_OFF 	0x00
#define FW_CFG_DMA_OFF 		0x10

struct fwcfg_dma_access {
	uint32_t control;
	uint32_t length;
	uint64_t address;
};

static vaddr_t _addr(vaddr_t base, vaddr_t offset)
{
	return (vaddr_t) phys_to_virt(base, MEM_AREA_IO_SEC) + offset;
}

int fwcfg_dma_access(uint32_t ctrl, uint32_t len, vaddr_t addr)
{
	volatile struct fwcfg_dma_access access;
	
	access.control = HTOBE(ctrl);
	access.length = HTOBE(len);
	access.address = HTOBE64(addr);
	
	vaddr_t access_addr = (vaddr_t) virt_to_phys((void*) &access);
	uint32_t access_addr_lo = (uint32_t) (access_addr & 0xFFFFFFFFU);
	uint32_t access_addr_hi = (uint32_t) (access_addr >> 32);
	
	io_write32(_addr(FW_CFG_BASE, FW_CFG_DMA_OFF), HTOBE(access_addr_hi));
	io_write32(_addr(FW_CFG_BASE, FW_CFG_DMA_OFF + 4), HTOBE(access_addr_lo));
	
	DMSG("Waiting on DMA Access");
	while ((HTOBE(access.control) & ~FWCFG_DMA_ERROR) != 0) {}
	DMSG("DMA Access Done");
	
	return FWCFG_OK;
}
The execution gets to "waiting on DMA acess" but never reaches "DMA access done". I pulled the check from Linux kernel. I have also tried:

Code: Select all

	DMSG("Waiting on DMA Access");
	while (io_read32(_addr(FW_CFG_BASE, FW_CFG_DMA_OFF)) != 0) {}
	DMSG("DMA Access Done");
with no success. Anyone have any hints?
foreverska
Posts: 4
Joined: Sat Apr 13, 2019 10:55 pm

Re: Qemu fw_cfg DMA aarch64

Post by foreverska »

For the posterity of future internet explorers this ended up being an issue in Qemu aarch64. Non-secure devices (FW_CFG at the time of this writing) cannot DMA secure memory (ie Secure ram). Make sure the devices are the same memory security levels.
Post Reply