Repository: lima-vm/lima
Pull request #3488 (2025-05-02 03:00:37)

qemu: experimental support for PPC64LE
Usage:
```
limactl start --arch ppc64le
```


Patch:
diff --git a/Kconfig b/Kconfig
index 31850bce..d52d6fb0 100644
--- a/Kconfig
+++ b/Kconfig
@@ -37,6 +37,12 @@ config GUESTAGENT_ARCH_S390X
 		Build lima-guestagent for "s390x" Arch
 	default y
 
+config GUESTAGENT_ARCH_PPC64LE
+	bool "guestagent Arch: ppc64le"
+	help
+		Build lima-guestagent for "ppc64le" Arch
+	default y
+
 config GUESTAGENT_COMPRESS
 	bool "guestagent compress"
 	help
diff --git a/Makefile b/Makefile
index 4f315662..5f679955 100644
--- a/Makefile
+++ b/Makefile
@@ -273,7 +273,7 @@ LINUX_GUESTAGENT_PATH_COMMON = _output/share/lima/lima-guestagent.Linux-
 # How to add architecture specific guestagent:
 # 1. Add the architecture to GUESTAGENT_ARCHS
 # 2. Add ENVS_$(*_GUESTAGENT_PATH_COMMON)<arch> to set GOOS, GOARCH, and other necessary environment variables
-LINUX_GUESTAGENT_ARCHS = aarch64 armv7l riscv64 s390x x86_64
+LINUX_GUESTAGENT_ARCHS = aarch64 armv7l ppc64le riscv64 s390x x86_64
 
 ifeq ($(CONFIG_GUESTAGENT_OS_LINUX),y)
 ALL_GUESTAGENTS_NOT_COMPRESSED += $(addprefix $(LINUX_GUESTAGENT_PATH_COMMON),$(LINUX_GUESTAGENT_ARCHS))
@@ -321,6 +321,7 @@ additional-guestagents: $(ADDITIONAL_GUESTAGENTS)
 # environment variables for linx-guestagent. these variable are used for checking force build.
 ENVS_$(LINUX_GUESTAGENT_PATH_COMMON)aarch64 = CGO_ENABLED=0 GOOS=linux GOARCH=arm64
 ENVS_$(LINUX_GUESTAGENT_PATH_COMMON)armv7l = CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7
+ENVS_$(LINUX_GUESTAGENT_PATH_COMMON)ppc64le = CGO_ENABLED=0 GOOS=linux GOARCH=ppc64le
 ENVS_$(LINUX_GUESTAGENT_PATH_COMMON)riscv64 = CGO_ENABLED=0 GOOS=linux GOARCH=riscv64
 ENVS_$(LINUX_GUESTAGENT_PATH_COMMON)s390x = CGO_ENABLED=0 GOOS=linux GOARCH=s390x
 ENVS_$(LINUX_GUESTAGENT_PATH_COMMON)x86_64 = CGO_ENABLED=0 GOOS=linux GOARCH=amd64
diff --git a/cmd/limactl/editflags/editflags.go b/cmd/limactl/editflags/editflags.go
index 83f9b9e2..3fad84f7 100644
--- a/cmd/limactl/editflags/editflags.go
+++ b/cmd/limactl/editflags/editflags.go
@@ -73,9 +73,9 @@ func RegisterCreate(cmd *cobra.Command, commentPrefix string) {
 	registerEdit(cmd, commentPrefix)
 	flags := cmd.Flags()
 
-	flags.String("arch", "", commentPrefix+"Machine architecture (x86_64, aarch64, riscv64, armv7l, s390x)") // colima-compatible
+	flags.String("arch", "", commentPrefix+"Machine architecture (x86_64, aarch64, riscv64, armv7l, s390x, ppc64le)") // colima-compatible
 	_ = cmd.RegisterFlagCompletionFunc("arch", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
-		return []string{"x86_64", "aarch64", "riscv64", "armv7l", "s390x"}, cobra.ShellCompDirectiveNoFileComp
+		return []string{"x86_64", "aarch64", "riscv64", "armv7l", "s390x", "ppc64le"}, cobra.ShellCompDirectiveNoFileComp
 	})
 
 	flags.String("containerd", "", commentPrefix+"containerd mode (user, system, user+system, none)")
diff --git a/config.mk b/config.mk
index 6b7ff313..9a02aae2 100644
--- a/config.mk
+++ b/config.mk
@@ -2,6 +2,7 @@ CONFIG_GUESTAGENT_OS_LINUX=y
 CONFIG_GUESTAGENT_ARCH_X8664=y
 CONFIG_GUESTAGENT_ARCH_AARCH64=y
 CONFIG_GUESTAGENT_ARCH_ARMV7L=y
