| Jiyong Park | b89e5e7 | 2021-02-24 01:41:21 +0900 | [diff] [blame] | 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 |  | 
|  | 15 | package filesystem | 
|  | 16 |  | 
|  | 17 | import ( | 
|  | 18 | "fmt" | 
|  | 19 | "strconv" | 
|  | 20 |  | 
|  | 21 | "github.com/google/blueprint/proptools" | 
|  | 22 |  | 
|  | 23 | "android/soong/android" | 
|  | 24 | ) | 
|  | 25 |  | 
|  | 26 | func init() { | 
|  | 27 | android.RegisterModuleType("logical_partition", logicalPartitionFactory) | 
|  | 28 | } | 
|  | 29 |  | 
|  | 30 | type logicalPartition struct { | 
|  | 31 | android.ModuleBase | 
|  | 32 |  | 
|  | 33 | properties logicalPartitionProperties | 
|  | 34 |  | 
|  | 35 | output     android.OutputPath | 
|  | 36 | installDir android.InstallPath | 
|  | 37 | } | 
|  | 38 |  | 
|  | 39 | type logicalPartitionProperties struct { | 
|  | 40 | // Set the name of the output. Defaults to <module_name>.img. | 
|  | 41 | Stem *string | 
|  | 42 |  | 
| Inseob Kim | a46b51c | 2021-03-31 16:28:35 +0900 | [diff] [blame] | 43 | // Total size of the logical partition. If set to "auto", total size is automatically | 
|  | 44 | // calcaulted as minimum. | 
| Jiyong Park | b89e5e7 | 2021-02-24 01:41:21 +0900 | [diff] [blame] | 45 | Size *string | 
|  | 46 |  | 
| Inseob Kim | 152a702 | 2021-03-30 16:51:08 +0900 | [diff] [blame] | 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 |  | 
| Jiyong Park | b89e5e7 | 2021-02-24 01:41:21 +0900 | [diff] [blame] | 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 |  | 
|  | 59 | type groupProperties struct { | 
| Inseob Kim | 152a702 | 2021-03-30 16:51:08 +0900 | [diff] [blame] | 60 | // Name of the partition group. Can't be "default"; use default_group instead. | 
| Jiyong Park | b89e5e7 | 2021-02-24 01:41:21 +0900 | [diff] [blame] | 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 |  | 
|  | 70 | type 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. | 
|  | 79 | func logicalPartitionFactory() android.Module { | 
|  | 80 | module := &logicalPartition{} | 
|  | 81 | module.AddProperties(&module.properties) | 
|  | 82 | android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst) | 
|  | 83 | return module | 
|  | 84 | } | 
|  | 85 |  | 
|  | 86 | func (l *logicalPartition) DepsMutator(ctx android.BottomUpMutatorContext) { | 
|  | 87 | // do nothing | 
|  | 88 | } | 
|  | 89 |  | 
|  | 90 | func (l *logicalPartition) installFileName() string { | 
|  | 91 | return proptools.StringDefault(l.properties.Stem, l.BaseModuleName()+".img") | 
|  | 92 | } | 
|  | 93 |  | 
|  | 94 | func (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) | 
| Inseob Kim | 152a702 | 2021-03-30 16:51:08 +0900 | [diff] [blame] | 100 |  | 
|  | 101 | sparsePartitions := func(partitions []partitionProperties) { | 
|  | 102 | for _, part := range partitions { | 
| Jiyong Park | b89e5e7 | 2021-02-24 01:41:21 +0900 | [diff] [blame] | 103 | sparseImg, sizeTxt := sparseFilesystem(ctx, part, builder) | 
|  | 104 | pName := proptools.String(part.Name) | 
|  | 105 | sparseImages[pName] = sparseImg | 
|  | 106 | sparseImageSizes[pName] = sizeTxt | 
|  | 107 | } | 
|  | 108 | } | 
|  | 109 |  | 
| Inseob Kim | 152a702 | 2021-03-30 16:51:08 +0900 | [diff] [blame] | 110 | for _, group := range l.properties.Groups { | 
|  | 111 | sparsePartitions(group.Partitions) | 
|  | 112 | } | 
|  | 113 |  | 
|  | 114 | sparsePartitions(l.properties.Default_group) | 
|  | 115 |  | 
| Jiyong Park | b89e5e7 | 2021-02-24 01:41:21 +0900 | [diff] [blame] | 116 | cmd := builder.Command().BuiltTool("lpmake") | 
|  | 117 |  | 
|  | 118 | size := proptools.String(l.properties.Size) | 
|  | 119 | if size == "" { | 
|  | 120 | ctx.PropertyErrorf("size", "must be set") | 
| Inseob Kim | a46b51c | 2021-03-31 16:28:35 +0900 | [diff] [blame] | 121 | } else if _, err := strconv.Atoi(size); err != nil && size != "auto" { | 
|  | 122 | ctx.PropertyErrorf("size", `must be a number or "auto"`) | 
| Jiyong Park | b89e5e7 | 2021-02-24 01:41:21 +0900 | [diff] [blame] | 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 |  | 
| Inseob Kim | 152a702 | 2021-03-30 16:51:08 +0900 | [diff] [blame] | 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 | 
| Jiyong Park | 43b157a | 2024-08-27 17:58:36 +0900 | [diff] [blame] | 149 | var pSize string | 
|  | 150 | if size, hasSize := sparseImageSizes[pName]; hasSize { | 
|  | 151 | pSize = fmt.Sprintf("$(cat %s)", size) | 
|  | 152 | } else { | 
|  | 153 | pSize = "0" | 
|  | 154 | } | 
| Inseob Kim | 152a702 | 2021-03-30 16:51:08 +0900 | [diff] [blame] | 155 | cmd.FlagWithArg("--partition=", fmt.Sprintf("%s:readonly:%s:%s", pName, pSize, gName)) | 
| Jiyong Park | 43b157a | 2024-08-27 17:58:36 +0900 | [diff] [blame] | 156 | if image, hasImage := sparseImages[pName]; hasImage { | 
|  | 157 | cmd.FlagWithInput("--image="+pName+"=", image) | 
|  | 158 | } | 
| Inseob Kim | 152a702 | 2021-03-30 16:51:08 +0900 | [diff] [blame] | 159 | } | 
|  | 160 | } | 
|  | 161 |  | 
|  | 162 | addPartitionsToGroup(l.properties.Default_group, "default") | 
|  | 163 |  | 
| Jiyong Park | b89e5e7 | 2021-02-24 01:41:21 +0900 | [diff] [blame] | 164 | for _, group := range l.properties.Groups { | 
|  | 165 | gName := proptools.String(group.Name) | 
|  | 166 | if gName == "" { | 
|  | 167 | ctx.PropertyErrorf("groups.name", "must be set") | 
| Inseob Kim | 152a702 | 2021-03-30 16:51:08 +0900 | [diff] [blame] | 168 | } else if gName == "default" { | 
|  | 169 | ctx.PropertyErrorf("groups.name", `can't use "default" as a group name. Use default_group instead`) | 
| Jiyong Park | b89e5e7 | 2021-02-24 01:41:21 +0900 | [diff] [blame] | 170 | } | 
|  | 171 | if _, ok := groupNames[gName]; ok { | 
|  | 172 | ctx.PropertyErrorf("group.name", "already exists") | 
|  | 173 | } else { | 
|  | 174 | groupNames[gName] = true | 
|  | 175 | } | 
|  | 176 | gSize := proptools.String(group.Size) | 
|  | 177 | if gSize == "" { | 
|  | 178 | ctx.PropertyErrorf("groups.size", "must be set") | 
|  | 179 | } | 
|  | 180 | if _, err := strconv.Atoi(gSize); err != nil { | 
|  | 181 | ctx.PropertyErrorf("groups.size", "must be a number") | 
|  | 182 | } | 
|  | 183 | cmd.FlagWithArg("--group=", gName+":"+gSize) | 
|  | 184 |  | 
| Inseob Kim | 152a702 | 2021-03-30 16:51:08 +0900 | [diff] [blame] | 185 | addPartitionsToGroup(group.Partitions, gName) | 
| Jiyong Park | b89e5e7 | 2021-02-24 01:41:21 +0900 | [diff] [blame] | 186 | } | 
|  | 187 |  | 
|  | 188 | l.output = android.PathForModuleOut(ctx, l.installFileName()).OutputPath | 
|  | 189 | cmd.FlagWithOutput("--output=", l.output) | 
|  | 190 |  | 
|  | 191 | builder.Build("build_logical_partition", fmt.Sprintf("Creating %s", l.BaseModuleName())) | 
|  | 192 |  | 
|  | 193 | l.installDir = android.PathForModuleInstall(ctx, "etc") | 
|  | 194 | ctx.InstallFile(l.installDir, l.installFileName(), l.output) | 
| mrziwang | 555d133 | 2024-06-07 11:15:33 -0700 | [diff] [blame] | 195 |  | 
|  | 196 | ctx.SetOutputFiles([]android.Path{l.output}, "") | 
| Jiyong Park | b89e5e7 | 2021-02-24 01:41:21 +0900 | [diff] [blame] | 197 | } | 
|  | 198 |  | 
|  | 199 | // Add a rule that converts the filesystem for the given partition to the given rule builder. The | 
|  | 200 | // path to the sparse file and the text file having the size of the partition are returned. | 
|  | 201 | func sparseFilesystem(ctx android.ModuleContext, p partitionProperties, builder *android.RuleBuilder) (sparseImg android.OutputPath, sizeTxt android.OutputPath) { | 
| Jiyong Park | 43b157a | 2024-08-27 17:58:36 +0900 | [diff] [blame] | 202 | if p.Filesystem == nil { | 
|  | 203 | return | 
|  | 204 | } | 
| Jiyong Park | b89e5e7 | 2021-02-24 01:41:21 +0900 | [diff] [blame] | 205 | img := android.PathForModuleSrc(ctx, proptools.String(p.Filesystem)) | 
|  | 206 | name := proptools.String(p.Name) | 
|  | 207 | sparseImg = android.PathForModuleOut(ctx, name+".img").OutputPath | 
|  | 208 |  | 
|  | 209 | builder.Temporary(sparseImg) | 
|  | 210 | builder.Command().BuiltTool("img2simg").Input(img).Output(sparseImg) | 
|  | 211 |  | 
|  | 212 | sizeTxt = android.PathForModuleOut(ctx, name+"-size.txt").OutputPath | 
|  | 213 | builder.Temporary(sizeTxt) | 
|  | 214 | builder.Command().BuiltTool("sparse_img").Flag("--get_partition_size").Input(sparseImg). | 
|  | 215 | Text("| ").Text("tr").FlagWithArg("-d ", "'\n'"). | 
|  | 216 | Text("> ").Output(sizeTxt) | 
|  | 217 |  | 
|  | 218 | return sparseImg, sizeTxt | 
|  | 219 | } | 
|  | 220 |  | 
|  | 221 | var _ android.AndroidMkEntriesProvider = (*logicalPartition)(nil) | 
|  | 222 |  | 
|  | 223 | // Implements android.AndroidMkEntriesProvider | 
|  | 224 | func (l *logicalPartition) AndroidMkEntries() []android.AndroidMkEntries { | 
|  | 225 | return []android.AndroidMkEntries{android.AndroidMkEntries{ | 
|  | 226 | Class:      "ETC", | 
|  | 227 | OutputFile: android.OptionalPathForPath(l.output), | 
|  | 228 | ExtraEntries: []android.AndroidMkExtraEntriesFunc{ | 
|  | 229 | func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { | 
| Colin Cross | c68db4b | 2021-11-11 18:59:15 -0800 | [diff] [blame] | 230 | entries.SetString("LOCAL_MODULE_PATH", l.installDir.String()) | 
| Jiyong Park | b89e5e7 | 2021-02-24 01:41:21 +0900 | [diff] [blame] | 231 | entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.installFileName()) | 
|  | 232 | }, | 
|  | 233 | }, | 
|  | 234 | }} | 
|  | 235 | } | 
|  | 236 |  | 
|  | 237 | var _ Filesystem = (*logicalPartition)(nil) | 
|  | 238 |  | 
|  | 239 | func (l *logicalPartition) OutputPath() android.Path { | 
|  | 240 | return l.output | 
|  | 241 | } | 
| Jiyong Park | b0eb319 | 2021-03-09 20:29:07 +0900 | [diff] [blame] | 242 |  | 
| Jiyong Park | 972e06c | 2021-03-15 23:32:49 +0900 | [diff] [blame] | 243 | func (l *logicalPartition) SignedOutputPath() android.Path { | 
|  | 244 | return nil // logical partition is not signed by itself | 
|  | 245 | } |