嵌入式技术论坛
直播中

申根换

7年用户 1594经验值
私信 关注
[经验]

ART-Pi Smart调试物理地址的驱动实现

因为 ART-Pi Smart 使用了 MMU 来管理内存,这在一定程度为调试外设驱动增加了复杂度,为了方便在命令行下调试物理内存,我写了一个 dbg_mm 驱动,导出 了两个命令:
rdbg_mm 物理地址 数据长度 # 备注地址要 4 字节对齐,长度以 4 字节为单位,地址和数据都要 0x/0X 开头
wdbg_mm 物理地址 数据0 数据1 .... # 备注地址要 4 字节对齐,数据 16 进制表示,地址和数据都要 0x/0X 开头
查看 ART-Pi Smart 的内存地址映射是这样的:

根据上述映射关系,实现的 dbg_mm 驱动的源码是这样的:
/******************************************************************************
* File:             drv_dbg_mm.c
*
* Author:           iysheng@163.com  
* Created:          04/26/22
* Description:      调试物理内存驱动
*****************************************************************************/
#include
#include
#include
#include "ioremap.h"
#include
#include
#define LOG_LVL              LOG_LVL_DBG
#define LOG_TAG              "dbg.mm"
#include
typedef enum {
    DBG_MM_SET_WIDTH_BITS,
    DBG_MM_GET_WIDTH_BITS,
    DBG_MM_SET_ADDR_STEP,
    DBG_MM_GET_ADDR_STEP,
}dbg_mm_cmd_E;
static struct rt_device gs_dbg_mm_dev;
/* 默认支持字节访问,但是 IMX6ULL 对数据访问总线宽度有严格的限制
* 在访问外设寄存器时,如果 width 宽度不匹配导致崩溃
* */
static int __gs_width_in_bits = 8;
/* 地址 step 默认和位宽保持一致 */
static int __gs_addr_step = 1;
#define PV_LEN   0x20000000
static void dbg_mm_hex_smart(rt_uint32_t paddr, rt_uint32_t addr, rt_size_t len)
{
    rt_size_t i = 0, j = 0;
    for (; i < len; i += 4)
    {
        rt_kprintf("0x%08x:", paddr + i * __gs_addr_step);
        for (j = 0; j < 4; j++)
        {
            if (j + i < len)
            {
                switch (__gs_width_in_bits / 8)
                {
                    case 1:
                        rt_kprintf("0x%02x ", *((rt_uint8_t *)addr));
                    break;
                    case 2:
                        rt_kprintf("0x%04x ", *((rt_uint16_t *)addr));
                    break;
                    case 4:
                        rt_kprintf("0x%08x ", *((rt_uint32_t *)addr));
                    break;
                }
                addr += __gs_addr_step;
            }
            else
            {
                break;
            }
        }
        rt_kprintf("
");
    }
}
static void dbg_mm_copy_smart(rt_uint32_t dst, rt_uint32_t src, rt_size_t len)
{
    rt_size_t i = 0;
    for (; i < len; i++)
    {
        switch (__gs_width_in_bits / 8)
        {
            case 1:
                *((rt_uint8_t *)dst) = *((rt_uint8_t *)src);
            break;
            case 2:
                *((rt_uint16_t *)dst) = *((rt_uint16_t *)src);
            break;
            case 4:
                *((rt_uint32_t *)dst) = *((rt_uint32_t *)src);
            break;
            default:
            break;
        }
        dst += __gs_addr_step;
        src += __gs_addr_step;
    }
}
static rt_err_t  dbg_mm_init(rt_device_t dev)
{
    return RT_EOK;
}
static rt_err_t  dbg_mm_open(rt_device_t dev, rt_uint16_t oflag)
{
    return RT_EOK;
}
static rt_err_t  dbg_mm_close(rt_device_t dev)
{
    return RT_EOK;
}
static rt_err_t dbg_mm_control(rt_device_t dev,
                                       int         cmd,
                                       void       *args)
{
    int ret = RT_EOK;
    switch (cmd)
    {
        case DBG_MM_SET_WIDTH_BITS:
            __gs_width_in_bits = *((int *)args);
            break;
        case DBG_MM_GET_WIDTH_BITS:
            *((int *)args) = __gs_width_in_bits;
            break;
        case DBG_MM_SET_ADDR_STEP:
            __gs_addr_step = *((int *)args);
            break;
        case DBG_MM_GET_ADDR_STEP:
            *((int *)args) = __gs_addr_step;
            break;
        default:
            break;
    }
    return ret;
}
static rt_size_t dbg_mm_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    rt_uint32_t * pvaddr = RT_NULL;
    int io_remaped = 0;
    if (pos >= KERNEL_VADDR_START + PV_OFFSET && pos < KERNEL_VADDR_START + PV_OFFSET + PV_LEN)
    {
        pvaddr = (rt_uint32_t *)(pos - PV_OFFSET);
    }
    else if (pos < KERNEL_VADDR_START + PV_OFFSET)
    {
        pvaddr = (rt_uint32_t *)rt_ioremap((void *)pos, __gs_addr_step * size);
        if (!pvaddr)
        {
            LOG_E("Failed io remap@%#x.", pos);
            return 0;
        }
        io_remaped = 1;
    }
    //LOG_I("Rpa@%#x v@%#x.", pos, pvaddr);
    dbg_mm_copy_smart((rt_uint32_t)buffer, (rt_uint32_t)pvaddr, size);
    dbg_mm_hex_smart(pos, (rt_uint32_t)pvaddr, size);
    if (io_remaped)
    {
        rt_iounmap((void *)pvaddr);
    }
    return size;
}
extern void rt_hw_cpu_dcache_clean(void *addr, int size);
static rt_size_t dbg_mm_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    rt_uint32_t pvaddr = 0;
    int io_remaped = 0;
    if (pos > KERNEL_VADDR_START + PV_OFFSET && pos < KERNEL_VADDR_START + PV_OFFSET + PV_LEN)
    {
        pvaddr = (rt_uint32_t)(pos - PV_OFFSET);
    }
    else if (pos < KERNEL_VADDR_START + PV_OFFSET)
    {
        pvaddr = (rt_uint32_t)rt_ioremap((void *)pos, size * __gs_addr_step);
        if (!pvaddr)
        {
            LOG_E("Failed io remap@%#x.", pos);
            return 0;
        }
        io_remaped = 1;
    }
    //LOG_I("pa@%#x v@%#x len=%u.", pvaddr, buffer, size);
    dbg_mm_copy_smart((rt_uint32_t)pvaddr, (rt_uint32_t)buffer, size);
    dbg_mm_hex_smart(pos, (rt_uint32_t)pvaddr, size);
    if (io_remaped)
    {
        rt_iounmap((void *)pvaddr);
    }
    else
    {
        rt_hw_cpu_dcache_clean((void *)pvaddr, size * __gs_addr_step);
    }
    return size;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops dbg_mm_ops =
{
    dbg_mm_init,
    dbg_mm_open,
    dbg_mm_close,
    dbg_mm_read,
    dbg_mm_write,
    dbg_mm_control
};
#endif
int rt_hw_dbg_mm_init(void)
{
    /* set device type */
    gs_dbg_mm_dev.type    = RT_Device_Class_Miscellaneous;
    /* initialize device interface */
#ifdef RT_USING_DEVICE_OPS
    gs_dbg_mm_dev.ops     = &dbg_mm_ops ;
#else
    gs_dbg_mm_dev.init    = dbg_mm_init;
    gs_dbg_mm_dev.open    = dbg_mm_open;
    gs_dbg_mm_dev.close   = dbg_mm_close;
    gs_dbg_mm_dev.read    = dbg_mm_read;
    gs_dbg_mm_dev.write   = dbg_mm_write;
    gs_dbg_mm_dev.control = dbg_mm_control;
#endif
    /* register to device manager */
    rt_device_register(&gs_dbg_mm_dev, "dbg_mm", RT_DEVICE_FLAG_RDWR);
    return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_dbg_mm_init);
