feat: add minimal NanoKVM image target (#835)

* feat: add minimal NanoKVM image target

Add an SG2002 LicheeRV Nano SD board variant based on sg2002_licheervnano_sd, with its own Buildroot configuration and the matching kernel, U-Boot, device tree, memory map, partition, and board initialization files.

Compared with the standard image, replace the broad Buildroot package set with a NanoKVM runtime allowlist. Remove Python 3 and its Python module collection; FFmpeg, OpenCV, ALSA utilities, mpg123, Tcl, and Expect; games, editors, and interactive tools such as tmux, htop, nano, and vim; GDB, tracing, benchmark, stress, memory-test, and SPI-test utilities; IPMI, serial, U-Boot, LCD, and TPU demo tools; and nonessential archive, compression, and SquashFS utilities.

Also omit general-purpose network and administration tools including aircrack-ng, bind, dnsmasq, iperf3, mosh, mtr, netcat, nftables userspace, pppd, rsync, ser2net, socat, SSDP responder, tcpdump, tinc, traceroute, WireGuard tools, and the standalone watchdog package. Disable unneeded hostapd and wpa_supplicant options such as EAP, WPS, IBSS, Wi-Fi Display, mesh, D-Bus, debug logging, and shared client support.

Keep the NanoKVM essentials: Bash, OpenSSH server and key generation, Avahi mDNS, CA certificates and curl, NTP, Ethernet and Wi-Fi management, iproute2, iptables, filesystem resize and virtual-media tools, AIC8800 firmware, the CVITEK runtime, and SG2002 codec firmware.

Run a post-build cleanup that removes SDK sample and test binaries, static and libtool archives, headers, documentation, man and info pages, pkg-config files, and CMake metadata. Optimize the target for size and reduce the Buildroot ext4 filesystem from 1536M to 192M while leaving the SD partition layout unchanged.

Mark minimal root filesystems so the NanoKVM merge step does not bundle FRP, Tailscale, or tailscaled. Keep their existing installation behavior for standard images, install the SSH init script only when OpenSSH is present, validate required init scripts earlier, and remove incomplete output images after recoverable failures.

Isolate Buildroot output and overlays by project, support board-specific Buildroot defconfigs, clean the matching output directory during rebuilds, and document how to build and merge the minimal image.

* feat: shrink minimal NanoKVM image

- reduce the minimal ext4 rootfs image to 112 MiB
- disable udev hwdb and remove stale ISP tuning blobs
- keep only LT6911 sensor support and remove the unused 8733bs driver
- strip NanoKVM-Server debug sections during image merge without modifying the source app

Validated with the minimal build, complete NanoKVM APP merge, ext4 free-space checks, and boot-area integrity verification.
This commit is contained in:
watermeko
2026-08-26 09:30:00 +08:00
committed by GitHub
parent 1559c57961
commit a8325c3070
15 changed files with 1893 additions and 42 deletions

View File

@@ -149,6 +149,31 @@ upgrade.zip
ls -lh "$SDK_DIR"/install/soc_sg2002_licheervnano_sd/images/*.img
```
### 1.6 编译 minimal 镜像
如果需要更小的 rootfs,可以选择 minimal 板型:
```shell
cd "$SDK_DIR"
source build/cvisetup.sh
defconfig sg2002_licheervnano_sd_minimal
clean_all
build_all
```
minimal 产物位于:
```shell
"$SDK_DIR/install/soc_sg2002_licheervnano_sd_minimal/"
```
minimal 镜像不预装 Python3、FRP 和 Tailscale,但保留 NanoKVM、SSH、mDNS、Bash 及基础网络功能。合并 APP 时需要显式指定 minimal 镜像:
```shell
./kvm/merge_nanokvm_app.sh \
-i install/soc_sg2002_licheervnano_sd_minimal/images/your-image-name.img
```
如果编译过程中停在下载进度位置,例如 `Buildroot` 正在下载 `Pillow`、`qt5base`、`qt5svg` 等源码包,通常是网络较慢,不一定是编译卡死。可以观察对应的临时下载文件大小是否仍在增长。
如果首次构建在 `qt5svg` 或 `qt5base` 附近失败,可以先重新执行:
@@ -565,7 +590,7 @@ SDK 的 `kvm/merge_nanokvm_app.sh` 脚本可以自动完成合并流程,包括
- 自动选择最新生成的原始 `.img`,或者使用你手动指定的镜像。
- 默认复制出一个新的 `*-nanokvm.img`,只修改这个输出镜像,不原地修改原始镜像。
- 挂载镜像的 ext4 分区。
- 写入 `kvmapp`、`frpc`、`tailscale`、`tailscaled`、内核模块、启动脚本、默认数据和默认 KVM 配置。
- 写入 `kvmapp`、内核模块、启动脚本、默认数据和默认 KVM 配置;普通镜像还会写入 `frpc`、`tailscale` 和 `tailscaled`。
- 每个关键步骤后检查挂载状态或目标文件,并校验第二分区之前的 boot 区域没有变化;失败时打印具体失败步骤并终止。
- 写入完成后自动卸载镜像。

View File

@@ -543,6 +543,10 @@ br-rootfs-prepare:export CROSS_COMPILE_SDK=$(patsubst "%",%,$(CONFIG_CROSS_COMPI
br-rootfs-prepare:
$(call print_target)
# copy ko and mmf libs
${Q}rm -rf $(BR_OVERLAY_DIR)
${Q}mkdir -p $(BR_OVERLAY_DIR)
${Q}cp -a $(BR_BASE_OVERLAY_DIR)/. $(BR_OVERLAY_DIR)/
${Q}rm -rf $(BR_OVERLAY_DIR)/mnt/system
${Q}mkdir -p $(BR_OVERLAY_DIR)/mnt/system
${Q}cp -arf ${SYSTEM_OUT_DIR}/* $(BR_OVERLAY_DIR)/mnt/system/
# strip
@@ -550,16 +554,16 @@ br-rootfs-prepare:
${Q}find $(BR_OVERLAY_DIR) -name "*.so*" -type f -printf 'striping %p\n' -exec $(CROSS_COMPILE_KERNEL)strip --strip-all {} \;
${Q}find $(BR_OVERLAY_DIR) -executable -type f ! -name "*.sh" ! -path "*etc*" ! -path "*.ko" -printf 'striping %p\n' -exec $(CROSS_COMPILE_SDK)strip --strip-all {} 2>/dev/null \;
br-rootfs-pack:export TARGET_OUTPUT_DIR=$(BR_DIR)/output/$(BR_BOARD)
br-rootfs-pack:export TARGET_OUTPUT_DIR=$(BR_OUTPUT_DIR)
br-rootfs-pack:
$(call print_target)
${Q}$(MAKE) -C $(BR_DIR) $(BR_DEFCONFIG) BR2_TOOLCHAIN_EXTERNAL_PATH=$(CROSS_COMPILE_PATH)
${Q}$(MAKE) -j${NPROC} -C $(BR_DIR) source
${Q}$(MAKE) -j${NPROC} -C $(BR_DIR)
${Q}$(MAKE) -C $(BR_DIR) O=$(TARGET_OUTPUT_DIR) $(BR_DEFCONFIG) BR2_DEFCONFIG=$(BR2_DEFCONFIG) BR2_ROOTFS_OVERLAY=$(BR_OVERLAY_DIR) BR2_TOOLCHAIN_EXTERNAL_PATH=$(CROSS_COMPILE_PATH)
${Q}$(MAKE) -j${NPROC} -C $(BR_DIR) O=$(TARGET_OUTPUT_DIR) BR2_ROOTFS_OVERLAY=$(BR_OVERLAY_DIR) source
${Q}$(MAKE) -j${NPROC} -C $(BR_DIR) O=$(TARGET_OUTPUT_DIR) BR2_ROOTFS_OVERLAY=$(BR_OVERLAY_DIR)
# ${Q}rm -rf $(BR_ROOTFS_DIR)/*
# copy rootfs to rawimg dir
${Q}cp $(BR_DIR)/output/images/rootfs.ext4 $(OUTPUT_DIR)/rawimages/rootfs.$(STORAGE_TYPE)
${Q}tar zcvf $(OUTPUT_DIR)/licheervnano-drivers.tar.gz $(BR_DIR)/output/target/mnt
${Q}cp $(TARGET_OUTPUT_DIR)/images/rootfs.ext4 $(OUTPUT_DIR)/rawimages/rootfs.$(STORAGE_TYPE)
${Q}tar zcvf $(OUTPUT_DIR)/licheervnano-drivers.tar.gz $(TARGET_OUTPUT_DIR)/target/mnt
$(call raw2cimg ,rootfs.$(STORAGE_TYPE))
ifeq ($(CONFIG_BUILDROOT_FS),y)

View File

@@ -0,0 +1,79 @@
BR2_riscv=y
BR2_RISCV_ISA_RVC=y
BR2_TOOLCHAIN_EXTERNAL=y
BR2_TOOLCHAIN_EXTERNAL_CUSTOM=y
BR2_TOOLCHAIN_EXTERNAL_CUSTOM_PREFIX="riscv64-unknown-linux-musl"
BR2_TOOLCHAIN_EXTERNAL_GCC_10=y
BR2_TOOLCHAIN_EXTERNAL_HEADERS_5_10=y
BR2_TOOLCHAIN_EXTERNAL_CUSTOM_MUSL=y
BR2_TOOLCHAIN_EXTERNAL_CXX=y
BR2_TOOLCHAIN_EXTERNAL_OPENMP=y
BR2_TARGET_LDFLAGS="-mcpu=c906fdv -march=rv64imafdcv0p7xthead -mcmodel=medany -mabi=lp64d"
BR2_CCACHE=y
BR2_OPTIMIZE_S=y
BR2_REPRODUCIBLE=y
BR2_PER_PACKAGE_DIRECTORIES=y
BR2_TARGET_GENERIC_HOSTNAME="licheervnano"
BR2_TARGET_GENERIC_ISSUE="Welcome to NanoKVM"
BR2_INIT_SYSV=y
BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y
# The fixed NanoKVM device set does not need the full udev hardware database.
# BR2_PACKAGE_EUDEV_ENABLE_HWDB is not set
BR2_ROOTFS_MERGED_USR=y
BR2_TARGET_ENABLE_ROOT_LOGIN=y
BR2_TARGET_GENERIC_ROOT_PASSWD="root"
BR2_ENABLE_LOCALE_WHITELIST="C en_US en_US.utf8"
BR2_ROOTFS_OVERLAY="$(TOPDIR)/board/cvitek/SG200X/overlay"
BR2_ROOTFS_POST_BUILD_SCRIPT="$(TOPDIR)/../build/boards/sg200x/sg2002_licheervnano_sd_minimal/post-build.sh"
BR2_PACKAGE_BUSYBOX_CONFIG_FRAGMENT_FILES="$(TOPDIR)/board/cvitek/SG200X/busybox-extra.config"
# Filesystem management used by first-boot expansion and virtual media.
BR2_PACKAGE_E2FSPROGS=y
BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y
BR2_PACKAGE_EXFATPROGS=y
BR2_PACKAGE_PARTED=y
# NanoKVM runtime and network services.
BR2_PACKAGE_AVAHI=y
# BR2_PACKAGE_AVAHI_AUTOIPD is not set
BR2_PACKAGE_AVAHI_DAEMON=y
BR2_PACKAGE_BASH=y
BR2_PACKAGE_CA_CERTIFICATES=y
BR2_PACKAGE_LIBCURL=y
BR2_PACKAGE_LIBCURL_CURL=y
BR2_PACKAGE_ETHTOOL=y
BR2_PACKAGE_HOSTAPD=y
BR2_PACKAGE_HOSTAPD_WPA3=y
BR2_PACKAGE_IPROUTE2=y
BR2_PACKAGE_IPTABLES=y
BR2_PACKAGE_IPUTILS=y
BR2_PACKAGE_IW=y
BR2_PACKAGE_NTP=y
BR2_PACKAGE_NTP_NTPD=y
BR2_PACKAGE_NTP_NTPDATE=y
BR2_PACKAGE_OPENSSH=y
# BR2_PACKAGE_OPENSSH_CLIENT is not set
BR2_PACKAGE_OPENSSH_SERVER=y
BR2_PACKAGE_OPENSSH_KEY_UTILS=y
# Python is intentionally excluded from the minimal image.
# BR2_PACKAGE_PYTHON3 is not set
BR2_PACKAGE_WIRELESS_REGDB=y
BR2_PACKAGE_WIRELESS_TOOLS=y
BR2_PACKAGE_WIRELESS_TOOLS_LIB=y
BR2_PACKAGE_WPA_SUPPLICANT=y
BR2_PACKAGE_WPA_SUPPLICANT_WEXT=y
BR2_PACKAGE_WPA_SUPPLICANT_AP_SUPPORT=y
BR2_PACKAGE_WPA_SUPPLICANT_AUTOSCAN=y
BR2_PACKAGE_WPA_SUPPLICANT_HOTSPOT=y
BR2_PACKAGE_WPA_SUPPLICANT_WPA3=y
BR2_PACKAGE_WPA_SUPPLICANT_CLI=y
BR2_PACKAGE_WPA_SUPPLICANT_PASSPHRASE=y
BR2_PACKAGE_UTIL_LINUX_RFKILL=y
# Board runtime, Wi-Fi firmware and video codec firmware.
BR2_PACKAGE_AIC8800_SDIO_FIRMWARE=y
BR2_PACKAGE_CVITEK_RISCV64_MUSL_SYSROOT=y
BR2_PACKAGE_SG2002_CODEC_FIRMWARE=y
BR2_TARGET_ROOTFS_EXT2=y
BR2_TARGET_ROOTFS_EXT2_4=y
BR2_TARGET_ROOTFS_EXT2_SIZE="112M"

View File

@@ -0,0 +1,9 @@
{
"ddr_cfg_list": [
"",
"ddr3_1866_x16",
"ddr3_2133_x16",
"ddr_auto_x16"
],
"board_information": "NanoKVM, C906B + DDR 256MB"
}

View File

@@ -0,0 +1,272 @@
/dts-v1/;
#include "soph_base_riscv.dtsi"
#include "soph_asic_qfn.dtsi"
#include "soph_asic_sd.dtsi"
#include "soph_default_memmap.dtsi"
#include <dt-bindings/input/linux-event-codes.h>
#include <dt-bindings/gpio/gpio.h>
&i2c0 {
status = "okay";
/delete-property/ scl-pinmux;
/delete-property/ sda-pinmux;
/delete-property/ scl-gpios;
/delete-property/ sda-gpios;
};
&i2c1 {
status = "okay";
/delete-property/ scl-pinmux;
/delete-property/ sda-pinmux;
/delete-property/ scl-gpios;
/delete-property/ sda-gpios;
};
&sd {
min-frequency = <400000>; // 400Khz
//max-frequency = <50000000>; // 50Mhz
max-frequency = <25000000>; // 25Mhz
//max-frequency = <10000000>; // 10Mhz
//max-frequency = <5000000>; // 5Mhz
};
&cvi_vo {
#ifndef __UBOOT__
/delete-property/ reset-gpio;
/delete-property/ pwm-gpio;
#else
// for uboot
reset-gpio = <&porte 0 1>;
pwm-gpio = <&porte 2 1>;
#endif
/delete-property/ power-ct-gpio;
};
&mipi_tx {
#ifndef __UBOOT__
// for linux kernel
reset-gpio = <&porte 0 1>;
pwm-gpio = <&porte 2 1>;
#else
/delete-property/ reset-gpio;
/delete-property/ pwm-gpio;
#endif
/delete-property/ power-ct-gpio;
status = "okay";
};
&uart0 {
clocks = <&clk CV181X_CLK_UART0>;
/delete-property/ clock-frequency;
};
&uart1 {
clocks = <&clk CV181X_CLK_UART1>;
/delete-property/ clock-frequency;
status = "okay";
};
&uart2 {
clocks = <&clk CV181X_CLK_UART2>;
/delete-property/ clock-frequency;
status = "okay";
};
&uart3 {
clocks = <&clk CV181X_CLK_UART3>;
/delete-property/ clock-frequency;
status = "okay";
};
&pwm0 {
status = "okay";
};
&pwm1 {
status = "okay";
};
&pwm2 {
status = "okay";
};
&i2c2 {
status = "disabled";
/delete-property/ scl-pinmux;
/delete-property/ sda-pinmux;
/delete-property/ scl-gpios;
/delete-property/ sda-gpios;
};
&i2c3 {
status = "okay";
/delete-property/ scl-pinmux;
/delete-property/ sda-pinmux;
/delete-property/ scl-gpios;
/delete-property/ sda-gpios;
};
&i2c4 {
status = "okay";
gt9xx: gt9xx@14 {
compatible = "goodix,gt9xx";
reg = <0x14>;
goodix,irq-gpio = <&porte 3 0>;
goodix,rst-gpio = <&porte 4 0>;
status = "okay";
};
hynitron: hynitron@5a {
compatible = "hynitron,hyn_ts", "hyn,7xx";
reg = <0x5a>;
irq-gpio = <&porte 3 0>;
hynitron,irq-gpio = <&porte 3 0>;
reset-gpio = <&porte 4 0>;
hynitron,reset-gpio = <&porte 4 0>;
max-touch-number = <5>;
hynitron,max-touch-number = <5>;
display-coords = <0 0 480 640>;
hynitron,display-coords = <368 552>;
pos-swap = <0>;
posx-reverse = <1>;
posy-reverse = <1>;
status = "okay";
};
};
&spi0 {
status = "okay";
};
&spi1 {
status = "okay";
};
&spi2 {
status = "okay";
// /delete-node/ spidev@0;
st7789: st7789@0{
compatible = "sitronix,st7789";
reg = <0>;
status = "disabled";
spi-max-frequency = <80000000>;
spi-cpol;
spi-cpha;
rotate = <0>;
fps = <60>;
rgb;
buswidth = <8>;
dc = <&porte 20 GPIO_ACTIVE_HIGH>;
reset = <&porta 14 GPIO_ACTIVE_LOW>;
debug = <0>;
};
};
&spi3 {
status = "okay";
};
&dac {
status = "okay";
/delete-property/ mute-gpio-r;
};
&fb_reserved {
status = "okay";
};
&cvi_fb {
status = "okay";
};
&mipi_rx {
snsr-reset = <&porte 1 GPIO_ACTIVE_LOW>;
};
/ {
model = "LicheeRv Nano";
spi4: spi4@gpio {
compatible = "spi-gpio";
sck-gpio = <&porta 22 0>;
miso-gpio = <&porta 23 0>;
mosi-gpio = <&porta 25 0>;
cs-gpio = <&porta 24 GPIO_ACTIVE_LOW>;
num-chipselects = <1>;
status = "okay";
spidev@0 {
compatible = "spidev";
reg = <0>;
};
};
i2c5: i2c5@gpio {
compatible = "i2c-gpio";
scl-gpios = <&porta 15 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
sda-gpios = <&porta 27 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <5>;
status = "okay";
};
gpio-keys {
compatible = "gpio-keys";
key-user {
label = "User Key";
linux,code = <KEY_DISPLAYTOGGLE>;
debounce-interval = <1>;
gpios = <&porta 30 1>;
};
};
leds {
compatible = "gpio-leds";
led-user {
gpios = <&porta 14 GPIO_ACTIVE_HIGH>;
linux,default-trigger = "activity";
};
};
// buggy pwm driver, not working, please use userspace pwm api
/*
lcd0_backlight: pwm-backlight@0 {
compatible = "pwm-backlight";
pwm-names = "backlight";
pwms = <&pwm2 2 50000000>; // 20Khz
brightness-levels = <0 2 4 8 16 32 64 128 255>;
default-brightness-level = <4>;
};
*/
wifisd:wifi-sd@4320000 {
compatible = "cvitek,cv181x-sdio";
bus-width = <4>;
reg = <0x0 0x4320000 0x0 0x1000>;
reg_names = "core_mem";
src-frequency = <375000000>;
min-frequency = <400000>; // 400Khz
//max-frequency = <50000000>; // 50Mhz
//max-frequency = <45000000>; // 45Mhz
//max-frequency = <40000000>; // 40Mhz
//max-frequency = <35000000>; // 35Mhz
//max-frequency = <30000000>; // 30Mhz
max-frequency = <25000000>; // 25Mhz
//max-frequency = <20000000>; // 20Mhz
//max-frequency = <15000000>; // 15Mhz
//max-frequency = <10000000>; // 10Mhz
//max-frequency = <5000000>; // 5Mhz
64_addressing;
reset_tx_rx_phy;
non-removable;
pll_index = <0x7>;
pll_reg = <0x300207C>;
no-mmc;
no-sd;
status = "okay";
interrupts = <38 IRQ_TYPE_LEVEL_HIGH>;
interrupt-parent = <&plic0>;
};
};

