VisionFive v1下移植xv6

1、目的:兴趣,本人74年,有生之年只想在riscv硬件上真实跑个小系统,自己能明明白地知道它怎么运行的,能够修修补补,退休后有所乐,真心不想跑在qemu上。本来想跑在StarFive2上,然能力不足,突破不了uart模式下加载32K的大小限制,偶然发现VisionFive v1下有更好方式加载运行xv6内核,依据“JH7100 Boot User Guide,Version: V1, Date: 2021-09-30”的


2、运行情况
1710291601884


3、主要修改地方
(1)kernel/memlayout.h加上
1710292392563
(2)kernel/uart.c中
1710292482234UART0改为UART3。
4、问题
运行到main.c时会出现莫名其妙错误,全局初始化变量started在mhartid=1核上会看到意想不到的值,后面全局变量都有同样的问题。

抱歉将VisionFive v1下的问题发在V2论坛里。

哥哥好雅兴。
我现在反正是没这个动脑的动力了。

尤其是 riscv 设计上就给系统层面做了分层。操作系统不是最上层。而且似乎还不是第二层,而是第三层。导致启动一个系统异常麻烦。以前的那些“裸硬件编程”的理解全都不好用了。 :face_exhaling:
话说这个“意想不到”的值有没有可能是因为数据不同步而被刷成别的了?

#include “types.h”
#include “param.h”
#include “memlayout.h”
#include “riscv.h”
#include “defs.h”

volatile static int started = 0;

// start() jumps here in supervisor mode on all CPUs.
void
main()
{
if(cpuid() == 0){
consoleinit();
printfinit();
printf(“\n”);
printf(“xv6 kernel is booting.”);
printf(“\n”);
kinit(); // physical page allocator
kvminit(); // create kernel page table
kvminithart(); // turn on paging
procinit(); // process table
trapinit(); // trap vectors
trapinithart(); // install kernel trap vector
plicinit(); // set up interrupt controller
plicinithart(); // ask PLIC for device interrupts
binit(); // buffer cache
iinit(); // inode table
fileinit(); // file table
virtio_disk_init(); // emulated hard disk
userinit(); // first user process
__sync_synchronize();
started = 1;
} else {
while(started == 0)
;
__sync_synchronize();
printf(“hart %d starting\n”, cpuid());
kvminithart(); // turn on paging
trapinithart(); // install kernel trap vector
plicinithart(); // ask PLIC for device interrupts
}

scheduler();
}
上面代码中,started变量未在其它地方引用,hart0,hart1都会执行到main函数,但started会出现除了0和1以外的第三个值。但当把started变量定义在main函数体内时,就不会出现上述情况,搞不懂。

估计是.bss段没有清零导致的。把started放到.data段试一下,比如

volatile static int started __attribute__((__section__(".data")));

如果没问题了那基本就是这个问题。那么你这个kernel要考虑到给自己的.bss清零的问题。上一个阶段的bootloader只会把你的kernel按照binary的方式加载,它不会管你后面的事情

1 Like

感谢,正如你所言解决了这个问题。
未修改时运行如下:

修改后:


运行如下:

感谢!再学习加载与链接。

1 Like

1、进展:uart3显示问题已解决。运行如下:


分页前后加了printf打印信息:

2、问题:分页后崩溃,已反复看了很多遍分页相关代码,没有看出问题,也查阅了《RISC‑V指令集手册,卷II:特权架构,版本1.10》 PMP、PMA和sv39分页相关章节,找不到问题所在,没有方向,请路过的大侠们指导一下,感谢!
打印了内核页表内容,节选后面部分:

和qemu下运行内核页表比较:

略有不同。

查看“JH7100 Data Sheet V01.01.04-EN (4-21-2021)”中Table 6-1 U74-MC Memory Map,这段:


与这段

上两段意思是:从0x008000_0000开始的32G内存是由0x10_0000_0000映射过来的,且无cache。
问题:xv6内核执行w_satp(MAKE_SATP(kernel_pagetable))分页时,会不会打乱上面的映射呢?刷新TLB时会不会也产生同样的问题呢?
注:Memory attributes: R - Read, W -Write, X - Execute, C - Cacheable, A - Atomics。

