├── .gitignore ├── .gitmodules ├── src ├── arch │ ├── arm64 │ │ ├── desc.h │ │ ├── vm-arch.h │ │ └── vm.c │ └── x86 │ │ ├── desc.h │ │ └── vm.c ├── err.h ├── serial.h ├── diskimg.h ├── virtio-net.h ├── bus.h ├── diskimg.c ├── virtio-blk.h ├── virtq.h ├── vm.h ├── bus.c ├── utils.h ├── virtq.c ├── pci.h ├── virtio-pci.h ├── main.c ├── vm.c ├── pci.c ├── virtio-blk.c ├── serial.c ├── virtio-net.c └── virtio-pci.c ├── AUTHORS ├── target └── rc-startup ├── .clang-format ├── .ci ├── check-format.sh └── check-newline.sh ├── mk ├── common.mk └── external.mk ├── LICENSE ├── .github └── workflows │ └── main.yml ├── Makefile ├── README.md ├── configs ├── linux-arm64.config ├── linux-x86.config └── busybox.config └── CONTRIBUTING.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.o.d 3 | *.dSYM 4 | .vscode 5 | .devcontainer 6 | core* 7 | kvm-host 8 | build 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/dtc"] 2 | path = src/dtc 3 | url = https://git.kernel.org/pub/scm/utils/dtc/dtc.git 4 | branch = v1.7.0 5 | -------------------------------------------------------------------------------- /src/arch/arm64/desc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define RAM_BASE (1UL << 31) 4 | #define SERIAL_IRQ 0 5 | #define VIRTIO_BLK_IRQ 1 6 | #define KERNEL_OPTS "console=ttyS0" 7 | -------------------------------------------------------------------------------- /src/arch/x86/desc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define RAM_BASE 0 4 | #define SERIAL_IRQ 4 5 | #define VIRTIO_NET_IRQ 14 6 | #define VIRTIO_BLK_IRQ 15 7 | #define KERNEL_OPTS "console=ttyS0 pci=conf1" 8 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | kvm-host is written by: 2 | Jim Huang 3 | RinHizakura 4 | ray90514 5 | Yan-Jie Wang 6 | 7 | Based on the original work from Serge Zaitsev. 8 | -------------------------------------------------------------------------------- /target/rc-startup: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mkdir /proc 3 | mount -t proc /proc /proc 4 | mkdir /sys 5 | mount -t sysfs sysfs /sys 6 | mount -t devtmpfs devtmpfs /dev 7 | mkdir -p /dev/pts 8 | mount -vt devpts -o gid=4,mode=620 none /dev/pts 9 | 10 | # virtio-blk 11 | mkdir /mnt 12 | echo "vda 0:0 660 @mount \$MDEV /mnt" > /etc/mdev.conf 13 | mdev -s 14 | -------------------------------------------------------------------------------- /src/err.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | static inline int throw_err(const char *str, ...) 8 | { 9 | va_list arg; 10 | va_start(arg, str); 11 | vfprintf(stderr, str, arg); 12 | fprintf(stderr, " (errno=%d)\n", errno); 13 | va_end(arg); 14 | return -1; 15 | } 16 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Chromium 2 | Language: Cpp 3 | MaxEmptyLinesToKeep: 3 4 | IndentCaseLabels: false 5 | AllowShortIfStatementsOnASingleLine: false 6 | AllowShortCaseLabelsOnASingleLine: false 7 | AllowShortLoopsOnASingleLine: false 8 | DerivePointerAlignment: false 9 | PointerAlignment: Right 10 | SpaceAfterCStyleCast: true 11 | TabWidth: 4 12 | UseTab: Never 13 | IndentWidth: 4 14 | BreakBeforeBraces: Linux 15 | AccessModifierOffset: -4 16 | -------------------------------------------------------------------------------- /.ci/check-format.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e -u -o pipefail 4 | 5 | SOURCES=$(find $(git rev-parse --show-toplevel) | egrep "\.(c|cxx|cpp|h|hpp)\$") 6 | 7 | set -x 8 | 9 | for file in ${SOURCES}; 10 | do 11 | clang-format-12 ${file} > expected-format 12 | diff -u -p --label="${file}" --label="expected coding style" ${file} expected-format 13 | done 14 | exit $(clang-format-12 --output-replacements-xml ${SOURCES} | egrep -c "") 15 | -------------------------------------------------------------------------------- /mk/common.mk: -------------------------------------------------------------------------------- 1 | UNAME_S := $(shell uname -s) 2 | ifeq ($(UNAME_S),Darwin) 3 | PRINTF = printf 4 | else 5 | PRINTF = env printf 6 | endif 7 | 8 | # Control the build verbosity 9 | ifeq ("$(VERBOSE)","1") 10 | Q := 11 | VECHO = @true 12 | REDIR = 13 | else 14 | Q := @ 15 | VECHO = @$(PRINTF) 16 | REDIR = >/dev/null 17 | endif 18 | 19 | # Test suite 20 | PASS_COLOR = \e[32;01m 21 | NO_COLOR = \e[0m 22 | 23 | notice = $(PRINTF) "$(PASS_COLOR)$(strip $1)$(NO_COLOR)\n" 24 | 25 | PARALLEL = -j $(shell nproc) 26 | -------------------------------------------------------------------------------- /src/serial.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "bus.h" 6 | 7 | #define COM1_PORT_BASE 0x03f8 8 | #define COM1_PORT_SIZE 8 9 | 10 | typedef struct serial_dev serial_dev_t; 11 | 12 | struct serial_dev { 13 | void *priv; 14 | pthread_t worker_tid; 15 | int infd; /* file descriptor for serial input */ 16 | struct dev dev; 17 | int irq_num; 18 | }; 19 | 20 | void serial_console(serial_dev_t *s); 21 | int serial_init(serial_dev_t *s, struct bus *bus); 22 | void serial_exit(serial_dev_t *s); 23 | -------------------------------------------------------------------------------- /.ci/check-newline.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e -u -o pipefail 4 | 5 | ret=0 6 | show=0 7 | # Reference: https://medium.com/@alexey.inkin/how-to-force-newline-at-end-of-files-and-why-you-should-do-it-fdf76d1d090e 8 | while IFS= read -rd '' f; do 9 | if file --mime-encoding "$f" | grep -qv binary; then 10 | tail -c1 < "$f" | read -r _ || show=1 11 | if [ $show -eq 1 ]; then 12 | echo "Warning: No newline at end of file $f" 13 | ret=1 14 | show=0 15 | fi 16 | fi 17 | done < <(git ls-files -z src tests/arch-test-target) 18 | 19 | exit $ret 20 | -------------------------------------------------------------------------------- /src/diskimg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /* simple backed by disk image file */ 6 | 7 | struct diskimg { 8 | int fd; 9 | size_t size; 10 | }; 11 | 12 | ssize_t diskimg_read(struct diskimg *diskimg, 13 | void *data, 14 | off_t offset, 15 | size_t size); 16 | ssize_t diskimg_write(struct diskimg *diskimg, 17 | void *data, 18 | off_t offset, 19 | size_t size); 20 | int diskimg_init(struct diskimg *diskimg, const char *file_path); 21 | void diskimg_exit(struct diskimg *diskimg); 22 | -------------------------------------------------------------------------------- /src/virtio-net.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "pci.h" 5 | #include "virtio-pci.h" 6 | #include "virtq.h" 7 | 8 | #define VIRTIO_NET_VIRTQ_NUM 2 9 | #define VIRTIO_NET_PCI_CLASS 0x020000 10 | 11 | struct virtio_net_dev { 12 | struct virtio_pci_dev virtio_pci_dev; 13 | struct virtio_net_config config; 14 | struct virtq vq[VIRTIO_NET_VIRTQ_NUM]; 15 | int tapfd; 16 | int irqfd; 17 | int rx_ioeventfd; 18 | int tx_ioeventfd; 19 | int irq_num; 20 | pthread_t rx_thread; 21 | pthread_t tx_thread; 22 | bool enable; 23 | }; 24 | 25 | bool virtio_net_init(struct virtio_net_dev *virtio_net_dev); 26 | void virtio_net_exit(struct virtio_net_dev *virtio_net_dev); 27 | void virtio_net_init_pci(struct virtio_net_dev *virtio_net_dev, 28 | struct pci *pci, 29 | struct bus *io_bus, 30 | struct bus *mmio_bus); 31 | -------------------------------------------------------------------------------- /src/bus.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct dev; 6 | 7 | typedef void (*dev_io_fn)(void *owner, 8 | void *data, 9 | uint8_t is_write, 10 | uint64_t offset, 11 | uint8_t size); 12 | 13 | struct dev { 14 | uint64_t base; 15 | uint64_t len; 16 | void *owner; 17 | dev_io_fn do_io; 18 | struct dev *next; 19 | }; 20 | 21 | struct bus { 22 | uint64_t dev_num; 23 | struct dev *head; 24 | }; 25 | 26 | void bus_register_dev(struct bus *bus, struct dev *dev); 27 | void bus_deregister_dev(struct bus *bus, struct dev *dev); 28 | void bus_handle_io(struct bus *bus, 29 | void *data, 30 | uint8_t is_write, 31 | uint64_t addr, 32 | uint8_t size); 33 | void bus_init(struct bus *bus); 34 | void dev_init(struct dev *dev, 35 | uint64_t base, 36 | uint64_t len, 37 | void *owner, 38 | dev_io_fn do_io); 39 | -------------------------------------------------------------------------------- /src/diskimg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "diskimg.h" 6 | 7 | ssize_t diskimg_read(struct diskimg *diskimg, 8 | void *data, 9 | off_t offset, 10 | size_t size) 11 | { 12 | lseek(diskimg->fd, offset, SEEK_SET); 13 | return read(diskimg->fd, data, size); 14 | } 15 | 16 | ssize_t diskimg_write(struct diskimg *diskimg, 17 | void *data, 18 | off_t offset, 19 | size_t size) 20 | { 21 | lseek(diskimg->fd, offset, SEEK_SET); 22 | return write(diskimg->fd, data, size); 23 | } 24 | 25 | int diskimg_init(struct diskimg *diskimg, const char *file_path) 26 | { 27 | diskimg->fd = open(file_path, O_RDWR); 28 | if (diskimg->fd < 0) 29 | return -1; 30 | struct stat st; 31 | fstat(diskimg->fd, &st); 32 | diskimg->size = st.st_size; 33 | return 0; 34 | } 35 | 36 | void diskimg_exit(struct diskimg *diskimg) 37 | { 38 | close(diskimg->fd); 39 | } 40 | -------------------------------------------------------------------------------- /src/virtio-blk.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "diskimg.h" 9 | #include "pci.h" 10 | #include "virtio-pci.h" 11 | #include "virtq.h" 12 | 13 | #define VIRTIO_BLK_VIRTQ_NUM 1 14 | #define VIRTIO_BLK_PCI_CLASS 0x018000 15 | 16 | struct virtio_blk_req { 17 | uint32_t type; 18 | uint32_t reserved; 19 | uint64_t sector; 20 | uint8_t *data; 21 | uint16_t data_size; 22 | uint8_t *status; 23 | }; 24 | 25 | struct virtio_blk_dev { 26 | struct virtio_pci_dev virtio_pci_dev; 27 | struct virtio_blk_config config; 28 | struct virtq vq[VIRTIO_BLK_VIRTQ_NUM]; 29 | int irqfd; 30 | int ioeventfd; 31 | int irq_num; 32 | pthread_t vq_avail_thread; 33 | pthread_t worker_thread; 34 | struct diskimg *diskimg; 35 | bool enable; 36 | }; 37 | 38 | void virtio_blk_init(struct virtio_blk_dev *virtio_blk_dev); 39 | void virtio_blk_exit(struct virtio_blk_dev *dev); 40 | void virtio_blk_init_pci(struct virtio_blk_dev *dev, 41 | struct diskimg *diskimg, 42 | struct pci *pci, 43 | struct bus *io_bus, 44 | struct bus *mmio_bus); 45 | -------------------------------------------------------------------------------- /src/virtq.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct virtq; 8 | 9 | struct virtq_ops { 10 | void (*complete_request)(struct virtq *vq); 11 | void (*enable_vq)(struct virtq *vq); 12 | void (*notify_used)(struct virtq *vq); 13 | }; 14 | 15 | struct virtq_info { 16 | uint16_t size; 17 | uint16_t msix_vector; 18 | uint16_t enable; 19 | uint16_t notify_off; 20 | uint64_t desc_addr; 21 | uint64_t device_addr; 22 | uint64_t driver_addr; 23 | } __attribute__((packed)); 24 | 25 | /* packed virtqueue */ 26 | struct virtq { 27 | struct vring_packed_desc *desc_ring; 28 | struct vring_packed_desc_event *device_event; 29 | struct vring_packed_desc_event *guest_event; 30 | struct virtq_info info; 31 | void *dev; 32 | uint16_t next_avail_idx; 33 | bool used_wrap_count; 34 | struct virtq_ops *ops; 35 | }; 36 | 37 | struct vring_packed_desc *virtq_get_avail(struct virtq *vq); 38 | bool virtq_check_next(struct vring_packed_desc *desc); 39 | void virtq_enable(struct virtq *vq); 40 | void virtq_disable(struct virtq *vq); 41 | void virtq_complete_request(struct virtq *vq); 42 | void virtq_notify_used(struct virtq *vq); 43 | void virtq_deassert_irq(struct virtq *vq); 44 | void virtq_handle_avail(struct virtq *vq); 45 | void virtq_init(struct virtq *vq, void *dev, struct virtq_ops *ops); 46 | -------------------------------------------------------------------------------- /src/arch/arm64/vm-arch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef SZ_64K 4 | #define SZ_64K (1UL << 16) 5 | #endif 6 | 7 | #include 8 | 9 | /* 10 | * The maximum size of the device tree is 2MB. 11 | * Reference: https://docs.kernel.org/arm64/booting.html 12 | */ 13 | #define FDT_MAX_SIZE (1UL << 21) 14 | 15 | /* 16 | * Memory map for guest memory 17 | * 18 | * 0 - 64K I/O Ports 19 | * 1M - 16M GIC 20 | * 1GB - 2GB PCI MMIO 21 | * 2GB - DRAM 22 | */ 23 | 24 | #define ARM_IOPORT_BASE 0 25 | #define ARM_IOPORT_SIZE (1UL << 16) 26 | 27 | #define ARM_GIC_BASE 0x100000UL 28 | 29 | #define ARM_GIC_DIST_BASE ARM_GIC_BASE 30 | #define ARM_GIC_DIST_SIZE KVM_VGIC_V3_DIST_SIZE 31 | 32 | #define ARM_GIC_REDIST_CPUI_BASE (ARM_GIC_DIST_BASE + ARM_GIC_DIST_SIZE) 33 | #define ARM_GIC_REDIST_CPUI_SIZE KVM_VGIC_V3_REDIST_SIZE 34 | 35 | #define ARM_PCI_CFG_BASE 0x40000000UL 36 | #define ARM_PCI_CFG_SIZE (1UL << 16) 37 | 38 | #define ARM_PCI_MMIO_BASE (ARM_PCI_CFG_BASE + ARM_PCI_CFG_SIZE) 39 | #define ARM_PCI_MMIO_SIZE (RAM_BASE - ARM_PCI_MMIO_BASE) 40 | 41 | /* 128 MB for kernel */ 42 | #define ARM_KERNEL_BASE RAM_BASE 43 | #define ARM_KERNEL_SIZE 0x8000000UL 44 | 45 | /* 128 MB for initrd */ 46 | #define ARM_INITRD_BASE (ARM_KERNEL_BASE + ARM_KERNEL_SIZE) 47 | #define ARM_INITRD_SIZE 0x8000000UL 48 | 49 | /* For FTB */ 50 | #define ARM_FDT_BASE (ARM_INITRD_BASE + ARM_INITRD_SIZE) 51 | #define ARM_FDT_SIZE FDT_MAX_SIZE 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | kvm-host is freely redistributable under the two-clause BSD License: 2 | 3 | Copyright (C) 2021-2023 National Cheng Kung University, Taiwan. 4 | Copyright (C) 2020 Serge Zaitsev. 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions 9 | are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /src/vm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define RAM_SIZE (1 << 30) 4 | 5 | #include "pci.h" 6 | #include "serial.h" 7 | #include "virtio-blk.h" 8 | #include "virtio-net.h" 9 | 10 | typedef struct { 11 | int kvm_fd, vm_fd, vcpu_fd; 12 | void *mem; 13 | serial_dev_t serial; 14 | struct bus mmio_bus; 15 | struct bus io_bus; 16 | struct pci pci; 17 | struct diskimg diskimg; 18 | struct virtio_blk_dev virtio_blk_dev; 19 | struct virtio_net_dev virtio_net_dev; 20 | void *priv; 21 | } vm_t; 22 | 23 | int vm_arch_init(vm_t *v); 24 | int vm_arch_cpu_init(vm_t *v); 25 | int vm_arch_init_platform_device(vm_t *v); 26 | int vm_arch_load_image(vm_t *v, void *image, size_t size); 27 | int vm_arch_load_initrd(vm_t *v, void *initrd, size_t size); 28 | 29 | int vm_init(vm_t *v); 30 | int vm_load_image(vm_t *v, const char *image_path); 31 | int vm_load_initrd(vm_t *v, const char *initrd_path); 32 | int vm_load_diskimg(vm_t *v, const char *diskimg_file); 33 | int vm_late_init(vm_t *v); 34 | int vm_enable_net(vm_t *v); 35 | int vm_run(vm_t *v); 36 | int vm_irq_line(vm_t *v, int irq, int level); 37 | void *vm_guest_to_host(vm_t *v, uint64_t guest); 38 | void vm_irqfd_register(vm_t *v, int fd, int gsi, int flags); 39 | void vm_ioeventfd_register(vm_t *v, 40 | int fd, 41 | unsigned long long addr, 42 | int len, 43 | int flags); 44 | void vm_handle_io(vm_t *v, struct kvm_run *run); 45 | void vm_handle_mmio(vm_t *v, struct kvm_run *run); 46 | void vm_exit(vm_t *v); 47 | -------------------------------------------------------------------------------- /src/bus.c: -------------------------------------------------------------------------------- 1 | #include "bus.h" 2 | #include 3 | 4 | static inline struct dev *bus_find_dev(struct bus *bus, uint64_t addr) 5 | { 6 | struct dev **p = &bus->head; 7 | 8 | for (; *p; p = &(*p)->next) { 9 | uint64_t start = (*p)->base; 10 | uint64_t end = start + (*p)->len - 1; 11 | if (addr >= start && addr <= end) 12 | return *p; 13 | } 14 | return NULL; 15 | } 16 | 17 | void bus_handle_io(struct bus *bus, 18 | void *data, 19 | uint8_t is_write, 20 | uint64_t addr, 21 | uint8_t size) 22 | { 23 | struct dev *dev = bus_find_dev(bus, addr); 24 | 25 | if (dev && addr + size - 1 <= dev->base + dev->len - 1) { 26 | dev->do_io(dev->owner, data, is_write, addr - dev->base, size); 27 | } 28 | } 29 | 30 | void bus_register_dev(struct bus *bus, struct dev *dev) 31 | { 32 | dev->next = bus->head; 33 | bus->head = dev; 34 | bus->dev_num++; 35 | } 36 | 37 | void bus_deregister_dev(struct bus *bus, struct dev *dev) 38 | { 39 | struct dev **p = &bus->head; 40 | 41 | while (*p != dev && *p) { 42 | p = &(*p)->next; 43 | } 44 | 45 | if (*p) 46 | *p = (*p)->next; 47 | } 48 | 49 | void bus_init(struct bus *bus) 50 | { 51 | bus->dev_num = 0; 52 | bus->head = NULL; 53 | } 54 | 55 | void dev_init(struct dev *dev, 56 | uint64_t base, 57 | uint64_t len, 58 | void *owner, 59 | dev_io_fn do_io) 60 | { 61 | dev->base = base; 62 | dev->len = len; 63 | dev->owner = owner; 64 | dev->do_io = do_io; 65 | dev->next = NULL; 66 | } 67 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #define container_of(ptr, type, member) \ 5 | ({ \ 6 | void *__mptr = (void *) (ptr); \ 7 | ((type *) (__mptr - offsetof(type, member))); \ 8 | }) 9 | 10 | #define FIFO_LEN 64 11 | #define FIFO_MASK (FIFO_LEN - 1) 12 | 13 | struct fifo { 14 | uint8_t data[FIFO_LEN]; 15 | unsigned int head, tail; 16 | }; 17 | 18 | #define fifo_is_empty(fifo) ((fifo)->head == (fifo)->tail) 19 | 20 | #define fifo_is_full(fifo) ((fifo)->tail - (fifo)->head > FIFO_MASK) 21 | 22 | #define fifo_capacity(fifo) ((fifo)->tail - (fifo)->head) 23 | 24 | #define fifo_put(fifo, value) \ 25 | ({ \ 26 | unsigned int __ret = !fifo_is_full(fifo); \ 27 | if (__ret) { \ 28 | (fifo)->data[(fifo)->tail & FIFO_MASK] = value; \ 29 | (fifo)->tail++; \ 30 | } \ 31 | __ret; \ 32 | }) 33 | 34 | #define fifo_get(fifo, value) \ 35 | ({ \ 36 | unsigned int __ret = !fifo_is_empty(fifo); \ 37 | if (__ret) { \ 38 | value = (fifo)->data[(fifo)->head & FIFO_MASK]; \ 39 | (fifo)->head++; \ 40 | } \ 41 | __ret; \ 42 | }) 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/virtq.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "virtq.h" 6 | 7 | void virtq_complete_request(struct virtq *vq) 8 | { 9 | vq->ops->complete_request(vq); 10 | } 11 | 12 | void virtq_notify_used(struct virtq *vq) 13 | { 14 | vq->ops->notify_used(vq); 15 | } 16 | 17 | void virtq_enable(struct virtq *vq) 18 | { 19 | vq->ops->enable_vq(vq); 20 | } 21 | 22 | void virtq_disable(struct virtq *vq) {} 23 | 24 | #define VIRTQ_SIZE 128 25 | void virtq_init(struct virtq *vq, void *dev, struct virtq_ops *ops) 26 | { 27 | vq->info.size = VIRTQ_SIZE; 28 | vq->info.enable = 0; 29 | vq->next_avail_idx = 0; 30 | vq->used_wrap_count = 1; 31 | vq->ops = ops; 32 | vq->dev = dev; 33 | } 34 | 35 | bool virtq_check_next(struct vring_packed_desc *desc) 36 | { 37 | return desc->flags & VRING_DESC_F_NEXT; 38 | } 39 | 40 | struct vring_packed_desc *virtq_get_avail(struct virtq *vq) 41 | { 42 | struct vring_packed_desc *desc = &vq->desc_ring[vq->next_avail_idx]; 43 | uint16_t flags = desc->flags; 44 | bool avail = flags & (1ULL << VRING_PACKED_DESC_F_AVAIL); 45 | bool used = flags & (1ULL << VRING_PACKED_DESC_F_USED); 46 | 47 | if (avail != vq->used_wrap_count || used == vq->used_wrap_count) { 48 | return NULL; 49 | } 50 | vq->next_avail_idx++; 51 | if (vq->next_avail_idx >= vq->info.size) { 52 | vq->next_avail_idx -= vq->info.size; 53 | vq->used_wrap_count ^= 1; 54 | } 55 | return desc; 56 | } 57 | 58 | void virtq_handle_avail(struct virtq *vq) 59 | { 60 | if (!vq->info.enable) 61 | return; 62 | virtq_complete_request(vq); 63 | if (vq->guest_event->flags == VRING_PACKED_EVENT_FLAG_ENABLE) 64 | virtq_notify_used(vq); 65 | } 66 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | detect-code-related-file-changes: 7 | runs-on: ubuntu-22.04 8 | outputs: 9 | has_code_related_changes: ${{ steps.set_has_code_related_changes.outputs.has_code_related_changes }} 10 | steps: 11 | - name: Check out the repo 12 | uses: actions/checkout@v4 13 | - name: Test changed files 14 | id: changed-files 15 | uses: tj-actions/changed-files@v44 16 | with: 17 | files: | 18 | .ci/** 19 | mk/** 20 | src/** 21 | target/** 22 | .clang-format 23 | Makefile 24 | - name: Set has_code_related_changes 25 | id: set_has_code_related_changes 26 | run: | 27 | if [[ ${{ steps.changed-files.outputs.any_changed }} == true ]]; then 28 | echo "has_code_related_changes=true" >> $GITHUB_OUTPUT 29 | else 30 | echo "has_code_related_changes=false" >> $GITHUB_OUTPUT 31 | fi 32 | 33 | host-x64: 34 | needs: [detect-code-related-file-changes] 35 | if: needs.detect-code-related-file-changes.outputs.has_code_related_changes == 'true' 36 | runs-on: ubuntu-22.04 37 | steps: 38 | - uses: actions/checkout@v4 39 | - name: default build 40 | run: make 41 | 42 | coding-style: 43 | needs: [detect-code-related-file-changes] 44 | if: needs.detect-code-related-file-changes.outputs.has_code_related_changes == 'true' 45 | runs-on: ubuntu-22.04 46 | steps: 47 | - uses: actions/checkout@v4 48 | - name: coding convention 49 | run: | 50 | sudo apt-get install -q -y clang-format-12 51 | .ci/check-newline.sh 52 | .ci/check-format.sh 53 | shell: bash 54 | -------------------------------------------------------------------------------- /src/pci.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "bus.h" 8 | 9 | union pci_config_address { 10 | struct { 11 | unsigned reg_offset : 2; 12 | unsigned reg_num : 6; 13 | unsigned func_num : 3; 14 | unsigned dev_num : 5; 15 | unsigned bus_num : 8; 16 | unsigned reserved : 7; 17 | unsigned enable_bit : 1; 18 | }; 19 | uint32_t value; 20 | }; 21 | 22 | #define PCI_HDR_READ(hdr, offset, width) \ 23 | (*((uint##width##_t *) ((uintptr_t) hdr + offset))) 24 | #define PCI_HDR_WRITE(hdr, offset, value, width) \ 25 | ((uint##width##_t *) ((uintptr_t) hdr + offset))[0] = value 26 | #define PCI_BAR_OFFSET(bar) (PCI_BASE_ADDRESS_0 + ((bar) << 2)) 27 | 28 | struct pci_dev { 29 | uint8_t cfg_space[PCI_CFG_SPACE_SIZE]; 30 | void *hdr; 31 | uint32_t bar_size[6]; 32 | bool bar_active[6]; 33 | bool bar_is_io_space[6]; 34 | struct dev space_dev[6]; 35 | struct dev config_dev; 36 | struct bus *io_bus; 37 | struct bus *mmio_bus; 38 | struct bus *pci_bus; 39 | }; 40 | 41 | struct pci { 42 | union pci_config_address pci_addr; 43 | struct bus pci_bus; 44 | struct dev pci_bus_dev; 45 | struct dev pci_addr_dev; 46 | struct dev pci_mmio_dev; 47 | }; 48 | 49 | void pci_set_bar(struct pci_dev *dev, 50 | uint8_t bar, 51 | uint32_t bar_size, 52 | bool is_io_space, 53 | dev_io_fn do_io); 54 | void pci_set_status(struct pci_dev *dev, uint16_t status); 55 | void pci_dev_register(struct pci_dev *dev); 56 | void pci_dev_init(struct pci_dev *dev, 57 | struct pci *pci, 58 | struct bus *io_bus, 59 | struct bus *mmio_bus); 60 | void pci_init(struct pci *pci); 61 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include mk/common.mk 2 | 3 | ARCH ?= $(shell uname -m) 4 | PWD ?= $(shell pwd) 5 | 6 | CC ?= gcc 7 | CFLAGS = -O2 8 | CFLAGS += -Wall -std=gnu99 9 | CFLAGS += -I$(PWD)/src 10 | CFLAGS += -g 11 | LDFLAGS = -lpthread 12 | 13 | OUT ?= build 14 | BIN = $(OUT)/kvm-host 15 | 16 | all: $(BIN) 17 | 18 | FDT_OBJS := \ 19 | dtc/libfdt/fdt.o \ 20 | dtc/libfdt/fdt_sw.o \ 21 | dtc/libfdt/fdt_strerror.o 22 | 23 | FDT_CFLAGS := -Isrc/dtc/libfdt 24 | 25 | OBJS := \ 26 | vm.o \ 27 | serial.o \ 28 | bus.o \ 29 | pci.o \ 30 | virtio-pci.o \ 31 | virtq.o \ 32 | virtio-blk.o \ 33 | virtio-net.o \ 34 | diskimg.o \ 35 | main.o 36 | 37 | ifeq ($(ARCH), x86_64) 38 | CFLAGS += -I$(PWD)/src/arch/x86 39 | CFLAGS += -include src/arch/x86/desc.h 40 | OBJS += arch/x86/vm.o 41 | endif 42 | ifeq ($(ARCH), aarch64) 43 | CFLAGS += -I$(PWD)/src/arch/arm64 44 | CFLAGS += -include src/arch/arm64/desc.h 45 | CFLAGS += $(FDT_CFLAGS) 46 | OBJS += arch/arm64/vm.o 47 | OBJS += $(FDT_OBJS) 48 | endif 49 | 50 | OBJS := $(addprefix $(OUT)/,$(OBJS)) 51 | deps := $(OBJS:%.o=%.o.d) 52 | 53 | $(BIN): $(OBJS) 54 | $(VECHO) " LD\t$@\n" 55 | $(Q)$(CC) $(LDFLAGS) -o $@ $^ $(LDFLAGS) 56 | 57 | $(OUT)/%.o: src/%.c 58 | $(Q)mkdir -p $(shell dirname $@) 59 | $(VECHO) " CC\t$@\n" 60 | $(Q)$(CC) -o $@ $(CFLAGS) -c -MMD -MF $@.d $< 61 | 62 | # Rules for downloading and building the minimal Linux system 63 | include mk/external.mk 64 | 65 | $(OUT)/ext4.img: 66 | $(Q)dd if=/dev/zero of=$@ bs=4k count=600 67 | $(Q)mkfs.ext4 -F $@ 68 | 69 | check: $(BIN) $(LINUX_IMG) $(ROOTFS_IMG) $(OUT)/ext4.img 70 | $(VECHO) "\nOnce the message 'Kernel panic' appears, press Ctrl-C to exit\n\n" 71 | $(Q)sudo $(BIN) -k $(LINUX_IMG) -i $(ROOTFS_IMG) -d $(OUT)/ext4.img 72 | 73 | clean: 74 | $(VECHO) "Cleaning...\n" 75 | $(Q)rm -f $(OBJS) $(deps) $(BIN) 76 | 77 | distclean: clean 78 | $(Q)rm -rf build 79 | 80 | -include $(deps) 81 | -------------------------------------------------------------------------------- /src/virtio-pci.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "pci.h" 6 | #include "virtq.h" 7 | 8 | #define VIRTIO_PCI_VENDOR_ID 0x1AF4 9 | #define VIRTIO_PCI_DEVICE_ID_NET 0x1041 10 | #define VIRTIO_PCI_DEVICE_ID_BLK 0x1042 11 | #define VIRTIO_PCI_CAP_NUM 5 12 | #define VIRTIO_PCI_ISR_QUEUE 1 13 | 14 | struct virtio_pci_isr_cap { 15 | uint32_t isr_status; 16 | }; 17 | 18 | struct virtio_pci_notify_data { 19 | uint16_t vqn; 20 | uint16_t next; 21 | } __attribute__((packed)); 22 | 23 | struct virtio_pci_config { 24 | struct virtio_pci_common_cfg common_cfg; 25 | struct virtio_pci_isr_cap isr_cap; 26 | struct virtio_pci_notify_data notify_data; 27 | void *dev_cfg; 28 | }; 29 | 30 | struct virtio_pci_dev { 31 | struct pci_dev pci_dev; 32 | struct virtio_pci_config config; 33 | uint64_t device_feature; 34 | uint64_t guest_feature; 35 | struct virtio_pci_notify_cap *notify_cap; 36 | struct virtio_pci_cap *dev_cfg_cap; 37 | struct virtq *vq; 38 | }; 39 | 40 | uint64_t virtio_pci_get_notify_addr(struct virtio_pci_dev *dev, 41 | struct virtq *vq); 42 | void virtio_pci_set_dev_cfg(struct virtio_pci_dev *virtio_pci_dev, 43 | void *dev_cfg, 44 | uint8_t len); 45 | void virtio_pci_set_pci_hdr(struct virtio_pci_dev *dev, 46 | uint16_t device_id, 47 | uint32_t class, 48 | uint8_t irq_line); 49 | void virtio_pci_set_virtq(struct virtio_pci_dev *dev, 50 | struct virtq *vq, 51 | uint16_t num_queues); 52 | void virtio_pci_add_feature(struct virtio_pci_dev *dev, uint64_t feature); 53 | void virtio_pci_enable(struct virtio_pci_dev *dev); 54 | void virtio_pci_init(struct virtio_pci_dev *dev, 55 | struct pci *pci, 56 | struct bus *io_bus, 57 | struct bus *mmio_bus); 58 | void virtio_pci_exit(); 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kvm-host 2 | 3 | `kvm-host` is a minimalist type 2 hypervisor using Linux [Kernel-based Virtual Machine](https://en.wikipedia.org/wiki/Kernel-based_Virtual_Machine) (KVM), 4 | capable of running Linux kernel partially. 5 | 6 | ## Supported Architecture 7 | 8 | * x86-64 9 | * Aarch64 (GICv2/GICv3) 10 | 11 | ## Build and Run 12 | 13 | Fetch required submodules (only necessary for ARM build): 14 | ```shell 15 | git submodule update --init --recursive 16 | ``` 17 | 18 | To compile: 19 | ```shell 20 | make 21 | ``` 22 | 23 | Download and build Linux kernel from scratch: 24 | ```shell 25 | make build/bzImage 26 | ``` 27 | (or `make build/Image` for Arm64 host) 28 | 29 | Run Linux guest with `kvm-host`: 30 | ```shell 31 | make check 32 | ``` 33 | 34 | ## Usage 35 | 36 | ``` 37 | build/kvm-host -k bzImage [-i initrd] [-d disk-image] 38 | ``` 39 | 40 | `bzImage` is the path to linux kernel bzImage. The bzImage file is in a specific format, 41 | containing concatenated `bootsect.o + setup.o + misc.o + piggy.o`. `initrd` is the path to 42 | initial RAM disk image, which is an optional argument. 43 | `disk-image` is the path to disk image which can be mounted as a block device via virtio. For the reference Linux guest, ext4 filesystem is used for disk image. 44 | 45 | To exit kvm-host, press "Ctrl-A", release both keys, and then press "x". 46 | 47 | ## License 48 | 49 | `kvm-host` is released under the BSD 2 clause license. Use of this source code is governed by 50 | a BSD-style license that can be found in the LICENSE file. 51 | 52 | ## References 53 | * [kvmtool](https://github.com/kvmtool/kvmtool) 54 | * [KVM (Kernel-based Virtual Machine) API](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt) 55 | * [The Linux/x86 Boot Protocol](https://www.kernel.org/doc/html/latest/x86/boot.html) 56 | * [Using the KVM API](https://lwn.net/Articles/658511/) 57 | * [gokvm](https://github.com/bobuhiro11/gokvm) 58 | * [KVM Host in a few lines of code](https://zserge.com/posts/kvm/) 59 | * [crosvm - The Chrome OS Virtual Machine Monitor](https://chromium.googlesource.com/chromiumos/platform/crosvm/) 60 | * [mvisor](https://github.com/tenclass/mvisor) 61 | -------------------------------------------------------------------------------- /configs/linux-arm64.config: -------------------------------------------------------------------------------- 1 | # CONFIG_CROSS_MEMORY_ATTACH is not set 2 | # CONFIG_CPU_ISOLATION is not set 3 | CONFIG_BLK_DEV_INITRD=y 4 | # CONFIG_RD_GZIP is not set 5 | # CONFIG_RD_BZIP2 is not set 6 | # CONFIG_RD_LZMA is not set 7 | # CONFIG_RD_XZ is not set 8 | # CONFIG_RD_LZO is not set 9 | # CONFIG_RD_LZ4 is not set 10 | # CONFIG_RD_ZSTD is not set 11 | CONFIG_CC_OPTIMIZE_FOR_SIZE=y 12 | # CONFIG_MULTIUSER is not set 13 | # CONFIG_SYSFS_SYSCALL is not set 14 | # CONFIG_FHANDLE is not set 15 | # CONFIG_POSIX_TIMERS is not set 16 | # CONFIG_BUG is not set 17 | # CONFIG_BASE_FULL is not set 18 | # CONFIG_FUTEX is not set 19 | # CONFIG_EPOLL is not set 20 | # CONFIG_SIGNALFD is not set 21 | # CONFIG_TIMERFD is not set 22 | # CONFIG_EVENTFD is not set 23 | # CONFIG_SHMEM is not set 24 | # CONFIG_AIO is not set 25 | # CONFIG_IO_URING is not set 26 | # CONFIG_ADVISE_SYSCALLS is not set 27 | # CONFIG_MEMBARRIER is not set 28 | # CONFIG_KALLSYMS is not set 29 | # CONFIG_RSEQ is not set 30 | CONFIG_EMBEDDED=y 31 | # CONFIG_UNMAP_KERNEL_AT_EL0 is not set 32 | # CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY is not set 33 | # CONFIG_RODATA_FULL_DEFAULT_ENABLED is not set 34 | # CONFIG_ARM64_TAGGED_ADDR_ABI is not set 35 | # CONFIG_ARM64_SVE is not set 36 | # CONFIG_EFI is not set 37 | # CONFIG_SECCOMP is not set 38 | # CONFIG_STACKPROTECTOR is not set 39 | # CONFIG_VMAP_STACK is not set 40 | # CONFIG_RANDOMIZE_KSTACK_OFFSET is not set 41 | # CONFIG_COREDUMP is not set 42 | CONFIG_PCI=y 43 | CONFIG_PCI_HOST_GENERIC=y 44 | CONFIG_DEVTMPFS=y 45 | CONFIG_DEVTMPFS_MOUNT=y 46 | # CONFIG_ALLOW_DEV_COREDUMP is not set 47 | # CONFIG_ARM_SMCCC_SOC_ID is not set 48 | CONFIG_VIRTIO_BLK=y 49 | CONFIG_SERIAL_8250=y 50 | CONFIG_SERIAL_8250_CONSOLE=y 51 | CONFIG_SERIAL_8250_NR_UARTS=1 52 | CONFIG_SERIAL_8250_RUNTIME_UARTS=1 53 | CONFIG_SERIAL_OF_PLATFORM=y 54 | # CONFIG_HWMON is not set 55 | # CONFIG_USB_SUPPORT is not set 56 | CONFIG_VIRTIO_PCI=y 57 | # CONFIG_VHOST_MENU is not set 58 | # CONFIG_SURFACE_PLATFORMS is not set 59 | # CONFIG_IOMMU_SUPPORT is not set 60 | CONFIG_EXT4_FS=y 61 | # CONFIG_FILE_LOCKING is not set 62 | # CONFIG_DNOTIFY is not set 63 | # CONFIG_INOTIFY_USER is not set 64 | # CONFIG_MISC_FILESYSTEMS is not set 65 | # CONFIG_CRYPTO_HW is not set 66 | # CONFIG_DEBUG_MISC is not set 67 | # CONFIG_FTRACE is not set 68 | # CONFIG_STRICT_DEVMEM is not set 69 | -------------------------------------------------------------------------------- /mk/external.mk: -------------------------------------------------------------------------------- 1 | # For each external target, the following must be defined in advance: 2 | # _SRC_URL : the hyperlink which points to archive. 3 | # _SRC : the file to be read by specific executable. 4 | # _SRC_SHA1 : the checksum of the content in _SRC 5 | 6 | TOP=$(shell pwd) 7 | CONF=$(TOP)/configs 8 | FILE=$(TOP)/target 9 | 10 | # Linux kernel 11 | LINUX_VER = 6.1.100 12 | LINUX_SRC_URL = https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-${LINUX_VER}.tar.xz 13 | LINUX_SRC = $(OUT)/linux-${LINUX_VER} 14 | LINUX_SRC_SHA1 = 49d075022276c627bf9ef583d5415d46a9aec665 15 | 16 | # BusyBox 17 | BUSYBOX_VER=1.36.1 18 | BUSYBOX_SRC_URL = https://busybox.net/downloads/busybox-${BUSYBOX_VER}.tar.bz2 19 | BUSYBOX_SRC = $(OUT)/busybox-${BUSYBOX_VER} 20 | BUSYBOX_SRC_SHA1 = a5d40ca0201b20909f7a8a561adf57adccc8a877 21 | 22 | define download-n-extract 23 | $(eval $(T)_SRC_ARCHIVE = $(OUT)/$(shell basename $($(T)_SRC_URL))) 24 | $($(T)_SRC_ARCHIVE): 25 | $(VECHO) " GET\t$$@\n" 26 | $(Q)curl --progress-bar -o $$@ -L -C - "$(strip $($(T)_SRC_URL))" 27 | $(Q)echo "$(strip $$($(T)_SRC_SHA1)) $$@" | shasum -c 28 | $($(T)_SRC): $($(T)_SRC_ARCHIVE) 29 | $(VECHO) "Unpacking $$@ ... " 30 | $(Q)tar -xf $$< -C ${OUT} && $(call notice, [OK]) 31 | endef 32 | 33 | EXTERNAL_SRC = LINUX BUSYBOX 34 | $(foreach T,$(EXTERNAL_SRC),$(eval $(download-n-extract))) 35 | 36 | # Build Linux kernel image 37 | ifeq ($(ARCH), x86_64) 38 | LINUX_IMG_NAME = bzImage 39 | ARCH = x86 40 | else ifeq ($(ARCH), aarch64) 41 | LINUX_IMG_NAME = Image 42 | ARCH = arm64 43 | else 44 | $(error Unsupported architecture) 45 | endif 46 | LINUX_IMG := $(addprefix $(OUT)/,$(LINUX_IMG_NAME)) 47 | 48 | $(LINUX_IMG): $(LINUX_SRC) 49 | $(VECHO) "Configuring Linux kernel... " 50 | $(Q)cp -f ${CONF}/linux-$(ARCH).config $/dev/null $(REDIR)) 65 | $(Q)(cd $< ; $(MAKE) CONFIG_PREFIX='../rootfs' install $(REDIR)) && $(call notice, [OK]) 66 | 67 | # Generate root file system 68 | ROOTFS_IMG = $(OUT)/rootfs.cpio 69 | $(ROOTFS_IMG): $(BUSYBOX_BIN) 70 | $(VECHO) "Generating root file system... " 71 | $(Q)(cd $(OUT)/rootfs ; \ 72 | mv linuxrc init ; \ 73 | mkdir -p etc/init.d ; \ 74 | cp -f $(FILE)/rc-startup etc/init.d/rcS ; \ 75 | chmod 755 etc/init.d/rcS ; \ 76 | find . | cpio -o --format=newc > $(abspath $@) 2>/dev/null) && $(call notice, [OK]) 77 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "err.h" 7 | #include "vm.h" 8 | 9 | static char *kernel_file = NULL, *initrd_file = NULL, *diskimg_file = NULL; 10 | 11 | #define print_option(args, help_msg) printf(" %-30s%s", args, help_msg) 12 | 13 | static void usage(const char *execpath) 14 | { 15 | printf("\n usage: %s -k bzImage [options]\n\n", execpath); 16 | printf("options:\n"); 17 | 18 | print_option("-h, --help", "Print help of CLI and exit.\n"); 19 | print_option("-i, --initrd initrd", "Initial RAM disk image\n"); 20 | print_option("-d, --disk disk-image", 21 | "Disk image for virtio-blk devices\n"); 22 | } 23 | 24 | static struct termios saved_attributes; 25 | 26 | static void reset_input_mode(void) 27 | { 28 | tcsetattr(STDIN_FILENO, TCSANOW, &saved_attributes); 29 | } 30 | 31 | static void set_input_mode(void) 32 | { 33 | struct termios tattr; 34 | /* Make sure stdin is a terminal. */ 35 | if (!isatty(STDIN_FILENO)) { 36 | fprintf(stderr, "Not a terminal.\n"); 37 | exit(EXIT_FAILURE); 38 | } 39 | 40 | /* Save the terminal attributes so we can restore them later. */ 41 | tcgetattr(STDIN_FILENO, &saved_attributes); 42 | atexit(reset_input_mode); 43 | 44 | tattr = saved_attributes; 45 | tattr.c_lflag &= ~(ICANON | ECHO | ISIG); 46 | tcsetattr(STDIN_FILENO, TCSANOW, &tattr); 47 | } 48 | 49 | int main(int argc, char *argv[]) 50 | { 51 | int option_index = 0; 52 | struct option opts[] = { 53 | {"kernel", 1, NULL, 'k'}, 54 | {"initrd", 1, NULL, 'i'}, 55 | {"disk", 1, NULL, 'd'}, 56 | {"help", 0, NULL, 'h'}, 57 | }; 58 | 59 | int c; 60 | while ((c = getopt_long(argc, argv, "k:i:d:h", opts, &option_index)) != 61 | -1) { 62 | switch (c) { 63 | case 'i': 64 | initrd_file = optarg; 65 | break; 66 | case 'k': 67 | kernel_file = optarg; 68 | break; 69 | case 'd': 70 | diskimg_file = optarg; 71 | break; 72 | case 'h': 73 | usage(argv[0]); 74 | exit(123); 75 | default: 76 | break; 77 | } 78 | } 79 | 80 | set_input_mode(); 81 | 82 | vm_t vm; 83 | if (vm_init(&vm) < 0) 84 | return throw_err("Failed to initialize guest vm"); 85 | 86 | if (!kernel_file) 87 | return throw_err( 88 | "The kernel image must be used as the input of kvm-host!"); 89 | 90 | if (vm_load_image(&vm, kernel_file) < 0) 91 | return throw_err("Failed to load guest image"); 92 | if (initrd_file && vm_load_initrd(&vm, initrd_file) < 0) 93 | return throw_err("Failed to load initrd"); 94 | if (diskimg_file && vm_load_diskimg(&vm, diskimg_file) < 0) 95 | return throw_err("Failed to load disk image"); 96 | if (vm_enable_net(&vm) < 0) 97 | fprintf(stderr, "Failed to enable virtio-net device\n"); 98 | 99 | if (vm_late_init(&vm) < 0) 100 | return -1; 101 | 102 | vm_run(&vm); 103 | vm_exit(&vm); 104 | 105 | return 0; 106 | } 107 | -------------------------------------------------------------------------------- /configs/linux-x86.config: -------------------------------------------------------------------------------- 1 | # CONFIG_LOCALVERSION_AUTO is not set 2 | CONFIG_KERNEL_XZ=y 3 | # CONFIG_CROSS_MEMORY_ATTACH is not set 4 | # CONFIG_PREEMPT_DYNAMIC is not set 5 | CONFIG_BLK_DEV_INITRD=y 6 | # CONFIG_RD_GZIP is not set 7 | # CONFIG_RD_BZIP2 is not set 8 | # CONFIG_RD_LZMA is not set 9 | # CONFIG_RD_XZ is not set 10 | # CONFIG_RD_LZO is not set 11 | # CONFIG_RD_LZ4 is not set 12 | # CONFIG_RD_ZSTD is not set 13 | CONFIG_CC_OPTIMIZE_FOR_SIZE=y 14 | # CONFIG_MULTIUSER is not set 15 | # CONFIG_SGETMASK_SYSCALL is not set 16 | # CONFIG_SYSFS_SYSCALL is not set 17 | # CONFIG_FHANDLE is not set 18 | # CONFIG_POSIX_TIMERS=y 19 | # CONFIG_BUG is not set 20 | # CONFIG_PCSPKR_PLATFORM is not set 21 | # CONFIG_BASE_FULL is not set 22 | # CONFIG_FUTEX is not set 23 | # CONFIG_EPOLL is not set 24 | # CONFIG_SIGNALFD is not set 25 | # CONFIG_TIMERFD is not set 26 | # CONFIG_EVENTFD is not set 27 | # CONFIG_SHMEM is not set 28 | # CONFIG_AIO is not set 29 | # CONFIG_IO_URING is not set 30 | # CONFIG_ADVISE_SYSCALLS is not set 31 | # CONFIG_MEMBARRIER is not set 32 | # CONFIG_KALLSYMS is not set 33 | # CONFIG_RSEQ is not set 34 | CONFIG_EMBEDDED=y 35 | # CONFIG_X86_EXTENDED_PLATFORM is not set 36 | # CONFIG_SCHED_OMIT_FRAME_POINTER is not set 37 | # CONFIG_DMI is not set 38 | # CONFIG_X86_MCE is not set 39 | # CONFIG_PERF_EVENTS_INTEL_UNCORE is not set 40 | # CONFIG_PERF_EVENTS_INTEL_RAPL is not set 41 | # CONFIG_PERF_EVENTS_INTEL_CSTATE is not set 42 | # CONFIG_PERF_EVENTS_AMD_UNCORE is not set 43 | # CONFIG_X86_VSYSCALL_EMULATION is not set 44 | # CONFIG_X86_IOPL_IOPERM is not set 45 | # CONFIG_MICROCODE is not set 46 | # CONFIG_X86_5LEVEL is not set 47 | # CONFIG_MTRR is not set 48 | # CONFIG_X86_UMIP is not set 49 | # CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS is not set 50 | # CONFIG_RELOCATABLE is not set 51 | CONFIG_LEGACY_VSYSCALL_NONE=y 52 | # CONFIG_MODIFY_LDT_SYSCALL is not set 53 | # CONFIG_SPECULATION_MITIGATIONS is not set 54 | # CONFIG_SUSPEND is not set 55 | # CONFIG_ACPI is not set 56 | # CONFIG_ISA_DMA_API is not set 57 | # CONFIG_VIRTUALIZATION is not set 58 | CONFIG_JUMP_LABEL=y 59 | # CONFIG_SECCOMP is not set 60 | # CONFIG_STACKPROTECTOR is not set 61 | # CONFIG_VMAP_STACK is not set 62 | # CONFIG_RANDOMIZE_KSTACK_OFFSET is not set 63 | # CONFIG_MQ_IOSCHED_DEADLINE is not set 64 | # CONFIG_MQ_IOSCHED_KYBER is not set 65 | # CONFIG_COREDUMP is not set 66 | # CONFIG_SWAP is not set 67 | CONFIG_SLOB=y 68 | # CONFIG_COMPAT_BRK is not set 69 | # CONFIG_COMPACTION is not set 70 | # CONFIG_ZONE_DMA is not set 71 | # CONFIG_VM_EVENT_COUNTERS is not set 72 | CONFIG_PCI=y 73 | # CONFIG_PCIEASPM is not set 74 | # CONFIG_PCI_QUIRKS is not set 75 | # CONFIG_VGA_ARB is not set 76 | CONFIG_DEVTMPFS=y 77 | CONFIG_DEVTMPFS_MOUNT=y 78 | # CONFIG_STANDALONE is not set 79 | # CONFIG_PREVENT_FIRMWARE_BUILD is not set 80 | # CONFIG_FW_LOADER is not set 81 | # CONFIG_ALLOW_DEV_COREDUMP is not set 82 | # CONFIG_FIRMWARE_MEMMAP is not set 83 | CONFIG_VIRTIO_BLK=y 84 | CONFIG_SERIAL_8250=y 85 | CONFIG_SERIAL_8250_CONSOLE=y 86 | # CONFIG_SERIAL_8250_PCI is not set 87 | # CONFIG_SERIAL_8250_LPSS is not set 88 | # CONFIG_SERIAL_8250_MID is not set 89 | # CONFIG_HW_RANDOM is not set 90 | # CONFIG_DEVMEM is not set 91 | # CONFIG_RANDOM_TRUST_BOOTLOADER is not set 92 | # CONFIG_HWMON is not set 93 | # CONFIG_VGA_CONSOLE is not set 94 | # CONFIG_USB_SUPPORT is not set 95 | CONFIG_VIRTIO_PCI=y 96 | # CONFIG_VIRTIO_PCI_LEGACY is not set 97 | # CONFIG_VHOST_MENU is not set 98 | # CONFIG_SURFACE_PLATFORMS is not set 99 | # CONFIG_X86_PLATFORM_DEVICES is not set 100 | CONFIG_COMMON_CLK=y 101 | # CONFIG_IOMMU_SUPPORT is not set 102 | CONFIG_EXT4_FS=y 103 | # CONFIG_EXT4_USE_FOR_EXT2 is not set 104 | # CONFIG_FILE_LOCKING is not set 105 | # CONFIG_DNOTIFY is not set 106 | # CONFIG_INOTIFY_USER is not set 107 | # CONFIG_MISC_FILESYSTEMS is not set 108 | # CONFIG_CRYPTO_HW is not set 109 | # CONFIG_DEBUG_MISC is not set 110 | CONFIG_FRAME_WARN=1024 111 | # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set 112 | # CONFIG_FTRACE is not set 113 | # CONFIG_X86_VERBOSE_BOOTUP is not set 114 | # CONFIG_EARLY_PRINTK is not set 115 | # CONFIG_X86_DEBUG_FPU is not set 116 | CONFIG_UNWINDER_GUESS=y 117 | # CONFIG_RUNTIME_TESTING_MENU is not set 118 | CONFIG_NET=y 119 | CONFIG_INET=y 120 | CONFIG_IP_MULTICAST=y 121 | CONFIG_IP_PNP=y 122 | CONFIG_IP_PNP_DHCP=y 123 | CONFIG_NETDEVICES=y 124 | CONFIG_VIRTIO=y 125 | CONFIG_VIRTIO_NET=y 126 | CONFIG_VIRTIO_RING=y 127 | CONFIG_UNIX=y 128 | CONFIG_UNIX_SCM=y 129 | CONFIG_AF_UNIX_OOB=y -------------------------------------------------------------------------------- /src/vm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "bus.h" 13 | #include "err.h" 14 | #include "vm.h" 15 | 16 | int vm_init(vm_t *v) 17 | { 18 | if ((v->kvm_fd = open("/dev/kvm", O_RDWR)) < 0) 19 | return throw_err("Failed to open /dev/kvm"); 20 | 21 | if ((v->vm_fd = ioctl(v->kvm_fd, KVM_CREATE_VM, 0)) < 0) 22 | return throw_err("Failed to create vm"); 23 | 24 | if (vm_arch_init(v) < 0) 25 | return -1; 26 | 27 | v->mem = mmap(NULL, RAM_SIZE, PROT_READ | PROT_WRITE, 28 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 29 | if (!v->mem) 30 | return throw_err("Failed to mmap vm memory"); 31 | 32 | struct kvm_userspace_memory_region region = { 33 | .slot = 0, 34 | .flags = 0, 35 | .guest_phys_addr = RAM_BASE, 36 | .memory_size = RAM_SIZE, 37 | .userspace_addr = (__u64) v->mem, 38 | }; 39 | if (ioctl(v->vm_fd, KVM_SET_USER_MEMORY_REGION, ®ion) < 0) 40 | return throw_err("Failed to set user memory region"); 41 | 42 | if ((v->vcpu_fd = ioctl(v->vm_fd, KVM_CREATE_VCPU, 0)) < 0) 43 | return throw_err("Failed to create vcpu"); 44 | 45 | if (vm_arch_cpu_init(v) < 0) 46 | return -1; 47 | 48 | bus_init(&v->io_bus); 49 | bus_init(&v->mmio_bus); 50 | 51 | if (vm_arch_init_platform_device(v) < 0) 52 | return -1; 53 | 54 | return 0; 55 | } 56 | 57 | int vm_load_image(vm_t *v, const char *image_path) 58 | { 59 | int fd = open(image_path, O_RDONLY); 60 | if (fd < 0) 61 | return 1; 62 | 63 | struct stat st; 64 | fstat(fd, &st); 65 | size_t datasz = st.st_size; 66 | void *data = mmap(0, datasz, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); 67 | close(fd); 68 | 69 | int ret = vm_arch_load_image(v, data, datasz); 70 | munmap(data, datasz); 71 | return ret; 72 | } 73 | 74 | int vm_load_initrd(vm_t *v, const char *initrd_path) 75 | { 76 | int fd = open(initrd_path, O_RDONLY); 77 | if (fd < 0) 78 | return 1; 79 | 80 | struct stat st; 81 | fstat(fd, &st); 82 | size_t datasz = st.st_size; 83 | void *data = mmap(0, datasz, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); 84 | close(fd); 85 | 86 | int ret = vm_arch_load_initrd(v, data, datasz); 87 | munmap(data, datasz); 88 | return ret; 89 | } 90 | 91 | int vm_load_diskimg(vm_t *v, const char *diskimg_file) 92 | { 93 | if (diskimg_init(&v->diskimg, diskimg_file) < 0) 94 | return -1; 95 | virtio_blk_init_pci(&v->virtio_blk_dev, &v->diskimg, &v->pci, &v->io_bus, 96 | &v->mmio_bus); 97 | return 0; 98 | } 99 | 100 | int vm_enable_net(vm_t *v) 101 | { 102 | if (!virtio_net_init(&v->virtio_net_dev)) 103 | return -1; 104 | virtio_net_init_pci(&v->virtio_net_dev, &v->pci, &v->io_bus, &v->mmio_bus); 105 | return 0; 106 | } 107 | 108 | void vm_handle_io(vm_t *v, struct kvm_run *run) 109 | { 110 | uint64_t addr = run->io.port; 111 | void *data = (void *) ((uintptr_t) run + run->io.data_offset); 112 | bool is_write = run->io.direction == KVM_EXIT_IO_OUT; 113 | 114 | for (int i = 0; i < run->io.count; i++) { 115 | bus_handle_io(&v->io_bus, data, is_write, addr, run->io.size); 116 | addr += run->io.size; 117 | } 118 | } 119 | 120 | void vm_handle_mmio(vm_t *v, struct kvm_run *run) 121 | { 122 | bus_handle_io(&v->mmio_bus, run->mmio.data, run->mmio.is_write, 123 | run->mmio.phys_addr, run->mmio.len); 124 | } 125 | 126 | int vm_run(vm_t *v) 127 | { 128 | int run_size = ioctl(v->kvm_fd, KVM_GET_VCPU_MMAP_SIZE, 0); 129 | struct kvm_run *run = 130 | mmap(0, run_size, PROT_READ | PROT_WRITE, MAP_SHARED, v->vcpu_fd, 0); 131 | 132 | while (1) { 133 | int err = ioctl(v->vcpu_fd, KVM_RUN, 0); 134 | if (err < 0 && (errno != EINTR && errno != EAGAIN)) { 135 | munmap(run, run_size); 136 | return throw_err("Failed to execute kvm_run"); 137 | } 138 | switch (run->exit_reason) { 139 | case KVM_EXIT_IO: 140 | vm_handle_io(v, run); 141 | break; 142 | case KVM_EXIT_MMIO: 143 | vm_handle_mmio(v, run); 144 | break; 145 | case KVM_EXIT_INTR: 146 | break; 147 | case KVM_EXIT_SHUTDOWN: 148 | printf("shutdown\n"); 149 | munmap(run, run_size); 150 | return 0; 151 | default: 152 | printf("reason: %d\n", run->exit_reason); 153 | munmap(run, run_size); 154 | return -1; 155 | } 156 | } 157 | } 158 | 159 | void *vm_guest_to_host(vm_t *v, uint64_t guest) 160 | { 161 | if (guest < RAM_BASE) 162 | return NULL; 163 | return (void *) ((uintptr_t) v->mem + guest - RAM_BASE); 164 | } 165 | 166 | void vm_irqfd_register(vm_t *v, int fd, int gsi, int flags) 167 | { 168 | struct kvm_irqfd irqfd = { 169 | .fd = fd, 170 | .gsi = gsi, 171 | .flags = flags, 172 | }; 173 | 174 | if (ioctl(v->vm_fd, KVM_IRQFD, &irqfd) < 0) 175 | throw_err("Failed to set the status of IRQFD"); 176 | } 177 | 178 | void vm_ioeventfd_register(vm_t *v, 179 | int fd, 180 | unsigned long long addr, 181 | int len, 182 | int flags) 183 | { 184 | struct kvm_ioeventfd ioeventfd = { 185 | .fd = fd, 186 | .addr = addr, 187 | .len = len, 188 | .flags = flags, 189 | }; 190 | 191 | if (ioctl(v->vm_fd, KVM_IOEVENTFD, &ioeventfd) < 0) 192 | throw_err("Failed to set the status of IOEVENTFD"); 193 | } 194 | 195 | void vm_exit(vm_t *v) 196 | { 197 | serial_exit(&v->serial); 198 | virtio_blk_exit(&v->virtio_blk_dev); 199 | close(v->kvm_fd); 200 | close(v->vm_fd); 201 | close(v->vcpu_fd); 202 | munmap(v->mem, RAM_SIZE); 203 | } 204 | -------------------------------------------------------------------------------- /src/pci.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "pci.h" 5 | #include "utils.h" 6 | 7 | static void pci_address_io(void *owner, 8 | void *data, 9 | uint8_t is_write, 10 | uint64_t offset, 11 | uint8_t size) 12 | { 13 | struct pci *pci = (struct pci *) owner; 14 | void *p = (void *) ((uintptr_t) &pci->pci_addr + offset); 15 | /* The data in port 0xCF8 is as an address when Guest Linux accesses the 16 | * configuration space. 17 | */ 18 | if (is_write) 19 | memcpy(p, data, size); 20 | else 21 | memcpy(data, p, size); 22 | pci->pci_addr.reg_offset = 0; 23 | } 24 | 25 | static inline void pci_activate_bar(struct pci_dev *dev, 26 | uint8_t bar, 27 | struct bus *bus) 28 | { 29 | uint32_t mask = ~(dev->bar_size[bar] - 1); 30 | if (!dev->bar_active[bar] && dev->space_dev[bar].base & mask) 31 | bus_register_dev(bus, &dev->space_dev[bar]); 32 | dev->bar_active[bar] = true; 33 | } 34 | 35 | static inline void pci_deactivate_bar(struct pci_dev *dev, 36 | uint8_t bar, 37 | struct bus *bus) 38 | { 39 | uint32_t mask = ~(dev->bar_size[bar] - 1); 40 | if (dev->bar_active[bar] && dev->space_dev[bar].base & mask) 41 | bus_deregister_dev(bus, &dev->space_dev[bar]); 42 | dev->bar_active[bar] = false; 43 | } 44 | 45 | #ifndef PCI_STD_NUM_BARS 46 | #define PCI_STD_NUM_BARS 6 /* Number of standard BARs */ 47 | #endif 48 | 49 | static void pci_command_bar(struct pci_dev *dev) 50 | { 51 | bool enable_io = PCI_HDR_READ(dev->hdr, PCI_COMMAND, 16) & PCI_COMMAND_IO; 52 | bool enable_mem = 53 | PCI_HDR_READ(dev->hdr, PCI_COMMAND, 16) & PCI_COMMAND_MEMORY; 54 | for (int i = 0; i < PCI_STD_NUM_BARS; i++) { 55 | struct bus *bus = dev->bar_is_io_space[i] ? dev->io_bus : dev->mmio_bus; 56 | bool enable = dev->bar_is_io_space[i] ? enable_io : enable_mem; 57 | 58 | if (enable) 59 | pci_activate_bar(dev, i, bus); 60 | else 61 | pci_deactivate_bar(dev, i, bus); 62 | } 63 | } 64 | 65 | static void pci_config_command(struct pci_dev *dev) 66 | { 67 | pci_command_bar(dev); 68 | } 69 | 70 | static void pci_config_bar(struct pci_dev *dev, uint8_t bar) 71 | { 72 | uint32_t mask = ~(dev->bar_size[bar] - 1); 73 | uint32_t old_bar = PCI_HDR_READ(dev->hdr, PCI_BAR_OFFSET(bar), 32); 74 | uint32_t new_bar = (old_bar & mask) | dev->bar_is_io_space[bar]; 75 | PCI_HDR_WRITE(dev->hdr, PCI_BAR_OFFSET(bar), new_bar, 32); 76 | dev->space_dev[bar].base = new_bar; 77 | } 78 | 79 | static void pci_config_write(struct pci_dev *dev, 80 | void *data, 81 | uint64_t offset, 82 | uint8_t size) 83 | { 84 | void *p = (void *) ((uintptr_t) dev->hdr + offset); 85 | 86 | memcpy(p, data, size); 87 | if (offset == PCI_COMMAND) { 88 | pci_config_command(dev); 89 | } else if (offset >= PCI_BASE_ADDRESS_0 && offset <= PCI_BASE_ADDRESS_5) { 90 | uint8_t bar = (offset - PCI_BASE_ADDRESS_0) >> 2; 91 | pci_config_bar(dev, bar); 92 | } else if (offset == PCI_ROM_ADDRESS) { 93 | PCI_HDR_WRITE(dev->hdr, PCI_ROM_ADDRESS, 0, 32); 94 | } 95 | /* TODO: write to capability */ 96 | } 97 | 98 | static void pci_config_read(struct pci_dev *dev, 99 | void *data, 100 | uint64_t offset, 101 | uint8_t size) 102 | { 103 | void *p = (void *) ((uintptr_t) dev->hdr + offset); 104 | memcpy(data, p, size); 105 | } 106 | 107 | static void pci_config_do_io(void *owner, 108 | void *data, 109 | uint8_t is_write, 110 | uint64_t offset, 111 | uint8_t size) 112 | { 113 | struct pci_dev *dev = (struct pci_dev *) owner; 114 | if (is_write) 115 | pci_config_write(dev, data, offset, size); 116 | else 117 | pci_config_read(dev, data, offset, size); 118 | } 119 | 120 | #define PCI_ADDR_ENABLE_BIT (1UL << 31) 121 | 122 | static void pci_data_io(void *owner, 123 | void *data, 124 | uint8_t is_write, 125 | uint64_t offset, 126 | uint8_t size) 127 | { 128 | struct pci *pci = (struct pci *) owner; 129 | if (pci->pci_addr.enable_bit) { 130 | uint64_t addr = (pci->pci_addr.value | offset) & ~(PCI_ADDR_ENABLE_BIT); 131 | bus_handle_io(&pci->pci_bus, data, is_write, addr, size); 132 | } 133 | } 134 | 135 | static void pci_mmio_io(void *owner, 136 | void *data, 137 | uint8_t is_write, 138 | uint64_t offset, 139 | uint8_t size) 140 | { 141 | struct pci *pci = (struct pci *) owner; 142 | bus_handle_io(&pci->pci_bus, data, is_write, offset, size); 143 | } 144 | 145 | void pci_set_bar(struct pci_dev *dev, 146 | uint8_t bar, 147 | uint32_t bar_size, 148 | bool is_io_space, 149 | dev_io_fn do_io) 150 | { 151 | /* TODO: mem type, prefetch */ 152 | /* FIXME: bar_size must be power of 2 */ 153 | PCI_HDR_WRITE(dev->hdr, PCI_BAR_OFFSET(bar), is_io_space, 32); 154 | dev->bar_size[bar] = bar_size; 155 | dev->bar_is_io_space[bar] = is_io_space; 156 | dev_init(&dev->space_dev[bar], 0, bar_size, dev, do_io); 157 | } 158 | 159 | void pci_set_status(struct pci_dev *dev, uint16_t status) 160 | { 161 | PCI_HDR_WRITE(dev->hdr, PCI_STATUS, status, 16); 162 | } 163 | 164 | void pci_dev_init(struct pci_dev *dev, 165 | struct pci *pci, 166 | struct bus *io_bus, 167 | struct bus *mmio_bus) 168 | { 169 | memset(dev, 0x00, sizeof(struct pci_dev)); 170 | dev->hdr = dev->cfg_space; 171 | dev->pci_bus = &pci->pci_bus; 172 | dev->io_bus = io_bus; 173 | dev->mmio_bus = mmio_bus; 174 | } 175 | 176 | void pci_dev_register(struct pci_dev *dev) 177 | { 178 | /* FIXEME: It just simplifies the registration on pci bus 0 */ 179 | /* FIXEME: dev_num might exceed 32 */ 180 | union pci_config_address addr = {.dev_num = dev->pci_bus->dev_num}; 181 | dev_init(&dev->config_dev, addr.value, PCI_CFG_SPACE_SIZE, dev, 182 | pci_config_do_io); 183 | bus_register_dev(dev->pci_bus, &dev->config_dev); 184 | } 185 | 186 | #define PCI_CONFIG_ADDR 0xCF8 187 | #define PCI_CONFIG_DATA 0xCFC 188 | #define PCI_MMIO_SIZE (1UL << 16) 189 | 190 | void pci_init(struct pci *pci) 191 | { 192 | dev_init(&pci->pci_addr_dev, PCI_CONFIG_ADDR, sizeof(uint32_t), pci, 193 | pci_address_io); 194 | dev_init(&pci->pci_bus_dev, PCI_CONFIG_DATA, sizeof(uint32_t), pci, 195 | pci_data_io); 196 | dev_init(&pci->pci_mmio_dev, 0, PCI_MMIO_SIZE, pci, pci_mmio_io); 197 | bus_init(&pci->pci_bus); 198 | } 199 | -------------------------------------------------------------------------------- /src/arch/x86/vm.c: -------------------------------------------------------------------------------- 1 | #if !defined(__x86_64__) || !defined(__linux__) 2 | #error "This implementation is dedicated to Linux/x86-64." 3 | #endif 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "err.h" 14 | #include "vm.h" 15 | 16 | static int vm_init_regs(vm_t *v) 17 | { 18 | struct kvm_sregs sregs; 19 | if (ioctl(v->vcpu_fd, KVM_GET_SREGS, &sregs) < 0) 20 | return throw_err("Failed to get registers"); 21 | 22 | #define X(R) sregs.R.base = 0, sregs.R.limit = ~0, sregs.R.g = 1 23 | X(cs), X(ds), X(fs), X(gs), X(es), X(ss); 24 | #undef X 25 | 26 | sregs.cs.db = 1; 27 | sregs.ss.db = 1; 28 | sregs.cr0 |= 1; /* enable protected mode */ 29 | 30 | if (ioctl(v->vcpu_fd, KVM_SET_SREGS, &sregs) < 0) 31 | return throw_err("Failed to set special registers"); 32 | 33 | struct kvm_regs regs; 34 | if (ioctl(v->vcpu_fd, KVM_GET_REGS, ®s) < 0) 35 | return throw_err("Failed to get registers"); 36 | 37 | regs.rflags = 2; 38 | regs.rip = 0x100000, regs.rsi = 0x10000; 39 | if (ioctl(v->vcpu_fd, KVM_SET_REGS, ®s) < 0) 40 | return throw_err("Failed to set registers"); 41 | 42 | return 0; 43 | } 44 | 45 | #define N_ENTRIES 100 46 | static void vm_init_cpu_id(vm_t *v) 47 | { 48 | struct { 49 | uint32_t nent; 50 | uint32_t padding; 51 | struct kvm_cpuid_entry2 entries[N_ENTRIES]; 52 | } kvm_cpuid = {.nent = N_ENTRIES}; 53 | ioctl(v->kvm_fd, KVM_GET_SUPPORTED_CPUID, &kvm_cpuid); 54 | 55 | for (unsigned int i = 0; i < N_ENTRIES; i++) { 56 | struct kvm_cpuid_entry2 *entry = &kvm_cpuid.entries[i]; 57 | if (entry->function == KVM_CPUID_SIGNATURE) { 58 | entry->eax = KVM_CPUID_FEATURES; 59 | entry->ebx = 0x4b4d564b; /* KVMK */ 60 | entry->ecx = 0x564b4d56; /* VMKV */ 61 | entry->edx = 0x4d; /* M */ 62 | } 63 | } 64 | ioctl(v->vcpu_fd, KVM_SET_CPUID2, &kvm_cpuid); 65 | } 66 | 67 | #define MSR_IA32_MISC_ENABLE 0x000001a0 68 | #define MSR_IA32_MISC_ENABLE_FAST_STRING_BIT 0 69 | #define MSR_IA32_MISC_ENABLE_FAST_STRING \ 70 | (1ULL << MSR_IA32_MISC_ENABLE_FAST_STRING_BIT) 71 | 72 | #define KVM_MSR_ENTRY(_index, _data) \ 73 | (struct kvm_msr_entry) { .index = _index, .data = _data } 74 | static void vm_init_msrs(vm_t *v) 75 | { 76 | int ndx = 0; 77 | struct kvm_msrs *msrs = 78 | calloc(1, sizeof(struct kvm_msrs) + (sizeof(struct kvm_msr_entry) * 1)); 79 | 80 | msrs->entries[ndx++] = 81 | KVM_MSR_ENTRY(MSR_IA32_MISC_ENABLE, MSR_IA32_MISC_ENABLE_FAST_STRING); 82 | msrs->nmsrs = ndx; 83 | 84 | ioctl(v->vcpu_fd, KVM_SET_MSRS, msrs); 85 | 86 | free(msrs); 87 | } 88 | 89 | int vm_arch_init(vm_t *v) 90 | { 91 | if (ioctl(v->vm_fd, KVM_SET_TSS_ADDR, 0xffffd000) < 0) 92 | return throw_err("Failed to set TSS addr"); 93 | 94 | __u64 map_addr = 0xffffc000; 95 | if (ioctl(v->vm_fd, KVM_SET_IDENTITY_MAP_ADDR, &map_addr) < 0) 96 | return throw_err("Failed to set identity map address"); 97 | 98 | if (ioctl(v->vm_fd, KVM_CREATE_IRQCHIP, 0) < 0) 99 | return throw_err("Failed to create IRQ chip"); 100 | 101 | struct kvm_pit_config pit = {.flags = 0}; 102 | if (ioctl(v->vm_fd, KVM_CREATE_PIT2, &pit) < 0) 103 | return throw_err("Failed to create i8254 interval timer"); 104 | 105 | return 0; 106 | } 107 | 108 | int vm_arch_cpu_init(vm_t *v) 109 | { 110 | vm_init_regs(v); 111 | vm_init_cpu_id(v); 112 | vm_init_msrs(v); 113 | return 0; 114 | } 115 | 116 | int vm_arch_init_platform_device(vm_t *v) 117 | { 118 | pci_init(&v->pci); 119 | bus_register_dev(&v->io_bus, &v->pci.pci_addr_dev); 120 | bus_register_dev(&v->io_bus, &v->pci.pci_bus_dev); 121 | if (serial_init(&v->serial, &v->io_bus)) 122 | return throw_err("Failed to init UART device"); 123 | virtio_blk_init(&v->virtio_blk_dev); 124 | return 0; 125 | } 126 | 127 | int vm_arch_load_image(vm_t *v, void *data, size_t datasz) 128 | { 129 | struct boot_params *boot = 130 | (struct boot_params *) ((uint8_t *) v->mem + 0x10000); 131 | void *cmdline = ((uint8_t *) v->mem) + 0x20000; 132 | void *kernel = ((uint8_t *) v->mem) + 0x100000; 133 | 134 | /* According to https://www.kernel.org/doc/html/next/x86/boot.html, 135 | * the first step in loading a Linux kernel should be to setup the boot 136 | * parameters (struct boot_params) and initialize it to all zero. Then, 137 | * the setup header at offset 0x01f1 of kernel image on should be loaded 138 | * into struct boot_params. */ 139 | memset(boot, 0, sizeof(struct boot_params)); 140 | memmove((void *) ((uintptr_t) boot + offsetof(struct boot_params, hdr)), 141 | (void *) ((uintptr_t) data + offsetof(struct boot_params, hdr)), 142 | sizeof(struct setup_header)); 143 | 144 | size_t setup_sectors = boot->hdr.setup_sects; 145 | size_t setupsz = (setup_sectors + 1) * 512; 146 | boot->hdr.vid_mode = 0xFFFF; // VGA 147 | boot->hdr.type_of_loader = 0xFF; 148 | boot->hdr.loadflags |= CAN_USE_HEAP | 0x01 | KEEP_SEGMENTS; 149 | boot->hdr.heap_end_ptr = 0xFE00; 150 | boot->hdr.ext_loader_ver = 0x0; 151 | boot->hdr.cmd_line_ptr = 0x20000; 152 | memset(cmdline, 0, boot->hdr.cmdline_size); 153 | memcpy(cmdline, KERNEL_OPTS, sizeof(KERNEL_OPTS)); 154 | memmove(kernel, (char *) data + setupsz, datasz - setupsz); 155 | 156 | /* setup E820 memory map to report usable address ranges for initrd */ 157 | unsigned int idx = 0; 158 | boot->e820_table[idx++] = (struct boot_e820_entry){ 159 | .addr = 0x0, 160 | .size = ISA_START_ADDRESS - 1, 161 | .type = E820_RAM, 162 | }; 163 | boot->e820_table[idx++] = (struct boot_e820_entry){ 164 | .addr = ISA_END_ADDRESS, 165 | .size = RAM_SIZE - ISA_END_ADDRESS, 166 | .type = E820_RAM, 167 | }; 168 | boot->e820_entries = idx; 169 | 170 | return 0; 171 | } 172 | 173 | int vm_arch_load_initrd(vm_t *v, void *data, size_t datasz) 174 | { 175 | struct boot_params *boot = 176 | (struct boot_params *) ((uint8_t *) v->mem + 0x10000); 177 | unsigned long addr = boot->hdr.initrd_addr_max & ~0xfffff; 178 | 179 | for (;;) { 180 | if (addr < 0x100000) 181 | return throw_err("Not enough memory for initrd"); 182 | if (addr < (RAM_SIZE - datasz)) 183 | break; 184 | addr -= 0x100000; 185 | } 186 | 187 | void *initrd = ((uint8_t *) v->mem) + addr; 188 | 189 | memset(initrd, 0, datasz); 190 | memmove(initrd, data, datasz); 191 | 192 | boot->hdr.ramdisk_image = addr; 193 | boot->hdr.ramdisk_size = datasz; 194 | return 0; 195 | } 196 | 197 | int vm_late_init(vm_t *v) 198 | { 199 | return 0; 200 | } 201 | 202 | int vm_irq_line(vm_t *v, int irq, int level) 203 | { 204 | struct kvm_irq_level irq_level = { 205 | {.irq = irq}, 206 | .level = level, 207 | }; 208 | 209 | if (ioctl(v->vm_fd, KVM_IRQ_LINE, &irq_level) < 0) 210 | return throw_err("Failed to set the status of an IRQ line"); 211 | 212 | return 0; 213 | } 214 | -------------------------------------------------------------------------------- /src/virtio-blk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "err.h" 11 | #include "utils.h" 12 | #include "virtio-blk.h" 13 | #include "vm.h" 14 | 15 | static void virtio_blk_notify_used(struct virtq *vq) 16 | { 17 | struct virtio_blk_dev *dev = (struct virtio_blk_dev *) vq->dev; 18 | uint64_t n = 1; 19 | 20 | if (write(dev->irqfd, &n, sizeof(n)) < 0) 21 | throw_err("Failed to write the irqfd"); 22 | } 23 | 24 | static int virtio_blk_virtq_available(struct virtio_blk_dev *dev, int timeout) 25 | { 26 | struct pollfd pollfd = (struct pollfd){ 27 | .fd = dev->ioeventfd, 28 | .events = POLLIN, 29 | }; 30 | return (poll(&pollfd, 1, timeout) > 0) && (pollfd.revents & POLLIN); 31 | } 32 | 33 | static volatile bool thread_stop = false; 34 | 35 | static void *virtio_blk_thread(struct virtio_blk_dev *dev) 36 | { 37 | while (!__atomic_load_n(&thread_stop, __ATOMIC_RELAXED)) { 38 | if (virtio_blk_virtq_available(dev, -1)) 39 | pthread_kill((pthread_t) dev->vq_avail_thread, SIGUSR1); 40 | } 41 | 42 | return NULL; 43 | } 44 | 45 | static void *virtio_blk_vq_avail_handler(void *arg) 46 | { 47 | struct virtq *vq = (struct virtq *) arg; 48 | struct virtio_blk_dev *dev = (struct virtio_blk_dev *) vq->dev; 49 | uint64_t n; 50 | 51 | while (read(dev->ioeventfd, &n, sizeof(n))) { 52 | virtq_handle_avail(vq); 53 | } 54 | return NULL; 55 | } 56 | 57 | static void virtio_blk_enable_vq(struct virtq *vq) 58 | { 59 | struct virtio_blk_dev *dev = (struct virtio_blk_dev *) vq->dev; 60 | vm_t *v = container_of(dev, vm_t, virtio_blk_dev); 61 | 62 | if (vq->info.enable) 63 | return; 64 | vq->info.enable = true; 65 | vq->desc_ring = 66 | (struct vring_packed_desc *) vm_guest_to_host(v, vq->info.desc_addr); 67 | vq->device_event = (struct vring_packed_desc_event *) vm_guest_to_host( 68 | v, vq->info.device_addr); 69 | vq->guest_event = (struct vring_packed_desc_event *) vm_guest_to_host( 70 | v, vq->info.driver_addr); 71 | 72 | uint64_t addr = virtio_pci_get_notify_addr(&dev->virtio_pci_dev, vq); 73 | vm_ioeventfd_register(v, dev->ioeventfd, addr, 74 | dev->virtio_pci_dev.notify_cap->cap.length, 0); 75 | pthread_create(&dev->vq_avail_thread, NULL, virtio_blk_vq_avail_handler, 76 | (void *) vq); 77 | } 78 | 79 | static ssize_t virtio_blk_write(struct virtio_blk_dev *dev, 80 | void *data, 81 | off_t offset, 82 | size_t size) 83 | { 84 | return diskimg_write(dev->diskimg, data, offset, size); 85 | } 86 | 87 | static ssize_t virtio_blk_read(struct virtio_blk_dev *dev, 88 | void *data, 89 | off_t offset, 90 | size_t size) 91 | { 92 | return diskimg_read(dev->diskimg, data, offset, size); 93 | } 94 | 95 | static void virtio_blk_complete_request(struct virtq *vq) 96 | { 97 | struct virtio_blk_dev *dev = (struct virtio_blk_dev *) vq->dev; 98 | vm_t *v = container_of(dev, vm_t, virtio_blk_dev); 99 | uint8_t status; 100 | struct vring_packed_desc *desc; 101 | struct virtio_blk_req req; 102 | 103 | while ((desc = virtq_get_avail(vq))) { 104 | struct vring_packed_desc *used_desc = desc; 105 | int r = 0; 106 | 107 | memcpy(&req, vm_guest_to_host(v, desc->addr), desc->len); 108 | if (req.type == VIRTIO_BLK_T_IN || req.type == VIRTIO_BLK_T_OUT) { 109 | if (!virtq_check_next(desc)) 110 | return; 111 | desc = virtq_get_avail(vq); 112 | req.data_size = desc->len; 113 | req.data = vm_guest_to_host(v, desc->addr); 114 | 115 | ssize_t r; 116 | if (req.type == VIRTIO_BLK_T_IN) 117 | r = virtio_blk_read(dev, req.data, req.sector << 9, 118 | req.data_size); 119 | else 120 | r = virtio_blk_write(dev, req.data, req.sector << 9, 121 | req.data_size); 122 | 123 | status = r < 0 ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK; 124 | } else { 125 | status = VIRTIO_BLK_S_UNSUPP; 126 | } 127 | if (!virtq_check_next(desc)) 128 | return; 129 | desc = virtq_get_avail(vq); 130 | req.status = vm_guest_to_host(v, desc->addr); 131 | *req.status = status; 132 | used_desc->flags ^= (1ULL << VRING_PACKED_DESC_F_USED); 133 | used_desc->len = r; 134 | dev->virtio_pci_dev.config.isr_cap.isr_status |= VIRTIO_PCI_ISR_QUEUE; 135 | } 136 | } 137 | 138 | static struct virtq_ops ops = { 139 | .enable_vq = virtio_blk_enable_vq, 140 | .complete_request = virtio_blk_complete_request, 141 | .notify_used = virtio_blk_notify_used, 142 | }; 143 | 144 | static void virtio_blk_setup(struct virtio_blk_dev *dev, 145 | struct diskimg *diskimg) 146 | { 147 | vm_t *v = container_of(dev, vm_t, virtio_blk_dev); 148 | 149 | dev->enable = true; 150 | /* FIXME: irq_num should be different to other devs */ 151 | dev->irq_num = VIRTIO_BLK_IRQ; 152 | dev->diskimg = diskimg; 153 | dev->config.capacity = diskimg->size >> 9; 154 | dev->ioeventfd = eventfd(0, EFD_CLOEXEC); 155 | dev->irqfd = eventfd(0, EFD_CLOEXEC); 156 | vm_irqfd_register(v, dev->irqfd, dev->irq_num, 0); 157 | for (int i = 0; i < VIRTIO_BLK_VIRTQ_NUM; i++) 158 | virtq_init(&dev->vq[i], dev, &ops); 159 | } 160 | 161 | void virtio_blk_init_pci(struct virtio_blk_dev *virtio_blk_dev, 162 | struct diskimg *diskimg, 163 | struct pci *pci, 164 | struct bus *io_bus, 165 | struct bus *mmio_bus) 166 | { 167 | struct virtio_pci_dev *dev = &virtio_blk_dev->virtio_pci_dev; 168 | /* Initialize the device based on PCI */ 169 | virtio_blk_setup(virtio_blk_dev, diskimg); 170 | virtio_pci_init(dev, pci, io_bus, mmio_bus); 171 | virtio_pci_set_dev_cfg(dev, &virtio_blk_dev->config, 172 | sizeof(virtio_blk_dev->config)); 173 | virtio_pci_set_pci_hdr(dev, VIRTIO_PCI_DEVICE_ID_BLK, VIRTIO_BLK_PCI_CLASS, 174 | virtio_blk_dev->irq_num); 175 | virtio_pci_set_virtq(dev, virtio_blk_dev->vq, VIRTIO_BLK_VIRTQ_NUM); 176 | virtio_pci_add_feature(dev, 0); 177 | virtio_pci_enable(dev); 178 | pthread_create(&virtio_blk_dev->worker_thread, NULL, 179 | (void *) virtio_blk_thread, (void *) virtio_blk_dev); 180 | } 181 | 182 | void virtio_blk_init(struct virtio_blk_dev *dev) 183 | { 184 | memset(dev, 0x00, sizeof(struct virtio_blk_dev)); 185 | } 186 | 187 | void virtio_blk_exit(struct virtio_blk_dev *dev) 188 | { 189 | if (!dev->enable) 190 | return; 191 | __atomic_store_n(&thread_stop, true, __ATOMIC_RELAXED); 192 | pthread_join(dev->vq_avail_thread, NULL); 193 | diskimg_exit(dev->diskimg); 194 | virtio_pci_exit(&dev->virtio_pci_dev); 195 | close(dev->irqfd); 196 | close(dev->ioeventfd); 197 | } 198 | -------------------------------------------------------------------------------- /src/serial.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "err.h" 12 | #include "serial.h" 13 | #include "utils.h" 14 | #include "vm.h" 15 | 16 | #define IO_READ8(data) *((uint8_t *) data) 17 | #define IO_WRITE8(data, value) ((uint8_t *) data)[0] = value 18 | 19 | struct serial_dev_priv { 20 | uint8_t dll; 21 | uint8_t dlm; 22 | uint8_t iir; 23 | uint8_t ier; 24 | uint8_t fcr; 25 | uint8_t lcr; 26 | uint8_t mcr; 27 | uint8_t lsr; 28 | uint8_t msr; 29 | uint8_t scr; 30 | 31 | struct fifo rx_buf; 32 | pthread_mutex_t lock; 33 | pthread_cond_t cond; 34 | }; 35 | 36 | static struct serial_dev_priv serial_dev_priv = { 37 | .iir = UART_IIR_NO_INT, 38 | .mcr = UART_MCR_OUT2, 39 | .lsr = UART_LSR_TEMT | UART_LSR_THRE, 40 | .msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS, 41 | .lock = PTHREAD_MUTEX_INITIALIZER, 42 | .cond = PTHREAD_COND_INITIALIZER, 43 | }; 44 | 45 | /* FIXME: This implementation is incomplete */ 46 | static void serial_update_irq(serial_dev_t *s) 47 | { 48 | struct serial_dev_priv *priv = (struct serial_dev_priv *) s->priv; 49 | uint8_t iir = UART_IIR_NO_INT; 50 | 51 | /* If enable receiver data interrupt and receiver data ready */ 52 | if ((priv->ier & UART_IER_RDI) && (priv->lsr & UART_LSR_DR)) 53 | iir = UART_IIR_RDI; 54 | /* If enable transmiter data interrupt and transmiter empty */ 55 | else if ((priv->ier & UART_IER_THRI) && (priv->lsr & UART_LSR_TEMT)) 56 | iir = UART_IIR_THRI; 57 | 58 | __atomic_store_n(&priv->iir, iir | 0xc0, __ATOMIC_RELEASE); 59 | 60 | /* FIXME: the return error of vm_irq_line should be handled */ 61 | vm_irq_line(container_of(s, vm_t, serial), s->irq_num, 62 | iir == UART_IIR_NO_INT ? 0 /* inactive */ : 1 /* active */); 63 | } 64 | 65 | static int serial_readable(serial_dev_t *s, int timeout) 66 | { 67 | struct pollfd pollfd = (struct pollfd){ 68 | .fd = s->infd, 69 | .events = POLLIN, 70 | }; 71 | return (poll(&pollfd, 1, timeout) > 0) && (pollfd.revents & POLLIN); 72 | } 73 | 74 | /* global state to stop the loop of thread */ 75 | static volatile bool thread_stop = false; 76 | 77 | static void *serial_thread(serial_dev_t *s) 78 | { 79 | struct serial_dev_priv *priv = (struct serial_dev_priv *) s->priv; 80 | while (!__atomic_load_n(&thread_stop, __ATOMIC_RELAXED)) { 81 | if (!serial_readable(s, -1)) 82 | continue; 83 | pthread_mutex_lock(&priv->lock); 84 | if (fifo_is_full(&priv->rx_buf)) { 85 | /* stdin is readable, but the rx_buf is full. 86 | * Wait for notification. 87 | */ 88 | pthread_cond_wait(&priv->cond, &priv->lock); 89 | } 90 | serial_console(s); 91 | pthread_mutex_unlock(&priv->lock); 92 | } 93 | 94 | return NULL; 95 | } 96 | 97 | #define TERMINAL_ESCAPE_CHAR 0x01 98 | #define TERMINAL_EXIT_CHAR 'x' 99 | 100 | void serial_console(serial_dev_t *s) 101 | { 102 | struct serial_dev_priv *priv = (struct serial_dev_priv *) s->priv; 103 | static bool escaped = false; 104 | 105 | while (!fifo_is_full(&priv->rx_buf) && serial_readable(s, 0)) { 106 | char c; 107 | if (read(s->infd, &c, 1) == -1) 108 | break; 109 | if (escaped && c == TERMINAL_EXIT_CHAR) { 110 | /* Terminate */ 111 | fprintf(stderr, "\n"); 112 | exit(0); 113 | } 114 | if (!escaped && c == TERMINAL_ESCAPE_CHAR) { 115 | escaped = true; 116 | continue; 117 | } 118 | escaped = false; 119 | if (!fifo_put(&priv->rx_buf, c)) 120 | break; 121 | __atomic_store_n(&priv->lsr, priv->lsr | UART_LSR_DR, __ATOMIC_RELEASE); 122 | } 123 | serial_update_irq(s); 124 | } 125 | 126 | static void serial_in(serial_dev_t *s, uint16_t offset, void *data) 127 | { 128 | struct serial_dev_priv *priv = (struct serial_dev_priv *) s->priv; 129 | uint8_t value; 130 | 131 | switch (offset) { 132 | case UART_RX: 133 | if (priv->lcr & UART_LCR_DLAB) { 134 | IO_WRITE8(data, priv->dll); 135 | } else { 136 | pthread_mutex_lock(&priv->lock); 137 | if (fifo_get(&priv->rx_buf, value)) 138 | IO_WRITE8(data, value); 139 | 140 | if (fifo_is_empty(&priv->rx_buf)) { 141 | priv->lsr &= ~UART_LSR_DR; 142 | serial_update_irq(s); 143 | } 144 | /* The worker thread waits on the condition variable when rx_buf is 145 | * full and stdin is still readable. Notify the worker thread when 146 | * the capacity of the buffer drops to its half size, so the worker 147 | * thread can read up to half of the buffer size before it is 148 | * blocked again. 149 | */ 150 | if (fifo_capacity(&priv->rx_buf) == FIFO_LEN / 2) 151 | pthread_cond_signal(&priv->cond); 152 | pthread_mutex_unlock(&priv->lock); 153 | } 154 | break; 155 | case UART_IER: 156 | if (priv->lcr & UART_LCR_DLAB) 157 | IO_WRITE8(data, priv->dlm); 158 | else 159 | IO_WRITE8(data, priv->ier); 160 | break; 161 | case UART_IIR: 162 | value = __atomic_load_n(&priv->iir, __ATOMIC_ACQUIRE); 163 | IO_WRITE8(data, value | 0xc0); /* 0xc0 stands for FIFO enabled */ 164 | break; 165 | case UART_LCR: 166 | IO_WRITE8(data, priv->lcr); 167 | break; 168 | case UART_MCR: 169 | IO_WRITE8(data, priv->mcr); 170 | break; 171 | case UART_LSR: 172 | value = __atomic_load_n(&priv->lsr, __ATOMIC_ACQUIRE); 173 | IO_WRITE8(data, priv->lsr); 174 | break; 175 | case UART_MSR: 176 | IO_WRITE8(data, priv->msr); 177 | break; 178 | case UART_SCR: 179 | IO_WRITE8(data, priv->scr); 180 | break; 181 | default: 182 | break; 183 | } 184 | } 185 | 186 | static void serial_out(serial_dev_t *s, uint16_t offset, void *data) 187 | { 188 | struct serial_dev_priv *priv = (struct serial_dev_priv *) s->priv; 189 | 190 | switch (offset) { 191 | case UART_TX: 192 | if (priv->lcr & UART_LCR_DLAB) { 193 | priv->dll = IO_READ8(data); 194 | } else { 195 | putchar(((char *) data)[0]); 196 | fflush(stdout); 197 | pthread_mutex_lock(&priv->lock); 198 | priv->lsr |= (UART_LSR_TEMT | UART_LSR_THRE); /* flush TX */ 199 | serial_update_irq(s); 200 | pthread_mutex_unlock(&priv->lock); 201 | } 202 | break; 203 | case UART_IER: 204 | if (!(priv->lcr & UART_LCR_DLAB)) { 205 | pthread_mutex_lock(&priv->lock); 206 | priv->ier = IO_READ8(data); 207 | serial_update_irq(s); 208 | pthread_mutex_unlock(&priv->lock); 209 | } else { 210 | priv->dlm = IO_READ8(data); 211 | } 212 | break; 213 | case UART_FCR: 214 | priv->fcr = IO_READ8(data); 215 | break; 216 | case UART_LCR: 217 | priv->lcr = IO_READ8(data); 218 | break; 219 | case UART_MCR: 220 | priv->mcr = IO_READ8(data); 221 | break; 222 | case UART_LSR: /* factory test */ 223 | case UART_MSR: /* not used */ 224 | break; 225 | case UART_SCR: 226 | priv->scr = IO_READ8(data); 227 | break; 228 | default: 229 | break; 230 | } 231 | } 232 | 233 | 234 | static void serial_handle_io(void *owner, 235 | void *data, 236 | uint8_t is_write, 237 | uint64_t offset, 238 | uint8_t size) 239 | { 240 | serial_dev_t *s = (serial_dev_t *) owner; 241 | void (*serial_op)(serial_dev_t *, uint16_t, void *) = 242 | is_write ? serial_out : serial_in; 243 | 244 | serial_op(s, offset, data); 245 | } 246 | 247 | int serial_init(serial_dev_t *s, struct bus *bus) 248 | { 249 | *s = (serial_dev_t){ 250 | .priv = (void *) &serial_dev_priv, 251 | .infd = STDIN_FILENO, 252 | .irq_num = SERIAL_IRQ, 253 | }; 254 | pthread_create(&s->worker_tid, NULL, (void *) serial_thread, (void *) s); 255 | 256 | dev_init(&s->dev, COM1_PORT_BASE, COM1_PORT_SIZE, s, serial_handle_io); 257 | bus_register_dev(bus, &s->dev); 258 | 259 | return 0; 260 | } 261 | 262 | void serial_exit(serial_dev_t *s) 263 | { 264 | __atomic_store_n(&thread_stop, true, __ATOMIC_RELAXED); 265 | pthread_join(s->worker_tid, NULL); 266 | } 267 | -------------------------------------------------------------------------------- /src/virtio-net.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "err.h" 18 | #include "utils.h" 19 | #include "virtio-net.h" 20 | #include "vm.h" 21 | 22 | #define TAP_INTERFACE "tap%d" 23 | #define VIRTQ_RX 0 24 | #define VIRTQ_TX 1 25 | #define NOTIFY_OFFSET 2 26 | 27 | static volatile bool thread_stop = false; 28 | 29 | static int virtio_net_virtq_available_rx(struct virtio_net_dev *dev, 30 | int timeout) 31 | { 32 | struct pollfd pollfd = (struct pollfd){ 33 | .fd = dev->tapfd, 34 | .events = POLLIN, 35 | }; 36 | return (poll(&pollfd, 1, timeout) > 0) && (pollfd.revents & POLLIN); 37 | } 38 | 39 | static int virtio_net_virtq_available_tx(struct virtio_net_dev *dev, 40 | int timeout) 41 | { 42 | struct pollfd pollfds[] = { 43 | [0] = {.fd = dev->tx_ioeventfd, .events = POLLIN}, 44 | [1] = {.fd = dev->tapfd, .events = POLLOUT}, 45 | }; 46 | 47 | int ret = poll(pollfds, 2, timeout); 48 | 49 | return ret > 0 && (pollfds[0].revents & POLLIN) && 50 | (pollfds[1].revents & POLLOUT); 51 | } 52 | 53 | static void *virtio_net_vq_avail_handler_rx(void *arg) 54 | { 55 | struct virtq *vq = (struct virtq *) arg; 56 | struct virtio_net_dev *dev = (struct virtio_net_dev *) vq->dev; 57 | 58 | while (!__atomic_load_n(&thread_stop, __ATOMIC_RELAXED)) { 59 | vq->guest_event->flags = VRING_PACKED_EVENT_FLAG_ENABLE; 60 | if (virtio_net_virtq_available_rx(dev, -1)) 61 | virtq_handle_avail(vq); 62 | } 63 | return NULL; 64 | } 65 | 66 | static void *virtio_net_vq_avail_handler_tx(void *arg) 67 | { 68 | struct virtq *vq = (struct virtq *) arg; 69 | struct virtio_net_dev *dev = (struct virtio_net_dev *) vq->dev; 70 | 71 | while (!__atomic_load_n(&thread_stop, __ATOMIC_RELAXED)) { 72 | vq->guest_event->flags = VRING_PACKED_EVENT_FLAG_ENABLE; 73 | if (virtio_net_virtq_available_tx(dev, -1)) 74 | virtq_handle_avail(vq); 75 | } 76 | return NULL; 77 | } 78 | 79 | static void virtio_net_enable_vq_rx(struct virtq *vq) 80 | { 81 | struct virtio_net_dev *dev = (struct virtio_net_dev *) vq->dev; 82 | vm_t *v = container_of(dev, vm_t, virtio_net_dev); 83 | 84 | if (vq->info.enable) 85 | return; 86 | vq->info.enable = true; 87 | vq->desc_ring = 88 | (struct vring_packed_desc *) vm_guest_to_host(v, vq->info.desc_addr); 89 | vq->device_event = (struct vring_packed_desc_event *) vm_guest_to_host( 90 | v, vq->info.device_addr); 91 | vq->guest_event = (struct vring_packed_desc_event *) vm_guest_to_host( 92 | v, vq->info.driver_addr); 93 | uint64_t addr = virtio_pci_get_notify_addr(&dev->virtio_pci_dev, vq); 94 | vm_ioeventfd_register(v, dev->rx_ioeventfd, addr, NOTIFY_OFFSET, 0); 95 | pthread_create(&dev->rx_thread, NULL, virtio_net_vq_avail_handler_rx, 96 | (void *) vq); 97 | } 98 | 99 | static void virtio_net_enable_vq_tx(struct virtq *vq) 100 | { 101 | struct virtio_net_dev *dev = (struct virtio_net_dev *) vq->dev; 102 | vm_t *v = container_of(dev, vm_t, virtio_net_dev); 103 | 104 | if (vq->info.enable) 105 | return; 106 | vq->info.enable = true; 107 | vq->desc_ring = 108 | (struct vring_packed_desc *) vm_guest_to_host(v, vq->info.desc_addr); 109 | vq->device_event = (struct vring_packed_desc_event *) vm_guest_to_host( 110 | v, vq->info.device_addr); 111 | vq->guest_event = (struct vring_packed_desc_event *) vm_guest_to_host( 112 | v, vq->info.driver_addr); 113 | 114 | uint64_t addr = virtio_pci_get_notify_addr(&dev->virtio_pci_dev, vq); 115 | vm_ioeventfd_register(v, dev->tx_ioeventfd, addr, NOTIFY_OFFSET, 0); 116 | pthread_create(&dev->tx_thread, NULL, virtio_net_vq_avail_handler_tx, 117 | (void *) vq); 118 | } 119 | 120 | static void virtio_net_notify_used_rx(struct virtq *vq) 121 | { 122 | struct virtio_net_dev *dev = (struct virtio_net_dev *) vq->dev; 123 | uint64_t n = 1; 124 | if (write(dev->irqfd, &n, sizeof(n)) < 0) 125 | throw_err("Failed to write the irqfd"); 126 | } 127 | 128 | static void virtio_net_notify_used_tx(struct virtq *vq) 129 | { 130 | struct virtio_net_dev *dev = (struct virtio_net_dev *) vq->dev; 131 | uint64_t n = 1; 132 | 133 | if (write(dev->irqfd, &n, sizeof(n)) < 0) 134 | throw_err("Failed to write the irqfd"); 135 | } 136 | 137 | void virtio_net_complete_request_rx(struct virtq *vq) 138 | { 139 | struct virtio_net_dev *dev = (struct virtio_net_dev *) vq->dev; 140 | vm_t *v = container_of(dev, vm_t, virtio_net_dev); 141 | struct vring_packed_desc *desc; 142 | 143 | while ((desc = virtq_get_avail(vq)) != NULL) { 144 | uint8_t *data = vm_guest_to_host(v, desc->addr); 145 | struct virtio_net_hdr_v1 *virtio_hdr = 146 | (struct virtio_net_hdr_v1 *) data; 147 | memset(virtio_hdr, 0, sizeof(struct virtio_net_hdr_v1)); 148 | 149 | virtio_hdr->num_buffers = 1; 150 | 151 | size_t virtio_header_len = sizeof(struct virtio_net_hdr_v1); 152 | ssize_t read_bytes = read(dev->tapfd, data + virtio_header_len, 153 | desc->len - virtio_header_len); 154 | if (read_bytes < 0) { 155 | vq->guest_event->flags = VRING_PACKED_EVENT_FLAG_DISABLE; 156 | return; 157 | } 158 | desc->len = virtio_header_len + read_bytes; 159 | 160 | desc->flags ^= (1ULL << VRING_PACKED_DESC_F_USED); 161 | dev->virtio_pci_dev.config.isr_cap.isr_status |= VIRTIO_PCI_ISR_QUEUE; 162 | return; 163 | } 164 | vq->guest_event->flags = VRING_PACKED_EVENT_FLAG_DISABLE; 165 | return; 166 | } 167 | 168 | void virtio_net_complete_request_tx(struct virtq *vq) 169 | { 170 | struct virtio_net_dev *dev = (struct virtio_net_dev *) vq->dev; 171 | vm_t *v = container_of(dev, vm_t, virtio_net_dev); 172 | struct vring_packed_desc *desc; 173 | while ((desc = virtq_get_avail(vq)) != NULL) { 174 | uint8_t *data = vm_guest_to_host(v, desc->addr); 175 | size_t virtio_header_len = sizeof(struct virtio_net_hdr_v1); 176 | 177 | if (desc->len < virtio_header_len) { 178 | vq->guest_event->flags = VRING_PACKED_EVENT_FLAG_DISABLE; 179 | return; 180 | } 181 | 182 | uint8_t *actual_data = data + virtio_header_len; 183 | size_t actual_data_len = desc->len - virtio_header_len; 184 | 185 | struct iovec iov[1]; 186 | iov[0].iov_base = actual_data; 187 | iov[0].iov_len = actual_data_len; 188 | 189 | ssize_t write_bytes = writev(dev->tapfd, iov, 1); 190 | if (write_bytes < 0) { 191 | vq->guest_event->flags = VRING_PACKED_EVENT_FLAG_DISABLE; 192 | return; 193 | } 194 | desc->flags ^= (1ULL << VRING_PACKED_DESC_F_USED); 195 | dev->virtio_pci_dev.config.isr_cap.isr_status |= VIRTIO_PCI_ISR_QUEUE; 196 | return; 197 | } 198 | vq->guest_event->flags = VRING_PACKED_EVENT_FLAG_DISABLE; 199 | return; 200 | } 201 | 202 | static struct virtq_ops virtio_net_ops[VIRTIO_NET_VIRTQ_NUM] = { 203 | [VIRTQ_RX] = {.enable_vq = virtio_net_enable_vq_rx, 204 | .complete_request = virtio_net_complete_request_rx, 205 | .notify_used = virtio_net_notify_used_rx}, 206 | [VIRTQ_TX] = {.enable_vq = virtio_net_enable_vq_tx, 207 | .complete_request = virtio_net_complete_request_tx, 208 | .notify_used = virtio_net_notify_used_tx}, 209 | }; 210 | 211 | bool virtio_net_init(struct virtio_net_dev *virtio_net_dev) 212 | { 213 | memset(virtio_net_dev, 0x00, sizeof(struct virtio_net_dev)); 214 | 215 | virtio_net_dev->tapfd = open("/dev/net/tun", O_RDWR); 216 | if (virtio_net_dev->tapfd < 0) { 217 | return false; 218 | } 219 | struct ifreq ifreq = {.ifr_flags = IFF_TAP | IFF_NO_PI}; 220 | strncpy(ifreq.ifr_name, TAP_INTERFACE, sizeof(ifreq.ifr_name)); 221 | if (ioctl(virtio_net_dev->tapfd, TUNSETIFF, &ifreq) < 0) { 222 | fprintf(stderr, "failed to allocate TAP device: %s\n", strerror(errno)); 223 | close(virtio_net_dev->tapfd); 224 | return false; 225 | } 226 | int flags = fcntl(virtio_net_dev->tapfd, F_GETFL, 0); 227 | flags |= O_NONBLOCK; 228 | if (fcntl(virtio_net_dev->tapfd, F_SETFL, flags) == -1) { 229 | fprintf(stderr, "failed to set flags on TAP device: %s\n", 230 | strerror(errno)); 231 | close(virtio_net_dev->tapfd); 232 | return false; 233 | } 234 | return true; 235 | } 236 | 237 | static void virtio_net_setup(struct virtio_net_dev *dev) 238 | { 239 | vm_t *v = container_of(dev, vm_t, virtio_net_dev); 240 | 241 | dev->enable = true; 242 | dev->irq_num = VIRTIO_NET_IRQ; 243 | dev->rx_ioeventfd = eventfd(0, EFD_CLOEXEC); 244 | dev->tx_ioeventfd = eventfd(0, EFD_CLOEXEC); 245 | dev->irqfd = eventfd(0, EFD_CLOEXEC); 246 | vm_irqfd_register(v, dev->irqfd, dev->irq_num, 0); 247 | for (int i = 0; i < VIRTIO_NET_VIRTQ_NUM; i++) { 248 | struct virtq_ops *ops = &virtio_net_ops[i]; 249 | dev->vq[i].info.notify_off = i; 250 | virtq_init(&dev->vq[i], dev, ops); 251 | } 252 | } 253 | 254 | void virtio_net_init_pci(struct virtio_net_dev *virtio_net_dev, 255 | struct pci *pci, 256 | struct bus *io_bus, 257 | struct bus *mmio_bus) 258 | { 259 | struct virtio_pci_dev *dev = &virtio_net_dev->virtio_pci_dev; 260 | virtio_net_setup(virtio_net_dev); 261 | virtio_pci_init(dev, pci, io_bus, mmio_bus); 262 | virtio_pci_set_dev_cfg(dev, &virtio_net_dev->config, 263 | sizeof(virtio_net_dev->config)); 264 | virtio_pci_set_pci_hdr(dev, VIRTIO_PCI_DEVICE_ID_NET, VIRTIO_NET_PCI_CLASS, 265 | virtio_net_dev->irq_num); 266 | dev->notify_cap->notify_off_multiplier = NOTIFY_OFFSET; 267 | virtio_pci_set_virtq(dev, virtio_net_dev->vq, VIRTIO_NET_VIRTQ_NUM); 268 | 269 | virtio_pci_add_feature(dev, VIRTIO_NET_F_MQ); 270 | virtio_pci_enable(dev); 271 | } 272 | 273 | void virtio_net_exit(struct virtio_net_dev *dev) 274 | { 275 | if (!dev->enable) 276 | return; 277 | __atomic_store_n(&thread_stop, true, __ATOMIC_RELAXED); 278 | pthread_join(dev->rx_thread, NULL); 279 | pthread_join(dev->tx_thread, NULL); 280 | virtio_pci_exit(&dev->virtio_pci_dev); 281 | close(dev->irqfd); 282 | close(dev->rx_ioeventfd); 283 | close(dev->tx_ioeventfd); 284 | close(dev->tapfd); 285 | } 286 | -------------------------------------------------------------------------------- /src/virtio-pci.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "pci.h" 8 | #include "utils.h" 9 | #include "virtio-pci.h" 10 | 11 | static void virtio_pci_select_device_feature(struct virtio_pci_dev *dev) 12 | { 13 | uint32_t select = dev->config.common_cfg.device_feature_select; 14 | uint64_t feature = dev->device_feature; 15 | 16 | switch (select) { 17 | case 0: 18 | dev->config.common_cfg.device_feature = feature; 19 | break; 20 | case 1: 21 | dev->config.common_cfg.device_feature = feature >> 32; 22 | break; 23 | default: 24 | dev->config.common_cfg.device_feature = 0; 25 | break; 26 | } 27 | } 28 | 29 | static void virtio_pci_write_guest_feature(struct virtio_pci_dev *dev) 30 | { 31 | uint32_t select = dev->config.common_cfg.guest_feature_select; 32 | uint32_t feature = dev->config.common_cfg.guest_feature; 33 | 34 | switch (select) { 35 | case 0: 36 | dev->guest_feature |= feature; 37 | break; 38 | case 1: 39 | dev->guest_feature |= (uint64_t) feature << 32; 40 | break; 41 | default: 42 | /* Ignore writing into the guest feature */ 43 | break; 44 | } 45 | } 46 | 47 | static void virtio_pci_reset(struct virtio_pci_dev *dev) 48 | { 49 | /* TODO: virtio pci reset */ 50 | } 51 | 52 | static void virtio_pci_write_status(struct virtio_pci_dev *dev) 53 | { 54 | uint8_t status = dev->config.common_cfg.device_status; 55 | if (status == 0) { 56 | virtio_pci_reset(dev); 57 | } 58 | } 59 | 60 | static void virtio_pci_select_virtq(struct virtio_pci_dev *dev) 61 | { 62 | uint16_t select = dev->config.common_cfg.queue_select; 63 | struct virtio_pci_common_cfg *config = &dev->config.common_cfg; 64 | 65 | if (select < config->num_queues) { 66 | uint64_t offset = offsetof(struct virtio_pci_common_cfg, queue_size); 67 | memcpy((void *) ((uintptr_t) config + offset), &dev->vq[select].info, 68 | sizeof(struct virtq_info)); 69 | } else { 70 | config->queue_size = 0; 71 | } 72 | } 73 | 74 | static void virtio_pci_enable_virtq(struct virtio_pci_dev *dev) 75 | { 76 | uint16_t select = dev->config.common_cfg.queue_select; 77 | virtq_enable(&dev->vq[select]); 78 | } 79 | 80 | static void virtio_pci_disable_virtq(struct virtio_pci_dev *dev) 81 | { 82 | uint16_t select = dev->config.common_cfg.queue_select; 83 | virtq_disable(&dev->vq[select]); 84 | } 85 | 86 | static void virtio_pci_space_write(struct virtio_pci_dev *dev, 87 | void *data, 88 | uint64_t offset, 89 | uint8_t size) 90 | { 91 | if (offset < offsetof(struct virtio_pci_config, dev_cfg)) { 92 | memcpy((void *) ((uintptr_t) &dev->config + offset), data, size); 93 | switch (offset) { 94 | case VIRTIO_PCI_COMMON_DFSELECT: 95 | virtio_pci_select_device_feature(dev); 96 | break; 97 | case VIRTIO_PCI_COMMON_GFSELECT: 98 | virtio_pci_write_guest_feature(dev); 99 | break; 100 | case VIRTIO_PCI_COMMON_STATUS: 101 | virtio_pci_write_status(dev); 102 | break; 103 | case VIRTIO_PCI_COMMON_Q_SELECT: 104 | virtio_pci_select_virtq(dev); 105 | break; 106 | case VIRTIO_PCI_COMMON_Q_ENABLE: 107 | if (dev->config.common_cfg.queue_enable) 108 | virtio_pci_enable_virtq(dev); 109 | else 110 | virtio_pci_disable_virtq(dev); 111 | break; 112 | default: 113 | if (offset >= VIRTIO_PCI_COMMON_Q_SIZE && 114 | offset <= VIRTIO_PCI_COMMON_Q_USEDHI) { 115 | uint16_t select = dev->config.common_cfg.queue_select; 116 | uint64_t info_offset = offset - VIRTIO_PCI_COMMON_Q_SIZE; 117 | if (select < dev->config.common_cfg.num_queues) 118 | memcpy((void *) ((uintptr_t) &dev->vq[select].info + 119 | info_offset), 120 | data, size); 121 | } 122 | /* guest notify buffer avail */ 123 | else if (offset == 124 | offsetof(struct virtio_pci_config, notify_data)) { 125 | virtq_handle_avail(&dev->vq[dev->config.notify_data.vqn]); 126 | } 127 | break; 128 | } 129 | return; 130 | } 131 | /* dev config write */ 132 | uint64_t dev_offset = offset - offsetof(struct virtio_pci_config, dev_cfg); 133 | memcpy((void *) ((uintptr_t) dev->config.dev_cfg + dev_offset), data, size); 134 | } 135 | 136 | static void virtio_pci_space_read(struct virtio_pci_dev *dev, 137 | void *data, 138 | uint64_t offset, 139 | uint8_t size) 140 | { 141 | if (offset < offsetof(struct virtio_pci_config, dev_cfg)) { 142 | memcpy(data, (void *) ((uintptr_t) &dev->config + offset), size); 143 | if (offset == offsetof(struct virtio_pci_config, isr_cap)) { 144 | dev->config.isr_cap.isr_status = 0; 145 | } 146 | } else { 147 | /* dev config read */ 148 | uint64_t dev_offset = 149 | offset - offsetof(struct virtio_pci_config, dev_cfg); 150 | memcpy(data, (void *) ((uintptr_t) dev->config.dev_cfg + dev_offset), 151 | size); 152 | } 153 | } 154 | 155 | static void virtio_pci_space_io(void *owner, 156 | void *data, 157 | uint8_t is_write, 158 | uint64_t offset, 159 | uint8_t size) 160 | { 161 | struct virtio_pci_dev *virtio_pci_dev = 162 | container_of(owner, struct virtio_pci_dev, pci_dev); 163 | if (is_write) 164 | virtio_pci_space_write(virtio_pci_dev, data, offset, size); 165 | else 166 | virtio_pci_space_read(virtio_pci_dev, data, offset, size); 167 | } 168 | 169 | static void virtio_pci_set_cap(struct virtio_pci_dev *dev, uint8_t next) 170 | { 171 | struct virtio_pci_cap *caps[VIRTIO_PCI_CAP_NUM + 1]; 172 | 173 | for (int i = 1; i < VIRTIO_PCI_CAP_NUM + 1; i++) { 174 | caps[i] = 175 | (struct virtio_pci_cap *) ((uintptr_t) dev->pci_dev.hdr + next); 176 | *caps[i] = (struct virtio_pci_cap){ 177 | .cap_vndr = PCI_CAP_ID_VNDR, 178 | .cfg_type = i, 179 | .cap_len = sizeof(struct virtio_pci_cap), 180 | .bar = 0, 181 | }; 182 | if (i == VIRTIO_PCI_CAP_NOTIFY_CFG || i == VIRTIO_PCI_CAP_PCI_CFG) 183 | caps[i]->cap_len += sizeof(uint32_t); 184 | next += caps[i]->cap_len; 185 | caps[i]->cap_next = next; 186 | } 187 | /* FIXME: The offset for the common config MUST be 4-byte aligned */ 188 | caps[VIRTIO_PCI_CAP_COMMON_CFG]->offset = 189 | offsetof(struct virtio_pci_config, common_cfg); 190 | caps[VIRTIO_PCI_CAP_COMMON_CFG]->length = 191 | sizeof(struct virtio_pci_common_cfg); 192 | 193 | /* FIXME: The offset for the notify cap MUST be 2-byte or 4-byte aligned */ 194 | caps[VIRTIO_PCI_CAP_NOTIFY_CFG]->offset = 195 | offsetof(struct virtio_pci_config, notify_data); 196 | caps[VIRTIO_PCI_CAP_NOTIFY_CFG]->length = 197 | sizeof(struct virtio_pci_notify_data); 198 | 199 | caps[VIRTIO_PCI_CAP_ISR_CFG]->offset = 200 | offsetof(struct virtio_pci_config, isr_cap); 201 | caps[VIRTIO_PCI_CAP_ISR_CFG]->length = sizeof(struct virtio_pci_isr_cap); 202 | 203 | /* FIXME: The offset for the dev-specific configuration MUST be 4-byte 204 | * aligned */ 205 | caps[VIRTIO_PCI_CAP_DEVICE_CFG]->offset = 206 | offsetof(struct virtio_pci_config, dev_cfg); 207 | caps[VIRTIO_PCI_CAP_DEVICE_CFG]->length = 0; 208 | 209 | dev->notify_cap = 210 | (struct virtio_pci_notify_cap *) caps[VIRTIO_PCI_CAP_NOTIFY_CFG]; 211 | dev->dev_cfg_cap = caps[VIRTIO_PCI_CAP_DEVICE_CFG]; 212 | } 213 | 214 | uint64_t virtio_pci_get_notify_addr(struct virtio_pci_dev *dev, 215 | struct virtq *vq) 216 | { 217 | uint64_t base = PCI_HDR_READ(dev->pci_dev.hdr, 218 | PCI_BAR_OFFSET(dev->notify_cap->cap.bar), 32); 219 | uint64_t offset = 220 | dev->notify_cap->cap.offset + 221 | dev->notify_cap->notify_off_multiplier * vq->info.notify_off; 222 | return base + offset; 223 | } 224 | 225 | void virtio_pci_set_dev_cfg(struct virtio_pci_dev *dev, 226 | void *dev_cfg, 227 | uint8_t len) 228 | { 229 | dev->config.dev_cfg = dev_cfg; 230 | dev->dev_cfg_cap->length = len; 231 | } 232 | 233 | void virtio_pci_set_virtq(struct virtio_pci_dev *dev, 234 | struct virtq *vq, 235 | uint16_t num_queues) 236 | { 237 | dev->config.common_cfg.num_queues = num_queues; 238 | dev->vq = vq; 239 | } 240 | 241 | void virtio_pci_add_feature(struct virtio_pci_dev *dev, uint64_t feature) 242 | { 243 | dev->device_feature |= feature; 244 | } 245 | 246 | void virtio_pci_set_pci_hdr(struct virtio_pci_dev *dev, 247 | uint16_t device_id, 248 | uint32_t class, 249 | uint8_t irq_line) 250 | { 251 | PCI_HDR_WRITE(dev->pci_dev.hdr, PCI_DEVICE_ID, device_id, 16); 252 | PCI_HDR_WRITE(dev->pci_dev.hdr, PCI_CLASS_REVISION, class << 8, 32); 253 | PCI_HDR_WRITE(dev->pci_dev.hdr, PCI_INTERRUPT_LINE, irq_line, 8); 254 | } 255 | 256 | void virtio_pci_init(struct virtio_pci_dev *dev, 257 | struct pci *pci, 258 | struct bus *io_bus, 259 | struct bus *mmio_bus) 260 | { 261 | /* The capability list begins at offset 0x40 of pci config space */ 262 | uint8_t cap_list = 0x40; 263 | 264 | memset(dev, 0x00, sizeof(struct virtio_pci_dev)); 265 | pci_dev_init(&dev->pci_dev, pci, io_bus, mmio_bus); 266 | PCI_HDR_WRITE(dev->pci_dev.hdr, PCI_VENDOR_ID, VIRTIO_PCI_VENDOR_ID, 16); 267 | PCI_HDR_WRITE(dev->pci_dev.hdr, PCI_CAPABILITY_LIST, cap_list, 8); 268 | PCI_HDR_WRITE(dev->pci_dev.hdr, PCI_HEADER_TYPE, PCI_HEADER_TYPE_NORMAL, 8); 269 | PCI_HDR_WRITE(dev->pci_dev.hdr, PCI_INTERRUPT_PIN, 1, 8); 270 | pci_set_status(&dev->pci_dev, PCI_STATUS_CAP_LIST | PCI_STATUS_INTERRUPT); 271 | pci_set_bar(&dev->pci_dev, 0, 0x100, PCI_BASE_ADDRESS_SPACE_MEMORY, 272 | virtio_pci_space_io); 273 | virtio_pci_set_cap(dev, cap_list); 274 | dev->device_feature |= 275 | (1ULL << VIRTIO_F_RING_PACKED) | (1ULL << VIRTIO_F_VERSION_1); 276 | } 277 | 278 | void virtio_pci_enable(struct virtio_pci_dev *dev) 279 | { 280 | pci_dev_register(&dev->pci_dev); 281 | } 282 | 283 | void virtio_pci_exit() 284 | { 285 | /* TODO: exit of the virtio pci device */ 286 | } 287 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to `kvm-host` 2 | 3 | :+1::tada: First off, thanks for taking the time to contribute! :tada::+1: 4 | 5 | The following is a set of guidelines for contributing to [kvm-host](https://github.com/sysprog21/kvm-host) 6 | hosted on GitHub. These are mostly guidelines, not rules. Use your best 7 | judgment, and feel free to propose changes to this document in a pull request. 8 | 9 | ## Issues 10 | 11 | This project uses GitHub Issues to track ongoing development, discuss project plans, and keep track of bugs. Be sure to search for existing issues before you create another one. 12 | 13 | Visit our [Issues page on GitHub](https://github.com/sysprog21/kvm-host/issues) to search and submit. 14 | 15 | ## Coding Convention 16 | 17 | We welcome all contributions from corporate, academic and individual developers. However, there are a number of fundamental ground rules that you must adhere to in order to participate. These rules are outlined as follows: 18 | * All code must adhere to the existing C coding style (see below). While we are somewhat flexible in basic style, you will adhere to what is currently in place. Uncommented, complicated algorithmic constructs will be rejected. 19 | * All external pull requests must contain sufficient documentation in the pull request comments in order to be accepted. 20 | 21 | Software requirement: [clang-format](https://clang.llvm.org/docs/ClangFormat.html) version 12 or later. 22 | 23 | Use the command `$ clang-format -i *.[ch]` to enforce a consistent coding style. 24 | 25 | ## Coding Style for Modern C 26 | 27 | This coding style is a variation of the K&R style. Some general principles: honor tradition, but accept progress; be consistent; 28 | embrace the latest C standards; embrace modern compilers, their static analysis 29 | capabilities and sanitizers. 30 | 31 | ### Indentation 32 | 33 | Use 4 spaces rather than tabs. 34 | 35 | ### Line length 36 | 37 | All lines should generally be within 80 characters. Wrap long lines. 38 | There are some good reasons behind this: 39 | * It forces the developer to write more succinct code; 40 | * Humans are better at processing information in smaller quantity portions; 41 | * It helps users of vi/vim (and potentially other editors) who use vertical splits. 42 | 43 | ### Comments 44 | 45 | Multi-line comments shall have the opening and closing characters 46 | in a separate line, with the lines containing the content prefixed by a space 47 | and the `*` characters for alignment, e.g., 48 | ```c 49 | /* 50 | * This is a multi-line comment. 51 | */ 52 | 53 | /* One line comment. */ 54 | ``` 55 | 56 | Use multi-line comments for more elaborative descriptions or before more 57 | significant logical block of code. 58 | 59 | Single-line comments shall be written in C89 style: 60 | ```c 61 | return (uintptr_t) val; /* return a bitfield */ 62 | ``` 63 | 64 | Leave two spaces between the statement and the inline comment. 65 | 66 | ### Spacing and brackets 67 | 68 | Use one space after the conditional or loop keyword, no spaces around 69 | their brackets, and one space before the opening curly bracket. 70 | 71 | Functions (their declarations or calls), `sizeof` operator or similar 72 | macros shall not have a space after their name/keyword or around the 73 | brackets, e.g., 74 | ```c 75 | unsigned total_len = offsetof(obj_t, items[n]); 76 | unsigned obj_len = sizeof(obj_t); 77 | ``` 78 | 79 | Use brackets to avoid ambiguity and with operators such as `sizeof`, 80 | but otherwise avoid redundant or excessive brackets. 81 | 82 | ### Variable names and declarations 83 | 84 | - Use descriptive names for global variables and short names for locals. 85 | Find the right balance between descriptive and succinct. 86 | 87 | - Use [snakecase](https://en.wikipedia.org/wiki/Snake_case). 88 | Do not use "camelcase". 89 | 90 | - Do not use Hungarian notation or other unnecessary prefixing or suffixing. 91 | 92 | - Use the following spacing for pointers: 93 | ```c 94 | const char *name; /* const pointer; '*' with the name and space before it */ 95 | conf_t * const cfg; /* pointer to a const data; spaces around 'const' */ 96 | const uint8_t * const charmap; /* const pointer and const data */ 97 | const void * restrict key; /* const pointer which does not alias */ 98 | ``` 99 | 100 | ### Type definitions 101 | 102 | Declarations shall be on the same line, e.g., 103 | ```c 104 | typedef void (*dir_iter_t)(void *, const char *, struct dirent *); 105 | ``` 106 | 107 | _Typedef_ structures rather than pointers. Note that structures can be kept 108 | opaque if they are not dereferenced outside the translation unit where they 109 | are defined. Pointers can be _typedefed_ only if there is a very compelling 110 | reason. 111 | 112 | New types may be suffixed with `_t`. Structure name, when used within the 113 | translation unit, may be omitted, e.g.: 114 | 115 | ```c 116 | typedef struct { 117 | unsigned if_index; 118 | unsigned addr_len; 119 | addr_t next_hop; 120 | } route_info_t; 121 | ``` 122 | 123 | ### Initialization 124 | 125 | Embrace C99 structure initialization where reasonable, e.g., 126 | ```c 127 | static const crypto_ops_t openssl_ops = { 128 | .create = openssl_crypto_create, 129 | .destroy = openssl_crypto_destroy, 130 | .encrypt = openssl_crypto_encrypt, 131 | .decrypt = openssl_crypto_decrypt, 132 | .hmac = openssl_crypto_hmac, 133 | }; 134 | ``` 135 | 136 | Embrace C99 array initialization, especially for the state machines, e.g., 137 | ```c 138 | static const uint8_t tcp_fsm[TCP_NSTATES][2][TCPFC_COUNT] = { 139 | [TCPS_CLOSED] = { 140 | [FLOW_FORW] = { 141 | /* Handshake (1): initial SYN. */ 142 | [TCPFC_SYN] = TCPS_SYN_SENT, 143 | }, 144 | }, 145 | ... 146 | } 147 | ``` 148 | 149 | ### Control structures 150 | 151 | Try to make the control flow easy to follow. Avoid long convoluted logic 152 | expressions; try to split them where possible (into inline functions, 153 | separate if-statements, etc). 154 | 155 | The control structure keyword and the expression in the brackets should be 156 | separated by a single space. The opening curly bracket shall be in the 157 | same line, also separated by a single space. Example: 158 | 159 | ```c 160 | for (;;) { 161 | obj = get_first(); 162 | while ((obj = get_next(obj))) { 163 | ... 164 | } 165 | if (done) 166 | break; 167 | } 168 | ``` 169 | 170 | Do not add inner spaces around the brackets. There should be one space after 171 | the semicolon when `for` has expressions: 172 | ```c 173 | for (unsigned i = 0; i < __arraycount(items); i++) { 174 | ... 175 | } 176 | ``` 177 | 178 | #### Avoid unnecessary nesting levels 179 | 180 | Avoid: 181 | ```c 182 | int inspect(obj_t *obj) 183 | { 184 | if (cond) { 185 | ... 186 | /* long code block */ 187 | ... 188 | return 0; 189 | } 190 | return -1; 191 | } 192 | ``` 193 | 194 | Consider: 195 | ```c 196 | int inspect(obj_t *obj) 197 | { 198 | if (!cond) 199 | return -1; 200 | 201 | ... 202 | return 0; 203 | } 204 | ``` 205 | 206 | However, do not make logic more convoluted. 207 | 208 | ### `if` statements 209 | 210 | Curly brackets and spacing follow the K&R style: 211 | ```c 212 | if (a == b) { 213 | .. 214 | } else if (a < b) { 215 | ... 216 | } else { 217 | ... 218 | } 219 | ``` 220 | 221 | Simple and succinct one-line if-statements may omit curly brackets: 222 | ```c 223 | if (!valid) 224 | return -1; 225 | ``` 226 | 227 | However, do prefer curly brackets with multi-line or more complex statements. 228 | If one branch uses curly brackets, then all other branches shall use the 229 | curly brackets too. 230 | 231 | Wrap long conditions to the if-statement indentation adding extra 4 spaces: 232 | ```c 233 | if (some_long_expression && 234 | another_expression) { 235 | ... 236 | } 237 | ``` 238 | 239 | #### Avoid redundant `else` 240 | 241 | Avoid: 242 | ```c 243 | if (flag & F_FEATURE_X) { 244 | ... 245 | return 0; 246 | } else { 247 | return -1; 248 | } 249 | ``` 250 | 251 | Consider: 252 | ```c 253 | if (flag & F_FEATURE_X) { 254 | ... 255 | return 0; 256 | } 257 | return -1; 258 | ``` 259 | 260 | ### `switch` statements 261 | 262 | Switch statements should have the `case` blocks at the same indentation 263 | level, e.g.: 264 | ```c 265 | switch (expr) { 266 | case A: 267 | ... 268 | break; 269 | case B: 270 | /* fallthrough */ 271 | case C: 272 | ... 273 | break; 274 | } 275 | ``` 276 | 277 | If the case bock does not break, then it is strongly recommended to add a 278 | comment containing "fallthrough" to indicate it. Modern compilers can also 279 | be configured to require such comment (see gcc `-Wimplicit-fallthrough`). 280 | 281 | ### Function definitions 282 | 283 | The opening and closing curly brackets shall also be in the separate lines (K&R style). 284 | 285 | ```c 286 | ssize_t hex_write(FILE *stream, const void *buf, size_t len) 287 | { 288 | ... 289 | } 290 | ``` 291 | 292 | Do not use old style K&R style C definitions. 293 | 294 | ### Object abstraction 295 | 296 | Objects are often "simulated" by the C programmers with a `struct` and 297 | its "public API". To enforce the information hiding principle, it is a 298 | good idea to define the structure in the source file (translation unit) 299 | and provide only the _declaration_ in the header. For example, `obj.c`: 300 | 301 | ```c 302 | #include "obj.h" 303 | 304 | struct obj { 305 | int value; 306 | } 307 | 308 | obj_t *obj_create(void) 309 | { 310 | return calloc(1, sizeof(obj_t)); 311 | } 312 | 313 | void obj_destroy(obj_t *obj) 314 | { 315 | free(obj); 316 | } 317 | ``` 318 | 319 | With an example `obj.h`: 320 | ```c 321 | #ifndef _OBJ_H_ 322 | #define _OBJ_H_ 323 | 324 | typedef struct obj; 325 | 326 | obj_t *obj_create(void); 327 | void obj_destroy(obj_t *); 328 | 329 | #endif 330 | ``` 331 | 332 | Such structuring will prevent direct access of the `obj_t` members outside 333 | the `obj.c` source file. The implementation (of such "class" or "module") 334 | may be large and abstracted within separate source files. In such case, 335 | consider separating structures and "methods" into separate headers (think of 336 | different visibility), for example `obj_impl.h` (private) and `obj.h` (public). 337 | 338 | Consider `crypto_impl.h`: 339 | ```c 340 | #ifndef _CRYPTO_IMPL_H_ 341 | #define _CRYPTO_IMPL_H_ 342 | 343 | #if !defined(__CRYPTO_PRIVATE) 344 | #error "only to be used by the crypto modules" 345 | #endif 346 | 347 | #include "crypto.h" 348 | 349 | typedef struct crypto { 350 | crypto_cipher_t cipher; 351 | void *key; 352 | size_t key_len; 353 | ... 354 | } 355 | ... 356 | 357 | #endif 358 | ``` 359 | 360 | And `crypto.h` (public API): 361 | 362 | ```c 363 | #ifndef _CRYPTO_H_ 364 | #define _CRYPTO_H_ 365 | 366 | typedef struct crypto crypto_t; 367 | 368 | crypto_t *crypto_create(crypto_cipher_t); 369 | void crypto_destroy(crypto_t *); 370 | ... 371 | 372 | #endif 373 | ``` 374 | 375 | ### Use reasonable types 376 | 377 | Use `unsigned` for general iterators; use `size_t` for general sizes; use 378 | `ssize_t` to return a size which may include an error. Of course, consider 379 | possible overflows. 380 | 381 | Avoid using `uint8_t` or `uint16_t` or other sub-word types for general 382 | iterators and similar cases, unless programming for micro-controllers or 383 | other constrained environments. 384 | 385 | C has rather peculiar _type promotion rules_ and unnecessary use of sub-word 386 | types might contribute to a bug once in a while. 387 | 388 | ### Embrace portability 389 | 390 | #### Byte-order 391 | 392 | Do not assume x86 or little-endian architecture. Use endian conversion 393 | functions for operating the on-disk and on-the-wire structures or other 394 | cases where it is appropriate. 395 | 396 | #### Types 397 | 398 | - Do not assume a particular 32-bit vs 64-bit architecture, e.g., do not 399 | assume the size of `long` or `unsigned long`. Use `int64_t` or `uint64_t` 400 | for the 8-byte integers. 401 | 402 | - Do not assume `char` is signed; for example, on Arm it is unsigned. 403 | 404 | - Use C99 macros for constant prefixes or formatting of the fixed-width 405 | types. 406 | 407 | Use: 408 | ```c 409 | #define SOME_CONSTANT (UINT64_C(1) << 48) 410 | printf("val %" PRIu64 "\n", SOME_CONSTANT); 411 | ``` 412 | 413 | Do not use: 414 | ```c 415 | #define SOME_CONSTANT (1ULL << 48) 416 | printf("val %lld\n", SOME_CONSTANT); 417 | ``` 418 | 419 | #### Avoid unaligned access 420 | 421 | Do not assume unaligned access is safe. It is not safe on Arm, POWER, 422 | and various other architectures. Moreover, even on x86 unaligned access 423 | is slower. 424 | 425 | #### Avoid extreme portability 426 | 427 | Unless programming for micro-controllers or exotic CPU architectures, 428 | focus on the common denominator of the modern CPU architectures, avoiding 429 | the very maximum portability which can make the code unnecessarily cumbersome. 430 | 431 | Some examples: 432 | - It is fair to assume `sizeof(int) == 4` since it is the case on all modern 433 | mainstream architectures. PDP-11 era is long gone. 434 | - Using `1U` instead of `UINT32_C(1)` or `(uint32_t) 1` is also fine. 435 | - It is fair to assume that `NULL` is matching `(uintptr_t) 0` and it is fair 436 | to `memset()` structures with zero. Non-zero `NULL` is for retro computing. 437 | 438 | ## References 439 | - [Linux kernel coding style](https://www.kernel.org/doc/html/latest/process/coding-style.html) 440 | - 1999, Brian W. Kernighan and Rob Pike, The Practice of Programming, Addison–Wesley. 441 | - 1993, Bill Shannon, [C Style and Coding Standards for SunOS](https://devnull-cz.github.io/unix-linux-prog-in-c/cstyle.ms.pdf) 442 | -------------------------------------------------------------------------------- /src/arch/arm64/vm.c: -------------------------------------------------------------------------------- 1 | #if !defined(__aarch64__) || !defined(__linux__) 2 | #error "This implementation is dedicated to Linux/aarch64." 3 | #endif 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "err.h" 13 | #include "vm-arch.h" 14 | #include "vm.h" 15 | 16 | #define IRQCHIP_TYPE_GIC_V2 1 17 | #define IRQCHIP_TYPE_GIC_V3 2 18 | 19 | typedef struct { 20 | uint64_t entry; 21 | size_t initrdsz; 22 | int gic_fd; 23 | int gic_type; 24 | 25 | /* This device is a bridge between mmio_bus and io_bus*/ 26 | struct dev iodev; 27 | } vm_arch_priv_t; 28 | 29 | static vm_arch_priv_t vm_arch_priv; 30 | 31 | static int create_irqchip(vm_t *v) 32 | { 33 | vm_arch_priv_t *priv = (vm_arch_priv_t *) v->priv; 34 | uint64_t dist_addr = ARM_GIC_DIST_BASE; 35 | uint64_t redist_cpui_addr = ARM_GIC_REDIST_CPUI_BASE; 36 | 37 | struct kvm_create_device device = { 38 | .type = KVM_DEV_TYPE_ARM_VGIC_V3, 39 | }; 40 | struct kvm_device_attr dist_attr = { 41 | .group = KVM_DEV_ARM_VGIC_GRP_ADDR, 42 | .attr = KVM_VGIC_V3_ADDR_TYPE_DIST, 43 | .addr = (uint64_t) &dist_addr, 44 | }; 45 | struct kvm_device_attr redist_cpui_attr = { 46 | .group = KVM_DEV_ARM_VGIC_GRP_ADDR, 47 | .attr = KVM_VGIC_V3_ADDR_TYPE_REDIST, 48 | .addr = (uint64_t) &redist_cpui_addr, 49 | }; 50 | 51 | priv->gic_type = IRQCHIP_TYPE_GIC_V3; 52 | if (ioctl(v->vm_fd, KVM_CREATE_DEVICE, &device) < 0) { 53 | /* Try to create GICv2 chip */ 54 | device.type = KVM_DEV_TYPE_ARM_VGIC_V2; 55 | if (ioctl(v->vm_fd, KVM_CREATE_DEVICE, &device) < 0) 56 | return throw_err("Failed to create IRQ chip\n"); 57 | 58 | dist_attr.attr = KVM_VGIC_V2_ADDR_TYPE_DIST; 59 | redist_cpui_attr.attr = KVM_VGIC_V2_ADDR_TYPE_CPU; 60 | priv->gic_type = IRQCHIP_TYPE_GIC_V2; 61 | } 62 | 63 | priv->gic_fd = device.fd; 64 | 65 | if (ioctl(priv->gic_fd, KVM_SET_DEVICE_ATTR, &dist_attr) < 0) 66 | return throw_err( 67 | "Failed to set the address of the distributor of GIC.\n"); 68 | 69 | if (ioctl(priv->gic_fd, KVM_SET_DEVICE_ATTR, &redist_cpui_attr) < 0) 70 | return throw_err("Failed to set the address of the %s of GIC.\n", 71 | device.type == KVM_DEV_TYPE_ARM_VGIC_V3 72 | ? "redistributer" 73 | : "CPU interface"); 74 | 75 | return 0; 76 | } 77 | 78 | int vm_arch_init(vm_t *v) 79 | { 80 | v->priv = &vm_arch_priv; 81 | 82 | /* Create IRQ chip */ 83 | if (create_irqchip(v) < 0) 84 | return -1; 85 | 86 | return 0; 87 | } 88 | 89 | int vm_arch_cpu_init(vm_t *v) 90 | { 91 | struct kvm_vcpu_init vcpu_init; 92 | if (ioctl(v->vm_fd, KVM_ARM_PREFERRED_TARGET, &vcpu_init) < 0) 93 | return throw_err("Failed to find perferred CPU type\n"); 94 | 95 | if (ioctl(v->vcpu_fd, KVM_ARM_VCPU_INIT, &vcpu_init)) 96 | return throw_err("Failed to initialize vCPU\n"); 97 | 98 | return 0; 99 | } 100 | 101 | /* This should be called after all vCPUs are created */ 102 | static int finalize_irqchip(vm_t *v) 103 | { 104 | vm_arch_priv_t *priv = (vm_arch_priv_t *) v->priv; 105 | 106 | struct kvm_device_attr vgic_init_attr = { 107 | .group = KVM_DEV_ARM_VGIC_GRP_CTRL, 108 | .attr = KVM_DEV_ARM_VGIC_CTRL_INIT, 109 | }; 110 | 111 | /* initialize GIC */ 112 | if (ioctl(priv->gic_fd, KVM_SET_DEVICE_ATTR, &vgic_init_attr) < 0) 113 | return throw_err("Failed to initialize the vGIC\n"); 114 | 115 | return 0; 116 | } 117 | 118 | static void pio_handler(void *owner, 119 | void *data, 120 | uint8_t is_write, 121 | uint64_t offset, 122 | uint8_t size) 123 | { 124 | vm_t *v = (vm_t *) owner; 125 | bus_handle_io(&v->io_bus, data, is_write, offset, size); 126 | } 127 | 128 | int vm_arch_init_platform_device(vm_t *v) 129 | { 130 | vm_arch_priv_t *priv = (vm_arch_priv_t *) v->priv; 131 | 132 | /* Initial system bus */ 133 | dev_init(&priv->iodev, ARM_IOPORT_BASE, ARM_IOPORT_SIZE, v, pio_handler); 134 | bus_register_dev(&v->mmio_bus, &priv->iodev); 135 | 136 | /* Initialize PCI bus */ 137 | pci_init(&v->pci); 138 | v->pci.pci_mmio_dev.base = ARM_PCI_CFG_BASE; 139 | bus_register_dev(&v->mmio_bus, &v->pci.pci_mmio_dev); 140 | 141 | /* Initialize serial device */ 142 | if (serial_init(&v->serial, &v->io_bus)) 143 | return throw_err("Failed to init UART device"); 144 | 145 | if (finalize_irqchip(v) < 0) 146 | return -1; 147 | 148 | return 0; 149 | } 150 | 151 | /* The arm64 kernel header 152 | * Reference https://docs.kernel.org/arch/arm64/booting.html 153 | */ 154 | typedef struct { 155 | uint32_t code0; /* Executable code */ 156 | uint32_t code1; /* Executable code */ 157 | uint64_t text_offset; /* Image load offset, little endian */ 158 | uint64_t image_size; /* Effective Image size, little endian */ 159 | uint64_t flags; /* kernel flags, little endian */ 160 | uint64_t res2; /* reserved */ 161 | uint64_t res3; /* reserved */ 162 | uint64_t res4; /* reserved */ 163 | uint32_t magic; /* Magic number, little endian, "ARM\x64" */ 164 | uint32_t res5; /* reserved (used for PE COFF offset) */ 165 | } arm64_kernel_header_t; 166 | 167 | int vm_arch_load_image(vm_t *v, void *data, size_t datasz) 168 | { 169 | vm_arch_priv_t *priv = (vm_arch_priv_t *) v->priv; 170 | 171 | arm64_kernel_header_t *header = data; 172 | if (header->magic != 0x644d5241U) 173 | return throw_err("Invalid kernel image\n"); 174 | 175 | uint64_t offset; 176 | if (header->image_size == 0) 177 | offset = 0x80000; 178 | else 179 | offset = header->text_offset; 180 | 181 | if (offset + datasz >= ARM_KERNEL_SIZE || 182 | offset + header->image_size >= ARM_KERNEL_SIZE) { 183 | return throw_err("Image size too large\n"); 184 | } 185 | 186 | void *dest = vm_guest_to_host(v, ARM_KERNEL_BASE + offset); 187 | memmove(dest, data, datasz); 188 | priv->entry = ARM_KERNEL_BASE + offset; 189 | return 0; 190 | } 191 | 192 | int vm_arch_load_initrd(vm_t *v, void *data, size_t datasz) 193 | { 194 | vm_arch_priv_t *priv = (vm_arch_priv_t *) v->priv; 195 | void *dest = vm_guest_to_host(v, ARM_INITRD_BASE); 196 | memmove(dest, data, datasz); 197 | priv->initrdsz = datasz; 198 | return 0; 199 | } 200 | 201 | /* MPIDR is used by fdt generation. 202 | Reference: 203 | * https://developer.arm.com/documentation/ddi0601/2022-03/AArch64-Registers/MPIDR-EL1--Multiprocessor-Affinity-Register?lang=en 204 | */ 205 | #define ARM_MPIDR_BITMASK 0xFF00FFFFFFUL 206 | #define ARM_MPIDR_REG_ID ARM64_SYS_REG(3, 0, 0, 0, 5) 207 | 208 | static int get_mpidr(vm_t *v, uint64_t *mpidr) 209 | { 210 | struct kvm_one_reg reg; 211 | reg.addr = (uint64_t) mpidr; 212 | reg.id = ARM_MPIDR_REG_ID; 213 | 214 | if (ioctl(v->vcpu_fd, KVM_GET_ONE_REG, ®) < 0) 215 | return throw_err("Failed to get MPIDR register\n"); 216 | 217 | *mpidr &= ARM_MPIDR_BITMASK; 218 | return 0; 219 | } 220 | 221 | /* The phandle of interrupt controller */ 222 | #define FDT_PHANDLE_GIC 1 223 | 224 | /* Definitions of PCI spaces in device tree. 225 | * Reference: 226 | * - PCI Bus Binding to: IEEE Std 1275-1994 227 | * https://www.devicetree.org/open-firmware/bindings/pci/pci2_1.pdf 228 | */ 229 | #define FDT_PCI_IO_SPACE 0x01000000L 230 | #define FDT_PCI_MMIO_SPACE 0x02000000L 231 | 232 | /* Definitions of interrupt mapping 233 | * Reference: 234 | * https://www.kernel.org/doc/Documentation/devicetree/bindings/interrupt-controller/arm%2Cgic.txt 235 | * 236 | * Interrupt is described as 237 | * */ 238 | #define ARM_FDT_IRQ_TYPE_SPI 0 239 | #define ARM_FDT_IRQ_TYPE_PPI 1 240 | #define ARM_FDT_IRQ_EDGE_TRIGGER 1 241 | #define ARM_FDT_IRQ_LEVEL_TRIGGER 4 242 | 243 | /* Helper macro to simplify error handling */ 244 | #define __FDT(action, ...) \ 245 | do { \ 246 | int __ret = fdt_##action(fdt, ##__VA_ARGS__); \ 247 | if (__ret >= 0) \ 248 | break; \ 249 | return throw_err("Failed to create device tree:\n %s\n %s\n", \ 250 | "fdt_" #action "(fdt" __VA_OPT__(", ") #__VA_ARGS__ \ 251 | ")", \ 252 | fdt_strerror(__ret)); \ 253 | } while (0) 254 | 255 | static int generate_fdt(vm_t *v) 256 | { 257 | vm_arch_priv_t *priv = (vm_arch_priv_t *) v->priv; 258 | void *fdt = vm_guest_to_host(v, ARM_FDT_BASE); 259 | 260 | /* Create an empty FDT */ 261 | __FDT(create, FDT_MAX_SIZE); 262 | __FDT(finish_reservemap); 263 | 264 | /* Create / node with its header */ 265 | __FDT(begin_node, ""); 266 | __FDT(property_cell, "#address-cells", 0x2); 267 | __FDT(property_cell, "#size-cells", 0x2); 268 | __FDT(property_cell, "interrupt-parent", FDT_PHANDLE_GIC); 269 | __FDT(property_string, "compatible", "linux,dummy-virt"); 270 | 271 | /* Create /chosen node */ 272 | __FDT(begin_node, "chosen"); 273 | __FDT(property_string, "bootargs", KERNEL_OPTS); 274 | __FDT(property_string, "stdout-path", "/uart"); 275 | if (priv->initrdsz > 0) { 276 | __FDT(property_u64, "linux,initrd-start", ARM_INITRD_BASE); 277 | __FDT(property_u64, "linux,initrd-end", 278 | ARM_INITRD_BASE + priv->initrdsz); 279 | } 280 | __FDT(end_node); /* End of /chosen node */ 281 | 282 | /* Create /memory node */ 283 | __FDT(begin_node, "memory"); 284 | __FDT(property_string, "device_type", "memory"); 285 | uint64_t mem_reg[2] = {cpu_to_fdt64(RAM_BASE), cpu_to_fdt64(RAM_SIZE)}; 286 | __FDT(property, "reg", mem_reg, sizeof(mem_reg)); 287 | __FDT(end_node); /* End of /memory node */ 288 | 289 | /* Create /cpus node */ 290 | __FDT(begin_node, "cpus"); 291 | /* /cpus node headers */ 292 | __FDT(property_cell, "#address-cells", 0x1); 293 | __FDT(property_cell, "#size-cells", 0x0); 294 | /* The only one CPU */ 295 | __FDT(begin_node, "cpu"); /* Create /cpus/cpu subnode */ 296 | uint64_t mpidr; 297 | if (get_mpidr(v, &mpidr) < 0) 298 | return -1; 299 | __FDT(property_cell, "reg", mpidr); 300 | __FDT(property_string, "device_type", "cpu"); 301 | __FDT(property_string, "compatible", "arm,arm-v8"); 302 | __FDT(end_node); /* End of /cpus/cpu */ 303 | __FDT(end_node); /* End of /cpu */ 304 | 305 | /* Create /timer node 306 | * Use the example from 307 | * https://www.kernel.org/doc/Documentation/devicetree/bindings/arm/arch_timer.txt 308 | */ 309 | __FDT(begin_node, "timer"); 310 | __FDT(property_string, "compatible", "arm,armv8-timer"); 311 | uint32_t timer_irq[] = { 312 | cpu_to_fdt32(1), cpu_to_fdt32(13), cpu_to_fdt32(0xf08), 313 | cpu_to_fdt32(1), cpu_to_fdt32(14), cpu_to_fdt32(0xf08), 314 | cpu_to_fdt32(1), cpu_to_fdt32(11), cpu_to_fdt32(0xf08), 315 | cpu_to_fdt32(1), cpu_to_fdt32(10), cpu_to_fdt32(0xf08)}; 316 | __FDT(property, "interrupts", &timer_irq, sizeof(timer_irq)); 317 | __FDT(property, "always-on", NULL, 0); 318 | __FDT(end_node); /* End of /timer node */ 319 | 320 | /* Create /intr node: The interrupt controller */ 321 | __FDT(begin_node, "intr"); 322 | uint64_t gic_reg[] = { 323 | cpu_to_fdt64(ARM_GIC_DIST_BASE), 324 | cpu_to_fdt64(ARM_GIC_DIST_SIZE), 325 | cpu_to_fdt64(ARM_GIC_REDIST_CPUI_BASE), 326 | cpu_to_fdt64(ARM_GIC_REDIST_CPUI_SIZE), 327 | }; 328 | if (priv->gic_type == IRQCHIP_TYPE_GIC_V3) 329 | __FDT(property_string, "compatible", "arm,gic-v3"); 330 | else 331 | __FDT(property_string, "compatible", "arm,cortex-a15-gic"); 332 | __FDT(property_cell, "#interrupt-cells", 3); 333 | __FDT(property, "interrupt-controller", NULL, 0); 334 | __FDT(property, "reg", &gic_reg, sizeof(gic_reg)); 335 | __FDT(property_cell, "phandle", FDT_PHANDLE_GIC); 336 | __FDT(end_node); 337 | 338 | /* /uart node: serial device */ 339 | /* The node name of the serial device is different from kvmtool. */ 340 | __FDT(begin_node, "uart"); 341 | __FDT(property_string, "compatible", "ns16550a"); 342 | __FDT(property_cell, "clock-frequency", 1843200); 343 | uint64_t serial_reg[] = {cpu_to_fdt64(ARM_IOPORT_BASE + COM1_PORT_BASE), 344 | cpu_to_fdt64(COM1_PORT_SIZE)}; 345 | __FDT(property, "reg", &serial_reg, sizeof(serial_reg)); 346 | uint32_t serial_irq[] = {cpu_to_fdt32(ARM_FDT_IRQ_TYPE_SPI), 347 | cpu_to_fdt32(SERIAL_IRQ), 348 | cpu_to_fdt32(ARM_FDT_IRQ_LEVEL_TRIGGER)}; 349 | __FDT(property, "interrupts", &serial_irq, sizeof(serial_irq)); 350 | __FDT(end_node); 351 | 352 | /* /pci node */ 353 | __FDT(begin_node, "pci"); 354 | __FDT(property_string, "device_type", "pci"); 355 | __FDT(property_cell, "#address-cells", 3); 356 | __FDT(property_cell, "#size-cells", 2); 357 | __FDT(property_cell, "#interrupt-cells", 1); 358 | __FDT(property_string, "compatible", "pci-host-cam-generic"); 359 | __FDT(property, "dma-coherent", NULL, 0); 360 | uint32_t pci_bus_range[] = {cpu_to_fdt32(0), cpu_to_fdt32(0)}; 361 | __FDT(property, "bus-range", &pci_bus_range, sizeof(pci_bus_range)); 362 | /* reg should contain the address of configuration space */ 363 | uint64_t pci_reg[] = {cpu_to_fdt64(ARM_PCI_CFG_BASE), 364 | cpu_to_fdt64(ARM_PCI_CFG_SIZE)}; 365 | __FDT(property, "reg", &pci_reg, sizeof(pci_reg)); 366 | /* ranges contains the mapping of the MMIO and IO space. 367 | * We only map the MMIO space here. 368 | */ 369 | struct { 370 | uint32_t pci_hi; 371 | uint64_t pci_addr; 372 | uint64_t cpu_addr; 373 | uint64_t size; 374 | } __attribute__((packed)) pci_ranges[] = { 375 | {cpu_to_fdt32(FDT_PCI_MMIO_SPACE), cpu_to_fdt64(ARM_PCI_MMIO_BASE), 376 | cpu_to_fdt64(ARM_PCI_MMIO_BASE), cpu_to_fdt64(ARM_PCI_MMIO_SIZE)}, 377 | }; 378 | __FDT(property, "ranges", &pci_ranges, sizeof(pci_ranges)); 379 | /* interrupt-map contains the interrupt mapping between the PCI device and 380 | * the IRQ number of interrupt controller. 381 | * virtio-blk is the only PCI device. 382 | */ 383 | struct virtio_blk_dev *virtio_blk = &v->virtio_blk_dev; 384 | struct pci_dev *virtio_blk_pci = (struct pci_dev *) virtio_blk; 385 | struct { 386 | uint32_t pci_hi; 387 | uint64_t pci_addr; 388 | uint32_t pci_irq; 389 | uint32_t intc; 390 | uint32_t gic_type; 391 | uint32_t gic_irqn; 392 | uint32_t gic_irq_type; 393 | } __attribute__((packed)) pci_irq_map[] = {{ 394 | cpu_to_fdt32(virtio_blk_pci->config_dev.base & ~(1UL << 31)), 395 | 0, 396 | cpu_to_fdt32(1), 397 | cpu_to_fdt32(FDT_PHANDLE_GIC), 398 | cpu_to_fdt32(ARM_FDT_IRQ_TYPE_SPI), 399 | cpu_to_fdt32(VIRTIO_BLK_IRQ), 400 | cpu_to_fdt32(ARM_FDT_IRQ_EDGE_TRIGGER), 401 | }}; 402 | __FDT(property, "interrupt-map", &pci_irq_map, sizeof(pci_irq_map)); 403 | __FDT(end_node); /* End of /pci node */ 404 | 405 | /* Finalize the device tree */ 406 | __FDT(end_node); /* End the root node */ 407 | __FDT(finish); 408 | 409 | /* Now, we have a valid device tree stored at ARM_FDT_BASE */ 410 | return 0; 411 | } 412 | #undef __FDT 413 | 414 | /* Initialize the vCPU registers according to Linux arm64 boot protocol 415 | * Reference: https://www.kernel.org/doc/Documentation/arm64/booting.txt 416 | */ 417 | static int init_reg(vm_t *v) 418 | { 419 | vm_arch_priv_t *priv = (vm_arch_priv_t *) v->priv; 420 | struct kvm_one_reg reg; 421 | uint64_t data; 422 | 423 | reg.addr = (uint64_t) &data; 424 | #define __REG(r) \ 425 | (KVM_REG_ARM_CORE_REG(r) | KVM_REG_ARM_CORE | KVM_REG_ARM64 | \ 426 | KVM_REG_SIZE_U64) 427 | 428 | /* Clear x1 ~ x3 */ 429 | for (int i = 0; i < 3; i++) { 430 | data = 0; 431 | reg.id = __REG(regs.regs[i]); 432 | if (ioctl(v->vcpu_fd, KVM_SET_ONE_REG, ®) < 0) 433 | return throw_err("Failed to set x%d\n", i); 434 | } 435 | 436 | /* Set x0 to the address of the device tree */ 437 | data = ARM_FDT_BASE; 438 | reg.id = __REG(regs.regs[0]); 439 | if (ioctl(v->vcpu_fd, KVM_SET_ONE_REG, ®) < 0) 440 | return throw_err("Failed to set x0\n"); 441 | 442 | /* Set program counter to the begining of kernel image */ 443 | data = priv->entry; 444 | reg.id = __REG(regs.pc); 445 | if (ioctl(v->vcpu_fd, KVM_SET_ONE_REG, ®) < 0) 446 | return throw_err("Failed to set program counter\n"); 447 | 448 | #undef _REG 449 | return 0; 450 | } 451 | 452 | int vm_late_init(vm_t *v) 453 | { 454 | if (generate_fdt(v) < 0) 455 | return -1; 456 | 457 | if (init_reg(v) < 0) 458 | return -1; 459 | 460 | return 0; 461 | } 462 | 463 | #define ARM_GIC_SPI_BASE 32 464 | 465 | int vm_irq_line(vm_t *v, int irq, int level) 466 | { 467 | struct kvm_irq_level irq_level = { 468 | .level = level, 469 | }; 470 | 471 | irq_level.irq = (KVM_ARM_IRQ_TYPE_SPI << KVM_ARM_IRQ_TYPE_SHIFT) | 472 | ((irq + ARM_GIC_SPI_BASE) & KVM_ARM_IRQ_NUM_MASK); 473 | 474 | if (ioctl(v->vm_fd, KVM_IRQ_LINE, &irq_level) < 0) 475 | return throw_err("Failed to set the status of an IRQ line, %llx\n", 476 | irq_level.irq); 477 | 478 | return 0; 479 | } 480 | -------------------------------------------------------------------------------- /configs/busybox.config: -------------------------------------------------------------------------------- 1 | CONFIG_HAVE_DOT_CONFIG=y 2 | 3 | # 4 | # Settings 5 | # 6 | # CONFIG_DESKTOP is not set 7 | # CONFIG_EXTRA_COMPAT is not set 8 | # CONFIG_FEDORA_COMPAT is not set 9 | # CONFIG_INCLUDE_SUSv2 is not set 10 | CONFIG_LONG_OPTS=y 11 | CONFIG_SHOW_USAGE=y 12 | CONFIG_FEATURE_VERBOSE_USAGE=y 13 | CONFIG_FEATURE_COMPRESS_USAGE=y 14 | # CONFIG_LFS is not set 15 | # CONFIG_PAM is not set 16 | CONFIG_FEATURE_DEVPTS=y 17 | # CONFIG_FEATURE_UTMP is not set 18 | # CONFIG_FEATURE_WTMP is not set 19 | CONFIG_FEATURE_PIDFILE=y 20 | CONFIG_PID_FILE_PATH="/var/run" 21 | CONFIG_BUSYBOX=y 22 | CONFIG_FEATURE_SHOW_SCRIPT=y 23 | CONFIG_FEATURE_INSTALLER=y 24 | # CONFIG_INSTALL_NO_USR is not set 25 | CONFIG_FEATURE_SUID=y 26 | CONFIG_FEATURE_SUID_CONFIG=y 27 | CONFIG_FEATURE_SUID_CONFIG_QUIET=y 28 | # CONFIG_FEATURE_PREFER_APPLETS is not set 29 | CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" 30 | # CONFIG_SELINUX is not set 31 | # CONFIG_FEATURE_CLEAN_UP is not set 32 | CONFIG_FEATURE_SYSLOG_INFO=y 33 | CONFIG_FEATURE_SYSLOG=y 34 | 35 | # 36 | # Build Options 37 | # 38 | CONFIG_STATIC=y 39 | # CONFIG_PIE is not set 40 | # CONFIG_NOMMU is not set 41 | # CONFIG_BUILD_LIBBUSYBOX is not set 42 | # CONFIG_FEATURE_LIBBUSYBOX_STATIC is not set 43 | # CONFIG_FEATURE_INDIVIDUAL is not set 44 | # CONFIG_FEATURE_SHARED_BUSYBOX is not set 45 | CONFIG_CROSS_COMPILER_PREFIX="" 46 | CONFIG_SYSROOT="" 47 | CONFIG_EXTRA_CFLAGS="" 48 | CONFIG_EXTRA_LDFLAGS="" 49 | CONFIG_EXTRA_LDLIBS="" 50 | # CONFIG_USE_PORTABLE_CODE is not set 51 | # CONFIG_STACK_OPTIMIZATION_386 is not set 52 | CONFIG_STATIC_LIBGCC=y 53 | 54 | # 55 | # Installation Options ("make install" behavior) 56 | # 57 | CONFIG_INSTALL_APPLET_SYMLINKS=y 58 | # CONFIG_INSTALL_APPLET_HARDLINKS is not set 59 | # CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set 60 | # CONFIG_INSTALL_APPLET_DONT is not set 61 | # CONFIG_INSTALL_SH_APPLET_SYMLINK is not set 62 | # CONFIG_INSTALL_SH_APPLET_HARDLINK is not set 63 | # CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set 64 | CONFIG_PREFIX="../rootfs" 65 | 66 | # 67 | # Debugging Options 68 | # 69 | # CONFIG_DEBUG is not set 70 | # CONFIG_DEBUG_PESSIMIZE is not set 71 | # CONFIG_DEBUG_SANITIZE is not set 72 | # CONFIG_UNIT_TEST is not set 73 | # CONFIG_WERROR is not set 74 | # CONFIG_WARN_SIMPLE_MSG is not set 75 | CONFIG_NO_DEBUG_LIB=y 76 | # CONFIG_DMALLOC is not set 77 | # CONFIG_EFENCE is not set 78 | 79 | # 80 | # Library Tuning 81 | # 82 | # CONFIG_FEATURE_USE_BSS_TAIL is not set 83 | CONFIG_FLOAT_DURATION=y 84 | CONFIG_FEATURE_RTMINMAX=y 85 | CONFIG_FEATURE_RTMINMAX_USE_LIBC_DEFINITIONS=y 86 | CONFIG_FEATURE_BUFFERS_USE_MALLOC=y 87 | # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set 88 | # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set 89 | CONFIG_PASSWORD_MINLEN=6 90 | CONFIG_MD5_SMALL=1 91 | CONFIG_SHA1_SMALL=3 92 | # CONFIG_SHA1_HWACCEL is not set 93 | # CONFIG_SHA256_HWACCEL is not set 94 | CONFIG_SHA3_SMALL=1 95 | CONFIG_FEATURE_NON_POSIX_CP=y 96 | # CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set 97 | CONFIG_FEATURE_USE_SENDFILE=y 98 | CONFIG_FEATURE_COPYBUF_KB=4 99 | CONFIG_MONOTONIC_SYSCALL=y 100 | CONFIG_IOCTL_HEX2STR_ERROR=y 101 | CONFIG_FEATURE_EDITING=y 102 | CONFIG_FEATURE_EDITING_MAX_LEN=1024 103 | # CONFIG_FEATURE_EDITING_VI is not set 104 | CONFIG_FEATURE_EDITING_HISTORY=255 105 | # CONFIG_FEATURE_EDITING_SAVEHISTORY is not set 106 | # CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set 107 | CONFIG_FEATURE_REVERSE_SEARCH=y 108 | CONFIG_FEATURE_TAB_COMPLETION=y 109 | CONFIG_FEATURE_USERNAME_COMPLETION=y 110 | CONFIG_FEATURE_EDITING_FANCY_PROMPT=y 111 | CONFIG_FEATURE_EDITING_WINCH=y 112 | # CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set 113 | # CONFIG_LOCALE_SUPPORT is not set 114 | CONFIG_UNICODE_SUPPORT=y 115 | # CONFIG_UNICODE_USING_LOCALE is not set 116 | # CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set 117 | CONFIG_SUBST_WCHAR=63 118 | CONFIG_LAST_SUPPORTED_WCHAR=767 119 | # CONFIG_UNICODE_COMBINING_WCHARS is not set 120 | # CONFIG_UNICODE_WIDE_WCHARS is not set 121 | # CONFIG_UNICODE_BIDI_SUPPORT is not set 122 | # CONFIG_UNICODE_NEUTRAL_TABLE is not set 123 | # CONFIG_UNICODE_PRESERVE_BROKEN is not set 124 | # CONFIG_LOOP_CONFIGURE is not set 125 | # CONFIG_NO_LOOP_CONFIGURE is not set 126 | CONFIG_TRY_LOOP_CONFIGURE=y 127 | 128 | # 129 | # Applets 130 | # 131 | 132 | # 133 | # Archival Utilities 134 | # 135 | CONFIG_FEATURE_SEAMLESS_XZ=y 136 | # CONFIG_FEATURE_SEAMLESS_LZMA is not set 137 | CONFIG_FEATURE_SEAMLESS_BZ2=y 138 | CONFIG_FEATURE_SEAMLESS_GZ=y 139 | # CONFIG_FEATURE_SEAMLESS_Z is not set 140 | # CONFIG_AR is not set 141 | # CONFIG_FEATURE_AR_LONG_FILENAMES is not set 142 | # CONFIG_FEATURE_AR_CREATE is not set 143 | # CONFIG_UNCOMPRESS is not set 144 | CONFIG_GUNZIP=y 145 | CONFIG_ZCAT=y 146 | CONFIG_FEATURE_GUNZIP_LONG_OPTIONS=y 147 | CONFIG_BUNZIP2=y 148 | CONFIG_BZCAT=y 149 | # CONFIG_UNLZMA is not set 150 | # CONFIG_LZCAT is not set 151 | # CONFIG_LZMA is not set 152 | CONFIG_UNXZ=y 153 | CONFIG_XZCAT=y 154 | CONFIG_XZ=y 155 | CONFIG_BZIP2=y 156 | CONFIG_BZIP2_SMALL=8 157 | CONFIG_FEATURE_BZIP2_DECOMPRESS=y 158 | # CONFIG_CPIO is not set 159 | # CONFIG_FEATURE_CPIO_O is not set 160 | # CONFIG_FEATURE_CPIO_P is not set 161 | # CONFIG_FEATURE_CPIO_IGNORE_DEVNO is not set 162 | # CONFIG_FEATURE_CPIO_RENUMBER_INODES is not set 163 | # CONFIG_DPKG is not set 164 | # CONFIG_DPKG_DEB is not set 165 | CONFIG_GZIP=y 166 | CONFIG_FEATURE_GZIP_LONG_OPTIONS=y 167 | CONFIG_GZIP_FAST=0 168 | # CONFIG_FEATURE_GZIP_LEVELS is not set 169 | CONFIG_FEATURE_GZIP_DECOMPRESS=y 170 | # CONFIG_LZOP is not set 171 | # CONFIG_UNLZOP is not set 172 | # CONFIG_LZOPCAT is not set 173 | # CONFIG_LZOP_COMPR_HIGH is not set 174 | # CONFIG_RPM is not set 175 | # CONFIG_RPM2CPIO is not set 176 | CONFIG_TAR=y 177 | CONFIG_FEATURE_TAR_LONG_OPTIONS=y 178 | CONFIG_FEATURE_TAR_CREATE=y 179 | CONFIG_FEATURE_TAR_AUTODETECT=y 180 | CONFIG_FEATURE_TAR_FROM=y 181 | CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y 182 | CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y 183 | CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y 184 | CONFIG_FEATURE_TAR_TO_COMMAND=y 185 | CONFIG_FEATURE_TAR_UNAME_GNAME=y 186 | CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y 187 | # CONFIG_FEATURE_TAR_SELINUX is not set 188 | CONFIG_UNZIP=y 189 | CONFIG_FEATURE_UNZIP_CDF=y 190 | # CONFIG_FEATURE_UNZIP_BZIP2 is not set 191 | # CONFIG_FEATURE_UNZIP_LZMA is not set 192 | # CONFIG_FEATURE_UNZIP_XZ is not set 193 | # CONFIG_FEATURE_LZMA_FAST is not set 194 | 195 | # 196 | # Coreutils 197 | # 198 | CONFIG_FEATURE_VERBOSE=y 199 | 200 | # 201 | # Common options for date and touch 202 | # 203 | # CONFIG_FEATURE_TIMEZONE is not set 204 | 205 | # 206 | # Common options for cp and mv 207 | # 208 | CONFIG_FEATURE_PRESERVE_HARDLINKS=y 209 | 210 | # 211 | # Common options for df, du, ls 212 | # 213 | CONFIG_FEATURE_HUMAN_READABLE=y 214 | CONFIG_BASENAME=y 215 | CONFIG_CAT=y 216 | CONFIG_FEATURE_CATN=y 217 | CONFIG_FEATURE_CATV=y 218 | CONFIG_CHGRP=y 219 | CONFIG_CHMOD=y 220 | CONFIG_CHOWN=y 221 | CONFIG_FEATURE_CHOWN_LONG_OPTIONS=y 222 | CONFIG_CHROOT=y 223 | # CONFIG_CKSUM is not set 224 | # CONFIG_CRC32 is not set 225 | # CONFIG_COMM is not set 226 | CONFIG_CP=y 227 | CONFIG_FEATURE_CP_LONG_OPTIONS=y 228 | CONFIG_FEATURE_CP_REFLINK=y 229 | CONFIG_CUT=y 230 | CONFIG_FEATURE_CUT_REGEX=y 231 | CONFIG_DATE=y 232 | CONFIG_FEATURE_DATE_ISOFMT=y 233 | # CONFIG_FEATURE_DATE_NANO is not set 234 | CONFIG_FEATURE_DATE_COMPAT=y 235 | CONFIG_DD=y 236 | CONFIG_FEATURE_DD_SIGNAL_HANDLING=y 237 | CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y 238 | CONFIG_FEATURE_DD_IBS_OBS=y 239 | CONFIG_FEATURE_DD_STATUS=y 240 | CONFIG_DF=y 241 | CONFIG_FEATURE_DF_FANCY=y 242 | CONFIG_FEATURE_SKIP_ROOTFS=y 243 | CONFIG_DIRNAME=y 244 | # CONFIG_DOS2UNIX is not set 245 | # CONFIG_UNIX2DOS is not set 246 | CONFIG_DU=y 247 | CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y 248 | CONFIG_ECHO=y 249 | CONFIG_FEATURE_FANCY_ECHO=y 250 | CONFIG_ENV=y 251 | # CONFIG_EXPAND is not set 252 | # CONFIG_UNEXPAND is not set 253 | CONFIG_EXPR=y 254 | CONFIG_EXPR_MATH_SUPPORT_64=y 255 | # CONFIG_FACTOR is not set 256 | CONFIG_FALSE=y 257 | # CONFIG_FOLD is not set 258 | CONFIG_HEAD=y 259 | CONFIG_FEATURE_FANCY_HEAD=y 260 | CONFIG_HOSTID=y 261 | CONFIG_ID=y 262 | CONFIG_GROUPS=y 263 | CONFIG_INSTALL=y 264 | CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y 265 | CONFIG_LINK=y 266 | CONFIG_LN=y 267 | # CONFIG_LOGNAME is not set 268 | CONFIG_LS=y 269 | CONFIG_FEATURE_LS_FILETYPES=y 270 | CONFIG_FEATURE_LS_FOLLOWLINKS=y 271 | CONFIG_FEATURE_LS_RECURSIVE=y 272 | CONFIG_FEATURE_LS_WIDTH=y 273 | CONFIG_FEATURE_LS_SORTFILES=y 274 | CONFIG_FEATURE_LS_TIMESTAMPS=y 275 | CONFIG_FEATURE_LS_USERNAME=y 276 | CONFIG_FEATURE_LS_COLOR=y 277 | CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y 278 | # CONFIG_MD5SUM is not set 279 | # CONFIG_SHA1SUM is not set 280 | # CONFIG_SHA256SUM is not set 281 | # CONFIG_SHA512SUM is not set 282 | # CONFIG_SHA3SUM is not set 283 | # CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set 284 | CONFIG_MKDIR=y 285 | CONFIG_MKFIFO=y 286 | CONFIG_MKNOD=y 287 | CONFIG_MKTEMP=y 288 | CONFIG_MV=y 289 | CONFIG_NICE=y 290 | # CONFIG_NL is not set 291 | CONFIG_NOHUP=y 292 | # CONFIG_NPROC is not set 293 | CONFIG_OD=y 294 | CONFIG_PASTE=y 295 | CONFIG_PRINTENV=y 296 | CONFIG_PRINTF=y 297 | CONFIG_PWD=y 298 | CONFIG_READLINK=y 299 | CONFIG_FEATURE_READLINK_FOLLOW=y 300 | CONFIG_REALPATH=y 301 | CONFIG_RM=y 302 | CONFIG_RMDIR=y 303 | CONFIG_SEQ=y 304 | # CONFIG_SHRED is not set 305 | # CONFIG_SHUF is not set 306 | CONFIG_SLEEP=y 307 | CONFIG_FEATURE_FANCY_SLEEP=y 308 | CONFIG_SORT=y 309 | CONFIG_FEATURE_SORT_BIG=y 310 | # CONFIG_FEATURE_SORT_OPTIMIZE_MEMORY is not set 311 | CONFIG_SPLIT=y 312 | CONFIG_FEATURE_SPLIT_FANCY=y 313 | CONFIG_STAT=y 314 | CONFIG_FEATURE_STAT_FORMAT=y 315 | CONFIG_FEATURE_STAT_FILESYSTEM=y 316 | CONFIG_STTY=y 317 | CONFIG_SUM=y 318 | CONFIG_SYNC=y 319 | CONFIG_FEATURE_SYNC_FANCY=y 320 | # CONFIG_FSYNC is not set 321 | # CONFIG_TAC is not set 322 | CONFIG_TAIL=y 323 | CONFIG_FEATURE_FANCY_TAIL=y 324 | CONFIG_TEE=y 325 | CONFIG_FEATURE_TEE_USE_BLOCK_IO=y 326 | CONFIG_TEST=y 327 | CONFIG_TEST1=y 328 | CONFIG_TEST2=y 329 | CONFIG_FEATURE_TEST_64=y 330 | # CONFIG_TIMEOUT is not set 331 | CONFIG_TOUCH=y 332 | CONFIG_FEATURE_TOUCH_SUSV3=y 333 | CONFIG_TR=y 334 | CONFIG_FEATURE_TR_CLASSES=y 335 | CONFIG_FEATURE_TR_EQUIV=y 336 | CONFIG_TRUE=y 337 | CONFIG_TRUNCATE=y 338 | CONFIG_TSORT=y 339 | CONFIG_TTY=y 340 | CONFIG_UNAME=y 341 | CONFIG_UNAME_OSNAME="GNU/Linux" 342 | # CONFIG_BB_ARCH is not set 343 | CONFIG_UNIQ=y 344 | CONFIG_UNLINK=y 345 | CONFIG_USLEEP=y 346 | # CONFIG_UUDECODE is not set 347 | # CONFIG_BASE32 is not set 348 | # CONFIG_BASE64 is not set 349 | # CONFIG_UUENCODE is not set 350 | CONFIG_WC=y 351 | CONFIG_FEATURE_WC_LARGE=y 352 | # CONFIG_WHO is not set 353 | # CONFIG_W is not set 354 | # CONFIG_USERS is not set 355 | CONFIG_WHOAMI=y 356 | CONFIG_YES=y 357 | 358 | # 359 | # Console Utilities 360 | # 361 | CONFIG_CHVT=y 362 | CONFIG_CLEAR=y 363 | # CONFIG_DEALLOCVT is not set 364 | # CONFIG_DUMPKMAP is not set 365 | # CONFIG_FGCONSOLE is not set 366 | # CONFIG_KBD_MODE is not set 367 | # CONFIG_LOADFONT is not set 368 | # CONFIG_SETFONT is not set 369 | # CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set 370 | CONFIG_DEFAULT_SETFONT_DIR="" 371 | # CONFIG_FEATURE_LOADFONT_PSF2 is not set 372 | # CONFIG_FEATURE_LOADFONT_RAW is not set 373 | # CONFIG_LOADKMAP is not set 374 | # CONFIG_OPENVT is not set 375 | # CONFIG_RESET is not set 376 | # CONFIG_RESIZE is not set 377 | # CONFIG_FEATURE_RESIZE_PRINT is not set 378 | # CONFIG_SETCONSOLE is not set 379 | # CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set 380 | # CONFIG_SETKEYCODES is not set 381 | # CONFIG_SETLOGCONS is not set 382 | # CONFIG_SHOWKEY is not set 383 | 384 | # 385 | # Debian Utilities 386 | # 387 | # CONFIG_PIPE_PROGRESS is not set 388 | # CONFIG_RUN_PARTS is not set 389 | # CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set 390 | # CONFIG_FEATURE_RUN_PARTS_FANCY is not set 391 | # CONFIG_START_STOP_DAEMON is not set 392 | # CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set 393 | # CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set 394 | CONFIG_WHICH=y 395 | 396 | # 397 | # klibc-utils 398 | # 399 | # CONFIG_MINIPS is not set 400 | # CONFIG_NUKE is not set 401 | # CONFIG_RESUME is not set 402 | CONFIG_RUN_INIT=y 403 | 404 | # 405 | # Editors 406 | # 407 | CONFIG_AWK=y 408 | CONFIG_FEATURE_AWK_LIBM=y 409 | CONFIG_FEATURE_AWK_GNU_EXTENSIONS=y 410 | CONFIG_CMP=y 411 | CONFIG_DIFF=y 412 | CONFIG_FEATURE_DIFF_LONG_OPTIONS=y 413 | CONFIG_FEATURE_DIFF_DIR=y 414 | # CONFIG_ED is not set 415 | # CONFIG_PATCH is not set 416 | CONFIG_SED=y 417 | CONFIG_VI=y 418 | CONFIG_FEATURE_VI_MAX_LEN=4096 419 | # CONFIG_FEATURE_VI_8BIT is not set 420 | CONFIG_FEATURE_VI_COLON=y 421 | CONFIG_FEATURE_VI_COLON_EXPAND=y 422 | CONFIG_FEATURE_VI_YANKMARK=y 423 | CONFIG_FEATURE_VI_SEARCH=y 424 | # CONFIG_FEATURE_VI_REGEX_SEARCH is not set 425 | CONFIG_FEATURE_VI_USE_SIGNALS=y 426 | CONFIG_FEATURE_VI_DOT_CMD=y 427 | CONFIG_FEATURE_VI_READONLY=y 428 | CONFIG_FEATURE_VI_SETOPTS=y 429 | CONFIG_FEATURE_VI_SET=y 430 | CONFIG_FEATURE_VI_WIN_RESIZE=y 431 | CONFIG_FEATURE_VI_ASK_TERMINAL=y 432 | CONFIG_FEATURE_VI_UNDO=y 433 | CONFIG_FEATURE_VI_UNDO_QUEUE=y 434 | CONFIG_FEATURE_VI_UNDO_QUEUE_MAX=256 435 | CONFIG_FEATURE_VI_VERBOSE_STATUS=y 436 | CONFIG_FEATURE_ALLOW_EXEC=y 437 | 438 | # 439 | # Finding Utilities 440 | # 441 | CONFIG_FIND=y 442 | CONFIG_FEATURE_FIND_PRINT0=y 443 | CONFIG_FEATURE_FIND_MTIME=y 444 | CONFIG_FEATURE_FIND_ATIME=y 445 | CONFIG_FEATURE_FIND_CTIME=y 446 | CONFIG_FEATURE_FIND_MMIN=y 447 | CONFIG_FEATURE_FIND_AMIN=y 448 | CONFIG_FEATURE_FIND_CMIN=y 449 | CONFIG_FEATURE_FIND_PERM=y 450 | CONFIG_FEATURE_FIND_TYPE=y 451 | CONFIG_FEATURE_FIND_EXECUTABLE=y 452 | CONFIG_FEATURE_FIND_XDEV=y 453 | CONFIG_FEATURE_FIND_MAXDEPTH=y 454 | CONFIG_FEATURE_FIND_NEWER=y 455 | CONFIG_FEATURE_FIND_INUM=y 456 | CONFIG_FEATURE_FIND_SAMEFILE=y 457 | CONFIG_FEATURE_FIND_EXEC=y 458 | CONFIG_FEATURE_FIND_EXEC_PLUS=y 459 | CONFIG_FEATURE_FIND_USER=y 460 | CONFIG_FEATURE_FIND_GROUP=y 461 | CONFIG_FEATURE_FIND_NOT=y 462 | CONFIG_FEATURE_FIND_DEPTH=y 463 | CONFIG_FEATURE_FIND_PAREN=y 464 | CONFIG_FEATURE_FIND_SIZE=y 465 | CONFIG_FEATURE_FIND_PRUNE=y 466 | CONFIG_FEATURE_FIND_QUIT=y 467 | CONFIG_FEATURE_FIND_DELETE=y 468 | CONFIG_FEATURE_FIND_EMPTY=y 469 | CONFIG_FEATURE_FIND_PATH=y 470 | CONFIG_FEATURE_FIND_REGEX=y 471 | # CONFIG_FEATURE_FIND_CONTEXT is not set 472 | CONFIG_FEATURE_FIND_LINKS=y 473 | CONFIG_GREP=y 474 | CONFIG_EGREP=y 475 | CONFIG_FGREP=y 476 | CONFIG_FEATURE_GREP_CONTEXT=y 477 | CONFIG_XARGS=y 478 | CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y 479 | CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y 480 | CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y 481 | CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y 482 | CONFIG_FEATURE_XARGS_SUPPORT_REPL_STR=y 483 | CONFIG_FEATURE_XARGS_SUPPORT_PARALLEL=y 484 | CONFIG_FEATURE_XARGS_SUPPORT_ARGS_FILE=y 485 | 486 | # 487 | # Init Utilities 488 | # 489 | # CONFIG_BOOTCHARTD is not set 490 | # CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set 491 | # CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set 492 | CONFIG_HALT=y 493 | # CONFIG_POWEROFF is not set 494 | # CONFIG_REBOOT is not set 495 | CONFIG_FEATURE_WAIT_FOR_INIT=y 496 | # CONFIG_FEATURE_CALL_TELINIT is not set 497 | CONFIG_TELINIT_PATH="" 498 | CONFIG_INIT=y 499 | CONFIG_LINUXRC=y 500 | CONFIG_FEATURE_USE_INITTAB=y 501 | # CONFIG_FEATURE_KILL_REMOVED is not set 502 | CONFIG_FEATURE_KILL_DELAY=0 503 | CONFIG_FEATURE_INIT_SCTTY=y 504 | CONFIG_FEATURE_INIT_SYSLOG=y 505 | CONFIG_FEATURE_INIT_QUIET=y 506 | # CONFIG_FEATURE_INIT_COREDUMPS is not set 507 | CONFIG_INIT_TERMINAL_TYPE="linux" 508 | CONFIG_FEATURE_INIT_MODIFY_CMDLINE=y 509 | 510 | # 511 | # Login/Password Management Utilities 512 | # 513 | # CONFIG_FEATURE_SHADOWPASSWDS is not set 514 | # CONFIG_USE_BB_PWD_GRP is not set 515 | # CONFIG_USE_BB_SHADOW is not set 516 | # CONFIG_USE_BB_CRYPT is not set 517 | # CONFIG_USE_BB_CRYPT_SHA is not set 518 | # CONFIG_ADD_SHELL is not set 519 | # CONFIG_REMOVE_SHELL is not set 520 | # CONFIG_ADDGROUP is not set 521 | # CONFIG_FEATURE_ADDUSER_TO_GROUP is not set 522 | # CONFIG_ADDUSER is not set 523 | # CONFIG_FEATURE_CHECK_NAMES is not set 524 | CONFIG_LAST_ID=0 525 | CONFIG_FIRST_SYSTEM_ID=0 526 | CONFIG_LAST_SYSTEM_ID=0 527 | # CONFIG_CHPASSWD is not set 528 | CONFIG_FEATURE_DEFAULT_PASSWD_ALGO="" 529 | # CONFIG_CRYPTPW is not set 530 | # CONFIG_MKPASSWD is not set 531 | # CONFIG_DELUSER is not set 532 | # CONFIG_DELGROUP is not set 533 | # CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set 534 | # CONFIG_GETTY is not set 535 | # CONFIG_LOGIN is not set 536 | # CONFIG_LOGIN_SESSION_AS_CHILD is not set 537 | # CONFIG_LOGIN_SCRIPTS is not set 538 | # CONFIG_FEATURE_NOLOGIN is not set 539 | # CONFIG_FEATURE_SECURETTY is not set 540 | # CONFIG_PASSWD is not set 541 | # CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set 542 | # CONFIG_SU is not set 543 | # CONFIG_FEATURE_SU_SYSLOG is not set 544 | # CONFIG_FEATURE_SU_CHECKS_SHELLS is not set 545 | # CONFIG_FEATURE_SU_BLANK_PW_NEEDS_SECURE_TTY is not set 546 | # CONFIG_SULOGIN is not set 547 | # CONFIG_VLOCK is not set 548 | 549 | # 550 | # Linux Ext2 FS Progs 551 | # 552 | # CONFIG_CHATTR is not set 553 | # CONFIG_FSCK is not set 554 | # CONFIG_LSATTR is not set 555 | # CONFIG_TUNE2FS is not set 556 | 557 | # 558 | # Linux Module Utilities 559 | # 560 | CONFIG_MODPROBE_SMALL=y 561 | # CONFIG_DEPMOD is not set 562 | CONFIG_INSMOD=y 563 | CONFIG_LSMOD=y 564 | # CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set 565 | CONFIG_MODINFO=y 566 | CONFIG_MODPROBE=y 567 | # CONFIG_FEATURE_MODPROBE_BLACKLIST is not set 568 | CONFIG_RMMOD=y 569 | 570 | # 571 | # Options common to multiple modutils 572 | # 573 | CONFIG_FEATURE_CMDLINE_MODULE_OPTIONS=y 574 | CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED=y 575 | # CONFIG_FEATURE_2_4_MODULES is not set 576 | # CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set 577 | # CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set 578 | # CONFIG_FEATURE_INSMOD_LOADINKMEM is not set 579 | # CONFIG_FEATURE_INSMOD_LOAD_MAP is not set 580 | # CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set 581 | # CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set 582 | # CONFIG_FEATURE_INSMOD_TRY_MMAP is not set 583 | # CONFIG_FEATURE_MODUTILS_ALIAS is not set 584 | # CONFIG_FEATURE_MODUTILS_SYMBOLS is not set 585 | CONFIG_DEFAULT_MODULES_DIR="/lib/modules" 586 | CONFIG_DEFAULT_DEPMOD_FILE="modules.dep" 587 | 588 | # 589 | # Linux System Utilities 590 | # 591 | # CONFIG_ACPID is not set 592 | # CONFIG_FEATURE_ACPID_COMPAT is not set 593 | # CONFIG_BLKDISCARD is not set 594 | # CONFIG_BLKID is not set 595 | # CONFIG_FEATURE_BLKID_TYPE is not set 596 | # CONFIG_BLOCKDEV is not set 597 | # CONFIG_CAL is not set 598 | CONFIG_CHRT=y 599 | CONFIG_DMESG=y 600 | CONFIG_FEATURE_DMESG_PRETTY=y 601 | # CONFIG_EJECT is not set 602 | # CONFIG_FEATURE_EJECT_SCSI is not set 603 | # CONFIG_FALLOCATE is not set 604 | # CONFIG_FATATTR is not set 605 | # CONFIG_FBSET is not set 606 | # CONFIG_FEATURE_FBSET_FANCY is not set 607 | # CONFIG_FEATURE_FBSET_READMODE is not set 608 | # CONFIG_FDFORMAT is not set 609 | # CONFIG_FDISK is not set 610 | # CONFIG_FDISK_SUPPORT_LARGE_DISKS is not set 611 | # CONFIG_FEATURE_FDISK_WRITABLE is not set 612 | # CONFIG_FEATURE_AIX_LABEL is not set 613 | # CONFIG_FEATURE_SGI_LABEL is not set 614 | # CONFIG_FEATURE_SUN_LABEL is not set 615 | # CONFIG_FEATURE_OSF_LABEL is not set 616 | # CONFIG_FEATURE_GPT_LABEL is not set 617 | # CONFIG_FEATURE_FDISK_ADVANCED is not set 618 | # CONFIG_FINDFS is not set 619 | # CONFIG_FLOCK is not set 620 | # CONFIG_FDFLUSH is not set 621 | # CONFIG_FREERAMDISK is not set 622 | # CONFIG_FSCK_MINIX is not set 623 | # CONFIG_FSFREEZE is not set 624 | # CONFIG_FSTRIM is not set 625 | # CONFIG_GETOPT is not set 626 | # CONFIG_FEATURE_GETOPT_LONG is not set 627 | # CONFIG_HEXDUMP is not set 628 | # CONFIG_HD is not set 629 | # CONFIG_XXD is not set 630 | # CONFIG_HWCLOCK is not set 631 | # CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set 632 | # CONFIG_IONICE is not set 633 | # CONFIG_IPCRM is not set 634 | # CONFIG_IPCS is not set 635 | # CONFIG_LAST is not set 636 | # CONFIG_FEATURE_LAST_FANCY is not set 637 | CONFIG_LOSETUP=y 638 | # CONFIG_LSPCI is not set 639 | # CONFIG_LSUSB is not set 640 | CONFIG_MDEV=y 641 | CONFIG_FEATURE_MDEV_CONF=y 642 | CONFIG_FEATURE_MDEV_RENAME=y 643 | CONFIG_FEATURE_MDEV_RENAME_REGEXP=y 644 | CONFIG_FEATURE_MDEV_EXEC=y 645 | CONFIG_FEATURE_MDEV_LOAD_FIRMWARE=y 646 | CONFIG_FEATURE_MDEV_DAEMON=y 647 | # CONFIG_MESG is not set 648 | # CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP is not set 649 | # CONFIG_MKE2FS is not set 650 | # CONFIG_MKFS_EXT2 is not set 651 | # CONFIG_MKFS_MINIX is not set 652 | # CONFIG_FEATURE_MINIX2 is not set 653 | # CONFIG_MKFS_REISER is not set 654 | # CONFIG_MKDOSFS is not set 655 | # CONFIG_MKFS_VFAT is not set 656 | # CONFIG_MKSWAP is not set 657 | # CONFIG_FEATURE_MKSWAP_UUID is not set 658 | CONFIG_MORE=y 659 | CONFIG_MOUNT=y 660 | CONFIG_FEATURE_MOUNT_FAKE=y 661 | CONFIG_FEATURE_MOUNT_VERBOSE=y 662 | # CONFIG_FEATURE_MOUNT_HELPERS is not set 663 | CONFIG_FEATURE_MOUNT_LABEL=y 664 | # CONFIG_FEATURE_MOUNT_NFS is not set 665 | # CONFIG_FEATURE_MOUNT_CIFS is not set 666 | CONFIG_FEATURE_MOUNT_FLAGS=y 667 | CONFIG_FEATURE_MOUNT_FSTAB=y 668 | CONFIG_FEATURE_MOUNT_OTHERTAB=y 669 | # CONFIG_MOUNTPOINT is not set 670 | CONFIG_NOLOGIN=y 671 | # CONFIG_NOLOGIN_DEPENDENCIES is not set 672 | # CONFIG_NSENTER is not set 673 | CONFIG_PIVOT_ROOT=y 674 | # CONFIG_RDATE is not set 675 | # CONFIG_RDEV is not set 676 | # CONFIG_READPROFILE is not set 677 | CONFIG_RENICE=y 678 | # CONFIG_REV is not set 679 | # CONFIG_RTCWAKE is not set 680 | # CONFIG_SCRIPT is not set 681 | # CONFIG_SCRIPTREPLAY is not set 682 | # CONFIG_SETARCH is not set 683 | # CONFIG_LINUX32 is not set 684 | # CONFIG_LINUX64 is not set 685 | # CONFIG_SETPRIV is not set 686 | # CONFIG_FEATURE_SETPRIV_DUMP is not set 687 | # CONFIG_FEATURE_SETPRIV_CAPABILITIES is not set 688 | # CONFIG_FEATURE_SETPRIV_CAPABILITY_NAMES is not set 689 | CONFIG_SETSID=y 690 | # CONFIG_SWAPON is not set 691 | # CONFIG_FEATURE_SWAPON_DISCARD is not set 692 | # CONFIG_FEATURE_SWAPON_PRI is not set 693 | # CONFIG_SWAPOFF is not set 694 | # CONFIG_FEATURE_SWAPONOFF_LABEL is not set 695 | CONFIG_SWITCH_ROOT=y 696 | # CONFIG_TASKSET is not set 697 | # CONFIG_FEATURE_TASKSET_FANCY is not set 698 | # CONFIG_FEATURE_TASKSET_CPULIST is not set 699 | CONFIG_UEVENT=y 700 | CONFIG_UMOUNT=y 701 | CONFIG_FEATURE_UMOUNT_ALL=y 702 | # CONFIG_UNSHARE is not set 703 | # CONFIG_WALL is not set 704 | 705 | # 706 | # Common options for mount/umount 707 | # 708 | CONFIG_FEATURE_MOUNT_LOOP=y 709 | CONFIG_FEATURE_MOUNT_LOOP_CREATE=y 710 | # CONFIG_FEATURE_MTAB_SUPPORT is not set 711 | CONFIG_VOLUMEID=y 712 | 713 | # 714 | # Filesystem/Volume identification 715 | # 716 | # CONFIG_FEATURE_VOLUMEID_BCACHE is not set 717 | # CONFIG_FEATURE_VOLUMEID_BTRFS is not set 718 | # CONFIG_FEATURE_VOLUMEID_CRAMFS is not set 719 | # CONFIG_FEATURE_VOLUMEID_EROFS is not set 720 | # CONFIG_FEATURE_VOLUMEID_EXFAT is not set 721 | CONFIG_FEATURE_VOLUMEID_EXT=y 722 | # CONFIG_FEATURE_VOLUMEID_F2FS is not set 723 | # CONFIG_FEATURE_VOLUMEID_FAT is not set 724 | # CONFIG_FEATURE_VOLUMEID_HFS is not set 725 | # CONFIG_FEATURE_VOLUMEID_ISO9660 is not set 726 | # CONFIG_FEATURE_VOLUMEID_JFS is not set 727 | # CONFIG_FEATURE_VOLUMEID_LFS is not set 728 | # CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set 729 | # CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set 730 | # CONFIG_FEATURE_VOLUMEID_LUKS is not set 731 | # CONFIG_FEATURE_VOLUMEID_MINIX is not set 732 | # CONFIG_FEATURE_VOLUMEID_NILFS is not set 733 | # CONFIG_FEATURE_VOLUMEID_NTFS is not set 734 | # CONFIG_FEATURE_VOLUMEID_OCFS2 is not set 735 | # CONFIG_FEATURE_VOLUMEID_REISERFS is not set 736 | # CONFIG_FEATURE_VOLUMEID_ROMFS is not set 737 | # CONFIG_FEATURE_VOLUMEID_SQUASHFS is not set 738 | # CONFIG_FEATURE_VOLUMEID_SYSV is not set 739 | # CONFIG_FEATURE_VOLUMEID_UBIFS is not set 740 | # CONFIG_FEATURE_VOLUMEID_UDF is not set 741 | # CONFIG_FEATURE_VOLUMEID_XFS is not set 742 | 743 | # 744 | # Miscellaneous Utilities 745 | # 746 | # CONFIG_ADJTIMEX is not set 747 | # CONFIG_ASCII is not set 748 | # CONFIG_BBCONFIG is not set 749 | # CONFIG_FEATURE_COMPRESS_BBCONFIG is not set 750 | # CONFIG_BC is not set 751 | # CONFIG_DC is not set 752 | # CONFIG_FEATURE_DC_BIG is not set 753 | # CONFIG_FEATURE_DC_LIBM is not set 754 | # CONFIG_FEATURE_BC_INTERACTIVE is not set 755 | # CONFIG_FEATURE_BC_LONG_OPTIONS is not set 756 | # CONFIG_BEEP is not set 757 | CONFIG_FEATURE_BEEP_FREQ=0 758 | CONFIG_FEATURE_BEEP_LENGTH_MS=0 759 | # CONFIG_CHAT is not set 760 | # CONFIG_FEATURE_CHAT_NOFAIL is not set 761 | # CONFIG_FEATURE_CHAT_TTY_HIFI is not set 762 | # CONFIG_FEATURE_CHAT_IMPLICIT_CR is not set 763 | # CONFIG_FEATURE_CHAT_SWALLOW_OPTS is not set 764 | # CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set 765 | # CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set 766 | # CONFIG_FEATURE_CHAT_CLR_ABORT is not set 767 | # CONFIG_CONSPY is not set 768 | # CONFIG_CROND is not set 769 | # CONFIG_FEATURE_CROND_D is not set 770 | # CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set 771 | # CONFIG_FEATURE_CROND_SPECIAL_TIMES is not set 772 | CONFIG_FEATURE_CROND_DIR="" 773 | # CONFIG_CRONTAB is not set 774 | # CONFIG_DEVFSD is not set 775 | # CONFIG_DEVFSD_MODLOAD is not set 776 | # CONFIG_DEVFSD_FG_NP is not set 777 | # CONFIG_DEVFSD_VERBOSE is not set 778 | # CONFIG_FEATURE_DEVFS is not set 779 | # CONFIG_DEVMEM is not set 780 | # CONFIG_FBSPLASH is not set 781 | # CONFIG_FLASH_ERASEALL is not set 782 | # CONFIG_FLASH_LOCK is not set 783 | # CONFIG_FLASH_UNLOCK is not set 784 | # CONFIG_FLASHCP is not set 785 | # CONFIG_HDPARM is not set 786 | # CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set 787 | # CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set 788 | # CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set 789 | # CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set 790 | # CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set 791 | # CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set 792 | # CONFIG_HEXEDIT is not set 793 | # CONFIG_I2CGET is not set 794 | # CONFIG_I2CSET is not set 795 | # CONFIG_I2CDUMP is not set 796 | # CONFIG_I2CDETECT is not set 797 | # CONFIG_I2CTRANSFER is not set 798 | # CONFIG_INOTIFYD is not set 799 | # CONFIG_LESS is not set 800 | CONFIG_FEATURE_LESS_MAXLINES=0 801 | # CONFIG_FEATURE_LESS_BRACKETS is not set 802 | # CONFIG_FEATURE_LESS_FLAGS is not set 803 | # CONFIG_FEATURE_LESS_TRUNCATE is not set 804 | # CONFIG_FEATURE_LESS_MARKS is not set 805 | # CONFIG_FEATURE_LESS_REGEXP is not set 806 | # CONFIG_FEATURE_LESS_WINCH is not set 807 | # CONFIG_FEATURE_LESS_ASK_TERMINAL is not set 808 | # CONFIG_FEATURE_LESS_DASHCMD is not set 809 | # CONFIG_FEATURE_LESS_LINENUMS is not set 810 | # CONFIG_FEATURE_LESS_RAW is not set 811 | # CONFIG_FEATURE_LESS_ENV is not set 812 | # CONFIG_LSSCSI is not set 813 | # CONFIG_MAKEDEVS is not set 814 | # CONFIG_FEATURE_MAKEDEVS_LEAF is not set 815 | # CONFIG_FEATURE_MAKEDEVS_TABLE is not set 816 | # CONFIG_MAN is not set 817 | # CONFIG_MICROCOM is not set 818 | # CONFIG_MIM is not set 819 | # CONFIG_MT is not set 820 | # CONFIG_NANDWRITE is not set 821 | # CONFIG_NANDDUMP is not set 822 | # CONFIG_PARTPROBE is not set 823 | # CONFIG_RAIDAUTORUN is not set 824 | # CONFIG_READAHEAD is not set 825 | # CONFIG_RFKILL is not set 826 | # CONFIG_RUNLEVEL is not set 827 | # CONFIG_RX is not set 828 | CONFIG_SEEDRNG=y 829 | # CONFIG_SETFATTR is not set 830 | # CONFIG_SETSERIAL is not set 831 | # CONFIG_STRINGS is not set 832 | # CONFIG_TIME is not set 833 | CONFIG_TREE=y 834 | # CONFIG_TS is not set 835 | # CONFIG_TTYSIZE is not set 836 | # CONFIG_UBIATTACH is not set 837 | # CONFIG_UBIDETACH is not set 838 | # CONFIG_UBIMKVOL is not set 839 | # CONFIG_UBIRMVOL is not set 840 | # CONFIG_UBIRSVOL is not set 841 | # CONFIG_UBIUPDATEVOL is not set 842 | # CONFIG_UBIRENAME is not set 843 | # CONFIG_VOLNAME is not set 844 | # CONFIG_WATCHDOG is not set 845 | # CONFIG_FEATURE_WATCHDOG_OPEN_TWICE is not set 846 | 847 | # 848 | # Networking Utilities 849 | # 850 | # CONFIG_FEATURE_IPV6 is not set 851 | # CONFIG_FEATURE_UNIX_LOCAL is not set 852 | CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y 853 | # CONFIG_VERBOSE_RESOLUTION_ERRORS is not set 854 | # CONFIG_FEATURE_ETC_NETWORKS is not set 855 | # CONFIG_FEATURE_ETC_SERVICES is not set 856 | # CONFIG_FEATURE_HWIB is not set 857 | # CONFIG_FEATURE_TLS_SHA1 is not set 858 | CONFIG_ARP=y 859 | CONFIG_ARPING=y 860 | # CONFIG_BRCTL is not set 861 | # CONFIG_FEATURE_BRCTL_FANCY is not set 862 | # CONFIG_FEATURE_BRCTL_SHOW is not set 863 | # CONFIG_DNSD is not set 864 | # CONFIG_ETHER_WAKE is not set 865 | # CONFIG_FTPD is not set 866 | # CONFIG_FEATURE_FTPD_WRITE is not set 867 | # CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST is not set 868 | # CONFIG_FEATURE_FTPD_AUTHENTICATION is not set 869 | # CONFIG_FTPGET is not set 870 | # CONFIG_FTPPUT is not set 871 | # CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS is not set 872 | CONFIG_HOSTNAME=y 873 | # CONFIG_DNSDOMAINNAME is not set 874 | # CONFIG_HTTPD is not set 875 | CONFIG_FEATURE_HTTPD_PORT_DEFAULT=0 876 | # CONFIG_FEATURE_HTTPD_RANGES is not set 877 | # CONFIG_FEATURE_HTTPD_SETUID is not set 878 | # CONFIG_FEATURE_HTTPD_BASIC_AUTH is not set 879 | # CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set 880 | # CONFIG_FEATURE_HTTPD_CGI is not set 881 | # CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set 882 | # CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set 883 | # CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set 884 | # CONFIG_FEATURE_HTTPD_ERROR_PAGES is not set 885 | # CONFIG_FEATURE_HTTPD_PROXY is not set 886 | # CONFIG_FEATURE_HTTPD_GZIP is not set 887 | # CONFIG_FEATURE_HTTPD_ETAG is not set 888 | # CONFIG_FEATURE_HTTPD_LAST_MODIFIED is not set 889 | # CONFIG_FEATURE_HTTPD_DATE is not set 890 | # CONFIG_FEATURE_HTTPD_ACL_IP is not set 891 | CONFIG_IFCONFIG=y 892 | CONFIG_FEATURE_IFCONFIG_STATUS=y 893 | CONFIG_FEATURE_IFCONFIG_SLIP=y 894 | CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y 895 | CONFIG_FEATURE_IFCONFIG_HW=y 896 | # CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set 897 | # CONFIG_IFENSLAVE is not set 898 | # CONFIG_IFPLUGD is not set 899 | CONFIG_IFUP=y 900 | CONFIG_IFDOWN=y 901 | CONFIG_IFUPDOWN_IFSTATE_PATH="" 902 | CONFIG_FEATURE_IFUPDOWN_IP=y 903 | CONFIG_FEATURE_IFUPDOWN_IPV4=y 904 | CONFIG_FEATURE_IFUPDOWN_IPV6=y 905 | CONFIG_FEATURE_IFUPDOWN_MAPPING=y 906 | # CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set 907 | # CONFIG_INETD is not set 908 | # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set 909 | # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set 910 | # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set 911 | # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set 912 | # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set 913 | # CONFIG_FEATURE_INETD_RPC is not set 914 | CONFIG_IP=y 915 | CONFIG_IPADDR=y 916 | CONFIG_IPLINK=y 917 | CONFIG_IPROUTE=y 918 | CONFIG_IPTUNNEL=y 919 | CONFIG_IPRULE=y 920 | CONFIG_IPNEIGH=y 921 | CONFIG_FEATURE_IP_ADDRESS=y 922 | CONFIG_FEATURE_IP_LINK=y 923 | CONFIG_FEATURE_IP_ROUTE=y 924 | CONFIG_FEATURE_IP_ROUTE_DIR="" 925 | CONFIG_FEATURE_IP_TUNNEL=y 926 | CONFIG_FEATURE_IP_RULE=y 927 | CONFIG_FEATURE_IP_NEIGH=y 928 | # CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set 929 | # CONFIG_IPCALC is not set 930 | # CONFIG_FEATURE_IPCALC_LONG_OPTIONS is not set 931 | # CONFIG_FEATURE_IPCALC_FANCY is not set 932 | # CONFIG_FAKEIDENTD is not set 933 | # CONFIG_NAMEIF is not set 934 | # CONFIG_FEATURE_NAMEIF_EXTENDED is not set 935 | # CONFIG_NBDCLIENT is not set 936 | # CONFIG_NC is not set 937 | # CONFIG_NETCAT is not set 938 | # CONFIG_NC_SERVER is not set 939 | # CONFIG_NC_EXTRA is not set 940 | # CONFIG_NC_110_COMPAT is not set 941 | CONFIG_NETSTAT=y 942 | # CONFIG_FEATURE_NETSTAT_WIDE is not set 943 | # CONFIG_FEATURE_NETSTAT_PRG is not set 944 | CONFIG_NSLOOKUP=y 945 | CONFIG_FEATURE_NSLOOKUP_BIG=y 946 | CONFIG_FEATURE_NSLOOKUP_LONG_OPTIONS=y 947 | # CONFIG_NTPD is not set 948 | # CONFIG_FEATURE_NTPD_SERVER is not set 949 | # CONFIG_FEATURE_NTPD_CONF is not set 950 | # CONFIG_FEATURE_NTP_AUTH is not set 951 | CONFIG_PING=y 952 | # CONFIG_PING6 is not set 953 | CONFIG_FEATURE_FANCY_PING=y 954 | # CONFIG_PSCAN is not set 955 | CONFIG_ROUTE=y 956 | # CONFIG_SLATTACH is not set 957 | # CONFIG_SSL_CLIENT is not set 958 | # CONFIG_TC is not set 959 | # CONFIG_FEATURE_TC_INGRESS is not set 960 | # CONFIG_TCPSVD is not set 961 | # CONFIG_UDPSVD is not set 962 | CONFIG_TELNET=y 963 | CONFIG_FEATURE_TELNET_TTYPE=y 964 | CONFIG_FEATURE_TELNET_AUTOLOGIN=y 965 | CONFIG_FEATURE_TELNET_WIDTH=y 966 | # CONFIG_TELNETD is not set 967 | # CONFIG_FEATURE_TELNETD_STANDALONE is not set 968 | CONFIG_FEATURE_TELNETD_PORT_DEFAULT=0 969 | # CONFIG_FEATURE_TELNETD_INETD_WAIT is not set 970 | # CONFIG_TFTP is not set 971 | # CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set 972 | # CONFIG_FEATURE_TFTP_HPA_COMPAT is not set 973 | # CONFIG_TFTPD is not set 974 | # CONFIG_FEATURE_TFTP_GET is not set 975 | # CONFIG_FEATURE_TFTP_PUT is not set 976 | # CONFIG_FEATURE_TFTP_BLOCKSIZE is not set 977 | # CONFIG_TFTP_DEBUG is not set 978 | # CONFIG_TLS is not set 979 | CONFIG_TRACEROUTE=y 980 | # CONFIG_TRACEROUTE6 is not set 981 | # CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set 982 | # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set 983 | # CONFIG_TUNCTL is not set 984 | # CONFIG_FEATURE_TUNCTL_UG is not set 985 | # CONFIG_VCONFIG is not set 986 | # CONFIG_WGET is not set 987 | # CONFIG_FEATURE_WGET_LONG_OPTIONS is not set 988 | # CONFIG_FEATURE_WGET_STATUSBAR is not set 989 | # CONFIG_FEATURE_WGET_FTP is not set 990 | # CONFIG_FEATURE_WGET_AUTHENTICATION is not set 991 | # CONFIG_FEATURE_WGET_TIMEOUT is not set 992 | # CONFIG_FEATURE_WGET_HTTPS is not set 993 | # CONFIG_FEATURE_WGET_OPENSSL is not set 994 | # CONFIG_WHOIS is not set 995 | # CONFIG_ZCIP is not set 996 | # CONFIG_UDHCPD is not set 997 | # CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set 998 | # CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set 999 | CONFIG_DHCPD_LEASES_FILE="" 1000 | # CONFIG_DUMPLEASES is not set 1001 | # CONFIG_DHCPRELAY is not set 1002 | CONFIG_UDHCPC=y 1003 | CONFIG_FEATURE_UDHCPC_ARPING=y 1004 | CONFIG_FEATURE_UDHCPC_SANITIZEOPT=y 1005 | CONFIG_UDHCPC_DEFAULT_SCRIPT="" 1006 | CONFIG_UDHCPC6_DEFAULT_SCRIPT="" 1007 | # CONFIG_UDHCPC6 is not set 1008 | # CONFIG_FEATURE_UDHCPC6_RFC3646 is not set 1009 | # CONFIG_FEATURE_UDHCPC6_RFC4704 is not set 1010 | # CONFIG_FEATURE_UDHCPC6_RFC4833 is not set 1011 | # CONFIG_FEATURE_UDHCPC6_RFC5970 is not set 1012 | 1013 | CONFIG_UDHCPC_DEFAULT_INTERFACE="" 1014 | # CONFIG_FEATURE_UDHCP_PORT is not set 1015 | CONFIG_UDHCP_DEBUG=0 1016 | CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0 1017 | # CONFIG_FEATURE_UDHCP_RFC3397 is not set 1018 | # CONFIG_FEATURE_UDHCP_8021Q is not set 1019 | CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="" 1020 | 1021 | # 1022 | # Print Utilities 1023 | # 1024 | # CONFIG_LPD is not set 1025 | # CONFIG_LPR is not set 1026 | # CONFIG_LPQ is not set 1027 | 1028 | # 1029 | # Mail Utilities 1030 | # 1031 | CONFIG_FEATURE_MIME_CHARSET="" 1032 | # CONFIG_MAKEMIME is not set 1033 | # CONFIG_POPMAILDIR is not set 1034 | # CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set 1035 | # CONFIG_REFORMIME is not set 1036 | # CONFIG_FEATURE_REFORMIME_COMPAT is not set 1037 | # CONFIG_SENDMAIL is not set 1038 | 1039 | # 1040 | # Process Utilities 1041 | # 1042 | # CONFIG_FEATURE_FAST_TOP is not set 1043 | CONFIG_FEATURE_SHOW_THREADS=y 1044 | CONFIG_FREE=y 1045 | # CONFIG_FUSER is not set 1046 | # CONFIG_IOSTAT is not set 1047 | CONFIG_KILL=y 1048 | CONFIG_KILLALL=y 1049 | CONFIG_KILLALL5=y 1050 | CONFIG_LSOF=y 1051 | # CONFIG_MPSTAT is not set 1052 | # CONFIG_NMETER is not set 1053 | # CONFIG_PGREP is not set 1054 | CONFIG_PKILL=y 1055 | CONFIG_PIDOF=y 1056 | CONFIG_FEATURE_PIDOF_SINGLE=y 1057 | CONFIG_FEATURE_PIDOF_OMIT=y 1058 | # CONFIG_PMAP is not set 1059 | # CONFIG_POWERTOP is not set 1060 | # CONFIG_FEATURE_POWERTOP_INTERACTIVE is not set 1061 | CONFIG_PS=y 1062 | CONFIG_FEATURE_PS_WIDE=y 1063 | CONFIG_FEATURE_PS_LONG=y 1064 | # CONFIG_FEATURE_PS_TIME is not set 1065 | # CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set 1066 | # CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set 1067 | CONFIG_PSTREE=y 1068 | # CONFIG_PWDX is not set 1069 | # CONFIG_SMEMCAP is not set 1070 | CONFIG_BB_SYSCTL=y 1071 | CONFIG_TOP=y 1072 | CONFIG_FEATURE_TOP_INTERACTIVE=y 1073 | CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y 1074 | CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y 1075 | CONFIG_FEATURE_TOP_SMP_CPU=y 1076 | CONFIG_FEATURE_TOP_DECIMALS=y 1077 | CONFIG_FEATURE_TOP_SMP_PROCESS=y 1078 | CONFIG_FEATURE_TOPMEM=y 1079 | CONFIG_UPTIME=y 1080 | # CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set 1081 | CONFIG_WATCH=y 1082 | 1083 | # 1084 | # Runit Utilities 1085 | # 1086 | # CONFIG_CHPST is not set 1087 | # CONFIG_SETUIDGID is not set 1088 | # CONFIG_ENVUIDGID is not set 1089 | # CONFIG_ENVDIR is not set 1090 | # CONFIG_SOFTLIMIT is not set 1091 | # CONFIG_RUNSV is not set 1092 | # CONFIG_RUNSVDIR is not set 1093 | # CONFIG_FEATURE_RUNSVDIR_LOG is not set 1094 | # CONFIG_SV is not set 1095 | CONFIG_SV_DEFAULT_SERVICE_DIR="" 1096 | # CONFIG_SVC is not set 1097 | # CONFIG_SVOK is not set 1098 | # CONFIG_SVLOGD is not set 1099 | # CONFIG_CHCON is not set 1100 | # CONFIG_GETENFORCE is not set 1101 | # CONFIG_GETSEBOOL is not set 1102 | # CONFIG_LOAD_POLICY is not set 1103 | # CONFIG_MATCHPATHCON is not set 1104 | # CONFIG_RUNCON is not set 1105 | # CONFIG_SELINUXENABLED is not set 1106 | # CONFIG_SESTATUS is not set 1107 | # CONFIG_SETENFORCE is not set 1108 | # CONFIG_SETFILES is not set 1109 | # CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set 1110 | # CONFIG_RESTORECON is not set 1111 | # CONFIG_SETSEBOOL is not set 1112 | 1113 | # 1114 | # Shells 1115 | # 1116 | CONFIG_SH_IS_ASH=y 1117 | # CONFIG_SH_IS_HUSH is not set 1118 | # CONFIG_SH_IS_NONE is not set 1119 | # CONFIG_BASH_IS_ASH is not set 1120 | # CONFIG_BASH_IS_HUSH is not set 1121 | CONFIG_BASH_IS_NONE=y 1122 | CONFIG_SHELL_ASH=y 1123 | CONFIG_ASH=y 1124 | CONFIG_ASH_OPTIMIZE_FOR_SIZE=y 1125 | CONFIG_ASH_INTERNAL_GLOB=y 1126 | CONFIG_ASH_BASH_COMPAT=y 1127 | # CONFIG_ASH_BASH_SOURCE_CURDIR is not set 1128 | CONFIG_ASH_BASH_NOT_FOUND_HOOK=y 1129 | CONFIG_ASH_JOB_CONTROL=y 1130 | CONFIG_ASH_ALIAS=y 1131 | CONFIG_ASH_RANDOM_SUPPORT=y 1132 | CONFIG_ASH_EXPAND_PRMT=y 1133 | CONFIG_ASH_IDLE_TIMEOUT=y 1134 | CONFIG_ASH_MAIL=y 1135 | CONFIG_ASH_ECHO=y 1136 | CONFIG_ASH_PRINTF=y 1137 | CONFIG_ASH_TEST=y 1138 | CONFIG_ASH_SLEEP=y 1139 | CONFIG_ASH_HELP=y 1140 | CONFIG_ASH_GETOPTS=y 1141 | CONFIG_ASH_CMDCMD=y 1142 | CONFIG_CTTYHACK=y 1143 | # CONFIG_HUSH is not set 1144 | # CONFIG_SHELL_HUSH is not set 1145 | # CONFIG_HUSH_BASH_COMPAT is not set 1146 | # CONFIG_HUSH_BRACE_EXPANSION is not set 1147 | # CONFIG_HUSH_BASH_SOURCE_CURDIR is not set 1148 | # CONFIG_HUSH_LINENO_VAR is not set 1149 | # CONFIG_HUSH_INTERACTIVE is not set 1150 | # CONFIG_HUSH_SAVEHISTORY is not set 1151 | # CONFIG_HUSH_JOB is not set 1152 | # CONFIG_HUSH_TICK is not set 1153 | # CONFIG_HUSH_IF is not set 1154 | # CONFIG_HUSH_LOOPS is not set 1155 | # CONFIG_HUSH_CASE is not set 1156 | # CONFIG_HUSH_FUNCTIONS is not set 1157 | # CONFIG_HUSH_LOCAL is not set 1158 | # CONFIG_HUSH_RANDOM_SUPPORT is not set 1159 | # CONFIG_HUSH_MODE_X is not set 1160 | # CONFIG_HUSH_ECHO is not set 1161 | # CONFIG_HUSH_PRINTF is not set 1162 | # CONFIG_HUSH_TEST is not set 1163 | # CONFIG_HUSH_HELP is not set 1164 | # CONFIG_HUSH_EXPORT is not set 1165 | # CONFIG_HUSH_EXPORT_N is not set 1166 | # CONFIG_HUSH_READONLY is not set 1167 | # CONFIG_HUSH_KILL is not set 1168 | # CONFIG_HUSH_WAIT is not set 1169 | # CONFIG_HUSH_COMMAND is not set 1170 | # CONFIG_HUSH_TRAP is not set 1171 | # CONFIG_HUSH_TYPE is not set 1172 | # CONFIG_HUSH_TIMES is not set 1173 | # CONFIG_HUSH_READ is not set 1174 | # CONFIG_HUSH_SET is not set 1175 | # CONFIG_HUSH_UNSET is not set 1176 | # CONFIG_HUSH_ULIMIT is not set 1177 | # CONFIG_HUSH_UMASK is not set 1178 | # CONFIG_HUSH_GETOPTS is not set 1179 | # CONFIG_HUSH_MEMLEAK is not set 1180 | 1181 | # 1182 | # Options common to all shells 1183 | # 1184 | CONFIG_FEATURE_SH_MATH=y 1185 | CONFIG_FEATURE_SH_MATH_64=y 1186 | CONFIG_FEATURE_SH_MATH_BASE=y 1187 | CONFIG_FEATURE_SH_EXTRA_QUIET=y 1188 | # CONFIG_FEATURE_SH_STANDALONE is not set 1189 | # CONFIG_FEATURE_SH_NOFORK is not set 1190 | CONFIG_FEATURE_SH_READ_FRAC=y 1191 | CONFIG_FEATURE_SH_HISTFILESIZE=y 1192 | CONFIG_FEATURE_SH_EMBEDDED_SCRIPTS=y 1193 | 1194 | # 1195 | # System Logging Utilities 1196 | # 1197 | # CONFIG_KLOGD is not set 1198 | # CONFIG_FEATURE_KLOGD_KLOGCTL is not set 1199 | # CONFIG_LOGGER is not set 1200 | # CONFIG_LOGREAD is not set 1201 | # CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set 1202 | # CONFIG_SYSLOGD is not set 1203 | # CONFIG_FEATURE_ROTATE_LOGFILE is not set 1204 | # CONFIG_FEATURE_REMOTE_LOG is not set 1205 | # CONFIG_FEATURE_SYSLOGD_DUP is not set 1206 | # CONFIG_FEATURE_SYSLOGD_CFG is not set 1207 | # CONFIG_FEATURE_SYSLOGD_PRECISE_TIMESTAMPS is not set 1208 | CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0 1209 | # CONFIG_FEATURE_IPC_SYSLOG is not set 1210 | CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0 1211 | # CONFIG_FEATURE_KMSG_SYSLOG is not set 1212 | --------------------------------------------------------------------------------