View File

@@ -0,0 +1,85 @@
SIZE_1M = 0x100000
SIZE_1K = 1024
# Only attributes in class MemoryMap are generated to .h
class MemoryMap:
# No prefix "CVIMMAP_" for the items in _no_prefix[]
_no_prefix = [
"CONFIG_SYS_TEXT_BASE" # u-boot's CONFIG_SYS_TEXT_BASE is used without CPP.
]
DRAM_BASE = 0x80000000
DRAM_SIZE = 256 * SIZE_1M
# ==============
# C906L FreeRTOS
# ==============
FREERTOS_SIZE = 2 * SIZE_1M
# FreeRTOS is at the end of DRAM
FREERTOS_ADDR = DRAM_BASE + DRAM_SIZE - FREERTOS_SIZE
FSBL_C906L_START_ADDR = FREERTOS_ADDR
# ==============================
# OpenSBI | arm-trusted-firmware
# ==============================
# Monitor is at the begining of DRAM
MONITOR_ADDR = DRAM_BASE
ATF_SIZE = 512 * SIZE_1K
OPENSBI_SIZE = 512 * SIZE_1K
OPENSBI_FDT_ADDR = MONITOR_ADDR + OPENSBI_SIZE
# =========================
# memory@DRAM_BASE in .dts.
# =========================
# Ignore the area of FreeRTOS in u-boot and kernel
KERNEL_MEMORY_ADDR = DRAM_BASE
KERNEL_MEMORY_SIZE = DRAM_SIZE - FREERTOS_SIZE
# =================
# Multimedia buffer. Used by u-boot/kernel/FreeRTOS
# =================
ION_SIZE = 75 * SIZE_1M
H26X_BITSTREAM_SIZE = 2 * SIZE_1M
H26X_ENC_BUFF_SIZE = 0
ISP_MEM_BASE_SIZE = 20 * SIZE_1M
FREERTOS_RESERVED_ION_SIZE = H26X_BITSTREAM_SIZE + H26X_ENC_BUFF_SIZE + ISP_MEM_BASE_SIZE
# ION after FreeRTOS
ION_ADDR = FREERTOS_ADDR - ION_SIZE
# Buffers of the fast image are inside the ION buffer
H26X_BITSTREAM_ADDR = ION_ADDR
H26X_ENC_BUFF_ADDR = H26X_BITSTREAM_ADDR + H26X_BITSTREAM_SIZE
ISP_MEM_BASE_ADDR = H26X_ENC_BUFF_ADDR + H26X_ENC_BUFF_SIZE
assert ISP_MEM_BASE_ADDR + ISP_MEM_BASE_SIZE <= ION_ADDR + ION_SIZE
# Boot logo is after the ION buffer
# Framebuffer uses boot logo's reserved memory
BOOTLOGO_SIZE = 8000 * SIZE_1K
BOOTLOGO_ADDR = ION_ADDR - BOOTLOGO_SIZE
FRAMEBUFFER_SIZE = BOOTLOGO_SIZE
FRAMEBUFFER_ADDR = BOOTLOGO_ADDR
# ===================
# FSBL and u-boot-2021
# ===================
CVI_UPDATE_HEADER_SIZE = SIZE_1K
UIMAG_SIZE = 16 * SIZE_1M
# kernel image loading buffer
UIMAG_ADDR = DRAM_BASE + 24 * SIZE_1M
CVI_UPDATE_HEADER_ADDR = UIMAG_ADDR - CVI_UPDATE_HEADER_SIZE
# FSBL decompress buffer
FSBL_UNZIP_ADDR = UIMAG_ADDR
FSBL_UNZIP_SIZE = UIMAG_SIZE
assert UIMAG_ADDR + UIMAG_SIZE <= BOOTLOGO_ADDR
# u-boot's run address and entry point
CONFIG_SYS_TEXT_BASE = DRAM_BASE + 2 * SIZE_1M
# u-boot's init stack point is only used before board_init_f()
CONFIG_SYS_INIT_SP_ADDR = UIMAG_ADDR + UIMAG_SIZE

