aboutsummaryrefslogtreecommitdiffstats
path: root/tools/arm64/registers.go
blob: dc68683fefa67ce67d3703d1d0931b1c696e8001 (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
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
// Copyright 2024 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// Generate KVM ARM64 register IDs for dev_kvm.txt
// Usage:
//
//	go run registers.go msr_mrs.txt
package main

import (
	"bytes"
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/google/syzkaller/pkg/tool"
)

func main() {
	if len(os.Args) != 2 {
		tool.Failf("usage: gen msr_mrs.txt")
	}
	input, err := os.ReadFile(os.Args[1])
	if err != nil {
		tool.Failf("failed to open input file: %v", err)
	}

	fmt.Printf("# Register descriptions generated by tools/arm64/registers.go\n")
	printSysRegIDs(input)
	printCoreRegs()
	fmt.Printf("# End of register descriptions generated by tools/arm64/registers.go\n")
}

// Process input lines and return a string containing the list of corresponding register IDs.
func printSysRegIDs(table []byte) {
	ret := ""
	for _, line := range bytes.Split(table, []byte("\n")) {
		if bytes.HasPrefix(line, []byte("#")) {
			continue
		}

		lineStr := strings.TrimSpace(string(line))
		if lineStr == "" {
			continue
		}
		expandedLines := expandLine(lineStr)
		for _, eline := range expandedLines {
			value, err := processLine(eline)
			if err == nil {
				if ret != "" {
					ret += ", "
				}
				ret += fmt.Sprintf("0x%x", value)
			} else {
				fmt.Fprintf(os.Stdout, "%v\n", err)
			}
		}
	}
	fmt.Printf("kvm_regs_arm64_sys = %s\n", ret)
}

// Process a single line of the following form:
//
//	`0b10    0b000   0b0000  0b0010  0b000   MDCCINT_EL1 ...`
//
// or
//
//	`0b00    0b000   0b0100  -       0b101   SPSel ...`
//
// - extract five operands from it (treat "-" as a zero) and generate a register ID from them.
func processLine(line string) (int64, error) {
	fields := strings.Fields(line)

	if len(fields) < 6 {
		return 0, fmt.Errorf("line has too few fields: %s", line)
	}

	var operands []int
	for i := 0; i < 5; i++ {
		if fields[i] != "-" {
			val, err := strconv.ParseInt(strings.TrimPrefix(fields[i], "0b"), 2, 64)
			if err != nil {
				return 0, fmt.Errorf("conversion error: %w", err)
			}
			operands = append(operands, int(val))
		} else {
			operands = append(operands, 0)
		}
	}
	id := arm64KVMRegID(operands)

	return id, nil
}

// If a line contains bit wildcards, replace them with all possible bit permutations.
//
// E.g. the following line:
//
//	`0b11    0b100   0b1100  0b1000  0b0:n[1:0]      ICH_AP0R<n>_EL2 ...`
//
// will be expanded to:
//
//	`0b11    0b100   0b1100  0b1000  0b000      ICH_AP0R<n>_EL2 ...`
//	`0b11    0b100   0b1100  0b1000  0b001      ICH_AP0R<n>_EL2 ...`
//	`0b11    0b100   0b1100  0b1000  0b010      ICH_AP0R<n>_EL2 ...`
//	`0b11    0b100   0b1100  0b1000  0b011      ICH_AP0R<n>_EL2 ...`
func expandLine(line string) []string {
	re := regexp.MustCompile(`(:)?n\[(\d+)(:(\d+))?\]`)
	match := re.FindStringSubmatch(line)
	if match == nil {
		return []string{line}
	}

	prefix := "0b"
	// If n[] is preceded by ":", there is a 0b prefix in front of it already.
	if match[1] == ":" {
		prefix = ""
	}
	start, _ := strconv.Atoi(match[2])
	end := start
	if match[3] != "" {
		end, _ = strconv.Atoi(match[4])
	}
	m := start - end + 1
	numPermutations := 1 << m

	expandedLines := make([]string, 0, numPermutations)
	for i := 0; i < numPermutations; i++ {
		bits := fmt.Sprintf("%s%0*b", prefix, m, i)
		newLine := strings.Replace(line, match[0], bits, 1)
		if strings.Contains(newLine, "n[") {
			secondary := expandLine(newLine)
			expandedLines = append(expandedLines, secondary...)
		} else {
			expandedLines = append(expandedLines, newLine)
		}
	}

	return expandedLines
}

const (
	// Constants from https://elixir.bootlin.com/linux/v6.10.2/source/arch/arm64/include/uapi/asm/kvm.h
	kvmRegArmCoprocShift       = 16
	kvmRegArm64Sysreg    int64 = (0x0013 << kvmRegArmCoprocShift)
	kvmRegSizeU64        int64 = 0x0030000000000000
	kvmRegArm64          int64 = 0x6000000000000000
)

// Generate register ID from Op0, Op1, CRn, CRm, Op2.
// See https://elixir.bootlin.com/linux/v6.10.2/source/arch/arm64/include/uapi/asm/kvm.h#L257 for more details.
func arm64KVMRegID(operands []int) int64 {
	shifts := [5]int64{14, 11, 7, 3, 0}
	ret := kvmRegSizeU64 | kvmRegArm64 | kvmRegArm64Sysreg
	for i := 0; i < 5; i++ {
		ret |= (int64(operands[i]) << shifts[i])
	}
	return ret
}

// Generate core register IDs.
// See https://docs.kernel.org/virt/kvm/api.html for more details.
func printCoreRegs() {
	fmt.Printf("# Extra registers that KVM_GET_REG_LIST prints on QEMU\n")
	// Some of these register IDs do not have corresponding registers, yet the kernel returns them.
	// TODO(glider): figure out why this is happening.
	fmt.Printf("kvm_regs_arm64_extra = 0x603000000013c01b, 0x603000000013c01f, 0x603000000013c022, 0x603000000013c023, " +
		"0x603000000013c025, 0x603000000013c026, 0x603000000013c027, 0x603000000013c02a, 0x603000000013c02b, " +
		"0x603000000013c02e, 0x603000000013c02f, 0x603000000013c033, 0x603000000013c034, 0x603000000013c035, " +
		"0x603000000013c036, 0x603000000013c037, 0x603000000013c03b, 0x603000000013c03c, 0x603000000013c03d, " +
		"0x603000000013c03e, 0x603000000013c03f, 0x603000000013c103, 0x603000000013c512, 0x603000000013c513\n")
}