Skip to content

Commit bc634c9

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

File tree

7 files changed

+741
-0
lines changed

7 files changed

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

0 commit comments

Comments
 (0)