View File

@@ -0,0 +1,4 @@
<physical_partition type="sd">
<partition label="BOOT" size_in_kb="80960" readonly="false" file="boot.sd"/>
<partition label="ROOTFS" size_in_kb="1581056" readonly="false" file="rootfs.sd" />
</physical_partition>

View File

@@ -0,0 +1,43 @@
#!/bin/sh
set -eu
target_dir=$1
# Production NanoKVM uses its own server and multimedia libraries. SDK sample
# programs and development metadata are not needed at runtime.
find "$target_dir/mnt/system/usr/bin" -type f \
\( -name 'sample_*' -o -name '*_test' -o -name 'test_*' -o -name 'ive_stress' -o -name 'sensor_test' \) \
-delete 2>/dev/null || true
touch "$target_dir/etc/nanokvm-minimal"
# The minimal board uses AIC8800; do not ship or load the unused 8733bs driver.
rm -f "$target_dir/mnt/system/ko/3rd/8733bs.ko"
if [ -f "$target_dir/etc/init.d/S25wifimod" ]; then
sed -i '/insmod 3rd\/8733bs\.ko/d' "$target_dir/etc/init.d/S25wifimod"
fi
# Do not retain stale hardware database files in an incremental build.
rm -rf "$target_dir/etc/udev/hwdb.bin" "$target_dir/etc/udev/hwdb.d"
# Keep only the sensor selected by the minimal board configuration.
rm -f \
"$target_dir/mnt/system/usr/lib/libsns_gc4653.so" \
"$target_dir/mnt/system/usr/lib/libov_ov2685.so" \
"$target_dir/mnt/system/usr/lib/libsns_os04a10.so" \
"$target_dir/mnt/system/usr/lib/libsns_sc035gs.so"
# LT6911 has no matching SDK ISP tuning blob; remove stale blobs from reuse builds.
find "$target_dir/mnt/cfg/param" "$target_dir/mnt/cfg/tmp_secure" \
-type f -delete 2>/dev/null || true
find "$target_dir" -type f \( -name '*.a' -o -name '*.la' \) -delete
rm -rf \
"$target_dir/usr/include" \
"$target_dir/usr/share/doc" \
"$target_dir/usr/share/info" \
"$target_dir/usr/share/man" \
"$target_dir/usr/share/pkgconfig" \
"$target_dir/usr/lib/pkgconfig" \
"$target_dir/usr/lib/cmake"

