msm平台,AP和CP封装在一起,公用一块内存。所以AP需要负责把整个modem, TZ , rpm等binary拷贝到内存中以供modem等subsystem去运行。那AP这边是怎么分配这些内存,又是怎么读出来相关的binary,又如何把binary上传上去的呢??
相关的feature
CONFIG_FW_LOADER
CONFIG_FW_LOADER_USER_HELPER
modem使用的内存申请要设置modem的内存大小,必须首先需要确认modem binary的大小,modem需要使用的内存大小等。这个在CMA相关的内容中说过。这里在说一下高通msm8916平台,modem大小检查以及修改方法。 1) modem binary的大小可以从以下编译的log里边看出来!!(modem_proc/build/ms目录下的pplk-XXX.log或者build_xxxx.log)。 根据大小对齐1MB大小,就是modem binary需要流出来的大小。看如下例子里边的log,总的大小是77.04, 所以需要在上面的dtsi文件中留出来78MB就可以。
Image loaded at virtual address 0xc0000000
Image: 55.44 MiB
AMSS Heap: 7.50 MiB (dynamic)
MPSS Heap: 4.00 MiB (dynamic)
DSM Pools: 5.06 MiB
Q6Zip RO, Swap Pool: 2.00 MiB (dynamic)
Q6Zip RW, Swap Pool: 1.00 MiB (dynamic)
Q6Zip RW, dlpager Heap: 1.00 MiB
Extra: 0.54 MiB
Pad ding: 0.37 MiB
End Address Alignment: 0.13 MiB
Total: 77.04 MiB
Available: 7.96 MiB
2) 然后去修改modem_proc/config/xxx/ 目录下的cust_config.xml文件中修改modem大小
<physical_pool name ="DEFAULT_PHYSPOOL" >
<region base ="0x88000000" size ="0x5500000" />
<region base ="0x88000000" size ="0x4E00000" />
</physical_pool >
以下是modem相关的device tree的设置。这些内容也在CMA和ion内存相关的帖子里边都讲过。 但之前有一个疑问就是,在CMA预留了一段内存之后,会把这个赋值给modem的dev->cma_area,然后在分配需要使用的内存的时候从dev->cma_area中取出来,那这个过程好像跟ion内存没有什么关系。能不能去掉下面msmxxx-ion.dtsi中 modem_adsp_mem相关的设置呢?? 是可以的!!!其他几个DMA区域,如果直接从CMA分配的话,应该都可以从msmxxx-ion.dtsi文件中去掉!! 也就是说下面qcom,ion-heap-type = “DMA”的部分其实都可以从msm8916-ion.dtsi文件中去掉,不影响。
qcom,mss@4080000 {
compatible = "qcom,pil-q6v56-mss" ;
....
linux,contiguous-region = <&modem_adsp_mem>;
};
qcom,ion-heap@26 {
compatible = "qcom,msm-ion-reserve" ;
reg = <26 >;
linux,contiguous-region = <&modem_adsp_mem>;
qcom,ion-heap-type = "DMA" ;
};
modem_adsp_mem: modem_adsp_region@0 {
linux,reserve-contiguous-region;
linux,reserve-region;
linux,remove-completely;
reg = <0x0 0x86800000 0x0 0x05800000 >;
label = "modem_adsp_mem" ;
};
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 modem_adsp_mem指定的区域,需要分配出来,以供下载modem binary。
static int pil_subsys_init (struct modem_data *drv,
struct platform_device *pdev)
{
...
drv->subsys_desc.name = "modem" ;
drv->subsys_desc.dev = &pdev->dev;
drv->subsys_desc.owner = THIS_MODULE;
drv->subsys_desc.shutdown = modem_shutdown;
drv->subsys_desc.powerup = modem_powerup;
drv->subsys_desc.ramdump = modem_ramdump;
drv->subsys_desc.free_memory = modem_free_memory;
drv->subsys_desc.crash_shutdown = modem_crash_shutdown;
drv->subsys_desc.err_fatal_handler = modem_err_fatal_intr_handler;
drv->subsys_desc.stop_ack_handler = modem_stop_ack_intr_handler;
drv->subsys_desc.wdog_bite_handler = modem_wdog_bite_intr_handler;
drv->subsys = subsys_register(&drv->subsys_desc);
drv->ramdump_dev = create_ramdump_device("modem" , &pdev->dev);
...
return ret;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 之后在modem_powerup()的时候,会先根据modem binary的elf结构独处modem的大小等,然后计算出align之后应该的大小。 pil_boot()-> request_firmware()读出elf头并计算大小等。 在pil_setup_region()->pil_alloc_region()的时候,传进去的大小就是从上面读出来的大小。
pil_alloc_region min_addr = 0xc0000000 , max_addr = 0xc2b00000 , aligned_size = 0x2b00000
这里看着和实际的内存大小一致!! 可能是因为留出来的CMA区域的大小正好和这个大小一致才这样的。 在实际调试过程中,也可以打印这个大小之后,调整CMA大小。
再看看实际的CMA大小是怎么申请的。
pil_boot ()->pil_init_mmap ()->pil_setup_region ()->pil_alloc_region ()->
dma_alloc_attrs ()->arm_dma_alloc ()->__dma_alloc ()->__alloc_from_contiguous ()->
这个调用的顺序,一步一步往下看可以看到,实际上分配的区域是一块CMA区域,而且就是在CMA注册之后,在相应的platform设备注册的时候保存到dev->cma_area中的区域。 在相应的设备注册的时候,如果设备的device tree中有”linux,contiguous-region”的时候,就会寻找相应的CMA区域并进行保留。这都是因为注册了platform_bus_typ的notifier函数
bus_register_notifier(&platform_bus_type, &cma_dev_init_nb);
看下面的log。
<6 >[0.487642 ] [0 :swapper/0 :1 ] cma: Assigned CMA region at 0 to 1 de0000.qcom,venus device
<6 >[0.489469 ] [0 :swapper/0 :1 ] cma: Assigned CMA region at 0 to 4080000. qcom,mss device
<6 >[0.490756 ] [0 :swapper/0 :1 ] cma: Assigned CMA region at 0 to a21b000.qcom,pronto device
<6 >[1.125342 ] [0 :swapper/0 :1 ] cma: Assigned CMA region at 0 to 8. qcom,ion-heap device
<6 >[1.125793 ] [0 :swapper/0 :1 ] cma: Assigned CMA region at 0 to 1b .qcom,ion-heap device
<6 >[1.126233 ] [0 :swapper/0 :1 ] cma: Assigned CMA region at 0 to 1 c.qcom,ion-heap device
<6 >[1.126671 ] [0 :swapper/0 :1 ] cma: Assigned CMA region at 0 to 17. qcom,ion-heap device
<6 >[1.127298 ] [0 :swapper/0 :1 ] cma: Assigned CMA region at 0 to 1 a.qcom,ion-heap device
这里看到4080000.qcom,mss这个device相应的区域已经保留了CMA区域。 然后在上面进行分配的时候,在 __alloc_from_contiguous()->dma_alloc_from_contiguous()->dev_get_cma_area()函数中取到 相应的dev->cma_area。
modem相关内存的使用和下载pil_load_seg()->request_firmware_direct()->_request_firmware()函数身生成相应的device节点,并通知ueventd去读取相应的binary然后下载。以下是pil_load_seg里边打印的正在试图下载的binary。
<6 >[29.129737 ] [1 :init:1 ] pil_load_seg fw_name = modem.b02
<6 >[29.157808 ] [1 :init:1 ] pil_load_seg fw_name = modem.b07
<6 >[29.191477 ] [1 :init:1 ] pil_load_seg fw_name = modem.b17
<6 >[29.348480 ] [1 :init:1 ] pil_load_seg fw_name = modem.b19
<6 >[29.409733 ] [1 :init:1 ] pil_load_seg fw_name = modem.b20
<6 >[29.489639 ] [1 :init:1 ] pil_load_seg fw_name = modem.b23
<6 >[29.519624 ] [1 :init:1 ] pil_load_seg fw_name = modem.b24
<6 >[29.549829 ] [1 :init:1 ] pil_load_seg fw_name = modem.b25
<6 >[29.591918 ] [1 :init:1 ] pil_load_seg fw_name = modem.b27
<6 >[31.997036 ] [0 :wcnss_service:307 ] pil_load_seg fw_name = wcnss.b02
<6 >[32.658390 ] [0 :wcnss_service:307 ] pil_load_seg fw_name = wcnss.b04
<6 >[32.693754 ] [0 :wcnss_service:307 ] pil_load_seg fw_name = wcnss.b06
<6 >[32.848104 ] [3 :wcnss_service:307 ] pil_load_seg fw_name = wcnss.b09
<6 >[32.854061 ] [3 :wcnss_service:307 ] pil_load_seg fw_name = wcnss.b10
<6 >[32.876115 ] [3 :wcnss_service:307 ] pil_load_seg fw_name = wcnss.b11
<6 >[37.384287 ] [1 :TimedEventQueue:771 ] pil_load_seg fw_name = venus.b02
<6 >[37.438222 ] [1 :TimedEventQueue:771 ] pil_load_seg fw_name = venus.b03
<6 >[37.484909 ] [2 :TimedEventQueue:771 ] pil_load_seg fw_name = venus.b04
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 在 _request_firmware()->fw_load_from_user_helper()->_request_firmware_load()函数中就在生成相应的dev节点,并通知ueventd。
static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
long timeout)
{
int retval = 0 ;
struct device *f_dev = &fw_priv->dev;
struct firmware_buf *buf = fw_priv->buf;
struct bin_attribute *fw_attr_data = buf->dest_addr ?
&firmware_direct_attr_data : &firmware_attr_data;
buf->is_paged_buf = buf->dest_addr ? false : true ;
dev_set_uevent_suppress(f_dev, true );
__module_get(THIS_MODULE);
retval = device_add(f_dev);
retval = device_create_bin_file(f_dev, fw_attr_data);
retval = device_create_file(f_dev, &dev_attr_loading);
if (retval) {
dev_err(f_dev, "%s: device_create_file failed\n" , __func__);
goto err_del_bin_attr;
}
if (uevent) {
dev_set_uevent_suppress(f_dev, false );
dev_dbg(f_dev, "firmware: requesting %s\n" , buf->fw_id);
if (timeout != MAX_SCHEDULE_TIMEOUT)
schedule_delayed_work(&fw_priv->timeout_work, timeout);
kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);
}
wait_for_completion(&buf->completion);
cancel_delayed_work_sync(&fw_priv->timeout_work);
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 _request_firmware() -> assign_firmware_buf() 这是做什么的??
来看一下ueventd.c文件中是怎么检测这个然后读binary,通过loading节点加载binary的。
int ueventd_main (int argc, char **argv) {
...
while (1 ) {
ufd.revents = 0 ;
nr = poll(&ufd, 1 , -1 );
if (nr <= 0 )
continue ;
if (ufd.revents & POLLIN)
handle_device_fd();
}
}
void handle_device_fd () {
...
handle_firmware_event(&uevent);
}
#define SYSFS_PREFIX "/sys"
#define FIRMWARE_DIR1 "/etc/firmware"
#define FIRMWARE_DIR2 "/vendor/firmware"
#define FIRMWARE_DIR3 "/firmware/image"
#define FIRMWARE_DIR4 "/firmware-modem/image"
#define DEVICES_BASE "/devices/soc.0"
static void process_firmware_event (struct uevent *uevent) {
...
l = asprintf(&root, SYSFS_PREFIX"%s/" , uevent->path);
l = asprintf(&loading, "%sloading" , root);
l = asprintf(&file1, FIRMWARE_DIR1"/%s" , uevent->firmware);
l = asprintf(&file2, FIRMWARE_DIR2"/%s" , uevent->firmware);
l = asprintf(&file3, FIRMWARE_DIR3"/%s" , uevent->firmware);
l = asprintf(&file4, FIRMWARE_DIR4"/%s" , uevent->firmware);
loading_fd = open(loading, O_WRONLY);
...
if (!load_firmware(fw_fd, loading_fd, data_fd))
INFO("firmware: copy success { '%s', '%s' }\n" , root, uevent->firmware);
else
INFO("firmware: copy failure { '%s', '%s' }\n" , root, uevent->firmware);
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 以下看看ueventd中,真正把读到的binary,传给kernel的函数
static int load_firmware (int fw_fd, int loading_fd, int data_fd)
{
struct stat st ;
long len_to_copy;
int ret = 0 ;
if (fstat(fw_fd, &st) < 0 )
return -1 ;
len_to_copy = st.st_size;
write(loading_fd, "1" , 1 );
while (len_to_copy > 0 ) {
char buf[PAGE_SIZE];
ssize_t nr;
nr = read(fw_fd, buf, sizeof (buf));
if (!nr)
break ;
if (nr < 0 ) {
ret = -1 ;
break ;
}
len_to_copy -= nr;
while (nr > 0 ) {
ssize_t nw = 0 ;
nw = write(data_fd, buf + nw, nr);
if (nw <= 0 ) {
ret = -1 ;
goto out;
}
nr -= nw;
}
}
out:
if (!ret)
write(loading_fd, "0" , 1 );
else
write(loading_fd, "-1" , 2 );
return ret;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 内核中,data节点出来write的函数在_request_firmware_load()中根据buf->dest_addr的值有所不同
static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
long timeout)
{
...
struct bin_attribute *fw_attr_data = buf->dest_addr ?
&firmware_direct_attr_data : &firmware_attr_data;
...
}
在下载modem.bxx的时候应该都是有buf->dest_addr才对
<6 >[29.355860 ] [0 :init:1 ] pil_load_seg fw_name = modem.b02
<6 >[29.361829 ] [0 :init:1 ] fw_load_from_user_helper start
<6 >[29.368051 ] [0 :init:1 ] _request_firmware_load buf->dest_addr = 0x86800000
<6 >[29.380308 ] [0 :ueventd:230 ] firmware_loading_store started
...
<6 >[29.391942 ] [0 :init:1 ] pil_load_seg fw_name = modem.b07
<6 >[29.398801 ] [0 :init:1 ] fw_load_from_user_helper start
<6 >[29.404996 ] [0 :init: 1 ] _request_firmware_load buf->dest_addr = 0x86840000
...
static ssize_t firmware_direct_write (struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buffer, loff_t offset, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
struct firmware_priv *fw_priv = to_firmware_priv(dev);
struct firmware *fw ;
ssize_t retval;
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
mutex_lock(&fw_lock);
fw = fw_priv->fw;
if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->buf->status)) {
retval = -ENODEV;
goto out;
}
retval = __firmware_data_rw(fw_priv, buffer, &offset, count, 0 );
if (retval < 0 )
goto out;
fw_priv->buf->size = max_t (size_t , offset, fw_priv->buf->size);
out:
mutex_unlock(&fw_lock);
return retval;
}
static int __firmware_data_rw(struct firmware_priv *fw_priv, char *buffer,
loff_t *offset, size_t count, int read)
{
u8 __iomem *fw_buf;
struct firmware_buf *buf = fw_priv->buf;
int retval = count;
if ((*offset + count) > buf->dest_size) {
pr_debug("%s: Failed size check.\n" , __func__);
retval = -EINVAL;
goto out;
}
fw_buf = buf->map_fw_mem(buf->dest_addr + *offset, count,
buf->map_data);
if (!fw_buf) {
pr_debug("%s: Failed ioremap.\n" , __func__);
retval = -ENOMEM;
goto out;
}
if (read)
memcpy (buffer, fw_buf, count);
else
memcpy (fw_buf, buffer, count);
*offset += count;
buf->unmap_fw_mem(fw_buf, count, buf->map_data);
out:
return retval;
}
static void *map_fw_mem (phys_addr_t paddr, size_t size, void *data)
{
struct pil_map_fw_info *info = data;
return dma_remap(info->dev, info->region, paddr, size,
&info->attrs);
}
static inline void *dma_remap (struct device *dev, void *cpu_addr,
dma_addr_t dma_handle, size_t size, struct dma_attrs *attrs)
{
const struct dma_map_ops *ops = get_dma_ops(dev);
BUG_ON(!ops);
if (!ops->remap) {
WARN_ONCE(1 , "Remap function not implemented for %pS\n" ,
ops->remap);
return NULL ;
}
return ops->remap(dev, cpu_addr, dma_handle, size, attrs);
}
static void *arm_dma_remap (struct device *dev, void *cpu_addr,
dma_addr_t handle, size_t size,
struct dma_attrs *attrs)
{
struct page *page = pfn_to_page(dma_to_pfn(dev, handle));
pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL);
unsigned long offset = handle & ~PAGE_MASK;
size = PAGE_ALIGN(size + offset);
return __dma_alloc_remap(page, size, GFP_KERNEL, prot,
__builtin_return_address(0 )) + offset;
}
static void *
__dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot,
const void *caller)
{
struct vm_struct *area ;
unsigned long addr;
area = get_vm_area_caller(size, VM_ARM_DMA_CONSISTENT | VM_USERMAP,
caller);
if (!area)
return NULL ;
addr = (unsigned long )area->addr;
area->phys_addr = __pfn_to_phys(page_to_pfn(page));
if (ioremap_page_range(addr, addr + size, area->phys_addr, prot)) {
vunmap((void *)addr);
return NULL ;
}
return (void *)addr;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 和ioremap_page_range()比较像。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· 程序员常用高效实用工具推荐,办公效率提升利器!
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 【译】WinForms:分析一下(我用 Visual Basic 写的)