OiO.lk Blog C# Kernel crash when loading data to physical Ram address via module
C#

Kernel crash when loading data to physical Ram address via module


I am trying to load a firmware file to a specific physical address in RAM on an arm64 device. The device tree already has an entry that defines the region of memory and uses no-map to prevent Linux from mapping virtual memory to that region:

.
.
.
    reserved-memory {
        #address-cells = <2>;
        #size-cells = <2>;
        ranges;

        /* Reserved memory for COM.2 firmware */
        reserved: memory@8000000 {
            no-map;
            reg = <0x0 0x08000000 0x0 0x01000000>;
        };
    };

    xlnx-com2@8000000 { 
        compatible = "xlnx,reserved-memory";
        memory-region = <&reserved>;
    };
    };
  };
};

My kernel driver successfully recognizes the designated region as a memory resource, maps to the region using memremap(), takes as module parameter the location of the firmware file to read the bitstream and then write it into memory region.

This is my code:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/firmware.h>
#include <linux/memremap.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/delay.h> 
#define DRV_NAME "com2"

static char *fpgaupdate;
module_param(fpgaupdate, charp, 0444);

static int com2_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct device_node *np;
    struct resource res;
    phys_addr_t mem_paddr;
    void __iomem *mem_vaddr;
    size_t mem_size;
    int ret = 0;

    const struct firmware *fw;
    size_t offset = 0;
    size_t chunk_size;

    if (fpgaupdate == NULL) {
        dev_err(dev, "fpgaupdate parameter is missing\n");
        return -EINVAL;
    }

    ret = request_firmware(&fw, fpgaupdate, dev);
    if (ret) {
        dev_err(dev, "failed to request firmware. error: %d\n", ret);
        return ret;
    }

    dev_info(dev, "firmware size: %ld bytes\n", fw->size);

    np = of_parse_phandle(dev->of_node, "memory-region", 0);
    if (!np) {
        dev_err(dev, "No %s specified\n", "memory-region");
        ret = -EINVAL;
        goto rel_fw;
    }

    ret = of_address_to_resource(np, 0, &res);
    if (ret) {
        dev_err(dev, "No memory address assigned to the region\n");
        goto rel_fw;
    }

    mem_paddr = res.start;
    mem_size = resource_size(&res);

    if (fw->size > mem_size) {
        dev_err(dev, "firmware size exceeds reserved memory size\n");
        ret = -ENOMEM;
        goto rel_fw;
    }

    while (offset < fw->size) {
        dev_info(dev, "fw_size 0x%lx, offset 0x%lx\n", fw->size, offset);

        chunk_size = min((size_t)PAGE_SIZE, fw->size - offset);

        // if (!IS_ALIGNED(mem_paddr + offset, PAGE_SIZE)) {
        //     dev_err(dev, "Memory access is not page aligned!\n");
        //     ret = -EINVAL;
        //     goto rel_fw;
        // }
        mem_vaddr = memremap(mem_paddr + offset, chunk_size, MEMREMAP_WB);
        if (!mem_vaddr) {
            dev_err(dev, "failed to remap reserved memory region\n");
            ret = -ENOMEM;
            goto rel_fw;
        }
        dev_info(dev, "vaddr received 0x%p, \n", mem_vaddr);

        memcpy_toio(mem_vaddr, fw->data + offset, chunk_size);
        dev_info(dev, "Copied %ld bytes to reserved memory at offset 0x%lx\n", chunk_size, offset);

        memunmap(mem_vaddr);

        offset += chunk_size;
    }

rel_fw:
    release_firmware(fw);

    return ret;
}

static int com2_remove(struct platform_device *pdev)
{
    dev_info(&pdev->dev, "firmware loader module removed\n");
    return 0;
}

static const struct of_device_id com2_of_match[] = {
    { .compatible = "xlnx,reserved-memory", },
    {},
};
MODULE_DEVICE_TABLE(of, com2_of_match);

static struct platform_driver com2_driver = {
    .probe = com2_probe,
    .remove = com2_remove,
    .driver = {
        .name = DRV_NAME,
        .of_match_table = com2_of_match,
    },
};

module_platform_driver(com2_driver);

MODULE_ALIAS("platform:" DRV_NAME);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("com2 driver");

The firmware file size is about 3MB and the region reserved in the RAM is 16MB.

Ideally I would want to map to the whole region once and then just copy the complete firmware bitstream to the memory region, but the kernel crashes during the memcpy(). That is why I copy the data in 4kb chunks to narrow down the source of error.

After successfully coping some chunks of data, it suddenly crashes. I noticed that it does not always crash at the same physical address. Sometimes it copies 1/3 of the data before crashing and sometimes just a few Kbs.

This is the error output:

[  127.396751] com2 xlnx-com2@8000000: firmware size: 3652740 bytes
[  127.396801] com2 xlnx-com2@8000000: fw_size 0x37bc84, offset 0x0
[  127.396819] com2 xlnx-com2@8000000: vaddr received 0x00000000e1ff176d,
[  127.396831] com2 xlnx-com2@8000000: Copied 4096 bytes to reserved memory at offset 0x0
[  127.396837] com2 xlnx-com2@8000000: fw_size 0x37bc84, offset 0x1000
[  127.396844] com2 xlnx-com2@8000000: vaddr received 0x000000003e2c1ae9,
[  127.396854] com2 xlnx-com2@8000000: Copied 4096 bytes to reserved memory at offset 0x1000
[  127.396860] com2 xlnx-com2@8000000: fw_size 0x37bc84, offset 0x2000
[  127.396866] com2 xlnx-com2@8000000: vaddr received 0x00000000edb34175,
[  127.396876] com2 xlnx-com2@8000000: Copied 4096 bytes to reserved memory at offset 0x2000
[  127.396957] com2 xlnx-com2@8000000: fw_size 0x37bc84, offset 0x3000
   .
   .
  (I removed some printlogs here to shorten the output...)
   .
   .
