feat: extend rtc with RtcDateTime
This commit is contained in:
1 parent
8af9a09f89
commit
30188c37d0
2 files changed
+165
-1
No files matched your search
@@ -0,0 +1,65 @@
|
||||
#ifndef FROSTBYTE_RTC_H
|
||||
#define FROSTBYTE_RTC_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/*
|
||||
* CMOS real-time clock driver (MC146818-compatible, ports 0x70/0x71) --
|
||||
* the first source of real wall-clock date/time this kernel has ever had.
|
||||
* Every other notion of "time" in frostbyte (pit.h's tick counter,
|
||||
* threadSleep, userAlarm) is relative to boot, not to the calendar; this
|
||||
* is the piece that lets a process ask "what day is it" at all.
|
||||
*
|
||||
* Honest limitation, same spirit as syscall.c's own documented fork()-less
|
||||
* design: the CMOS century register's index is not standardized (it is
|
||||
* only discoverable via the ACPI FADT table, which this kernel does not
|
||||
* parse), so rtcReadDateTime hardcodes the century to 20 -- correct for
|
||||
* any date from 2000 through 2099, which covers every real boot this
|
||||
* kernel will ever see, but not a documented general-purpose solution.
|
||||
*/
|
||||
|
||||
typedef struct RtcDateTime {
|
||||
int64 year; /* full year, e.g. 2026 */
|
||||
int64 month; /* 1-12 */
|
||||
int64 day; /* 1-31 */
|
||||
int64 hour; /* 0-23, already normalized out of 12-hour/PM if needed */
|
||||
int64 minute; /* 0-59 */
|
||||
int64 second; /* 0-59 */
|
||||
} RtcDateTime;
|
||||
|
||||
/* Converts a single CMOS BCD byte (e.g. 0x59) to its binary value (59).
|
||||
Exposed on its own, like ata.c's ataModelString, because it is a real
|
||||
piece of the conversion pipeline worth testing in isolation from any
|
||||
hardware access. */
|
||||
uint8 rtcBcdToBinary(uint8 bcd);
|
||||
|
||||
/* Reads the current date/time off the CMOS RTC. Polls Status Register A's
|
||||
Update-In-Progress bit (bounded, like ata.c's ataPollTimeout) before
|
||||
each of two back-to-back raw reads and only accepts the result once both
|
||||
raw reads agree byte-for-byte, which rules out a read torn mid-tick by
|
||||
the RTC's own once-a-second update. Returns false only if the two raw
|
||||
reads never agreed within the bounded retry budget (never hangs). */
|
||||
bool rtcReadDateTime(RtcDateTime* out);
|
||||
|
||||
/* Civil calendar date/time -> seconds since the Unix epoch
|
||||
(1970-01-01T00:00:00Z), via Howard Hinnant's days_from_civil algorithm
|
||||
(proleptic Gregorian, correct for any year the RtcDateTime fields above
|
||||
can represent). Pure arithmetic, no hardware access -- deterministic and
|
||||
testable without a real clock. */
|
||||
int64 rtcEpochSeconds(const RtcDateTime* dt);
|
||||
|
||||
/* rtcReadDateTime + rtcEpochSeconds in one call, the usual entry point for
|
||||
anything that just wants "now" as a plain integer (sysTime, syscall.c).
|
||||
Returns 0 if the underlying read failed. */
|
||||
int64 rtcNow(void);
|
||||
|
||||
/* Seconds since the Unix epoch -> civil calendar date/time, the exact
|
||||
inverse of rtcEpochSeconds above (Howard Hinnant's civil_from_days,
|
||||
proleptic Gregorian, same algorithm family as daysFromCivil in rtc.c).
|
||||
Correct for any epochSeconds value representable in int64, including
|
||||
negative ones (dates before 1970). Pure arithmetic, no hardware access
|
||||
-- the piece sfs.h's on-disk mtime/ctime (raw epoch integers) needs to
|
||||
become a human-readable date, e.g. for a shell `ls -l`. */
|
||||
void rtcCivilFromEpoch(int64 epochSeconds, RtcDateTime* out);
|
||||
|
||||
#endif
|
||||
+100
-1
@@ -3,13 +3,78 @@
|
||||
|
||||
#include "types.h"
|
||||
#include "vfs.h"
|
||||
#include "prioq.h"
|
||||
#include "isr.h"
|
||||
#include "signalnum.h"
|
||||
#include "mmap.h"
|
||||
|
||||
/* Default scheduling weight (xv6/CFS-style "nice" niceWeight -- higher runs
|
||||
more often, not longer). threadSetPriority scales relative to this. */
|
||||
#define schedDefaultWeight 1024u
|
||||
|
||||
/* Returned by threadCreate/threadCreateUser when the thread table is full
|
||||
(see threadCapacity) -- never a valid thread id, since id 0 is always the
|
||||
permanent idle thread. Callers must check for this before using the
|
||||
result to index into anything thread-related. */
|
||||
#define threadInvalidId 0xFFFFFFFFu
|
||||
|
||||
/* Capacity of Thread.name, including the terminator. Large enough for every
|
||||
name this kernel currently produces (kernel thread literals, "/bin/..."
|
||||
paths passed to threadCreateUser by spawnUser/doSpawn) with room to
|
||||
spare; longer names are truncated, never overflowed. */
|
||||
#define threadNameMax 32
|
||||
|
||||
/* Capacity of Thread.env below: envMaxVars is how many "NAME=VALUE" slots
|
||||
a process may hold at once, envEntryMax is the max size in bytes of a
|
||||
single slot INCLUDING both the '=' separator and the nul terminator
|
||||
(so "NAME=VALUE\0" must fit in envEntryMax bytes). Generous enough for
|
||||
anything this kernel's own shell/demos actually set (see user/envprint.c)
|
||||
with room to spare, the same "small fixed table, not a growable heap
|
||||
allocation" choice Thread.cwd already made for a single path. */
|
||||
#define envMaxVars 8
|
||||
#define envEntryMax 64
|
||||
|
||||
/* One bit per signal number in Thread.sigPending/sigBlocked, and the size
|
||||
of Thread.sigHandlers[] below -- see signal.h for the actual signal
|
||||
subsystem this supports. Defined here, not signal.h, purely to break the
|
||||
include cycle (signal.h includes sched.h for the Thread type). */
|
||||
#define sigMaxSignals 32
|
||||
|
||||
/* Depth of the per-thread nested-handler context stack (Thread.sigSavedRegs/
|
||||
sigSavedBlocked below) -- how many signal handlers may genuinely interrupt
|
||||
each other (handler A running, a second signal arrives and is not blocked
|
||||
by A's own sa_mask, so handler B runs nested inside A, then A resumes once
|
||||
B returns) before sigConsumePending (signal.c) refuses to deliver a new
|
||||
one and simply leaves it pending. A real kernel bounds this too (rlimit
|
||||
on the signal-frame stack it pushes into user memory); this one has no
|
||||
user-memory signal frame to overflow at all (see signal.h's file comment
|
||||
-- the saved context lives in the Thread struct itself), so the bound
|
||||
exists purely to keep that struct's size fixed and to fail a runaway
|
||||
signal-storm safely (further deliveries just wait) instead of unboundedly
|
||||
growing kernel memory per thread. 4 is generous headroom for anything
|
||||
this kernel's own handlers actually do (none nest more than one deep
|
||||
today -- see user/nesttarget.c) without being large enough to make
|
||||
Thread noticeably bigger than sigMaxSignals-worth of handlers already
|
||||
makes it. */
|
||||
#define sigMaxNestDepth 4
|
||||
|
||||
typedef enum {
|
||||
threadUnused = 0,
|
||||
threadRunnable = 1,
|
||||
threadRunning = 2,
|
||||
threadSleeping = 3,
|
||||
threadDead = 4
|
||||
threadDead = 4,
|
||||
threadBlocked = 5,
|
||||
/* Job-control stop (signal.h's SIGSTOP/SIGTSTP): excluded from
|
||||
schedule()'s ready-queue rebuild exactly like Sleeping/Blocked
|
||||
already are (only threadRunnable is ever inserted), so a stopped
|
||||
thread simply never gets picked until threadContinue moves it back
|
||||
to threadRunnable. Distinct from threadBlocked because it is NOT
|
||||
waiting on a wait-channel condition -- schedWakeChannel must never
|
||||
treat it as a match (see schedWakeChannel), only threadContinue
|
||||
(an explicit SIGCONT, not "the resource became available") ever
|
||||
moves a thread out of this state. */
|
||||
threadStopped = 6
|
||||
} ThreadState;
|
||||
|
||||
typedef void (*ThreadEntry)(void);
|
||||
@@ -19,7 +84,41 @@ typedef struct Thread {
|
||||
uint64 stackBase;
|
||||
uint64 kernelStackTop;
|
||||
uint64 sleepUntil;
|
||||
/* Absolute pitTicks() deadline for a pending alarm(2)-style SIGALRM
|
||||
(0 == no alarm pending), set by threadSetAlarm and consulted once
|
||||
per timer tick by schedule() -- the exact same shape as sleepUntil
|
||||
above, checked in the exact same loop, except expiry raises a real
|
||||
signal (sigRaise, signal.h) against the owning thread instead of
|
||||
just flipping its own ThreadState back to Runnable. Deliberately a
|
||||
plain deadline, not a wait-channel/ThreadState change: unlike
|
||||
threadSleep, a thread with a pending alarm keeps running (or
|
||||
sleeping, or blocked on something unrelated) exactly as it already
|
||||
was right up until the deadline, exactly like real alarm()'s timer
|
||||
ticking down in the background regardless of what the process is
|
||||
doing. */
|
||||
uint64 alarmAt;
|
||||
/* poll.h's sysPoll (syscall.c) uses the same restart-the-syscall park
|
||||
pattern sysRead/sysWrite/sysWait already use, but a poll call can be
|
||||
woken by any one of several unrelated fds, not one -- so instead of
|
||||
parking on a single fd's own wait channel (vfsBlockingChannel),
|
||||
sysPoll blocks every waiting thread on ONE shared channel
|
||||
(schedPollChannel below) and simply re-checks its whole fd list from
|
||||
scratch on every wake, spurious or not, exactly like the retry loop
|
||||
every OTHER restartable syscall already has. pollActive marks "this
|
||||
thread is mid a not-yet-satisfied sysPoll call" (so a retry entry
|
||||
can tell itself apart from a genuinely fresh call); pollDeadline is
|
||||
the absolute pitTicks() deadline schedule()'s own expiry scan forces
|
||||
a wake at if no fd ever becomes ready first (0 == wait forever,
|
||||
meaningful only while pollActive is true -- the same "0 reserved"
|
||||
convention alarmAt above already uses, but here 0 is a real,
|
||||
legal, requested state (a timeout of -1) rather than "nothing
|
||||
pending"). */
|
||||
bool pollActive;
|
||||
uint64 pollDeadline;
|
||||
uint64 brk;
|
||||
uint64 vruntime;
|
||||
uint64 exitCode;
|
||||
uint32 weight;
|
||||
uint32 id;
|
||||
ThreadState state;
|
||||
const char* name;
|
||||
|
||||
Reference in new issue
Block a user