这个映射是在硬件实现的,目的是把DDR映射两遍,80000000开始的是正常的cacheable,1000000000开始的是不经过cache的,主要是为了方便写driver给那些非缓存一致性的设备。这里可以不用关心,把80000000当作DDR起始就行。我觉得不是这个映射的问题。首先得确认在JH7100里面你的xv6运行在什么模式下,应该是M模式,因为你是直接从ROM引导的,还没有加openSBI。那么就出问题了,看起来这个内核期待运行在S模式。而QEMU默认是会帮你把openSBI都加载好的,可以直接引导。所以你要么自己实现一个简单的sbi,或者你需要自己build openSBI,然后把xv6当作openSBI的FW_PAYLOAD,在build阶段嵌入到openSBI里面,然后再用ROM加载最终的fw_payload.bin

感谢解答。
1、内核运行在VisionFive v1单板上,不是QEMU中;
2、已切换到S模式下;
1710919746547
3、通过固件更新方式加载在 NOR Flash的0x40000 处,如下图,选择0。


4、运行路径:BootROM->SecondBootLoader->DDRInit->xv6;
5、xv6中有类似的sbi实现。

OK 那么你的control-flow就和QEMU不一样了。那么你直接用qemu -bios xv6.bin启动qemu,然后看一下。这样qemu也是用M mode启动xv6,会尽可能接近jh7100的行为

运行不起来,感谢。

sv39分页,真的搞不定了,感觉没什么,可就是不行。有没有大佬可指导一下,u74下sv39分页,感激不尽!!

在“Memorandum -L2 Cache Coherence”文档有说明Root causes - L2 Cache Coherence (JH7100 SOC)问题,并给出解决办法如下:


1、这个是造成分页不成功的原因吗?
2、上图代码中的dcache_add是什么,对DDR内存来讲就是0x1000000000?
感谢大佬解答下。

  1. 应该不是。你xv6从jh7100 M模式启动,此时cache controller还没有enable。xv6估计也不会去enable
  2. DDR内存被map两次,一次是通过cache controller,一次是bypass cache controller。这个offset就是两个的offset,换言之你可以通过给一个内存地址加上这个offset直接绕过cache controller访问DDR。

我觉得因该不是这个问题。改一下xv6 M模式的部分,在遇到exception的时候把exception打印出来看看

1 Like
  已查到dcache_addr出处"SiFive U74 Core Complex Manual"中的L2 Cache Controller:

1711367095862

感谢解答。

1、xv6没有做cache controller的enable;xv6是从Qspi模式启动的,启动前的ScondBoot和DDRInit有没有做过呢?分页前要不要做cache controllerr的enable?
2、在分页前开了中断,捕捉到的exception:

这是S模式下的外部中断,

1711370956143
与分页应该无关吧?
3、在S模式下,读mcause会死机,也许是我还不知道 怎么处理。

等一下,你xv6具体改了那些东西?有没有一个你的repo给大家看一下你现在的xv6代码?

[quote="ganboing, post:18, topic:4196, full:true"]

等一下,你xv6具体改了那些东西?有没有一个你的repo给大家看一下你现在的xv6代码?
[/quote]

https://github.com/ahczgy/xv6vf1.git。基本没改 :smiley:

破案了,原因是U74的MMU行为和QEMU的MMU是不一样的,xv6根本没有考虑到这种情况

Two schemes to manage the A and D bits are permitted:

• When a virtual page is accessed and the A bit is clear, or is written and the D bit is clear, a page-fault exception is raised.
• When a virtual page is accessed and the A bit is clear, or is written and the D bit is clear, the corresponding bit(s) are set in the PTE. The PTE update is atomic with respect to other accesses to the PTE, and memory access will not occur until the PTE update is visible globally.

For non-leaf PTEs, the D, A, and U bits are reserved for future use and must be cleared by software for forward compatibility. It is important to note the U7 does not automatically set the accessed (A) and dirty (D) bits in a Sv39 Page Table Entry (PTE). Instead, the U7 MMU will raise a page fault exception for a read to a page with PTE.A=0 or a write to a page with PTE.D=0.

本质上就是PTE_APTE_D是用来跟踪使用过的page,不过U7不会自动更新这两个bit,而是让软件来维护。所以你xv6需要把 leaf-PTE 全部都加上PTE_A | PTE_D 不然直接就page-fault寄了

1 Like

感谢大佬,分页成功。