From 3aa02520e32282c8fa2f822ec1573030d362a1f2 Mon Sep 17 00:00:00 2001 From: laokz Date: Sun, 7 Mar 2021 21:48:40 +0800 Subject: [PATCH] arm-virt platform: add virtio-mmio net device driver Change-Id: I28c8a2006ebfc62e1ca1f98f7eea416b9d8805bd --- arm_virt/README.md | 20 +- arm_virt/README_zh.md | 20 +- arm_virt/config/virtnet/Makefile | 7 + arm_virt/config/virtnet/virtnet.c | 579 ++++++++++++++++++ arm_virt/config/virtnet/virtnet.h | 186 ++++++ .../liteos_a/config/board/os_adapt/os_adapt.c | 7 + drivers/lite.mk | 3 +- 7 files changed, 817 insertions(+), 5 deletions(-) create mode 100755 arm_virt/config/virtnet/Makefile create mode 100644 arm_virt/config/virtnet/virtnet.c create mode 100644 arm_virt/config/virtnet/virtnet.h diff --git a/arm_virt/README.md b/arm_virt/README.md index 515fef3..f6d00a5 100755 --- a/arm_virt/README.md +++ b/arm_virt/README.md @@ -66,10 +66,26 @@ sudo rmmod mtdram ``` Note: bootargs only rootsize is adjustable. 3nd partition beyond it mounted at /storage, writable. -c) Run `qemu-system-arm`, enter user-space command line. +c) Config host net bridge device. In Linux we can do like this: +``` +sudo modprobe tun tap +sudo ip link add br0 type bridge +sudo ip address add 10.0.2.2/24 dev br0 +sudo ip link set dev br0 up + +# comment these if already done +sudo mkdir -p /etc/qemu +echo 'allow br0' | sudo tee -a /etc/qemu/bridge.conf + +# comment this if the file doesn't exist +echo 0 | sudo tee /proc/sys/net/bridge/bridge-nf-call-iptables +``` +Note: The guest network is hardcoded as 10.0.2.0/24, gateway 10.0.2.2, default ip 10.0.2.15. Different guest instance should use different MAC and IP(better use different flash image). MAC can be assigned through command line. IP can be changed in OHOS command line, e.g. `ifconfig vn0 inet 10.0.2.30`, or whatever method. + +d) Run `qemu-system-arm`, enter user-space command line. ``` -qemu-system-arm -M virt,gic-version=2,secure -cpu cortex-a7 -smp cpus=1 -nographic -m 1G -drive if=pflash,file=flash.img,format=raw +qemu-system-arm -M virt,gic-version=2,secure -cpu cortex-a7 -smp cpus=1 -nographic -m 1G -drive if=pflash,file=flash.img,format=raw -netdev bridge,id=net0 -device virtio-net-device,netdev=net0,mac=12:22:33:44:55:66 ``` diff --git a/arm_virt/README_zh.md b/arm_virt/README_zh.md index fef149d..016a856 100755 --- a/arm_virt/README_zh.md +++ b/arm_virt/README_zh.md @@ -60,10 +60,26 @@ sudo rmmod mtdram ``` 提示:bootargs中仅rootsize可调整,分区三rootsize以外空间安装在/storage目录,可读可写。 -c) 运行`qemu-system-arm`,进入用户态命令行。 +c) 配置主机网桥设备。Linux系统可参考以下命令: +``` +sudo modprobe tun tap +sudo ip link add br0 type bridge +sudo ip address add 10.0.2.2/24 dev br0 +sudo ip link set dev br0 up + +# 以下命令执行一次后即可注释掉 +sudo mkdir -p /etc/qemu +echo 'allow br0' | sudo tee -a /etc/qemu/bridge.conf + +# 如果这个文件不存在可删除此命令 +echo 0 | sudo tee /proc/sys/net/bridge/bridge-nf-call-iptables +``` +提示:系统网络硬编码为10.0.2.0/24,网关10.0.2.2,默认网址10.0.2.15。不同的客户机实例应使用不同的MAC和IP地址(flash映像文件也最好不同),MAC地址可通过QEMU命令行传递,IP地址可在OHOS命令行中调整,如`ifconfig vn0 inet 10.0.2.30`,或使用其它方法。 + +d) 运行`qemu-system-arm`,进入用户态命令行。 ``` -qemu-system-arm -M virt,gic-version=2,secure -cpu cortex-a7 -smp cpus=1 -nographic -m 1G -drive if=pflash,file=flash.img,format=raw +qemu-system-arm -M virt,gic-version=2,secure -cpu cortex-a7 -smp cpus=1 -nographic -m 1G -drive if=pflash,file=flash.img,format=raw -netdev bridge,id=net0 -device virtio-net-device,netdev=net0,mac=12:22:33:44:55:66 ``` ``` diff --git a/arm_virt/config/virtnet/Makefile b/arm_virt/config/virtnet/Makefile new file mode 100755 index 0000000..d4c3ecf --- /dev/null +++ b/arm_virt/config/virtnet/Makefile @@ -0,0 +1,7 @@ +include $(LITEOSTOPDIR)/../../drivers/adapter/khdf/liteos/lite.mk +MODULE_NAME := $(notdir $(shell pwd)) +LOCAL_INCLUDE := -I $(LITEOSTOPDIR)/../../third_party/lwip/src/include/ +LOCAL_SRCS := virtnet.c +LOCAL_CFLAGS += $(LOCAL_INCLUDE) + +include $(HDF_DRIVER) diff --git a/arm_virt/config/virtnet/virtnet.c b/arm_virt/config/virtnet/virtnet.c new file mode 100644 index 0000000..bd1df71 --- /dev/null +++ b/arm_virt/config/virtnet/virtnet.c @@ -0,0 +1,579 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* enable lwip `netif_add` API */ +#define __LWIP__ + +#include "los_base.h" +#include "los_printf.h" +#include "los_vm_zone.h" +#include "los_vm_phys.h" +#include "los_hwi.h" + +/* kernel header changed some lwip behavior, so this should be prior to them */ +#include "virtnet.h" + +#include "lwip/netif.h" +#include "lwip/etharp.h" +#include "lwip/tcpip.h" +#include "lwip/mem.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +static inline VADDR_T RegVaddr(const struct VirtNetif *nic, unsigned reg) +{ + return nic->base + reg; +} + +static inline uint32_t VirtioGetStatus(const struct VirtNetif *nic) +{ + VADDR_T v = RegVaddr(nic, VIRTMMIO_REG_STATUS); + return GET_UINT32(v) & VIRTIO_STATUS_MASK; +} + +static inline void VirtioAddStatus(const struct VirtNetif *nic, uint32_t val) +{ + VADDR_T v = RegVaddr(nic, VIRTMMIO_REG_STATUS); + WRITE_UINT32(VirtioGetStatus(nic) | val, v); +} + +static inline void VirtioResetStatus(const struct VirtNetif *nic) +{ + VADDR_T v = RegVaddr(nic, VIRTMMIO_REG_STATUS); + WRITE_UINT32(VIRTIO_STATUS_RESET, v); +} + +static err_t DiscoverVirtnet(struct VirtNetif *nic) +{ + VADDR_T base; + int i; + uint32_t *p = NULL; + + base = IO_DEVICE_ADDR(VIRTMMIO_BASE_ADDR) + VIRTMMIO_BASE_SIZE * (NUM_VIRTIO_TRANSPORTS - 1); + for (i = NUM_VIRTIO_TRANSPORTS - 1; i >= 0; i--) { + p = (uint32_t *)base; + if ((*p == VIRTMMIO_MAGIC) && + (*(p + 1) == VIRTMMIO_VERSION_LEGACY) && + (*(p + 2) == VIRTMMIO_DEVICE_ID_NET)) { + nic->base = base; + nic->irq = IRQ_SPI_BASE + VIRTMMIO_BASE_IRQ + i; + return ERR_OK; + } + + base -= VIRTMMIO_BASE_SIZE; + } + + return ERR_IF; +} + +static err_t NegotiateFeature(struct netif *netif) +{ + uint32_t features, supported, i; + struct VirtNetif *nic = netif->state; + + WRITE_UINT32(VIRTIO_FEATURE_WORD, RegVaddr(nic, VIRTMMIO_REG_DEVFEATURESEL)); + features = GET_UINT32(RegVaddr(nic, VIRTMMIO_REG_DEVFEATURE)); + supported = 0; + + if (features & VIRTIO_NET_F_MAC) { + for (i = 0; i < ETHARP_HWADDR_LEN; i++) { + netif->hwaddr[i] = GET_UINT8(RegVaddr(nic, VIRTMMIO_REG_CONFIG + i)); + } + netif->hwaddr_len = ETHARP_HWADDR_LEN; + supported |= VIRTIO_NET_F_MAC; + } else { + PRINT_ERR("no MAC found\n"); + return ERR_IF; + } + + WRITE_UINT32(VIRTIO_FEATURE_WORD, RegVaddr(nic, VIRTMMIO_REG_DRVFEATURESEL)); + WRITE_UINT32(supported, RegVaddr(nic, VIRTMMIO_REG_DRVFEATURE)); + + VirtioAddStatus(nic, VIRTIO_STATUS_FEATURES_OK); + if ((VirtioGetStatus(nic) & VIRTIO_STATUS_FEATURES_OK) == 0) { + PRINT_ERR("negotiate feature %#08x failed\n", supported); + return ERR_IF; + } + + return ERR_OK; +} + +static err_t InitTxFreelist(struct VirtNetif *nic) +{ + int i; + + LOS_SpinInit(&nic->transLock); + nic->tbufRec = (struct TbufRecord *)mem_malloc(sizeof(struct TbufRecord) * nic->trans.qsz); + if (nic->tbufRec == NULL) { + PRINT_ERR("alloc nic->tbufRec memory failed\n"); + return ERR_MEM; + } + + for (i = 0; i < nic->trans.qsz - 1; i++) { + nic->trans.desc[i].flag = VIRTQ_DESC_F_NEXT; + nic->trans.desc[i].next = i + 1; + } + nic->tFreeHdr = 0; + nic->tFreeNum = nic->trans.qsz; + + return ERR_OK; +} + +static void FreeTxEntry(struct VirtNetif *nic, uint16_t head) +{ + uint32_t count, idx, tail; + struct pbuf *phead = NULL; + struct Virtq *q = &nic->trans; + + idx = q->desc[head].next; + phead = nic->tbufRec[idx].head; + count = nic->tbufRec[idx].count; + tail = nic->tbufRec[idx].tail; + + LOS_SpinLock(&nic->transLock); + if (nic->tFreeNum > 0) { + q->desc[tail].next = nic->tFreeHdr; + q->desc[tail].flag = VIRTQ_DESC_F_NEXT; + } + nic->tFreeNum += count; + nic->tFreeHdr = head; + LOS_SpinUnlock(&nic->transLock); + + pbuf_free_callback(phead); +} + +static void ReleaseRvEntry(struct pbuf *p) +{ + struct RbufRecord *pc = (struct RbufRecord *)p; + struct VirtNetif *nic = pc->nic; + uint32_t intSave; + + LOS_SpinLockSave(&nic->recvLock, &intSave); + nic->recv.avail->ring[nic->recv.avail->index % nic->recv.qsz] = pc->id; + Dsb(); + nic->recv.avail->index++; + LOS_SpinUnlockRestore(&nic->recvLock, intSave); + + if (nic->recv.used->flag != VIRTQ_USED_F_NO_NOTIFY) { + WRITE_UINT32(VIRTQ_IDX_RECV, RegVaddr(nic, VIRTMMIO_REG_QUEUENOTIFY)); + } +} + +static err_t ConfigRvBuffer(struct VirtNetif *nic) +{ + uint32_t i; + PADDR_T paddr; + struct Virtq *q = &nic->recv; + + LOS_SpinInit(&nic->recvLock); + nic->rbufRec = (struct RbufRecord *)mem_calloc(q->qsz, sizeof(struct RbufRecord)); + if (nic->rbufRec == NULL) { + PRINT_ERR("alloc nic->rbufRec memory failed\n"); + return ERR_MEM; + } + + /* receive buffer begin after queues */ + nic->packetBase = (VADDR_T)q->desc + PAGE_SIZE * VIRTQ_NUMS * VIRTQ_PAGES; + paddr = OsVmVaddrToPage((void *)nic->packetBase)->physAddr; + + /* one buffer receive one packet(no more than 1514B) */ + for (i = 0; i < q->qsz; i++) { + q->desc[i].pAddr = paddr; + q->desc[i].len = VIRTQ_RECV_BUF_SIZE; + q->desc[i].flag = VIRTQ_DESC_F_WRITE; + paddr += VIRTQ_RECV_BUF_SIZE; + + q->avail->ring[i] = i; + + nic->rbufRec[i].cbuf.custom_free_function = ReleaseRvEntry; + nic->rbufRec[i].nic = nic; + nic->rbufRec[i].id = i; + } + + /* now let bypass `VirtnetHdr` and point to packet base */ + nic->packetBase += sizeof(struct VirtnetHdr); + return ERR_OK; +} + +static err_t ConfigQueue(struct VirtNetif *nic) +{ + uint32_t i, num, pfn, qsz; + void *firstPage = NULL; + struct Virtq *q = NULL; + err_t ret; + + num = VIRTQ_NUMS * VIRTQ_PAGES + VIRTQ_RECV_BUF_PAGES; + firstPage = LOS_PhysPagesAllocContiguous(num); + if (firstPage == NULL) { + PRINT_ERR("alloc queue memory failed\n"); + return ERR_MEM; + } + memset_s(firstPage, num * PAGE_SIZE, 0, num * PAGE_SIZE); + pfn = OsVmVaddrToPage(firstPage)->physAddr >> PAGE_SHIFT; + + WRITE_UINT32(PAGE_SIZE, RegVaddr(nic, VIRTMMIO_REG_GUESTPAGESIZE)); + + for (i = 0; i < VIRTQ_NUMS; i++) { + if (i == 0) { + q = &nic->recv; + qsz = VIRTQ_RECV_QSZ; + } else { /* not support for VIRTQ_NUMS>2 yet */ + q = &nic->trans; + qsz = VIRTQ_TRANS_QSZ; + } + + /* put here mainly for Deinit when error */ + q->desc = (struct VirtqDesc *)((UINTPTR)firstPage + i * VIRTQ_PAGES * PAGE_SIZE); + + WRITE_UINT32(i, RegVaddr(nic, VIRTMMIO_REG_QUEUESEL)); + + num = GET_UINT32(RegVaddr(nic, VIRTMMIO_REG_QUEUEPFN)); + if (num != 0) { + PRINT_ERR("queue %#x inused\n", num << PAGE_SHIFT); + return ERR_IF; + } + num = GET_UINT32(RegVaddr(nic, VIRTMMIO_REG_QUEUENUMMAX)); + if (num < qsz) { + PRINT_ERR("queue %u not available: max qsz=%d\n", i, num); + return ERR_IF; + } + + q->qsz = qsz; + q->avail = (struct VirtqAvail *)((UINTPTR)q->desc + sizeof(struct VirtqDesc) * qsz); + q->used = (struct VirtqUsed *)((UINTPTR)q->desc + VIRTQ_PAGES / VIRTQ_NUMS * PAGE_SIZE); + + WRITE_UINT32(qsz, RegVaddr(nic, VIRTMMIO_REG_QUEUENUM)); + WRITE_UINT32(PAGE_SIZE, RegVaddr(nic, VIRTMMIO_REG_QUEUEALIGN)); + WRITE_UINT32(pfn + i * VIRTQ_PAGES, RegVaddr(nic, VIRTMMIO_REG_QUEUEPFN)); + + if (i == 0) { + if ((ret = ConfigRvBuffer(nic)) != ERR_OK) { + return ret; + } + } else { + if ((ret = InitTxFreelist(nic)) != ERR_OK) { + return ret; + } + } + } + + return ERR_OK; +} + +static uint32_t GetTxFreeEntry(struct VirtNetif *nic, uint32_t count) +{ + uint32_t intSave, head, tail, idx; + uint32_t tmp = count; + +RETRY: + LOS_SpinLockSave(&nic->transLock, &intSave); + if (count > nic->tFreeNum) { + LOS_SpinUnlockRestore(&nic->transLock, intSave); + LOS_TaskYield(); + goto RETRY; + } + + head = nic->tFreeHdr; + idx = head; + while (tmp--) { + tail = idx; + idx = nic->trans.desc[idx].next; + } + nic->tFreeHdr = idx; /* may be invalid if empty, but tFreeNum must be valid: 0 */ + nic->tFreeNum -= count; + LOS_SpinUnlockRestore(&nic->transLock, intSave); + nic->trans.desc[tail].flag &= ~VIRTQ_DESC_F_NEXT; + + return head; +} + +static err_t LowLevelOutput(struct netif *netif, struct pbuf *p) +{ + uint32_t add, idx, head, tmp; + struct pbuf *q = NULL; + struct VirtNetif *nic = netif->state; + struct Virtq *trans = &nic->trans; + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + + /* plus 1 for VirtnetHdr */ + add = pbuf_clen(p) + 1; + if (add > trans->qsz) { + PRINT_ERR("packet pbuf_clen %u larger than supported %u\n", add - 1, trans->qsz - 1); + return ERR_IF; + } + + head = GetTxFreeEntry(nic, add); + trans->desc[head].pAddr = nic->vnHdrPaddr; + trans->desc[head].len = sizeof(struct VirtnetHdr); + idx = trans->desc[head].next; + tmp = head; + q = p; + while (q != NULL) { + tmp = trans->desc[tmp].next; + trans->desc[tmp].pAddr = (PADDR_T)q->payload - KERNEL_ASPACE_BASE + SYS_MEM_BASE; + trans->desc[tmp].len = q->len; + q = q->next; + } + + nic->tbufRec[idx].head = p; + nic->tbufRec[idx].count = add; + nic->tbufRec[idx].tail = tmp; + pbuf_ref(p); + + trans->avail->ring[trans->avail->index % trans->qsz] = head; + Dsb(); + trans->avail->index++; + WRITE_UINT32(VIRTQ_IDX_TRANS, RegVaddr(nic, VIRTMMIO_REG_QUEUENOTIFY)); + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + + return ERR_OK; +} + +static struct pbuf *LowLevelInput(const struct netif *netif, const struct VirtqUsedElem *e) +{ + struct VirtNetif *nic = netif->state; + struct pbuf *p = NULL; + uint16_t len; + VADDR_T payload; + + payload = nic->packetBase + VIRTQ_RECV_BUF_SIZE * e->id; +#if ETH_PAD_SIZE + payload -= ETH_PAD_SIZE; +#endif + pbuf_alloced_custom(PBUF_RAW, ETH_FRAME_LEN, PBUF_ROM | PBUF_ALLOC_FLAG_RX, + &nic->rbufRec[e->id].cbuf, (void *)payload, ETH_FRAME_LEN); + + len = e->len - sizeof(struct VirtnetHdr); + if ((len > ETH_FRAME_LEN) || (nic->recv.desc[e->id].flag & VIRTQ_DESC_F_NEXT)) { + PRINT_ERR("packet error: len=%u, queue flag=%u\n", len, nic->recv.desc[e->id].flag); + return NULL; + } +#if ETH_PAD_SIZE + len += ETH_PAD_SIZE; +#endif + + p = &nic->rbufRec[e->id].cbuf.pbuf; + p->len = len; + p->tot_len = p->len; + return p; +} + +static void VirtnetRvHandle(struct netif *netif) +{ + struct VirtNetif *nic = netif->state; + struct Virtq *q = &nic->recv; + struct pbuf *buf = NULL; + struct VirtqUsedElem *e = NULL; + + q->avail->flag = VIRTQ_AVAIL_F_NO_INTERRUPT; + while (1) { + if (q->last == q->used->index) { + q->avail->flag = 0; + /* recheck if new one come in between empty ring and enable interrupt */ + Dsb(); + if (q->last == q->used->index) { + break; + } + q->avail->flag = VIRTQ_AVAIL_F_NO_INTERRUPT; + } + + Dsb(); + e = &q->used->ring[q->last % q->qsz]; + buf = LowLevelInput(netif, e); + if (netif->input(buf, netif) != ERR_OK) { + LWIP_DEBUGF(NETIF_DEBUG, ("IP input error\n")); + ReleaseRvEntry(buf); + } + + q->last++; + } +} + +static void VirtnetTxHandle(struct VirtNetif *nic) +{ + struct Virtq *q = &nic->trans; + struct VirtqUsedElem *e = NULL; + + /* Bypass recheck as VirtnetRvHandle */ + q->avail->flag = VIRTQ_AVAIL_F_NO_INTERRUPT; + while (q->last != q->used->index) { + Dsb(); + e = &q->used->ring[q->last % q->qsz]; + FreeTxEntry(nic, e->id); + q->last++; + } + q->avail->flag = 0; +} + +void VirtnetIRQhandle(int swIrq, void *pDevId) +{ + (void)swIrq; + struct netif *netif = pDevId; + struct VirtNetif *nic = netif->state; + + if (!(GET_UINT32(RegVaddr(nic, VIRTMMIO_REG_INTERRUPTSTATUS)) & VIRTMMIO_IRQ_NOTIFY_USED)) { + return; + } + + VirtnetRvHandle(netif); + + VirtnetTxHandle(nic); + + WRITE_UINT32(VIRTMMIO_IRQ_NOTIFY_USED, RegVaddr(nic, VIRTMMIO_REG_INTERRUPTACK)); +} + +static err_t LowLevelInit(struct netif *netif) +{ + struct VirtNetif *nic = netif->state; + int ret; + + if (DiscoverVirtnet(nic) != ERR_OK) { + PRINT_ERR("virtio-mmio net device not found\n"); + return ERR_IF; + } + + VirtioResetStatus(nic); + + VirtioAddStatus(nic, VIRTIO_STATUS_ACK); + + VirtioAddStatus(nic, VIRTIO_STATUS_DRIVER); + while ((VirtioGetStatus(nic) & VIRTIO_STATUS_DRIVER) == 0) { } + + if ((ret = NegotiateFeature(netif)) != ERR_OK) { + goto ERR_OUT; + } + + if ((ret = ConfigQueue(nic)) != ERR_OK) { + goto ERR_OUT; + } + + nic->vnHdrPaddr = (PADDR_T)(&nic->vnHdr) - KERNEL_ASPACE_BASE + SYS_MEM_BASE; + + HwiIrqParam param = {0, netif, VIRTMMIO_NETIF_NAME}; + ret = LOS_HwiCreate(nic->irq, OS_HWI_PRIO_HIGHEST, IRQF_SHARED, + (HWI_PROC_FUNC)VirtnetIRQhandle, ¶m); + if (ret != 0) { + PRINT_ERR("virtio-mmio net device IRQ register failed: %d\n", ret); + ret = ERR_IF; + goto ERR_OUT; + } + HalIrqUnmask(nic->irq); + nic->irq |= ~_IRQ_MASK; + + VirtioAddStatus(nic, VIRTIO_STATUS_DRIVER_OK); + + /* everything is ready, now notify device the receive buffer */ + nic->recv.avail->index += nic->recv.qsz; + WRITE_UINT32(VIRTQ_IDX_RECV, RegVaddr(nic, VIRTMMIO_REG_QUEUENOTIFY)); + return ERR_OK; + +ERR_OUT: + VirtioAddStatus(nic, VIRTIO_STATUS_FAILED); + return ret; +} + +static err_t EthernetIfInit(struct netif *netif) +{ + struct VirtNetif *nic = NULL; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + + nic = mem_calloc(1, sizeof(struct VirtNetif)); + if (nic == NULL) { + PRINT_ERR("alloc nic memory failed\n"); + return ERR_MEM; + } + netif->state = nic; + +#if LWIP_NETIF_HOSTNAME + netif->hostname = VIRTMMIO_NETIF_NAME; +#endif + + netif->full_name[0] = netif->name[0] = VIRTMMIO_NETIF_NICK0; + netif->full_name[1] = netif->name[1] = VIRTMMIO_NETIF_NICK1; + netif->full_name[2] = VIRTMMIO_NETIF_NICK2; + netif->full_name[3] = '\0'; + + netif->output = etharp_output; + netif->linkoutput = LowLevelOutput; + + netif->mtu = VIRTMMIO_NETIF_MTU; + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; + + return LowLevelInit(netif); +} + +static void VirtnetDeInit(struct netif *netif) +{ + struct VirtNetif *nic = netif->state; + + if (nic && (nic->irq & ~_IRQ_MASK)) { + HwiIrqParam param = {0, netif, VIRTMMIO_NETIF_NAME}; + LOS_HwiDelete(nic->irq & _IRQ_MASK, ¶m); + } + if (nic && nic->rbufRec) { + mem_free(nic->rbufRec); + } + if (nic && nic->tbufRec) { + mem_free(nic->tbufRec); + } + if (nic && nic->recv.desc) { + LOS_PhysPagesFreeContiguous(nic->recv.desc, VIRTQ_NUMS * VIRTQ_PAGES + VIRTQ_RECV_BUF_PAGES); + } + if (nic) { + mem_free(nic); + } + mem_free(netif); +} + +struct netif *VirtnetInit(void) +{ + ip4_addr_t ip, mask, gw; + struct netif *netif = NULL; + + netif = mem_calloc(1, sizeof(struct netif)); + if (netif == NULL) { + PRINT_ERR("alloc netif memory failed\n"); + return NULL; + } + + ip.addr = ipaddr_addr(VIRTMMIO_NETIF_DFT_IP); + mask.addr = ipaddr_addr(VIRTMMIO_NETIF_DFT_MASK); + gw.addr = ipaddr_addr(VIRTMMIO_NETIF_DFT_GW); + if (netif_add(netif, &ip, &mask, &gw, netif->state, + EthernetIfInit, tcpip_input) == NULL) { + PRINT_ERR("add virtio-mmio net device failed\n"); + VirtnetDeInit(netif); + return NULL; + } + + return netif; +} + +#ifdef __cplusplus +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __cplusplus */ diff --git a/arm_virt/config/virtnet/virtnet.h b/arm_virt/config/virtnet/virtnet.h new file mode 100644 index 0000000..a0fac01 --- /dev/null +++ b/arm_virt/config/virtnet/virtnet.h @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __VIRTNET_H__ +#define __VIRTNET_H__ + +/* + * Only legacy device supported and endianness ignored, + * for virtqueue use native endianness of the guest. + */ + +#include "stdint.h" +#include "los_typedef.h" +#include "los_vm_common.h" +#include "los_spinlock.h" +#include "netinet/if_ether.h" +#include "lwip/pbuf.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#define VIRTIO_STATUS_RESET 0 +#define VIRTIO_STATUS_ACK 1 +#define VIRTIO_STATUS_DRIVER 2 +#define VIRTIO_STATUS_DRIVER_OK 4 +#define VIRTIO_STATUS_FEATURES_OK 8 +#define VIRTIO_STATUS_DEVICE_NEEDS_RESET 64 +#define VIRTIO_STATUS_FAILED 128 +#define VIRTIO_STATUS_MASK 0xFF + +#define VIRTIO_FEATURE_WORD 0 +#define VIRTIO_NET_F_MAC (1 << 5) + +#define VIRTMMIO_REG_DEVFEATURE 0x10 +#define VIRTMMIO_REG_DEVFEATURESEL 0x14 +#define VIRTMMIO_REG_DRVFEATURE 0x20 +#define VIRTMMIO_REG_DRVFEATURESEL 0x24 +#define VIRTMMIO_REG_GUESTPAGESIZE 0x28 +#define VIRTMMIO_REG_QUEUESEL 0x30 +#define VIRTMMIO_REG_QUEUENUMMAX 0x34 +#define VIRTMMIO_REG_QUEUENUM 0x38 +#define VIRTMMIO_REG_QUEUEALIGN 0x3C +#define VIRTMMIO_REG_QUEUEPFN 0x40 +#define VIRTMMIO_REG_QUEUENOTIFY 0x50 +#define VIRTMMIO_REG_INTERRUPTSTATUS 0x60 +#define VIRTMMIO_REG_INTERRUPTACK 0x64 +#define VIRTMMIO_REG_STATUS 0x70 +#define VIRTMMIO_REG_CONFIG 0x100 + +/* QEMU 5.2 virtio-mmio */ +#define VIRTMMIO_MAGIC 0x74726976 +#define VIRTMMIO_VERSION_LEGACY 1 +#define VIRTMMIO_DEVICE_ID_NET 1 +#define VIRTMMIO_BASE_ADDR 0x0A000000 +#define VIRTMMIO_BASE_SIZE 0x200 +#define VIRTMMIO_BASE_IRQ 16 +#define NUM_VIRTIO_TRANSPORTS 32 + +#define VIRTMMIO_NETIF_MTU 1500 +#define VIRTMMIO_NETIF_NAME "virtnet" +#define VIRTMMIO_NETIF_NICK0 'v' +#define VIRTMMIO_NETIF_NICK1 'n' +#define VIRTMMIO_NETIF_NICK2 '0' +#define VIRTMMIO_NETIF_DFT_IP "10.0.2.15" +#define VIRTMMIO_NETIF_DFT_GW "10.0.2.2" +#define VIRTMMIO_NETIF_DFT_MASK "255.255.255.0" + +#define VIRTQ_DESC_F_NEXT 1 +#define VIRTQ_DESC_F_WRITE 2 +#define VIRTQ_USED_F_NO_NOTIFY 1 +#define VIRTQ_AVAIL_F_NO_INTERRUPT 1 +#define VIRTMMIO_IRQ_NOTIFY_USED 1 + +struct VirtqDesc { + uint64_t pAddr; + uint32_t len; + uint16_t flag; + uint16_t next; +}; + +struct VirtqAvail { + uint16_t flag; + uint16_t index; + uint16_t ring[]; + /* We do not use VIRTIO_F_EVENT_IDX, so no other member */ +}; + +struct VirtqUsedElem { + uint32_t id; + uint32_t len; +}; + +struct VirtqUsed { + uint16_t flag; + uint16_t index; + struct VirtqUsedElem ring[]; + /* We do not use VIRTIO_F_EVENT_IDX, so no other member */ +}; + +struct Virtq { + unsigned int qsz; + unsigned int last; + + struct VirtqDesc *desc; + struct VirtqAvail *avail; + struct VirtqUsed *used; +}; + +/* This struct is actually ignored */ +struct VirtnetHdr { + uint8_t flag; + uint8_t gsoType; + uint16_t hdrLen; + uint16_t gsoSize; + uint16_t csumStart; + uint16_t csumOffset; +}; + +/* + * We use two queues for Tx/Rv respectively, each has minimal two-page-size. + * When Tx/Rv, no dynamic memory alloc/free: output pbuf directly put into + * queue and freed by tcpip_thread when used; input has some fixed-size buffers + * just after the queues and released by application when consumed. + */ +#define VIRTQ_IDX_RECV 0 +#define VIRTQ_IDX_TRANS 1 +#define VIRTQ_NUMS 2 +#define VIRTQ_PAGES 2 +#define VIRTQ_TRANS_QSZ 128 +#define VIRTQ_RECV_QSZ 32 +#define VIRTQ_RECV_BUF_SIZE ROUNDUP(sizeof(struct VirtnetHdr) + ETH_FRAME_LEN, 4) +#define VIRTQ_RECV_BUF_PAGES (ROUNDUP(VIRTQ_RECV_QSZ * VIRTQ_RECV_BUF_SIZE, PAGE_SIZE) / PAGE_SIZE) + +struct RbufRecord { + struct pbuf_custom cbuf; + struct VirtNetif *nic; + uint32_t id; /* index to recv.desc[] */ +}; + +struct TbufRecord { + struct pbuf *head; /* first pbuf address of this pbuf chain */ + uint32_t count; /* occupied desc entries, include VirtnetHdr */ + uint32_t tail; /* tail pbuf's index to recv.desc[] */ +}; + +struct VirtNetif { + VADDR_T base; /* I/O base address */ +#define _IRQ_MASK 0xFF + int irq; /* higher bytes as HWI registered flag */ + + struct Virtq recv; + VADDR_T packetBase; /* actual packet base in receive buffer */ + struct RbufRecord *rbufRec; + SPIN_LOCK_S recvLock; + + struct Virtq trans; + unsigned int tFreeHdr; /* head of free desc entries list */ + unsigned int tFreeNum; + struct TbufRecord *tbufRec; + SPIN_LOCK_S transLock; + + struct VirtnetHdr vnHdr; + PADDR_T vnHdrPaddr; +}; + +#ifdef __cplusplus +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#endif diff --git a/arm_virt/liteos_a/config/board/os_adapt/os_adapt.c b/arm_virt/liteos_a/config/board/os_adapt/os_adapt.c index eb6a51e..7cba171 100644 --- a/arm_virt/liteos_a/config/board/os_adapt/os_adapt.c +++ b/arm_virt/liteos_a/config/board/os_adapt/os_adapt.c @@ -82,6 +82,13 @@ extern void tcpip_init(tcpip_init_done_fn initfunc, void *arg); } } while (netif_is_link_up(pnetif) == 0); #endif + + extern struct netif *VirtnetInit(void); + struct netif *p = VirtnetInit(); + if (p) { + netif_set_default(p); + (void)netifapi_netif_set_up(p); + } } #endif diff --git a/drivers/lite.mk b/drivers/lite.mk index 44c48d2..a141424 100644 --- a/drivers/lite.mk +++ b/drivers/lite.mk @@ -25,8 +25,9 @@ ifeq ($(BUILD_FROM_SOURCE), y) endif ###################### SELF-DEVELOPED DRIVER ###################### -LITEOS_BASELIB += -lcfiflash +LITEOS_BASELIB += -lcfiflash -lvirtnet LIB_SUBDIRS += $(LITEOSTOPDIR)/../../device/$(patsubst "%",%,$(LOSCFG_DEVICE_COMPANY))/$(patsubst "%",%,$(LOSCFG_PRODUCT_NAME))/config/cfiflash +LIB_SUBDIRS += $(LITEOSTOPDIR)/../../device/$(patsubst "%",%,$(LOSCFG_DEVICE_COMPANY))/$(patsubst "%",%,$(LOSCFG_PRODUCT_NAME))/config/virtnet ###################### HDF DRIVER ###################### ifeq ($(LOSCFG_DRIVERS_HDF_PLATFORM_UART), y) -- Gitee