[  127.400867] com2 xlnx-com2@8000000: fw_size 0x37bc84, offset 0xbe000
[  127.400873] com2 xlnx-com2@8000000: vaddr received 0x00000000a7c0dd17,
[  127.400881] com2 xlnx-com2@8000000: Copied 4096 bytes to reserved memory at offset 0xbe000
[  127.400887] com2 xlnx-com2@8000000: fw_size 0x37bc84, offset 0xbf000
[  127.400893] com2 xlnx-com2@8000000: vaddr received 0x00000000c459ba52,
[  127.400901] com2 xlnx-com2@8000000: Copied 4096 bytes to reserved memory at offset 0xbf000
[  127.400906] com2 xlnx-com2@8000000: fw_size 0x37bc84, offset 0xc0000
[  127.400913] com2 xlnx-com2@8000000: vaddr received 0x000000001477362a,
[  127.400921] com2 xlnx-com2@8000000: Copied 4096 bytes to reserved memory at offset 0xc0000
[  127.400927] com2 xlnx-com2@8000000: fw_size 0x37bc84, offset 0xc1000
[  127.400941] Unable to handle kernel paging request at virtual address 30a1001cb9f45354
[  127.409371] Mem abort info:
[  127.412169]   ESR = 0x0000000096000004
[  127.415917]   EC = 0x25: DABT (current EL), IL = 32 bits
[  127.421234]   SET = 0, FnV = 0
[  127.424283]   EA = 0, S1PTW = 0
[  127.427420]   FSC = 0x04: level 0 translation fault
[  127.432296] Data abort info:
[  127.435167]   ISV = 0, ISS = 0x00000004
[  127.439000]   CM = 0, WnR = 0
[  127.441965] [30a1001cb9f45354] address between user and kernel address ranges
[  127.449109] Internal error: Oops: 0000000096000004 [#1] PREEMPT_RT SMP
[  127.449117] Modules linked in: com2(OE+) - XXXXXXXX
[  127.449187] CPU: 2 PID: 1192 Comm: insmod Tainted: G           OE      6.1.33-rt11-31 #1
[  127.449195] Hardware name: XXXXXXXXXX
[  127.449199] pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[  127.449206] pc : region_intersects+0x78/0xf8
[  127.449221] lr : region_intersects+0x40/0xf8
[  127.449229] sp : ffffff800839b820
[  127.449231] x29: ffffff800839b820 x28: ffffff80080c0000 x27: 0000000000001000
[  127.449241] x26: 0000000008000000 x25: ffffffc000a0c118 x24: ffffffc000a0c160
[  127.449249] x23: 0000000001000200 x22: 0000000000000000 x21: 00000000080c1000
[  127.449258] x20: ffffffc008fd1090 x19: 0000000000000000 x18: 0000000000000020
[  127.449267] x17: 000058d800005ce8 x16: 000061300000d160 x15: ffffff8004e93c90
[  127.449275] x14: 0000000000000000 x13: ffffffc009154542 x12: ffffffc00915453b
[  127.449284] x11: 6d656d2064657672 x10: 000000000000000a x9 : ffffffc0089dd534
[  127.449293] x8 : 000000000000000a x7 : 000000000000000f x6 : 00000000fffff40b
[  127.449301] x5 : ffffff807fe6c8d8 x4 : 30a1001cb9f4533c x3 : 30a0000330e00017
[  127.449310] x2 : 10d6000011130000 x1 : 00000000080c1fff x0 : 0000000000000001
[  127.449319] Call trace:
[  127.449321]  region_intersects+0x78/0xf8
[  127.449331]  memremap+0x38/0x238
[  127.449341]  com2_probe+0x1bc/0x25c [com2]
[  127.449359]  platform_probe+0x70/0xe0
[  127.449368]  really_probe+0xc4/0x2b0
[  127.449374]  __driver_probe_device+0x80/0x120
[  127.449380]  driver_probe_device+0x44/0x120
[  127.449385]  __driver_attach+0x7c/0x130
[  127.449391]  bus_for_each_dev+0x78/0xd0
[  127.449401]  driver_attach+0x2c/0x38
[  127.449410]  bus_add_driver+0x15c/0x210
[  127.449418]  driver_register+0x6c/0x128
[  127.449424]  __platform_driver_register+0x30/0x40
[  127.449431]  com2_driver_init+0x28/0x1000 [com2]
[  127.449445]  do_one_initcall+0x50/0x2e0
[  127.449453]  do_init_module+0x50/0x1f8
[  127.449462]  load_module+0x1828/0x1d50
[  127.449470]  __do_sys_finit_module+0xc0/0x118
[  127.449478]  __arm64_sys_finit_module+0x28/0x38
[  127.449485]  invoke_syscall+0x4c/0x110
[  127.449494]  el0_svc_common.constprop.0+0x4c/0xf8
[  127.449501]  do_el0_svc+0x24/0x30
[  127.449509]  el0_svc+0x20/0x60
[  127.449518]  el0t_64_sync_handler+0xb8/0xc0
[  127.449526]  el0t_64_sync+0x174/0x178
[  127.449536] Code: eb0202bf 1a938673 f9401884 b4000224 (f9400c83)

looks like a page fault or access to virtual page that doesn’t exist.

Any help or hint would be greatly appreciated. Thanks!

I followed xilinx documentation on reserved memory: https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18841683/Linux+Reserved+Memory.
I also tried removing no-map so see if it had any affect…but I still get the same error.



You need to sign in to view this answers

Exit mobile version