|
| 1 | +// Copyright (c) 2023-2026, Nubificus LTD |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +// See the License for the specific language governing permissions and |
| 13 | +// limitations under the License. |
| 14 | + |
| 15 | +package unikernels |
| 16 | + |
| 17 | +import ( |
| 18 | + "fmt" |
| 19 | + "path/filepath" |
| 20 | + "strconv" |
| 21 | + "strings" |
| 22 | + |
| 23 | + "github.com/urunc-dev/urunc/pkg/unikontainers/types" |
| 24 | +) |
| 25 | + |
| 26 | +const ( |
| 27 | + FreeBSDUnikernel string = "freebsd" |
| 28 | + netStartMarker string = "UNS" // Net configuration start marker |
| 29 | + netEndMarker string = "UNE" // Net configuration end marker |
| 30 | + paddingMarker string = "PAD" // Padding bytes |
| 31 | +) |
| 32 | + |
| 33 | +type FreeBSD struct { |
| 34 | + Command []string |
| 35 | + Monitor string |
| 36 | + Env []string |
| 37 | + BlockImgAsRootfs bool |
| 38 | + Net FreeBSDNet |
| 39 | + Block []types.BlockDevParams |
| 40 | + ProcConfig types.ProcessConfig |
| 41 | +} |
| 42 | + |
| 43 | +type FreeBSDNet struct { |
| 44 | + Address string |
| 45 | + Gateway string |
| 46 | + Mask string |
| 47 | +} |
| 48 | + |
| 49 | +func (f *FreeBSD) CommandString() (string, error) { |
| 50 | + if f.BlockImgAsRootfs { |
| 51 | + return "vfs.root.mountfrom=ext2fs:/dev/vtbd0", nil |
| 52 | + } |
| 53 | + return "vfs.root.mountfrom=/dev/vtbd0", nil |
| 54 | +} |
| 55 | + |
| 56 | +func (f *FreeBSD) SupportsBlock() bool { |
| 57 | + return true |
| 58 | +} |
| 59 | + |
| 60 | +func (f *FreeBSD) SupportsFS(fsType string) bool { |
| 61 | + switch fsType { |
| 62 | + case "ext2": |
| 63 | + return true |
| 64 | + default: |
| 65 | + return false |
| 66 | + } |
| 67 | +} |
| 68 | + |
| 69 | +func (f *FreeBSD) MonitorNetCli(_ string, _ string) string { |
| 70 | + // TODO: Commenting out, since FreeBSd does not work properly over Qemu microVM |
| 71 | + // switch f.Monitor { |
| 72 | + // TODO: Commenting out, since it is not working properly |
| 73 | + // case "qemu": |
| 74 | + // netOption := " -netdev tap,id=net0,script=no,downscript=no,ifname=" + ifName |
| 75 | + // netOption += " -device virtio-net-device,netdev=net0,mac=" + mac |
| 76 | + // return netOption |
| 77 | + // default: |
| 78 | + // return "" |
| 79 | + // } |
| 80 | + return "" |
| 81 | +} |
| 82 | + |
| 83 | +func (f *FreeBSD) MonitorBlockCli() []types.MonitorBlockArgs { |
| 84 | + if len(f.Block) == 0 { |
| 85 | + return nil |
| 86 | + } |
| 87 | + blkArgs := make([]types.MonitorBlockArgs, 0, len(f.Block)) |
| 88 | + switch f.Monitor { |
| 89 | + // TODO: Commenting out, since it is not working properly |
| 90 | + // case "qemu": |
| 91 | + // for _, aBlock := range f.Block { |
| 92 | + // bcli1 := fmt.Sprintf(" -device virtio-blk-device,serial=%s,drive=%s", aBlock.ID, aBlock.ID) |
| 93 | + // bcli2 := fmt.Sprintf(" -drive format=raw,if=none,id=%s,file=%s", aBlock.ID, aBlock.Source) |
| 94 | + // blkArgs = append(blkArgs, types.MonitorBlockArgs{ |
| 95 | + // ExactArgs: bcli1 + bcli2, |
| 96 | + // }) |
| 97 | + // } |
| 98 | + case "firecracker": |
| 99 | + for _, aBlock := range f.Block { |
| 100 | + blkArgs = append(blkArgs, types.MonitorBlockArgs{ |
| 101 | + ID: "FC" + aBlock.ID, |
| 102 | + Path: aBlock.Source, |
| 103 | + }) |
| 104 | + } |
| 105 | + blkArgs = append(blkArgs, types.MonitorBlockArgs{ |
| 106 | + ID: "FC_URUNIT_CONFIG", |
| 107 | + Path: urunitConfPath, |
| 108 | + }) |
| 109 | + default: |
| 110 | + return nil |
| 111 | + } |
| 112 | + |
| 113 | + return blkArgs |
| 114 | +} |
| 115 | + |
| 116 | +func (f *FreeBSD) MonitorCli() types.MonitorCliArgs { |
| 117 | + // TODO: Commenting out, since FreeBSd does not work properly over Qemu microVM |
| 118 | + // switch f.Monitor { |
| 119 | + // case "qemu": |
| 120 | + // monArgs := " -M microvm,rtc=on,acpi=off,pic=off,accel=kvm -global virtio-mmio.force-legacy=false -no-reboot -display none -nodefaults -serial stdio" |
| 121 | + // return types.MonitorCliArgs{ |
| 122 | + // OtherArgs: monArgs, |
| 123 | + // } |
| 124 | + // default: |
| 125 | + // return types.MonitorCliArgs{} |
| 126 | + // } |
| 127 | + return types.MonitorCliArgs{} |
| 128 | +} |
| 129 | + |
| 130 | +func (f *FreeBSD) Init(data types.UnikernelParams) error { |
| 131 | + // if Mask is empty, there is no network support |
| 132 | + if data.Net.Mask != "" { |
| 133 | + f.Net.Address = data.Net.IP |
| 134 | + f.Net.Gateway = data.Net.Gateway |
| 135 | + f.Net.Mask = data.Net.Mask |
| 136 | + } |
| 137 | + f.Block = data.Block |
| 138 | + f.Env = data.EnvVars |
| 139 | + f.Command = data.CmdLine |
| 140 | + f.Monitor = data.Monitor |
| 141 | + f.ProcConfig = data.ProcConf |
| 142 | + f.BlockImgAsRootfs = false |
| 143 | + if data.Rootfs.MountedPath != "" { |
| 144 | + f.BlockImgAsRootfs = true |
| 145 | + } |
| 146 | + |
| 147 | + err := f.setupUrunitConfig(data.Rootfs) |
| 148 | + if err != nil { |
| 149 | + return err |
| 150 | + } |
| 151 | + |
| 152 | + return nil |
| 153 | +} |
| 154 | + |
| 155 | +// setupUrunitConfig creates the urunit configuration file with environment variables. |
| 156 | +func (f *FreeBSD) setupUrunitConfig(rfs types.RootfsParams) error { |
| 157 | + urunitConfig := f.buildUrunitConfig() |
| 158 | + |
| 159 | + urunitConfigFile := filepath.Join(rfs.MonRootfs, urunitConfPath) |
| 160 | + err := createFile(urunitConfigFile, urunitConfig) |
| 161 | + if err != nil { |
| 162 | + return fmt.Errorf("failed to setup urunit config: %w", err) |
| 163 | + } |
| 164 | + |
| 165 | + return nil |
| 166 | +} |
| 167 | + |
| 168 | +// buildEnvConfig creates the environment configuration content for urunit. |
| 169 | +func (f *FreeBSD) buildUrunitConfig() string { |
| 170 | + // Format: UES\n<env1>\n<env2>\n...\nUEE\n |
| 171 | + var sb strings.Builder |
| 172 | + sb.WriteString(envStartMarker) |
| 173 | + sb.WriteString("\n") |
| 174 | + if len(f.Env) > 0 { |
| 175 | + sb.WriteString(strings.Join(f.Env, "\n")) |
| 176 | + sb.WriteString("\n") |
| 177 | + } |
| 178 | + sb.WriteString(envEndMarker) |
| 179 | + sb.WriteString("\n") |
| 180 | + sb.WriteString(lpcStartMarker) |
| 181 | + sb.WriteString("\n") |
| 182 | + sb.WriteString("UID:") |
| 183 | + sb.WriteString(strconv.FormatUint(uint64(f.ProcConfig.UID), 10)) |
| 184 | + sb.WriteString("\n") |
| 185 | + sb.WriteString("GID:") |
| 186 | + sb.WriteString(strconv.FormatUint(uint64(f.ProcConfig.GID), 10)) |
| 187 | + sb.WriteString("\n") |
| 188 | + sb.WriteString("WD:") |
| 189 | + sb.WriteString(f.ProcConfig.WorkDir) |
| 190 | + sb.WriteString("\n") |
| 191 | + sb.WriteString("ARC:") |
| 192 | + sb.WriteString(strconv.FormatUint(uint64(len(f.Command)), 10)) |
| 193 | + sb.WriteString("\n") |
| 194 | + for _, c := range f.Command { |
| 195 | + sb.WriteString("ARV:") |
| 196 | + sb.WriteString(c) |
| 197 | + sb.WriteString("\n") |
| 198 | + } |
| 199 | + sb.WriteString(lpcEndMarker) |
| 200 | + sb.WriteString("\n") |
| 201 | + sb.WriteString(blkStartMarker) |
| 202 | + sb.WriteString("\n") |
| 203 | + for _, b := range f.Block { |
| 204 | + if b.ID == "rootfs" { |
| 205 | + continue |
| 206 | + } |
| 207 | + sb.WriteString("ID:") |
| 208 | + if f.Monitor == "firecracker" { |
| 209 | + sb.WriteString("FC") |
| 210 | + } |
| 211 | + sb.WriteString(b.ID) |
| 212 | + sb.WriteString("\n") |
| 213 | + sb.WriteString("MP:") |
| 214 | + sb.WriteString(b.MountPoint) |
| 215 | + sb.WriteString("\n") |
| 216 | + } |
| 217 | + sb.WriteString(blkEndMarker) |
| 218 | + sb.WriteString("\n") |
| 219 | + sb.WriteString(netStartMarker) |
| 220 | + sb.WriteString("\n") |
| 221 | + sb.WriteString("IP:") |
| 222 | + sb.WriteString(f.Net.Address) |
| 223 | + sb.WriteString("\n") |
| 224 | + sb.WriteString("GW:") |
| 225 | + sb.WriteString(f.Net.Gateway) |
| 226 | + sb.WriteString("\n") |
| 227 | + sb.WriteString("MSK:") |
| 228 | + sb.WriteString(f.Net.Mask) |
| 229 | + sb.WriteString("\n") |
| 230 | + sb.WriteString(netEndMarker) |
| 231 | + sb.WriteString("\n") |
| 232 | + for i := 0; i < 128; i++ { |
| 233 | + sb.WriteString(paddingMarker) |
| 234 | + sb.WriteString("\n") |
| 235 | + } |
| 236 | + return sb.String() |
| 237 | +} |
| 238 | + |
| 239 | +func newFreeBSD() *FreeBSD { |
| 240 | + freebsdStruct := new(FreeBSD) |
| 241 | + return freebsdStruct |
| 242 | +} |
0 commit comments