Skip to content

Commit a126fbb

Browse files
committed
support for libkrun using krunkit
Signed-off-by: Ansuman Sahoo <anshumansahoo500@gmail.com>
1 parent 9234371 commit a126fbb

File tree

6 files changed

+657
-0
lines changed

6 files changed

+657
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package main
5+
6+
import (
7+
"context"
8+
9+
"github.com/lima-vm/lima/v2/pkg/driver/external/server"
10+
"github.com/lima-vm/lima/v2/pkg/driver/krunkit"
11+
)
12+
13+
// To be used as an external driver for Lima.
14+
func main() {
15+
server.Serve(context.Background(), krunkit.New())
16+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package krunkit
5+
6+
import "errors"
7+
8+
var errUnimplemented = errors.New("unimplemented by the krunkit driver")
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/bin/bash
2+
3+
# SPDX-FileCopyrightText: Copyright The Lima Authors
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
set -eux -o pipefail
7+
8+
# Install required packages
9+
sudo dnf install -y dnf-plugins-core dnf-plugin-versionlock llvm18-libs
10+
11+
# Install Vulkan and MESA base packages
12+
sudo dnf install -y \
13+
mesa-vulkan-drivers \
14+
vulkan-loader-devel \
15+
vulkan-headers \
16+
vulkan-tools \
17+
vulkan-loader \
18+
glslc
19+
20+
# Enable COPR repo with patched MESA for Venus support
21+
sudo dnf copr enable -y slp/mesa-krunkit fedora-40-aarch64
22+
23+
# Downgrade to patched MESA version from COPR
24+
sudo dnf downgrade -y mesa-vulkan-drivers.aarch64 \
25+
--repo=copr:copr.fedorainfracloud.org:slp:mesa-krunkit
26+
27+
# Lock MESA version to prevent automatic upgrades
28+
sudo dnf versionlock add mesa-vulkan-drivers
29+
30+
# Clean up
31+
sudo dnf clean all
32+
33+
echo "Krunkit GPU(Venus) setup complete. Verify Vulkan installation by running 'vulkaninfo --summary'."
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package krunkit
5+
6+
import (
7+
"context"
8+
"errors"
9+
"fmt"
10+
"net"
11+
"os"
12+
"os/exec"
13+
"path/filepath"
14+
"strconv"
15+
16+
"github.com/docker/go-units"
17+
"github.com/lima-vm/lima/v2/pkg/driver/vz"
18+
"github.com/lima-vm/lima/v2/pkg/imgutil/proxyimgutil"
19+
"github.com/lima-vm/lima/v2/pkg/iso9660util"
20+
"github.com/lima-vm/lima/v2/pkg/limatype"
21+
"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
22+
"github.com/lima-vm/lima/v2/pkg/limayaml"
23+
"github.com/lima-vm/lima/v2/pkg/networks"
24+
"github.com/lima-vm/lima/v2/pkg/networks/usernet"
25+
)
26+
27+
const logLevelInfo = "3"
28+
29+
// Cmdline constructs the command line arguments for krunkit based on the instance configuration.
30+
func Cmdline(inst *limatype.Instance) (*exec.Cmd, error) {
31+
memBytes, err := units.RAMInBytes(*inst.Config.Memory)
32+
if err != nil {
33+
return nil, err
34+
}
35+
36+
args := []string{
37+
// Memory in MiB
38+
"--memory", strconv.FormatInt(memBytes/units.MiB, 10),
39+
"--cpus", fmt.Sprintf("%d", *inst.Config.CPUs),
40+
"--device", fmt.Sprintf("virtio-serial,logFilePath=%s", filepath.Join(inst.Dir, filenames.SerialLog)),
41+
"--krun-log-level", logLevelInfo,
42+
"--restful-uri", "none://",
43+
44+
// First virtio-blk device is the boot disk
45+
"--device", fmt.Sprintf("virtio-blk,path=%s,format=raw", filepath.Join(inst.Dir, filenames.DiffDisk)),
46+
"--device", fmt.Sprintf("virtio-blk,path=%s", filepath.Join(inst.Dir, filenames.CIDataISO)),
47+
}
48+
49+
// Network commands
50+
networkArgs, err := buildNetworkArgs(inst)
51+
if err != nil {
52+
return nil, fmt.Errorf("failed to build network arguments: %w", err)
53+
}
54+
55+
// File sharing commands
56+
if *inst.Config.MountType == limatype.VIRTIOFS {
57+
for i, mount := range inst.Config.Mounts {
58+
if _, err := os.Stat(mount.Location); errors.Is(err, os.ErrNotExist) {
59+
if err := os.MkdirAll(mount.Location, 0o750); err != nil {
60+
return nil, err
61+
}
62+
}
63+
tag := fmt.Sprintf("mount%d", i)
64+
mountArg := fmt.Sprintf("virtio-fs,sharedDir=%s,mountTag=%s", mount.Location, tag)
65+
args = append(args, "--device", mountArg)
66+
}
67+
}
68+
69+
args = append(args, networkArgs...)
70+
cmd := exec.CommandContext(context.Background(), "krunkit", args...)
71+
72+
return cmd, nil
73+
}
74+
75+
func buildNetworkArgs(inst *limatype.Instance) ([]string, error) {
76+
var args []string
77+
78+
// Configure default usernetwork with limayaml.MACAddress(inst.Dir) for eth0 interface
79+
firstUsernetIndex := limayaml.FirstUsernetIndex(inst.Config)
80+
if firstUsernetIndex == -1 {
81+
// slirp network using gvisor netstack
82+
krunkitSock, err := usernet.SockWithDirectory(inst.Dir, "", usernet.FDSock)
83+
if err != nil {
84+
return nil, err
85+
}
86+
client, err := vz.PassFDToUnix(krunkitSock)
87+
if err != nil {
88+
return nil, err
89+
}
90+
91+
args = append(args, "--device", fmt.Sprintf("virtio-net,type=unixgram,fd=%d,mac=%s", client.Fd(), limayaml.MACAddress(inst.Dir)))
92+
}
93+
94+
for _, nw := range inst.Networks {
95+
var sock string
96+
var mac string
97+
if nw.Lima != "" {
98+
nwCfg, err := networks.LoadConfig()
99+
if err != nil {
100+
return nil, err
101+
}
102+
switch nw.Lima {
103+
case networks.ModeUserV2:
104+
sock, err = usernet.Sock(nw.Lima, usernet.QEMUSock)
105+
if err != nil {
106+
return nil, err
107+
}
108+
mac = limayaml.MACAddress(inst.Dir)
109+
case networks.ModeShared, networks.ModeBridged:
110+
socketVMNetInstalled, err := nwCfg.IsDaemonInstalled(networks.SocketVMNet)
111+
if err != nil {
112+
return nil, err
113+
}
114+
if !socketVMNetInstalled {
115+
return nil, errors.New("socket_vmnet is not installed")
116+
}
117+
sock, err = networks.Sock(nw.Lima)
118+
if err != nil {
119+
return nil, err
120+
}
121+
mac = nw.MACAddress
122+
default:
123+
return nil, fmt.Errorf("invalid network spec %+v", nw)
124+
}
125+
} else if nw.Socket != "" {
126+
sock = nw.Socket
127+
mac = nw.MACAddress
128+
} else {
129+
return nil, fmt.Errorf("invalid network spec %+v", nw)
130+
}
131+
132+
device := fmt.Sprintf("virtio-net,type=unixstream,path=%s,mac=%s", sock, mac)
133+
args = append(args, "--device", device)
134+
}
135+
136+
if len(args) == 0 {
137+
return args, errors.New("no socket_vment networks defined")
138+
}
139+
140+
return args, nil
141+
}
142+
143+
func EnsureDisk(ctx context.Context, inst *limatype.Instance) error {
144+
diffDisk := filepath.Join(inst.Dir, filenames.DiffDisk)
145+
if _, err := os.Stat(diffDisk); err == nil || !errors.Is(err, os.ErrNotExist) {
146+
// disk is already ensured
147+
return err
148+
}
149+
150+
diskUtil := proxyimgutil.NewDiskUtil(ctx)
151+
152+
baseDisk := filepath.Join(inst.Dir, filenames.BaseDisk)
153+
154+
diskSize, _ := units.RAMInBytes(*inst.Config.Disk)
155+
if diskSize == 0 {
156+
return nil
157+
}
158+
isBaseDiskISO, err := iso9660util.IsISO9660(baseDisk)
159+
if err != nil {
160+
return err
161+
}
162+
if isBaseDiskISO {
163+
// Create an empty data volume (sparse)
164+
diffDiskF, err := os.Create(diffDisk)
165+
if err != nil {
166+
return err
167+
}
168+
169+
err = diskUtil.MakeSparse(ctx, diffDiskF, 0)
170+
if err != nil {
171+
diffDiskF.Close()
172+
return fmt.Errorf("failed to create sparse diff disk %q: %w", diffDisk, err)
173+
}
174+
return diffDiskF.Close()
175+
}
176+
177+
// Krunkit also supports qcow2 disks but raw is faster to create and use.
178+
if err = diskUtil.ConvertToRaw(ctx, baseDisk, diffDisk, &diskSize, false); err != nil {
179+
return fmt.Errorf("failed to convert %q to a raw disk %q: %w", baseDisk, diffDisk, err)
180+
}
181+
return err
182+
}
183+
184+
func startUsernet(ctx context.Context, inst *limatype.Instance) (*usernet.Client, context.CancelFunc, error) {
185+
if firstUsernetIndex := limayaml.FirstUsernetIndex(inst.Config); firstUsernetIndex != -1 {
186+
return usernet.NewClientByName(inst.Config.Networks[firstUsernetIndex].Lima), nil, nil
187+
}
188+
// Start a in-process gvisor-tap-vsock
189+
endpointSock, err := usernet.SockWithDirectory(inst.Dir, "", usernet.EndpointSock)
190+
if err != nil {
191+
return nil, nil, err
192+
}
193+
krunkitSock, err := usernet.SockWithDirectory(inst.Dir, "", usernet.FDSock)
194+
if err != nil {
195+
return nil, nil, err
196+
}
197+
os.RemoveAll(endpointSock)
198+
os.RemoveAll(krunkitSock)
199+
ctx, cancel := context.WithCancel(ctx)
200+
err = usernet.StartGVisorNetstack(ctx, &usernet.GVisorNetstackOpts{
201+
MTU: 1500,
202+
Endpoint: endpointSock,
203+
FdSocket: krunkitSock,
204+
Async: true,
205+
DefaultLeases: map[string]string{
206+
networks.SlirpIPAddress: limayaml.MACAddress(inst.Dir),
207+
},
208+
Subnet: networks.SlirpNetwork,
209+
})
210+
if err != nil {
211+
defer cancel()
212+
return nil, nil, err
213+
}
214+
subnetIP, _, err := net.ParseCIDR(networks.SlirpNetwork)
215+
return usernet.NewClient(endpointSock, subnetIP), cancel, err
216+
}

0 commit comments

Comments
 (0)