blob: 43a2f37124f0cbe264786caafd67d7d0c9b0ba53 [file] [log] [blame]
Jiyong Park972e06c2021-03-15 23:32:49 +09001// Copyright (C) 2021 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 filesystem
16
17import (
18 "fmt"
19 "strconv"
20
21 "github.com/google/blueprint"
22 "github.com/google/blueprint/proptools"
23
24 "android/soong/android"
25)
26
27func init() {
28 android.RegisterModuleType("vbmeta", vbmetaFactory)
29}
30
31type vbmeta struct {
32 android.ModuleBase
33
34 properties vbmetaProperties
35
36 output android.OutputPath
37 installDir android.InstallPath
38}
39
40type vbmetaProperties struct {
41 // Name of the partition stored in vbmeta desc. Defaults to the name of this module.
42 Partition_name *string
43
44 // Set the name of the output. Defaults to <module_name>.img.
45 Stem *string
46
47 // Path to the private key that avbtool will use to sign this vbmeta image.
48 Private_key *string `android:"path"`
49
50 // Algorithm that avbtool will use to sign this vbmeta image. Default is SHA256_RSA4096.
51 Algorithm *string
52
53 // File whose content will provide the rollback index. If unspecified, the rollback index
54 // is from PLATFORM_SECURITY_PATCH
55 Rollback_index_file *string `android:"path"`
56
57 // Rollback index location of this vbmeta image. Must be 0, 1, 2, etc. Default is 0.
58 Rollback_index_location *int64
59
60 // List of filesystem modules that this vbmeta has descriptors for. The filesystem modules
61 // have to be signed (use_avb: true).
62 Partitions []string
63
64 // List of chained partitions that this vbmeta deletages the verification.
65 Chained_partitions []chainedPartitionProperties
Seungjae Yoo9f263712023-11-16 17:22:57 +090066
67 // List of key-value pair of avb properties
68 Avb_properties []avbProperty
69}
70
71type avbProperty struct {
72 // Key of given avb property
73 Key *string
74
75 // Value of given avb property
76 Value *string
Jiyong Park972e06c2021-03-15 23:32:49 +090077}
78
79type chainedPartitionProperties struct {
80 // Name of the chained partition
81 Name *string
82
83 // Rollback index location of the chained partition. Must be 0, 1, 2, etc. Default is the
84 // index of this partition in the list + 1.
85 Rollback_index_location *int64
86
87 // Path to the public key that the chained partition is signed with. If this is specified,
88 // private_key is ignored.
89 Public_key *string `android:"path"`
90
91 // Path to the private key that the chained partition is signed with. If this is specified,
92 // and public_key is not specified, a public key is extracted from this private key and
93 // the extracted public key is embedded in the vbmeta image.
94 Private_key *string `android:"path"`
95}
96
97// vbmeta is the partition image that has the verification information for other partitions.
98func vbmetaFactory() android.Module {
99 module := &vbmeta{}
100 module.AddProperties(&module.properties)
101 android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
102 return module
103}
104
105type vbmetaDep struct {
106 blueprint.BaseDependencyTag
107 kind string
108}
109
110var vbmetaPartitionDep = vbmetaDep{kind: "partition"}
111
112func (v *vbmeta) DepsMutator(ctx android.BottomUpMutatorContext) {
113 ctx.AddDependency(ctx.Module(), vbmetaPartitionDep, v.properties.Partitions...)
114}
115
116func (v *vbmeta) installFileName() string {
117 return proptools.StringDefault(v.properties.Stem, v.BaseModuleName()+".img")
118}
119
120func (v *vbmeta) partitionName() string {
121 return proptools.StringDefault(v.properties.Partition_name, v.BaseModuleName())
122}
123
Jiyong Parkda2d6ee2021-04-14 16:42:24 +0900124// See external/avb/libavb/avb_slot_verify.c#VBMETA_MAX_SIZE
125const vbmetaMaxSize = 64 * 1024
126
Jiyong Park972e06c2021-03-15 23:32:49 +0900127func (v *vbmeta) GenerateAndroidBuildActions(ctx android.ModuleContext) {
128 extractedPublicKeys := v.extractPublicKeys(ctx)
129
130 v.output = android.PathForModuleOut(ctx, v.installFileName()).OutputPath
131
132 builder := android.NewRuleBuilder(pctx, ctx)
133 cmd := builder.Command().BuiltTool("avbtool").Text("make_vbmeta_image")
134
135 key := android.PathForModuleSrc(ctx, proptools.String(v.properties.Private_key))
136 cmd.FlagWithInput("--key ", key)
137
138 algorithm := proptools.StringDefault(v.properties.Algorithm, "SHA256_RSA4096")
139 cmd.FlagWithArg("--algorithm ", algorithm)
140
141 cmd.FlagWithArg("--rollback_index ", v.rollbackIndexCommand(ctx))
142 ril := proptools.IntDefault(v.properties.Rollback_index_location, 0)
143 if ril < 0 {
144 ctx.PropertyErrorf("rollback_index_location", "must be 0, 1, 2, ...")
145 return
146 }
147 cmd.FlagWithArg("--rollback_index_location ", strconv.Itoa(ril))
148
Seungjae Yoo9f263712023-11-16 17:22:57 +0900149 for _, avb_prop := range v.properties.Avb_properties {
150 key := proptools.String(avb_prop.Key)
151 if key == "" {
152 ctx.PropertyErrorf("avb_properties", "key must be specified")
153 continue
154 }
155 value := proptools.String(avb_prop.Value)
156 if value == "" {
157 ctx.PropertyErrorf("avb_properties", "value must be specified")
158 continue
159 }
160 cmd.FlagWithArg("--prop ", key+":"+value)
161 }
162
Jiyong Park972e06c2021-03-15 23:32:49 +0900163 for _, p := range ctx.GetDirectDepsWithTag(vbmetaPartitionDep) {
164 f, ok := p.(Filesystem)
165 if !ok {
166 ctx.PropertyErrorf("partitions", "%q(type: %s) is not supported",
167 p.Name(), ctx.OtherModuleType(p))
168 continue
169 }
170 signedImage := f.SignedOutputPath()
171 if signedImage == nil {
172 ctx.PropertyErrorf("partitions", "%q(type: %s) is not signed. Use `use_avb: true`",
173 p.Name(), ctx.OtherModuleType(p))
174 continue
175 }
176 cmd.FlagWithInput("--include_descriptors_from_image ", signedImage)
177 }
178
179 for i, cp := range v.properties.Chained_partitions {
180 name := proptools.String(cp.Name)
181 if name == "" {
182 ctx.PropertyErrorf("chained_partitions", "name must be specified")
183 continue
184 }
185
186 ril := proptools.IntDefault(cp.Rollback_index_location, i+1)
187 if ril < 0 {
188 ctx.PropertyErrorf("chained_partitions", "must be 0, 1, 2, ...")
189 continue
190 }
191
192 var publicKey android.Path
193 if cp.Public_key != nil {
194 publicKey = android.PathForModuleSrc(ctx, proptools.String(cp.Public_key))
195 } else {
196 publicKey = extractedPublicKeys[name]
197 }
198 cmd.FlagWithArg("--chain_partition ", fmt.Sprintf("%s:%d:%s", name, ril, publicKey.String()))
199 cmd.Implicit(publicKey)
200 }
201
202 cmd.FlagWithOutput("--output ", v.output)
Jiyong Parkda2d6ee2021-04-14 16:42:24 +0900203
204 // libavb expects to be able to read the maximum vbmeta size, so we must provide a partition
205 // which matches this or the read will fail.
206 builder.Command().Text("truncate").
207 FlagWithArg("-s ", strconv.Itoa(vbmetaMaxSize)).
208 Output(v.output)
209
Jiyong Park972e06c2021-03-15 23:32:49 +0900210 builder.Build("vbmeta", fmt.Sprintf("vbmeta %s", ctx.ModuleName()))
211
212 v.installDir = android.PathForModuleInstall(ctx, "etc")
213 ctx.InstallFile(v.installDir, v.installFileName(), v.output)
214}
215
216// Returns the embedded shell command that prints the rollback index
217func (v *vbmeta) rollbackIndexCommand(ctx android.ModuleContext) string {
218 var cmd string
219 if v.properties.Rollback_index_file != nil {
220 f := android.PathForModuleSrc(ctx, proptools.String(v.properties.Rollback_index_file))
221 cmd = "cat " + f.String()
222 } else {
223 cmd = "date -d 'TZ=\"GMT\" " + ctx.Config().PlatformSecurityPatch() + "' +%s"
224 }
225 // Take the first line and remove the newline char
226 return "$(" + cmd + " | head -1 | tr -d '\n'" + ")"
227}
228
229// Extract public keys from chained_partitions.private_key. The keys are indexed with the partition
230// name.
231func (v *vbmeta) extractPublicKeys(ctx android.ModuleContext) map[string]android.OutputPath {
232 result := make(map[string]android.OutputPath)
233
234 builder := android.NewRuleBuilder(pctx, ctx)
235 for _, cp := range v.properties.Chained_partitions {
236 if cp.Private_key == nil {
237 continue
238 }
239
240 name := proptools.String(cp.Name)
241 if name == "" {
242 ctx.PropertyErrorf("chained_partitions", "name must be specified")
243 continue
244 }
245
246 if _, ok := result[name]; ok {
247 ctx.PropertyErrorf("chained_partitions", "name %q is duplicated", name)
248 continue
249 }
250
251 privateKeyFile := android.PathForModuleSrc(ctx, proptools.String(cp.Private_key))
252 publicKeyFile := android.PathForModuleOut(ctx, name+".avbpubkey").OutputPath
253
254 builder.Command().
255 BuiltTool("avbtool").
256 Text("extract_public_key").
257 FlagWithInput("--key ", privateKeyFile).
258 FlagWithOutput("--output ", publicKeyFile)
259
260 result[name] = publicKeyFile
261 }
262 builder.Build("vbmeta_extract_public_key", fmt.Sprintf("Extract public keys for %s", ctx.ModuleName()))
263 return result
264}
265
266var _ android.AndroidMkEntriesProvider = (*vbmeta)(nil)
267
268// Implements android.AndroidMkEntriesProvider
269func (v *vbmeta) AndroidMkEntries() []android.AndroidMkEntries {
270 return []android.AndroidMkEntries{android.AndroidMkEntries{
271 Class: "ETC",
272 OutputFile: android.OptionalPathForPath(v.output),
273 ExtraEntries: []android.AndroidMkExtraEntriesFunc{
274 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
Colin Crossc68db4b2021-11-11 18:59:15 -0800275 entries.SetString("LOCAL_MODULE_PATH", v.installDir.String())
Jiyong Park972e06c2021-03-15 23:32:49 +0900276 entries.SetString("LOCAL_INSTALLED_MODULE_STEM", v.installFileName())
277 },
278 },
279 }}
280}
281
282var _ Filesystem = (*vbmeta)(nil)
283
284func (v *vbmeta) OutputPath() android.Path {
285 return v.output
286}
287
288func (v *vbmeta) SignedOutputPath() android.Path {
289 return v.OutputPath() // vbmeta is always signed
290}
291
292var _ android.OutputFileProducer = (*vbmeta)(nil)
293
294// Implements android.OutputFileProducer
295func (v *vbmeta) OutputFiles(tag string) (android.Paths, error) {
296 if tag == "" {
297 return []android.Path{v.output}, nil
298 }
299 return nil, fmt.Errorf("unsupported module reference tag %q", tag)
300}