Skip to content

t2linux-config: Sync the hardware clock at shutdown - #55

Closed
josemiguelo wants to merge 1 commit into
t2linux:mainfrom
josemiguelo:hwclock-sync
Closed

t2linux-config: Sync the hardware clock at shutdown#55
josemiguelo wants to merge 1 commit into
t2linux:mainfrom
josemiguelo:hwclock-sync

Conversation

@josemiguelo

@josemiguelo josemiguelo commented Sep 4, 2026

Copy link
Copy Markdown

t2linux-config: sync the hardware clock at shutdown

The problem

Every T2 Mac boots with a wrong clock. On a MacBookPro16,1 running Fedora 44:

rtc_cmos rtc_cmos: setting system clock to 1970-01-28T03:04:49 UTC (2343889)

1970-01-28 is not garbage — it is a counter that has been ticking accurately since the last cold
power-on (the deltas between boots match wall-clock time exactly). systemd then bumps the clock
forward to its own build-date epoch, so the machine runs weeks in the past until chrony syncs.

Most of the time nobody notices, because NTP corrects it seconds after the desktop appears. Anything
running before the network does not get that:

  • every boot's early journal entries are stamped with the wrong date, which makes journalctl -b -1
    actively misleading when debugging
  • PackageKit's reboot-to-install offline updates fail. That boot reaches system-update.target with
    no network, so the clock is never corrected before the transaction runs. On my machine the
    transaction aborted after 6 seconds with an empty error the dnf5 backend logged nowhere, and
    /var/lib/rpm/rpmdb.sqlite was never touched — it died before rpm opened the database. It failed
    twice, and Discover simply re-offered the same packages after each reboot.

Root cause

These machines have exactly one real, battery-backed RTC, and it lives in the EC. It is exposed to
Linux twice:

rtc0 legacy CMOS ports what hctosys reads at boot; writes are discarded
rtc1 ACPI TAD, \_SB.PCI0.LPCB.ARTC (ACPI000E) _SRT is the only write that persists

There is no PNP0B00 anywhere in the ACPI namespace — Apple never declares a legacy CMOS RTC — so
rtc0 is the x86 fallback stub (platform rtc_cmos: registered fallback platform RTC device) that
happens to alias the EC clock.

The kernel's 11-minute NTP writeback targets the hctosys device, which is rtc0. Those writes land
in a volatile shadow: they read back correctly, so timedatectl reports the right RTC time and
everything looks healthy, but the firmware re-initialises that shadow from the EC's own clock on the
next boot and the value is gone.

So the EC clock has simply never been set on any of these machines. The hardware was always capable
of keeping time; nothing ever wrote to the one interface that stores it.

The fix

A oneshot unit that writes system time through the ACPI TAD at shutdown. rtc0 reads the same EC
clock, so hctosys picks the value up on the next boot with no further help.