/*
* dbg_mm <-w> <-b width> <-s step> addr
* */
long dbg_mm(int argc, char * argv[])
{
    int i = 0;
    rt_uint32_t paddr = 0x0;
    rt_size_t len = 1;
    int widths_in_bits = 0, addr_in_step = -1, read_or_write = 0 /* 默认读数据 */;
    rt_uint32_t buffer[128];
    int opt;
    optind = 0;
    while ((opt = getopt(argc, argv, "wb:s:")) != -1)
    {
        switch (opt)
        {
            case 'b':widths_in_bits = atoi(optarg);break;
            case 's':addr_in_step = atoi(optarg);break;
            /* 修改为写数据 */
            case 'w':read_or_write = 1;break;
            default:break;
        }
    }
    if (widths_in_bits)
    {
        if (!gs_dbg_mm_dev.ops->control(&gs_dbg_mm_dev, DBG_MM_SET_WIDTH_BITS, &widths_in_bits))
        {
            LOG_I("widths_int_bits:%d.", widths_in_bits);
        }
    }
    else
    {
        if (!gs_dbg_mm_dev.ops->control(&gs_dbg_mm_dev, DBG_MM_GET_WIDTH_BITS, &widths_in_bits))
        {
            LOG_I("widths_int_bits:%d.", widths_in_bits);
        }
    }
    if (addr_in_step != -1)
    {
        if (!gs_dbg_mm_dev.ops->control(&gs_dbg_mm_dev, DBG_MM_SET_ADDR_STEP, &addr_in_step))
        {
            LOG_I("addr_in_step:%d.", addr_in_step);
        }
    }
    else
    {
        if (!gs_dbg_mm_dev.ops->control(&gs_dbg_mm_dev, DBG_MM_GET_ADDR_STEP, &addr_in_step))
        {
            LOG_I("addr_in_step:%d.", addr_in_step);
        }
    }
    if (optind >= argc)
    {
        LOG_E("invalid format");
        return -2;
    }
    if (rt_strstr(argv[optind], "0x") || rt_strstr(argv[optind], "0X"))
    {
        sscanf(argv[optind++], "0x%x", &paddr);
        if (paddr % (widths_in_bits / 8))
        {
            LOG_E("addr should align with:%d", widths_in_bits / 8);
            return -3;
        }
    }
    else
    {
        LOG_E("invalid address format, use 0x/0X prefix please.");
        return -4;
    }
    if (!read_or_write)
    {
        if (optind < argc)
        {
            len = atoi(argv[optind]);
        }
#ifdef RT_USING_DEVICE_OPS
        gs_dbg_mm_dev.ops->read(&gs_dbg_mm_dev, paddr, buffer, len);
#else
        gs_dbg_mm_dev.read(&gs_dbg_mm_dev, paddr, buffer, len);
#endif
    }
    else
    {
        for (i = 0; i < argc - optind; i++)
        {
            if (rt_strstr(argv[i + optind], "0x") || rt_strstr(argv[i + optind], "0X"))
            {
                switch (addr_in_step)
                {
                    case 1:
                        sscanf(argv[i + optind], "0x%hhx", (rt_uint8_t *)((rt_uint32_t)buffer + i * addr_in_step));
                        break;
                    case 2:
                        sscanf(argv[i + optind], "0x%hx", (rt_uint16_t *)((rt_uint32_t)buffer + i * addr_in_step));
                        break;
                    case 4:
                        sscanf(argv[i + optind], "0x%x", (rt_uint32_t *)((rt_uint32_t)buffer + i * addr_in_step));
                        break;
                    default:
                        break;
                }
            }
            else
            {
                LOG_E("invalid data format, use 0x/0X prefix please.");
                return -3;
            }
        }
#ifdef RT_USING_DEVICE_OPS
        gs_dbg_mm_dev.ops->write(&gs_dbg_mm_dev, paddr, buffer, i);
#else
        gs_dbg_mm_dev.write(&gs_dbg_mm_dev, paddr, buffer, i);
#endif
    }
    return 0;
}
MSH_CMD_EXPORT(dbg_mm, read dbg mm);
实现的效果是这样的:

