From fc0da53ddd72aac3bd0258c14b077c757ff92f02 Mon Sep 17 00:00:00 2001 From: lindongping Date: Wed, 21 Aug 2024 17:36:48 +0800 Subject: [PATCH 1/9] add phytium-firstlogin package Signed-off-by: lindongping --- .../common/debian-additional_packages_list | 7 +- board/phytium/common/debian-package-installer | 24 +- board/phytium/common/desktop_device_table.txt | 1 - board/phytium/common/device_table.txt | 1 - board/phytium/common/post-build.sh | 8 - configs/phytiumpi_defconfig | 3 + configs/phytiumpi_desktop_defconfig | 3 + package/Config.in | 1 + .../phytium-desktop-tools.mk | 3 +- .../src/switch_to_headphone.sh | 9 +- package/phytium-firstlogin/Config.in | 2 + .../phytium-firstlogin/phytium-firstlogin.mk | 25 + package/phytium-firstlogin/src/override.conf | 4 + .../src/phytium-check-first-login.sh | 12 + .../src/phytium-disable-autologin.service | 9 + .../src/phytium-disable-autologin.timer | 8 + .../phytium-firstlogin/src/phytium-firstlogin | 766 ++++++++++++++++++ 17 files changed, 851 insertions(+), 35 deletions(-) create mode 100644 package/phytium-firstlogin/Config.in create mode 100644 package/phytium-firstlogin/phytium-firstlogin.mk create mode 100644 package/phytium-firstlogin/src/override.conf create mode 100644 package/phytium-firstlogin/src/phytium-check-first-login.sh create mode 100644 package/phytium-firstlogin/src/phytium-disable-autologin.service create mode 100644 package/phytium-firstlogin/src/phytium-disable-autologin.timer create mode 100755 package/phytium-firstlogin/src/phytium-firstlogin diff --git a/board/phytium/common/debian-additional_packages_list b/board/phytium/common/debian-additional_packages_list index 7df03cb6..c84c862a 100644 --- a/board/phytium/common/debian-additional_packages_list +++ b/board/phytium/common/debian-additional_packages_list @@ -5,5 +5,10 @@ additional_full_packages_list=" python3-pygame python3-tk thonny python3-pgzero additional_desktop_packages_list="tigervnc-standalone-server vlc rfkill blueman bluetooth gstreamer1.0-tools fbset audacity" # for Lite userland gathered from main repo -additional_base_packages_list="systemd-timesyncd ssh sudo psmisc strace ncdu parted build-essential bash-completion gdb pkg-config python-is-python3 v4l-utils python3-gpiozero avahi-daemon lua5.1 luajit hardlink curl fake-hwclock nfs-common usbutils dosfstools dphys-swapfile apt-listchanges usb-modeswitch libmtp-runtime rsync htop man-db rng-tools ssh-import-id ethtool ntfs-3g pciutils udisks2 zip p7zip-full file cifs-utils mkvtoolnix wpasupplicant wireless-tools net-tools vim locales" +additional_base_packages_list="systemd-timesyncd ssh sudo psmisc strace ncdu parted build-essential bash-completion gdb pkg-config python-is-python3 \ +v4l-utils python3-gpiozero avahi-daemon lua5.1 luajit hardlink curl fake-hwclock nfs-common usbutils dosfstools \ +dphys-swapfile apt-listchanges usb-modeswitch libmtp-runtime rsync htop man-db rng-tools \ +ssh-import-id ethtool ntfs-3g pciutils udisks2 zip p7zip-full file \ +cifs-utils mkvtoolnix wpasupplicant wireless-tools net-tools \ +vim locales network-manager jq" diff --git a/board/phytium/common/debian-package-installer b/board/phytium/common/debian-package-installer index b7d5b5bc..b8baeb05 100755 --- a/board/phytium/common/debian-package-installer +++ b/board/phytium/common/debian-package-installer @@ -15,21 +15,11 @@ do_distrorfs_second_stage() { echo "4." $4 echo "5." $5 echo "6." $6 - if [ ! -d /home/user ]; then - useradd -m -d /home/user -s /bin/bash user - gpasswd -a user sudo - echo -e 'root\nroot\n' | passwd root - echo -e 'user\nuser\n' | passwd user - usermod -aG sudo user - chown -R user:user /home/user - fi + if [ -d /etc/shadow ]; then cd /etc chmod u=rw,g=r,o=r shadow fi - if [ -e /home/user/.bashrc ]; then - echo "PATH=$PATH:/usr/sbin " >> /home/user/.bashrc - fi # set default hostname echo localhost > /etc/hostname @@ -68,10 +58,9 @@ do_distrorfs_second_stage() { rm -rf /usr/share/arctica-greeter/backgrounds/arctica-greeter.png || true rm -rf /usr/share/arctica-greeter/logo.png || true done - usermod -a -G video,render,audio user - ln -sf /lib/systemd/system/lightdm.service /etc/systemd/system/display-manager.service - sed -i "s/gdm3/lightdm/" /etc/X11/default-display-manager + rm -f /etc/systemd/system/display-manager.service + sed -i "s/gdm3/lightdm/" /etc/X11/default-display-manager echo '[SeatDefaults]' >> /etc/lightdm/lightdm.conf echo 'greeter-session=arctica-greeter' >> /etc/lightdm/lightdm.conf echo 'user-session=xfce' >> /etc/lightdm/lightdm.conf @@ -90,11 +79,10 @@ do_distrorfs_second_stage() { echo 'update-sink-proplist 0 device.description=HDMI' >> /etc/pulse/default.pa echo 'set-default-sink 1' >> /etc/pulse/default.pa echo 'load-module module-switch-on-connect' >> /etc/pulse/default.pa - - # set the configuration of vim - echo 'set mouse-=a' >> /home/user/.vimrc - echo 'syntax on' >> /home/user/.vimrc + # touch .desktop_firstrun + touch /root/.desktop_firstlogin + echo remove packages. DEBIAN_FRONTEND=noninteractive apt-get -y remove --purge parole || exit 1 fi diff --git a/board/phytium/common/desktop_device_table.txt b/board/phytium/common/desktop_device_table.txt index ee776525..90982841 100644 --- a/board/phytium/common/desktop_device_table.txt +++ b/board/phytium/common/desktop_device_table.txt @@ -1,4 +1,3 @@ # /var/cache/man r 755 man man - - - - - /var/lib/lightdm r 755 lightdm lightdm - - - - - -/home/user r 755 user user - - - - - diff --git a/board/phytium/common/device_table.txt b/board/phytium/common/device_table.txt index ad7693ed..fc0ecb78 100644 --- a/board/phytium/common/device_table.txt +++ b/board/phytium/common/device_table.txt @@ -1,3 +1,2 @@ # /var/cache/man r 755 man man - - - - - -/home/user r 755 user user - - - - - diff --git a/board/phytium/common/post-build.sh b/board/phytium/common/post-build.sh index 8df77d80..b4e0d9a7 100755 --- a/board/phytium/common/post-build.sh +++ b/board/phytium/common/post-build.sh @@ -10,14 +10,6 @@ main() ln -sf /lib/systemd/system/phytium-tee-supplicant.service $1/etc/systemd/system/sysinit.target.wants/phytium-tee-supplicant.service fi - # change the priority order of the shared library directories - if ! grep -q "/usr/local/lib" $1/etc/ld.so.conf; then - sed -i '1 i /usr/local/lib' $1/etc/ld.so.conf - sudo chroot $1 ldconfig - sudo chown -R $USER:$GROUPS $1/etc/ld.so.cache - sudo chown -R $USER:$GROUPS $1/var/cache/ldconfig/aux-cache - fi - # alsa UCM if grep -Eq "^BR2_PACKAGE_PHYTIUM_DESKTOP_TOOLS=y$" ${BR2_CONFIG}; then ln -sf ../../Phytium/es8336/es8336.conf $1/usr/share/alsa/ucm2/conf.d/simple-card/phytium,pe220x-i2s-audio.conf diff --git a/configs/phytiumpi_defconfig b/configs/phytiumpi_defconfig index 544bbeb8..cdec088f 100644 --- a/configs/phytiumpi_defconfig +++ b/configs/phytiumpi_defconfig @@ -66,6 +66,9 @@ BR2_PACKAGE_VPU_LIB_CPU_MODEL="e2000" # Phytium_tools BR2_PACKAGE_PHYTIUM_TOOLS=y +# Phytium_firstrun +BR2_PACKAGE_PHYTIUM_FIRSTLOGIN=y + # util-linux BR2_PACKAGE_UTIL_LINUX=y BR2_PACKAGE_UTIL_LINUX_BINARIES=y diff --git a/configs/phytiumpi_desktop_defconfig b/configs/phytiumpi_desktop_defconfig index 3ee080ed..6554b98c 100644 --- a/configs/phytiumpi_desktop_defconfig +++ b/configs/phytiumpi_desktop_defconfig @@ -60,6 +60,9 @@ BR2_PACKAGE_HOST_GENIMAGE=y BR2_PACKAGE_PHYTIUM_TOOLS=y BR2_PACKAGE_PHYTIUM_DESKTOP_TOOLS=y +# Phytium_firstrun +BR2_PACKAGE_PHYTIUM_FIRSTLOGIN=y + BR2_PACKAGE_ROOTFS_DESKTOP=y BR2_ROOTFS_DEVICE_TABLE="system/device_table.txt board/phytium/common/desktop_device_table.txt" BR2_PACKAGE_BUSYBOX=n diff --git a/package/Config.in b/package/Config.in index 3ed1096b..16c10c40 100644 --- a/package/Config.in +++ b/package/Config.in @@ -258,6 +258,7 @@ menu "Filesystem and flash utilities" source "package/phytium-tools/Config.in" source "package/phytium-desktop-tools/Config.in" source "package/kernel-headers/Config.in" + source "package/phytium-firstlogin/Config.in" endmenu menu "Fonts, cursors, icons, sounds and themes" diff --git a/package/phytium-desktop-tools/phytium-desktop-tools.mk b/package/phytium-desktop-tools/phytium-desktop-tools.mk index b1293231..751f77b6 100644 --- a/package/phytium-desktop-tools/phytium-desktop-tools.mk +++ b/package/phytium-desktop-tools/phytium-desktop-tools.mk @@ -18,14 +18,13 @@ define PHYTIUM_DESKTOP_TOOLS_INSTALL_TARGET_CMDS mkdir -p $(TARGET_DIR)/usr/share/alsa/ucm2/ mkdir -p $(TARGET_DIR)/usr/share/alsa/ucm2/conf.d/simple-card/ mkdir -p $(TARGET_DIR)/usr/share/alsa/ucm2/conf.d/PMDK-I2S/ - mkdir -p $(TARGET_DIR)/home/user/.config/autostart/ $(INSTALL) -m 777 -D $(@D)/dark.jpg $(TARGET_DIR)/usr/share/images/desktop-base/ $(INSTALL) -m 755 -D $(@D)/01_debian.conf $(TARGET_DIR)/usr/share/lightdm/lightdm-gtk-greeter.conf.d/ $(INSTALL) -m 755 -D $(@D)/xfce4-desktop.xml $(TARGET_DIR)/usr/share/desktop-base/profiles/xdg-config/xfce4/xfconf/xfce-perchannel-xml/ $(INSTALL) -m 644 -D $(@D)/asound.state $(TARGET_DIR)/var/lib/alsa/ cp -rf $(@D)/Phytium/ $(TARGET_DIR)/usr/share/alsa/ucm2/ $(INSTALL) -m 755 -D $(@D)/switch_to_headphone.sh $(TARGET_DIR)/usr/bin/ - $(INSTALL) -m 644 -D $(@D)/sound_output_device_switching.desktop $(TARGET_DIR)/home/user/.config/autostart/ + $(INSTALL) -m 644 -D $(@D)/sound_output_device_switching.desktop $(TARGET_DIR)/usr/share/alsa/ucm2/ endef $(eval $(generic-package)) diff --git a/package/phytium-desktop-tools/src/switch_to_headphone.sh b/package/phytium-desktop-tools/src/switch_to_headphone.sh index 38791565..6b690270 100755 --- a/package/phytium-desktop-tools/src/switch_to_headphone.sh +++ b/package/phytium-desktop-tools/src/switch_to_headphone.sh @@ -8,11 +8,12 @@ while true; do FLAG="$(pactl list sinks | grep -oP '\[Out\] Headphones:.*?\bnot available\b' | grep -o 'not available')" - - if [ -z "$FLAG" ]; then - pacmd set-default-sink 1 + if pgrep -x pulseaudio > /dev/null + then + if [ -z "$FLAG" ]; then + pacmd set-default-sink 1 + fi fi - sleep 2 done diff --git a/package/phytium-firstlogin/Config.in b/package/phytium-firstlogin/Config.in new file mode 100644 index 00000000..62d0ee5b --- /dev/null +++ b/package/phytium-firstlogin/Config.in @@ -0,0 +1,2 @@ +config BR2_PACKAGE_PHYTIUM_FIRSTLOGIN + bool "phytium_firstlogin" diff --git a/package/phytium-firstlogin/phytium-firstlogin.mk b/package/phytium-firstlogin/phytium-firstlogin.mk new file mode 100644 index 00000000..89aef558 --- /dev/null +++ b/package/phytium-firstlogin/phytium-firstlogin.mk @@ -0,0 +1,25 @@ +################################################################################ +# +# phytium-firstlogin +# +################################################################################ + +PHYTIUM_FIRSTLOGIN_VERSION = 0.1 +PHYTIUM_FIRSTLOGIN_SITE = package/phytium-firstlogin/src +PHYTIUM_FIRSTLOGIN_SITE_METHOD = local +PHYTIUM_FIRSTLOGIN_INSTALL_TARGET_CMDS = YES + +define PHYTIUM_FIRSTLOGIN_INSTALL_TARGET_CMDS + mkdir -p $(TARGET_DIR)/etc/systemd/system/serial-getty@.service.d/ + mkdir -p $(TARGET_DIR)/etc/systemd/system/getty@.service.d/ + mkdir -p $(TARGET_DIR)/usr/lib/phytium/ + $(INSTALL) -m 664 -D $(@D)/override.conf $(TARGET_DIR)/etc/systemd/system/serial-getty@.service.d/ + $(INSTALL) -m 664 -D $(@D)/override.conf $(TARGET_DIR)/etc/systemd/system/getty@.service.d/ + $(INSTALL) -m 644 -D $(@D)/phytium-check-first-login.sh $(TARGET_DIR)/etc/profile.d/ + touch $(TARGET_DIR)/root/.not_logged_in_yet + $(INSTALL) -m 755 -D $(@D)/phytium-firstlogin $(TARGET_DIR)/usr/lib/phytium/ + $(INSTALL) -m 644 -D $(@D)/phytium-disable-autologin.timer $(TARGET_DIR)/lib/systemd/system/ + $(INSTALL) -m 644 -D $(@D)/phytium-disable-autologin.service $(TARGET_DIR)/lib/systemd/system/ +endef + +$(eval $(generic-package)) diff --git a/package/phytium-firstlogin/src/override.conf b/package/phytium-firstlogin/src/override.conf new file mode 100644 index 00000000..59d3fd3e --- /dev/null +++ b/package/phytium-firstlogin/src/override.conf @@ -0,0 +1,4 @@ +[Service] +ExecStart= +ExecStart=-/sbin/agetty --noissue --autologin root %I $TERM +Type=idle diff --git a/package/phytium-firstlogin/src/phytium-check-first-login.sh b/package/phytium-firstlogin/src/phytium-check-first-login.sh new file mode 100644 index 00000000..715fc40a --- /dev/null +++ b/package/phytium-firstlogin/src/phytium-check-first-login.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# +# Copyright (c) Authors: https://www.armbian.com/authors +# +# This file is licensed under the terms of the GNU General Public +# License version 2. This program is licensed "as is" without any +# warranty of any kind, whether express or implied. + +# First login as root? +if [ -w /root/ -a -f /root/.not_logged_in_yet ]; then + bash /usr/lib/phytium/phytium-firstlogin +fi diff --git a/package/phytium-firstlogin/src/phytium-disable-autologin.service b/package/phytium-firstlogin/src/phytium-disable-autologin.service new file mode 100644 index 00000000..df1e8198 --- /dev/null +++ b/package/phytium-firstlogin/src/phytium-disable-autologin.service @@ -0,0 +1,9 @@ +[Unit] +Description=Disable automated desktop login + +[Service] +Type=oneshot +ExecStart=-rm -f /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf +ExecStart=-rm -f /etc/sddm.conf.d/autologin.conf +TimeoutStopSec=10 +ExecStop=systemctl disable phytium-disable-autologin.timer diff --git a/package/phytium-firstlogin/src/phytium-disable-autologin.timer b/package/phytium-firstlogin/src/phytium-disable-autologin.timer new file mode 100644 index 00000000..fc64e09b --- /dev/null +++ b/package/phytium-firstlogin/src/phytium-disable-autologin.timer @@ -0,0 +1,8 @@ +[Unit] +Description=Disable automated desktop login + +[Timer] +OnActiveSec=1min + +[Install] +WantedBy=timers.target diff --git a/package/phytium-firstlogin/src/phytium-firstlogin b/package/phytium-firstlogin/src/phytium-firstlogin new file mode 100755 index 00000000..1e7ea471 --- /dev/null +++ b/package/phytium-firstlogin/src/phytium-firstlogin @@ -0,0 +1,766 @@ +#!/bin/bash +# +# Copyright (c) Authors: https://www.armbian.com/authors +# +# This file is licensed under the terms of the GNU General Public +# License version 2. This program is licensed "as is" without any +# warranty of any kind, whether express or implied. +# +# Modification Notice: +# Modifier: Phytium +# Modification Date: 2024-08-07 +# Modifications: +# 1. Add some configurations in function add_user +# 2. Comment out some codes about displaying the information of Armbian +# + +# read distribution status +# shellcheck source=/dev/null +[[ -f /etc/lsb-release ]] && . /etc/lsb-release +[[ -f /etc/os-release ]] && . /etc/os-release +[[ -z "$DISTRIB_CODENAME" ]] && DISTRIB_CODENAME="${VERSION_CODENAME}" +[[ -n "$DISTRIB_CODENAME" && -f /etc/armbian-distribution-status ]] && DISTRIBUTION_STATUS=$(grep "$DISTRIB_CODENAME" /etc/armbian-distribution-status | cut -d"=" -f2) + +#. /etc/armbian-release + +check_abort() { + + echo -e "\nDisabling user account creation procedure\n" + rm -f /root/.not_logged_in_yet + if [[ ${USER_SHELL} == zsh ]]; then + printf "\nYou selected \e[0;91mZSH\x1B[0m as your default shell. If you want to use it right away, please logout and login! \n\n" + fi + trap - INT + exit 0 + +} + +mask2cidr() { + nbits=0 + IFS=. + for dec in $1 ; do + case $dec in + 255) let nbits+=8;; + 254) let nbits+=7;; + 252) let nbits+=6;; + 248) let nbits+=5;; + 240) let nbits+=4;; + 224) let nbits+=3;; + 192) let nbits+=2;; + 128) let nbits+=1;; + 0);; + *) echo "Error: $dec is not recognised"; exit 1 + esac + done + echo "$nbits" +} + +do_firstrun_automated_network_configuration() +{ + #----------------------------------------------------------------------------- + #Config FP + local fp_config='/root/.not_logged_in_yet' + + #----------------------------------------------------------------------------- + #Grab user requested settings + if [[ -f $fp_config ]]; then + + # Convert line endings to Unix from Dos + sed -i $'s/\r$//' "$fp_config" + + # check syntax + bash -n "$fp_config" || return + + # Load vars directly from file + source "$fp_config" + + # Obtain backward configuration compatibility + PRESET_NET_STATIC_DNS=${PRESET_NET_STATIC_DNS// /,} + PRESET_NET_STATIC_MASK=$(mask2cidr $PRESET_NET_STATIC_MASK) + + #----------------------------------------------------------------------------- + # Set Network + if [[ $PRESET_NET_CHANGE_DEFAULTS == 1 ]]; then + # Only run at tty1 + if [ "$(who am i | awk '{print $2}')" != "tty1" ];then + exit + fi + # - Get name of 1st available ethernet and wifi adapter + eth_index="$(nmcli d | grep ethernet | cut -d ' ' -f 1 | head -n 1)" + wlan_index="$(nmcli d | grep wifi | cut -d ' ' -f 1 | head -n 1)" + + # for static IP we only append settings + if [[ $PRESET_NET_USE_STATIC == 1 ]]; then + local FIXED_IP_SETTINGS="ipv4.method manual ipv4.address ${PRESET_NET_STATIC_IP}/${PRESET_NET_STATIC_MASK} ipv4.dns ${PRESET_NET_STATIC_DNS} ipv4.gateway ${PRESET_NET_STATIC_GATEWAY}" + fi + + if [[ -n $eth_index || -n $wlan_index ]]; then + # delete all current connections + LC_ALL=C nmcli -t -f UUID,DEVICE connection show | awk '{print $1}' | cut -f1 -d":" | xargs nmcli connection delete + + # - Wifi enable + if [[ $PRESET_NET_WIFI_ENABLED == 1 ]]; then + + #Set wifi country code + iw reg set "$PRESET_NET_WIFI_COUNTRYCODE" + + nmcli con add con-name "Armbian wireless" type wifi ifname ${wlan_index} ssid "$PRESET_NET_WIFI_SSID" -- wifi-sec.key-mgmt wpa-psk wifi-sec.psk "$PRESET_NET_WIFI_KEY" ${FIXED_IP_SETTINGS} + nmcli con up "Armbian wireless" + + #Enable Wlan, disable Eth + PRESET_NET_ETHERNET_ENABLED=0 + + # - Ethernet enable + elif [[ $PRESET_NET_ETHERNET_ENABLED == 1 ]]; then + + nmcli con add con-name "Armbian ethernet" type ethernet ifname ${eth_index} -- ${FIXED_IP_SETTINGS} + nmcli con up "Armbian ethernet" + + #Enable Eth, disable Wlan + PRESET_NET_WIFI_ENABLED=0 + + fi + fi + fi + fi +} #do_firstrun_automated_network_configuration + +read_password() { + + unset password + unset charcount + prompt="$1 password: " + + stty -echo + + charcount=0 + while IFS= read -p "$prompt" -r -s -n 1 char; do + if [[ $char == $'\0' ]]; then + break + fi + + # Handle backspace + if [[ $char == $'\177' ]]; then + if [ $charcount -gt 0 ]; then + charcount=$((charcount - 1)) + prompt=$'\b \b' + password="${password%?}" + else + prompt='' + fi + else + charcount=$((charcount + 1)) + prompt='*' + password+="$char" + fi + done + + stty echo + +} + +set_shell() { + + readarray -t optionsAudits <<< "$(grep "zsh\|/bash" /etc/shells | sed 's/\/bin\///g' | sed 's/\/usr//g' | uniq)" + USER_SHELL="bash" + + if [[ "${#optionsAudits[@]}" -gt 1 ]]; then + while :; do + while [[ ! "${reply}" =~ ^(1|2)$ ]]; do + i=1 + echo -e "\nChoose default system command shell:\n" + for o in "${optionsAudits[@]}"; do + echo "$i) $o" + ((i++)) || true + done + if [ -z $PRESET_USER_SHELL ];then + read -r reply + else + reply=1 + for index in "${!optionsAudits[@]}"; do + if [[ "${optionsAudits[$index]}" == "$PRESET_USER_SHELL" ]]; then + reply=$(($index + 1)) + break + fi + done + fi + done + case $reply in + "1" | "${optionsAudits[0]}") + USER_SHELL="${optionsAudits[0]}" + break + ;; + "2" | "${optionsAudits[1]}") + USER_SHELL="${optionsAudits[1]}" + break + ;; + *) + USER_SHELL="${optionsAudits[0]}" + break + ;; + esac + done + fi + SHELL_PATH=$(grep "/$USER_SHELL$" /etc/shells | tail -1) + + chsh -s "$(grep -iF "/$USER_SHELL" /etc/shells | tail -1)" + echo -e "\nShell: \x1B[92m${USER_SHELL^^}\x1B[0m" + + # change shell for future users + sed -i "s|^SHELL=.*|SHELL=${SHELL_PATH}|" /etc/default/useradd + sed -i "s|^DSHELL=.*|DSHELL=${SHELL_PATH}|" /etc/adduser.conf + +} + +set_timezone_and_locales() { + + # Grab this machine's public IP address + PUBLIC_IP=$(curl --max-time 5 -s https://ipinfo.io/ip) + + # Check if we have wireless adaptor + WIFI_DEVICE=$(LC_ALL=C nmcli dev status | grep " wifi " 2> /dev/null) + + if [ -z "$PUBLIC_IP" ]; then + + # ask for connecting to wireless if wifi device is found + if [[ -n "$WIFI_DEVICE" ]]; then + echo -e "Internet connection was \x1B[91mnot detected\x1B[0m." + echo "" + unset response + while [[ ! "${response}" =~ ^(Y|y|N|n)$ ]]; do + if [ -z $PRESET_CONNECT_WIRELESS ];then + read -r -p "Connect via wireless? [Y/n] " response + response=${response:-Y} + else + response=n + fi + echo "$response" + done + if [[ "${response}" =~ ^(Y|y)$ ]]; then + nmtui-connect + fi + echo "" + fi + fi + + # Grab IP once again if not found + [[ -z "$PUBLIC_IP" && -n "$WIFI_DEVICE" ]] && PUBLIC_IP=$(curl --max-time 5 -s https://ipinfo.io/ip) + + # Call the geolocation API and capture the output + RES=$( + curl --max-time 5 -s "http://ipwhois.app/json/${PUBLIC_IP}" | + jq '.timezone, .country, .country_code' | + while read -r TIMEZONE; do + read -r COUNTRY + echo "${TIMEZONE},${COUNTRY},${COUNTRYCODE}" | tr --delete '"\n' + done + ) + + TZDATA=$(echo "${RES}" | cut -d"," -f1) + CCODE=$(echo "${RES}" | cut -d"," -f3 | xargs) + echo -e "Detected timezone: \x1B[92m$TZDATA\x1B[0m" + echo "" + unset response + while [[ ! "${response}" =~ ^(Y|y|N|n)$ ]]; do + if [ -z $SET_LANG_BASED_ON_LOCATION ];then + read -r -p "Set user language based on your location? [Y/n] " response + response=${response:-Y} + else + response=$SET_LANG_BASED_ON_LOCATION + fi + echo "$response" + done + # change it only if we have a match and if we agree + if [[ "${response}" =~ ^(N|n)$ ]]; then + unset CCODE TZDATA + fi + + LOCALES=$(grep territory /usr/share/i18n/locales/* | grep _"$CCODE" | cut -d ":" -f 1 | cut -d "/" -f 6 | + xargs -I{} grep {} /usr/share/i18n/SUPPORTED | grep "UTF-8$" | cut -d " " -f 1) + # UTF8 is not present everywhere so check again in case it returns empty value + [[ -z "$LOCALES" ]] && LOCALES=$(grep territory /usr/share/i18n/locales/* | grep _"$CCODE" | cut -d ":" -f 1 | cut -d "/" -f 6 | + xargs -I{} grep {} /usr/share/i18n/SUPPORTED | cut -d " " -f 1) + + readarray -t options <<< "${LOCALES}" + + if [ -z $PRESET_LOCALE ];then + # when having more locales, prompt for choosing one + if [[ "${#options[@]}" -gt 1 ]]; then + + options+=("Skip generating locales") + echo -e "\nAt your location, more locales are possible:\n" + PS3='Please enter your choice:' + select opt in "${options[@]}"; do + if [[ " ${options[*]} " == *" ${opt} "* ]]; then + LOCALES=${opt} + break + fi + done + fi + else + LOCALES=$PRESET_LOCALE + fi + + if [[ "${LOCALES}" != *Skip* ]]; then + + if [ -z $PRESET_TIMEZONE ];then + # if TZDATA was not detected, we need to select one + if [[ -z ${TZDATA} ]]; then + TZDATA=$(tzselect | tail -1) + fi + else + TZDATA=$PRESET_TIMEZONE + fi + + timedatectl set-timezone "${TZDATA}" + dpkg-reconfigure --frontend=noninteractive tzdata > /dev/null 2>&1 + + # generate locales + echo "" + sed -i 's/# '"${LOCALES}"'/'"${LOCALES}"'/' /etc/locale.gen + echo -e "Generating locales: \x1B[92m${LOCALES}\x1B[0m" + locale-gen "${LOCALES}" > /dev/null 2>&1 + + # setting detected locales only for user + { + echo "export LC_ALL=$LOCALES" + echo "export LANG=$LOCALES" + echo "export LANGUAGE=$LOCALES" + } >> /home/"$RealUserName"/.bashrc + { + echo "export LC_ALL=$LOCALES" + echo "export LANG=$LOCALES" + echo "export LANGUAGE=$LOCALES" + } >> /home/"$RealUserName"/.xsessionrc + + fi +} + +add_profile_sync_settings() { + if [[ ! -f /usr/bin/psd ]]; then + return 0 + fi + + /usr/bin/psd > /dev/null 2>&1 + config_file="${HOME}/.config/psd/psd.conf" + if [ -f "${config_file}" ]; then + # test for overlayfs + sed -i 's/#USE_OVERLAYFS=.*/USE_OVERLAYFS="yes"/' "${config_file}" + case $(/usr/bin/psd p 2> /dev/null | grep Overlayfs) in + *active*) + echo -e "\nConfigured profile sync daemon with overlayfs." + ;; + *) + echo -e "\nConfigured profile sync daemon." + sed -i 's/USE_OVERLAYFS="yes"/#USE_OVERLAYFS="no"/' "${config_file}" + ;; + esac + fi + systemctl --user enable psd.service > /dev/null 2>&1 + systemctl --user start psd.service > /dev/null 2>&1 +} + +add_user() { + read -r -t 0 _ + REPEATS=3 + while [ -f "/root/.not_logged_in_yet" ]; do + echo -e "\nPlease provide a username (eg. your first name): \c" + if [ -z $PRESET_USER_NAME ];then + read -r -e username + else + username=$PRESET_USER_NAME + fi + if ! grep '^[a-zA-Z][a-zA-Z0-9]*$' <<< "$username" > /dev/null; then + echo -e "\n\x1B[91mError\x1B[0m: illegal characters in username" + return + fi + + RealUserName="$(echo "$username" | tr '[:upper:]' '[:lower:]' | tr -d -c '[:alnum:]')" + [ -z "$RealUserName" ] && return + if ! id "$RealUserName" > /dev/null 2>&1; then break; else echo -e "Username \e[0;31m$RealUserName\x1B[0m already exists on the system."; fi + done + + while [ -f "/root/.not_logged_in_yet" ]; do + if [ -z $PRESET_USER_PASSWORD ];then + read_password "Create user ($username)" + else + password=$PRESET_USER_PASSWORD + fi + first_input="$password" + echo "" + if [ -z $PRESET_USER_PASSWORD ];then + read_password "Repeat user ($username)" + else + password=$PRESET_USER_PASSWORD + fi + second_input="$password" + echo "" + if [[ "$first_input" == "$second_input" ]]; then + # minimal images might not have this + if command -v cracklib-check > /dev/null 2>&1; then + result="$(cracklib-check <<< "$password")" + okay="$(awk -F': ' '{ print $2}' <<< "$result")" + if [[ "$okay" != "OK" ]]; then + echo -e "\n\e[0;31mWarning:\x1B[0m Weak password, $okay \b!" + fi + fi + echo -e "" + if [ -z $PRESET_DEFAULT_REALNAME ];then + read -r -e -p "Please provide your real name: " -i "${RealUserName^}" RealName + else + RealName=$PRESET_DEFAULT_REALNAME + fi + + adduser --quiet --disabled-password --home /home/"$RealUserName" --gecos "$RealName" "$RealUserName" + if [[ -n "$first_input" ]]; then + ( + echo "$first_input" + echo "$second_input" + ) | passwd "$RealUserName" > /dev/null 2>&1 + else + passwd -d "$RealUserName" > /dev/null 2>&1 + fi + for additionalgroup in sudo netdev audio video disk tty users games dialout plugdev input bluetooth systemd-journal ssh render; do + usermod -aG "${additionalgroup}" "${RealUserName}" 2> /dev/null + done + + # fix for gksu in Xenial + touch /home/"$RealUserName"/.Xauthority + chown "$RealUserName":"$RealUserName" /home/"$RealUserName"/.Xauthority + RealName="$(awk -F":" "/^${RealUserName}:/ {print \$5}" < /etc/passwd | cut -d',' -f1)" + [ -z "$RealName" ] && RealName="$RealUserName" + echo -e "\nDear \e[0;92m${RealName}\x1B[0m, your account \e[0;92m${RealUserName}\x1B[0m has been created and is sudo enabled." + echo -e "Please use this account for your daily work from now on.\n" + rm -f /root/.not_logged_in_yet + chmod +x /etc/update-motd.d/* + # set up profile sync daemon on desktop systems + if command -v psd > /dev/null 2>&1; then + echo -e "${RealUserName} ALL=(ALL) NOPASSWD: /usr/bin/psd-overlay-helper" >> /etc/sudoers + touch /home/"${RealUserName}"/.activate_psd + chown "$RealUserName":"$RealUserName" /home/"${RealUserName}"/.activate_psd + fi + + # set the configuration of vim + touch /home/"$RealUserName"/.vimrc + chown "$RealUserName":"$RealUserName" /home/"$RealUserName"/.vimrc + echo 'set mouse-=a' >> /home/"$RealUserName"/.vimrc + echo 'syntax on' >> /home/"$RealUserName"/.vimrc + + # automatically switching the sound output device + if [ -e /usr/bin/switch_to_headphone.sh ]; then + mkdir -p /home/"$RealUserName"/.config/autostart + mv /usr/share/alsa/ucm2/sound_output_device_switching.desktop /home/"$RealUserName"/.config/autostart + chown -R "$RealUserName":"$RealUserName" /home/"$RealUserName"/.config + fi + + # add /usr/sbin to $PATH for "$RealUserName" + if [ -e /home/"$RealUserName"/.bashrc ]; then + echo "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/usr/sbin " >> /home/"$RealUserName"/.bashrc + fi + + # change the priority order of the shared library directories + if ! grep -q "/usr/local/lib" /etc/ld.so.conf; then + sed -i '1 i /usr/local/lib' /etc/ld.so.conf + ldconfig + chown -R "$RealUserName":"$RealUserName" /etc/ld.so.cache + chown -R "$RealUserName":"$RealUserName" /var/cache/ldconfig/aux-cache + fi + break + elif [[ -n $password ]]; then + echo -e "Rejected - \e[0;31mpasswords do not match.\x1B[0m Try again [${REPEATS}]." + REPEATS=$((REPEATS - 1)) + fi + [[ "$REPEATS" -eq 0 ]] && logout + done + +} + +if [[ -f /root/.not_logged_in_yet && -n $(tty) ]]; then + do_firstrun_automated_network_configuration + + # disable autologin + rm -f /etc/systemd/system/getty@.service.d/override.conf + rm -f /etc/systemd/system/serial-getty@.service.d/override.conf + systemctl daemon-reload + + declare desktop_dm="none" + declare -i desktop_is_sddm=0 desktop_is_lightdm=0 desktop_is_gdm3=0 + if [[ -f /usr/bin/sddm ]]; then + desktop_dm="sddm" + desktop_is_sddm=1 + fi + if [[ -f /usr/sbin/lightdm ]]; then + desktop_dm="lightdm" + desktop_is_lightdm=1 + fi + if [[ -f /usr/sbin/gdm3 ]]; then + desktop_dm="gdm3" + desktop_is_gdm3=1 + fi + + echo -e "\nWaiting for system to finish booting ..." + systemctl is-system-running --wait > /dev/null + + # enable hiDPI support + if [[ "$(cut -d, -f1 < /sys/class/graphics/fb0/virtual_size 2> /dev/null)" -gt 1920 ]]; then + # lightdm + [[ -f /etc/lightdm/slick-greeter.conf ]] && echo "enable-hidpi = on" >> /etc/lightdm/slick-greeter.conf + # xfce + [[ -f /etc/skel/.config/xfce4/xfconf/xfce-perchannel-xml/xsettings.xml ]] && sed -i 's||g' /etc/skel/.config/xfce4/xfconf/xfce-perchannel-xml/xsettings.xml + + # framebuffer console larger font + setfont /usr/share/consolefonts/Uni3-TerminusBold32x16.psf.gz + fi + + clear + + echo -e "Welcome to \e[1m\e[97m"Phytium Pi OS firstlogin"\x1B[0m! \n" + #echo -e "Documentation: \e[1m\e[92m${VENDORDOCS}\x1B[0m | Community support: \e[1m\e[92m${VENDORSUPPORT}\x1B[0m\n" + #GET_IP=$(bash /etc/update-motd.d/30-armbian-sysinfo | grep IP | sed "s/.*IP://" | sed 's/^[ \t]*//') + #[[ -n "$GET_IP" ]] && echo -e "IP address: $GET_IP\n" + + trap '' 2 + REPEATS=3 + while [ -f "/root/.not_logged_in_yet" ]; do + #. /root/.not_logged_in_yet + if [ -z $PRESET_ROOT_PASSWORD ];then + read_password "Create root" + else + if [ "$(who am i | awk '{print $2}')" != "tty1" ];then + exit + fi + password=$PRESET_ROOT_PASSWORD + fi + + # only allow one login. Once you enter root password, kill others. + loginfrom=$(who am i | awk '{print $2}') + who -la | grep root | grep -v "$loginfrom" | awk '{print $7}' | xargs --no-run-if-empty kill -9 > /dev/null 2>&1 + + first_input="$password" + echo "" + if [ -z $PRESET_ROOT_PASSWORD ];then + read_password "Repeat root" + else + password=$PRESET_ROOT_PASSWORD + fi + second_input="$password" + echo "" + if [[ "$first_input" == "$second_input" ]]; then + # minimal might not have this + if command -v cracklib-check > /dev/null 2>&1; then + result="$(cracklib-check <<< "$password")" + okay="$(awk -F': ' '{ print $2}' <<< "$result")" + if [[ "$okay" != "OK" ]]; then + echo -e "\n\e[0;31mWarning:\x1B[0m Weak password, $okay \b!" + fi + fi + ( + echo "$first_input" + echo "$second_input" + ) | passwd root > /dev/null 2>&1 + break + elif [[ -n $password ]]; then + echo -e "Rejected - \e[0;31mpasswords do not match.\x1B[0m Try again [${REPEATS}]." + REPEATS=$((REPEATS - 1)) + fi + [[ "$REPEATS" -eq 0 ]] && exit + done + trap - INT TERM EXIT + + # display support status + if [ "$IMAGE_TYPE" != "nightly" ]; then + if [[ "$BRANCH" == "edge" ]]; then + echo -e "\nSupport status: \e[0;31mcommunity support\x1B[0m (edge kernel branch)" + elif [[ "$DISTRIBUTION_STATUS" != "supported" ]]; then + echo -e "\nSupport status: \e[0;31mcommunity support\x1B[0m (unsupported userspace)" + elif [[ "$BOARD_TYPE" != "conf" ]]; then + echo -e "\nSupport status: \e[0;31mcommunity support\x1B[0m (looking for a dedicated maintainer)" + fi + else + + echo -e "\e[0;31m\nWARNING!\x1B[0m\n\nYou are using an \e[0;31mautomated build\x1B[0m meant only for developers to provide" + echo -e "constructive feedback to improve build system, OS settings or UX.\n" + + echo -e "If this does not apply to you, \e[0;31mSTOP NOW!\x1B[0m Especially don't use this " + echo -e "image for production since things might not work as expected or at " + echo -e "all. They may break anytime with next update." + + fi + + # ask user to select shell + trap '' 2 + set_shell + trap - INT TERM EXIT + + trap check_abort INT + + while [ -f "/root/.not_logged_in_yet" ]; do + echo -e "\nCreating a new user account. Press to abort" + [[ "${desktop_dm}" != "none" ]] && echo -e "\n\e[0;31mDesktop environment will not be enabled if you abort the new user creation\x1B[0m" + add_user + done + trap - INT TERM EXIT + + # ask user to select automated locales or not + if [[ -f /root/.desktop_firstlogin ]]; then + trap '' 2 + set_timezone_and_locales + trap - INT TERM EXIT + rm /root/.desktop_firstlogin + fi + + if [[ ${USER_SHELL} == zsh ]]; then + printf "\nYou selected \e[0;91mZSH\x1B[0m as your default shell. If you want to use it right away, please logout and login! \n\n" + fi + + # re-enable passing locale environment via ssh + sed -e '/^#AcceptEnv LANG/ s/^#//' -i /etc/ssh/sshd_config + # restart sshd daemon + systemctl restart ssh.service + + # rpardini: hacks per-dm, very much legacy stuff that works by a miracle + if [[ "${desktop_dm}" == "lightdm" ]] && [ -n "$RealName" ]; then + + mkdir -p /etc/lightdm/lightdm.conf.d + cat <<- EOF > /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf + [Seat:*] + autologin-user=$RealUserName + autologin-user-timeout=0 + user-session=xfce + EOF + + # select gnome session (has to be first or it breaks budgie/cinnamon desktop autologin and user-session) + # @TODO: remove this, gnome should use gdm3, not lightdm + [[ -x $(command -v gnome-session) ]] && sed -i "s/user-session.*/user-session=ubuntu/" /etc/lightdm/lightdm.conf.d/11-armbian.conf + [[ -x $(command -v gnome-session) ]] && sed -i "s/user-session.*/user-session=ubuntu/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf + + # select awesome session + [[ -x $(command -v awesome) ]] && sed -i "s/user-session.*/user-session=awesome/" /etc/lightdm/lightdm.conf.d/11-armbian.conf + [[ -x $(command -v awesome) ]] && sed -i "s/user-session.*/user-session=awesome/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf + + # select budgie session + [[ -x $(command -v budgie-desktop) ]] && sed -i "s/user-session.*/user-session=budgie-desktop/" /etc/lightdm/lightdm.conf.d/11-armbian.conf + [[ -x $(command -v budgie-desktop) ]] && sed -i "s/user-session.*/user-session=budgie-desktop/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf + + # select cinnamon session + [[ -x $(command -v cinnamon) ]] && sed -i "s/user-session.*/user-session=cinnamon/" /etc/lightdm/lightdm.conf.d/11-armbian.conf + [[ -x $(command -v cinnamon) ]] && sed -i "s/user-session.*/user-session=cinnamon/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf + + # select deepin session + [[ -x $(command -v deepin-wm) ]] && sed -i "s/user-session.*/user-session=deepin/" /etc/lightdm/lightdm.conf.d/11-armbian.conf + [[ -x $(command -v deepin-wm) ]] && sed -i "s/user-session.*/user-session=deepin/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf + + # select ice-wm session + [[ -x $(command -v icewm-session) ]] && sed -i "s/user-session.*/user-session=icewm-session/" /etc/lightdm/lightdm.conf.d/11-armbian.conf + [[ -x $(command -v icewm-session) ]] && sed -i "s/user-session.*/user-session=icewm-session/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf + + # select i3 session + [[ -x $(command -v i3) ]] && sed -i "s/user-session.*/user-session=i3/" /etc/lightdm/lightdm.conf.d/11-armbian.conf + [[ -x $(command -v i3) ]] && sed -i "s/user-session.*/user-session=i3/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf + + # select lxde session + [[ -x $(command -v startlxde) ]] && sed -i "s/user-session.*/user-session=LXDE/" /etc/lightdm/lightdm.conf.d/11-armbian.conf + [[ -x $(command -v startlxde) ]] && sed -i "s/user-session.*/user-session=LXDE/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf + + # select lxqt session + [[ -x $(command -v startlxqt) ]] && sed -i "s/user-session.*/user-session=lxqt/" /etc/lightdm/lightdm.conf.d/11-armbian.conf + [[ -x $(command -v startlxqt) ]] && sed -i "s/user-session.*/user-session=lxqt/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf + + # select mate session + [[ -x $(command -v mate-wm) ]] && sed -i "s/user-session.*/user-session=mate/" /etc/lightdm/lightdm.conf.d/11-armbian.conf + [[ -x $(command -v mate-wm) ]] && sed -i "s/user-session.*/user-session=mate/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf + + # select sway wayland session + [[ -x $(command -v sway) ]] && sed -i "s/user-session.*/user-session=sway/" /etc/lightdm/lightdm.conf.d/11-armbian.conf + [[ -x $(command -v sway) ]] && sed -i "s/user-session.*/user-session=sway/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf + + # select xmonad session + [[ -x $(command -v xmonad) ]] && sed -i "s/user-session.*/user-session=xmonad/" /etc/lightdm/lightdm.conf.d/11-armbian.conf + [[ -x $(command -v xmonad) ]] && sed -i "s/user-session.*/user-session=xmonad/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf + + ln -sf /lib/systemd/system/lightdm.service /etc/systemd/system/display-manager.service + + if [[ -f /var/run/resize2fs-reboot ]]; then + # Let the user reboot now otherwise start desktop environment + printf "\n\n\e[0;91mWarning: a reboot is needed to finish resizing the filesystem \x1B[0m \n" + printf "\e[0;91mPlease reboot the system now \x1B[0m \n\n" + else + echo -e "\n\e[1m\e[39mNow starting desktop environment...\x1B[0m\n" + sleep 1 + service lightdm start 2> /dev/null + if [ -f /root/.desktop_autologin ]; then + rm /root/.desktop_autologin + else + systemctl -q enable phytium-disable-autologin.timer + systemctl start phytium-disable-autologin.timer + fi + # logout if logged at console + who -la | grep root | grep -q tty1 && exit 1 + fi + + elif [[ "${desktop_dm}" == "gdm3" ]] && [ -n "$RealName" ]; then + # 1st run goes without login + mkdir -p /etc/gdm3 + cat <<- EOF > /etc/gdm3/custom.conf + [daemon] + AutomaticLoginEnable = true + AutomaticLogin = $RealUserName + EOF + + ln -sf /lib/systemd/system/gdm3.service /etc/systemd/system/display-manager.service + + if [[ -f /var/run/resize2fs-reboot ]]; then + # Let the user reboot now otherwise start desktop environment + printf "\n\n\e[0;91mWarning: a reboot is needed to finish resizing the filesystem \x1B[0m \n" + printf "\e[0;91mPlease reboot the system now \x1B[0m \n\n" + else + echo -e "\n\e[1m\e[39mNow starting desktop environment...\x1B[0m\n" + sleep 1 + service gdm3 start 2> /dev/null + if [ -f /root/.desktop_autologin ]; then + rm /root/.desktop_autologin + else + ( + sleep 20 + sed -i "s/AutomaticLoginEnable.*/AutomaticLoginEnable = false/" /etc/gdm3/custom.conf + ) & + fi + # logout if logged at console + who -la | grep root | grep -q tty1 && exit 1 + fi + elif [[ "${desktop_dm}" == "sddm" ]] && [ -n "$RealName" ]; then + + # create default sddm config + mkdir -p /etc/sddm.conf.d + cat <<- EOF > /etc/sddm.conf.d/armbian.conf + [Theme] + Current=breeze + [General] + InputMethod=none + EOF + + # 1st run goes without login + cat <<- EOF > /etc/sddm.conf.d/autologin.conf + [Autologin] + User=$RealUserName + EOF + echo -e "\n\e[1m\e[39mNow starting desktop environment via ${desktop_dm}...\x1B[0m\n" + systemctl enable --now sddm 2> /dev/null + + if [ -f /root/.desktop_autologin ]; then + rm /root/.desktop_autologin + else + systemctl -q enable armbian-disable-autologin.timer + fi + # logout if logged at console + who -la | grep root | grep -q tty1 && exit 1 + + else + # no display manager detected + # Display reboot recommendation if necessary + if [[ -f /var/run/resize2fs-reboot ]]; then + printf "\n\n\e[0;91mWarning: a reboot is needed to finish resizing the filesystem \x1B[0m \n" + printf "\e[0;91mPlease reboot the system now \x1B[0m \n\n" + fi + fi +fi -- Gitee From 3ead5e0321a3f36595318a8f49c7a066eb81c05e Mon Sep 17 00:00:00 2001 From: lindongping2027 Date: Fri, 23 Aug 2024 14:53:53 +0800 Subject: [PATCH 2/9] add phytium-zram Signed-off-by: lindongping2027 --- board/phytium/common/post-build.sh | 6 +- configs/phytiumpi_defconfig | 5 +- configs/phytiumpi_desktop_defconfig | 5 +- package/Config.in | 1 + .../phytium-desktop-tools.mk | 2 +- .../phytium-firstlogin/phytium-firstlogin.mk | 2 +- package/phytium-tools/phytium-tools.mk | 2 +- package/phytium-zram/Config.in | 2 + package/phytium-zram/phytium-zram.mk | 21 +++ package/phytium-zram/src/phytium-zram-config | 158 ++++++++++++++++++ .../src/phytium-zram-config.service | 19 +++ .../phytium-zram/src/phytium-zram-user-config | 40 +++++ package/phytium-zram/src/zram.conf | 7 + 13 files changed, 264 insertions(+), 6 deletions(-) create mode 100644 package/phytium-zram/Config.in create mode 100644 package/phytium-zram/phytium-zram.mk create mode 100755 package/phytium-zram/src/phytium-zram-config create mode 100644 package/phytium-zram/src/phytium-zram-config.service create mode 100755 package/phytium-zram/src/phytium-zram-user-config create mode 100644 package/phytium-zram/src/zram.conf diff --git a/board/phytium/common/post-build.sh b/board/phytium/common/post-build.sh index b4e0d9a7..e49ee3e4 100755 --- a/board/phytium/common/post-build.sh +++ b/board/phytium/common/post-build.sh @@ -16,7 +16,11 @@ main() ln -sf ../../Phytium/PMDK-I2S/PMDK-I2S.conf $1/usr/share/alsa/ucm2/conf.d/PMDK-I2S/PMDK-I2S.conf fi - exit $? + # zram + if grep -Eq "^BR2_PACKAGE_PHYTIUM_ZRAM=y$" ${BR2_CONFIG}; then + sudo chroot ${1} systemctl enable phytium-zram-config.service + fi + exit $? } main $@ diff --git a/configs/phytiumpi_defconfig b/configs/phytiumpi_defconfig index cdec088f..c35a195a 100644 --- a/configs/phytiumpi_defconfig +++ b/configs/phytiumpi_defconfig @@ -34,7 +34,7 @@ BR2_LINUX_KERNEL=y BR2_LINUX_KERNEL_CUSTOM_GIT=y BR2_LINUX_KERNEL_CUSTOM_REPO_URL="https://gitee.com/phytium_embedded/phytium-linux-kernel.git" # kernel 6.6 -BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="cafc1fab3d50c5e884a7c55df15ef0d013b94b57" +BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="662bfb88959757471794bd9a6089d373903898f3" BR2_LINUX_KERNEL_INTREE_DTS_NAME="phytium/phytiumpi_firefly" BR2_LINUX_KERNEL_DTS_SUPPORT=y BR2_LINUX_KERNEL_DEFCONFIG="phytium" @@ -69,6 +69,9 @@ BR2_PACKAGE_PHYTIUM_TOOLS=y # Phytium_firstrun BR2_PACKAGE_PHYTIUM_FIRSTLOGIN=y +# Phytium_zram +BR2_PACKAGE_PHYTIUM_ZRAM=y + # util-linux BR2_PACKAGE_UTIL_LINUX=y BR2_PACKAGE_UTIL_LINUX_BINARIES=y diff --git a/configs/phytiumpi_desktop_defconfig b/configs/phytiumpi_desktop_defconfig index 6554b98c..6ed2191b 100644 --- a/configs/phytiumpi_desktop_defconfig +++ b/configs/phytiumpi_desktop_defconfig @@ -34,7 +34,7 @@ BR2_LINUX_KERNEL=y BR2_LINUX_KERNEL_CUSTOM_GIT=y BR2_LINUX_KERNEL_CUSTOM_REPO_URL="https://gitee.com/phytium_embedded/phytium-linux-kernel.git" # kernel 6.6 -BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="cafc1fab3d50c5e884a7c55df15ef0d013b94b57" +BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="662bfb88959757471794bd9a6089d373903898f3" BR2_LINUX_KERNEL_INTREE_DTS_NAME="phytium/phytiumpi_firefly" BR2_LINUX_KERNEL_DTS_SUPPORT=y BR2_LINUX_KERNEL_DEFCONFIG="phytium" @@ -63,6 +63,9 @@ BR2_PACKAGE_PHYTIUM_DESKTOP_TOOLS=y # Phytium_firstrun BR2_PACKAGE_PHYTIUM_FIRSTLOGIN=y +# Phytium_zram +BR2_PACKAGE_PHYTIUM_ZRAM=y + BR2_PACKAGE_ROOTFS_DESKTOP=y BR2_ROOTFS_DEVICE_TABLE="system/device_table.txt board/phytium/common/desktop_device_table.txt" BR2_PACKAGE_BUSYBOX=n diff --git a/package/Config.in b/package/Config.in index 16c10c40..4786aeb3 100644 --- a/package/Config.in +++ b/package/Config.in @@ -259,6 +259,7 @@ menu "Filesystem and flash utilities" source "package/phytium-desktop-tools/Config.in" source "package/kernel-headers/Config.in" source "package/phytium-firstlogin/Config.in" + source "package/phytium-zram/Config.in" endmenu menu "Fonts, cursors, icons, sounds and themes" diff --git a/package/phytium-desktop-tools/phytium-desktop-tools.mk b/package/phytium-desktop-tools/phytium-desktop-tools.mk index 751f77b6..89541c1b 100644 --- a/package/phytium-desktop-tools/phytium-desktop-tools.mk +++ b/package/phytium-desktop-tools/phytium-desktop-tools.mk @@ -7,7 +7,7 @@ PHYTIUM_DESKTOP_TOOLS_VERSION = 0.1 PHYTIUM_DESKTOP_TOOLS_SITE = package/phytium-desktop-tools/src PHYTIUM_DESKTOP_TOOLS_SITE_METHOD = local -PHYTIUM_DESKTOP_TOOLS_INSTALL_TARGET_CMDS = YES +PHYTIUM_DESKTOP_TOOLS_INSTALL_TARGET = YES define PHYTIUM_DESKTOP_TOOLS_INSTALL_TARGET_CMDS mkdir -p $(TARGET_DIR)/usr/bin diff --git a/package/phytium-firstlogin/phytium-firstlogin.mk b/package/phytium-firstlogin/phytium-firstlogin.mk index 89aef558..e85b1b12 100644 --- a/package/phytium-firstlogin/phytium-firstlogin.mk +++ b/package/phytium-firstlogin/phytium-firstlogin.mk @@ -7,7 +7,7 @@ PHYTIUM_FIRSTLOGIN_VERSION = 0.1 PHYTIUM_FIRSTLOGIN_SITE = package/phytium-firstlogin/src PHYTIUM_FIRSTLOGIN_SITE_METHOD = local -PHYTIUM_FIRSTLOGIN_INSTALL_TARGET_CMDS = YES +PHYTIUM_FIRSTLOGIN_INSTALL_TARGET = YES define PHYTIUM_FIRSTLOGIN_INSTALL_TARGET_CMDS mkdir -p $(TARGET_DIR)/etc/systemd/system/serial-getty@.service.d/ diff --git a/package/phytium-tools/phytium-tools.mk b/package/phytium-tools/phytium-tools.mk index 3a00fdd9..5f3862c0 100644 --- a/package/phytium-tools/phytium-tools.mk +++ b/package/phytium-tools/phytium-tools.mk @@ -7,7 +7,7 @@ PHYTIUM_TOOLS_VERSION = 0.1 PHYTIUM_TOOLS_SITE = package/phytium-tools/src PHYTIUM_TOOLS_SITE_METHOD = local -PHYTIUM_TOOLS_INSTALL_TARGET_CMDS = YES +PHYTIUM_TOOLS_INSTALL_TARGET = YES define PHYTIUM_TOOLS_INSTALL_TARGET_CMDS mkdir -p $(TARGET_DIR)/usr/bin diff --git a/package/phytium-zram/Config.in b/package/phytium-zram/Config.in new file mode 100644 index 00000000..258840bc --- /dev/null +++ b/package/phytium-zram/Config.in @@ -0,0 +1,2 @@ +config BR2_PACKAGE_PHYTIUM_ZRAM + bool "phytium_zram" diff --git a/package/phytium-zram/phytium-zram.mk b/package/phytium-zram/phytium-zram.mk new file mode 100644 index 00000000..55fabc35 --- /dev/null +++ b/package/phytium-zram/phytium-zram.mk @@ -0,0 +1,21 @@ +################################################################################ +# +# phytium-zram +# +################################################################################ + +PHYTIUM_ZRAM_VERSION = 0.1 +PHYTIUM_ZRAM_SITE = package/phytium-zram/src +PHYTIUM_ZRAM_SITE_METHOD = local +PHYTIUM_ZRAM_INSTALL_TARGET = YES + +define PHYTIUM_ZRAM_INSTALL_TARGET_CMDS + mkdir -p $(TARGET_DIR)/usr/lib/phytium/ + mkdir -p $(TARGET_DIR)/etc/modules-load.d/ + $(INSTALL) -m 664 -D $(@D)/phytium-zram-user-config $(TARGET_DIR)/etc/default/ + $(INSTALL) -m 755 -D $(@D)/phytium-zram-config $(TARGET_DIR)/usr/lib/phytium/ + $(INSTALL) -m 644 -D $(@D)/phytium-zram-config.service $(TARGET_DIR)/lib/systemd/system/ + $(INSTALL) -m 644 -D $(@D)/zram.conf $(TARGET_DIR)/etc/modules-load.d/ +endef + +$(eval $(generic-package)) diff --git a/package/phytium-zram/src/phytium-zram-config b/package/phytium-zram/src/phytium-zram-config new file mode 100755 index 00000000..1c879c25 --- /dev/null +++ b/package/phytium-zram/src/phytium-zram-config @@ -0,0 +1,158 @@ +#!/bin/bash +# +# Copyright (c) Authors: https://www.armbian.com/authors +# +# This file is licensed under the terms of the GNU General Public +# License version 2. This program is licensed "as is" without any +# warranty of any kind, whether express or implied. + +# Functions: +# +# activate_zram +# activate_zram_swap +# activate_ramlog_partition +# activate_compressed_tmp + + +# Read in basic OS image information +#. /etc/armbian-release +# and script configuration +#. /usr/lib/armbian/armbian-common +Log=/var/log/armbian-hardware-monitor.log + +# It's possible to override SWAP, ZRAM_PERCENTAGE, MEM_LIMIT_PERCENTAGE, ZRAM_MAX_DEVICES, +# SWAP_ALGORITHM, RAMLOG_ALGORITHM, TMP_ALGORITHM and TMP_SIZE here: +ENABLED=false +[ -f /etc/default/phytium-zram-user-config ] && . /etc/default/phytium-zram-user-config +# Exit if not Enabled +[[ "$ENABLED" != "true" ]] && exit 0 + +# Do not interfere with already present zram-config package +dpkg -l | grep -q 'zram-config' && exit 0 + +activate_zram() { + # Load zram module with n instances for swap: one per CPU core, $ZRAM_MAX_DEVICES + # defines the maximum, on modern kernels we overwrite this with 1 and rely on + # max_comp_streams being set to count of CPU cores or $ZRAM_MAX_DEVICES + uname -r | grep -q '^3.' && zram_max_devs=${ZRAM_MAX_DEVICES:=4} || zram_max_devs=1 + cpu_cores=$(grep -c '^processor' /proc/cpuinfo | sed 's/^0$/1/') + [[ ${cpu_cores} -gt ${zram_max_devs} ]] && zram_devices=${zram_max_devs} || zram_devices=${cpu_cores} + module_args="$(modinfo zram | awk -F" " '/num_devices/ {print $2}' | cut -f1 -d:)" + [[ -n ${module_args} ]] && modprobe zram ${module_args}=$(( zram_devices + 2 )) || return + + swap_algo=${SWAP_ALGORITHM:=lzo} + # Expose 50% of real memory as swap space by default + zram_percent=${ZRAM_PERCENTAGE:=50} + mem_info=$(LC_ALL=C free -w 2>/dev/null | grep "^Mem" || LC_ALL=C free | grep "^Mem") + mem_info=$(echo $mem_info | awk '{print $2}') + memory_total=$(( mem_info * 1024 )) + mem_per_zram_device=$(( memory_total * zram_percent / zram_devices / 100 )) + + # Limit memory available to zram to 50% by default + mem_limit_percent=${MEM_LIMIT_PERCENTAGE:=50} + mem_limit_per_zram_device=$(( memory_total * mem_limit_percent / zram_devices / 100 )) +} + +activate_zram_swap() { + # Return is SWAP is disabled (enabled by default) + [[ -n "$SWAP" && "$SWAP" != "true" ]] && return; + + # Disable zswap if zram should be used. To make use of zswap instead a + # swap file or partition on *capable* storage needs to be chosen and + # defined as swap and also in /etc/default/phytium-zram-user-config SWAP=false + # needs to be set. + echo 0 >/sys/module/zswap/parameters/enabled 2>/dev/null + + # Limit Journal size to 20Mb + sed -i "s/.*SystemMaxUse=$/SystemMaxUse=20M/" /etc/systemd/journald.conf + + for (( i=1; i<=zram_devices; i++ )); do + swap_device=$(zramctl -f |sed 's/\/dev\///') + [[ ! ${swap_device} =~ ^zram ]] && printf "\n### No more available zram devices (%s)\n" "${swap_device}" >> ${Log} && exit 1; + if [ -f /sys/block/${swap_device}/comp_algorithm ]; then + # set compression algorithm, if defined as lzo choose lzo-rle if available + # https://www.phoronix.com/scan.php?page=news_item&px=ZRAM-Linux-5.1-Better-Perform + grep -q 'lzo-rle' /sys/block/${swap_device}/comp_algorithm && \ + [[ "X${swap_algo}" = "Xlzo" ]] && swap_algo="lzo-rle" + echo ${swap_algo} >/sys/block/${swap_device}/comp_algorithm 2>/dev/null + fi + if [ "X${ZRAM_BACKING_DEV}" != "X" ]; then + echo ${ZRAM_BACKING_DEV} >/sys/block/${swap_device}/backing_dev + fi + echo -n ${ZRAM_MAX_DEVICES:=4} > /sys/block/${swap_device}/max_comp_streams + echo -n ${mem_per_zram_device} > /sys/block/${swap_device}/disksize + echo -n ${mem_limit_per_zram_device} > /sys/block/${swap_device}/mem_limit + mkswap /dev/${swap_device} + swapon -p 5 /dev/${swap_device} + done + + # Swapping to HDDs is stupid so switch to settings made for flash memory and zram/zswap + echo 0 > /proc/sys/vm/page-cluster + + printf "\n### Activated %s %s zram swap devices with %dMB each.\n" "${zram_devices}" "${swap_algo}" "$((mem_per_zram_device / 1048576))" >> ${Log} +} # activate_zram_swap + +activate_ramlog_partition() { + # /dev/zram0 will be used as a compressed /var/log partition in RAM if + # ENABLED=true in /etc/default/armbian-ramlog is set + ENABLED=$(awk -F"=" '/^ENABLED/ {print $2}' /etc/default/armbian-ramlog) + [[ "$ENABLED" != "true" ]] && return + log_device=$(zramctl -f |sed 's/\/dev\///') + [[ ! ${log_device} =~ ^zram ]] && printf "\n### No more available zram devices (%s)\n" "${log_device}" >> ${Log} && exit 1; + + # read size also from /etc/default/armbian-ramlog + ramlogsize=$(awk -F"=" '/^SIZE/ {print $2}' /etc/default/armbian-ramlog) + disksize=$(sed -e 's/M$/*1048576/' -e 's/K$/*1024/' <<<${ramlogsize:=50M} | bc) + + # choose RAMLOG_ALGORITHM if defined in /etc/default/phytium-zram-user-config + # otherwise try to choose most efficient compression scheme available. + # See https://patchwork.kernel.org/patch/9918897/ + if [ "X${RAMLOG_ALGORITHM}" = "X" ]; then + for algo in lz4 lz4hc quicklz zlib brotli zstd ; do + echo ${algo} >/sys/block/${log_device}/comp_algorithm 2>/dev/null + done + else + echo ${RAMLOG_ALGORITHM} >/sys/block/${log_device}/comp_algorithm 2>/dev/null + fi + echo -n ${disksize} > /sys/block/${log_device}/disksize + + # if it fails, select $swap_algo. Workaround for some older kernels + if [[ $? == 1 ]]; then + echo ${swap_algo} > /sys/block/${log_device}/comp_algorithm 2>/dev/null + echo -n ${disksize} > /sys/block/${log_device}/disksize + fi + + mkfs.ext4 -O ^has_journal -s 1024 -L log2ram /dev/${log_device} + algo=$(sed 's/.*\[\([^]]*\)\].*/\1/g' > ${Log} +} # activate_ramlog_partition + +activate_compressed_tmp() { + # create /tmp not as tmpfs but zram compressed if no fstab entry exists + grep -q '^tmpfs /tmp' /etc/mtab && return + tmp_device=$(zramctl -f |sed 's/\/dev\///') + [[ ! ${tmp_device} =~ ^zram ]] && printf "\n### No more available zram devices (%s)\n" "${tmp_device}" >> ${Log} && exit 1; + + if [[ -f /sys/block/${tmp_device}/comp_algorithm ]]; then + if [ "X${TMP_ALGORITHM}" = "X" ]; then + echo ${swap_algo} >/sys/block/${tmp_device}/comp_algorithm 2>/dev/null + else + echo ${TMP_ALGORITHM} >/sys/block/${tmp_device}/comp_algorithm 2>/dev/null + fi + fi + [[ -z ${TMP_SIZE} ]] && echo -n $(( memory_total / 2 )) > /sys/block/${tmp_device}/disksize || echo -n ${TMP_SIZE} > /sys/block/${tmp_device}/disksize + mkfs.ext4 -O ^has_journal -s 1024 -L tmp /dev/${tmp_device} + mount -o nosuid,discard /dev/${tmp_device} /tmp + chmod 1777 /tmp + algo=$(sed 's/.*\[\([^]]*\)\].*/\1/g' > ${Log} +} # activate_compressed_tmp + +case $1 in + *start*) + activate_zram + activate_zram_swap + #activate_ramlog_partition + #activate_compressed_tmp + ;; +esac diff --git a/package/phytium-zram/src/phytium-zram-config.service b/package/phytium-zram/src/phytium-zram-config.service new file mode 100644 index 00000000..c069ffc6 --- /dev/null +++ b/package/phytium-zram/src/phytium-zram-config.service @@ -0,0 +1,19 @@ +# Phytium ZRAM configuration service +# Create 1 + number of cores compressed block devices +# This service may block the boot process for up to 30 sec + +[Unit] +Description=Phytium ZRAM config +DefaultDependencies=no +After=local-fs.target +Conflicts=shutdown.target + +[Service] +Type=oneshot +ExecStart=/usr/lib/phytium/phytium-zram-config start +ExecStop=/usr/lib/phytium/phytium-zram-config stop +RemainAfterExit=yes +TimeoutStartSec=30sec + +[Install] +WantedBy=sysinit.target diff --git a/package/phytium-zram/src/phytium-zram-user-config b/package/phytium-zram/src/phytium-zram-user-config new file mode 100755 index 00000000..ae558c2c --- /dev/null +++ b/package/phytium-zram/src/phytium-zram-user-config @@ -0,0 +1,40 @@ +# configuration values for the phytium-zram-config service +# +# enable the phytium-zram-config service? +ENABLED=true + +# Zram swap enabled by default, unless set to disabled +# SWAP=false + +# percentage of zram used as swap compared to physically available DRAM. +# Huge overcommitment (300) is possible and sometimes desirable. See +# https://forum.armbian.com/topic/5565-zram-vs-swap/?do=findComment&comment=61082 +# and don't forget to adjust $MEM_LIMIT_PERCENTAGE below too. +# ZRAM_PERCENTAGE=50 + +# percentage of DRAM available to zram. If this amount is exceeded the zram +# devices used for swap simply behave as if the device is full. You need to +# adjust/increase this value only if you want to work with massive memory +# overcommitment (ZRAM_PERCENTAGE exceeding 150 for example) +# MEM_LIMIT_PERCENTAGE=50 + +# create how many zram devices max for swap +# ZRAM_MAX_DEVICES=4 + +# Which algorithm for zram based swapping. Seems lzo is best choice on ARM: +# https://forum.armbian.com/topic/8161-swap-on-sbc/?do=findComment&comment=61668 +# SWAP_ALGORITHM=lzo + +# Which algorithm to choose for zram based ramlog partition +# RAMLOG_ALGORITHM=zstd + +# Which algorithm to choose for zram based /tmp +# TMP_ALGORITHM=zstd + +# Size for zram based /tmp, total memory / 2 by default +# TMP_SIZE=500M + +# If defined a separate partition will be used as zram backing device. Be CAREFUL +# which partition you assign and read starting from CONFIG_ZRAM_WRITEBACK in +# https://www.kernel.org/doc/Documentation/blockdev/zram.txt +# ZRAM_BACKING_DEV=/dev/nvme0n2 diff --git a/package/phytium-zram/src/zram.conf b/package/phytium-zram/src/zram.conf new file mode 100644 index 00000000..11bc8586 --- /dev/null +++ b/package/phytium-zram/src/zram.conf @@ -0,0 +1,7 @@ +842 +lz4hc +lz4 +zstd +lzo +zram +zsmalloc -- Gitee From f52af5d2513f77beb2ecc3130d618a69e6cd3c9a Mon Sep 17 00:00:00 2001 From: lindongping2027 Date: Fri, 6 Sep 2024 11:16:15 +0800 Subject: [PATCH 3/9] add phytium_ota Signed-off-by: lindongping2027 --- package/phytium-tools/phytium-tools.mk | 1 + package/phytium-tools/src/phytium_ota | 184 +++++++++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100755 package/phytium-tools/src/phytium_ota diff --git a/package/phytium-tools/phytium-tools.mk b/package/phytium-tools/phytium-tools.mk index 5f3862c0..ce73552e 100644 --- a/package/phytium-tools/phytium-tools.mk +++ b/package/phytium-tools/phytium-tools.mk @@ -26,6 +26,7 @@ define PHYTIUM_TOOLS_INSTALL_TARGET_CMDS $(INSTALL) -m 755 -D $(@D)/rtlbt/rtl8821c_config $(TARGET_DIR)/lib/firmware/rtl_bt/rtl8821c_config.bin $(INSTALL) -m 755 -D $(@D)/rtlbt/rtl8821c_fw $(TARGET_DIR)/lib/firmware/rtl_bt/rtl8821c_fw.bin $(INSTALL) -m 755 -D $(@D)/rtw88.conf $(TARGET_DIR)/etc/modprobe.d/ + $(INSTALL) -m 755 -D $(@D)/phytium_ota $(TARGET_DIR)/usr/bin/ endef $(eval $(generic-package)) diff --git a/package/phytium-tools/src/phytium_ota b/package/phytium-tools/src/phytium_ota new file mode 100755 index 00000000..cc783137 --- /dev/null +++ b/package/phytium-tools/src/phytium_ota @@ -0,0 +1,184 @@ +#!/bin/bash + +# Phytium phytiumpi ota script +# +# Copyright (c) 2024 Phytium Technology Co., Ltd. +# +# phytium_ota.sh - To replace fip-all.bin and fitImage through the +# network on the phytiumpi development board. +# + +download_dir="/usr/local/phytium-libs" +download_site="https://gitee.com/phytium_embedded/phytium-rogue-umlibs.git" +dest_dir="/usr/local/phytium-ota" + +usage() { + echo "Usage: sudo $0 {all|fitImage|uboot} {version}" + echo -e " for \033[32muboot\033[0m, {version} can be v1.x or latest + for \033[32mfitImage\033[0m and \033[32mall\033[0m, {version} only be latest" + echo " Example 1: $0 uboot v1.x + --update uboot to version v1.x + Example 2: $0 fitImage latest + --update fitImage to the latest + Example 3: $0 all latest + --update uboot and fit Image to the latest" +} + +while true; do + case $1 in + "-h" | "-help") + usage + exit + ;; + *) + break + ;; + esac +done + +check_args() +{ + if [ "$#" -ne 2 ]; then + echo -e "\033[1;31mUsage: sudo $0 {all|fitImage|uboot} {latest|v1.x}\033[0m" + exit 1 + fi + if [ "$1" != "all" ] && [ "$1" != "fitImage" ] && [ "$1" != "uboot" ]; then + echo -e "\033[1;31mError: First argument must be 'all', 'fitImage', or 'uboot'.\033[0m" + exit 1 + fi + + if [[ "$2" != "latest" && ! "$2" =~ ^v1\.[0-9]+$ ]]; then + echo -e "\033[1;31mError: Second argument must be 'latest' or 'v1.x'.\033[0m" + exit 1 + fi +} + +mem_info=$(LC_ALL=C free -w 2>/dev/null | grep "^Mem" || LC_ALL=C free | grep "^Mem") +mem_info=$(echo $mem_info | awk '{print $2}') +memory_total=$(( mem_info * 1024 )) +# compare to 3GB +if [ $memory_total -ge 3221225472 ]; then + mem=4 +else + mem=2 +fi +#echo $mem + +# get kernel version +kernel_version=$(uname -r) + +# unsupport rt kernel and 4.19 +if [[ $kernel_version =~ ^((5\.10\.[0-9]+))-phytium.* ]]; then + kernel_version="5.10" +elif [[ $kernel_version =~ ^((6\.6\.[0-9]+))-phytium.* ]]; then + kernel_version="6.6" +else + echo -e "\033[31munsupport kernel version: $kernel_version\033[0m" + exit 1 +fi + +detect_and_set_network() { + # Grab this machine's public IP address + PUBLIC_IP=$(curl --max-time 5 -s https://ipinfo.io/ip) + + # Check if we have wireless adaptor + WIFI_DEVICE=$(LC_ALL=C nmcli dev status | grep " wifi " 2> /dev/null) + + if [ -z "$PUBLIC_IP" ]; then + + # ask for connecting to wireless if wifi device is found + if [[ -n "$WIFI_DEVICE" ]]; then + echo -e "Internet connection was \x1B[91mnot detected\x1B[0m." + echo "" + unset response + while [[ ! "${response}" =~ ^(Y|y|N|n)$ ]]; do + if [ -z $PRESET_CONNECT_WIRELESS ];then + read -r -p "Connect via wireless? [Y/n] " response + response=${response:-Y} + else + response=n + fi + echo "$response" + done + if [[ "${response}" =~ ^(Y|y)$ ]]; then + nmtui-connect + fi + echo "" + fi + fi + # Grab IP once again if not found + [[ -z "$PUBLIC_IP" && -n "$WIFI_DEVICE" ]] && PUBLIC_IP=$(curl --max-time 5 -s https://ipinfo.io/ip) + + if [ -z "$PUBLIC_IP" ]; then + echo -e "Internet connection was \x1B[91mnot detected\x1B[0m." + echo "exit" + exit 1; + fi +} # detect_and_set_network + +# get fip-all.bin and fitImage from gitee. +get_uboot_and_fitImage() { + + rm -rf $download_dir + mkdir -p $download_dir + + git clone -b develop --depth 1 $download_site $download_dir + if [ "$?" != "0" ]; then + echo -e "\033[1;33mget uboot and fitImage failed\033[0m" + exit 1 + fi + echo -e "\033[1;32mget uboot and fitImage success\033[0m" + rm -rf $dest_dir + cp -rf $download_dir/phytium-ota /usr/local + +} # get_uboot_and_fitImage + +# replace_uboot_and_fitImage +# $1: can be all\uboot\fitImage +# $2: can be latest\v1.5,etc +replace_uboot_and_fitImage() { + + if [ "$1" == "all" ]; then + echo -e echo -e "\033[1;32mupdate uboot...\033[0m" + #to preserve the partition table. + dd if=/dev/mmcblk0 of=start.img bs=512 count=1 >> $dest_dir/uboot/$2/replace.log 2>&1 + dd if=$dest_dir/uboot/$2/fip-all.bin of=/dev/mmcblk0 bs=1M count=4 >> $dest_dir/uboot/$2/replace.log 2>&1 + dd if=start.img of=/dev/mmcblk0 bs=512 count=1 >> $dest_dir/uboot/$2/replace.log 2>&1 + rm -f start.img + echo -e echo -e "\033[1;32mupdate fitImage...\033[0m" + dd if=$dest_dir/fitImage/$2/$kernel_version/fitImage of=/dev/mmcblk0 bs=1M seek=4 count=60 >> $dest_dir/fitImage/$2/$kernel_version/replace.log 2>&1 + dpkg -i $dest_dir/fitImage/$2/$kernel_version/linux-headers-$kernel_version.deb + dpkg -i $dest_dir/fitImage/$2/$kernel_version/linux-image-$kernel_version.deb + elif [ "$1" == "uboot" ]; then + echo -e echo -e "\033[1;32mupdate uboot...\033[0m" + #to preserve the partition table. + dd if=/dev/mmcblk0 of=start.img bs=512 count=1 >> $dest_dir/$1/$2/replace.log 2>&1 + dd if=$dest_dir/$1/$2/fip-all.bin of=/dev/mmcblk0 bs=1M count=4 >> $dest_dir/$1/$2/replace.log 2>&1 + dd if=start.img of=/dev/mmcblk0 bs=512 count=1 >> $dest_dir/$1/$2/replace.log 2>&1 + rm -f start.img + elif [ "$1" == "fitImage" ]; then + echo -e echo -e "\033[1;32mupdate fitImage...\033[0m" + dd if=$dest_dir/$1/$2/$kernel_version/fitImage of=/dev/mmcblk0 bs=1M seek=4 count=60 >> $dest_dir/$1/$2/$kernel_version/replace.log 2>&1 + dpkg -i $dest_dir/$1/$2/$kernel_version/linux-headers-$kernel_version.deb + dpkg -i $dest_dir/$1/$2/$kernel_version/linux-image-$kernel_version.deb + else + echo -e "\033[1;31margs error, exit\033[0m" + fi + + if [ "$?" != "0" ]; then + echo -e "\033[1;31mreplace_uboot_and_fitImage failed\033[0m" + exit 1 + fi + + echo -e "\033[1;32mupdate success, please reboot now\033[0m" +} # replace_uboot_and_fitImage + + +main() { + check_args $1 $2 + detect_and_set_network + get_uboot_and_fitImage $1 $2 + replace_uboot_and_fitImage $1 $2 +} + +main $@ -- Gitee From e7a7546e6ee79ec0bcdd2ecc836c3e3d05f0f63a Mon Sep 17 00:00:00 2001 From: lindongping2027 Date: Tue, 10 Sep 2024 09:48:44 +0800 Subject: [PATCH 4/9] README: add phytium_ota Signed-off-by: lindongping2027 --- README.md | 9 +++++++-- package/phytium-tools/src/phytium_ota | 6 +++--- package/phytium-tools/src/runtime_replace_bootloader.sh | 6 +++--- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 29e18524..39454b79 100644 --- a/README.md +++ b/README.md @@ -153,8 +153,13 @@ LINUX_OVERRIDE_SRCDIR指定了一个本地的内核源码目录,这样就不 (3)开发板运行时,如果需要更换uboot、Image和dtb,可以在飞腾派上执行对应脚本,用法如下: `$ sudo runtime_replace_bootloader.sh all` `$ sudo runtime_replace_bootloader.sh uboot` -`$ sudo runtime_replace_bootloader.sh image` -以上三条命令分别实现了更换uboot+image+dtb、更换uboot和更换image+dtb的功能,要求当前目录下有fip-all.bin和fitImage文件。 +`$ sudo runtime_replace_bootloader.sh fitImage` +以上三条命令分别实现了更换uboot+Image+dtb、更换uboot和更换Image+dtb的功能,要求当前目录下有fip-all.bin和fitImage文件。 +(4)开发板运行时,如果需要远程升级uboot、Image和dtb,可以在飞腾派上执行对应脚本,用法如下: +`$ sudo phytium_ota all latest` +`$ sudo phytium_ota uboot latest` +`$ sudo phytium_ota fitImage latest` +以上三条命令通过远程分别将uboot+Image+dtb、uboot和Image+dtb升级到最新版本。 # 编译内核模块 关于如何编译内核外部模块,可参考https://www.kernel.org/doc/html/latest/kbuild/modules.html diff --git a/package/phytium-tools/src/phytium_ota b/package/phytium-tools/src/phytium_ota index cc783137..98ead4da 100755 --- a/package/phytium-tools/src/phytium_ota +++ b/package/phytium-tools/src/phytium_ota @@ -16,11 +16,11 @@ usage() { echo "Usage: sudo $0 {all|fitImage|uboot} {version}" echo -e " for \033[32muboot\033[0m, {version} can be v1.x or latest for \033[32mfitImage\033[0m and \033[32mall\033[0m, {version} only be latest" - echo " Example 1: $0 uboot v1.x + echo " Example 1: sudo $0 uboot v1.x --update uboot to version v1.x - Example 2: $0 fitImage latest + Example 2: sudo $0 fitImage latest --update fitImage to the latest - Example 3: $0 all latest + Example 3: sudo $0 all latest --update uboot and fit Image to the latest" } diff --git a/package/phytium-tools/src/runtime_replace_bootloader.sh b/package/phytium-tools/src/runtime_replace_bootloader.sh index 029fce66..76fc7e1d 100755 --- a/package/phytium-tools/src/runtime_replace_bootloader.sh +++ b/package/phytium-tools/src/runtime_replace_bootloader.sh @@ -12,8 +12,8 @@ usage() echo " [Option] can be: " echo " all: replace uboot+Image+dtb" echo " uboot: replace uboot" - echo " image: replace Image+dtb" - echo " For example: $0 all" + echo " fitImage: replace Image+dtb" + echo " For example: sudo $0 all" echo " Please make sure that the current directory contains fitImage or fip-all.bin that you want to replace." } @@ -45,7 +45,7 @@ main() dd if=fip-all.bin of=/dev/mmcblk0 bs=1M count=4 >> replace.log 2>&1 dd if=start.img of=/dev/mmcblk0 bs=512 count=1 >> replace.log 2>&1 rm -f start.img - elif [ "$1" == "image" ]; then + elif [ "$1" == "fitImage" ]; then dd if=fitImage of=/dev/mmcblk0 bs=1M seek=4 count=60 >> replace.log 2>&1 else echo "args error, exit" -- Gitee From 96d7ed81f9043cb3d6052c95f36004c20581c659 Mon Sep 17 00:00:00 2001 From: lindongping2027 Date: Wed, 18 Sep 2024 10:51:47 +0800 Subject: [PATCH 5/9] README: add line feed Signed-off-by: lindongping2027 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 39454b79..3fd18010 100644 --- a/README.md +++ b/README.md @@ -156,9 +156,9 @@ LINUX_OVERRIDE_SRCDIR指定了一个本地的内核源码目录,这样就不 `$ sudo runtime_replace_bootloader.sh fitImage` 以上三条命令分别实现了更换uboot+Image+dtb、更换uboot和更换Image+dtb的功能,要求当前目录下有fip-all.bin和fitImage文件。 (4)开发板运行时,如果需要远程升级uboot、Image和dtb,可以在飞腾派上执行对应脚本,用法如下: -`$ sudo phytium_ota all latest` -`$ sudo phytium_ota uboot latest` -`$ sudo phytium_ota fitImage latest` +`$ sudo phytium_ota all latest` +`$ sudo phytium_ota uboot latest` +`$ sudo phytium_ota fitImage latest` 以上三条命令通过远程分别将uboot+Image+dtb、uboot和Image+dtb升级到最新版本。 # 编译内核模块 -- Gitee From f116d81ac98cf029e7e2731c908d1db344001ca8 Mon Sep 17 00:00:00 2001 From: lindongping2027 Date: Mon, 23 Sep 2024 15:13:42 +0800 Subject: [PATCH 6/9] resolve the network security issues reported by openvas Signed-off-by: lindongping2027 --- board/phytium/common/debian-package-installer | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/board/phytium/common/debian-package-installer b/board/phytium/common/debian-package-installer index b8baeb05..7dda5305 100755 --- a/board/phytium/common/debian-package-installer +++ b/board/phytium/common/debian-package-installer @@ -82,7 +82,12 @@ do_distrorfs_second_stage() { # touch .desktop_firstrun touch /root/.desktop_firstlogin - + + #cups disable TLSv1.0 and TLSv1.1 + if [ -f /etc/cups/cupsd.conf ]; then + echo "SSLOptions MinTLS1.2" >> /etc/cups/cupsd.conf + fi + echo remove packages. DEBIAN_FRONTEND=noninteractive apt-get -y remove --purge parole || exit 1 fi @@ -106,6 +111,12 @@ do_distrorfs_second_stage() { echo 'defaults.ctl.!card phytiumpe220xi2' >> /etc/asound.conf fi + if dpkg -s openssh-server &> /dev/null; then + sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config + #ssh: disable weak MAC algorithm + echo "MACs hmac-sha2-256,hmac-sha2-512" >> /etc/ssh/sshd_config + fi + # clean cached packages apt-get clean -- Gitee From efdb88e80bd7e1e47b00b17dfebfacdcccb1e536 Mon Sep 17 00:00:00 2001 From: lindongping Date: Mon, 2 Dec 2024 14:50:15 +0800 Subject: [PATCH 7/9] add hash check for phytium-ota Signed-off-by: lindongping --- .../common/debian-additional_packages_list | 2 +- package/phytium-tools/phytium-tools.mk | 4 + package/phytium-tools/src/check-hash | 107 ++++++++++++++++++ .../fitImage/latest/6.6/fitImage.hash | 1 + .../latest/6.6/linux-headers-6.6.deb.hash | 1 + .../latest/6.6/linux-image-6.6.deb.hash | 1 + .../uboot/5g/fip-all-5g-4GB.bin.hash | 1 + .../uboot/latest/fip-all-optee-2GB.bin.hash | 1 + .../uboot/latest/fip-all-optee-4GB.bin.hash | 1 + .../uboot/msata/fip-all-msata-4GB.bin.hash | 1 + .../uboot/v1.5/fip-all-optee-2GB.bin.hash | 2 + .../uboot/v1.5/fip-all-optee-4GB.bin.hash | 2 + package/phytium-tools/src/phytium_ota | 57 +++++++--- package/phytium-tools/src/quectel-CM | Bin 0 -> 223928 bytes 14 files changed, 164 insertions(+), 17 deletions(-) create mode 100755 package/phytium-tools/src/check-hash create mode 100644 package/phytium-tools/src/phytium-ota-hash/fitImage/latest/6.6/fitImage.hash create mode 100644 package/phytium-tools/src/phytium-ota-hash/fitImage/latest/6.6/linux-headers-6.6.deb.hash create mode 100644 package/phytium-tools/src/phytium-ota-hash/fitImage/latest/6.6/linux-image-6.6.deb.hash create mode 100644 package/phytium-tools/src/phytium-ota-hash/uboot/5g/fip-all-5g-4GB.bin.hash create mode 100644 package/phytium-tools/src/phytium-ota-hash/uboot/latest/fip-all-optee-2GB.bin.hash create mode 100644 package/phytium-tools/src/phytium-ota-hash/uboot/latest/fip-all-optee-4GB.bin.hash create mode 100644 package/phytium-tools/src/phytium-ota-hash/uboot/msata/fip-all-msata-4GB.bin.hash create mode 100644 package/phytium-tools/src/phytium-ota-hash/uboot/v1.5/fip-all-optee-2GB.bin.hash create mode 100644 package/phytium-tools/src/phytium-ota-hash/uboot/v1.5/fip-all-optee-4GB.bin.hash create mode 100644 package/phytium-tools/src/quectel-CM diff --git a/board/phytium/common/debian-additional_packages_list b/board/phytium/common/debian-additional_packages_list index c84c862a..9a4eccb6 100644 --- a/board/phytium/common/debian-additional_packages_list +++ b/board/phytium/common/debian-additional_packages_list @@ -10,5 +10,5 @@ v4l-utils python3-gpiozero avahi-daemon lua5.1 luajit hardlink curl fake-hwclock dphys-swapfile apt-listchanges usb-modeswitch libmtp-runtime rsync htop man-db rng-tools \ ssh-import-id ethtool ntfs-3g pciutils udisks2 zip p7zip-full file \ cifs-utils mkvtoolnix wpasupplicant wireless-tools net-tools \ -vim locales network-manager jq" +vim locales network-manager jq git" diff --git a/package/phytium-tools/phytium-tools.mk b/package/phytium-tools/phytium-tools.mk index ce73552e..08b527b5 100644 --- a/package/phytium-tools/phytium-tools.mk +++ b/package/phytium-tools/phytium-tools.mk @@ -16,6 +16,7 @@ define PHYTIUM_TOOLS_INSTALL_TARGET_CMDS mkdir -p $(TARGET_DIR)/lib/firmware/rtw88/ mkdir -p $(TARGET_DIR)/lib/firmware/rtl_bt/ mkdir -p $(TARGET_DIR)/etc/modprobe.d/ + mkdir -p $(TARGET_DIR)/usr/lib/phytium/ $(INSTALL) -m 755 -D $(@D)/rtlbt/* $(TARGET_DIR)/lib/firmware/rtlbt/ $(INSTALL) -m 755 -D $(@D)/rtk_hciattach $(TARGET_DIR)/usr/bin/ $(INSTALL) -m 755 -D $(@D)/resize.sh $(TARGET_DIR)/usr/bin/ @@ -27,6 +28,9 @@ define PHYTIUM_TOOLS_INSTALL_TARGET_CMDS $(INSTALL) -m 755 -D $(@D)/rtlbt/rtl8821c_fw $(TARGET_DIR)/lib/firmware/rtl_bt/rtl8821c_fw.bin $(INSTALL) -m 755 -D $(@D)/rtw88.conf $(TARGET_DIR)/etc/modprobe.d/ $(INSTALL) -m 755 -D $(@D)/phytium_ota $(TARGET_DIR)/usr/bin/ + $(INSTALL) -m 755 -D $(@D)/quectel-CM $(TARGET_DIR)/usr/bin/ + $(INSTALL) -m 755 -D $(@D)/check-hash $(TARGET_DIR)/usr/bin/ + cp -rf $(@D)/phytium-ota-hash/ $(TARGET_DIR)/usr/lib/phytium/ endef $(eval $(generic-package)) diff --git a/package/phytium-tools/src/check-hash b/package/phytium-tools/src/check-hash new file mode 100755 index 00000000..5a47f49b --- /dev/null +++ b/package/phytium-tools/src/check-hash @@ -0,0 +1,107 @@ +#!/usr/bin/env bash +set -e + +# Helper to check a file matches its known hash +# Call it with: +# $1: the path of the file containing all the expected hashes +# $2: the full path to the temporary file that was downloaded, and +# that is to be checked +# $3: the final basename of the file, to which it will be ultimately +# saved as, to be able to match it to the corresponding hashes +# in the .hash file +# +# Exit codes: +# 0: the hash file exists and the file to check matches all its hashes, +# or the hash file does not exist +# 1: unknown command-line option +# 2: the hash file exists and the file to check does not match at least +# one of its hashes +# 3: the hash file exists and there was no hash to check the file against +# 4: the hash file exists and at least one hash type is unknown + +while getopts :q OPT; do + case "${OPT}" in + q) exec >/dev/null;; + \?) exit 1;; + esac +done +shift $((OPTIND-1)) + +h_file="${1}" +file="${2}" +base="${3}" + +# Bail early if no hash to check +if [ -z "${h_file}" ]; then + exit 0 +fi +# Does the hash-file exist? +if [ ! -f "${h_file}" ]; then + printf "WARNING: no hash file for %s\n" "${base}" >&2 + exit 0 +fi + +# Check one hash for a file +# $1: algo hash +# $2: known hash +# $3: file (full path) +check_one_hash() { + _h="${1}" + _known="${2}" + _file="${3}" + + # Note: md5 is supported, but undocumented on purpose. + # Note: sha3 is not supported, since there is currently no implementation + # (the NIST has yet to publish the parameters). + case "${_h}" in + md5|sha1) ;; + sha224|sha256|sha384|sha512) ;; + *) # Unknown hash, exit with error + printf "ERROR: unknown hash '%s' for '%s'\n" \ + "${_h}" "${base}" >&2 + exit 4 + ;; + esac + + # Do the hashes match? + _hash=$( ${_h}sum "${_file}" |cut -d ' ' -f 1 ) + if [ "${_hash}" = "${_known}" ]; then + printf "%s: OK (%s: %s)\n" "${base}" "${_h}" "${_hash}" + return 0 + fi + + printf "ERROR: %s has wrong %s hash:\n" "${base}" "${_h}" >&2 + printf "ERROR: expected: %s\n" "${_known}" >&2 + printf "ERROR: got : %s\n" "${_hash}" >&2 + printf "ERROR: Incomplete download, or man-in-the-middle (MITM) attack\n" >&2 + + exit 2 +} + +# Do we know one or more hashes for that file? +nb_checks=0 +while read t h f; do + case "${t}" in + ''|'#'*) + # Skip comments and empty lines + continue + ;; + *) + if [ "${f}" = "${base}" ]; then + check_one_hash "${t}" "${h}" "${file}" + : $((nb_checks++)) + fi + ;; + esac +done <"${h_file}" + +if [ ${nb_checks} -eq 0 ]; then + case " ${BR_NO_CHECK_HASH_FOR} " in + *" ${base} "*) + # File explicitly has no hash + exit 0 + ;; + esac + printf "ERROR: No hash found for %s\n" "${base}" >&2 + exit 3 +fi diff --git a/package/phytium-tools/src/phytium-ota-hash/fitImage/latest/6.6/fitImage.hash b/package/phytium-tools/src/phytium-ota-hash/fitImage/latest/6.6/fitImage.hash new file mode 100644 index 00000000..fcd7f715 --- /dev/null +++ b/package/phytium-tools/src/phytium-ota-hash/fitImage/latest/6.6/fitImage.hash @@ -0,0 +1 @@ +sha256 7227f5d936e30ada647381b6b07010bc837d2167399c35abdf02c24255743fa9 fitImage diff --git a/package/phytium-tools/src/phytium-ota-hash/fitImage/latest/6.6/linux-headers-6.6.deb.hash b/package/phytium-tools/src/phytium-ota-hash/fitImage/latest/6.6/linux-headers-6.6.deb.hash new file mode 100644 index 00000000..54f25259 --- /dev/null +++ b/package/phytium-tools/src/phytium-ota-hash/fitImage/latest/6.6/linux-headers-6.6.deb.hash @@ -0,0 +1 @@ +sha256 a2e62b56a15589ef3952e70b96d9d64056bf0fc16170f92bd22c5fc6f54b0d56 linux-headers-6.6.deb diff --git a/package/phytium-tools/src/phytium-ota-hash/fitImage/latest/6.6/linux-image-6.6.deb.hash b/package/phytium-tools/src/phytium-ota-hash/fitImage/latest/6.6/linux-image-6.6.deb.hash new file mode 100644 index 00000000..4e034910 --- /dev/null +++ b/package/phytium-tools/src/phytium-ota-hash/fitImage/latest/6.6/linux-image-6.6.deb.hash @@ -0,0 +1 @@ +sha256 daaa543f0e58eb7886dc1585874828d5734b5b6e913f870134f53dc0e02e846d linux-image-6.6.deb diff --git a/package/phytium-tools/src/phytium-ota-hash/uboot/5g/fip-all-5g-4GB.bin.hash b/package/phytium-tools/src/phytium-ota-hash/uboot/5g/fip-all-5g-4GB.bin.hash new file mode 100644 index 00000000..824c2b11 --- /dev/null +++ b/package/phytium-tools/src/phytium-ota-hash/uboot/5g/fip-all-5g-4GB.bin.hash @@ -0,0 +1 @@ +sha256 1ebc3dc344886ffb2029a33fd3fa4a9f22ff981255cb69835e84f247cfe60f11 fip-all-5g-4GB.bin diff --git a/package/phytium-tools/src/phytium-ota-hash/uboot/latest/fip-all-optee-2GB.bin.hash b/package/phytium-tools/src/phytium-ota-hash/uboot/latest/fip-all-optee-2GB.bin.hash new file mode 100644 index 00000000..8702bafc --- /dev/null +++ b/package/phytium-tools/src/phytium-ota-hash/uboot/latest/fip-all-optee-2GB.bin.hash @@ -0,0 +1 @@ +sha256 5ea39ddf7bcfd8ff1b3448fa0c71847a9c1e653f2cdd2cfc2451f813d8c946e4 fip-all-optee-2GB.bin diff --git a/package/phytium-tools/src/phytium-ota-hash/uboot/latest/fip-all-optee-4GB.bin.hash b/package/phytium-tools/src/phytium-ota-hash/uboot/latest/fip-all-optee-4GB.bin.hash new file mode 100644 index 00000000..d2e913ab --- /dev/null +++ b/package/phytium-tools/src/phytium-ota-hash/uboot/latest/fip-all-optee-4GB.bin.hash @@ -0,0 +1 @@ +sha256 7cc1bed898467814362553ace772ece3031819d1f62616ea6a66981014e72567 fip-all-optee-4GB.bin diff --git a/package/phytium-tools/src/phytium-ota-hash/uboot/msata/fip-all-msata-4GB.bin.hash b/package/phytium-tools/src/phytium-ota-hash/uboot/msata/fip-all-msata-4GB.bin.hash new file mode 100644 index 00000000..e5519f6a --- /dev/null +++ b/package/phytium-tools/src/phytium-ota-hash/uboot/msata/fip-all-msata-4GB.bin.hash @@ -0,0 +1 @@ +sha256 991d1cacff5a732a153eafb28c8c35d58e4455a82512c70c92e94a489bf02ac8 fip-all-msata-4GB.bin diff --git a/package/phytium-tools/src/phytium-ota-hash/uboot/v1.5/fip-all-optee-2GB.bin.hash b/package/phytium-tools/src/phytium-ota-hash/uboot/v1.5/fip-all-optee-2GB.bin.hash new file mode 100644 index 00000000..6da303ba --- /dev/null +++ b/package/phytium-tools/src/phytium-ota-hash/uboot/v1.5/fip-all-optee-2GB.bin.hash @@ -0,0 +1,2 @@ +sha256 5ea39ddf7bcfd8ff1b3448fa0c71847a9c1e653f2cdd2cfc2451f813d8c946e4 fip-all-optee-2GB.bin + diff --git a/package/phytium-tools/src/phytium-ota-hash/uboot/v1.5/fip-all-optee-4GB.bin.hash b/package/phytium-tools/src/phytium-ota-hash/uboot/v1.5/fip-all-optee-4GB.bin.hash new file mode 100644 index 00000000..80d83247 --- /dev/null +++ b/package/phytium-tools/src/phytium-ota-hash/uboot/v1.5/fip-all-optee-4GB.bin.hash @@ -0,0 +1,2 @@ +sha256 7cc1bed898467814362553ace772ece3031819d1f62616ea6a66981014e72567 fip-all-optee-4GB.bin + diff --git a/package/phytium-tools/src/phytium_ota b/package/phytium-tools/src/phytium_ota index 98ead4da..98d392df 100755 --- a/package/phytium-tools/src/phytium_ota +++ b/package/phytium-tools/src/phytium_ota @@ -11,10 +11,11 @@ download_dir="/usr/local/phytium-libs" download_site="https://gitee.com/phytium_embedded/phytium-rogue-umlibs.git" dest_dir="/usr/local/phytium-ota" +hash_dir="/usr/lib/phytium/phytium-ota-hash" usage() { echo "Usage: sudo $0 {all|fitImage|uboot} {version}" - echo -e " for \033[32muboot\033[0m, {version} can be v1.x or latest + echo -e " for \033[32muboot\033[0m, {version} can be v1.x or latest, etc for \033[32mfitImage\033[0m and \033[32mall\033[0m, {version} only be latest" echo " Example 1: sudo $0 uboot v1.x --update uboot to version v1.x @@ -47,8 +48,8 @@ check_args() exit 1 fi - if [[ "$2" != "latest" && ! "$2" =~ ^v1\.[0-9]+$ ]]; then - echo -e "\033[1;31mError: Second argument must be 'latest' or 'v1.x'.\033[0m" + if [[ "$2" != "latest" && "$2" != "msata" && "$2" != "5g" && ! "$2" =~ ^v1\.[0-9]+$ ]]; then + echo -e "\033[1;31mError: Second argument must be 'latest', "msata", "5g" or 'v1.x'.\033[0m" exit 1 fi } @@ -58,11 +59,21 @@ mem_info=$(echo $mem_info | awk '{print $2}') memory_total=$(( mem_info * 1024 )) # compare to 3GB if [ $memory_total -ge 3221225472 ]; then - mem=4 + mem=4GB else - mem=2 + mem=2GB fi -#echo $mem + +# uboot +uboot_type=optee +if [ "$2" == "msata" ]; then + uboot_type=msata +elif [ "$2" == "5g" ]; then + uboot_type=5g +fi +uboot_bin="fip-all-$uboot_type-$mem.bin" + +uboot_hash="$uboot_bin.hash" # get kernel version kernel_version=$(uname -r) @@ -124,7 +135,7 @@ get_uboot_and_fitImage() { git clone -b develop --depth 1 $download_site $download_dir if [ "$?" != "0" ]; then - echo -e "\033[1;33mget uboot and fitImage failed\033[0m" + echo -e "\033[1;31mget uboot and fitImage failed\033[0m" exit 1 fi echo -e "\033[1;32mget uboot and fitImage success\033[0m" @@ -135,29 +146,43 @@ get_uboot_and_fitImage() { # replace_uboot_and_fitImage # $1: can be all\uboot\fitImage -# $2: can be latest\v1.5,etc +# $2: can be latest\v1.5\msata, etc replace_uboot_and_fitImage() { if [ "$1" == "all" ]; then - echo -e echo -e "\033[1;32mupdate uboot...\033[0m" - #to preserve the partition table. + echo -e "\033[1;32mcheck hash...\033[0m" + check-hash $hash_dir/uboot/$2/$uboot_hash $dest_dir/uboot/$2/$uboot_bin $uboot_bin + check-hash $hash_dir/fitImage/$2/$kernel_version/fitImage.hash $dest_dir/fitImage/$2/$kernel_version/fitImage fitImage + check-hash $hash_dir/fitImage/$2/$kernel_version/linux-headers-$kernel_version.deb.hash $dest_dir/fitImage/$2/$kernel_version/linux-headers-$kernel_version.deb linux-headers-$kernel_version.deb + check-hash $hash_dir/fitImage/$2/$kernel_version/linux-image-$kernel_version.deb.hash $dest_dir/fitImage/$2/$kernel_version/linux-image-$kernel_version.deb linux-image-$kernel_version.deb + + echo -e "\033[1;32mupdate uboot...\033[0m" + # to preserve the partition table. dd if=/dev/mmcblk0 of=start.img bs=512 count=1 >> $dest_dir/uboot/$2/replace.log 2>&1 - dd if=$dest_dir/uboot/$2/fip-all.bin of=/dev/mmcblk0 bs=1M count=4 >> $dest_dir/uboot/$2/replace.log 2>&1 + dd if=$dest_dir/uboot/$2/$uboot_bin of=/dev/mmcblk0 bs=1M count=4 >> $dest_dir/uboot/$2/replace.log 2>&1 dd if=start.img of=/dev/mmcblk0 bs=512 count=1 >> $dest_dir/uboot/$2/replace.log 2>&1 rm -f start.img - echo -e echo -e "\033[1;32mupdate fitImage...\033[0m" + echo -e "\033[1;32mupdate fitImage...\033[0m" dd if=$dest_dir/fitImage/$2/$kernel_version/fitImage of=/dev/mmcblk0 bs=1M seek=4 count=60 >> $dest_dir/fitImage/$2/$kernel_version/replace.log 2>&1 dpkg -i $dest_dir/fitImage/$2/$kernel_version/linux-headers-$kernel_version.deb dpkg -i $dest_dir/fitImage/$2/$kernel_version/linux-image-$kernel_version.deb elif [ "$1" == "uboot" ]; then - echo -e echo -e "\033[1;32mupdate uboot...\033[0m" - #to preserve the partition table. + echo -e "\033[1;32mcheck hash...\033[0m" + check-hash $hash_dir/uboot/$2/$uboot_hash $dest_dir/uboot/$2/$uboot_bin $uboot_bin + + echo -e "\033[1;32mupdate uboot...\033[0m" + # to preserve the partition table. dd if=/dev/mmcblk0 of=start.img bs=512 count=1 >> $dest_dir/$1/$2/replace.log 2>&1 - dd if=$dest_dir/$1/$2/fip-all.bin of=/dev/mmcblk0 bs=1M count=4 >> $dest_dir/$1/$2/replace.log 2>&1 + dd if=$dest_dir/$1/$2/$uboot_bin of=/dev/mmcblk0 bs=1M count=4 >> $dest_dir/$1/$2/replace.log 2>&1 dd if=start.img of=/dev/mmcblk0 bs=512 count=1 >> $dest_dir/$1/$2/replace.log 2>&1 rm -f start.img elif [ "$1" == "fitImage" ]; then - echo -e echo -e "\033[1;32mupdate fitImage...\033[0m" + echo -e "\033[1;32mcheck hash...\033[0m" + check-hash $hash_dir/fitImage/$2/$kernel_version/fitImage.hash $dest_dir/fitImage/$2/$kernel_version/fitImage fitImage + check-hash $hash_dir/fitImage/$2/$kernel_version/linux-headers-$kernel_version.deb.hash $dest_dir/fitImage/$2/$kernel_version/linux-headers-$kernel_version.deb linux-headers-$kernel_version.deb + check-hash $hash_dir/fitImage/$2/$kernel_version/linux-image-$kernel_version.deb.hash $dest_dir/fitImage/$2/$kernel_version/linux-image-$kernel_version.deb linux-image-$kernel_version.deb + + echo -e "\033[1;32mupdate fitImage...\033[0m" dd if=$dest_dir/$1/$2/$kernel_version/fitImage of=/dev/mmcblk0 bs=1M seek=4 count=60 >> $dest_dir/$1/$2/$kernel_version/replace.log 2>&1 dpkg -i $dest_dir/$1/$2/$kernel_version/linux-headers-$kernel_version.deb dpkg -i $dest_dir/$1/$2/$kernel_version/linux-image-$kernel_version.deb diff --git a/package/phytium-tools/src/quectel-CM b/package/phytium-tools/src/quectel-CM new file mode 100644 index 0000000000000000000000000000000000000000..ae7746075cd214121e9eeed7d89b6c5c1002f0e2 GIT binary patch literal 223928 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%0D0H#5q6j^O>R7`!it8OLMJ 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 Date: Tue, 21 Jan 2025 09:40:40 +0800 Subject: [PATCH 8/9] README: add phytium-firstlogin Signed-off-by: lindongping --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3fd18010..4cf68a95 100644 --- a/README.md +++ b/README.md @@ -150,16 +150,18 @@ LINUX_OVERRIDE_SRCDIR指定了一个本地的内核源码目录,这样就不 如果编译的是phytiumpi_desktop_defconfig,使用第一条命令; 如果编译的是phytiumpi_defconfig,使用第二条命令。 (2)SD卡接到开发板,启动开发板电源,启动文件系统 -(3)开发板运行时,如果需要更换uboot、Image和dtb,可以在飞腾派上执行对应脚本,用法如下: + (3)开发板第一次启动时,系统首先运行phytium-firstlogin程序。该程序的主要功能包括设置root用户密码、创建普通用户、连接网络、设置区域和设置语言环境等。详细见[phytium-firstlogin介绍与使用 +](https://gitee.com/phytium_embedded/phytium-pi-os/wikis/phytium-firstlogin%E4%BB%8B%E7%BB%8D%E4%B8%8E%E4%BD%BF%E7%94%A8) +(4)开发板运行时,如果需要更换uboot、Image和dtb,可以在飞腾派上执行对应脚本,用法如下: `$ sudo runtime_replace_bootloader.sh all` `$ sudo runtime_replace_bootloader.sh uboot` `$ sudo runtime_replace_bootloader.sh fitImage` 以上三条命令分别实现了更换uboot+Image+dtb、更换uboot和更换Image+dtb的功能,要求当前目录下有fip-all.bin和fitImage文件。 -(4)开发板运行时,如果需要远程升级uboot、Image和dtb,可以在飞腾派上执行对应脚本,用法如下: +(5)开发板运行时,如果需要远程升级uboot、Image和dtb,可以在飞腾派上执行对应脚本,用法如下: `$ sudo phytium_ota all latest` `$ sudo phytium_ota uboot latest` `$ sudo phytium_ota fitImage latest` -以上三条命令通过远程分别将uboot+Image+dtb、uboot和Image+dtb升级到最新版本。 +以上三条命令通过远程分别将uboot+Image+dtb、uboot和Image+dtb升级到最新版本。 # 编译内核模块 关于如何编译内核外部模块,可参考https://www.kernel.org/doc/html/latest/kbuild/modules.html -- Gitee From fa58e9548211f407a044d6ee0181c25e40c442fe Mon Sep 17 00:00:00 2001 From: lindongping Date: Wed, 22 Jan 2025 11:07:04 +0800 Subject: [PATCH 9/9] defconfig: delete vpu-lib package Signed-off-by: lindongping --- configs/phytiumpi_defconfig | 4 ---- configs/phytiumpi_desktop_defconfig | 4 ---- 2 files changed, 8 deletions(-) diff --git a/configs/phytiumpi_defconfig b/configs/phytiumpi_defconfig index c35a195a..7012aa06 100644 --- a/configs/phytiumpi_defconfig +++ b/configs/phytiumpi_defconfig @@ -59,10 +59,6 @@ BR2_ROOTFS_DEVICE_TABLE="system/device_table.txt board/phytium/common/device_tab BR2_PACKAGE_BUSYBOX=n -# Vpu-lib -BR2_PACKAGE_VPU_LIB=y -BR2_PACKAGE_VPU_LIB_CPU_MODEL="e2000" - # Phytium_tools BR2_PACKAGE_PHYTIUM_TOOLS=y diff --git a/configs/phytiumpi_desktop_defconfig b/configs/phytiumpi_desktop_defconfig index 6ed2191b..ec39ff68 100644 --- a/configs/phytiumpi_desktop_defconfig +++ b/configs/phytiumpi_desktop_defconfig @@ -70,10 +70,6 @@ BR2_PACKAGE_ROOTFS_DESKTOP=y BR2_ROOTFS_DEVICE_TABLE="system/device_table.txt board/phytium/common/desktop_device_table.txt" BR2_PACKAGE_BUSYBOX=n -# Vpu-lib -BR2_PACKAGE_VPU_LIB=y -BR2_PACKAGE_VPU_LIB_CPU_MODEL="e2000" - # util-linux BR2_PACKAGE_UTIL_LINUX=y BR2_PACKAGE_UTIL_LINUX_BINARIES=y -- Gitee