blob: d0888a9c82b5058cd5a08700b9179013dd20cc43 [file] [log] [blame]
Jiyong Parkb89e5e72021-02-24 01:41:21 +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/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
Cole Faust4e9f5922024-11-13 16:09:23 -080035 output android.Path
Jiyong Parkb89e5e72021-02-24 01:41:21 +090036 installDir android.InstallPath
37}
38
39type logicalPartitionProperties struct {
40 // Set the name of the output. Defaults to <module_name>.img.
41 Stem *string
42
Inseob Kima46b51c2021-03-31 16:28:35 +090043 // Total size of the logical partition. If set to "auto", total size is automatically
44 // calcaulted as minimum.
Jiyong Parkb89e5e72021-02-24 01:41:21 +090045 Size *string
46
Inseob Kim152a7022021-03-30 16:51:08 +090047 // 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 Parkb89e5e72021-02-24 01:41:21 +090051 // 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 {
Inseob Kim152a7022021-03-30 16:51:08 +090060 // Name of the partition group. Can't be "default"; use default_group instead.
Jiyong Parkb89e5e72021-02-24 01:41:21 +090061 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
Cole Faust4e9f5922024-11-13 16:09:23 -080098 sparseImages := make(map[string]android.Path)
99 sparseImageSizes := make(map[string]android.Path)
Inseob Kim152a7022021-03-30 16:51:08 +0900100
101 sparsePartitions := func(partitions []partitionProperties) {
102 for _, part := range partitions {
Cole Faust4e9f5922024-11-13 16:09:23 -0800103 if part.Filesystem == nil {
104 continue
105 }
Jiyong Parkb89e5e72021-02-24 01:41:21 +0900106 sparseImg, sizeTxt := sparseFilesystem(ctx, part, builder)
107 pName := proptools.String(part.Name)
108 sparseImages[pName] = sparseImg
109 sparseImageSizes[pName] = sizeTxt
110 }
111 }
112
Inseob Kim152a7022021-03-30 16:51:08 +0900113 for _, group := range l.properties.Groups {
114 sparsePartitions(group.Partitions)
115 }
116
117 sparsePartitions(l.properties.Default_group)
118
Jiyong Parkb89e5e72021-02-24 01:41:21 +0900119 cmd := builder.Command().BuiltTool("lpmake")
120
121 size := proptools.String(l.properties.Size)
122 if size == "" {
123 ctx.PropertyErrorf("size", "must be set")
Inseob Kima46b51c2021-03-31 16:28:35 +0900124 } else if _, err := strconv.Atoi(size); err != nil && size != "auto" {
125 ctx.PropertyErrorf("size", `must be a number or "auto"`)
Jiyong Parkb89e5e72021-02-24 01:41:21 +0900126 }
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
Inseob Kim152a7022021-03-30 16:51:08 +0900140 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
Jiyong Park43b157a2024-08-27 17:58:36 +0900152 var pSize string
153 if size, hasSize := sparseImageSizes[pName]; hasSize {
154 pSize = fmt.Sprintf("$(cat %s)", size)
155 } else {
156 pSize = "0"
157 }
Inseob Kim152a7022021-03-30 16:51:08 +0900158 cmd.FlagWithArg("--partition=", fmt.Sprintf("%s:readonly:%s:%s", pName, pSize, gName))
Jiyong Park43b157a2024-08-27 17:58:36 +0900159 if image, hasImage := sparseImages[pName]; hasImage {
160 cmd.FlagWithInput("--image="+pName+"=", image)
161 }
Inseob Kim152a7022021-03-30 16:51:08 +0900162 }
163 }
164
165 addPartitionsToGroup(l.properties.Default_group, "default")
166
Jiyong Parkb89e5e72021-02-24 01:41:21 +0900167 for _, group := range l.properties.Groups {
168 gName := proptools.String(group.Name)
169 if gName == "" {
170 ctx.PropertyErrorf("groups.name", "must be set")
Inseob Kim152a7022021-03-30 16:51:08 +0900171 } else if gName == "default" {
172 ctx.PropertyErrorf("groups.name", `can't use "default" as a group name. Use default_group instead`)
Jiyong Parkb89e5e72021-02-24 01:41:21 +0900173 }
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
Inseob Kim152a7022021-03-30 16:51:08 +0900188 addPartitionsToGroup(group.Partitions, gName)
Jiyong Parkb89e5e72021-02-24 01:41:21 +0900189 }
190
Cole Faust4e9f5922024-11-13 16:09:23 -0800191 output := android.PathForModuleOut(ctx, l.installFileName())
192 cmd.FlagWithOutput("--output=", output)
Jiyong Parkb89e5e72021-02-24 01:41:21 +0900193
194 builder.Build("build_logical_partition", fmt.Sprintf("Creating %s", l.BaseModuleName()))
195
196 l.installDir = android.PathForModuleInstall(ctx, "etc")
Cole Faust4e9f5922024-11-13 16:09:23 -0800197 ctx.InstallFile(l.installDir, l.installFileName(), output)
mrziwang555d1332024-06-07 11:15:33 -0700198
Cole Faust4e9f5922024-11-13 16:09:23 -0800199 ctx.SetOutputFiles([]android.Path{output}, "")
200 l.output = output
Jiyong Parkb89e5e72021-02-24 01:41:21 +0900201}
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.
Cole Faust4e9f5922024-11-13 16:09:23 -0800205func sparseFilesystem(ctx android.ModuleContext, p partitionProperties, builder *android.RuleBuilder) (android.Path, android.Path) {
206 img := android.PathForModuleSrc(ctx, *p.Filesystem)
Jiyong Parkb89e5e72021-02-24 01:41:21 +0900207 name := proptools.String(p.Name)
Jiyong Parkb89e5e72021-02-24 01:41:21 +0900208
Cole Faust4e9f5922024-11-13 16:09:23 -0800209 sparseImg := android.PathForModuleOut(ctx, name+".img")
Jiyong Parkb89e5e72021-02-24 01:41:21 +0900210 builder.Temporary(sparseImg)
211 builder.Command().BuiltTool("img2simg").Input(img).Output(sparseImg)
212
Cole Faust4e9f5922024-11-13 16:09:23 -0800213 sizeTxt := android.PathForModuleOut(ctx, name+"-size.txt")
Jiyong Parkb89e5e72021-02-24 01:41:21 +0900214 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) {
Colin Crossc68db4b2021-11-11 18:59:15 -0800231 entries.SetString("LOCAL_MODULE_PATH", l.installDir.String())
Jiyong Parkb89e5e72021-02-24 01:41:21 +0900232 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}
Jiyong Parkb0eb3192021-03-09 20:29:07 +0900243
Jiyong Park972e06c2021-03-15 23:32:49 +0900244func (l *logicalPartition) SignedOutputPath() android.Path {
245 return nil // logical partition is not signed by itself
246}