View File

@@ -0,0 +1,23 @@
CONFIG_CHIP_sg2002=y
CONFIG_BOARD_licheervnano_sd_minimal=y
CONFIG_DDR_CFG_ddr3_1866_x16=y
CONFIG_ARCH="riscv"
CONFIG_CROSS_COMPILE="riscv64-unknown-linux-musl-"
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_KERNEL_ENTRY_HACK=y
CONFIG_KERNEL_ENTRY_HACK_ADDR="0x80200000"
CONFIG_TOOLCHAIN_MUSL_RISCV64=y
CONFIG_BOOT_IMAGE_SINGLE_DTB=y
# CONFIG_FLASH_SIZE_SHRINK is not set
CONFIG_CP_EXT_WIRELESS=y
CONFIG_STORAGE_TYPE_sd=y
CONFIG_MIPI_PANEL_ZCT2133V1=y
CONFIG_SENSOR_LONTIUM_LT6911=y
CONFIG_UBOOT_2021_10=y
CONFIG_KERNEL_SRC_5.10=y
CONFIG_KERNEL_UNCOMPRESSED=y
# CONFIG_SKIP_RAMDISK is not set
# CONFIG_SENSOR_TUNING_PARAM_sg200x_src_ov_os04a10=y
# CONFIG_ROOTFS_OVERLAYFS is not set
# CONFIG_ENABLE_FREERTOS=y
# CONFIG_TARGET_PACKAGE_GATORD is not set

