--- b6p/rootfs/usr/sbin/aurora-modem 2026-09-30 22:24:42.244902667 +0600 +++ b7/b7c/rootfs/usr/sbin/aurora-modem 2026-10-02 16:02:56.501057243 +0600 @@ -8,6 +8,10 @@ # stop: disconnect -> stop MM (+qmi-proxy) -> BAM-DMUX suspended -> SIGTERM rmtfs (rmtfs writes 'stop' -> MSS offline) -> rmtfs exits last. # Never writes eMMC/NV: rmtfs -r keeps EFS writes in RAM; refuses to start unless modemst1/2, fsc, fsg, persist, mmcblk0 are getro=1 and /firmware is ro. # Does not need a valid wall clock (all timing from /proc/uptime). No automatic MSS/rmtfs retry. +# B7C: time bootstrap. At fixed points of start (QMI_READY, SIM_READY, RADIO_ONLINE, each REG transition, REGISTERED, BEARER_CONNECTED) logs QMI DMS +# + 3GPP NITZ. The first valid DMS (>= 2026, >= image build, plausible, |DMS-NITZ| < 2 s; NITZ required unless TIME_REQUIRE_NITZ=0) steps +# CLOCK_REALTIME once with date -s (never backwards, never RTC). After START OK, a background SNTP check (VNIIFTRI, query only) logs the residual +# and steps only if |offset| >= TIME_NTP_STEP s. No slew/discipline: STA_UNSYNC stays set, so the kernel RTC_SYSTOHC 11-min RTC write never engages. # Exit: 0 ok, 1 failure/timeout (start rolls back with a full stop unless MODEM_KEEP_ON_FAIL=1), 2 usage/config error. CONF=${MODEM_CONF:-/etc/aurora/modem.conf} @@ -21,6 +25,7 @@ T_RMTFS=5 T_MSS=60 T_QMI_DEV=30 T_QMI=45 T_SIM=60 T_ONLINE=30 T_REG=180 T_MM_DETECT=150 T_MM_REG=60 T_CONNECT=150 T_DISCONNECT=60 T_DISABLE=30 T_MM_EXIT=30 T_BAM_IDLE=15 T_MSS_STOP=30 T_RMTFS_EXIT=15 APN= USER= PASSWORD= AUTH= IP_TYPE=ipv4 REG_TIMEOUT=600 ROUTES= KEEP=${MODEM_KEEP_ON_FAIL:-0} +TIME_BOOTSTRAP=1 TIME_REQUIRE_NITZ=1 TIME_MAX_DMS_NITZ=2 TIME_NTP="ntp1.vniiftri.ru ntp2.vniiftri.ru ntp3.vniiftri.ru" TIME_NTP_STEP=1 [ -f "$CONF" ] && . "$CONF" MODEM_APN=$APN MODEM_USER=$USER MODEM_PASSWORD=$PASSWORD MODEM_AUTH=$AUTH MODEM_IP_TYPE=${IP_TYPE:-ipv4} MODEM_TEST_ROUTES=$ROUTES T_REG=${REG_TIMEOUT:-600} MODEM_KEEP_ON_FAIL=${MODEM_KEEP_ON_FAIL:-$KEEP} @@ -90,7 +95,7 @@ "$(qv 'EUTRA Absolute RF Channel Number' $D/qmi-cl.txt)" "$(qv 'Serving Cell ID' $D/qmi-cl.txt)" "$SIT" "$SIE" \ "$(qv 'Tracking Area Code' $D/qmi-cl.txt)" "$(qv 'Global Cell ID' $D/qmi-cl.txt)" "$(qv RSRP $D/qmi-sig.txt)" "$(qv RSRQ $D/qmi-sig.txt)" "$(qv SNR $D/qmi-sig.txt)" >> $T log "REG t+$(since $t0)s: $RG / PS $PS / $ST ($TS, $DM) $PL pci=$(qv 'Serving Cell ID' $D/qmi-cl.txt) si=$SIT/$SIE cl=$(qv 'Tracking Area Code' $D/qmi-cl.txt)/$(qv 'Global Cell ID' $D/qmi-cl.txt)" - last=$key + last=$key; tprobe "REG:$RG/$PS" fi echo $nd > $D/reg-denied-count [ "$RG" = registered ] && [ "$PS" = attached ] && return 0 @@ -100,6 +105,55 @@ } bearer_field() { awk -F': ' -v k="$1" '$1 ~ " "k"$" {gsub(/ /,"",$2); print $2; exit}' $BEARER.txt; } +# --- B7C time bootstrap. Variables are _t-prefixed: tprobe runs inside reg_sm (n, T, last, t0 belong to it). +NTPBB=/usr/libexec/aurora/busybox; GPS_EPOCH=315964800; Y2026=1767225600 +time_floor() { _tf=$(cat /etc/aurora/build-epoch 2>/dev/null); case "$_tf" in ''|*[!0-9]*) _tf=$Y2026;; esac; [ $_tf -lt $Y2026 ] && _tf=$Y2026; echo $_tf; } +# tprobe PHASE: read DMS + NITZ (raw kept in $D/time-probe-N.*), validate, log; first VALID sample while the clock is still pre-build -> tset +tprobe() { + [ "$TIME_BOOTSTRAP" = 1 ] || return 0 + _tn=$(( $(cat $D/time-probes 2>/dev/null || echo 0) + 1 )); echo $_tn > $D/time-probes; _tp=$D/time-probe-$_tn + _u0=$(now); timeout 5 $Q --dms-get-time > $_tp.dms 2>&1; _u1=$(now); timeout 5 $Q --nas-get-network-time > $_tp.nitz 2>&1; _u2=$(now) + _ms=$(sed -n "s/.*System time: '\([0-9]*\).*/\1/p" $_tp.dms); _nd=$(qv '3GPP Date' $_tp.nitz); _nt=$(qv '3GPP Time' $_tp.nitz) + _ne=; [ -n "$_nd" ] && [ -n "$_nt" ] && _ne=$(date -u -d "$_nd $_nt" +%s 2>/dev/null); _clk=$(date -u +%s) + # -> "VERDICT dms_utc dms_uptime dms-nitz reason"; dms-nitz compares DMS projected to the NITZ read instant with whole-second NITZ + _v=$(awk -v ms="$_ms" -v g=$GPS_EPOCH -v u0=$_u0 -v u1=$_u1 -v u2=$_u2 -v ne="$_ne" -v fl=$(time_floor) -v y=$Y2026 -v rn=$TIME_REQUIRE_NITZ -v mx=$TIME_MAX_DMS_NITZ 'BEGIN{ + if (ms !~ /^[0-9]+$/) { print "INVALID - - - dms-unreadable"; exit } + dms=ms/1000+g; at=(u0+u1)/2; r="ok"; d="-" + if (dms < y) r="year<2026"; else if (dms < fl) r="before-build"; else if (dms > fl+5*365*86400) r="implausible" + if (ne ~ /^[0-9]+$/) { dd=dms+((u1+u2)/2-at)-ne; d=sprintf("%+.2f", dd); if (r=="ok" && (dd<-mx || dd>mx)) r="dms-nitz-mismatch" } + else if (r=="ok" && rn==1) r="no-nitz" + printf "%s %.3f %.2f %s %s\n", (r=="ok" ? "VALID" : "INVALID"), dms, at, d, r }') + set -- "$1" $_v + printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' $_tn "$1" $_u0 "${_ms:--}" "$3" "${_nd:--}T${_nt:--}" "$5" $_clk "$2/$6" >> $D/time.tsv + log "TIME probe $_tn [$1]: dms_ms=${_ms:-none} dms_utc=$3 nitz=${_nd:-none} ${_nt} dms-nitz=$5 clock=$_clk -> $2 ($6)" + [ "$2" = VALID ] && [ ! -f $D/time-set ] && [ $_clk -lt $(time_floor) ] && tset "$1" $3 $4 $5 + return 0; } +# tset PHASE DMS_UTC DMS_UPTIME DIFF: step CLOCK_REALTIME once, landing on a whole UTC second (uptime-based wait, no fixed bias correction) +tset() { + _N=$(awk -v d=$2 -v a=$3 -v u=$(now) 'BEGIN{printf "%d", int(d+(u-a))+1}') + _w=$(awk -v N=$_N -v d=$2 -v a=$3 -v u=$(now) 'BEGIN{w=N-(d+(u-a)); printf "%.3f", (w>0 ? w : 0)}') + sleep $_w; _old=$(date -u +%s); [ $_N -gt $_old ] || { log "TIME SET skipped: target $_N not after clock $_old (never backwards)"; return 0; } + date -u -s @$_N > /dev/null 2>&1; _rc=$? + [ $_rc = 0 ] && echo "$_N $(now) $1 qmi-dms" > $D/time-set + log "TIME SET rc=$_rc source=qmi-dms phase=$1 old=$_old ($(date -u -d @$_old '+%F %T')) new=$_N ($(date -u '+%F %T')) uptime=$(now) dms-nitz=$4 validation=VALID rtc=untouched"; } +# after START OK, background: resolve VNIIFTRI via bearer DNS, /32 routes via addroute (removed by stop/disconnect), ntpd -w (query only) +time_sntp() { + [ "$TIME_BOOTSTRAP" = 1 ] && [ -x $NTPBB ] && [ -n "$TIME_NTP" ] || { log "SNTP: skipped (bootstrap=$TIME_BOOTSTRAP bin=$([ -x $NTPBB ] && echo yes || echo no))"; return 0; } + . $D/ipcfg 2>/dev/null || return 0; _dns=${DNS%%,*}; _ips= + for _h in $TIME_NTP; do _ip=$(nslookup $_h $_dns 2>/dev/null | awk '/^Name:/{f=1} f && /^Address/{print $2; exit}') + case "$_ip" in [0-9]*.[0-9]*.[0-9]*.[0-9]*) addroute $_ip && _ips="$_ips $_ip";; *) log "SNTP: $_h not resolved via $_dns";; esac; done + [ -n "$_ips" ] || return 0 + _a=; for _ip in $_ips; do _a="$_a -p $_ip"; done + timeout 15 $NTPBB ntpd -w -d $_a > $D/sntp.txt 2>&1 + _b=$(awk '/reply from/{o="";d=""; for(i=1;i<=NF;i++){ if($i~/^offset:/) o=substr($i,8); if($i~/^delay:/) d=substr($i,7) } if(o!="" && (bd=="" || d+0 0 = clock behind)" + [ -n "$_b" ] || return 0 + if [ "$(awk -v o=${_b%% *} -v s=$TIME_NTP_STEP 'BEGIN{print (o>=s || o<=-s)}')" = 1 ]; then + _old=$(date -u +%s); _N=$(awk -v c=$_old -v o=${_b%% *} 'BEGIN{printf "%d", c+o+0.5}'); date -u -s @$_N > /dev/null 2>&1 + log "SNTP STEP rc=$? old=$_old new=$_N (|offset| >= ${TIME_NTP_STEP}s) rtc=untouched" + else log "SNTP: |offset| < ${TIME_NTP_STEP}s - clock kept (no slew: STA_UNSYNC stays, RTC_SYSTOHC never engages)"; fi; } + check_safety() { for p in modemst1 modemst2 fsc fsg persist; do d=/dev/disk/by-partlabel/$p; [ -e $d ] || return 1 @@ -171,9 +225,9 @@ setstate QMI_READY else t=$(now); wait_for $T_QMI 1 qmi_ok || fail_start "QMI DMS not answering after ${T_QMI}s" - setstate QMI_READY; log "DMS $(grep -o "Mode: '[^']*'" $D/qmi-mode.txt) after $(since $t)s" + setstate QMI_READY; log "DMS $(grep -o "Mode: '[^']*'" $D/qmi-mode.txt) after $(since $t)s"; tprobe QMI_READY t=$(now); wait_for $T_SIM 1 sim_ready || fail_start "SIM/USIM not ready after ${T_SIM}s ($(grep -E "Card state|Application state" $D/qmi-uim.txt | head -2 | tr -s ' \t\n' ' '))" - setstate SIM_READY; log "UIM card present, USIM app ready after $(since $t)s" + setstate SIM_READY; log "UIM card present, USIM app ready after $(since $t)s"; tprobe SIM_READY if ! grep -q "Mode: 'online'" $D/qmi-mode.txt; then log "SET: $Q --dms-set-operating-mode=online (retried only on DeviceNotReady, <= ${T_ONLINE}s)" rm -f $D/qmi-set.err; t=$(now); wait_for $T_ONLINE 1 set_online || fail_start "SET online: DeviceNotReady for ${T_ONLINE}s" @@ -181,13 +235,13 @@ qmi_ok && grep -q "Mode: 'online'" $D/qmi-mode.txt || fail_start "mode not online after SET" log "DMS online after $(since $t)s" fi - setstate RADIO_ONLINE + setstate RADIO_ONLINE; tprobe RADIO_ONLINE t=$(now); reg_sm $t; rr=$? [ $rr = 2 ] && fail_start "LOCAL FAILURE during registration: $(cat $D/reg-fail)" [ $rr = 1 ] && fail_start "$(cat $D/reg-fail)" log "registered: $(grep -oE "(MCC|MNC|Description): '[^']*'" $D/qmi-ss.txt | tr '\n' ' ')after $(since $t)s" - setstate REGISTERED + setstate REGISTERED; tprobe REGISTERED killall qmi-proxy 2>/dev/null; wait_for 5 0.2 sh -c '! pidof qmi-proxy >/dev/null' fi @@ -237,6 +291,7 @@ ip route | grep -q "^default" && log "WARNING: a default route exists (not created by us)" setstate BEARER_CONNECTED log "=== START OK in $(since $T0)s ($IF $A/$PF, routes: $(tr '\n' ' ' < $ROUTES))" + tprobe BEARER_CONNECTED; ( time_sntp ) & } do_stop() { @@ -324,6 +379,7 @@ echo "bam-dmux: $(bam_pm) active_ms=$(cat $BAM/power/runtime_active_time 2>/dev/null) suspended_ms=$(cat $BAM/power/runtime_suspended_time 2>/dev/null) control=$(cat $BAM/power/control 2>/dev/null)" echo "usb0: $(ip -4 addr show usb0 | awk '/inet /{print $2}') route-to-host: $(ip route get 172.16.42.2 2>&1 | head -1)" echo "emmc: $(awk '{print "writes_completed="$5" sectors_written="$7}' /sys/block/mmcblk0/stat)" + echo "time: $(date -u '+%F %T') UTC; set: $(cat $D/time-set 2>/dev/null || echo no); probes: $(cat $D/time-probes 2>/dev/null || echo 0)" echo "dmesg-errs: $(dmesg | grep -cE 'Failed to resume|Resource temporarily|crash detected|fatal error|[Ww]atchdog|Oops|Kernel panic|Unable to handle')" }