登录 用户中心() [退出] 后台管理 注册
   
您的位置: 首页 >> CLQ工作室开源代码 >> 主题: ⭐有趣的 qemu virt 机器裸机输出字符串的程序     [回主站]     [分站链接]
标题
⭐有趣的 qemu virt 机器裸机输出字符串的程序
clq
浏览(95) + 2024-09-07 14:03:31 发表 编辑

关键字:

[2024-09-22 13:02:41 最后更新]
⭐有趣的 qemu virt 机器裸机输出字符串的程序

--------------------------------------------------------
更新记录:

1.发现裸程序其实也并不一定要依赖名为 "_start" 的函数,其实上就当前的实例,程序会在 qemu 中固定以第一个 c 函数为入口点,而不管它是否是 _start() !
这是我测试了一晚上发现的,询问 ai 后证实了我的想法。那么要在 start 中调用其实子函数就不能写在它前面了?
我实测的结果是这样,而且似乎也不能用提前声明的方式。目前的解决办法是用 extern 可以在 start 之前声明子函数。尚未发现其他能用的方式。

--------------------------------------------------------

qemu 运行一个虚拟机比如 riscv ubuntu 时要加一大堆参数。这里面除了虚拟机的参数外,一般还会包含一个 boot (例如 uboot),然后才到真正的操作系统里去,我一直奇怪,那我一个小程序直接做成 boot 跑不就行了?
这确实是可以的,在嵌入式他们那里据说称之为 “裸机” 程序。一个没什么功能的裸程序是非常简单的,就是纯二进制代码或者理解为纯汇编也可以。
那又好奇了,没有进入操作系统之前是不可能操作 printf 或者是 riscv ecall 的呀,那是怎么输出字符串的?

带着这个疑问,我找遍了整个互联网以及各个 ai 。这个问题应该很少人问,解决就更少了,各个 ai 的回答也都是错的,不过倒是有不少可参考的信息。

最后在一位大神的博客看到了,一串非常神奇的几句代码,居然真的能行!但大神没有留下任何解释,又参考了不少文章终于知道了。

首先这只能在 qemu 的 virt 中运行。virt 是一个模拟出来的物理设备,它有一个串口设备,往里写就能输出在 qemu 里看到了 ...

就这么简单。另外只能输出一个字符,因为要等待别人接收前一个字符,连续输出需要写个驱动 ... 不过这个所谓驱动比 windows 下那么麻烦的好太多了,其实只是写完后判断一个,再重置设备就可以了,逻辑代码不超过三行。

好了,代码如下。(不过,因为 gcc 这些编译器会附带很多别的东西,要编译出纯裸机程序需要技巧的)

--------------------------------------------------------

#include |stdint.h|
void _start() {
volatile uint8_t *p = (uint8_t *)(uintptr_t)0x10000000;
*p++ = 'A';
*p++ = 'B';
//while (1);
}

--------------------------------------------------------
在以下网址
https://blog.csdn.net/Quner6/article/details/136200458

另,可参考
https://blog.csdn.net/df12138/article/details/120528750
“RISC-V from scratch 4: 写 UART 驱动”
它翻译自一位老外的
https://twilco.github.io/riscv-from-scratch/2019/07/28/riscv-from-scratch-4.html
它的编译命令为
riscv64-unknown-elf-gcc -g -ffreestanding -O0 -Wl,--gc-sections \
-nostartfiles -nostdlib -nodefaultlibs -Wl,-T,riscv64-virt.ld \
crt0.s main.c ns16550a.c
————————————————
其中 riscv64-virt.ld 就是 link 文件,指挥 ld 操作的。

http://admin.guyuehome.com/44713
“开发一个RISC-V上的操作系统(三)—— 串口驱动程序(UART)”
这也是一篇非常有趣的文章。


--------------------------------------------------------
--------------------------------------------------------



3.2 编译

riscv64-linux-gnu-gcc -ffreestanding -nostdlib -Wl,-Ttext=0x80000000 -O2 a.c

1

4. 使用qemu仿真运行riscv裸机程序

启动仿真

qemu-system-riscv32 -nographic -M virt -bios none -kernel a.out

1

运行结果如下:

$ qemu-system-riscv32 -nographic -M virt -bios none -kernel a.out
A
————————————————
这位仁兄和我一样没有按一般教程去自己编译 riscv 的编译器,而是用的系统自带的安装就行了。本来嘛,写程序的花了一大堆时间去折腾编译器,只能说开源的人太闲,头发太长 ...