View File

@@ -0,0 +1,164 @@
__attribute__((optimize("O0")))
// 1.26ms
void suck_loop(uint64_t loop) {
loop = loop * 50 * 100;
uint64_t a;
while (loop > 0) {
a = loop / (uint64_t)99;
a = loop / (uint64_t)77;
a = loop / (uint64_t)55;
a = loop / (uint64_t)33;
a = loop / (uint64_t)11;
a = loop / (uint64_t)999;
a = loop / (uint64_t)777;
a = loop / (uint64_t)555;
a = loop / (uint64_t)333;
a = loop / (uint64_t)111;
a = loop / (uint64_t)9999;
a = loop / (uint64_t)7777;
a = loop / (uint64_t)5555;
a = loop / (uint64_t)3333;
a = loop / (uint64_t)1111;
a = loop / (uint64_t)99999;
a = loop / (uint64_t)77777;
a = loop / (uint64_t)55555;
a = loop / (uint64_t)33333;
a = loop / (uint64_t)11111;
loop--;
}
}
static inline void user_led_on(void) {
uint32_t val;
val = mmio_read_32(0x03020000);
val |= (1 << 14);
mmio_write_32(0x03020000, val);
}
static inline void user_led_off(void) {
uint32_t val;
val = mmio_read_32(0x03020000);
val &= ~(1 << 14);
mmio_write_32(0x03020000, val);
}
static inline void user_led_toggle(void) {
uint32_t val;
val = mmio_read_32(0x03020000);
val ^= (1 << 14);
mmio_write_32(0x03020000, val);
}
int cvi_board_init(void)
{
uint32_t val;
// user led
mmio_write_32(0x03001038, 0x3); // GPIOA 14 GPIO_MODE
val = mmio_read_32(0x03020004); // GPIOA DIR
val |= (1 << 14); // output
mmio_write_32(0x03020004, val);
user_led_toggle();
// wifi power reset
mmio_write_32(0x0300104C, 0x3); // GPIOA 26
val = mmio_read_32(0x03020004); // GPIOA DIR
val |= (1 << 26); // output
mmio_write_32(0x03020004, val);
val = mmio_read_32(0x03020000); // signal level
val &= ~(1 << 26); // set level to low
mmio_write_32(0x03020000, val);
suck_loop(50);
user_led_toggle();
val = mmio_read_32(0x03020000); // signal level
val |= (1 << 26); // set level to high
mmio_write_32(0x03020000, val);
// wifi sdio pinmux
mmio_write_32(0x030010D0, 0x0); // D3
mmio_write_32(0x030010D4, 0x0); // D2
mmio_write_32(0x030010D8, 0x0); // D1
mmio_write_32(0x030010DC, 0x0); // D0
mmio_write_32(0x030010E0, 0x0); // CMD
mmio_write_32(0x030010E4, 0x0); // CLK
// spi2 pinmux
// mmio_write_32(0x030010D0, 0x1); // CS
// mmio_write_32(0x030010DC, 0x1); // MISO
// mmio_write_32(0x030010E0, 0x1); // MOSI
// mmio_write_32(0x030010E4, 0x1); // SCK
// mmio_write_32(0x030010D8, 0x3); // DC
// mmio_write_32(0x03001038, 0x3); // RESET
// uart bluetooth
mmio_write_32(0x03001070, 0x1); // GPIOA 28 UART1 TX
mmio_write_32(0x03001074, 0x1); // GPIOA 29 UART1 RX
mmio_write_32(0x03001068, 0x4); // GPIOA 18 UART1 CTS
mmio_write_32(0x03001064, 0x4); // GPIOA 19 UART1 RTS
// PWM
//mmio_write_32(0x03001068, 0x2); // GPIOA 18 PWM 6
// lcd reset
mmio_write_32(0x030010A4, 0x0); // PWRGPIO 0 GPIO_MODE
user_led_toggle();
// lcd backlight
//mmio_write_32(0x030010EC, 0x0); // GPIOB 0 PWM0_BUCK
// for licheervnano alpha
val = mmio_read_32(0x03021000); // signal level
val |= (1 << 0); // set level to high
mmio_write_32(0x03021000, val);
val = mmio_read_32(0x03021004); // GPIOB DIR
val |= (1 << 0); // output
mmio_write_32(0x03021004, val);
mmio_write_32(0x030010EC, 0x3); // GPIOB 0 GPIO_MODE
// for licheervnano beta
//mmio_write_32(0x030010ac, 0x4); // PWRGPIO 2 PWM 10
mmio_write_32(0x030010ac, 0x0); // PWRGPIO 2 GPIO_MODE
// camera function
//mmio_write_32(0x0300116C, 0x5); // RX4N CAM_MCLK0 for alpha
mmio_write_32(0x0300118C, 0x5); // RX0N CAM_MCLK1 for beta
// spi1 on mipi csi
/*
mmio_write_32(0x0300116C, 0x7); // spi1 clk GPIOC2 MIPI_RX4N
mmio_write_32(0x03001170, 0x7); // spi1 cs GPIOC3 MIPI_RX4P
mmio_write_32(0x03001174, 0x7); // spi1 miso GPIOC4 MIPI_RX3N
mmio_write_32(0x03001178, 0x7); // spi1 mosi GPIOC5 MIPI_RX3P
*/
// camera/tp i2c
mmio_write_32(0x03001090, 0x5); // PWR_GPIO6 IIC4_SCL
mmio_write_32(0x03001098, 0x5); // PWR_GPIO8 IIC4_SDA
// tp function
mmio_write_32(0x03001084, 0x3); // PWR_SEQ1 PWR_GPIO[3]
mmio_write_32(0x03001088, 0x3); // PWR_SEQ2 PWR_GPIO[4]
mmio_write_32(0x05027078, 0x11);// Unlock PWR_GPIO[3]
mmio_write_32(0x0502707c, 0x11);// Unlock PWR_GPIO[4]
// bitbang i2c
mmio_write_32(0x0300103C, 0x03); // GPIOA 15 GPIO_MODE
mmio_write_32(0x03001058, 0x03); // GPIOA 27 GPIO_MODE
// bitbang spi
mmio_write_32(0x03001060, 0x03); // GPIOA 24 GPIO_MODE
mmio_write_32(0x0300105C, 0x03); // GPIOA 23 GPIO_MODE
mmio_write_32(0x03001054, 0x03); // GPIOA 25 GPIO_MODE
mmio_write_32(0x03001050, 0x03); // GPIOA 22 GPIO_MODE
// wait hardware bootup
suck_loop(50);
user_led_off();
return 0;
}

