目录
- 编译框架
- 模块初始化
- pstore子系统
- ramoops模块
- 初始化
- 实例化
- 注册回调
- 数据结构
- pstore_blk模块
- pstore_zone模块
- 测试
- 扩展
- 调试
编译框架
目标结构
linux_5.10/fs/pstore/
├── blk.c
├── ftrace.c
├── inode.c // 核心1
├── internal.h
├── Kconfig
├── Makefile
├── platform.c // 核心2,负责:注册分发工作、record写入
├── pmsg.c
├── ram.c // ram后端1
├── ram_core.c // ram后端2
└── zone.c
Makefile文件
# pstore文件系统
obj-$(CONFIG_PSTORE) += pstore.o
pstore-objs += inode.o platform.o# ramoops模块,后端
ramoops-objs += ram.o ram_core.o
obj-$(CONFIG_PSTORE_RAM) += ramoops.o
可知:
-
pstore
模块,根据配置CONFIG_PSTORE=y
,内嵌pstore
;核心实现文件:
inode.o platform.c
。 -
ramoops
模块,根据配置CONFIG_PSTORE_RAM=m
,生成模块ramoops.ko
;实现文件:
ram.c、ram_core.c
模块初始化
pstore子系统
先查找模块初始化入口,文件:linux_5.10/fs/pstore/platform.c
late_initcall(pstore_init); # late_initcall 阶段执行;static int __init pstore_init(void)pstore_choose_compression(); # 选择压缩算法,加载时如果没有指定则跳过if (!compress) return;allocate_buf_for_compression(); # 申请压缩用的缓存,暂不展开ret = pstore_init_fs();// linux_5.10/fs/pstore/inode.c //
int __init pstore_init_fs(void)sysfs_create_mount_point(fs_kobj, "pstore"); # 在sys下创建挂载目录:pstore,即完整路径:/sys/fs/pstoreregister_filesystem(&pstore_fs_type); # 注册pstore文件系统static struct file_system_type pstore_fs_type = {.owner = THIS_MODULE,.name = "pstore",.mount = pstore_mount, # 关注mount实现.kill_sb = pstore_kill_sb,
};
至此,ramoops模块初始化完成,主要完成如下工作:
- 选择压缩算法并申请内存;
- 注册
pstore
文件系统;
ramoops模块
初始化
跟读文件:linux_5.10/fs/pstore/ram.c
postcore_initcall(ramoops_init);static int __init ramoops_init(void)struct ramoops_platform_data pdata;ramoops_register_dummy();if (!mem_size) // 如果附带参数中没有指定mem_size,退出return;pr_info("using module parameters\n"); // 打印:memset(&pdata, 0, sizeof(pdata)); // 使用一个局部变量pdata,临时完成参数配置pdata.mem_size = mem_size; // 模块参数pdata.record_size = record_size; // 模块参数,默认4KB;... // 其他参数配置dummy = platform_device_register_data(NULL, "ramoops", -1, &pdata, sizeof(pdata)); // 作为全局变量dummy的平台数据platform_driver_register(&ramoops_driver); // 平台驱动:ramoops_driver// 跟进平台驱动:ramoops_driver
//--------------------------------------------------------------------------------------------------
static const struct of_device_id dt_match[] = {{ .compatible = "ramoops" }, // 匹配DTS中节点中compatible字段为ramoops的设备{}
};static struct platform_driver ramoops_driver = {.probe = ramoops_probe,.remove = ramoops_remove,.driver = {.name = "ramoops", // 匹配同名平台设备.of_match_table = dt_match,},// 跟进平台驱动:ramoops_driver,probe实现(基于平台总线的设备与驱动匹配测试)
//--------------------------------------------------------------------------------------------------
static int ramoops_probe(struct platform_device *pdev) 。。。struct ramoops_context *cxt = &oops_cxt; // oops能力集。。。if (cxt->max_dump_cnt) { // 仅能有一个ramoops实例pr_err("already initialized\n");goto fail_out; if (dev_of_node(dev) && !pdata) { // 如果设备来自dts定义,解析节点,暂不展开pdata = &pdata_local; 。。。cxt->size = pdata->mem_size; // cxt 初始化cxt->phys_addr = pdata->mem_address; // 物理地址... cxt->record_size = pdata->record_size; // 记录大小... // 其他参数paddr = cxt->phys_addr; dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size - cxt->pmsg_size; // dump文件大小计算ramoops_init_przs("dmesg", dev, cxt, &cxt->dprzs, &paddr, dump_mem_sz, cxt->record_size, &cxt->max_dump_cnt, 0, 0); if (mem_sz == 0 || record_size == 0) { // 如果没有内存剩余或record_size参数值为0,退出return 0; if (record_size < 0) { // 参数record_size如果为负数:重新计算...} else { *cnt = mem_sz / record_size; // 换算出record个数;...if (*paddr + mem_sz - cxt->phys_addr > cxt->size) { // 检查mem_sz是否超出总大小,注:paddr = cxt->phys_addr...zone_sz = mem_sz / *cnt; prz_ar = kcalloc(*cnt, sizeof(**przs), GFP_KERNEL); for (i = 0; i < *cnt; i++) {if (*cnt == 1)label = kasprintf(GFP_KERNEL, "ramoops:%s", name); // record 文件名,按顺序:ramoops:dmsg,ramoops:dmsg(1/n),ramoops:dmsg(2/n),...,ramoops:dmsg(n-1/n)...
* prz_ar[i] = persistent_ram_new(*paddr, zone_sz, sig, &cxt->ecc_info, cxt->memtype, flags, label); // 内存映射,关联擦物理内存 *paddr += zone_sz;prz_ar[i]->type = pstore_name_to_type(name); // 名字转换成对应类型ramoops_init_prz("console", dev, cxt, &cxt->cprz, &paddr, cxt->console_size, 0); ramoops_init_przs("ftrace", dev, cxt, &cxt->fprzs, &paddr, cxt->ftrace_size, -1, &cxt->max_ftrace_cnt, LINUX_VERSION_CODE, (cxt->flags & RAMOOPS_FLAG_FTRACE_PER_CPU) ? PRZ_FLAG_NO_LOCK : 0); ramoops_init_prz("pmsg", dev, cxt, &cxt->mprz, &paddr, cxt->pmsg_size, 0); cxt->pstore.data = cxt; // psi私有数据关联cxtcxt->pstore.flags = 0;if (cxt->max_dump_cnt) {cxt->pstore.flags |= PSTORE_FLAGS_DMESG;... // 其他参数
* pstore_register(&cxt->pstore); // pstore 注册,已完善的oops_cxt;mem_size = pdata->mem_size; // 更新模块参数,以便调试查看:/sys/module/ramoops/parameters/mem_address = pdata->mem_address;... // 其他参数pr_info("using 0x%lx@0x%llx, ecc: %d\n", // 初始化完成打印// 注
//--------------------------------------------------------------------------------------------------
// 全局变量
static struct platform_device *dummy; // dummy,平台设备
struct pstore_info *psinfo; // psinfo,能力集(open、write,...);
static struct ramoops_context oops_cxt = { // oops_cxt,.pstore = { // 与上面的psinfo 紧密关联;.owner = THIS_MODULE,.name = "ramoops",.open = ramoops_pstore_open,.read = ramoops_pstore_read,.write = ramoops_pstore_write,.write_user = ramoops_pstore_write_user,.erase = ramoops_pstore_erase,},
};
总结ramoops
驱动入口及完成的工作:
-
oops_cxt
是ramoops
的基础能力集,其提供了最基本的文件读写方法,实现了数据的读写管理; -
postcore_initcall
初始化函数,具体实现方法由宏CONFIG_PSTORE_RAM
的值决定:y
,即ramoops
内嵌到内核中,此时实现方法为__define_initcall(fn, 2)
,属于靠前的初始化阶段;m
,即ramoops
编译成模块,此时实现方法为module_init
;
-
dummy
是一个临时使用的平台设备,用以辅助ramoops的初始化。dummy生效有以下几个条件:- 模块参数
mem_size
,不能为0。如果为零,相当于告知ramoops:没有内存可保存dump信息;
当然,
mem_size
也是相对于模块参数mem_address
而言的。总结就是:已知ramoops初始化至少需要两个参数:mem_address、mem_size
。 - 模块参数
-
ramoops_driver
是一个平台驱动,是ramoops
模块的载体。需要关注
ramoops_driver
这个平台驱动的其probe
实现,即如何与平台设备匹配的,有以下两种方式:- 直接创建的平台设备,如:上面提及的平台设备
dummy
(使用platform_device_register_data创建平台设备); - 基于平台设备模式的DTS中定义的of设备;
- 直接创建的平台设备,如:上面提及的平台设备
-
ramoops_init_przs
,是具体前端的实例化过程实现,实例化为prz
(struct persistent_ram_zone)对象;该对象的特性在章节《数据结构》中说明,而涉及内存申请、映射的细节在章节《前端实例化》中;
-
pstore_register
,向各前端所属子系统注册pstore回调,各前端的回调所有不同,细节较多,在章节《前端注册》中详细说明,以下列出大致关联:- kmesg,所属:printk,回调注册:
kmsg_dump_register
,回调对象:struct kmsg_dumper
,实体:pstore_dumper
; - console,所属:printk,回调注册:
register_console
,回调对象:struct console
,实体:pstore_console
; - 其他,暂不展开;
- kmesg,所属:printk,回调注册:
实例化
以persistent_ram_new
为入口,跟读ramoops
模块如何针对不同前端进制实例化工作,参考文件:linux_5.10/fs/pstore/ram_core.c
struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size, u32 sig, struct persistent_ram_ecc_info *ecc_info, unsigned int memtype, u32 flags, char *label)struct persistent_ram_zone *prz;prz = kzalloc(sizeof(struct persistent_ram_zone), GFP_KERNEL);...persistent_ram_buffer_map(start, size, prz, memtype); // 映射物理内存prz->paddr = start;prz->size = size; if (pfn_valid(start >> PAGE_SHIFT)) // 检查mem_addr所在页帧是否落在DRAM范围内prz->vaddr = persistent_ram_vmap(start, size, memtype); // 在DRAM范围内,使用vmappage_start = start - offset_in_page(start); // 页对齐处理:start、countpage_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE); if (memtype) // 0(默认),pgprot_writecombine,prot = pgprot_noncached(PAGE_KERNEL); // 页属性:无缓存elseprot = pgprot_writecombine(PAGE_KERNEL);pages = kmalloc_array(page_count, sizeof(struct page *), GFP_KERNEL); // kmalloc申请多个库 for (i = 0; i < page_count; i++) {phys_addr_t addr = page_start + i * PAGE_SIZE;pages[i] = pfn_to_page(addr >> PAGE_SHIFT);vaddr = vmap(pages, page_count, VM_MAP, prot); // 虚拟地址:从物理页帧映射到虚拟内存kfree(pages);return vaddr + offset_in_page(start); // 返回映射后的虚拟地址elseprz->vaddr = persistent_ram_iomap(start, size, memtype, prz->label); // 在DRAM范围外,使用iomaprequest_mem_region(start, size, label ?: "ramoops");if (memtype)va = ioremap(start, size);elseva = ioremap_wc(start, size); return va; ... prz->buffer = prz->vaddr; // buffer信息保存prz->buffer_size = size - sizeof(struct persistent_ram_buffer); persistent_ram_post_init(prz, sig, ecc_info); // 内存引导初始化persistent_ram_init_ecc(prz, ecc_info); // ECC初始化sig ^= PERSISTENT_RAM_SIG; if (prz->buffer->sig == sig) {if (buffer_size(prz) == 0) { pr_debug("found existing empty buffer\n");return 0; pr_debug("attached %s 0x%zx@0x%llx: %zu header, %zu data, %zu ecc (%d/%d)\n", // 打印(Debug等级):内存信息return prz; // 返回prz;
总结ramoops
实例化的核心工作是完成一个或多个prz的创建,其中过程涉及的工作:
-
prz
实例化,prz
只是个门面,承载的是ram、blk或mtd,面对的是ramoops
; -
prz
内存管理,依据内存范围、属性,申请与映射方式有所不同:-
内存申请,依据类型内存可分为
DRAM
与寄存器
,两者的区别如下:DRAM
,使用kmalloc_array
申请物理页;寄存器
,使用request_mem_region
声明资源使用;
另外,依据模块参数
mem_type
与平台是否支持,在申请页时会附带属性:noncached
或writecombine
。 -
内存映射,依据类型内存可分为
DRAM
与寄存器
,两者的区别如下:DRAM
,使用vmap
映射,物理内存的映射;寄存器
,使用iomap
映射,I/O内存的直接映射;
总结
prz
内存管理策略:-
物理内存,使用
kmalloc_array
+vmap
,最终得到虚拟地址; -
I/O内存,使用
request_mem_region
+iomap
,最终得到虚拟地址;
-
-
prz
读写管理,使用persistent_ram_buffer
完成数据的环形覆盖读写;
注册回调
以pstore_register
为入口,跟读ramoops
模块如何依据不同前端向相应子系统注册回调,参考文件:linux_5.10/fs/pstore/ram_core.c
// 跟进注册实现:pstore_register
//--------------------------------------------------------------------------------------------------
//linux_5.10/fs/pstore/platform.c
int pstore_register(struct pstore_info *psi)if (backend && strcmp(backend, psi->name)) { // 后端检查,此时为:ramoopsif (!psi->flags) { // 前端检查 if (!psi->read || !psi->write) { // 能力集检查...if (!psi->write_user)psi->write_user = pstore_write_user_compat; // 写实现psinfo = psi; // 全局变量:psinfomutex_init(&psinfo->read_mutex); // 锁初始化:读、bufsema_init(&psinfo->buf_lock, 1); if (psi->flags & PSTORE_FLAGS_DMESG) { pstore_dumper.max_reason = psinfo->max_reason;pstore_register_kmsg(); // 注册dmsg前端
* kmsg_dump_register(&pstore_dumper); // 注册并设置回调dump方法:pstore_deumpif (psi->flags & PSTORE_FLAGS_CONSOLE)pstore_register_console(); // 注册console前端...
* register_console(&pstore_console); // 注册并设置回调wirte方法:pstore_console_write... // 其他前端 pstore_timer_kick(); // 启动timerif (pstore_update_ms < 0) // 如果模块参数update_ms为负数,退出return;mod_timer(&pstore_timer, jiffies + msecs_to_jiffies(pstore_update_ms)); // 定时update_ms... pr_info("Registered %s as persistent store backend\n", psi->name); // 前端注册成功打印:// 向各子系统中注册回调:pstore_dump
//--------------------------------------------------------------------------------------------------
static struct kmsg_dumper pstore_dumper = {.dump = pstore_dump,
};static void pstore_dump(struct kmsg_dumper *dumper, num kmsg_dump_reason reason) ’... why = kmsg_dump_reason_str(reason); if (down_trylock(&psinfo->buf_lock)) {if (pstore_cannot_wait(reason)) { // 如果pstore中不做等待的事件类型...oopscount++;while (total < kmsg_bytes) {struct pstore_record record;pstore_record_init(&record, psinfo); // 创建一个record,并初始化record.type = PSTORE_TYPE_DMESG;... // 其他参数 header_size = snprintf(dst, dst_size, "%s#%d Part%u\n", why, oopscount, part); // 文件加头处理dst_size -= header_size; kmsg_dump_get_buffer(dumper, true, dst + header_size, dst_size, &dump_size); // 实质数据if (big_oops_buf) { // 如果文件过大,压缩处理zipped_len = pstore_compress(dst, psinfo->buf, header_size + dump_size, psinfo->bufsize); psinfo->write(&record); // 写操作total += record.size;part++;up(&psinfo->buf_lock); // 对上down_trylock static struct console pstore_console = {.write = pstore_console_write,.index = -1,
}; static void pstore_console_write(struct console *con, const char *s, unsigned c)struct pstore_record record;... pstore_record_init(&record, psinfo);record.type = PSTORE_TYPE_CONSOLE;record.buf = (char *)s;record.size = c;psinfo->write(&record);
总结ramoops
注册工作的流程,其中过程涉及的重要工作:
-
psi
,是向pstore框架注册和管理的对象; -
psinfo
,全局变量初始化,指示ramoops是否已经注册并防止重入; -
锁初始化,涉及两个锁:
psinfo->read_mutex
,psinfo 读锁;psinfo->buf_lock
,psinfo buf数据锁;
-
psinfo->flags
,前端类型的位置映射。在注册过程中将会遍历并相应创建实例:-
PSTORE_FLAGS_DMESG
, dmesg前端,使用kmsg_dump_register
向prink注册struct kmsg_dumper
类型的回调:pstore_dumper; -
PSTORE_FLAGS_CONSOLE
,console前端,使用register_console
向printk注册struct console
类型的回调:pstore_console; -
PSTORE_FLAGS_FTRACE
,ftrace前端,使用register_ftrace_function
向ftrace注册struct ftrace_ops
类型的回调:pstore_ftrace_ops;使用
debugfs_create_{dir,file}
在debugfs挂载路径下创建节点:xxx/pstore/record_ftrace
; -
PSTORE_FLAGS_PMSG
,pmsg前端,使用register_chrdev
创建一个字符设备(主设备号随机)并初始化其集:psg_fops;使用
{clase,device}_create
创建了设备节点:dev/pmsg0
-
-
pstore_timer_kick
,启动定时。该定时器定期检查pstore_new_entry
是否置位并提交工作到对列pstore_dowork
最终创建新record;
数据结构
由以上章节梳理出ramoops模块主要数据结构的关系图如下:
总结ramoops
主要数据结构,对其中关键对象进制说明:
-
ramoops_diver
,基于平台驱动,是模块的入口; -
struct persistent_ram_zone
,即prz
。用于管理内存,依据不同前端实例化,手段为:- 申请一个
prz
:ramoops_init_prz
- 申请多个
prz
:ramoops_init_przs
;
- 申请一个
-
struct pstore_info
,即psi
。ramoops的能力集包含:name
,后端名,固定为:“ramoops”;open
、close
,record文件的打开、关闭方法;read
、write
、erase
,record文件的读、写、清空方法;
-
struct ramoops_context
,即cxt
,ramoops对象,是关联其他数据的枢纽是最关键的数据结构;oops_cxt
是cxt的一个实例,是ramoops模块的一个全局变量;pstore
是cxt的一个成员,是接入pstore子系统的入口;
注:
psore->data
在驱动probe阶段与cxt绑定,在后续record文件操作(读、写)中使用。
pstore_blk模块
待跟进
pstore_zone模块
待跟进
测试
-
将dts中已定义了ramoops
[root@milkv-duo]~# ll /sys/devices/platform/9fd00000.ramoops/ total 0 drwxr-xr-x 2 root root 0 Jan 1 00:01 ./ drwxr-xr-x 79 root root 0 Jan 1 00:00 ../ lrwxrwxrwx 1 root root 0 Jan 1 00:01 driver -> ../../../bus/platform/drivers/ramoops/ -rw-r--r-- 1 root root 4096 Jan 1 00:01 driver_override -r--r--r-- 1 root root 4096 Jan 1 00:01 modalias lrwxrwxrwx 1 root root 0 Jan 1 00:01 of_node -> ../../../firmware/devicetree/base/reserved-memory/ramoops/ lrwxrwxrwx 1 root root 0 Jan 1 00:01 subsystem -> ../../../bus/platform/ -rw-r--r-- 1 root root 4096 Jan 1 00:01 uevent [root@milkv-duo]~# [root@milkv-duo]~# dmesg |tail [ 21.504624] bm-dwmac 4070000.ethernet eth0: Link is Down [ 35.841599] bm-dwmac 4070000.ethernet eth0: Link is Up - 100Mbps/Full - flow control rx/tx [ 37.888625] bm-dwmac 4070000.ethernet eth0: Link is Down [ 43.824771] ramoops: using module parameters [ 43.826552] pstore: Using crash dump compression: deflate [ 43.826622] printk: console [ramoops-1] enabled [ 43.826634] pstore: Registered ramoops as persistent store backend [ 43.826646] ramoops: using 0x100000@0x9fd00000, ecc: 0 [ 43.826968] ramoops: already initialized # 提示已初始化,退出probe [ 43.831937] ramoops: probe of ramoops failed with error -22
-
无dts,加载时附带参数
[root@milkv-duo]~# ll /sys/devices/platform/ramoops/ total 0 drwxr-xr-x 2 root root 0 Apr 11 03:17 ./ drwxr-xr-x 78 root root 0 Jan 1 1970 ../ lrwxrwxrwx 1 root root 0 Apr 11 03:17 driver -> ../../../bus/platform/drivers/ramoops/ -rw-r--r-- 1 root root 4096 Apr 11 03:17 driver_override -r--r--r-- 1 root root 4096 Apr 11 03:17 modalias lrwxrwxrwx 1 root root 0 Apr 11 03:17 subsystem -> ../../../bus/platform/ -rw-r--r-- 1 root root 4096 Apr 11 03:17 uevent [root@milkv-duo]~# [root@milkv-duo]~# dmesg |tail [ 209.152219] sysrq: Changing Loglevel [ 209.156272] ttyS ttyS0: 3 input overrun(s) [ 209.163388] sysrq: Loglevel set to 7 [ 217.217505] sysrq: Changing Loglevel [ 217.222373] sysrq: Loglevel set to 7 [ 231.098897] ramoops: using module parameters [ 231.106996] pstore: Using crash dump compression: deflate [ 231.112913] printk: console [ramoops-1] enabled [ 231.118193] pstore: Registered ramoops as persistent store backend [ 231.124866] ramoops: using 0x100000@0x9fd00000, ecc: 0
扩展
-
request_mem_region()
是一个在 Linux 内核中用于内存管理的函数,它的主要作用是请求指定的物理内存区域供设备驱动程序或其他内核组件使用。具体来说:- 内存区域声明: 当一个设备驱动或其他内核模块需要访问特定的物理内存区域时,如设备的硬件寄存器或共享内存,它会调用
request_mem_region()
函数来声明对该区域的使用权。这样做可以确保内核知道这块内存区域已经被某个模块“预定”,防止其他模块误用或冲突。 - 资源保护: 通过调用
request_mem_region()
,内核可以防止其他驱动程序或模块意外覆盖或干扰请求的内存区域。这样可以确保在多设备、多驱动共存的复杂环境中,内存资源得到妥善管理和保护,避免数据损坏或系统崩溃。 - 调试辅助: 函数接受一个
label
参数,该参数用于为请求的内存区域提供一个可读的标签或名称。当系统日志中报告内存相关的问题时,这个标签有助于快速识别问题所涉及的特定内存区域,便于调试和故障排查。 - 权限管理: 请求内存区域后,内核会赋予请求者对这片内存的访问权限。在某些情况下,内核可能会要求驱动程序进一步使用
ioremap()
函数将物理地址映射到内核虚拟地址空间,以便驱动程序能够通过虚拟地址安全、有效地访问这片物理内存。 - 资源释放: 当设备驱动不再需要访问该内存区域时,应调用
release_mem_region()
函数释放先前请求的资源,以便其他驱动或模块可以重新使用这片内存。
总结来说,request_mem_region()
是Linux内核中用于管理物理内存资源的重要接口,它确保了多个驱动程序或内核模块在访问共享内存资源时的互斥性和安全性,是构建健壮、稳定的设备驱动程序不可或缺的一部分。
- 内存区域声明: 当一个设备驱动或其他内核模块需要访问特定的物理内存区域时,如设备的硬件寄存器或共享内存,它会调用
-
在 Linux 内核中,
pgprot_noncached
和pgprot_writecombine
都是用于定义页面保护属性的宏,主要用于设置页面的缓存属性。它们分别表示两种不同的页面属性设置:-
pgprot_noncached
:表示页面是非缓存的,即在对该页面进行读写时不会使用缓存。通常用于需要直接和硬件设备进行交互的内存页面,以避免缓存带来的影响。 -
pgprot_writecombine
:表示页面使用写组合属性,即页面被标记为写组合缓存属性。写组合缓存是一种缓存策略,通常用于提高写入性能。在写组合缓存中,系统先将写入操作积累在缓冲区中,然后一次性写入内存,减少了直接写入内存的频率,从而提高了性能。总结来说,
pgprot_noncached
适用于需要避免缓存影响的内存页面,而pgprot_writecombine
主要用于提高写入性能的内存页面。选择使用哪种页面保护属性取决于具体的硬件设备要求或性能需求。
-
-
__builtin_constant_p
是 GCC(GNU Compiler Collection)提供的一个内建函数,用于在编译时检查表达式是否是常量表达式。这个能力允许程序员编写更高效或条件性的代码,特别是在宏和内联函数中,可以根据表达式是否为常量来选择不同的执行路径或优化策略。
调试
-
编译环境,初始化
source build/milkvsetup.sh# Duo S source device/milkv-duos-sd/boardconfig.sh defconfig cv1813h_milkv_duos_sd
-
Server,资源更新
cp -rf ./linux_5.10/build/cv1813h_milkv_duos_sd/fs/pstore/ramoops.ko ~/tftp/ cp -rf linux_5.10/build/cv1813h_milkv_duos_sd/lib/reed_solomon/reed_solomon.ko ~/tftp/ cp -rf ./install/soc_cv1813h_milkv_duos_sd/rawimages/boot.sd ~/tftp/
-
Device,资源更新
tftp -b 65536 -gr ramoops.ko -l /mnt/system/ko/ramoops.ko 192.168.8.108 tftp -b 65536 -gr reed_solomon.ko -l /mnt/system/ko/reed_solomon.ko 192.168.8.108 tftp -b 65535 -gr boot.sd -l /mnt/boot/boot.sd 192.168.8.108
-
Device,测试环境搭建
# 调试系统日志等级 echo 8 > /proc/sysrq-trigger# 挂载pstore文件系统 mount -t pstore pstore /sys/fs/pstore# 加载Pstore与依赖模块 insmod /mnt/system/ko/reed_solomon.ko insmod /mnt/system/ko/ramoops.ko mem_address=0x9FD00000 mem_size=0x100000 mem_type=1
-
Device,测试Panic
# Panic发生3S后重启 echo 3 > /proc/sys/kernel/panic # 触发panic echo c > /proc/sysrq-trigger
-
Device,动态日志调试
mkdir -p /data/debugfs && mount -t debugfs none /data/debugfsecho -n 'file fs/pstore/ram.c +p' > /data/debugfs/dynamic_debug/control