Skip to content

Commit fc6c17c

Browse files
authored
refactor(wsl): improve BIOS check (#108)
Signed-off-by: Kevin Cui <bh@bugs.cc>
1 parent e5f0eaa commit fc6c17c

File tree

2 files changed

+141
-33
lines changed

2 files changed

+141
-33
lines changed

pkg/wsl/check.go

Lines changed: 50 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ func Check(ctx context.Context, opt *types.InitOpt) {
2828
return
2929
}
3030

31-
if ok := checkBIOS(ctx, opt); !ok {
31+
if ok := checkBIOS(opt); !ok {
32+
opt.Logger.Info("Virtualization is not supported")
33+
event.NotifyInit(event.NotSupportVirtualization)
34+
35+
<-ctx.Done()
3236
return
3337
}
3438

@@ -77,28 +81,38 @@ func checkVersion(ctx context.Context, opt *types.InitOpt) bool {
7781
}
7882
}
7983

80-
func checkBIOS(ctx context.Context, opt *types.InitOpt) bool {
84+
func checkBIOS(opt *types.InitOpt) bool {
8185
log := opt.Logger
8286

8387
if list, err := getAllWSLDistros(log, false); err == nil && len(list) != 0 {
84-
log.Info("Exist WSL distros, BIOS may support virtualization")
85-
return true
86-
}
88+
var first string
89+
for key := range list {
90+
first = key
91+
break
92+
}
8793

88-
if isSupportedVirtualization(log) {
89-
log.Info("Virtualization is supported")
90-
return true
94+
flag := "TEST_PASS"
95+
96+
var out string
97+
_ = Exec(log).SetAllOut(&out).SetDistro(first).Run("echo", flag)
98+
if strings.Contains(out, flag) {
99+
log.Info("Exist WSL distros and succeeded invoke, BIOS support virtualization")
100+
return true
101+
}
102+
103+
if strings.Contains(out, "HCS_E_HYPERV_NOT_INSTALLED") {
104+
log.Info("execute wsl command failed, BIOS not support virtualization")
105+
return false
106+
}
91107
}
92108

93-
if isWillReportExpectedErrorInMountVHDX(log, opt) {
94-
log.Info("Expected error in mount vhdx, BIOS may support virtualization")
109+
recordCPUFeature(log)
110+
111+
if tryImportTestDistro(log) {
112+
log.Info("Test distro imported successfully, maybe BIOS support virtualization")
95113
return true
96114
}
97115

98-
log.Info("Virtualization is not supported")
99-
event.NotifyInit(event.NotSupportVirtualization)
100-
101-
<-ctx.Done()
102116
return false
103117
}
104118

@@ -164,7 +178,7 @@ func SkipConfigCheck(opt *types.InitOpt) {
164178
}
165179
}
166180

167-
func isSupportedVirtualization(log *logger.Context) bool {
181+
func recordCPUFeature(log *logger.Context) {
168182
vf, slat := sys.IsSupportedVirtualization()
169183
if !slat {
170184
log.Warn("SLAT is not supported")
@@ -173,35 +187,38 @@ func isSupportedVirtualization(log *logger.Context) bool {
173187
if !vf {
174188
log.Warn("VT-x is not supported")
175189
}
176-
177-
// If the CPU does not support SLAT, WSL2 cannot be started (but WSL1 can be started).
178-
// In modern CPUs, almost all CPUs support SLAT.
179-
// It is not possible to strictly determine this through `vf && slat`, because in VMware, SLAT is always false (even if "Virtualize Intel VT-x/EPT or AMD-V/RVI" is checked).
180-
// See:
181-
// https://github.com/microsoft/WSL/issues/4709
182-
// https://www.reddit.com/r/bashonubuntuonwindows/comments/izf4qp/cpus_without_slat_capability_cant_run_wsl_2/
183-
return vf
184190
}
185191

186-
func isWillReportExpectedErrorInMountVHDX(log *logger.Context, opt *types.InitOpt) bool {
187-
tempVhdx := filepath.Join(os.TempDir(), fmt.Sprintf("ovm-win-%s-%s.vhdx", opt.Name, util.RandomString(5)))
192+
func tryImportTestDistro(log *logger.Context) bool {
193+
random := fmt.Sprintf("ovm-test-distro-%s", util.RandomString(5))
194+
tempDir := filepath.Join(os.TempDir(), random)
188195
defer func() {
189-
_ = os.RemoveAll(tempVhdx)
196+
_ = os.RemoveAll(tempDir)
190197
}()
191198

192-
_, err := wslExec(log, "--mount", "--bare", "--vhd", tempVhdx)
193-
if err == nil {
194-
_, _ = wslExec(log, "--unmount", tempVhdx)
195-
log.Warnf("Unexpected loading succeeded; WSL may have modified the mechanism. In this case, we believe there is no issue")
199+
target := filepath.Join(tempDir, "target")
200+
emptyTar := filepath.Join(tempDir, "empty.tar")
201+
202+
if err := os.MkdirAll(target, 0755); err != nil {
203+
log.Warnf("Failed to create target directory: %v", err)
196204
return true
197205
}
198206

199-
if strings.Contains(err.Error(), "WSL_E_WSL2_NEEDED") {
200-
log.Warn("Mount vhdx failed, BIOS may not support virtualization")
207+
if err := os.WriteFile(emptyTar, []byte{}, 0644); err != nil {
208+
log.Warnf("Failed to create empty tar file: %v", err)
209+
return true
210+
}
211+
212+
var out string
213+
_ = Exec(log).SetAllOut(&out).Run("--import", random, target, emptyTar, "--version", "2")
214+
if strings.Contains(out, "HCS_E_HYPERV_NOT_INSTALLED") {
215+
_ = log.Errorf("Import test distro failed, BIOS not support virtualization")
201216
return false
202217
}
203218

204-
log.Infof("Mounting vhdx results in an expected error: %v", err)
219+
if err := Exec(log).Run("--unregister", random); err != nil {
220+
log.Warnf("Failed to unregister test distro: %v", err)
221+
}
205222

206223
return true
207224
}

pkg/wsl/exec.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// SPDX-FileCopyrightText: 2025 OOMOL, Inc. <https://www.oomol.com>
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package wsl
5+
6+
import (
7+
"bytes"
8+
"fmt"
9+
"strings"
10+
11+
"github.com/oomol-lab/ovm-win/pkg/logger"
12+
"github.com/oomol-lab/ovm-win/pkg/util"
13+
)
14+
15+
type ExecContext struct {
16+
log *logger.Context
17+
18+
distro string
19+
stdout *string
20+
stderr *string
21+
allOut *string
22+
}
23+
24+
func Exec(log *logger.Context) *ExecContext {
25+
return &ExecContext{
26+
log: log,
27+
}
28+
}
29+
30+
func (c *ExecContext) SetDistro(name string) *ExecContext {
31+
c.distro = name
32+
return c
33+
}
34+
35+
func (c *ExecContext) SetStdout(stdout *string) *ExecContext {
36+
if stdout != nil {
37+
c.stdout = stdout
38+
}
39+
return c
40+
}
41+
42+
func (c *ExecContext) SetStderr(stderr *string) *ExecContext {
43+
if stderr != nil {
44+
c.stderr = stderr
45+
}
46+
return c
47+
}
48+
49+
func (c *ExecContext) SetAllOut(allOut *string) *ExecContext {
50+
if allOut != nil {
51+
c.allOut = allOut
52+
}
53+
return c
54+
}
55+
56+
func (c *ExecContext) Run(args ...string) error {
57+
var newArgs []string
58+
if c.distro != "" {
59+
newArgs = append(newArgs, "-d", c.distro)
60+
}
61+
newArgs = append(newArgs, args...)
62+
63+
cmd := util.SilentCmd(Find(), newArgs...)
64+
var stdout bytes.Buffer
65+
var stderr bytes.Buffer
66+
cmd.Stdout = &stdout
67+
cmd.Stderr = &stderr
68+
cmd.Env = []string{"WSL_UTF8=1"}
69+
70+
cmdStr := fmt.Sprintf("%s %s", Find(), strings.Join(newArgs, " "))
71+
72+
c.log.Infof("Running wsl command: %s", cmdStr)
73+
74+
err := cmd.Run()
75+
76+
if c.stdout != nil {
77+
*c.stdout = stdout.String()
78+
}
79+
if c.stderr != nil {
80+
*c.stderr = stderr.String()
81+
}
82+
if c.allOut != nil {
83+
*c.allOut = stdout.String() + stderr.String()
84+
}
85+
86+
if err != nil {
87+
return fmt.Errorf("failed to run command `%s`: %s %s (%w)", cmdStr, stdout.String(), stderr.String(), err)
88+
}
89+
90+
return nil
91+
}

0 commit comments

Comments
 (0)