为什么实现的这么复杂呢,因为我调试的时候发现,IMX6ULL 对外设地址的对齐访问要求的比较严格。以0x20e01e0 这个地址为例,查看手册:

这个地址是 32 bit的,如果我以1字节对齐访问就是出现这样的错误:
backtrace:
[98553] W/BACKTRACE: unwind: Unsupported personality routine 81019b40 in the index at c00ed168

data abort:Execption:
r00:0xc0144234 r01:0xf01951e0 r02:0xf01951e0 r03:0xc0144234
r04:0xc0002e5c r05:0xdeadbeef r06:0xdeadbeef r07:0xdeadbeef
r08:0xdeadbeef r09:0xdeadbeef r10:0xdeadbeef
fp :0xc01441f4 ip :0x00000000
sp :0xc01441d8 lr :0xc0002f3c pc :0xc0002ca0
cpsr:0x60000013
dfsr:0x00001008
ttbr0:0x8012c018
dfar:0xf01951e0
0xf01951e0 -> 0x020e01e0
thread   pri  status      sp     stack size max used left tick  error
-------- ---  ------- ---------- ----------  ------  ---------- ---
tshell    20  running 0x000001dc 0x00001000    38%   0x00000007 -09
sysinfo_  25  suspend 0x0000011c 0x00004000    02%   0x0000012c 000
sys_work  23  suspend 0x00000088 0x00001000    03%   0x0000000a 000
usbd       8  suspend 0x000000ec 0x00001000    05%   0x00000014 -09
link_d1   30  suspend 0x000000c4 0x00001000    11%   0x00000002 000
tcpip     10  suspend 0x00000110 0x00002000    08%   0x0000000f -09
etx       12  suspend 0x000000d4 0x00002000    03%   0x00000010 -09
erx       12  suspend 0x000000dc 0x00002000    08%   0x00000008 -09
mmcsd_de  22  suspend 0x000000dc 0x00001000    15%   0x00000002 -09
tidle0    31  ready   0x00000068 0x00002000    02%   0x0000000b 000
timer      4  suspend 0x00000084 0x00001000    03%   0x00000009 000
main      10  suspend 0x000000cc 0x00001000    34%   0x00000007 000
shutdown...
(0) assertion failed at function:rt_hw_cpu_shutdown, line number:87
再举一个例子0x21a0000地址为例:

