github.com/9elements/firmware-action/action@v0.0.0-20240514065043-044ed91c9ed8/recipes/edk2.go (about) 1 // SPDX-License-Identifier: MIT 2 3 // Package recipes / edk2 4 package recipes 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 "log/slog" 11 "os" 12 13 "dagger.io/dagger" 14 "github.com/9elements/firmware-action/action/container" 15 ) 16 17 // Edk2Specific is used to store data specific to coreboot. 18 /* TODO: removed because of issue #92 19 type Edk2Specific struct { 20 // Gives the (relative) path to the defconfig that should be used to build the target. 21 // For EDK2 this is a one-line file containing the build arguments such as 22 // '-D BOOTLOADER=COREBOOT -D TPM_ENABLE=TRUE -D NETWORK_IPXE=TRUE'. 23 // Some arguments will be added automatically: 24 // '-a <architecture>' 25 // '-p <edk2__platform>' 26 // '-b <edk2__release_type>' 27 // '-t <GCC version>' (defined as part of container toolchain, selected by SdkURL) 28 DefconfigPath string `json:"defconfig_path" validate:"filepath"` 29 30 // Specifies the DSC to use when building EDK2 31 // Example: UefiPayloadPkg/UefiPayloadPkg.dsc 32 Platform string `json:"platform" validate:"filepath"` 33 34 // Specifies the build type to use when building EDK2 35 // Supported options: DEBUG, RELEASE 36 ReleaseType string `json:"release_type" validate:"required"` 37 38 // Specifies which build command to use 39 // Examples: 40 // "source ./edksetup.sh; build" 41 // "python UefiPayloadPkg/UniversalPayloadBuild.py" 42 // "Intel/AlderLakeFspPkg/BuildFv.sh" 43 BuildCommand string `json:"build_command" validate:"required"` 44 } 45 */ 46 // ANCHOR: Edk2Specific 47 // Edk2Specific is used to store data specific to coreboot. 48 type Edk2Specific struct { 49 // Specifies which build command to use 50 // GCC version is exposed in the container container as USE_GCC_VERSION environment variable 51 // Examples: 52 // "source ./edksetup.sh; build -t GCC5 -a IA32 -p UefiPayloadPkg/UefiPayloadPkg.dsc" 53 // "python UefiPayloadPkg/UniversalPayloadBuild.py" 54 // "Intel/AlderLakeFspPkg/BuildFv.sh" 55 BuildCommand string `json:"build_command" validate:"required"` 56 } 57 58 // ANCHOR_END: Edk2Specific 59 60 // Edk2Opts is used to store all data needed to build edk2. 61 type Edk2Opts struct { 62 // List of IDs this instance depends on 63 // Example: [ "MyLittleCoreboot", "MyLittleLinux"] 64 Depends []string `json:"depends"` 65 66 // Common options like paths etc. 67 CommonOpts 68 69 // Specifies target architecture, such as 'x86' or 'arm64'. Currently unused for coreboot. 70 // Supported options: 71 // - 'AARCH64' 72 // - 'ARM' 73 // - 'IA32' 74 // - 'IA32X64' 75 // - 'X64' 76 Arch string `json:"arch"` 77 78 // Gives the (relative) path to the defconfig that should be used to build the target. 79 // For EDK2 this is a one-line file containing the build arguments such as 80 // '-D BOOTLOADER=COREBOOT -D TPM_ENABLE=TRUE -D NETWORK_IPXE=TRUE'. 81 DefconfigPath string `json:"defconfig_path" validate:"filepath"` 82 83 // Coreboot specific options 84 Edk2Specific `validate:"required"` 85 } 86 87 // GetDepends is used to return list of dependencies 88 func (opts Edk2Opts) GetDepends() []string { 89 return opts.Depends 90 } 91 92 // GetArtifacts returns list of wanted artifacts from container 93 func (opts Edk2Opts) GetArtifacts() *[]container.Artifacts { 94 return opts.CommonOpts.GetArtifacts() 95 } 96 97 // buildFirmware builds edk2 or Intel FSP 98 func (opts Edk2Opts) buildFirmware(ctx context.Context, client *dagger.Client, dockerfileDirectoryPath string) (*dagger.Container, error) { 99 envVars := map[string]string{ 100 "WORKSPACE": ContainerWorkDir, 101 "EDK_TOOLS_PATH": "/tools/Edk2/BaseTools", 102 } 103 104 // Spin up container 105 containerOpts := container.SetupOpts{ 106 ContainerURL: opts.SdkURL, 107 MountContainerDir: ContainerWorkDir, 108 MountHostDir: opts.RepoPath, 109 WorkdirContainer: ContainerWorkDir, 110 } 111 112 myContainer, err := container.Setup(ctx, client, &containerOpts, dockerfileDirectoryPath) 113 if err != nil { 114 slog.Error( 115 "Failed to start a container", 116 slog.Any("error", err), 117 ) 118 return nil, err 119 } 120 121 // Setup environment variables in the container 122 for key, value := range envVars { 123 myContainer = myContainer.WithEnvVariable(key, value) 124 } 125 126 // Get GCC version from environment variable 127 /* TODO: removed because of issue #92 128 gccVersion, err := myContainer.EnvVariable(ctx, "USE_GCC_VERSION") 129 if err != nil { 130 return err 131 } 132 */ 133 134 // Figure out target architectures 135 /* TODO: removed because of issue #92 136 architectures := map[string]string{ 137 "AARCH64": "-a AARCH64", 138 "ARM": "-a ARM", 139 "IA32": "-a IA32", 140 "IA32X64": "-a IA32 -a X64", 141 "X64": "-a X64", 142 } 143 arch, ok := architectures[opts.Arch] 144 if !ok { 145 return fmt.Errorf("%w: %s", errUnknownArch, opts.Arch) 146 } 147 */ 148 149 // Assemble build arguments 150 // and read content of the config file at "defconfig_path" 151 // NOTE: removed because of issue #92 152 // buildArgs := fmt.Sprintf("%s -p %s -b %s -t GCC%s", arch, opts.Specific.Platform, opts.Specific.ReleaseType, gccVersion) 153 var defconfigFileArgs []byte 154 if opts.DefconfigPath != "" { 155 if _, err := os.Stat(opts.DefconfigPath); !errors.Is(err, os.ErrNotExist) { 156 defconfigFileArgs, err = os.ReadFile(opts.DefconfigPath) 157 if err != nil { 158 return nil, err 159 } 160 } else { 161 slog.Warn( 162 fmt.Sprintf("Failed to read file '%s' as defconfig_path: file does not exist", opts.DefconfigPath), 163 slog.String("suggestion", "Double check the path for defconfig"), 164 slog.Any("error", err), 165 ) 166 } 167 } 168 169 // Assemble commands to build 170 buildSteps := [][]string{ 171 //{"bash", "-c", fmt.Sprintf("source ./edksetup.sh; build %s %s", buildArgs, string(defconfigFileArgs))}, 172 {"bash", "-c", fmt.Sprintf("%s %s", opts.BuildCommand, string(defconfigFileArgs))}, 173 } 174 175 // Build 176 var myContainerPrevious *dagger.Container 177 for step := range buildSteps { 178 myContainerPrevious = myContainer 179 myContainer, err = myContainer. 180 WithExec(buildSteps[step]). 181 Sync(ctx) 182 if err != nil { 183 slog.Error( 184 "Failed to build edk2", 185 slog.Any("error", err), 186 ) 187 return myContainerPrevious, fmt.Errorf("edk2 build failed: %w", err) 188 } 189 } 190 191 // Extract artifacts 192 return myContainer, container.GetArtifacts(ctx, myContainer, opts.GetArtifacts()) 193 }