xref: /aosp_15_r20/build/soong/filesystem/logical_partition.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
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