aboutsummaryrefslogtreecommitdiff
path: root/core/sys/info/cpu_linux_riscv64.odin
blob: e65e8a3d2120964455bda8e8450b256c42dc3c12 (plain)
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
#+build riscv64
#+build linux
package sysinfo

import "base:intrinsics"

import "core:sys/linux"

@(init, private)
init_cpu_features :: proc "contextless" () {
	_features: CPU_Features
	defer cpu.features = _features

	HWCAP_Bits :: enum u64 {
		I = 'I' - 'A',
		M = 'M' - 'A',
		A = 'A' - 'A',
		F = 'F' - 'A',
		D = 'D' - 'A',
		C = 'C' - 'A',
		V = 'V' - 'A',
	}
	HWCAP :: bit_set[HWCAP_Bits; u64]

	// Read HWCAP for base extensions, we can get this info through hwprobe too but that is Linux 6.4+ only.
	{
		fd, err := linux.open("/proc/self/auxv", {})
		if err != .NONE { return }
		defer linux.close(fd)

		// This is probably enough right?
		buf: [4096]byte
		n, rerr := linux.read(fd, buf[:])
		if rerr != .NONE || n == 0 { return }

		ulong     :: u64
		AT_HWCAP  :: 16

		auxv := buf[:n]
		for len(auxv) >= size_of(ulong)*2 {
			key := intrinsics.unaligned_load((^ulong)(&auxv[0]))
			val := intrinsics.unaligned_load((^ulong)(&auxv[size_of(ulong)]))
			auxv = auxv[2*size_of(ulong):]

			if key != AT_HWCAP {
				continue
			}

			cap := transmute(HWCAP)(val)
			if .I in cap {
				_features += { .I }
			}
			if .M in cap {
				_features += { .M }
			}
			if .A in cap {
				_features += { .A }
			}
			if .F in cap {
				_features += { .F }
			}
			if .D in cap {
				_features += { .D }
			}
			if .C in cap {
				_features += { .C }
			}
			if .V in cap {
				_features += { .V }
			}
			break
		}
	}

	// hwprobe for other features.
	{
		pairs := []linux.RISCV_HWProbe{
			{ key = .IMA_EXT_0 },
			{ key = .CPUPERF_0 },
			{ key = .MISALIGNED_SCALAR_PERF },
		}
		err := linux.riscv_hwprobe(raw_data(pairs), len(pairs), 0, nil, {})
		if err != nil {
			assert_contextless(err == .ENOSYS, "unexpected error from riscv_hwprobe()")
			return
		}

		assert_contextless(pairs[0].key == .IMA_EXT_0)
		exts := pairs[0].value.ima_ext_0
		exts -= { .FD, .C, .V }
		_features += transmute(CPU_Features)exts

		if pairs[2].key == .MISALIGNED_SCALAR_PERF {
			if pairs[2].value.misaligned_scalar_perf == .FAST {
				_features += { .Misaligned_Supported, .Misaligned_Fast }
			} else if pairs[2].value.misaligned_scalar_perf != .UNSUPPORTED {
				_features += { .Misaligned_Supported }
			}
		} else {
			assert_contextless(pairs[1].key == .CPUPERF_0)
			if .FAST in pairs[1].value.cpu_perf_0 {
				_features += { .Misaligned_Supported, .Misaligned_Fast }
			} else if .UNSUPPORTED not_in pairs[1].value.cpu_perf_0 {
				_features += { .Misaligned_Supported }
			}
		}
	}
}

@(init, private)
init_cpu_name :: proc "contextless" () {
	cpu.name = "RISCV64"
}