View File

@@ -0,0 +1,66 @@
CONFIG_RISCV=y
CONFIG_SYS_MALLOC_F_LEN=0x2000
CONFIG_NR_DRAM_BANKS=1
CONFIG_DEFAULT_DEVICE_TREE="soph_asic"
CONFIG_IDENT_STRING=" soph"
CONFIG_ARCH_RV64I=y
CONFIG_RISCV_SMODE=y
CONFIG_TARGET_CVITEK_CV181X=y
CONFIG_DISTRO_DEFAULTS=y
CONFIG_FIT=y
# CONFIG_ARCH_FIXUP_FDT_MEMORY is not set
CONFIG_BOOTDELAY=0
CONFIG_SYS_PROMPT="soph# "
# CONFIG_CMD_CONSOLE is not set
# CONFIG_CMD_XIMG is not set
# CONFIG_CMD_EDITENV is not set
# CONFIG_CMD_CRC32 is not set
# CONFIG_CMD_LZMADEC is not set
# CONFIG_CMD_UNLZ4 is not set
# CONFIG_CMD_UNZIP is not set
# CONFIG_CMD_LOADS is not set
CONFIG_CMD_MMC=y
# CONFIG_CMD_ITEST is not set
CONFIG_CMD_CACHE=y
CONFIG_CMD_CVI_SD_UPDATE=y
# CONFIG_ISO_PARTITION is not set
# CONFIG_EFI_PARTITION is not set
CONFIG_NET_RANDOM_ETHADDR=y
CONFIG_MMC=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_SDMA=y
CONFIG_MMC_SDHCI_CVITEK=y
CONFIG_MTD=y
# CONFIG_PHY_SMSC is not set
CONFIG_DM_ETH=y
CONFIG_ETH_DESIGNWARE=y
CONFIG_PHY_CVITEK=y
CONFIG_SYS_NS16550=y
CONFIG_SPI=y
CONFIG_LZ4=y
CONFIG_LZMA=y
# CONFIG_TOOLS_LIBCRYPTO is not set
CONFIG_ENV_IS_NOWHERE=y
# for licheervnano
CONFIG_UDP_FUNCTION_FASTBOOT=y
CONFIG_FASTBOOT_FLASH=y
CONFIG_FASTBOOT_BUF_ADDR=0x81800000
CONFIG_FASTBOOT_BUF_SIZE=0x1000000
CONFIG_FASTBOOT_FLASH_MMC_DEV=0
CONFIG_DISPLAY=y
CONFIG_DM_VIDEO=y
CONFIG_DM_GPIO=y
CONFIG_DWAPB_GPIO=y
CONFIG_CMD_VIDCONSOLE=y
CONFIG_VIDEO_BPP16=y
CONFIG_VIDEO_BPP32=y
CONFIG_VIDEO_CVITEK=y
CONFIG_DISPLAY_CVITEK_MIPI=y
CONFIG_BMP_16BPP=y
CONFIG_BMP_24BPP=y
CONFIG_BMP_32BPP=y
CONFIG_BOOTLOGO=y
CONFIG_CMD_CVI_VO=y
CONFIG_CMD_GPIO=y
CONFIG_CMD_MBR=y

View File