+CONFIG_GUESTAGENT_ARCH_PPC64LE=y
 CONFIG_GUESTAGENT_ARCH_RISCV64=y
 CONFIG_GUESTAGENT_ARCH_S390X=y
 CONFIG_GUESTAGENT_COMPRESS=n
diff --git a/hack/inject-cmdline-to-template.sh b/hack/inject-cmdline-to-template.sh
index 08b92899..c1745f11 100755
--- a/hack/inject-cmdline-to-template.sh
+++ b/hack/inject-cmdline-to-template.sh
@@ -26,6 +26,8 @@ case "${arch}" in
 amd64 | x86_64) arch=x86_64 ;;
 aarch64 | arm64) arch=aarch64 ;;
 armv7l | armhf) arch=armv7l ;;
+ppc64el | ppc64le) arch=ppc64le ;;
+s390x) arch=s390x ;;
 riscv64) arch=riscv64 ;;
 *)
 	echo "Unsupported arch: ${arch}" >&2
diff --git a/hack/update-template.sh b/hack/update-template.sh
index 9fc4b9bf..06e91190 100755
--- a/hack/update-template.sh
+++ b/hack/update-template.sh
@@ -84,6 +84,7 @@ function limayaml_arch() {
 	arch=${arch/amd64/x86_64}
 	arch=${arch/arm64/aarch64}
 	arch=${arch/armhf/armv7l}
+	arch=${arch/ppc64el/ppc64le}
 	echo "${arch}"
 }
 
