riscv架构下linux4.15实现early打印

在高版本linux6.12.7源码中,early console介绍,可参考《riscv架构下linux6.12.7实现early打印》文章。

1 什么是early打印

适配内核到新的平台,基本环境搭建好之后,首要的就是要调通串口,方便后面的信息打印。
正常流程 init/main.c 中 start_kernel 入口,要到 console_init 之后才能真正打印,前面的打印,都是缓存在 printk 的 ringbuffer 中的。
如果在 console_init 前就异常了,此时就看不到打印信息,为了调试 console_init 前的状态,需要能更早的打印,内核提供了一种 early 打印的方式,尤其是 riscv 平台我们可以直接 ecall 调用 opensbi 的打印,这样 opensbi 适配好之后,这里就可以直接使用。

earlyprintk 的实现依赖于特定的硬件平台,并且通常与特定固件配合使用。
earlyprintk 是一个高级功能,主要用于内核开发和调试。在生产环境中,通常不需要启用此功能,因为它可能会干扰系统的正常启动过程,或暴露潜在的敏感信息。

从 earlyprintk 到串行控制台的转换,通常发生在内核初始化过程中,特别是在 register_console 函数被调用之后。这个函数负责注册串行控制台,并使其成为内核默认的打印信息输出设备。一旦串行控制台被注册,内核就会开始使用它来输出打印信息,而 earlyprintk 则不再被需要。

2 printk函数实现

printk函数代码实现,如下所示:

// 1.riscv-linux-4.15/include/linux/printk.h:
#define pr_info(fmt, ...) \printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)  调用==>// 2.riscv-linux-4.15/kernel/printk/printk.c:
asmlinkage __visible int printk(const char *fmt, ...)
{va_list args;int r;va_start(args, fmt);r = vprintk_func(fmt, args);   调用==>va_end(args);return r;
}// 3.riscv-linux-4.15/kernel/printk/printk_safe.c:
__printf(1, 0) int vprintk_func(const char *fmt, va_list args)
{...return vprintk_default(fmt, args);    调用==>
}// 4.riscv-linux-4.15/kernel/printk/printk.c:
int vprintk_default(const char *fmt, va_list args)
{...r = vprintk_emit(0, LOGLEVEL_DEFAULT, NULL, 0, fmt, args);    调用==>return r;
}// 5.riscv-linux-4.15/kernel/printk/printk.c:
asmlinkage int vprintk_emit(int facility, int level,const char *dict, size_t dictlen,const char *fmt, va_list args)
{...printed_len = log_output(facility, level, lflags, dict, dictlen, text, text_len);...console_unlock();    调用==>...
}// 6.riscv-linux-4.15/kernel/printk/printk.c:
void console_unlock(void)
{...call_console_drivers(ext_text, ext_len, text, len);    调用==>...
}// 7.riscv-linux-4.15/kernel/printk/printk.c:
static void call_console_drivers(const char *ext_text, size_t ext_len,const char *text, size_t len)
{struct console *con;trace_console_rcuidle(text, len); if (!console_drivers)return;for_each_console(con) {  // 遍历console_drivers中每个consoleif (exclusive_console && con != exclusive_console)continue;if (!(con->flags & CON_ENABLED))continue;if (!con->write)continue;if (!cpu_online(smp_processor_id()) &&!(con->flags & CON_ANYTIME))continue;if (con->flags & CON_EXTENDED)con->write(con, ext_text, ext_len);elsecon->write(con, text, len); // 通过console的write函数打印内容}
}

在代码中,从printk开始,层层分析,最后在call_console_drivers函数中,会遍历console_drivers中每个console,并调用console的write函数来完成内容打印。

pr_info ==>
printk ==>
vprintk_func ==>
vprintk_default ==>
vprintk_emit ==>
console_unlock ==>
call_console_drivers ==>
con->write

3 console注册

console结构体定义:

// riscv-linux-4.15/include/linux/console.h:
struct console {char	name[16];void	(*write)(struct console *, const char *, unsigned);int	(*read)(struct console *, char *, unsigned);struct tty_driver *(*device)(struct console *, int *);void	(*unblank)(void);int	(*setup)(struct console *, char *);int	(*match)(struct console *, char *name, int idx, char *options);short	flags;short	index;int	cflag;void	*data;struct	 console *next;
};

通过register_console函数,可以将一个console进行注册,放入console_drivers链表中,如下:

// riscv-linux-4.15/arch/riscv/kernel/setup.c:
void __init setup_arch(char **cmdline_p)
{
#if defined(CONFIG_EARLY_PRINTK)if (likely(early_console == NULL)) {early_console = &riscv_sbi_early_console_dev;register_console(early_console); // 注册early console}
#endif*cmdline_p = boot_command_line;...
}// early console定义
struct console riscv_sbi_early_console_dev __initdata = {.name	= "early",.write	= sbi_console_write,.flags	= CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME,.index	= -1
};// early console的write函数
static void sbi_console_write(struct console *co, const char *buf,unsigned int n)
{int i;for (i = 0; i < n; ++i) {if (buf[i] == '\n')sbi_console_putchar('\r');sbi_console_putchar(buf[i]);}
}// riscv-linux-4.15/arch/riscv/include/asm/sbi.h:
#define SBI_CALL(which, arg0, arg1, arg2) ({			\register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0);	\register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1);	\register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2);	\register uintptr_t a7 asm ("a7") = (uintptr_t)(which);	\asm volatile ("ecall"					\: "+r" (a0)				\: "r" (a1), "r" (a2), "r" (a7)		\: "memory");				\a0;							\
})/* Lazy implementations until SBI is finalized */
#define SBI_CALL_0(which) SBI_CALL(which, 0, 0, 0)
#define SBI_CALL_1(which, arg0) SBI_CALL(which, arg0, 0, 0)
#define SBI_CALL_2(which, arg0, arg1) SBI_CALL(which, arg0, arg1, 0)static inline void sbi_console_putchar(int ch)
{SBI_CALL_1(SBI_CONSOLE_PUTCHAR, ch);
}static inline int sbi_console_getchar(void)
{return SBI_CALL_0(SBI_CONSOLE_GETCHAR);
}