@@ -525,6 +525,7 @@ function clean_ramdisk()
rm -rf "${RAMDISK_PATH:?}"/"$RAMDISK_OUTPUT_BASE"
rm -rf "$SYSTEM_OUT_DIR"
rm -rf "$ROOTFS_DIR"
rm -rf "${BR_OUTPUT_DIR:?}"
}
function build_access_guard_turnkey_app()
@@ -876,9 +877,16 @@ function cvi_setup_env()
# buildroot config
export BR_DIR="$TOP_DIR"/buildroot
export BR_BOARD=cvitek_${CHIP_ARCH}_${SDK_VER}
export BR_OVERLAY_DIR=${BR_DIR}/board/cvitek/${CHIP_ARCH}/overlay
export BR_DEFCONFIG=${BR_BOARD}_defconfig
echo "BR2_DEFCONFIG: ${BRDEFCONFIG}"
export BR_OUTPUT_DIR=${BR_DIR}/output/${PROJECT_FULLNAME}
export BR_BASE_OVERLAY_DIR=${BR_DIR}/board/cvitek/${CHIP_ARCH}/overlay
export BR_OVERLAY_DIR=${BR_OUTPUT_DIR}/overlay
export BR_DEFCONFIG=defconfig
export BR2_DEFCONFIG=${BR_DIR}/configs/${BR_BOARD}_defconfig
board_br_defconfig="$BUILD_PATH/boards/${CHIP_ARCH,,}/$PROJECT_FULLNAME/buildroot.config"
if [ -f "$board_br_defconfig" ]; then
export BR2_DEFCONFIG=$board_br_defconfig
fi
echo "BR2_DEFCONFIG: ${BR2_DEFCONFIG}"
}
cvi_print_env()

View File