diff --git a/pkg/limatmpl/locator.go b/pkg/limatmpl/locator.go
index 6ddf16a3..a2f6ca0d 100644
--- a/pkg/limatmpl/locator.go
+++ b/pkg/limatmpl/locator.go
@@ -139,6 +139,8 @@ var archKeywords = map[string]limayaml.Arch{
 	"arm64":   limayaml.AARCH64,
 	"armhf":   limayaml.ARMV7L,
 	"armv7l":  limayaml.ARMV7L,
+	"ppc64el": limayaml.PPC64LE,
+	"ppc64le": limayaml.PPC64LE,
 	"riscv64": limayaml.RISCV64,
 	"s390x":   limayaml.S390X,
 	"x86_64":  limayaml.X8664,
diff --git a/pkg/limayaml/defaults.go b/pkg/limayaml/defaults.go
index 7e42e7d1..1736b96f 100644
--- a/pkg/limayaml/defaults.go
+++ b/pkg/limayaml/defaults.go
@@ -77,6 +77,7 @@ func defaultCPUType() CPUType {
 		AARCH64: "max",
 		ARMV7L:  "max",
 		X8664:   defaultX8664,
+		PPC64LE: "max",
 		RISCV64: "max",
 		S390X:   "max",
 	}
@@ -1095,6 +1096,8 @@ func NewArch(arch string) Arch {
 		}
 		logrus.Warnf("Unknown arm: %d", arm)
 		return arch
+	case "ppc64le":
+		return PPC64LE
 	case "riscv64":
 		return RISCV64
 	case "s390x":
@@ -1283,9 +1286,10 @@ func IsNativeArch(arch Arch) bool {
 	nativeX8664 := arch == X8664 && runtime.GOARCH == "amd64"
 	nativeAARCH64 := arch == AARCH64 && runtime.GOARCH == "arm64"
 	nativeARMV7L := arch == ARMV7L && runtime.GOARCH == "arm" && goarm() == 7
+	nativePPC64LE := arch == PPC64LE && runtime.GOARCH == "ppc64le"
 	nativeRISCV64 := arch == RISCV64 && runtime.GOARCH == "riscv64"
 	nativeS390X := arch == S390X && runtime.GOARCH == "s390x"
-	return nativeX8664 || nativeAARCH64 || nativeARMV7L || nativeRISCV64 || nativeS390X
+	return nativeX8664 || nativeAARCH64 || nativeARMV7L || nativePPC64LE || nativeRISCV64 || nativeS390X
 }
 
 func unique(s []string) []string {
diff --git a/pkg/limayaml/limayaml.go b/pkg/limayaml/limayaml.go
index ddbcaa0b..4082b82d 100644
--- a/pkg/limayaml/limayaml.go
+++ b/pkg/limayaml/limayaml.go
@@ -76,6 +76,7 @@ const (
 	X8664   Arch = "x86_64"
 	AARCH64 Arch = "aarch64"
 	ARMV7L  Arch = "armv7l"
+	PPC64LE Arch = "ppc64le"
 	RISCV64 Arch = "riscv64"
 	S390X   Arch = "s390x"
 
@@ -91,7 +92,7 @@ const (
 
 var (
 	OSTypes    = []OS{LINUX}
-	ArchTypes  = []Arch{X8664, AARCH64, ARMV7L, RISCV64, S390X}
+	ArchTypes  = []Arch{X8664, AARCH64, ARMV7L, PPC64LE, RISCV64, S390X}
 	MountTypes = []MountType{REVSSHFS, NINEP, VIRTIOFS, WSLMount}
 	VMTypes    = []VMType{QEMU, VZ, WSL2}
 )
diff --git a/pkg/limayaml/validate.go b/pkg/limayaml/validate.go
index abc3fe10..42c4faa5 100644
--- a/pkg/limayaml/validate.go
+++ b/pkg/limayaml/validate.go
@@ -35,10 +35,8 @@ func validateFileObject(f File, fieldName string) error {
 		}
 		// f.Location does NOT need to be accessible, so we do NOT check os.Stat(f.Location)
 	}
-	switch f.Arch {
-	case X8664, AARCH64, ARMV7L, RISCV64, S390X:
-	default:
-		return fmt.Errorf("field `arch` must be %q, %q, %q, %q, or %q; got %q", X8664, AARCH64, ARMV7L, RISCV64, S390X, f.Arch)
+	if !slices.Contains(ArchTypes, f.Arch) {
+		return fmt.Errorf("field `arch` must be one of %v; got %q", ArchTypes, f.Arch)
 	}
 	if f.Digest != "" {
 		if !f.Digest.Algorithm().Available() {
@@ -77,12 +75,9 @@ func Validate(y *LimaYAML, warn bool) error {
 	default:
 		return fmt.Errorf("field `os` must be %q; got %q", LINUX, *y.OS)
 	}
-	switch *y.Arch {
-	case X8664, AARCH64, ARMV7L, RISCV64, S390X:
-	default:
-		return fmt.Errorf("field `arch` must be %q, %q, %q, %q or %q; got %q", X8664, AARCH64, ARMV7L, RISCV64, S390X, *y.Arch)
+	if !slices.Contains(ArchTypes, *y.Arch) {
+		return fmt.Errorf("field `arch` must be one of %v; got %q", ArchTypes, *y.Arch)
 	}
-
 	switch *y.VMType {
 	case QEMU:
 		// NOP
@@ -125,10 +120,7 @@ func Validate(y *LimaYAML, warn bool) error {
 	}
 
 	for arch := range y.CPUType {
-		switch arch {
-		case AARCH64, X8664, ARMV7L, RISCV64, S390X:
-			// these are the only supported architectures
-		default:
+		if !slices.Contains(ArchTypes, arch) {
 			return fmt.Errorf("field `cpuType` uses unsupported arch %q", arch)
 		}
 	}
@@ -589,7 +581,7 @@ func warnExperimental(y *LimaYAML) {
 		logrus.Warn("`mountType: virtiofs` on Linux is experimental")
 	}
 	switch *y.Arch {
-	case RISCV64, ARMV7L, S390X:
+	case RISCV64, ARMV7L, S390X, PPC64LE:
 		logrus.Warnf("`arch: %s ` is experimental", *y.Arch)
 	}
 	if y.Video.Display != nil && strings.Contains(*y.Video.Display, "vnc") {
diff --git a/pkg/qemu/qemu.go b/pkg/qemu/qemu.go
index d5d93647..4211d2e6 100644
--- a/pkg/qemu/qemu.go
+++ b/pkg/qemu/qemu.go
@@ -540,7 +540,8 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
 			}
 		}
 	}
-	if !strings.Contains(string(features.CPUHelp), strings.Split(cpu, ",")[0]) {
+	// `qemu-system-ppc64 -help` does not show "max", but it is actually accepted
+	if cpu != "max" && !strings.Contains(string(features.CPUHelp), strings.Split(cpu, ",")[0]) {
 		return "", nil, fmt.Errorf("cpu %q is not supported by %s", cpu, exe)
 	}
 	args = appendArgsIfNoConflict(args, "-cpu", cpu)
@@ -588,6 +589,9 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
 	case limayaml.ARMV7L:
 		machine := "virt,accel=" + accel
 		args = appendArgsIfNoConflict(args, "-machine", machine)
+	case limayaml.PPC64LE:
+		machine := "pseries,accel=" + accel
+		args = appendArgsIfNoConflict(args, "-machine", machine)
 	case limayaml.S390X:
 		machine := "s390-ccw-virtio,accel=" + accel
 		args = appendArgsIfNoConflict(args, "-machine", machine)
@@ -603,7 +607,7 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
 		logrus.Warnf("field `firmware.legacyBIOS` is not supported for architecture %q, ignoring", *y.Arch)
 		legacyBIOS = false
 	}
-	noFirmware := *y.Arch == limayaml.S390X || legacyBIOS
+	noFirmware := *y.Arch == limayaml.PPC64LE || *y.Arch == limayaml.S390X || legacyBIOS
 	if !noFirmware {
 		var firmware string
 		firmwareInBios := runtime.GOOS == "windows"
@@ -877,7 +881,7 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
 		args = append(args, "-device", "virtio-keyboard-pci")
 		args = append(args, "-device", "virtio-"+input+"-pci")
 		args = append(args, "-device", "qemu-xhci,id=usb-bus")
-	case limayaml.AARCH64, limayaml.ARMV7L, limayaml.S390X:
+	case limayaml.AARCH64, limayaml.ARMV7L, limayaml.PPC64LE, limayaml.S390X:
 		if features.VersionGEQ7 {
 			args = append(args, "-device", "virtio-gpu")
 			args = append(args, "-device", "virtio-keyboard-pci")
@@ -1094,10 +1098,14 @@ func VirtiofsdCmdline(cfg Config, mountIndex int) ([]string, error) {
 
 // qemuArch returns the arch string used by qemu.
 func qemuArch(arch limayaml.Arch) string {
-	if arch == limayaml.ARMV7L {
+	switch arch {
+	case limayaml.ARMV7L:
 		return "arm"
+	case limayaml.PPC64LE:
+		return "ppc64"
+	default:
+		return arch
 	}
-	return arch
 }
 
 // qemuEdk2 returns the arch string used by `/usr/local/share/qemu/edk2-*-code.fd`.
diff --git a/pkg/sshutil/sshutil.go b/pkg/sshutil/sshutil.go
index 1d8a5f15..0bf390b3 100644
--- a/pkg/sshutil/sshutil.go
+++ b/pkg/sshutil/sshutil.go
@@ -425,5 +425,5 @@ func detectAESAcceleration() bool {
 		logrus.Warn("Failed to detect CPU features. Assuming that AES acceleration is not available.")
 		return false
 	}
-	return cpu.ARM.HasAES || cpu.ARM64.HasAES || cpu.S390X.HasAES || cpu.X86.HasAES
+	return cpu.ARM.HasAES || cpu.ARM64.HasAES || cpu.PPC64.IsPOWER8 || cpu.S390X.HasAES || cpu.X86.HasAES
 }
diff --git a/templates/_images/almalinux-8.yaml b/templates/_images/almalinux-8.yaml
index 31f5f476..4255afa4 100644
--- a/templates/_images/almalinux-8.yaml
+++ b/templates/_images/almalinux-8.yaml
@@ -13,6 +13,10 @@ images:
   arch: s390x
   digest: sha256:7f8866a4247ad57c81f5d2c5a0fa64940691f9df1e858a1510d34a0de008eb16
 
+- location: https://repo.almalinux.org/almalinux/8.10/cloud/ppc64le/images/AlmaLinux-8-GenericCloud-8.10-20240819.ppc64le.qcow2
+  arch: ppc64le
+  digest: sha256:01acccc74009ebe8ddb13ea6a313479b85a372cb5b898f9090a12a2bd2d62520
+
 # Fallback to the latest release image.
 # Hint: run `limactl prune` to invalidate the cache
 
@@ -25,6 +29,9 @@ images:
 - location: https://repo.almalinux.org/almalinux/8/cloud/s390x/images/AlmaLinux-8-GenericCloud-latest.s390x.qcow2
   arch: s390x
 
+- location: https://repo.almalinux.org/almalinux/8/cloud/ppc64le/images/AlmaLinux-8-GenericCloud-latest.ppc64le.qcow2
+  arch: ppc64le
+
 mountTypesUnsupported: [9p]
 
 cpuType:
diff --git a/templates/_images/almalinux-9.yaml b/templates/_images/almalinux-9.yaml
index b13fa427..313ab383 100644
--- a/templates/_images/almalinux-9.yaml
+++ b/templates/_images/almalinux-9.yaml
@@ -11,6 +11,10 @@ images:
   arch: s390x
   digest: sha256:9948ea1c5ee1c6497bde5cec18c71c5f9e50861af4c0f1400c1af504eee2b995
 
+- location: https://repo.almalinux.org/almalinux/9.5/cloud/ppc64le/images/AlmaLinux-9-GenericCloud-9.5-20241120.ppc64le.qcow2
+  arch: ppc64le
+  digest: sha256:24feab47c4a89f7d3bda58001dde59ea74904b25edbb68a207f39277c0772522
+
 # Fallback to the latest release image.
 # Hint: run `limactl prune` to invalidate the cache
 
@@ -23,4 +27,7 @@ images:
 - location: https://repo.almalinux.org/almalinux/9/cloud/s390x/images/AlmaLinux-9-GenericCloud-latest.s390x.qcow2
   arch: s390x
 
+- location: https://repo.almalinux.org/almalinux/9/cloud/ppc64le/images/AlmaLinux-9-GenericCloud-latest.ppc64le.qcow2
+  arch: ppc64le
+
 mountTypesUnsupported: [9p]
diff --git a/templates/_images/centos-stream-10.yaml b/templates/_images/centos-stream-10.yaml
index ee981b26..1f0afc4e 100644
--- a/templates/_images/centos-stream-10.yaml
+++ b/templates/_images/centos-stream-10.yaml
@@ -10,6 +10,10 @@ images:
 - location: "https://cloud.centos.org/centos/10-stream/s390x/images/CentOS-Stream-GenericCloud-10-20250429.0.s390x.qcow2"
   arch: "s390x"
   digest: "sha256:d102c9ee9c1ff72d93f349ab30980c6901be5f85083ef2bfd5a882f164fcb692"
+- location: "https://cloud.centos.org/centos/10-stream/ppc64le/images/CentOS-Stream-GenericCloud-10-20250429.0.ppc64le.qcow2"
+  arch: "ppc64le"
+  digest: "sha256:75157ca6cdf43ad755f99904219c4b4285126df194ed38d558cd5de0f8f1cd97"
+
 # Fallback to the latest release image.
 # Hint: run `limactl prune` to invalidate the cache
 
@@ -22,6 +26,9 @@ images:
 - location: https://cloud.centos.org/centos/10-stream/s390x/images/CentOS-Stream-GenericCloud-10-latest.s390x.qcow2
   arch: s390x
 
+- location: https://cloud.centos.org/centos/10-stream/ppc64le/images/CentOS-Stream-GenericCloud-10-latest.ppc64le.qcow2
+  arch: ppc64le
+
 mountTypesUnsupported: [9p]
 
 firmware:
diff --git a/templates/_images/centos-stream-9.yaml b/templates/_images/centos-stream-9.yaml
index 2df0a1e4..ad9a5b25 100644
--- a/templates/_images/centos-stream-9.yaml
+++ b/templates/_images/centos-stream-9.yaml
@@ -10,6 +10,10 @@ images:
 - location: "https://cloud.centos.org/centos/9-stream/s390x/images/CentOS-Stream-GenericCloud-9-20250428.0.s390x.qcow2"
   arch: "s390x"
   digest: "sha256:ec270d96ac73e1fe28587df500fd1602c8d80a7bddf354e25b7d2b91a936993c"
+- location: "https://cloud.centos.org/centos/9-stream/ppc64le/images/CentOS-Stream-GenericCloud-9-20250428.0.ppc64le.qcow2"
+  arch: "ppc64le"
+  digest: "sha256:926da7012700e4c4f4ef04643baaee9c0ac7293bcdb4acdb8982266b8fe9a908"
+
 # Fallback to the latest release image.
 # Hint: run `limactl prune` to invalidate the cache
 
@@ -22,6 +26,9 @@ images:
 - location: https://cloud.centos.org/centos/9-stream/s390x/images/CentOS-Stream-GenericCloud-9-latest.s390x.qcow2
   arch: s390x
 
+- location: https://cloud.centos.org/centos/9-stream/ppc64le/images/CentOS-Stream-GenericCloud-9-latest.ppc64le.qcow2
+  arch: ppc64le
+
 mountTypesUnsupported: [9p]
 
 firmware:
diff --git a/templates/_images/debian-11.yaml b/templates/_images/debian-11.yaml
index ef9ffadb..05a8474e 100644
--- a/templates/_images/debian-11.yaml
+++ b/templates/_images/debian-11.yaml
@@ -7,6 +7,10 @@ images:
 - location: "https://cloud.debian.org/images/cloud/bullseye/20250429-2097/debian-11-genericcloud-arm64-20250429-2097.qcow2"
   arch: "aarch64"
   digest: "sha512:f27fa0af9ab5edd232d8d418daa755ffe27d6382b25208f01c4959444c7968629e91f9ea112cbffc49ec29536dab02f30edcdacfdff588ccad16a99a572bba0f"
+- location: "https://cloud.debian.org/images/cloud/bullseye/20250429-2097/debian-11-genericcloud-ppc64el-20250429-2097.qcow2"
+  arch: "ppc64le"
+  digest: "sha512:ab23cc83b58f0d63450277d6d94a173ea2503ee0594c535a4432eca130cd5d06a646b24bae8bdcf633e73e2fc4dfeaffac9c5ec213c7267f3f8db048d256e85f"
+
 # Fallback to the latest release image.
 # Hint: run `limactl prune` to invalidate the cache
 
@@ -16,6 +20,9 @@ images:
 - location: https://cloud.debian.org/images/cloud/bullseye/latest/debian-11-genericcloud-arm64.qcow2
   arch: aarch64
 
+- location: https://cloud.debian.org/images/cloud/bullseye/latest/debian-11-genericcloud-ppc64el.qcow2
+  arch: ppc64le
+
 mountTypesUnsupported: [9p]
 
 # debian-11 seems incompatible with vz
diff --git a/templates/_images/debian-12.yaml b/templates/_images/debian-12.yaml
index 667d322f..cae7d0cc 100644
--- a/templates/_images/debian-12.yaml
+++ b/templates/_images/debian-12.yaml
@@ -7,6 +7,10 @@ images:
 - location: "https://cloud.debian.org/images/cloud/bookworm/20250428-2096/debian-12-genericcloud-arm64-20250428-2096.qcow2"
   arch: "aarch64"
   digest: "sha512:a669b9b82fc3afc8c9ef84c7e9a38ffdec060b51b47fe33a66f2b65d0da9a742f9645a4fa24b5c8cb4e5a37123a0ae4ad810410c4160a6f6825c7189c296f5ec"
+- location: "https://cloud.debian.org/images/cloud/bookworm/20250428-2096/debian-12-genericcloud-ppc64el-20250428-2096.qcow2"
+  arch: "ppc64le"
+  digest: "sha512:a40a41d341e5e76ff189ffcc172a510617f1af46bb2a08e54b1570cd594e99c69912640b007a0581232c6e3a22e7a3d38de3babf70aced68ba902726146ee628"
+
 # Fallback to the latest release image.
 # Hint: run `limactl prune` to invalidate the cache
 
@@ -16,4 +20,7 @@ images:
 - location: https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-arm64.qcow2
   arch: aarch64
 
+- location: https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-ppc64el.qcow2
+  arch: ppc64le
+
 mountTypesUnsupported: [9p]
diff --git a/templates/_images/rocky-9.yaml b/templates/_images/rocky-9.yaml
index 9377d554..82ed7ed2 100644
--- a/templates/_images/rocky-9.yaml
+++ b/templates/_images/rocky-9.yaml
@@ -7,6 +7,10 @@ images:
   arch: aarch64
   digest: sha256:5443bcc0507fadc3d7bd3e8d266135ab8db6966c703216933f824164fd3252f1
 
+- location: https://dl.rockylinux.org/pub/rocky/9.5/images/ppc64le/Rocky-9-GenericCloud-Base-9.5-20241118.0.ppc64le.qcow2
+  arch: ppc64le
+  digest: sha256:814a5b80bacb370baa705766de40c4c96e44cc7fa03fcb6f41c8dfbc89aa971a
+
 # Fallback to the latest release image.
 # Hint: run `limactl prune` to invalidate the cache
 
@@ -16,4 +20,7 @@ images:
 - location: https://dl.rockylinux.org/pub/rocky/9/images/aarch64/Rocky-9-GenericCloud.latest.aarch64.qcow2
   arch: aarch64
 
+- location: https://dl.rockylinux.org/pub/rocky/9/images/ppc64le/Rocky-9-GenericCloud.latest.ppc64le.qcow2
+  arch: ppc64le
+
 mountTypesUnsupported: [9p]
diff --git a/templates/_images/ubuntu-20.04.yaml b/templates/_images/ubuntu-20.04.yaml
index 9b40619d..74daa49f 100644
--- a/templates/_images/ubuntu-20.04.yaml
+++ b/templates/_images/ubuntu-20.04.yaml
@@ -13,6 +13,10 @@ images:
 - location: "https://cloud-images.ubuntu.com/releases/focal/release-20250429/ubuntu-20.04-server-cloudimg-s390x.img"
   arch: "s390x"
   digest: "sha256:710fc0a25cf1c3684d87202cccf10761e5c6e690500e7a17f7da9a9965a1d35d"
+- location: "https://cloud-images.ubuntu.com/releases/focal/release-20250429/ubuntu-20.04-server-cloudimg-ppc64el.img"
+  arch: "ppc64le"
+  digest: "sha256:45e79fb0ae143fdad797802f586f318f1c98199d6a6f1366f3f64242ceb7c07e"
+
 # Fallback to the latest release image.
 # Hint: run `limactl prune` to invalidate the cache
 
@@ -27,3 +31,6 @@ images:
 
 - location: https://cloud-images.ubuntu.com/releases/focal/release/ubuntu-20.04-server-cloudimg-s390x.img
   arch: s390x
+
+- location: https://cloud-images.ubuntu.com/releases/focal/release/ubuntu-20.04-server-cloudimg-ppc64el.img
+  arch: ppc64le
diff --git a/templates/_images/ubuntu-22.04.yaml b/templates/_images/ubuntu-22.04.yaml
index cf72cd5b..bf9ecd81 100644
--- a/templates/_images/ubuntu-22.04.yaml
+++ b/templates/_images/ubuntu-22.04.yaml
@@ -16,6 +16,10 @@ images:
 - location: "https://cloud-images.ubuntu.com/releases/jammy/release-20250429/ubuntu-22.04-server-cloudimg-s390x.img"
   arch: "s390x"
   digest: "sha256:dd2a7139ba741391fe62a2fb7f80beef72300126ac080d3e46c46e4f9dbafe20"
+- location: "https://cloud-images.ubuntu.com/releases/jammy/release-20250429/ubuntu-22.04-server-cloudimg-ppc64el.img"
+  arch: "ppc64le"
+  digest: "sha256:b1ece28847210487a452eb77601dbb89f613d8f31a8a971932d38b4eabc3f08d"
+
 # Fallback to the latest release image.
 # Hint: run `limactl prune` to invalidate the cache
 
@@ -33,3 +37,6 @@ images:
 
 - location: https://cloud-images.ubuntu.com/releases/jammy/release/ubuntu-22.04-server-cloudimg-s390x.img
   arch: s390x
+
+- location: https://cloud-images.ubuntu.com/releases/jammy/release/ubuntu-22.04-server-cloudimg-ppc64el.img
+  arch: ppc64le
diff --git a/templates/_images/ubuntu-24.04.yaml b/templates/_images/ubuntu-24.04.yaml
index a834aafd..500a2895 100644
--- a/templates/_images/ubuntu-24.04.yaml
+++ b/templates/_images/ubuntu-24.04.yaml
@@ -16,6 +16,10 @@ images:
 - location: "https://cloud-images.ubuntu.com/releases/noble/release-20250430/ubuntu-24.04-server-cloudimg-s390x.img"
   arch: "s390x"
   digest: "sha256:f4b61d53b30e08b3e44ac99012c933b92d00620c789154511ef62d44fae15c04"
+- location: "https://cloud-images.ubuntu.com/releases/noble/release-20250430/ubuntu-24.04-server-cloudimg-ppc64el.img"
+  arch: "ppc64le"
+  digest: "sha256:36c52f130c91478b1069ee29bf4c70a4e44de986d0716dd56e3af165ac5a04fe"
+
 # Fallback to the latest release image.
 # Hint: run `limactl prune` to invalidate the cache
 
@@ -33,3 +37,6 @@ images:
 
 - location: https://cloud-images.ubuntu.com/releases/noble/release/ubuntu-24.04-server-cloudimg-s390x.img
   arch: s390x
+
+- location: https://cloud-images.ubuntu.com/releases/noble/release/ubuntu-24.04-server-cloudimg-ppc64el.img
+  arch: ppc64le
diff --git a/templates/_images/ubuntu-24.10.yaml b/templates/_images/ubuntu-24.10.yaml
index 6b449aa9..61f6f14a 100644
--- a/templates/_images/ubuntu-24.10.yaml
+++ b/templates/_images/ubuntu-24.10.yaml
@@ -16,6 +16,10 @@ images:
 - location: "https://cloud-images.ubuntu.com/releases/oracular/release-20250430/ubuntu-24.10-server-cloudimg-s390x.img"
   arch: "s390x"
   digest: "sha256:dea8034b9401a0946dda898ceb61129d8f0d41de162f7cf143bdcc4664d3e831"
+- location: "https://cloud-images.ubuntu.com/releases/oracular/release-20250430/ubuntu-24.10-server-cloudimg-ppc64el.img"
+  arch: "ppc64le"
+  digest: "sha256:ec15e530f9b594c59a81ccebea1af26d3a61b18a84bfda048e6913cef034c19c"
+
 # Fallback to the latest release image.
 # Hint: run `limactl prune` to invalidate the cache
 
@@ -34,6 +38,9 @@ images:
 - location: https://cloud-images.ubuntu.com/releases/oracular/release/ubuntu-24.10-server-cloudimg-s390x.img
   arch: s390x
 
+- location: https://cloud-images.ubuntu.com/releases/oracular/release/ubuntu-24.10-server-cloudimg-ppc64el.img
+  arch: ppc64le
+
 # 9p is broken in Linux v6.9, v6.10, and v6.11 (used by Ubuntu 24.10).
 # The issue was fixed in Linux v6.12-rc5 (https://github.com/torvalds/linux/commit/be2ca38).
 mountTypesUnsupported: [9p]
diff --git a/templates/_images/ubuntu-25.04.yaml b/templates/_images/ubuntu-25.04.yaml
index 5757ad0a..ec19458d 100644
--- a/templates/_images/ubuntu-25.04.yaml
+++ b/templates/_images/ubuntu-25.04.yaml
@@ -16,6 +16,10 @@ images:
 - location: "https://cloud-images.ubuntu.com/releases/plucky/release-20250424/ubuntu-25.04-server-cloudimg-s390x.img"
   arch: "s390x"
   digest: "sha256:34020c9ced44598cd0d8ea77527aad61db4498f5151fac9fafdd1199cddf8919"
+- location: "https://cloud-images.ubuntu.com/releases/plucky/release-20250424/ubuntu-25.04-server-cloudimg-ppc64el.img"
+  arch: "ppc64le"
+  digest: "sha256:d1190f1db8eb6602d3f7de35f6f425fb671bb9ed953df821c0d1cc8cedbaa2ef"
+
 # Fallback to the latest release image.
 # Hint: run `limactl prune` to invalidate the cache
 
@@ -34,6 +38,9 @@ images:
 - location: https://cloud-images.ubuntu.com/releases/plucky/release/ubuntu-25.04-server-cloudimg-s390x.img
   arch: s390x
 
+- location: https://cloud-images.ubuntu.com/releases/plucky/release/ubuntu-25.04-server-cloudimg-ppc64el.img
+  arch: ppc64le
+
 # # NOTE: Intel Mac requires setting vmType to qemu
 # # https://github.com/lima-vm/lima/issues/3334
 # vmType: qemu
diff --git a/templates/experimental/debian-sid.yaml b/templates/experimental/debian-sid.yaml
index f4e9daad..065a2f5a 100644
--- a/templates/experimental/debian-sid.yaml
+++ b/templates/experimental/debian-sid.yaml
@@ -12,4 +12,7 @@ images:
 - location: https://cloud.debian.org/images/cloud/sid/daily/latest/debian-sid-genericcloud-riscv64-daily.qcow2
   arch: riscv64
 
+- location: https://cloud.debian.org/images/cloud/sid/daily/latest/debian-sid-genericcloud-ppc64el-daily.qcow2
+  arch: ppc64le
+
 mountTypesUnsupported: [9p]
diff --git a/website/content/en/docs/releases/experimental.md b/website/content/en/docs/releases/experimental.md
index 0c0ca298..d22181be 100644
--- a/website/content/en/docs/releases/experimental.md
+++ b/website/content/en/docs/releases/experimental.md
@@ -8,7 +8,7 @@ The following features are experimental and subject to change:
 
 - `mountType: virtiofs` on Linux
 - `vmType: wsl2` and relevant configurations (`mountType: wsl2`)
-- `arch: riscv64`, `arch: armv7l`, and `arch: s390x`
+- `arch`: `riscv64`, `armv7l`, `s390x`, and `ppc64le`
 - `video.display: vnc` and relevant configuration (`video.vnc.display`)
 - `audio.device`
 - `mountInotify: true`


Test patch:
diff --git a/pkg/limayaml/defaults_test.go b/pkg/limayaml/defaults_test.go
index b74a6d72..63842709 100644
--- a/pkg/limayaml/defaults_test.go
+++ b/pkg/limayaml/defaults_test.go
@@ -50,6 +50,8 @@ func TestFillDefault(t *testing.T) {
 			t.Skipf("unsupported GOARM: %d", arm)
 		}
 		arch = ARMV7L
+	case "ppc64le":
+		arch = PPC64LE
 	case "riscv64":
 		arch = RISCV64
 	case "s390x":
@@ -340,6 +342,7 @@ func TestFillDefault(t *testing.T) {
 			AARCH64: "arm64",
 			ARMV7L:  "armhf",
 			X8664:   "amd64",
+			PPC64LE: "ppc64le",
 			RISCV64: "riscv64",
 			S390X:   "s390x",
 		},
@@ -559,6 +562,7 @@ func TestFillDefault(t *testing.T) {
 			AARCH64: "uber-arm",
 			ARMV7L:  "armv8",
 			X8664:   "pentium",
+			PPC64LE: "power10",
 			RISCV64: "sifive-u54",
 			S390X:   "z14",
 		},