xref: /aosp_15_r20/build/soong/fsgen/vbmeta_partitions.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1// Copyright (C) 2024 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package fsgen
16
17import (
18	"android/soong/android"
19	"android/soong/filesystem"
20	"slices"
21	"strconv"
22	"strings"
23
24	"github.com/google/blueprint/proptools"
25)
26
27type vbmetaModuleInfo struct {
28	// The name of the generated vbmeta module
29	moduleName string
30	// The name of the module that avb understands. This is the name passed to --chain_partition,
31	// and also the basename of the output file. (the output file is called partitionName + ".img")
32	partitionName string
33}
34
35// Creates the vbmeta partition and the chained vbmeta partitions. Returns the list of module names
36// that the function created. May return nil if the product isn't using avb.
37//
38// AVB is Android Verified Boot: https://source.android.com/docs/security/features/verifiedboot
39// It works by signing all the partitions, but then also including an extra metadata paritition
40// called vbmeta that depends on all the other signed partitions. This creates a requirement
41// that you update all those partitions and the vbmeta partition together, so in order to relax
42// that requirement products can set up "chained" vbmeta partitions, where a chained partition
43// like vbmeta_system might contain the avb metadata for just a few products. In cuttlefish
44// vbmeta_system contains metadata about product, system, and system_ext. Using chained partitions,
45// that group of partitions can be updated independently from the other signed partitions.
46func createVbmetaPartitions(ctx android.LoadHookContext, generatedPartitionTypes []string) []vbmetaModuleInfo {
47	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
48	// Some products seem to have BuildingVbmetaImage as true even when BoardAvbEnable is false
49	if !partitionVars.BuildingVbmetaImage || !partitionVars.BoardAvbEnable {
50		return nil
51	}
52
53	var result []vbmetaModuleInfo
54
55	var chainedPartitions []string
56	var partitionTypesHandledByChainedPartitions []string
57	for _, chainedName := range android.SortedKeys(partitionVars.ChainedVbmetaPartitions) {
58		props := partitionVars.ChainedVbmetaPartitions[chainedName]
59		chainedName = "vbmeta_" + chainedName
60		if len(props.Partitions) == 0 {
61			continue
62		}
63		if len(props.Key) == 0 {
64			ctx.ModuleErrorf("No key found for chained avb partition %q", chainedName)
65			continue
66		}
67		if len(props.Algorithm) == 0 {
68			ctx.ModuleErrorf("No algorithm found for chained avb partition %q", chainedName)
69			continue
70		}
71		if len(props.RollbackIndex) == 0 {
72			ctx.ModuleErrorf("No rollback index found for chained avb partition %q", chainedName)
73			continue
74		}
75		ril, err := strconv.ParseInt(props.RollbackIndexLocation, 10, 32)
76		if err != nil {
77			ctx.ModuleErrorf("Rollback index location must be an int, got %q", props.RollbackIndexLocation)
78			continue
79		}
80		// The default is to use the PlatformSecurityPatch, and a lot of product config files
81		// just set it to the platform security patch, so detect that and don't set the property
82		// in soong.
83		var rollbackIndex *int64
84		if props.RollbackIndex != ctx.Config().PlatformSecurityPatch() {
85			i, err := strconv.ParseInt(props.RollbackIndex, 10, 32)
86			if err != nil {
87				ctx.ModuleErrorf("Rollback index must be an int, got %q", props.RollbackIndex)
88				continue
89			}
90			rollbackIndex = &i
91		}
92
93		var partitionModules []string
94		for _, partition := range props.Partitions {
95			partitionTypesHandledByChainedPartitions = append(partitionTypesHandledByChainedPartitions, partition)
96			if !slices.Contains(generatedPartitionTypes, partition) {
97				// The partition is probably unsupported.
98				continue
99			}
100			partitionModules = append(partitionModules, generatedModuleNameForPartition(ctx.Config(), partition))
101		}
102
103		name := generatedModuleName(ctx.Config(), chainedName)
104		ctx.CreateModuleInDirectory(
105			filesystem.VbmetaFactory,
106			".", // Create in the root directory for now so its easy to get the key
107			&filesystem.VbmetaProperties{
108				Partition_name:          proptools.StringPtr(chainedName),
109				Stem:                    proptools.StringPtr(chainedName + ".img"),
110				Private_key:             proptools.StringPtr(props.Key),
111				Algorithm:               &props.Algorithm,
112				Rollback_index:          rollbackIndex,
113				Rollback_index_location: &ril,
114				Partitions:              proptools.NewSimpleConfigurable(partitionModules),
115			}, &struct {
116				Name *string
117			}{
118				Name: &name,
119			},
120		).HideFromMake()
121
122		chainedPartitions = append(chainedPartitions, name)
123
124		result = append(result, vbmetaModuleInfo{
125			moduleName:    name,
126			partitionName: chainedName,
127		})
128	}
129
130	vbmetaModuleName := generatedModuleName(ctx.Config(), "vbmeta")
131
132	var algorithm *string
133	var ri *int64
134	var key *string
135	if len(partitionVars.BoardAvbKeyPath) == 0 {
136		// Match make's defaults: https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4568;drc=5b55f926830963c02ab1d2d91e46442f04ba3af0
137		key = proptools.StringPtr("external/avb/test/data/testkey_rsa4096.pem")
138		algorithm = proptools.StringPtr("SHA256_RSA4096")
139	} else {
140		key = proptools.StringPtr(partitionVars.BoardAvbKeyPath)
141		algorithm = proptools.StringPtr(partitionVars.BoardAvbAlgorithm)
142	}
143	if len(partitionVars.BoardAvbRollbackIndex) > 0 {
144		parsedRi, err := strconv.ParseInt(partitionVars.BoardAvbRollbackIndex, 10, 32)
145		if err != nil {
146			ctx.ModuleErrorf("Rollback index location must be an int, got %q", partitionVars.BoardAvbRollbackIndex)
147		}
148		ri = &parsedRi
149	}
150
151	var partitionModules []string
152	for _, partitionType := range generatedPartitionTypes {
153		if slices.Contains(partitionTypesHandledByChainedPartitions, partitionType) {
154			// Already handled by a chained vbmeta partition
155			continue
156		}
157		if strings.Contains(partitionType, "ramdisk") || strings.Contains(partitionType, "boot") {
158			// ramdisk is never signed with avb information
159			// boot partitions just have the avb footer, and don't have a corresponding vbmeta
160			// partition.
161			continue
162		}
163		partitionModules = append(partitionModules, generatedModuleNameForPartition(ctx.Config(), partitionType))
164	}
165
166	ctx.CreateModuleInDirectory(
167		filesystem.VbmetaFactory,
168		".", // Create in the root directory for now so its easy to get the key
169		&filesystem.VbmetaProperties{
170			Stem:               proptools.StringPtr("vbmeta.img"),
171			Algorithm:          algorithm,
172			Private_key:        key,
173			Rollback_index:     ri,
174			Chained_partitions: chainedPartitions,
175			Partitions:         proptools.NewSimpleConfigurable(partitionModules),
176		}, &struct {
177			Name *string
178		}{
179			Name: &vbmetaModuleName,
180		},
181	).HideFromMake()
182
183	result = append(result, vbmetaModuleInfo{
184		moduleName:    vbmetaModuleName,
185		partitionName: "vbmeta",
186	})
187	return result
188}
189