@@ -199,6 +199,33 @@ fi
[ "$OUTPUT_IMAGE_FILE" != "$SOURCE_IMAGE_FILE" ] || die "output image must be different from input image"
MOUNTED=0
OUTPUT_CREATED=0
STRIP_TEMP_FILE=""
cleanup() {
status=$?
cleanup_ok=1
if [ "$MOUNTED" -eq 1 ]; then
info "cleanup: unmount $MOUNT_DIR"
if umount "$MOUNT_DIR"; then
MOUNTED=0
else
cleanup_ok=0
warn "failed to unmount $MOUNT_DIR; please run: umount \"$MOUNT_DIR\""
fi
fi
if [ "$status" -ne 0 ] && [ "$OUTPUT_CREATED" -eq 1 ] && [ "$cleanup_ok" -eq 1 ]; then
info "cleanup: remove incomplete output image $OUTPUT_IMAGE_FILE"
rm -f "$OUTPUT_IMAGE_FILE" || warn "failed to remove incomplete output image: $OUTPUT_IMAGE_FILE"
fi
if [ -n "$STRIP_TEMP_FILE" ]; then
rm -f "$STRIP_TEMP_FILE"
fi
exit "$status"
}
trap cleanup EXIT
trap 'exit 1' HUP INT TERM
if [ -e "$OUTPUT_IMAGE_FILE" ]; then
if [ "$FORCE_OUTPUT" -eq 1 ]; then
run_step "remove old output image" rm -f "$OUTPUT_IMAGE_FILE"
@@ -207,6 +234,7 @@ if [ -e "$OUTPUT_IMAGE_FILE" ]; then
fi
fi
OUTPUT_CREATED=1
run_step "copy input image to output image" copy_image "$SOURCE_IMAGE_FILE" "$OUTPUT_IMAGE_FILE"
IMAGE_FILE=$OUTPUT_IMAGE_FILE
@@ -238,42 +266,23 @@ if [ "$COPY_KVMAPP" -eq 1 ]; then
run_step "remove old SDK kvmapp" rm -rf "$SCRIPT_DIR/kvmapp"
run_step "copy NanoKVM app from $APP_DIR/kvmapp" cp -a "$APP_DIR/kvmapp" "$SCRIPT_DIR/"
run_step "create kvm_new_img marker" touch "$SCRIPT_DIR/kvmapp/kvm_new_img"
run_step "make SDK kvmapp files executable" chmod -R +x "$SCRIPT_DIR/kvmapp"
else
info "use existing SDK kvmapp: $SCRIPT_DIR/kvmapp"
fi
need_dir "$SCRIPT_DIR/kvmapp"
run_step "make SDK kvmapp files executable" chmod -R +x "$SCRIPT_DIR/kvmapp"
need_exec "$SCRIPT_DIR/kvmapp/server/NanoKVM-Server"
need_exec "$SCRIPT_DIR/kvmapp/kvm_system/kvm_system"
need_file "$SCRIPT_DIR/kvmapp/system/ko/soph_mipi_rx.ko"
need_file "$SCRIPT_DIR/kvmapp/system/init.d/S95nanokvm"
need_file "$SCRIPT_DIR/kvmapp/version"
for script in S00kmod S01fs S03usbdev S15kvmhwd S30eth S30wifi S95nanokvm; do
need_file "$SCRIPT_DIR/kvmapp/system/init.d/$script"
done
need_dir "$SCRIPT_DIR/data"
need_file "$SCRIPT_DIR/data/sensor_cfg.ini"
FRP_DIR=$(find "$SCRIPT_DIR" -maxdepth 1 -type d -name 'frp_*_linux_riscv64' | sort -V | tail -n 1)
TAILSCALE_DIR=$(find "$SCRIPT_DIR" -maxdepth 1 -type d -name 'tailscale_*_riscv64' | sort -V | tail -n 1)
[ -n "$FRP_DIR" ] || die "missing frp_*_linux_riscv64 directory under $SCRIPT_DIR"
[ -n "$TAILSCALE_DIR" ] || die "missing tailscale_*_riscv64 directory under $SCRIPT_DIR"
need_exec "$FRP_DIR/frpc"
need_exec "$TAILSCALE_DIR/tailscale"
need_exec "$TAILSCALE_DIR/tailscaled"
MOUNTED=0
cleanup() {
status=$?
if [ "$MOUNTED" -eq 1 ]; then
info "cleanup: unmount $MOUNT_DIR"
if ! umount "$MOUNT_DIR"; then
warn "failed to unmount $MOUNT_DIR; please run: umount \"$MOUNT_DIR\""
fi
fi
exit "$status"
}
trap cleanup EXIT HUP INT TERM
info "mount image: $IMAGE_FILE"
if "$SDK_DIR/host/mount_ext4.sh" "$IMAGE_FILE" "$MOUNT_DIR"; then
:
@@ -292,31 +301,81 @@ need_dir "$MOUNT_DIR/usr"
need_dir "$MOUNT_DIR/mnt"
info "mount check passed"
MINIMAL_IMAGE=0
if [ -e "$MOUNT_DIR/etc/nanokvm-minimal" ]; then
MINIMAL_IMAGE=1
else
FRP_DIR=$(find "$SCRIPT_DIR" -maxdepth 1 -type d -name 'frp_*_linux_riscv64' | sort -V | tail -n 1)
TAILSCALE_DIR=$(find "$SCRIPT_DIR" -maxdepth 1 -type d -name 'tailscale_*_riscv64' | sort -V | tail -n 1)
[ -n "$FRP_DIR" ] || die "missing frp_*_linux_riscv64 directory under $SCRIPT_DIR"
[ -n "$TAILSCALE_DIR" ] || die "missing tailscale_*_riscv64 directory under $SCRIPT_DIR"
need_exec "$FRP_DIR/frpc"
need_exec "$TAILSCALE_DIR/tailscale"
need_exec "$TAILSCALE_DIR/tailscaled"
fi
SSH_AVAILABLE=0
if [ -x "$MOUNT_DIR/usr/sbin/sshd" ] && [ -x "$MOUNT_DIR/usr/bin/ssh-keygen" ]; then
SSH_AVAILABLE=1
need_file "$SCRIPT_DIR/kvmapp/system/init.d/S50sshd"
fi
run_step "remove old image kvmapp" rm -rf "$MOUNT_DIR/kvmapp"
run_step "copy kvmapp into image" cp -a "$SCRIPT_DIR/kvmapp" "$MOUNT_DIR/"
strip_tool="${CROSS_COMPILE_STRIP:-}"
if [ -z "$strip_tool" ]; then
strip_tool=$(command -v riscv64-unknown-linux-musl-strip 2>/dev/null || true)
fi
if [ -z "$strip_tool" ]; then
strip_tool="$SDK_DIR/host-tools/gcc/riscv64-linux-musl-x86_64/bin/riscv64-unknown-linux-musl-strip"
fi
case "$strip_tool" in
*/*) need_exec "$strip_tool" ;;
*) command -v "$strip_tool" >/dev/null 2>&1 || die "missing strip tool: $strip_tool" ;;
esac
STRIP_TEMP_FILE=$(mktemp "${TMPDIR:-/tmp}/nanokvm-server.XXXXXX") || die "failed to create strip temporary file"
run_step "strip NanoKVM-Server debug information" \
"$strip_tool" --strip-debug -o "$STRIP_TEMP_FILE" "$SCRIPT_DIR/kvmapp/server/NanoKVM-Server"
run_step "install stripped NanoKVM-Server" \
install -m 755 "$STRIP_TEMP_FILE" "$MOUNT_DIR/kvmapp/server/NanoKVM-Server"
need_exec "$MOUNT_DIR/kvmapp/server/NanoKVM-Server"
need_file "$MOUNT_DIR/kvmapp/system/init.d/S95nanokvm"
need_file "$MOUNT_DIR/kvmapp/version"
info "kvmapp install check passed"
run_step "create frp and tailscale target directories" install -d "$MOUNT_DIR/usr/bin" "$MOUNT_DIR/usr/sbin"
run_step "install frpc" install -m 755 "$FRP_DIR/frpc" "$MOUNT_DIR/usr/bin/frpc"
run_step "install tailscale" install -m 755 "$TAILSCALE_DIR/tailscale" "$MOUNT_DIR/usr/bin/tailscale"
run_step "install tailscaled" install -m 755 "$TAILSCALE_DIR/tailscaled" "$MOUNT_DIR/usr/sbin/tailscaled"
need_exec "$MOUNT_DIR/usr/bin/frpc"
need_exec "$MOUNT_DIR/usr/bin/tailscale"
need_exec "$MOUNT_DIR/usr/sbin/tailscaled"
info "frp and tailscale install check passed"
if [ "$MINIMAL_IMAGE" -eq 1 ]; then
info "minimal image: omit bundled frp and tailscale"
run_step "remove bundled frp and tailscale" rm -f \
"$MOUNT_DIR/usr/bin/frpc" \
"$MOUNT_DIR/usr/bin/tailscale" \
"$MOUNT_DIR/usr/sbin/tailscaled" \
"$MOUNT_DIR/etc/init.d/S98tailscaled"
else
run_step "create frp and tailscale target directories" install -d "$MOUNT_DIR/usr/bin" "$MOUNT_DIR/usr/sbin"
run_step "install frpc" install -m 755 "$FRP_DIR/frpc" "$MOUNT_DIR/usr/bin/frpc"
run_step "install tailscale" install -m 755 "$TAILSCALE_DIR/tailscale" "$MOUNT_DIR/usr/bin/tailscale"
run_step "install tailscaled" install -m 755 "$TAILSCALE_DIR/tailscaled" "$MOUNT_DIR/usr/sbin/tailscaled"
need_exec "$MOUNT_DIR/usr/bin/frpc"
need_exec "$MOUNT_DIR/usr/bin/tailscale"
need_exec "$MOUNT_DIR/usr/sbin/tailscaled"
info "frp and tailscale install check passed"
fi
run_step "create kernel module and init target directories" install -d "$MOUNT_DIR/mnt/system/ko" "$MOUNT_DIR/etc/init.d"
run_step "install soph_mipi_rx.ko" install -m 755 "$SCRIPT_DIR/kvmapp/system/ko/soph_mipi_rx.ko" "$MOUNT_DIR/mnt/system/ko/soph_mipi_rx.ko"
need_file "$MOUNT_DIR/mnt/system/ko/soph_mipi_rx.ko"
for script in S00kmod S01fs S03usbdev S15kvmhwd S30eth S30wifi S95nanokvm; do
need_file "$SCRIPT_DIR/kvmapp/system/init.d/$script"
run_step "install init script: $script" install -m 755 "$SCRIPT_DIR/kvmapp/system/init.d/$script" "$MOUNT_DIR/etc/init.d/$script"
need_exec "$MOUNT_DIR/etc/init.d/$script"
done
if [ "$SSH_AVAILABLE" -eq 1 ]; then
run_step "install init script: S50sshd" install -m 755 "$SCRIPT_DIR/kvmapp/system/init.d/S50sshd" "$MOUNT_DIR/etc/init.d/S50sshd"
need_exec "$MOUNT_DIR/etc/init.d/S50sshd"
else
warn "input image has no OpenSSH server; SSH remains unavailable"
run_step "remove unavailable SSH init script" rm -f "$MOUNT_DIR/etc/init.d/S50sshd"
fi
info "kernel module and init script install check passed"
run_step "create default data and kvm config directories" install -d "$MOUNT_DIR/mnt/data" "$MOUNT_DIR/etc/kvm" "$MOUNT_DIR/kvmapp/kvm"