• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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	"strconv"
21
22	"github.com/google/blueprint/proptools"
23)
24
25type vbmetaModuleInfo struct {
26	// The name of the generated vbmeta module
27	moduleName string
28	// The name of the module that avb understands. This is the name passed to --chain_partition,
29	// and also the basename of the output file. (the output file is called partitionName + ".img")
30	partitionName string
31}
32
33// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4849;drc=62e20f0d218f60bae563b4ee742d88cca1fc1901
34var avbPartitions = []string{
35	"boot",
36	"init_boot",
37	"vendor_boot",
38	"vendor_kernel_boot",
39	"system",
40	"vendor",
41	"product",
42	"system_ext",
43	"odm",
44	"vendor_dlkm",
45	"odm_dlkm",
46	"system_dlkm",
47	"dtbo",
48	"pvmfw",
49	"recovery",
50	"vbmeta_system",
51	"vbmeta_vendor",
52}
53
54// Creates the vbmeta partition and the chained vbmeta partitions. Returns the list of module names
55// that the function created. May return nil if the product isn't using avb.
56//
57// AVB is Android Verified Boot: https://source.android.com/docs/security/features/verifiedboot
58// It works by signing all the partitions, but then also including an extra metadata paritition
59// called vbmeta that depends on all the other signed partitions. This creates a requirement
60// that you update all those partitions and the vbmeta partition together, so in order to relax
61// that requirement products can set up "chained" vbmeta partitions, where a chained partition
62// like vbmeta_system might contain the avb metadata for just a few products. In cuttlefish
63// vbmeta_system contains metadata about product, system, and system_ext. Using chained partitions,
64// that group of partitions can be updated independently from the other signed partitions.
65func (f *filesystemCreator) createVbmetaPartitions(ctx android.LoadHookContext, partitions allGeneratedPartitionData) []vbmetaModuleInfo {
66	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
67	// Some products seem to have BuildingVbmetaImage as true even when BoardAvbEnable is false
68	if !partitionVars.BuildingVbmetaImage || !partitionVars.BoardAvbEnable {
69		return nil
70	}
71
72	var result []vbmetaModuleInfo
73
74	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4593;drc=62e20f0d218f60bae563b4ee742d88cca1fc1901
75	var internalAvbPartitionsInChainedVbmetaImages []string
76	var chainedPartitionTypes []string
77	for _, chainedName := range android.SortedKeys(partitionVars.ChainedVbmetaPartitions) {
78		props := partitionVars.ChainedVbmetaPartitions[chainedName]
79		filesystemPartitionType := chainedName
80		chainedName = "vbmeta_" + chainedName
81		if len(props.Partitions) == 0 {
82			continue
83		}
84		internalAvbPartitionsInChainedVbmetaImages = append(internalAvbPartitionsInChainedVbmetaImages, props.Partitions...)
85		if len(props.Key) == 0 {
86			ctx.ModuleErrorf("No key found for chained avb partition %q", chainedName)
87			continue
88		}
89		if len(props.Algorithm) == 0 {
90			ctx.ModuleErrorf("No algorithm found for chained avb partition %q", chainedName)
91			continue
92		}
93		if len(props.RollbackIndex) == 0 {
94			ctx.ModuleErrorf("No rollback index found for chained avb partition %q", chainedName)
95			continue
96		}
97		ril, err := strconv.ParseInt(props.RollbackIndexLocation, 10, 32)
98		if err != nil {
99			ctx.ModuleErrorf("Rollback index location must be an int, got %q", props.RollbackIndexLocation)
100			continue
101		}
102		// The default is to use the PlatformSecurityPatch, and a lot of product config files
103		// just set it to the platform security patch, so detect that and don't set the property
104		// in soong.
105		var rollbackIndex *int64
106		if props.RollbackIndex != ctx.Config().PlatformSecurityPatch() {
107			i, err := strconv.ParseInt(props.RollbackIndex, 10, 32)
108			if err != nil {
109				ctx.ModuleErrorf("Rollback index must be an int, got %q", props.RollbackIndex)
110				continue
111			}
112			rollbackIndex = &i
113		}
114
115		var partitionModules []string
116		for _, partition := range props.Partitions {
117			if modName := partitions.nameForType(partition); modName != "" {
118				partitionModules = append(partitionModules, modName)
119			}
120		}
121
122		name := generatedModuleNameForPartition(ctx.Config(), chainedName)
123		ctx.CreateModuleInDirectory(
124			filesystem.VbmetaFactory,
125			".", // Create in the root directory for now so its easy to get the key
126			&filesystem.VbmetaProperties{
127				Partition_name:            proptools.StringPtr(chainedName),
128				Filesystem_partition_type: proptools.StringPtr(filesystemPartitionType),
129				Stem:                      proptools.StringPtr(chainedName + ".img"),
130				Private_key:               proptools.StringPtr(props.Key),
131				Algorithm:                 &props.Algorithm,
132				Rollback_index:            rollbackIndex,
133				Rollback_index_location:   &ril,
134				Partitions:                proptools.NewSimpleConfigurable(partitionModules),
135			}, &struct {
136				Name *string
137			}{
138				Name: &name,
139			},
140		).HideFromMake()
141
142		result = append(result, vbmetaModuleInfo{
143			moduleName:    name,
144			partitionName: chainedName,
145		})
146
147		chainedPartitionTypes = append(chainedPartitionTypes, chainedName)
148	}
149
150	vbmetaModuleName := generatedModuleNameForPartition(ctx.Config(), "vbmeta")
151
152	var algorithm *string
153	var ri *int64
154	var key *string
155	if len(partitionVars.BoardAvbKeyPath) == 0 {
156		// Match make's defaults: https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4568;drc=5b55f926830963c02ab1d2d91e46442f04ba3af0
157		key = proptools.StringPtr("external/avb/test/data/testkey_rsa4096.pem")
158		algorithm = proptools.StringPtr("SHA256_RSA4096")
159	} else {
160		key = proptools.StringPtr(partitionVars.BoardAvbKeyPath)
161		algorithm = proptools.StringPtr(partitionVars.BoardAvbAlgorithm)
162	}
163	if len(partitionVars.BoardAvbRollbackIndex) > 0 {
164		parsedRi, err := strconv.ParseInt(partitionVars.BoardAvbRollbackIndex, 10, 32)
165		if err != nil {
166			ctx.ModuleErrorf("Rollback index location must be an int, got %q", partitionVars.BoardAvbRollbackIndex)
167		}
168		ri = &parsedRi
169	}
170
171	// --chain_partition argument is only set for partitions that set
172	// `BOARD_AVB_<partition name>_KEY_PATH` value and is not "recovery"
173	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4823;drc=62e20f0d218f60bae563b4ee742d88cca1fc1901
174	includeAsChainedPartitionInVbmeta := func(partition string) bool {
175		val, ok := partitionVars.PartitionQualifiedVariables[partition]
176		return ok && len(val.BoardAvbKeyPath) > 0 && partition != "recovery"
177	}
178
179	// --include_descriptors_from_image is passed if both conditions are met:
180	// - `BOARD_AVB_<partition name>_KEY_PATH` value is not set
181	// - not included in INTERNAL_AVB_PARTITIONS_IN_CHAINED_VBMETA_IMAGES
182	// for partitions that set INSTALLED_<partition name>IMAGE_TARGET
183	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4827;drc=62e20f0d218f60bae563b4ee742d88cca1fc1901
184	includeAsIncludedPartitionInVbmeta := func(partition string) bool {
185		if android.InList(partition, internalAvbPartitionsInChainedVbmetaImages) {
186			// Already handled by chained vbmeta partitions
187			return false
188		}
189		partitionQualifiedVars := partitionVars.PartitionQualifiedVariables[partition]
190
191		// The return logic in the switch cases below are identical to
192		// ifdef INSTALLED_<partition name>IMAGE_TARGET
193		switch partition {
194		case "boot":
195			return partitionQualifiedVars.BuildingImage || partitionQualifiedVars.PrebuiltImage || partitionVars.BoardUsesRecoveryAsBoot
196		case "vendor_kernel_boot", "dtbo":
197			return partitionQualifiedVars.PrebuiltImage
198		case "system":
199			return partitionQualifiedVars.BuildingImage
200		case "init_boot", "vendor_boot", "vendor", "product", "system_ext", "odm", "vendor_dlkm", "odm_dlkm", "system_dlkm":
201			return partitionQualifiedVars.BuildingImage || partitionQualifiedVars.PrebuiltImage
202		// TODO: Import BOARD_USES_PVMFWIMAGE
203		// ifeq ($(BOARD_USES_PVMFWIMAGE),true)
204		// case "pvmfw":
205		case "recovery":
206			// ifdef INSTALLED_RECOVERYIMAGE_TARGET
207			return !ctx.DeviceConfig().BoardUsesRecoveryAsBoot() && !ctx.DeviceConfig().BoardMoveRecoveryResourcesToVendorBoot()
208		// Technically these partitions are determined based on len(BOARD_AVB_VBMETA_SYSTEM) and
209		// len(BOARD_AVB_VBMETA_VENDOR) but if these are non empty these partitions are
210		// already included in the chained partitions.
211		case "vbmeta_system", "vbmeta_vendor":
212			return false
213		default:
214			return false
215		}
216	}
217
218	var chainedPartitionModules []string
219	var includePartitionModules []string
220	allGeneratedPartitionTypes := append(partitions.types(),
221		chainedPartitionTypes...,
222	)
223	if len(f.properties.Boot_image) > 0 {
224		allGeneratedPartitionTypes = append(allGeneratedPartitionTypes, "boot")
225	}
226	if len(f.properties.Init_boot_image) > 0 {
227		allGeneratedPartitionTypes = append(allGeneratedPartitionTypes, "init_boot")
228	}
229	if len(f.properties.Vendor_boot_image) > 0 {
230		allGeneratedPartitionTypes = append(allGeneratedPartitionTypes, "vendor_boot")
231	}
232
233	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4919;drc=62e20f0d218f60bae563b4ee742d88cca1fc1901
234	for _, partitionType := range android.SortedUniqueStrings(append(avbPartitions, chainedPartitionTypes...)) {
235		if !android.InList(partitionType, allGeneratedPartitionTypes) {
236			// Skip if the partition is not auto generated
237			continue
238		}
239		name := partitions.nameForType(partitionType)
240		if name == "" {
241			name = generatedModuleNameForPartition(ctx.Config(), partitionType)
242		}
243		if includeAsChainedPartitionInVbmeta(partitionType) {
244			chainedPartitionModules = append(chainedPartitionModules, name)
245		} else if includeAsIncludedPartitionInVbmeta(partitionType) {
246			includePartitionModules = append(includePartitionModules, name)
247		}
248	}
249
250	ctx.CreateModuleInDirectory(
251		filesystem.VbmetaFactory,
252		".", // Create in the root directory for now so its easy to get the key
253		&filesystem.VbmetaProperties{
254			Stem:               proptools.StringPtr("vbmeta.img"),
255			Algorithm:          algorithm,
256			Private_key:        key,
257			Rollback_index:     ri,
258			Chained_partitions: chainedPartitionModules,
259			Partitions:         proptools.NewSimpleConfigurable(includePartitionModules),
260			Partition_name:     proptools.StringPtr("vbmeta"),
261		}, &struct {
262			Name *string
263		}{
264			Name: &vbmetaModuleName,
265		},
266	).HideFromMake()
267
268	result = append(result, vbmetaModuleInfo{
269		moduleName:    vbmetaModuleName,
270		partitionName: "vbmeta",
271	})
272	return result
273}
274