4 SBI_CALL

console的write函数:先调用sbi_console_putchar,再调SBI_CALL。
SBI_CALL实现的功能,可大致理解为:

SBI_CALL(which, arg0, arg1, arg2) 
{			a0寄存器 = (uintptr_t)(arg0);	a1寄存器 = (uintptr_t)(arg1);	a2寄存器 = (uintptr_t)(arg2);	a7寄存器 = (uintptr_t)(which);	执行ecall指令;					
}

SBI_CALL宏,通过在RISC-V处理器上,执行ecall指令来调用一个服务:

  • 它通过将参数,放入特定的寄存器(a0、a1、a2);
  • 并将服务标识符(调用号),放入a7寄存器;
  • 然后,它执行ecall指令,并返回a0寄存器的值作为结果。

ecall系统调用,会触发异常(mcause寄存器定义的异常8或9)。只不过这种异常,是由U或S模式下,程序通过ecall指令,软件触发的异常,主要用于系统调用,实现一些底层调用,例如输出打印信息到串口等。

可以看到,这里定义了很多调用号Timer、Console、IPI、Shutdown等,如下:

// riscv-linux-4.15/arch/riscv/include/asm/sbi.h:
#define SBI_SET_TIMER 0
#define SBI_CONSOLE_PUTCHAR 1
#define SBI_CONSOLE_GETCHAR 2
#define SBI_CLEAR_IPI 3
#define SBI_SEND_IPI 4
#define SBI_REMOTE_FENCE_I 5
#define SBI_REMOTE_SFENCE_VMA 6
#define SBI_REMOTE_SFENCE_VMA_ASID 7
#define SBI_SHUTDOWN 8

