blob: cf7e125a099ab79beea247358da9b2eeac23ee13 [file] [log] [blame]
mrziwang79730d42024-12-02 22:13:59 -08001// 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 filesystem
16
17import (
18 "fmt"
19 "path/filepath"
Cole Faust498ffc12025-01-15 14:19:32 -080020 "regexp"
Cole Faust74ee4e02025-01-16 14:55:35 -080021 "slices"
mrziwang79730d42024-12-02 22:13:59 -080022 "strconv"
23 "strings"
24
25 "android/soong/android"
Cole Faust2bdc5e52025-01-10 10:29:36 -080026
mrziwang79730d42024-12-02 22:13:59 -080027 "github.com/google/blueprint"
28 "github.com/google/blueprint/proptools"
29)
30
31func init() {
32 android.RegisterModuleType("super_image", SuperImageFactory)
33}
34
35type superImage struct {
36 android.ModuleBase
37
38 properties SuperImageProperties
39 partitionProps SuperImagePartitionNameProperties
40
41 installDir android.InstallPath
42}
43
44type SuperImageProperties struct {
45 // the size of the super partition
46 Size *int64
47 // the block device where metadata for dynamic partitions is stored
48 Metadata_device *string
49 // the super partition block device list
mrziwangf3c8ddf2024-12-05 17:15:11 -080050 Block_devices []string
mrziwang79730d42024-12-02 22:13:59 -080051 // whether A/B updater is used
52 Ab_update *bool
53 // whether dynamic partitions is enabled on devices that were launched without this support
54 Retrofit *bool
mrziwang79730d42024-12-02 22:13:59 -080055 // whether the output is a sparse image
56 Sparse *bool
57 // information about how partitions within the super partition are grouped together
58 Partition_groups []PartitionGroupsInfo
Cole Faust74ee4e02025-01-16 14:55:35 -080059 // Name of the system_other partition filesystem module. This module will be installed to
60 // the "b" slot of the system partition in a/b partition builds.
61 System_other_partition *string
mrziwang79730d42024-12-02 22:13:59 -080062 // whether dynamic partitions is used
63 Use_dynamic_partitions *bool
Cole Faust498ffc12025-01-15 14:19:32 -080064 Virtual_ab struct {
65 // whether virtual A/B seamless update is enabled
66 Enable *bool
67 // whether retrofitting virtual A/B seamless update is enabled
68 Retrofit *bool
69 // If set, device uses virtual A/B Compression
70 Compression *bool
71 // This value controls the compression algorithm used for VABC.
72 // Valid options are defined in system/core/fs_mgr/libsnapshot/cow_writer.cpp
73 // e.g. "none", "gz", "brotli"
74 Compression_method *string
75 // Specifies maximum bytes to be compressed at once during ota. Options: 4096, 8192, 16384, 32768, 65536, 131072, 262144.
76 Compression_factor *int64
77 // Specifies COW version to be used by update_engine and libsnapshot. If this value is not
78 // specified we default to COW version 2 in update_engine for backwards compatibility
79 Cow_version *int64
80 }
Spandan Das9da8a2d2025-02-28 07:45:30 +000081 // Whether the super image will be disted in the update package
82 Super_image_in_update_package *bool
Spandan Dasd73441e2025-03-10 22:34:25 +000083 // Whether a super_empty.img should be created
84 Create_super_empty *bool
mrziwang79730d42024-12-02 22:13:59 -080085}
86
87type PartitionGroupsInfo struct {
88 Name string
89 GroupSize string
90 PartitionList []string
91}
92
93type SuperImagePartitionNameProperties struct {
94 // Name of the System partition filesystem module
95 System_partition *string
96 // Name of the System_ext partition filesystem module
97 System_ext_partition *string
98 // Name of the System_dlkm partition filesystem module
99 System_dlkm_partition *string
100 // Name of the System_other partition filesystem module
101 System_other_partition *string
102 // Name of the Product partition filesystem module
103 Product_partition *string
104 // Name of the Vendor partition filesystem module
105 Vendor_partition *string
106 // Name of the Vendor_dlkm partition filesystem module
107 Vendor_dlkm_partition *string
108 // Name of the Odm partition filesystem module
109 Odm_partition *string
110 // Name of the Odm_dlkm partition filesystem module
111 Odm_dlkm_partition *string
112}
113
Cole Faust2bdc5e52025-01-10 10:29:36 -0800114type SuperImageInfo struct {
115 // The built super.img file, which contains the sub-partitions
116 SuperImage android.Path
117
118 // Mapping from the sub-partition type to its re-exported FileSystemInfo providers from the
119 // sub-partitions.
120 SubImageInfo map[string]FilesystemInfo
Spandan Dasf079f0d2025-02-27 13:13:50 +0000121
122 DynamicPartitionsInfo android.Path
Spandan Dasd73441e2025-03-10 22:34:25 +0000123
124 SuperEmptyImage android.Path
Spandan Das8d1d4e02025-03-18 20:07:51 +0000125
126 AbUpdate bool
Cole Faust2bdc5e52025-01-10 10:29:36 -0800127}
128
129var SuperImageProvider = blueprint.NewProvider[SuperImageInfo]()
130
mrziwang79730d42024-12-02 22:13:59 -0800131func SuperImageFactory() android.Module {
132 module := &superImage{}
133 module.AddProperties(&module.properties, &module.partitionProps)
134 android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
135 return module
136}
137
138type superImageDepTagType struct {
139 blueprint.BaseDependencyTag
140}
141
Cole Faust2bdc5e52025-01-10 10:29:36 -0800142var subImageDepTag superImageDepTagType
mrziwang79730d42024-12-02 22:13:59 -0800143
Cole Faust74ee4e02025-01-16 14:55:35 -0800144type systemOtherDepTagType struct {
145 blueprint.BaseDependencyTag
146}
147
148var systemOtherDepTag systemOtherDepTagType
149
mrziwang79730d42024-12-02 22:13:59 -0800150func (s *superImage) DepsMutator(ctx android.BottomUpMutatorContext) {
151 addDependencyIfDefined := func(dep *string) {
152 if dep != nil {
Cole Faust2bdc5e52025-01-10 10:29:36 -0800153 ctx.AddDependency(ctx.Module(), subImageDepTag, proptools.String(dep))
mrziwang79730d42024-12-02 22:13:59 -0800154 }
155 }
156
157 addDependencyIfDefined(s.partitionProps.System_partition)
158 addDependencyIfDefined(s.partitionProps.System_ext_partition)
159 addDependencyIfDefined(s.partitionProps.System_dlkm_partition)
160 addDependencyIfDefined(s.partitionProps.System_other_partition)
161 addDependencyIfDefined(s.partitionProps.Product_partition)
162 addDependencyIfDefined(s.partitionProps.Vendor_partition)
163 addDependencyIfDefined(s.partitionProps.Vendor_dlkm_partition)
164 addDependencyIfDefined(s.partitionProps.Odm_partition)
165 addDependencyIfDefined(s.partitionProps.Odm_dlkm_partition)
Cole Faust74ee4e02025-01-16 14:55:35 -0800166 if s.properties.System_other_partition != nil {
167 ctx.AddDependency(ctx.Module(), systemOtherDepTag, *s.properties.System_other_partition)
168 }
mrziwang79730d42024-12-02 22:13:59 -0800169}
170
171func (s *superImage) GenerateAndroidBuildActions(ctx android.ModuleContext) {
Spandan Dasd73441e2025-03-10 22:34:25 +0000172 miscInfo, deps, subImageInfos := s.buildMiscInfo(ctx, false)
mrziwang79730d42024-12-02 22:13:59 -0800173 builder := android.NewRuleBuilder(pctx, ctx)
174 output := android.PathForModuleOut(ctx, s.installFileName())
175 lpMake := ctx.Config().HostToolPath(ctx, "lpmake")
176 lpMakeDir := filepath.Dir(lpMake.String())
177 deps = append(deps, lpMake)
178 builder.Command().Textf("PATH=%s:\\$PATH", lpMakeDir).
179 BuiltTool("build_super_image").
180 Text("-v").
181 Input(miscInfo).
182 Implicits(deps).
183 Output(output)
184 builder.Build("build_super_image", fmt.Sprintf("Creating super image %s", s.BaseModuleName()))
Spandan Dasd73441e2025-03-10 22:34:25 +0000185 var superEmptyImage android.WritablePath
186 if proptools.Bool(s.properties.Create_super_empty) {
187 superEmptyImageBuilder := android.NewRuleBuilder(pctx, ctx)
188 superEmptyImage = android.PathForModuleOut(ctx, "super_empty.img")
189 superEmptyMiscInfo, superEmptyDeps, _ := s.buildMiscInfo(ctx, true)
190 if superEmptyDeps != nil {
191 ctx.ModuleErrorf("TODO: Handle additional deps when building super_empty.img")
192 }
193 superEmptyImageBuilder.Command().Textf("PATH=%s:\\$PATH", lpMakeDir).
194 BuiltTool("build_super_image").
195 Text("-v").
196 Input(superEmptyMiscInfo).
197 Implicit(lpMake).
198 Output(superEmptyImage)
199 superEmptyImageBuilder.Build("build_super_empty_image", fmt.Sprintf("Creating super empty image %s", s.BaseModuleName()))
200 }
Cole Faust2bdc5e52025-01-10 10:29:36 -0800201 android.SetProvider(ctx, SuperImageProvider, SuperImageInfo{
Spandan Dasf079f0d2025-02-27 13:13:50 +0000202 SuperImage: output,
203 SubImageInfo: subImageInfos,
204 DynamicPartitionsInfo: s.generateDynamicPartitionsInfo(ctx),
Spandan Dasd73441e2025-03-10 22:34:25 +0000205 SuperEmptyImage: superEmptyImage,
Spandan Das8d1d4e02025-03-18 20:07:51 +0000206 AbUpdate: proptools.Bool(s.properties.Ab_update),
Cole Faust2bdc5e52025-01-10 10:29:36 -0800207 })
mrziwang79730d42024-12-02 22:13:59 -0800208 ctx.SetOutputFiles([]android.Path{output}, "")
Cole Faust2bdc5e52025-01-10 10:29:36 -0800209 ctx.CheckbuildFile(output)
Wei Li7b8455f2025-03-05 16:05:51 -0800210
211 buildComplianceMetadata(ctx, subImageDepTag)
mrziwang79730d42024-12-02 22:13:59 -0800212}
213
214func (s *superImage) installFileName() string {
Cole Fausta472a6f2025-02-10 16:10:04 -0800215 return "super.img"
mrziwang79730d42024-12-02 22:13:59 -0800216}
217
Spandan Dasd73441e2025-03-10 22:34:25 +0000218func (s *superImage) buildMiscInfo(ctx android.ModuleContext, superEmpty bool) (android.Path, android.Paths, map[string]FilesystemInfo) {
mrziwang79730d42024-12-02 22:13:59 -0800219 var miscInfoString strings.Builder
Spandan Dasf079f0d2025-02-27 13:13:50 +0000220 partitionList := s.dumpDynamicPartitionInfo(ctx, &miscInfoString)
mrziwang79730d42024-12-02 22:13:59 -0800221 addStr := func(name string, value string) {
222 miscInfoString.WriteString(name)
223 miscInfoString.WriteRune('=')
224 miscInfoString.WriteString(value)
225 miscInfoString.WriteRune('\n')
226 }
Spandan Das9284f672025-03-10 17:29:53 +0000227 addStr("ab_update", strconv.FormatBool(proptools.Bool(s.properties.Ab_update)))
Spandan Dasd73441e2025-03-10 22:34:25 +0000228 if superEmpty {
229 miscInfo := android.PathForModuleOut(ctx, "misc_info_super_empty.txt")
230 android.WriteFileRule(ctx, miscInfo, miscInfoString.String())
231 return miscInfo, nil, nil
232 }
mrziwang79730d42024-12-02 22:13:59 -0800233
Cole Faust2bdc5e52025-01-10 10:29:36 -0800234 subImageInfo := make(map[string]FilesystemInfo)
235 var deps android.Paths
236
237 missingPartitionErrorMessage := ""
238 handleSubPartition := func(partitionType string, name *string) {
239 if proptools.String(name) == "" {
240 missingPartitionErrorMessage += fmt.Sprintf("%s image listed in partition groups, but its module was not specified. ", partitionType)
241 return
mrziwang79730d42024-12-02 22:13:59 -0800242 }
Cole Faust2bdc5e52025-01-10 10:29:36 -0800243 mod := ctx.GetDirectDepWithTag(*name, subImageDepTag)
244 if mod == nil {
245 ctx.ModuleErrorf("Could not get dep %q", *name)
246 return
247 }
248 info, ok := android.OtherModuleProvider(ctx, mod, FilesystemProvider)
249 if !ok {
250 ctx.ModuleErrorf("Expected dep %q to provide FilesystemInfo", *name)
251 return
252 }
253 addStr(partitionType+"_image", info.Output.String())
254 deps = append(deps, info.Output)
255 if _, ok := subImageInfo[partitionType]; ok {
256 ctx.ModuleErrorf("Already set subimageInfo for %q", partitionType)
257 }
258 subImageInfo[partitionType] = info
mrziwang79730d42024-12-02 22:13:59 -0800259 }
260
261 // Build partitionToImagePath, because system partition may need system_other
262 // partition image path
263 for _, p := range partitionList {
mrziwang79730d42024-12-02 22:13:59 -0800264 switch p {
265 case "system":
Cole Faust2bdc5e52025-01-10 10:29:36 -0800266 handleSubPartition("system", s.partitionProps.System_partition)
mrziwang79730d42024-12-02 22:13:59 -0800267 case "system_dlkm":
Cole Faust2bdc5e52025-01-10 10:29:36 -0800268 handleSubPartition("system_dlkm", s.partitionProps.System_dlkm_partition)
mrziwang79730d42024-12-02 22:13:59 -0800269 case "system_ext":
Cole Faust2bdc5e52025-01-10 10:29:36 -0800270 handleSubPartition("system_ext", s.partitionProps.System_ext_partition)
mrziwang79730d42024-12-02 22:13:59 -0800271 case "product":
Cole Faust2bdc5e52025-01-10 10:29:36 -0800272 handleSubPartition("product", s.partitionProps.Product_partition)
mrziwang79730d42024-12-02 22:13:59 -0800273 case "vendor":
Cole Faust2bdc5e52025-01-10 10:29:36 -0800274 handleSubPartition("vendor", s.partitionProps.Vendor_partition)
mrziwang79730d42024-12-02 22:13:59 -0800275 case "vendor_dlkm":
Cole Faust2bdc5e52025-01-10 10:29:36 -0800276 handleSubPartition("vendor_dlkm", s.partitionProps.Vendor_dlkm_partition)
mrziwang79730d42024-12-02 22:13:59 -0800277 case "odm":
Cole Faust2bdc5e52025-01-10 10:29:36 -0800278 handleSubPartition("odm", s.partitionProps.Odm_partition)
mrziwang79730d42024-12-02 22:13:59 -0800279 case "odm_dlkm":
Cole Faust2bdc5e52025-01-10 10:29:36 -0800280 handleSubPartition("odm_dlkm", s.partitionProps.Odm_dlkm_partition)
mrziwang79730d42024-12-02 22:13:59 -0800281 default:
Cole Faust2bdc5e52025-01-10 10:29:36 -0800282 ctx.ModuleErrorf("partition %q is not a super image supported partition", p)
mrziwang79730d42024-12-02 22:13:59 -0800283 }
284 }
285
Cole Faust74ee4e02025-01-16 14:55:35 -0800286 if s.properties.System_other_partition != nil {
287 if !slices.Contains(partitionList, "system") {
288 ctx.PropertyErrorf("system_other_partition", "Must have a system partition to use a system_other partition")
289 }
290 systemOther := ctx.GetDirectDepProxyWithTag(*s.properties.System_other_partition, systemOtherDepTag)
291 systemOtherFiles := android.OutputFilesForModule(ctx, systemOther, "")
292 if len(systemOtherFiles) != 1 {
293 ctx.PropertyErrorf("system_other_partition", "Expected 1 output file from module %q", *&s.properties.System_other_partition)
294 } else {
Spandan Das7a42d1c2025-02-12 01:32:21 +0000295 handleSubPartition("system_other", s.partitionProps.System_other_partition)
Cole Faust74ee4e02025-01-16 14:55:35 -0800296 }
297 }
298
Cole Faust2bdc5e52025-01-10 10:29:36 -0800299 // Delay the error message until execution time because on aosp-main-future-without-vendor,
300 // BUILDING_VENDOR_IMAGE is false so we don't get the vendor image, but it's still listed in
301 // BOARD_GOOGLE_DYNAMIC_PARTITIONS_PARTITION_LIST.
302 missingPartitionErrorMessageFile := android.PathForModuleOut(ctx, "missing_partition_error.txt")
303 if missingPartitionErrorMessage != "" {
304 ctx.Build(pctx, android.BuildParams{
305 Rule: android.ErrorRule,
306 Output: missingPartitionErrorMessageFile,
307 Args: map[string]string{
308 "error": missingPartitionErrorMessage,
309 },
310 })
311 } else {
312 ctx.Build(pctx, android.BuildParams{
313 Rule: android.Touch,
314 Output: missingPartitionErrorMessageFile,
315 })
Naresh Kumar Podishetty (xWF)4173c5b2025-01-09 20:20:32 -0800316 }
317
mrziwang79730d42024-12-02 22:13:59 -0800318 miscInfo := android.PathForModuleOut(ctx, "misc_info.txt")
Cole Faust2bdc5e52025-01-10 10:29:36 -0800319 android.WriteFileRule(ctx, miscInfo, miscInfoString.String(), missingPartitionErrorMessageFile)
320 return miscInfo, deps, subImageInfo
mrziwang79730d42024-12-02 22:13:59 -0800321}
Spandan Dasf079f0d2025-02-27 13:13:50 +0000322
323func (s *superImage) dumpDynamicPartitionInfo(ctx android.ModuleContext, sb *strings.Builder) []string {
324 addStr := func(name string, value string) {
325 sb.WriteString(name)
326 sb.WriteRune('=')
327 sb.WriteString(value)
328 sb.WriteRune('\n')
329 }
330
Spandan Dasf079f0d2025-02-27 13:13:50 +0000331 addStr("use_dynamic_partitions", strconv.FormatBool(proptools.Bool(s.properties.Use_dynamic_partitions)))
332 if proptools.Bool(s.properties.Retrofit) {
333 addStr("dynamic_partition_retrofit", "true")
334 }
335 addStr("lpmake", "lpmake")
Spandan Das0ff1dcd2025-03-11 18:21:40 +0000336 addStr("build_super_partition", "true")
337 if proptools.Bool(s.properties.Create_super_empty) {
338 addStr("build_super_empty_partition", "true")
339 }
Spandan Dasf079f0d2025-02-27 13:13:50 +0000340 addStr("super_metadata_device", proptools.String(s.properties.Metadata_device))
341 if len(s.properties.Block_devices) > 0 {
342 addStr("super_block_devices", strings.Join(s.properties.Block_devices, " "))
343 }
Spandan Dasf079f0d2025-02-27 13:13:50 +0000344 // TODO: In make, there's more complicated logic than just this surrounding super_*_device_size
345 addStr("super_super_device_size", strconv.Itoa(proptools.Int(s.properties.Size)))
346 var groups, partitionList []string
347 for _, groupInfo := range s.properties.Partition_groups {
348 groups = append(groups, groupInfo.Name)
349 partitionList = append(partitionList, groupInfo.PartitionList...)
Spandan Dasf079f0d2025-02-27 13:13:50 +0000350 }
Spandan Das0ff1dcd2025-03-11 18:21:40 +0000351 addStr("dynamic_partition_list", strings.Join(android.SortedUniqueStrings(partitionList), " "))
352 addStr("super_partition_groups", strings.Join(groups, " "))
Spandan Dasf079f0d2025-02-27 13:13:50 +0000353 initialPartitionListLen := len(partitionList)
354 partitionList = android.SortedUniqueStrings(partitionList)
355 if len(partitionList) != initialPartitionListLen {
356 ctx.ModuleErrorf("Duplicate partitions found in the partition_groups property")
357 }
Spandan Das0ff1dcd2025-03-11 18:21:40 +0000358 // Add Partition group info after adding `super_partition_groups` and `dynamic_partition_list`
359 for _, groupInfo := range s.properties.Partition_groups {
360 addStr("super_"+groupInfo.Name+"_group_size", groupInfo.GroupSize)
361 addStr("super_"+groupInfo.Name+"_partition_list", strings.Join(groupInfo.PartitionList, " "))
362 }
363
364 if proptools.Bool(s.properties.Super_image_in_update_package) {
365 addStr("super_image_in_update_package", "true")
366 }
367 addStr("super_partition_size", strconv.Itoa(proptools.Int(s.properties.Size)))
Spandan Dasf079f0d2025-02-27 13:13:50 +0000368
Spandan Dasf079f0d2025-02-27 13:13:50 +0000369 if proptools.Bool(s.properties.Virtual_ab.Enable) {
370 addStr("virtual_ab", "true")
371 if proptools.Bool(s.properties.Virtual_ab.Retrofit) {
372 addStr("virtual_ab_retrofit", "true")
373 }
374 addStr("virtual_ab_compression", strconv.FormatBool(proptools.Bool(s.properties.Virtual_ab.Compression)))
375 if s.properties.Virtual_ab.Compression_method != nil {
376 matched, _ := regexp.MatchString("^[a-zA-Z0-9_-]+$", *s.properties.Virtual_ab.Compression_method)
377 if !matched {
378 ctx.PropertyErrorf("virtual_ab.compression_method", "compression_method cannot have special characters")
379 }
380 addStr("virtual_ab_compression_method", *s.properties.Virtual_ab.Compression_method)
381 }
Spandan Dasf079f0d2025-02-27 13:13:50 +0000382 if s.properties.Virtual_ab.Cow_version != nil {
383 addStr("virtual_ab_cow_version", strconv.FormatInt(*s.properties.Virtual_ab.Cow_version, 10))
384 }
Spandan Das0ff1dcd2025-03-11 18:21:40 +0000385 if s.properties.Virtual_ab.Compression_factor != nil {
386 addStr("virtual_ab_compression_factor", strconv.FormatInt(*s.properties.Virtual_ab.Compression_factor, 10))
387 }
Spandan Dasf079f0d2025-02-27 13:13:50 +0000388
389 } else {
390 if s.properties.Virtual_ab.Retrofit != nil {
391 ctx.PropertyErrorf("virtual_ab.retrofit", "This property cannot be set when virtual_ab is disabled")
392 }
393 if s.properties.Virtual_ab.Compression != nil {
394 ctx.PropertyErrorf("virtual_ab.compression", "This property cannot be set when virtual_ab is disabled")
395 }
396 if s.properties.Virtual_ab.Compression_method != nil {
397 ctx.PropertyErrorf("virtual_ab.compression_method", "This property cannot be set when virtual_ab is disabled")
398 }
399 if s.properties.Virtual_ab.Compression_factor != nil {
400 ctx.PropertyErrorf("virtual_ab.compression_factor", "This property cannot be set when virtual_ab is disabled")
401 }
402 }
403
404 return partitionList
405}
406
407func (s *superImage) generateDynamicPartitionsInfo(ctx android.ModuleContext) android.Path {
408 var contents strings.Builder
409 s.dumpDynamicPartitionInfo(ctx, &contents)
410 dynamicPartitionsInfo := android.PathForModuleOut(ctx, "dynamic_partitions_info.txt")
Spandan Das0ff1dcd2025-03-11 18:21:40 +0000411 android.WriteFileRuleVerbatim(ctx, dynamicPartitionsInfo, contents.String())
Spandan Dasf079f0d2025-02-27 13:13:50 +0000412 return dynamicPartitionsInfo
413}