1// 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/proptools" 22 23 "android/soong/android" 24) 25 26func init() { 27 android.RegisterModuleType("logical_partition", logicalPartitionFactory) 28} 29 30type logicalPartition struct { 31 android.ModuleBase 32 33 properties logicalPartitionProperties 34 35 output android.Path 36 installDir android.InstallPath 37} 38 39type logicalPartitionProperties struct { 40 // Set the name of the output. Defaults to <module_name>.img. 41 Stem *string 42 43 // Total size of the logical partition. If set to "auto", total size is automatically 44 // calcaulted as minimum. 45 Size *string 46 47 // List of partitions for default group. Default group has no size limit and automatically 48 // minimized when creating an image. 49 Default_group []partitionProperties 50 51 // List of groups. A group defines a fixed sized region. It can host one or more logical 52 // partitions and their total size is limited by the size of the group they are in. 53 Groups []groupProperties 54 55 // Whether the output is a sparse image or not. Default is false. 56 Sparse *bool 57} 58 59type groupProperties struct { 60 // Name of the partition group. Can't be "default"; use default_group instead. 61 Name *string 62 63 // Size of the partition group 64 Size *string 65 66 // List of logical partitions in this group 67 Partitions []partitionProperties 68} 69 70type partitionProperties struct { 71 // Name of the partition 72 Name *string 73 74 // Filesystem that is placed on the partition 75 Filesystem *string `android:"path"` 76} 77 78// logical_partition is a partition image which has one or more logical partitions in it. 79func logicalPartitionFactory() android.Module { 80 module := &logicalPartition{} 81 module.AddProperties(&module.properties) 82 android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst) 83 return module 84} 85 86func (l *logicalPartition) DepsMutator(ctx android.BottomUpMutatorContext) { 87 // do nothing 88} 89 90func (l *logicalPartition) installFileName() string { 91 return proptools.StringDefault(l.properties.Stem, l.BaseModuleName()+".img") 92} 93 94func (l *logicalPartition) GenerateAndroidBuildActions(ctx android.ModuleContext) { 95 builder := android.NewRuleBuilder(pctx, ctx) 96 97 // Sparse the filesystem images and calculate their sizes 98 sparseImages := make(map[string]android.Path) 99 sparseImageSizes := make(map[string]android.Path) 100 101 sparsePartitions := func(partitions []partitionProperties) { 102 for _, part := range partitions { 103 if part.Filesystem == nil { 104 continue 105 } 106 sparseImg, sizeTxt := sparseFilesystem(ctx, part, builder) 107 pName := proptools.String(part.Name) 108 sparseImages[pName] = sparseImg 109 sparseImageSizes[pName] = sizeTxt 110 } 111 } 112 113 for _, group := range l.properties.Groups { 114 sparsePartitions(group.Partitions) 115 } 116 117 sparsePartitions(l.properties.Default_group) 118 119 cmd := builder.Command().BuiltTool("lpmake") 120 121 size := proptools.String(l.properties.Size) 122 if size == "" { 123 ctx.PropertyErrorf("size", "must be set") 124 } else if _, err := strconv.Atoi(size); err != nil && size != "auto" { 125 ctx.PropertyErrorf("size", `must be a number or "auto"`) 126 } 127 cmd.FlagWithArg("--device-size=", size) 128 129 // TODO(jiyong): consider supporting A/B devices. Then we need to adjust num of slots. 130 cmd.FlagWithArg("--metadata-slots=", "2") 131 cmd.FlagWithArg("--metadata-size=", "65536") 132 133 if proptools.Bool(l.properties.Sparse) { 134 cmd.Flag("--sparse") 135 } 136 137 groupNames := make(map[string]bool) 138 partitionNames := make(map[string]bool) 139 140 addPartitionsToGroup := func(partitions []partitionProperties, gName string) { 141 for _, part := range partitions { 142 pName := proptools.String(part.Name) 143 if pName == "" { 144 ctx.PropertyErrorf("groups.partitions.name", "must be set") 145 } 146 if _, ok := partitionNames[pName]; ok { 147 ctx.PropertyErrorf("groups.partitions.name", "already exists") 148 } else { 149 partitionNames[pName] = true 150 } 151 // Get size of the partition by reading the -size.txt file 152 var pSize string 153 if size, hasSize := sparseImageSizes[pName]; hasSize { 154 pSize = fmt.Sprintf("$(cat %s)", size) 155 } else { 156 pSize = "0" 157 } 158 cmd.FlagWithArg("--partition=", fmt.Sprintf("%s:readonly:%s:%s", pName, pSize, gName)) 159 if image, hasImage := sparseImages[pName]; hasImage { 160 cmd.FlagWithInput("--image="+pName+"=", image) 161 } 162 } 163 } 164 165 addPartitionsToGroup(l.properties.Default_group, "default") 166 167 for _, group := range l.properties.Groups { 168 gName := proptools.String(group.Name) 169 if gName == "" { 170 ctx.PropertyErrorf("groups.name", "must be set") 171 } else if gName == "default" { 172 ctx.PropertyErrorf("groups.name", `can't use "default" as a group name. Use default_group instead`) 173 } 174 if _, ok := groupNames[gName]; ok { 175 ctx.PropertyErrorf("group.name", "already exists") 176 } else { 177 groupNames[gName] = true 178 } 179 gSize := proptools.String(group.Size) 180 if gSize == "" { 181 ctx.PropertyErrorf("groups.size", "must be set") 182 } 183 if _, err := strconv.Atoi(gSize); err != nil { 184 ctx.PropertyErrorf("groups.size", "must be a number") 185 } 186 cmd.FlagWithArg("--group=", gName+":"+gSize) 187 188 addPartitionsToGroup(group.Partitions, gName) 189 } 190 191 output := android.PathForModuleOut(ctx, l.installFileName()) 192 cmd.FlagWithOutput("--output=", output) 193 194 builder.Build("build_logical_partition", fmt.Sprintf("Creating %s", l.BaseModuleName())) 195 196 l.installDir = android.PathForModuleInstall(ctx, "etc") 197 ctx.InstallFile(l.installDir, l.installFileName(), output) 198 199 ctx.SetOutputFiles([]android.Path{output}, "") 200 l.output = output 201} 202 203// Add a rule that converts the filesystem for the given partition to the given rule builder. The 204// path to the sparse file and the text file having the size of the partition are returned. 205func sparseFilesystem(ctx android.ModuleContext, p partitionProperties, builder *android.RuleBuilder) (android.Path, android.Path) { 206 img := android.PathForModuleSrc(ctx, *p.Filesystem) 207 name := proptools.String(p.Name) 208 209 sparseImg := android.PathForModuleOut(ctx, name+".img") 210 builder.Temporary(sparseImg) 211 builder.Command().BuiltTool("img2simg").Input(img).Output(sparseImg) 212 213 sizeTxt := android.PathForModuleOut(ctx, name+"-size.txt") 214 builder.Temporary(sizeTxt) 215 builder.Command().BuiltTool("sparse_img").Flag("--get_partition_size").Input(sparseImg). 216 Text("| ").Text("tr").FlagWithArg("-d ", "'\n'"). 217 Text("> ").Output(sizeTxt) 218 219 return sparseImg, sizeTxt 220} 221 222var _ android.AndroidMkEntriesProvider = (*logicalPartition)(nil) 223 224// Implements android.AndroidMkEntriesProvider 225func (l *logicalPartition) AndroidMkEntries() []android.AndroidMkEntries { 226 return []android.AndroidMkEntries{android.AndroidMkEntries{ 227 Class: "ETC", 228 OutputFile: android.OptionalPathForPath(l.output), 229 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 230 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 231 entries.SetString("LOCAL_MODULE_PATH", l.installDir.String()) 232 entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.installFileName()) 233 }, 234 }, 235 }} 236} 237 238var _ Filesystem = (*logicalPartition)(nil) 239 240func (l *logicalPartition) OutputPath() android.Path { 241 return l.output 242} 243 244func (l *logicalPartition) SignedOutputPath() android.Path { 245 return nil // logical partition is not signed by itself 246} 247