~eliasnaur/unik

ref: cd0f505f7c2c06bac9c3655ab38b7bbe2d1761f3 unik/kernel/cmos_amd64.go -rw-r--r-- 4.1 KiB
cd0f505fElias Naur initial import 6 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// SPDX-License-Identifier: Unlicense OR MIT

package kernel

// Functions for reading the real time clock of the CMOS.

// readCMOSTime reads the CMOS clock and converts it to UNIX time in
// seconds.
//go:nosplit
func readCMOSTime() int64 {
	waitForCMOS()
	t := readCMOSTime0()
	// The CMOS may be updating its time during our reading.
	// Read the time until it is stable.
	for {
		waitForCMOS()
		t2 := readCMOSTime0()
		if t2 == t {
			break
		}
		t = t2
	}
	return t
}

//go:nosplit
func readCMOSTime0() int64 {
	sec, min, hour := readCMOSReg(0x00), readCMOSReg(0x02), readCMOSReg(0x04)
	day, month, year, century := readCMOSReg(0x07), readCMOSReg(0x08), readCMOSReg(0x09), readCMOSReg(0x32)
	statusB := readCMOSReg(0x0b)
	pm := false
	// Check for 12-hour format.
	if statusB&1<<1 != 0 {
		// Read and reset PM bit.
		pm = hour&1<<7 != 0
		hour = hour & 0x7f
	}
	// Check for decimal format.
	if statusB&1<<2 == 0 {
		sec, min, hour = decToBin(sec), decToBin(min), decToBin(hour)
		day, month, year, century = decToBin(day), decToBin(month), decToBin(year), decToBin(century)
	}
	if pm {
		hour = (hour + 12) % 24
	}
	return cmosToUnix(int(century)*100+int(year), int(month)-1, int(day)-1, int(hour), int(min), int(sec))
}

// decToBin converts a decimal value to binary.
//go:nosplit
func decToBin(v uint8) uint8 {
	return (v & 0x0F) + ((v / 16) * 10)
}

// cmosToUnix is similar to time.Date(...).Unix(), except that we can't use
// that from the kernel. Note that the month and day are zero-based.
//go:nosplit
func cmosToUnix(year, month, day, hour, min, sec int) int64 {
	const (
		secondsPerMinute = 60
		secondsPerHour   = 60 * secondsPerMinute
		secondsPerDay    = 24 * secondsPerHour
		absoluteZeroYear = -292277022399
		internalYear     = 1

		absoluteToInternal int64 = (absoluteZeroYear - internalYear) * 365.2425 * secondsPerDay
		unixToInternal     int64 = (1969*365 + 1969/4 - 1969/100 + 1969/400) * secondsPerDay
		internalToUnix     int64 = -unixToInternal
		daysPer400Years          = 365*400 + 97
		daysPer100Years          = 365*100 + 24
		daysPer4Years            = 365*4 + 1
	)

	var daysBefore = [...]int32{
		0,
		31,
		31 + 28,
		31 + 28 + 31,
		31 + 28 + 31 + 30,
		31 + 28 + 31 + 30 + 31,
		31 + 28 + 31 + 30 + 31 + 30,
		31 + 28 + 31 + 30 + 31 + 30 + 31,
		31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
		31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
		31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
		31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
		31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
	}
	// Normalize.
	year, month = norm(year, month, 11)
	min, sec = norm(min, sec, 60)
	hour, min = norm(hour, min, 60)
	day, hour = norm(day, hour, 24)

	y := uint64(int64(year) - absoluteZeroYear)

	// Compute days since the absolute epoch.

	// Add in days from 400-year cycles.
	n := y / 400
	y -= 400 * n
	d := daysPer400Years * n

	// Add in 100-year cycles.
	n = y / 100
	y -= 100 * n
	d += daysPer100Years * n

	// Add in 4-year cycles.
	n = y / 4
	y -= 4 * n
	d += daysPer4Years * n

	// Add in non-leap years.
	n = y
	d += 365 * n

	// Add in days before this month.
	d += uint64(daysBefore[month])
	if isLeap(year) && month >= 2 /* March */ {
		d++ // February 29
	}

	// Add in days before today.
	d += uint64(day)

	// Add in time elapsed today.
	abs := d * secondsPerDay
	abs += uint64(hour*secondsPerHour + min*secondsPerMinute + sec)

	unix := int64(abs) + (absoluteToInternal + internalToUnix)

	return unix
}

//go:nosplit
func isLeap(year int) bool {
	return year%4 == 0 && (year%100 != 0 || year%400 == 0)
}

// norm returns nhi, nlo such that
//	hi * base + lo == nhi * base + nlo
//	0 <= nlo < base
//go:nosplit
func norm(hi, lo, base int) (nhi, nlo int) {
	if lo < 0 {
		n := (-lo-1)/base + 1
		hi -= n
		lo += n * base
	}
	if lo >= base {
		n := lo / base
		hi += n
		lo -= n * base
	}
	return hi, lo
}

// waitForCMOS waits for the CMOS busy flag to clear. The
// flag is in bit 7 of the status A register.
//go:nosplit
func waitForCMOS() {
	for {
		statusA := readCMOSReg(0x0a)
		if statusA&1<<7 == 0 {
			return
		}
	}
}

//go:nosplit
func readCMOSReg(reg uint8) uint8 {
	const (
		cmosAddr = 0x70
		cmosData = 0x71
	)
	outb(cmosAddr, reg)
	return inb(cmosData)
}