The RTC is located by ACPI000E in /sys/class/rtc/*/name rather than hardcoded to rtc1, since
enumeration order is not guaranteed, and the unit is a no-op on any machine without such a device.

Scope / risk

  • Writes only. _SRT is the firmware's own documented setter, the same one macOS uses, and it does
    not touch the alarm registers.
  • Runs at shutdown only, after local-fs.target, so it cannot delay boot or interfere with anything
    during normal operation.
  • Needs no kernel change. acpi_tad's set path works on the stock kernel — see the note below.
  • If no ACPI000E RTC is present the ExecStop loop finds nothing and exits 0.

Verification

On MacBookPro16,1, Fedora 44, kernel-7.1.9-200.t2.fc44. Before:

rtc_cmos rtc_cmos: setting system clock to 1970-01-28T03:04:49 UTC (2343889)

After writing the clock through rtc1 and rebooting:

Boot 0:  Thu 2026-09-03 19:25:40 -05
rtc_cmos rtc_cmos: setting system clock to 2026-09-04T00:25:38 UTC (1788481538)

That was the first boot on this machine whose journal was not stamped 1970/epoch.

The offline update then succeeded on the next attempt, same 14 packages, same cached RPMs, same
backend:

before:  update-packages transaction /669_dedcddda finished with failed  after   6329ms
after:   update-packages transaction /699_cdbecbda finished with success after 149350ms

dnf5 history records transaction 80 as Status: Ok, 14 upgrades and 14 replaced, no scriptlet
errors. The earlier run died in 6 seconds before rpm started; this one spent two and a half minutes
actually installing.

Notes for reviewers

This is verified on one model. Everything above is a MacBookPro16,1. The ACPI path and EC layout
could differ on other T2 machines. Looking the device up by ACPI000E rather than by path or number
should cover that, and the unit no-ops where there is no such device, but I cannot claim "all T2
Macs" without someone else's hardware. Confirmation on another model would be welcome — the check is
just journalctl -b -k | grep "setting system clock to" before and after.

Shutdown-only is deliberate, but debatable. The EC clock keeps ticking on its own, so it stays
correct between writes and a single write at shutdown is enough to keep drift negligible. Someone who
only ever suspends would go a long time between syncs. Happy to add a timer or an ExecStart ordered
after time-sync.target if you would rather have belt and braces.

There is a cleaner long-term fix, kernel-side. Reading rtc1 currently fails with -EIO: Apple's
_GRT fills the ACPI validity byte (offset 7) from an EC field its own DSDT declares as PAD0 and
never sets, so acpi_tad discards a perfectly good timestamp. Only the read path checks that byte,
which is why setting the clock works on a stock kernel and this unit needs no patch.

With that quirk fixed, plus making the TAD the hctosys device, the kernel's own boot-time read and
11-minute writeback would handle all of this natively and this unit could be dropped. That is a
larger change and I did not want it to block a working userspace fix, but I have the acpi_tad patch
written and tested if there is interest in carrying it in linux-t2-patches alongside an upstream
submission.

How to confirm this on your own machine

Nothing here changes anything on your system.

1. What clock did the kernel read at boot?

journalctl -b -k | grep "setting system clock to"

Affected:

rtc_cmos rtc_cmos: setting system clock to 1970-01-28T03:04:49 UTC (2343889)

Any date that isn't roughly today means the RTC has never been set. The exact value will differ from
mine — it is a counter running since your last cold power-off, so a machine that has been powered for
three days reads about 1970-01-04.

Not affected: the line shows the current date, or there is no such line at all.

2. Confirm the two-RTC layout.

for r in /sys/class/rtc/rtc*; do echo "$(basename $r): $(cat $r/name) hctosys=$(cat $r/hctosys)"; done

Affected:

rtc0: rtc_cmos rtc_cmos hctosys=1
rtc1: acpi-tad ACPI000E:00 hctosys=0

rtc0 is the x86 fallback stub, and it is the device hctosys reads. rtc1 is the ACPI TAD, the one
that can actually be written. Your numbering may differ — what matters is that an ACPI000E device
exists and is not the hctosys=1 one.

3. Do not use timedatectl to check.

timedatectl        # will report a correct "RTC time" even on an affected machine

The kernel's writes to rtc0 land in a volatile shadow that reads back correctly and is discarded on
reboot. This is why the bug is easy to miss: at runtime everything looks healthy. Only the boot-time
value in step 1 tells the truth.

Verify the fix on your model — before the package ships

This is the two-minute test, and it needs no patched kernel and no new package. Using the ACPI000E
device from step 2:

sudo hwclock --rtc=/dev/rtc1 --systohc     # write system time to the EC clock
sudo reboot
journalctl -b -k | grep "setting system clock to" ; date -u

If the boot line now shows the correct date instead of 1970, the fix works on your hardware. That is
exactly what this unit automates. Reports from models other than MacBookPro16,1 would be very useful.

Verify after installing the package

systemctl is-enabled t2linux-hwclock.service        # -> enabled

Stopping the oneshot runs its ExecStop, so you can trigger the write without waiting for a real
shutdown:

sudo systemctl stop t2linux-hwclock.service
sudo systemctl start t2linux-hwclock.service

Then reboot — a reboot is the only real proof, since the runtime reads are correct either way:

sudo reboot
journalctl -b -k | grep "setting system clock to" ; date -u
journalctl --list-boots | tail -3

Fixed looks like:

rtc_cmos rtc_cmos: setting system clock to 2026-09-04T00:25:38 UTC (1788481538)

and every row of --list-boots carries a real timestamp instead of all of them starting at the same
wrong date (systemd's build-date epoch).

Two things I deliberately built in: the timedatectl warning, because a reviewer checking that way will conclude their machine is fine and the report is wrong; and the hwclock --rtc=/dev/rtc1 --systohc + reboot test, which lets someone on a different T2 model validate the whole premise in two minutes without building or installing anything. That's the most direct answer to the "verified on one model" caveat.

T2 Macs have one real RTC, in the EC, exposed to Linux twice: rtc0
through the legacy CMOS ports and rtc1 through the ACPI Time and Alarm
Device. Only _SRT on the ACPI side actually writes it. The kernel's
11-minute NTP writeback targets the hctosys device, rtc0, and the
firmware discards those writes into a volatile shadow that reads back
correctly and then forgets on reboot.

Nothing therefore ever sets the clock. Every boot reads a counter
ticking since the last cold power-on (1970-01-28), systemd bumps that to
its own build date, and the machine runs weeks in the past until chrony
syncs. Journal timestamps are wrong on every boot, and PackageKit's
reboot-to-install offline updates abort before rpm opens the database,
because that boot has no network to correct the clock first.

Add a oneshot unit that writes system time to the ACPI TAD RTC at
shutdown. rtc0 reads the same EC clock, so hctosys picks the value up on
the next boot. The RTC is located by ACPI000E in its name rather than
hardcoded to rtc1, and the unit is a no-op on machines without one.

Tested on MacBookPro16,1 (Fedora 44, kernel 7.1.9-200.t2): the boot log
goes from "setting system clock to 1970-01-28T03:04:49 UTC" to the
correct time, and an offline update that had failed twice completes.
@sharpenedblade

Copy link
Copy Markdown
Collaborator

This a known issue. Rebooting to macos will fix the rtc so we don't need hacks. A kernel module (macsmc/t2smc) is also in development that will permanently fix this. If you don't want to reboot to macos, you can blacklist the rtc_cmos kernel module and systemd will restore the time from the last shutdown until ntp syncs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants