Skip to content

Commit a9049fb

Browse files
ayushr2gvisor-bot
authored andcommitted
Update nvidia_driver_differ to also check ioctl numbers.
Updated the tooling to fetch the ioctl number used in the driver source code and compare it to the one used in nvproxy. It is possible that ioctl numbers change across driver versions (under our foot). It is imperative that we mimic these changes in nvproxy. For example, in 575.57.08 driver release, NV2080_CTRL_CMD_FB_QUERY_DRAM_ENCRYPTION_INFOROM_SUPPORT changed from 0x20801358 to 0x20801357. To achieve this effect, the AST parser has been updated in the previous change to be able to parse numerical constants. However, it is unable to parse macros that evaluate to constants (trust me, I tried). So as a workaround, the nvidia_driver_differ declares "const uint64_t" variables in the source file which is created with all relevant includes (see parser.WriteIncludeFile()). These variables are defined with name "GVISOR_{IOCTL_MACRO} = IOCTL_MACRO" so they should not conflict with anything in the driver. Then we use the AST parser to evaluate the value of the const variable and return that. PiperOrigin-RevId: 793350829
1 parent d54f31a commit a9049fb

File tree

8 files changed

+282
-151
lines changed

8 files changed

+282
-151
lines changed

pkg/sentry/devices/nvproxy/nvproxy_driver_parity_test.go

Lines changed: 58 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,16 @@ func createParserRunner(t *testing.T) *parser.Runner {
5454
return runner
5555
}
5656

57-
func getDriverDefs(t *testing.T, runner *parser.Runner, version nvconf.DriverVersion) ([]nvproxy.DriverStruct, *parser.OutputJSON) {
57+
func getDriverDefs(t *testing.T, runner *parser.Runner, version nvconf.DriverVersion) (*nvproxy.DriverABIInfo, *parser.OutputJSON) {
5858
t.Helper()
5959

60-
structs, ok := nvproxy.SupportedStructTypes(version)
60+
info, ok := nvproxy.SupportedIoctls(version)
6161
if !ok {
6262
t.Fatalf("failed to get struct names for driver %q", version.String())
6363
}
6464

6565
// Create structs file for parser
66-
if err := runner.CreateStructsFile(structs); err != nil {
66+
if err := runner.CreateInputFile(info); err != nil {
6767
t.Fatalf("failed to create temporary structs list: %v", err)
6868
}
6969

@@ -73,7 +73,7 @@ func getDriverDefs(t *testing.T, runner *parser.Runner, version nvconf.DriverVer
7373
t.Fatalf("failed to run driver_ast_parser: %v", err)
7474
}
7575

76-
return structs, defs
76+
return info, defs
7777
}
7878

7979
// TestStructDefinitionParity tests that the struct definitions in nvproxy are the same as the
@@ -86,43 +86,68 @@ func TestStructDefinitionParity(t *testing.T) {
8686
t.Parallel()
8787
runner := createParserRunner(t)
8888

89-
nvproxyDefs, defs := getDriverDefs(t, runner, version)
89+
nvproxyIoctls, defs := getDriverDefs(t, runner, version)
9090

91-
for _, nvproxyDef := range nvproxyDefs {
92-
// Check if the nvproxy definition has disallowed types.
93-
if nvproxyDef.Type != nil {
94-
fields := flattenNvproxyStruct(t, nvproxyDef.Type)
95-
for _, field := range fields {
96-
if _, ok := typeAllowlist[field.Type.Kind()]; !ok {
97-
t.Errorf("struct %q has disallowed type %q in nvproxy", nvproxyDef.Name, field.Type.Name())
91+
checkIoctl := func(ioctlNum uint32, nvproxyIoctl nvproxy.IoctlInfo) {
92+
if nvproxyIoctl.Name == "" {
93+
return
94+
}
95+
// Check if the ioctl number has changed.
96+
if driverIoctlNum, ok := defs.Constants[nvproxyIoctl.Name]; !ok {
97+
t.Errorf("ioctl %q not found in driver source code", nvproxyIoctl.Name)
98+
} else if driverIoctlNum != uint64(ioctlNum) {
99+
t.Errorf("ioctl %q has changed numbers between nvproxy (%#x) and driver (%#x)",
100+
nvproxyIoctl.Name, ioctlNum, defs.Constants[nvproxyIoctl.Name])
101+
}
102+
103+
for _, nvproxyDef := range nvproxyIoctl.Structs {
104+
// Check if the nvproxy definition has disallowed types.
105+
if nvproxyDef.Type != nil {
106+
fields := flattenNvproxyStruct(t, nvproxyDef.Type)
107+
for _, field := range fields {
108+
if _, ok := typeAllowlist[field.Type.Kind()]; !ok {
109+
t.Errorf("struct %q has disallowed type %q in nvproxy", nvproxyDef.Name, field.Type.Name())
110+
}
98111
}
99112
}
100-
}
101113

102-
// Compare the nvproxy definition to the parser output.
103-
name := nvproxyDef.Name
104-
_, isRecord := defs.Records[name]
105-
aliasDef, isAlias := defs.Aliases[name]
106-
if !isRecord && !isAlias {
107-
t.Errorf("struct %q not found in parser output for version %q", name, version.String())
108-
continue
109-
}
114+
// Compare the nvproxy definition to the parser output.
115+
name := nvproxyDef.Name
116+
_, isRecord := defs.Records[name]
117+
aliasDef, isAlias := defs.Aliases[name]
118+
if !isRecord && !isAlias {
119+
t.Errorf("struct %q not found in parser output for version %q", name, version.String())
120+
continue
121+
}
110122

111-
switch {
112-
case isRecord && nvproxyDef.Type == nil:
113-
checkSimpleRecord(t, name, defs)
114-
case isRecord && nvproxyDef.Type != nil:
115-
if err := compareStructs(t, nvproxyDef.Type, name, defs); err != nil {
116-
t.Errorf("struct %q has different definitions between nvproxy and driver: %v", name, err)
123+
switch {
124+
case isRecord && nvproxyDef.Type == nil:
125+
checkSimpleRecord(t, name, defs)
126+
case isRecord && nvproxyDef.Type != nil:
127+
if err := compareStructs(t, nvproxyDef.Type, name, defs); err != nil {
128+
t.Errorf("struct %q has different definitions between nvproxy and driver: %v", name, err)
129+
}
130+
case isAlias && nvproxyDef.Type == nil:
131+
// For now, there is no good way to check if an alias is still simple.
132+
// Regardless, none of the current ioctls fall into this category.
133+
t.Errorf("struct %q is a simple alias, which is not supported yet", name)
134+
case isAlias && nvproxyDef.Type != nil:
135+
checkComplexAlias(t, nvproxyDef, aliasDef)
117136
}
118-
case isAlias && nvproxyDef.Type == nil:
119-
// For now, there is no good way to check if an alias is still simple.
120-
// Regardless, none of the current ioctls fall into this category.
121-
t.Errorf("struct %q is a simple alias, which is not supported yet", name)
122-
case isAlias && nvproxyDef.Type != nil:
123-
checkComplexAlias(t, nvproxyDef, aliasDef)
124137
}
125138
}
139+
for num, info := range nvproxyIoctls.FrontendInfos {
140+
checkIoctl(num, info)
141+
}
142+
for num, info := range nvproxyIoctls.ControlInfos {
143+
checkIoctl(num, info)
144+
}
145+
for num, info := range nvproxyIoctls.AllocationInfos {
146+
checkIoctl(uint32(num), info)
147+
}
148+
for num, info := range nvproxyIoctls.UvmInfos {
149+
checkIoctl(num, info)
150+
}
126151
})
127152
})
128153
}

pkg/sentry/devices/nvproxy/nvproxy_test.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,25 +54,37 @@ func TestABIStructNamesInSync(t *testing.T) {
5454
info := abi.getInfo()
5555

5656
for ioctl := range abi.frontendIoctl {
57-
if _, ok := info.frontendInfos[ioctl]; !ok {
58-
t.Errorf("Frontend ioctl %#x not found in struct names for version %v", ioctl, version.String())
57+
if _, ok := info.FrontendInfos[ioctl]; !ok {
58+
t.Errorf("Frontend ioctl %#x not found in struct names for version %s", ioctl, version)
5959
}
6060
}
61+
if len(abi.frontendIoctl) != len(info.FrontendInfos) {
62+
t.Errorf("Frontend ioctl count mismatch for version %s: %d != %d", version, len(abi.frontendIoctl), len(info.FrontendInfos))
63+
}
6164
for ioctl := range abi.uvmIoctl {
62-
if _, ok := info.uvmInfos[ioctl]; !ok {
63-
t.Errorf("UVM ioctl %#x not found in struct names for version %v", ioctl, version.String())
65+
if _, ok := info.UvmInfos[ioctl]; !ok {
66+
t.Errorf("UVM ioctl %#x not found in struct names for version %s", ioctl, version)
6467
}
6568
}
69+
if len(abi.uvmIoctl) != len(info.UvmInfos) {
70+
t.Errorf("UVM ioctl count mismatch for version %s: %d != %d", version, len(abi.uvmIoctl), len(info.UvmInfos))
71+
}
6672
for ioctl := range abi.controlCmd {
67-
if _, ok := info.controlInfos[ioctl]; !ok {
68-
t.Errorf("Control command %#x not found in struct names for version %v", ioctl, version.String())
73+
if _, ok := info.ControlInfos[ioctl]; !ok {
74+
t.Errorf("Control command %#x not found in struct names for version %s", ioctl, version)
6975
}
7076
}
77+
if len(abi.controlCmd) != len(info.ControlInfos) {
78+
t.Errorf("Control command count mismatch for version %s: %d != %d", version, len(abi.controlCmd), len(info.ControlInfos))
79+
}
7180
for ioctl := range abi.allocationClass {
72-
if _, ok := info.allocationInfos[ioctl]; !ok {
73-
t.Errorf("Alloc class %#x not found in struct names for version %v", ioctl, version.String())
81+
if _, ok := info.AllocationInfos[ioctl]; !ok {
82+
t.Errorf("Alloc class %#x not found in struct names for version %s", ioctl, version)
7483
}
7584
}
85+
if len(abi.allocationClass) != len(info.AllocationInfos) {
86+
t.Errorf("Alloc class count mismatch for version %s: %d != %d", version, len(abi.allocationClass), len(info.AllocationInfos))
87+
}
7688
})
7789
}
7890
}

0 commit comments

Comments
 (0)