blob: 42ec062c1b05209962673488de1cb6c67d5a85c3 [file] [log] [blame]
Mateusz Zalega8ed99762022-01-25 19:02:22 +01001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17// mkpayload is an objcopy wrapper that builds EFI unified kernel images. It
18// performs actions that can't be realized by either objcopy or the
19// buildsystem.
20package main
21
22import (
23 "flag"
24 "fmt"
25 "log"
26 "os"
27 "os/exec"
28 "strings"
29)
30
31var (
32 // sections contains VMAs and source files of the payload PE sections. The
33 // file path pointers will be filled in when the flags are parsed. It's used
34 // to generate objcopy command line arguments. Entries that are "required"
35 // will cause the program to stop and print usage information if not provided
36 // as command line parameters.
37 sections = map[string]struct {
38 descr string
39 vma string
40 required bool
41 file *string
42 }{
43 "linux": {"Linux kernel image", "0x2000000", true, nil},
44 "initrd": {"initramfs", "0x5000000", false, nil},
45 "osrel": {"OS release file in text format", "0x20000", false, nil},
46 "cmdline": {"a file containting additional kernel command line parameters", "0x30000", false, nil},
47 "splash": {"a splash screen image in BMP format", "0x40000", false, nil},
48 }
49 objcopy = flag.String("objcopy", "", "objcopy executable")
50 stub = flag.String("stub", "", "the EFI stub executable")
51 output = flag.String("output", "", "objcopy output")
52 rootfs_dm_table = flag.String("rootfs_dm_table", "", "a text file containing the DeviceMapper rootfs target table")
53)
54
55func main() {
56 // Register parameters related to the EFI payload sections, then parse the flags.
57 for k, v := range sections {
58 v.file = flag.String(k, "", v.descr)
59 sections[k] = v
60 }
61 flag.Parse()
62
63 // Ensure all the required parameters are filled in.
64 for n, s := range sections {
65 if s.required && *s.file == "" {
66 log.Fatalf("-%s parameter is missing.", n)
67 }
68 }
69 if *objcopy == "" {
70 log.Fatalf("-objcopy parameter is missing.")
71 }
72 if *stub == "" {
73 log.Fatalf("-stub parameter is missing.")
74 }
75 if *output == "" {
76 log.Fatalf("-output parameter is missing.")
77 }
78
79 // If a DeviceMapper table was passed, configure the kernel to boot from the
80 // device described by it, while keeping any other kernel command line
81 // parameters that might have been passed through "-cmdline".
82 if *rootfs_dm_table != "" {
83 var cmdline string
84 p := *sections["cmdline"].file
85 if p != "" {
86 c, err := os.ReadFile(p)
87 if err != nil {
88 log.Fatalf("%v", err)
89 }
90 cmdline = string(c[:])
91
92 if strings.Contains(cmdline, "root=") {
93 log.Fatalf("A DeviceMapper table was passed, however the kernel command line already contains a \"root=\" statement.")
94 }
95 }
96
97 vt, err := os.ReadFile(*rootfs_dm_table)
98 if err != nil {
99 log.Fatalf("%v", err)
100 }
101 cmdline += fmt.Sprintf(" dm-mod.create=\"rootfs,,,ro,%s\" root=/dev/dm-0", vt)
102
103 out, err := os.CreateTemp(".", "cmdline")
104 if err != nil {
105 log.Fatalf("%v", err)
106 }
107 defer os.Remove(out.Name())
108 if _, err = out.Write([]byte(cmdline[:])); err != nil {
109 log.Fatalf("%v", err)
110 }
111 out.Close()
112
113 *sections["cmdline"].file = out.Name()
114 }
115
116 // Execute objcopy
117 var args []string
118 for name, c := range sections {
119 if *c.file != "" {
120 args = append(args, []string{
121 "--add-section", fmt.Sprintf(".%s=%s", name, *c.file),
122 "--change-section-vma", fmt.Sprintf(".%s=%s", name, c.vma),
123 }...)
124 }
125 }
126 args = append(args, []string{
127 *stub,
128 *output,
129 }...)
130 cmd := exec.Command(*objcopy, args...)
131 cmd.Stderr = os.Stderr
132 cmd.Stdout = os.Stdout
133 err := cmd.Run()
134 if err == nil {
135 return
136 }
137 // Exit with objcopy's return code.
138 if e, ok := err.(*exec.ExitError); ok {
139 os.Exit(e.ExitCode())
140 }
141 log.Fatalf("Could not start command: %v", err)
142}