如果我以32bit对齐访问,还是会出问题:
backtrace:
[109559] W/BACKTRACE: unwind: Unsupported personality routine 81019b40 in the index at c00ed168
data abort:Execption:
r00:0xc0144234 r01:0xf0195000 r02:0xf0195000 r03:0xc0144234
r04:0xc0002e5c r05:0xdeadbeef r06:0xdeadbeef r07:0xdeadbeef
r08:0xdeadbeef r09:0xdeadbeef r10:0xdeadbeef
fp :0xc01441f4 ip :0x00000000
sp :0xc01441d8 lr :0xc0002f3c pc :0xc0002cc8
cpsr:0x60000013
dfsr:0x00001008
ttbr0:0x8012c018
dfar:0xf0195000
0xf0195000 -> 0x021a0000
thread   pri  status      sp     stack size max used left tick  error
-------- ---  ------- ---------- ----------  ------  ---------- ---
tshell    20  running 0x000001dc 0x00001000    38%   0x00000002 -09
sysinfo_  25  suspend 0x0000011c 0x00004000    02%   0x0000012c 000
sys_work  23  suspend 0x00000088 0x00001000    03%   0x0000000a 000
usbd       8  suspend 0x000000ec 0x00001000    05%   0x00000014 -09
link_d1   30  suspend 0x000000c4 0x00001000    11%   0x00000002 000
tcpip     10  suspend 0x00000110 0x00002000    08%   0x00000011 -09
etx       12  suspend 0x000000d4 0x00002000    03%   0x00000010 -09
erx       12  suspend 0x000000dc 0x00002000    08%   0x0000000b -09
mmcsd_de  22  suspend 0x000000dc 0x00001000    15%   0x00000002 -09
tidle0    31  ready   0x00000068 0x00002000    02%   0x00000003 000
timer      4  suspend 0x00000084 0x00001000    03%   0x00000009 000
main      10  suspend 0x000000cc 0x00001000    34%   0x00000007 000
为了保证可以改善上述问题,我开发了这个驱动用来调试物理内存。根据上述例子,查看的步骤是这样的:
msh />dbg_mm -b 32 -s 4 0x20e01e0
[86777] I/dbg.mm: widths_int_bits:32.
[86782] I/dbg.mm: addr_in_step:4.
0x020e01e0:0x00000010
msh />dbg_mm -b 16 -s 4 0x21a0000 5
[37268] I/dbg.mm: widths_int_bits:16.
[37272] I/dbg.mm: addr_in_step:4.
0x021a0000:0x0000 0x001f 0x0080 0x0081
0x021a0010:0x0078
这个工具还支持修改指定的物理地址,我以内存为例记录下方法:
msh />dbg_mm -b 16 -s 2 0x90000000 16
[30352] I/dbg.mm: widths_int_bits:16.
[30356] I/dbg.mm: addr_in_step:2.
0x90000000:0x1111 0x1111 0x2222 0x0000
0x90000008:0x3333 0x3333 0x0067 0x0000
0x90000010:0x0012 0x0000 0x1900 0x0000
0x90000018:0x9dbc 0xc00b 0x00e1 0x0500
msh />dbg_mm -b 16 -s 2 -w 0x90000000 0xaaaa 0xbbbb 0xcccc 0xdddd 0xeeee 0xffff
[69352] I/dbg.mm: widths_int_bits:16.
[69356] I/dbg.mm: addr_in_step:2.
0x90000000:0xaaaa 0xbbbb 0xcccc 0xdddd
0x90000008:0xeeee 0xffff
使用上述命令的时候还是要小心不要越界,否则还是会崩溃的。

原作者:iysheng

更多回帖

发帖
×
20
完善资料,
赚取积分