在这里,就是:

  • 将调用号1放入a7寄存器,将欲打印字符ch放入a0寄存器,然后CPU执行ecall指令,就会触发一个异常;
  • 然后CPU会处理该异常,由于当前kernel运行在S模式,因此CPU会进入M模式,并跳转到M模式异常处理入口(Open SBI),在固件OpenSBI的处理代码中,会判断当调用号为1时,将字符ch打印出来(打印的方式,可以通过Uart或HTIF)。
    在这里插入图片描述
    在riscv-pk开源项目中,也支持通过ecall指令,来使用Uart或HTIF输出打印信息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/3412.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【论文阅读笔记】基于YOLO和ResNet深度卷积神经网络的结直肠息肉检测

作者&#xff1a;李素琴、吴练练、宫德馨、胡珊、陈奕云、朱晓云、李夏、于红刚 效果视频链接&#xff1a;https://www.xhnj.com/m/video/1008384.htm 小结 从算法的角度来说&#xff0c;作为2020发布的论文&#xff0c;使用的技术是比较落后的了。一个息肉检测项目&#xff0…

win32汇编环境,窗口程序中基础列表框的应用举例

;运行效果 ;win32汇编环境,窗口程序中基础列表框的应用举例 ;比如在窗口程序中生成列表框&#xff0c;增加子项&#xff0c;删除某项&#xff0c;取得指定项内容等 ;直接抄进RadAsm可编译运行。重点部分加备注。 ;以下是ASM文件 ;>>>>>>>>>>>…

Lora理解QLoRA

Parameter-Efficient Fine-Tuning (PEFT) &#xff1a;节约开销的做法&#xff0c;fine-tune少量参数&#xff0c;而不是整个模型&#xff1b; Low-Rank Adaptation (LoRA) &#xff1a;是PEFT的一种&#xff1b;冻结原参数矩阵&#xff0c;只更新2个小参数矩阵。 原文经过对比…

YOLOv5训练长方形图像详解

文章目录 YOLOv5训练长方形图像详解一、引言二、数据集准备1、创建文件夹结构2、标注图像3、生成标注文件 三、配置文件1、创建数据集配置文件2、选择模型配置文件 四、训练模型1、修改训练参数2、开始训练 五、使用示例1、测试模型2、评估模型 六、总结 YOLOv5训练长方形图像详…

