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.OutputPath 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.OutputPath) 99 sparseImageSizes := make(map[string]android.OutputPath) 100 101 sparsePartitions := func(partitions []partitionProperties) { 102 for _, part := range partitions { 103 sparseImg, sizeTxt := sparseFilesystem(ctx, part, builder) 104 pName := proptools.String(part.Name) 105 sparseImages[pName] = sparseImg 106 sparseImageSizes[pName] = sizeTxt 107 } 108 } 109 110 for _, group := range l.properties.Groups { 111 sparsePartitions(group.Partitions) 112 } 113 114 sparsePartitions(l.properties.Default_group) 115 116 cmd := builder.Command().BuiltTool("lpmake") 117 118 size := proptools.String(l.properties.Size) 119 if size == "" { 120 ctx.PropertyErrorf("size", "must be set") 121 } else if _, err := strconv.Atoi(size); err != nil && size != "auto" { 122 ctx.PropertyErrorf("size", `must be a number or "auto"`) 123 } 124 cmd.FlagWithArg("--device-size=", size) 125 126 // TODO(jiyong): consider supporting A/B devices. Then we need to adjust num of slots. 127 cmd.FlagWithArg("--metadata-slots=", "2") 128 cmd.FlagWithArg("--metadata-size=", "65536") 129 130 if proptools.Bool(l.properties.Sparse) { 131 cmd.Flag("--sparse") 132 } 133 134 groupNames := make(map[string]bool) 135 partitionNames := make(map[string]bool) 136 137 addPartitionsToGroup := func(partitions []partitionProperties, gName string) { 138 for _, part := range partitions { 139 pName := proptools.String(part.Name) 140 if pName == "" { 141 ctx.PropertyErrorf("groups.partitions.name", "must be set") 142 } 143 if _, ok := partitionNames[pName]; ok { 144 ctx.PropertyErrorf("groups.partitions.name", "already exists") 145 } else { 146 partitionNames[pName] = true 147 } 148 // Get size of the partition by reading the -size.txt file 149 pSize := fmt.Sprintf("$(cat %s)", sparseImageSizes[pName]) 150 cmd.FlagWithArg("--partition=", fmt.Sprintf("%s:readonly:%s:%s", pName, pSize, gName)) 151 cmd.FlagWithInput("--image="+pName+"=", sparseImages[pName]) 152 } 153 } 154 155 addPartitionsToGroup(l.properties.Default_group, "default") 156 157 for _, group := range l.properties.Groups { 158 gName := proptools.String(group.Name) 159 if gName == "" { 160 ctx.PropertyErrorf("groups.name", "must be set") 161 } else if gName == "default" { 162 ctx.PropertyErrorf("groups.name", `can't use "default" as a group name. Use default_group instead`) 163 } 164 if _, ok := groupNames[gName]; ok { 165 ctx.PropertyErrorf("group.name", "already exists") 166 } else { 167 groupNames[gName] = true 168 } 169 gSize := proptools.String(group.Size) 170 if gSize == "" { 171 ctx.PropertyErrorf("groups.size", "must be set") 172 } 173 if _, err := strconv.Atoi(gSize); err != nil { 174 ctx.PropertyErrorf("groups.size", "must be a number") 175 } 176 cmd.FlagWithArg("--group=", gName+":"+gSize) 177 178 addPartitionsToGroup(group.Partitions, gName) 179 } 180 181 l.output = android.PathForModuleOut(ctx, l.installFileName()).OutputPath 182 cmd.FlagWithOutput("--output=", l.output) 183 184 builder.Build("build_logical_partition", fmt.Sprintf("Creating %s", l.BaseModuleName())) 185 186 l.installDir = android.PathForModuleInstall(ctx, "etc") 187 ctx.InstallFile(l.installDir, l.installFileName(), l.output) 188} 189 190// Add a rule that converts the filesystem for the given partition to the given rule builder. The 191// path to the sparse file and the text file having the size of the partition are returned. 192func sparseFilesystem(ctx android.ModuleContext, p partitionProperties, builder *android.RuleBuilder) (sparseImg android.OutputPath, sizeTxt android.OutputPath) { 193 img := android.PathForModuleSrc(ctx, proptools.String(p.Filesystem)) 194 name := proptools.String(p.Name) 195 sparseImg = android.PathForModuleOut(ctx, name+".img").OutputPath 196 197 builder.Temporary(sparseImg) 198 builder.Command().BuiltTool("img2simg").Input(img).Output(sparseImg) 199 200 sizeTxt = android.PathForModuleOut(ctx, name+"-size.txt").OutputPath 201 builder.Temporary(sizeTxt) 202 builder.Command().BuiltTool("sparse_img").Flag("--get_partition_size").Input(sparseImg). 203 Text("| ").Text("tr").FlagWithArg("-d ", "'\n'"). 204 Text("> ").Output(sizeTxt) 205 206 return sparseImg, sizeTxt 207} 208 209var _ android.AndroidMkEntriesProvider = (*logicalPartition)(nil) 210 211// Implements android.AndroidMkEntriesProvider 212func (l *logicalPartition) AndroidMkEntries() []android.AndroidMkEntries { 213 return []android.AndroidMkEntries{android.AndroidMkEntries{ 214 Class: "ETC", 215 OutputFile: android.OptionalPathForPath(l.output), 216 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 217 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 218 entries.SetString("LOCAL_MODULE_PATH", l.installDir.ToMakePath().String()) 219 entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.installFileName()) 220 }, 221 }, 222 }} 223} 224 225var _ Filesystem = (*logicalPartition)(nil) 226 227func (l *logicalPartition) OutputPath() android.Path { 228 return l.output 229} 230 231func (l *logicalPartition) SignedOutputPath() android.Path { 232 return nil // logical partition is not signed by itself 233} 234 235var _ android.OutputFileProducer = (*logicalPartition)(nil) 236 237// Implements android.OutputFileProducer 238func (l *logicalPartition) OutputFiles(tag string) (android.Paths, error) { 239 if tag == "" { 240 return []android.Path{l.output}, nil 241 } 242 return nil, fmt.Errorf("unsupported module reference tag %q", tag) 243} 244