Compare commits

..

1 Commits

2 changed files with 0 additions and 594 deletions

View File

@ -1,249 +0,0 @@
package main
import (
"flag"
"fmt"
"io"
"log"
"net/http"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
)
func downloadKernel(latest string) error {
out, err := os.Create(filepath.Base(latest))
if err != nil {
return err
}
defer out.Close()
if kernel, err := os.Open("/tmp/buildresult/" + filepath.Base(latest)); err == nil {
defer kernel.Close()
if _, err := io.Copy(out, kernel); err != nil {
return err
}
return out.Close()
}
resp, err := http.Get(latest)
if err != nil {
return err
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusOK; got != want {
return fmt.Errorf("unexpected HTTP status code for %s: got %d, want %d", latest, got, want)
}
if _, err := io.Copy(out, resp.Body); err != nil {
return err
}
return out.Close()
}
func applyPatches(srcdir string) error {
patches, err := filepath.Glob("*.patch")
if err != nil {
return err
}
for _, patch := range patches {
log.Printf("applying patch %q", patch)
f, err := os.Open(patch)
if err != nil {
return err
}
defer f.Close()
cmd := exec.Command("patch", "-p1")
cmd.Dir = srcdir
cmd.Stdin = f
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return err
}
f.Close()
}
return nil
}
func compile(cross, flavor string) error {
defconfig := exec.Command("make", "defconfig")
if flavor == "raspberrypi" {
// TODO(https://github.com/gokrazy/gokrazy/issues/223): is it
// necessary/desirable to switch to bcm2712_defconfig?
defconfig = exec.Command("make", "ARCH=arm64", "bcm2711_defconfig")
}
defconfig.Stdout = os.Stdout
defconfig.Stderr = os.Stderr
if err := defconfig.Run(); err != nil {
return fmt.Errorf("make defconfig: %v", err)
}
// Change answers from mod to no if possible, i.e. disable all modules so
// that we end up with a minimal set of modules (from the config addendum).
mod2noconfig := exec.Command("make", "mod2noconfig")
mod2noconfig.Stdout = os.Stdout
mod2noconfig.Stderr = os.Stderr
if err := mod2noconfig.Run(); err != nil {
return fmt.Errorf("make mod2noconfig: %v", err)
}
f, err := os.OpenFile(".config", os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer f.Close()
addendum, err := os.ReadFile("/usr/src/config.addendum.txt")
if err != nil {
return err
}
if _, err := f.Write(addendum); err != nil {
return err
}
if err := f.Close(); err != nil {
return err
}
olddefconfig := exec.Command("make", "olddefconfig")
olddefconfig.Stdout = os.Stdout
olddefconfig.Stderr = os.Stderr
if err := olddefconfig.Run(); err != nil {
return fmt.Errorf("make olddefconfig: %v", err)
}
env := append(os.Environ(),
"KBUILD_BUILD_USER=gokrazy",
"KBUILD_BUILD_HOST=docker",
"KBUILD_BUILD_TIMESTAMP=Wed Mar 1 20:57:29 UTC 2017",
)
make := exec.Command("make", "bzImage", "modules", "-j"+strconv.Itoa(runtime.NumCPU()))
if cross == "arm64" {
make = exec.Command("make", "Image.gz", "dtbs", "modules", "-j"+strconv.Itoa(runtime.NumCPU()))
}
make.Env = env
make.Stdout = os.Stdout
make.Stderr = os.Stderr
if err := make.Run(); err != nil {
return fmt.Errorf("make: %v", err)
}
make = exec.Command("make", "INSTALL_MOD_PATH=/tmp/buildresult", "modules_install", "-j"+strconv.Itoa(runtime.NumCPU()))
make.Env = env
make.Stdout = os.Stdout
make.Stderr = os.Stderr
if err := make.Run(); err != nil {
return fmt.Errorf("make: %v", err)
}
make = exec.Command("make", "INSTALL_DTBS_PATH=/tmp/buildresult/dtbs", "dtbs_install", "-j"+strconv.Itoa(runtime.NumCPU()))
make.Env = env
make.Stdout = os.Stdout
make.Stderr = os.Stderr
if err := make.Run(); err != nil {
return fmt.Errorf("make: %v", err)
}
return nil
}
func indockerMain() {
cross := flag.String("cross",
"",
"if non-empty, cross-compile for the specified arch (one of 'arm64')")
flavor := flag.String("flavor",
"vanilla",
"which kernel flavor to build. one of vanilla (kernel.org) or raspberrypi (https://github.com/raspberrypi/linux/tags)")
flag.Parse()
latest := flag.Arg(0)
if latest == "" {
log.Fatalf("syntax: %s <upstream-URL>", os.Args[0])
}
log.Printf("downloading kernel source: %s", latest)
if err := downloadKernel(latest); err != nil {
log.Fatal(err)
}
log.Printf("unpacking kernel source")
untar := exec.Command("tar", "xf", filepath.Base(latest))
untar.Stdout = os.Stdout
untar.Stderr = os.Stderr
if err := untar.Run(); err != nil {
log.Fatalf("untar: %v", err)
}
srcdir := strings.TrimSuffix(filepath.Base(latest), ".tar.xz")
if *flavor == "raspberrypi" {
srcdir = strings.TrimSuffix("linux-"+filepath.Base(latest), ".tar.gz")
}
log.Printf("applying patches")
if err := applyPatches(srcdir); err != nil {
log.Fatal(err)
}
if err := os.Chdir(srcdir); err != nil {
log.Fatal(err)
}
if *cross == "arm64" {
log.Printf("exporting ARCH=arm64, CROSS_COMPILE=aarch64-linux-gnu-")
os.Setenv("ARCH", "arm64")
os.Setenv("CROSS_COMPILE", "aarch64-linux-gnu-")
}
log.Printf("compiling kernel")
if err := compile(*cross, *flavor); err != nil {
log.Fatal(err)
}
if *cross == "arm64" {
if err := copyFile("/tmp/buildresult/vmlinuz", "arch/arm64/boot/Image"); err != nil {
log.Fatal(err)
}
switch *flavor {
case "vanilla":
cp := exec.Command("cp", "-r", filepath.Join("arch/arm64/boot/dts/"), "/tmp/buildresult/dts")
cp.Stdout = os.Stdout
cp.Stderr = os.Stderr
log.Printf("%v", cp.Args)
if err := cp.Run(); err != nil {
log.Fatalf("%v: %v", cp.Args, err)
}
case "raspberrypi":
// copy all dtb and dtbos (+ overlay_map) to buildresult
dtbs, err := filepath.Glob("arch/arm64/boot/dts/broadcom/*.dtb")
if err != nil {
log.Fatal(err)
}
for _, fn := range dtbs {
if err := copyFile(filepath.Join("/tmp/buildresult/", filepath.Base(fn)), fn); err != nil {
log.Fatal(err)
}
}
dtbos, err := filepath.Glob("arch/arm64/boot/dts/overlays/*.dtbo")
if err != nil {
log.Fatal(err)
}
dtbos = append(dtbos, "arch/arm64/boot/dts/overlays/overlay_map.dtb")
if err := os.MkdirAll("/tmp/buildresult/overlays", 0755); err != nil {
log.Fatal(err)
}
for _, fn := range dtbos {
if err := copyFile(filepath.Join("/tmp/buildresult/overlays/", filepath.Base(fn)), fn); err != nil {
log.Fatal(err)
}
}
}
} else {
if err := copyFile("/tmp/buildresult/vmlinuz", "arch/x86/boot/bzImage"); err != nil {
log.Fatal(err)
}
}
}

View File

@ -1,345 +0,0 @@
package main
import (
"flag"
"fmt"
"io"
"log"
"os"
"os/exec"
"os/user"
"path/filepath"
"strings"
"text/template"
)
const dockerFileContents = `
FROM debian:bookworm
RUN apt-get update && apt-get install -y \
{{ if (eq .Cross "arm64") -}}
crossbuild-essential-arm64 \
{{ end -}}
build-essential bc libssl-dev bison flex libelf-dev ncurses-dev ca-certificates zstd kmod python3
COPY gokr-rebuild-kernel /usr/bin/gokr-rebuild-kernel
COPY config.addendum.txt /usr/src/config.addendum.txt
{{- range $idx, $path := .Patches }}
COPY {{ $path }} /usr/src/{{ $path }}
{{- end }}
RUN echo 'builduser:x:{{ .Uid }}:{{ .Gid }}:nobody:/:/bin/sh' >> /etc/passwd && \
chown -R {{ .Uid }}:{{ .Gid }} /usr/src
USER builduser
WORKDIR /usr/src
ENV GOKRAZY_IN_DOCKER=1
ENTRYPOINT ["/usr/bin/gokr-rebuild-kernel"]
`
var dockerFileTmpl = template.Must(template.New("dockerfile").
Funcs(map[string]interface{}{
"basename": func(path string) string {
return filepath.Base(path)
},
}).
Parse(dockerFileContents))
func copyFile(dest, src string) error {
log.Printf("copyFile(dest=%s, src=%s)", dest, src)
out, err := os.Create(dest)
if err != nil {
return err
}
defer out.Close()
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
n, err := io.Copy(out, in)
if err != nil {
return err
}
log.Printf(" -> %d bytes copied", n)
st, err := in.Stat()
if err != nil {
return err
}
if err := out.Chmod(st.Mode()); err != nil {
return err
}
return out.Close()
}
func find(filename string) (string, error) {
if _, err := os.Stat(filename); err == nil {
return filename, nil
}
return "", fmt.Errorf("could not find file %q", filename)
}
func getContainerExecutable() (string, error) {
// Probe podman first, because the docker binary might actually
// be a thin podman wrapper with podman behavior.
choices := []string{"podman", "docker"}
for _, exe := range choices {
p, err := exec.LookPath(exe)
if err != nil {
continue
}
resolved, err := filepath.EvalSymlinks(p)
if err != nil {
return "", err
}
return resolved, nil
}
return "", fmt.Errorf("none of %v found in $PATH", choices)
}
func rebuildKernel() error {
overwriteContainerExecutable := flag.String("overwrite_container_executable",
"",
"E.g. docker or podman to overwrite the automatically detected container executable")
keepBuildContainer := flag.Bool("keep_build_container",
false,
"do not delete build container after building the kernel")
cross := flag.String("cross",
"",
"if non-empty, cross-compile for the specified arch (one of 'arm64')")
flavor := flag.String("flavor",
"vanilla",
"which kernel flavor to build. one of vanilla (kernel.org) or raspberrypi (https://github.com/raspberrypi/linux/tags)")
dtbs := flag.String("dtbs",
"raspberrypi",
"which device tree files (.dtb files) to copy. 'raspberrypi' or empty")
flag.Parse()
if *cross != "" && *cross != "arm64" {
return fmt.Errorf("invalid -cross value %q: expected one of 'arm64'")
}
abs, err := os.Getwd()
if err != nil {
return err
}
if !strings.HasSuffix(strings.TrimSuffix(abs, "/"), "/_build") {
return fmt.Errorf("gokr-rebuild-kernel is not run from a _build directory")
}
series, err := os.ReadFile("series")
if err != nil {
return err
}
patches := strings.Split(strings.TrimSpace(string(series)), "\n")
executable, err := getContainerExecutable()
if err != nil {
return err
}
if *overwriteContainerExecutable != "" {
executable = *overwriteContainerExecutable
}
execName := filepath.Base(executable)
var patchPaths []string
for _, filename := range patches {
path, err := find(filename)
if err != nil {
return err
}
patchPaths = append(patchPaths, path)
}
kernelPath, err := find("../vmlinuz")
if err != nil {
return err
}
libPath, err := find("../lib")
if err != nil {
return err
}
// TODO: just ensure the file exists, i.e. we are in _build
if _, err := find("config.addendum.txt"); err != nil {
return err
}
u, err := user.Current()
if err != nil {
return err
}
upstreamURL, err := os.ReadFile("upstream-url.txt")
if err != nil {
return err
}
dockerFile, err := os.Create("Dockerfile")
if err != nil {
return err
}
if err := dockerFileTmpl.Execute(dockerFile, struct {
Uid string
Gid string
Patches []string
Cross string
}{
Uid: u.Uid,
Gid: u.Gid,
Patches: patches,
Cross: *cross,
}); err != nil {
return err
}
if err := dockerFile.Close(); err != nil {
return err
}
log.Printf("building %s container for kernel compilation", execName)
dockerBuild := exec.Command(execName,
"build",
// "--platform=linux/amd64",
"--rm=true",
"--tag=gokr-rebuild-kernel",
".")
dockerBuild.Stdout = os.Stdout
dockerBuild.Stderr = os.Stderr
log.Printf("%v", dockerBuild.Args)
if err := dockerBuild.Run(); err != nil {
return fmt.Errorf("%s build: %v (cmd: %v)", execName, err, dockerBuild.Args)
}
log.Printf("compiling kernel")
var dockerRun *exec.Cmd
dockerArgs := []string{
"run",
// "--platform=linux/amd64",
"--volume", abs + ":/tmp/buildresult:Z",
}
if !*keepBuildContainer {
dockerArgs = append(dockerArgs, "--rm")
}
if execName == "podman" {
dockerArgs = append(dockerArgs, "--userns=keep-id")
}
dockerArgs = append(dockerArgs,
"gokr-rebuild-kernel",
"-cross="+*cross,
"-flavor="+*flavor,
strings.TrimSpace(string(upstreamURL)))
dockerRun = exec.Command(executable, dockerArgs...)
dockerRun.Stdout = os.Stdout
dockerRun.Stderr = os.Stderr
log.Printf("%v", dockerRun.Args)
if err := dockerRun.Run(); err != nil {
return fmt.Errorf("%s run: %v (cmd: %v)", execName, err, dockerRun.Args)
}
if err := copyFile(kernelPath, "vmlinuz"); err != nil {
return err
}
// remove symlinks that only work when source/build directory are present
for _, subdir := range []string{"build", "source"} {
matches, err := filepath.Glob(filepath.Join("lib/modules", "*", subdir))
if err != nil {
return err
}
for _, match := range matches {
log.Printf("removing build/source symlink %s", match)
if err := os.Remove(match); err != nil {
return err
}
}
}
// replace kernel modules directory
rm := exec.Command("rm", "-rf", filepath.Join(libPath, "modules"))
rm.Stdout = os.Stdout
rm.Stderr = os.Stderr
log.Printf("%v", rm.Args)
if err := rm.Run(); err != nil {
return fmt.Errorf("%v: %v", rm.Args, err)
}
cp := exec.Command("cp", "-r", filepath.Join("lib/modules"), libPath)
cp.Stdout = os.Stdout
cp.Stderr = os.Stderr
log.Printf("%v", cp.Args)
if err := cp.Run(); err != nil {
return fmt.Errorf("%v: %v", cp.Args, err)
}
if *cross == "arm64" {
if *dtbs != "" {
// replace device tree files
rm = exec.Command("sh", "-c", "rm ../*.dtb")
rm.Stdout = os.Stdout
rm.Stderr = os.Stderr
log.Printf("%v", rm.Args)
if err := rm.Run(); err != nil {
return fmt.Errorf("%v: %v", rm.Args, err)
}
cp = exec.Command("sh", "-c", "cp *.dtb ..")
cp.Stdout = os.Stdout
cp.Stderr = os.Stderr
log.Printf("%v", cp.Args)
if err := cp.Run(); err != nil {
return fmt.Errorf("%v: %v", cp.Args, err)
}
}
if *flavor == "raspberrypi" {
// replace overlays directory
overlaysPath, err := find("../overlays")
if err != nil {
return err
}
rm = exec.Command("rm", "-rf", overlaysPath)
rm.Stdout = os.Stdout
rm.Stderr = os.Stderr
log.Printf("%v", rm.Args)
if err := rm.Run(); err != nil {
return fmt.Errorf("%v: %v", rm.Args, err)
}
cp = exec.Command("cp", "-r", "overlays", overlaysPath)
cp.Stdout = os.Stdout
cp.Stderr = os.Stderr
log.Printf("%v", cp.Args)
if err := cp.Run(); err != nil {
return fmt.Errorf("%v: %v", cp.Args, err)
}
}
}
return nil
}
func main() {
if os.Getenv("GOKRAZY_IN_DOCKER") == "1" {
indockerMain()
} else {
if err := rebuildKernel(); err != nil {
log.Fatal(err)
}
}
}