Linux 核心專題 - 問題紀錄
rpmsg_client_sample
這個是位在 linux/samples
目錄底下的一支範例程式,可以編譯成核心模組。這個模組現在的編譯方法是連同核心一起編譯成動態模組,並且目前可以正常掛載並卸除在 Milk-V 的 linux 環境當中。但是現在還無法驗證這個 driver 模組是否可以運作,因為我還沒找到其相對應的 device ,照 Linux 的說明書文件來看,這個 device 的名稱叫作 channel
, 是與另一個核的溝通界面。 所以接著可能要觀察 milkv-duo SoC 的 dts
檔案。
RPMsg-lite
在這個專案中,有個需要引入的檔案名為 rpmsg_platform.c
(下圖中的 platfrom.c ), 專案有因應不同的 SoC 分別製作屬於他們的 rpmsg_platform.c
, 但是顯然 cvitek 的 cv1812cp
並未被涵蓋在裡面,因此我必須製作屬於 milkv-duo256m 的 rpmsg_platform.c
。
目前發現我只須實作 3 個方法,就能支援 RPMsg-lite
專案的所有功能。這三個函式分別是 platform_notify
, platform_init_interrupt
, platform_deinit_interrupt
. 而其中這三個函式與處理器相關的程式碼,分別是 觸發、啟動、停用 關於 Virtio 的中斷事件。
因此我現在想要研究關於 milkv-duo256m 所採用的 cv1812cp
中 Virtio 的機制,來找出實作 RPMsg-lite
的方法。但是我在想最快的方法是,確認 rpmsg_client_sample 可以正常運作 , 如果這個模組可以運作也就代表下層的 Virtio 以及 Mailbox 這兩個機制是有正常運行的,並且我就有機會實作 RPMsg-lite
。
Image Not Showing
Possible Reasons
- The image was uploaded to a note which you don't have access to
- The note which the image was originally uploaded to has been deleted
Learn More →
linux-zephyr rpmsg on milkv-duo
論壇文章: link
modified milkv linux kernel : link
milkv-zephyr : link
這個是我在論壇找到的文章,這個作者將原本的 FreeRTOS 改為 ZephyrOS , 並且使用 OpenAMP 提供的 RPMsg 作為大小核的通訊方式。 我發現我可以參考它對 Linux 核心的改動,來確保我現在的 milkv 可以同時運作 mailbox 、 virtio 、 rpmsg 。
想要解決的問題
- (已解決) 為了讓 RPMsg-lite 可以順利運作,我想先讓 rpmsg_client_sample 可以配對到 channel 裝置,並且檢查
probe
、 remove
及 callback
功能, 但是目前還是沒有很確定實作內容,因此我會參考 linux-zephyr 這篇它對 linux 核心的改動。 想請問老師是否有更好的辦法可以解決目前的問題。
先分析遇到的問題,例如 Mailbox 的中斷處理以及佇列配置,不要急著下手寫程式。
- (已解決) ThreadX 當中的 tx_api.h 存在許多已宣告但未被實作的函式,而 rpmsg_env_threadx.c 又大量引用裡面的函式,例如 tx_semaphore_create ,這導致在連結階段時出現大量問題,想請問有什麼比較可行的解決辦法。
ThreadX 應該編譯並用 ar 彙整為 libtx.a
謝謝老師! 後來使用 ThreadX-to-RISC-V64 編譯出的 libkernel.a ,解決了連結階段的問題,這個靜態函式庫包含了 ThreadX API 的相關實作。
- (已解決) 目前已經有將運行於大核 (C906B) 的 RPMsg-lite 執行檔編譯出來,但隨即遇到執行階段的問題,當前是使用 dmesg 命令將核心偵測到的錯誤內容顯示出來,但是沒有對應的程式碼可以除錯。想請問若是 Milk-V Duo ,該使用 Valgrind 或是 GDB 作為除錯工具較好,目前論壇有人使用 GDB 應用在 Milk-V Duo 上。
問題四
關於 RPMsg , 目前是依照 Linux-and-Zephyr 這篇演講的方式將 Linux 上的 RPMsg 及 Virtio Driver 組態開啟,並且按照演講中的方式編譯 imx_rpmsg.c 作為 Virtio 的裝置 , 但是編譯完的裝置在 virtio_rpmsg_bus.c 這個 Virtio Driver 的 probe 動作下,會出現問題導致 Kernel Oops 。 其中 RPMsg 以及 Virtio 這兩個原始碼皆是原本 linux kernel 5.10 mainline 的內容 , 額外新加入的就是 imx_rpmsg.c 以及 DTS 的修改 。
接著說明 Kernel Oops 的報錯內容 , Oops - load access fault
, 出錯位置來自於這個巨集 #define sg_is_last(sg) ((sg)->page_link & SG_END)
。 我推測原因在於 virtqueue_add_inbuf
的函式當中我所加入的 buffer 其配置的空間是來自於 vmalloc , 而非 kmalloc , 導致 scatterlist 初始化的結果與預期不符,而要求記憶體配置的函式並非 vmalloc 、 kmalloc 而是 dma_alloc_coherent ,有趣的是這項函式我們預期得到的是 kmalloc 的空間但並非如此,於是才導致了 Oops - load access fault
的問題發生。
至於 imx_rpmsg.c 的初始化過程有順利得到兩個 vring 以及註冊 virtio 裝置 , DTS 我所新增的節點也沒有問題 ,不過我認為上述所產生的錯誤也與這兩個我所更改的部份有關,於是我會放到附件當中 , 我會先附上出錯的程式片段到共筆當中。
- virtio_rpmsg_bus.c (line 71 error)
static inline int virtqueue_add_packed(struct virtqueue *_vq,
struct scatterlist *sgs[],
unsigned int total_sg,
unsigned int out_sgs,
unsigned int in_sgs,
void *data,
void *ctx,
gfp_t gfp)
{
struct vring_virtqueue *vq = to_vvq(_vq);
struct vring_packed_desc *desc;
struct scatterlist *sg;
unsigned int i, n, c, descs_used, err_idx;
__le16 head_flags, flags;
u16 head, id, prev, curr, avail_used_flags;
START_USE(vq);
BUG_ON(data == NULL);
BUG_ON(ctx && vq->indirect);
if (unlikely(vq->broken)) {
END_USE(vq);
return -EIO;
}
LAST_ADD_TIME_UPDATE(vq);
BUG_ON(total_sg == 0);
if (virtqueue_use_indirect(_vq, total_sg))
return virtqueue_add_indirect_packed(vq, sgs, total_sg,
out_sgs, in_sgs, data, gfp);
head = vq->packed.next_avail_idx;
avail_used_flags = vq->packed.avail_used_flags;
WARN_ON_ONCE(total_sg > vq->packed.vring.num && !vq->indirect);
desc = vq->packed.vring.desc;
i = head;
descs_used = total_sg;
if (unlikely(vq->vq.num_free < descs_used)) {
pr_debug("Can't add buf len %i - avail = %i\n",
descs_used, vq->vq.num_free);
END_USE(vq);
return -ENOSPC;
}
id = vq->free_head;
BUG_ON(id == vq->packed.vring.num);
curr = id;
c = 0;
for (n = 0; n < out_sgs + in_sgs; n++) {
for (sg = sgs[n]; sg; sg = sg_next(sg)) {
dma_addr_t addr = vring_map_one_sg(vq, sg, n < out_sgs ?
DMA_TO_DEVICE : DMA_FROM_DEVICE);
if (vring_mapping_error(vq, addr))
goto unmap_release;
flags = cpu_to_le16(vq->packed.avail_used_flags |
(++c == total_sg ? 0 : VRING_DESC_F_NEXT) |
(n < out_sgs ? 0 : VRING_DESC_F_WRITE));
if (i == head)
head_flags = flags;
else
desc[i].flags = flags;
desc[i].addr = cpu_to_le64(addr);
desc[i].len = cpu_to_le32(sg->length);
desc[i].id = cpu_to_le16(id);
if (unlikely(vq->use_dma_api)) {
vq->packed.desc_extra[curr].addr = addr;
vq->packed.desc_extra[curr].len = sg->length;
vq->packed.desc_extra[curr].flags =
le16_to_cpu(flags);
}
prev = curr;
curr = vq->packed.desc_state[curr].next;
if ((unlikely(++i >= vq->packed.vring.num))) {
i = 0;
vq->packed.avail_used_flags ^=
1 << VRING_PACKED_DESC_F_AVAIL |
1 << VRING_PACKED_DESC_F_USED;
}
}
}
if (i < head)
vq->packed.avail_wrap_counter ^= 1;
vq->vq.num_free -= descs_used;
vq->packed.next_avail_idx = i;
vq->free_head = curr;
vq->packed.desc_state[id].num = descs_used;
vq->packed.desc_state[id].data = data;
vq->packed.desc_state[id].indir_desc = ctx;
vq->packed.desc_state[id].last = prev;
virtio_wmb(vq->weak_barriers);
vq->packed.vring.desc[head].flags = head_flags;
vq->num_added += descs_used;
pr_debug("Added buffer head %i to %p\n", head, vq);
END_USE(vq);
return 0;
unmap_release:
err_idx = i;
i = head;
vq->packed.avail_used_flags = avail_used_flags;
for (n = 0; n < total_sg; n++) {
if (i == err_idx)
break;
vring_unmap_desc_packed(vq, &desc[i]);
i++;
if (i >= vq->packed.vring.num)
i = 0;
}
END_USE(vq);
return -EIO;
}
static inline int virtqueue_add(struct virtqueue *_vq,
struct scatterlist *sgs[],
unsigned int total_sg,
unsigned int out_sgs,
unsigned int in_sgs,
void *data,
void *ctx,
gfp_t gfp)
{
struct vring_virtqueue *vq = to_vvq(_vq);
return vq->packed_ring ? virtqueue_add_packed(_vq, sgs, total_sg,
out_sgs, in_sgs, data, ctx, gfp) :
virtqueue_add_split(_vq, sgs, total_sg,
out_sgs, in_sgs, data, ctx, gfp);
}
int virtqueue_add_inbuf(struct virtqueue *vq,
struct scatterlist *sg, unsigned int num,
void *data,
gfp_t gfp)
{
return virtqueue_add(vq, &sg, num, 0, 1, data, NULL, gfp);
}
EXPORT_SYMBOL_GPL(virtqueue_add_inbuf);
- imx_rpmsg.c (register virtio device)

問題簡述: 目前已知 imx_rpmsg.c 會生成兩個 vring 以及 virtio device , 到了 virtio_rpmsg_bus.c 的 virtio driver probe 階段,會透過 dma_alloc_coherent 得到 vring 的虛擬位址,
問題五
我會盡量把問題講的詳盡一點,同上一題問題四出現的問題,程式在呼叫 sg_next
這個函式時,會出現 Load Access Fault 這項問題,經過我查詢的結果發現這是 RISC-V 的記憶體保護機制所偵測到的問題,也發現到出現存取問題的位址在 000000000190200e
, 正好是我 rpmsg 裝置的起始位址加上 14
。 之所以會有這個結果,首先要從我的 virtqueue 講起,我的 virtqueue 是透過 dma_alloc_coherent
的方式,映射我的 rpmsg 裝置到實體記憶體當中,而回傳的虛擬位址 bufs_va
也就當做是我 virtqueue 的起始位址 。 之後依序加入 virtqueue 的 buffer 其位址會被