基于微信小程序的电子点菜系统设计与实现(KLW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

Titans 架构中的记忆整合:Memory as a Context;Gated Memory;Memory as a Layer

Titans 架构中的记忆整合 Titans 架构中的记忆整合 Memory as a Context(MAC)变体:在处理长序列数据时,将序列分段,对于当前段 S ( t ) S^{(t)}

洛谷P3916 图的遍历

题目描述 给出 N 个点,M 条边的有向图&#xff0c;对于每个点 v&#xff0c;求 A(v) 表示从点 v 出发&#xff0c;能到达的编号最大的点。 输入格式 第 1 行 2 个整数 N,M&#xff0c;表示点数和边数。 接下来 M 行&#xff0c;每行 2 个整数 Ui,Vi​&#xff0c;表示边 (U…

【python】实现图像中的阴影去除 | 方案和代码

去除图像中的阴影是一项复杂的图像处理任务&#xff0c;尤其是当阴影区域与图像的其他部分混合时。阴影的存在会影响图像的颜色平衡和亮度&#xff0c;导致图像分析和理解的困难。 目录 一 安装依赖 二 函数 ① rgb2hsv ② hsv2rgb 三 实现图像中的阴影去除的方法 四 实…

记录一次 centos 启动失败

文章目录 现场1分析1现场2分析2搜索实际解决过程 现场1 一次断电,导致 之前能正常启动的centos 7.7 起不来了有部分log , 关键信息如下 [1.332724] XFS(sda3): Internal error xfs ... at line xxx of fs/xfs/xfs_trans.c [1.332724] XFS(sda3): Corruption of in-memory data…

文件操作:系统IO

文件操作 目录 基本概念Linux文件特点操作方式1-系统IO操作方式2-标准IO两种操作模式的对比 基本概念 什么是文件 简单的说&#xff0c;文件就是存储在硬件磁盘上的数据集合 文件通过什么来标识&#xff1f; 系统中在处理的文件&#xff08;读、写操作&#xff09;的时候…

ComfyUI-PromptOptimizer:文生图提示优化节点

ComfyUI-PromptOptimizer 是 ComfyUI 的一个自定义节点&#xff0c;旨在优化文本转图像模型的提示。它将用户输入的提示转换为更详细、更多样化、更生动的描述&#xff0c;使其更适合生成高质量的图像。无需本地模型。 1、功能 提示优化&#xff1a;优化用户输入的提示以生成…

windows 搭建flutter环境,开发windows程序

环境安装配置&#xff1a; 下载flutter sdk https://docs.flutter.dev/get-started/install/windows 下载到本地后&#xff0c;随便找个地方解压&#xff0c;然后配置下系统环境变量 编译windows程序本地需要安装vs2019或更新的开发环境 主要就这2步安装后就可以了&#xff0…

从玩具到工业控制--51单片机的跨界传奇【3】

在科技的浩瀚宇宙中&#xff0c;51 单片机就像一颗独特的星辰&#xff0c;散发着神秘而迷人的光芒。对于无数电子爱好者而言&#xff0c;点亮 51 单片机上的第一颗 LED 灯&#xff0c;不仅仅是一次简单的操作&#xff0c;更像是开启了一扇通往新世界的大门。这小小的 LED 灯&am…

构建一个简单的深度学习模型

构建一个简单的深度学习模型通常包括以下几个步骤&#xff1a;定义模型架构、编译模型、训练模型和评估模型。下面是一个使用Keras&#xff08;TensorFlow的高级API&#xff09;构建和训练一个简单的全连接神经网络&#xff08;也称为多层感知器&#xff0c;MLP&#xff09;的示…

linux下的NFS和FTP部署

目录 NFS应用场景架构通信原理部署权限认证Kerberos5其他认证方式 命令serverclient查看测试系统重启后自动挂载 NFS 共享 高可用实现 FTP对比一些ftp服务器1. **vsftpd (Very Secure FTP Daemon)**2. **ProFTPD (Professional FTP Daemon)**3. **Pure-FTPd**4. **WU-FTPD (Was…

Python操作Excel——openpyxl使用笔记(3)

3 单元格基本操作 3.1 访问单元格和读写其内容 在前面的例子中&#xff0c;已经简单演示过了向单元格中写入和读取数据。这里进一步提供访问单元格的一些方法。和前面一样&#xff0c;使用工作表的索引方式&#xff0c;可以快速定位一个单元格&#xff1a; import openpyxl w…

【漏洞预警】FortiOS 和 FortiProxy 身份认证绕过漏洞(CVE-2024-55591)

文章目录 一、产品简介二、漏洞描述三、影响版本四、漏洞检测方法五、解决方案 一、产品简介 FortiOS是Fortinet公司核心的网络安全操作系统&#xff0c;广泛应用于FortiGate下一代防火墙&#xff0c;为用户提供防火墙、VPN、入侵防御、应用控制等多种安全功能。 FortiProxy则…

一、1-2 5G-A通感融合基站产品及开通

1、通感融合定义和场景&#xff08;阅读&#xff09; 1.1通感融合定义 1.2通感融合应用场景 2、通感融合架构和原理&#xff08;较难&#xff0c;理解即可&#xff09; 2.1 感知方式 2.2 通感融合架构 SF&#xff08;Sensing Function&#xff09;&#xff1a;核心网感知控制…

头盔识别技术

本项目参考b站视频https://www.bilibili.com/video/BV1EhkiY2Epg/?spm_id_from333.999.0.0&vd_source6c722ac1eba24d4cbadc587e4d1892a7 1.下载miniconda 使用 Miniconda 来管理 Python 环境&#xff08;如 yolov8&#xff09;&#xff0c;就可以通过 conda create -n y…

某讯一面,感觉问Redis的难度不是很大

前不久&#xff0c;有位朋友去某讯面试&#xff0c;他说被问到了很多关于 Redis 的问题&#xff0c;比如为什么用 Redis 作为 MySQL 的缓存&#xff1f;Redis 中大量 key 集中过期怎么办&#xff1f;如何保证缓存和数据库数据的一致性&#xff1f;我将它们整理出来&#xff0c;…