2. 工具准备

# for riscv64-linux-gnu-gcc
sudo apt-get install gcc-riscv64-linux-gnu

# for qemu-system-riscv32 / qemu-system-riscv64
sudo apt install qemu-system-misc
————————————————

代码中的 0x10000000 就是串口设备的访问地址。


=============================================
编译器部分就啰嗦多了,比较忙先说几点不常见的,以后再补充其他。

1.首先 gcc 编译为纯 bin的步骤可以分成好几步的,各个参数一起上的话有些未必能编译过去,这时候可以拆分。

2.可以编译汇编代码。

3.对于 gcc 来说,后缀 .s 和 .S 是不同的,大写的据说可以用宏。

4.有些示例中的 link 文件可以独立调用。

5.据说带有 linux 字样的编译器加上 -nostdlib 这些参数就和 unknown 类型的 gcc 是一样的了。未测试。

--------------------------------------------------------
Bare-metal C Program

You need to make an ELF file without headers, which starts at the address 0x8000_0000 by the following instructions:

// Make an assembly file from a C file.
$ riscv64-unknown-elf-gcc -S -nostdlib foo.c

// Make a binary file from an assembly file with start position 0x8000_0000.
$ riscv64-unknown-elf-gcc -Wl,-Ttext=0x80000000 -nostdlib -o foo foo.s

// Remove headers from a binary file.
$ riscv64-unknown-elf-objcopy -O binary foo foo.text

--------------------------------------------------------
link 的内容可能是

SECTIONS
{
. = 0x80000000;
.text : { *(.text*) }
.data : { *(.data*) }
.bss : { *(.bss*) }
. = ALIGN(8);
stack_top = .;
}


--------------------------------------------------------
但似乎只有 riscv64-unknown-elf-gcc 这样 的程序

riscv64-unknown-elf-as boot.S -o boot.o
riscv64-unknown-elf-ld boot.o -o boot

--------------------------------------------------------

这个 link.ld 可以独立调用吗

link.ld 是一个链接脚本,它用于指定如何将多个目标文件(例如,你的汇编代码和任何其他需要的库)链接在一起,以生成一个可执行文件。链接脚本本身不能独立调用,它需要在链接器(如 ld)的上下文中使用。

链接脚本的主要作用是定义输出文件的布局,包括各个段(如代码段、数据段、BSS段等)的起始地址和大小,以及如何将输入文件中的段映射到输出文件中。

在你的例子中,link.ld 脚本定义了一个简单的布局,其中代码段从地址 0x80000000 开始,数据段和BSS段紧随其后。链接器使用这个脚本来确定如何将 boot.S 中的代码和数据段映射到输出文件 boot.elf 中。

如果你想要独立调用链接脚本,你需要使用链接器(如 ld)来执行它。例如,你可以使用以下命令来链接你的程序:
riscv64-unknown-elf-ld -T link.ld -o boot.elf boot.o
在这个命令中,-T link.ld 选项告诉链接器使用 link.ld 脚本,-o boot.elf 选项指定输出文件的名称,boot.o 是输入文件(你的汇编代码已经汇编成目标文件)。









clq
2024-09-07 16:28:37 发表 编辑

https://m.sohu.com/a/729343141_827544/

大神用Scratch手搓RISC-V模拟器,成功运行Linux内核
OSC开源社区
2023-10-18 18:45
+订阅

出品 | OSC开源社区(ID:oschina2013)

近日小编在网上冲浪时被一个项目震惊到了 —— 完全用 Scratch 代码编写了成功运行 Linux 内核的模拟器。

Scratch 是以积木块为基础的可视化程序设计语言开发平台,通过点击并拖拽的方式可视化完成编程。

作者介绍称,他用 Scratch 编写了 RISC-V (rv32ima) 指令集,然后将其作为模拟器运行 Linux 6.1 内核。该模拟器基于纯 C 实现的 mini-rv32ima 模拟器构建。

viahttps://scratch.mit.edu/projects/892602496

下面是项目的运行截图:

相关链接:https://github.com/cnlohr/mini-rv32ima





总数:1 页次:1/1 首页 尾页  
总数:1 页次:1/1 首页 尾页  


所在合集/目录
riscv 更多



发表评论:
文本/html模式切换 插入图片 文本/html模式切换


附件:



NEWBT官方QQ群1: 276678893
可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.

Copyright © 2005-2020 clq, All Rights Reserved
版权所有
桂ICP备15002303号-1