github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/tools/arm64/registers.go (about)

     1  // Copyright 2024 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  // Generate KVM ARM64 register IDs for dev_kvm.txt
     5  // Usage:
     6  //
     7  //	go run registers.go msr_mrs.txt
     8  package main
     9  
    10  import (
    11  	"bytes"
    12  	"fmt"
    13  	"os"
    14  	"regexp"
    15  	"strconv"
    16  	"strings"
    17  
    18  	"github.com/google/syzkaller/pkg/tool"
    19  )
    20  
    21  func main() {
    22  	if len(os.Args) != 2 {
    23  		tool.Failf("usage: gen msr_mrs.txt")
    24  	}
    25  	input, err := os.ReadFile(os.Args[1])
    26  	if err != nil {
    27  		tool.Failf("failed to open input file: %v", err)
    28  	}
    29  
    30  	fmt.Printf("# Register descriptions generated by tools/arm64/registers.go\n")
    31  	printSysRegIDs(input)
    32  	printCoreRegs()
    33  	fmt.Printf("# End of register descriptions generated by tools/arm64/registers.go\n")
    34  }
    35  
    36  // Process input lines and return a string containing the list of corresponding register IDs.
    37  func printSysRegIDs(table []byte) {
    38  	ret := ""
    39  	for _, line := range bytes.Split(table, []byte("\n")) {
    40  		if bytes.HasPrefix(line, []byte("#")) {
    41  			continue
    42  		}
    43  
    44  		lineStr := strings.TrimSpace(string(line))
    45  		if lineStr == "" {
    46  			continue
    47  		}
    48  		expandedLines := expandLine(lineStr)
    49  		for _, eline := range expandedLines {
    50  			value, err := processLine(eline)
    51  			if err == nil {
    52  				if ret != "" {
    53  					ret += ", "
    54  				}
    55  				ret += fmt.Sprintf("0x%x", value)
    56  			} else {
    57  				fmt.Fprintf(os.Stdout, "%v\n", err)
    58  			}
    59  		}
    60  	}
    61  	fmt.Printf("kvm_regs_arm64_sys = %s\n", ret)
    62  }
    63  
    64  // Process a single line of the following form:
    65  //
    66  //	`0b10    0b000   0b0000  0b0010  0b000   MDCCINT_EL1 ...`
    67  //
    68  // or
    69  //
    70  //	`0b00    0b000   0b0100  -       0b101   SPSel ...`
    71  //
    72  // - extract five operands from it (treat "-" as a zero) and generate a register ID from them.
    73  func processLine(line string) (int64, error) {
    74  	fields := strings.Fields(line)
    75  
    76  	if len(fields) < 6 {
    77  		return 0, fmt.Errorf("line has too few fields: %s", line)
    78  	}
    79  
    80  	var operands []int
    81  	for i := 0; i < 5; i++ {
    82  		if fields[i] != "-" {
    83  			val, err := strconv.ParseInt(strings.TrimPrefix(fields[i], "0b"), 2, 64)
    84  			if err != nil {
    85  				return 0, fmt.Errorf("conversion error: %w", err)
    86  			}
    87  			operands = append(operands, int(val))
    88  		} else {
    89  			operands = append(operands, 0)
    90  		}
    91  	}
    92  	id := arm64KVMRegID(operands)
    93  
    94  	return id, nil
    95  }
    96  
    97  // If a line contains bit wildcards, replace them with all possible bit permutations.
    98  //
    99  // E.g. the following line:
   100  //
   101  //	`0b11    0b100   0b1100  0b1000  0b0:n[1:0]      ICH_AP0R<n>_EL2 ...`
   102  //
   103  // will be expanded to:
   104  //
   105  //	`0b11    0b100   0b1100  0b1000  0b000      ICH_AP0R<n>_EL2 ...`
   106  //	`0b11    0b100   0b1100  0b1000  0b001      ICH_AP0R<n>_EL2 ...`
   107  //	`0b11    0b100   0b1100  0b1000  0b010      ICH_AP0R<n>_EL2 ...`
   108  //	`0b11    0b100   0b1100  0b1000  0b011      ICH_AP0R<n>_EL2 ...`
   109  func expandLine(line string) []string {
   110  	re := regexp.MustCompile(`(:)?n\[(\d+)(:(\d+))?\]`)
   111  	match := re.FindStringSubmatch(line)
   112  	if match == nil {
   113  		return []string{line}
   114  	}
   115  
   116  	prefix := "0b"
   117  	// If n[] is preceded by ":", there is a 0b prefix in front of it already.
   118  	if match[1] == ":" {
   119  		prefix = ""
   120  	}
   121  	start, _ := strconv.Atoi(match[2])
   122  	end := start
   123  	if match[3] != "" {
   124  		end, _ = strconv.Atoi(match[4])
   125  	}
   126  	m := start - end + 1
   127  	numPermutations := 1 << m
   128  
   129  	expandedLines := make([]string, 0, numPermutations)
   130  	for i := 0; i < numPermutations; i++ {
   131  		bits := fmt.Sprintf("%s%0*b", prefix, m, i)
   132  		newLine := strings.Replace(line, match[0], bits, 1)
   133  		if strings.Contains(newLine, "n[") {
   134  			secondary := expandLine(newLine)
   135  			expandedLines = append(expandedLines, secondary...)
   136  		} else {
   137  			expandedLines = append(expandedLines, newLine)
   138  		}
   139  	}
   140  
   141  	return expandedLines
   142  }
   143  
   144  const (
   145  	// Constants from https://elixir.bootlin.com/linux/v6.10.2/source/arch/arm64/include/uapi/asm/kvm.h
   146  	kvmRegArmCoprocShift       = 16
   147  	kvmRegArm64Sysreg    int64 = (0x0013 << kvmRegArmCoprocShift)
   148  	kvmRegSizeU64        int64 = 0x0030000000000000
   149  	kvmRegArm64          int64 = 0x6000000000000000
   150  )
   151  
   152  // Generate register ID from Op0, Op1, CRn, CRm, Op2.
   153  // See https://elixir.bootlin.com/linux/v6.10.2/source/arch/arm64/include/uapi/asm/kvm.h#L257 for more details.
   154  func arm64KVMRegID(operands []int) int64 {
   155  	shifts := [5]int64{14, 11, 7, 3, 0}
   156  	ret := kvmRegSizeU64 | kvmRegArm64 | kvmRegArm64Sysreg
   157  	for i := 0; i < 5; i++ {
   158  		ret |= (int64(operands[i]) << shifts[i])
   159  	}
   160  	return ret
   161  }
   162  
   163  // Generate core register IDs.
   164  // See https://docs.kernel.org/virt/kvm/api.html for more details.
   165  func printCoreRegs() {
   166  	fmt.Printf("# Extra registers that KVM_GET_REG_LIST prints on QEMU\n")
   167  	// Some of these register IDs do not have corresponding registers, yet the kernel returns them.
   168  	// TODO(glider): figure out why this is happening.
   169  	fmt.Printf("kvm_regs_arm64_extra = 0x603000000013c01b, 0x603000000013c01f, 0x603000000013c022, 0x603000000013c023, " +
   170  		"0x603000000013c025, 0x603000000013c026, 0x603000000013c027, 0x603000000013c02a, 0x603000000013c02b, " +
   171  		"0x603000000013c02e, 0x603000000013c02f, 0x603000000013c033, 0x603000000013c034, 0x603000000013c035, " +
   172  		"0x603000000013c036, 0x603000000013c037, 0x603000000013c03b, 0x603000000013c03c, 0x603000000013c03d, " +
   173  		"0x603000000013c03e, 0x603000000013c03f, 0x603000000013c103, 0x603000000013c512, 0x603000000013c513\n")
   174  }