# VMware Workstation 17.6.4 在 Deepin 25 上的安装记录与踩坑总结

> 本文档记录一次完整的 VMware Workstation 17.6.4 在 Deepin 25（磐石不可变系统）上的安装过程，
> 包括所有踩到的坑与对应的解决方案，供后续其他用户参考。

---

## 一、系统与软件版本信息

| 项目 | 版本 |
| --- | --- |
| 操作系统 | Deepin 25（社区版，代号 crimson） |
| 系统类型 | Desktop，不可变/磐石系统（ostree 只读 `/usr`） |
| 内核版本 | `6.18.36-amd64-desktop-rolling`（`#25.01.01.27 SMP PREEMPT_DYNAMIC`，2026-06-23 构建） |
| 内核源码/头文件 | `/usr/src/linux-headers-6.18.36-amd64-desktop-rolling` |
| GCC（Deepin） | 12.3.0（`gcc (Deepin 12.3.0-17deepin18)`） |
| 内核编译所用 GCC | `gcc (Deepin 12.3.0-17deepin12)` 12.3.0 |
| VMware 安装包 | `VMware-Workstation-Full-17.6.4-24832109.x86_64.bundle` |
| VMware 版本 | VMware Workstation 17.6.4 build-24832109 |
| 模块安装位置 | `/lib/modules/6.18.36-amd64-desktop-rolling/misc/{vmmon,vmnet}.ko` |

---

## 二、整体结论

- VMware Workstation 17.6.4 可在 Deepin 25 + 内核 6.18.36 上**完整安装并运行**。
- 官方 17.6.4 自带的 vmmon/vmnet 源码**无法直接编译**（内核 6.18 移除了数个旧 API），需要打补丁。
- mkubecek/vmware-host-modules 仓库最高只支持到 workstation-17.5.1，**不支持 17.6.x**，且其 17.5.1 分支在内核 6.18 上同样编译失败，因此只能给官方自带源码打补丁。

---

## 三、安装步骤总览

1. 临时将 `/usr` 设为可写（磐石系统）。
2. 重启系统。
3. 用 `--console` + 自动应答 EULA 的方式执行 bundle 安装。
4. 对内核模块源码打补丁并编译 vmmon.ko / vmnet.ko。
5. 安装模块、`depmod`、加载模块、创建设备节点。
6. 启动 VMware 验证。

---

## 四、坑点 1：磐石系统 `/usr` 只读导致安装失败

### 现象

执行 bundle 安装时报错：

```
[Error] Failed to run as root user without username configured...
Read-only file system: '/usr/lib/vmware-installer'
Installation was unsuccessful
```

Deepin 25 是**不可变系统（磐石）**，`/usr` 挂载为只读 overlay：

```
/usr  overlay  ro  (usr-overlay)
```

### 解决方案

使用官方工具临时开启 `/usr` 可写（`-d` 指定目录，`-r` 指定只读）：

```bash
sudo deepin-immutable-writable enable -d /usr
# 输出示例：Enable: true, OverlayDirs: [/usr], ClearAfterReboot: false
sudo reboot
```

> 注意：执行后**必须重启**，重启后 `/usr` 才会变为可写（`mount` 显示 `rw`）。
> 本次设置 `ClearAfterReboot: false`，即重启后仍然保持可写。
> 如需还原为只读，可执行 `sudo deepin-immutable-writable disable -r /usr`。

---

## 五、坑点 2：bundle 安装器交互式 EULA 卡死

### 现象

使用 `--console` 安装时，安装器要求对**多个 EULA 逐一交互**（每个协议都要按 `Enter` 翻页 + 输入 `yes` 同意）。
简单地用 `yes |` 管道、或用 expect 脚本时出现各类问题：

- `yes |` 无法在合适时机输入回车/`yes`，安装器卡住。
- expect 脚本若对 `password.*` 或 EULA 正文中的关键词做过匹配，容易把密码无限重注入，产生几十万行日志。
- 管道提前 EOF 时安装器报 `EOFError: EOF when reading a line` 直接崩溃。

### 解决方案

关键点：**用管道把「回车 + yes」按足够的次数喂给安装器**，并通过环境变量 `PAGER=cat` 禁止分页：

```bash
{ printf '当前机器的root密码\n'; for i in $(seq 1 40); do echo yes; done; } \
  | sudo -S env PAGER=cat \
      ./VMware-Workstation-Full-17.6.4-24832109.x86_64.bundle --console \
  > /tmp/vmware-pipe-install.log 2>&1
echo $?   # 期望输出 0
```

要点说明：
- `printf '当前机器的root密码\n'` 是第一行的 sudo 密码（sudo 会要求先输入密码）。
- 每个 EULA 通常需要两次 `yes`（一个协议内两个问题），40 次覆盖 17.6.4 的全部协议绰绰有余。
- `PAGER=cat` 让安装器不再进入分页模式，避免“按任意键继续”的交互。
- 必须用 `sudo -S` 从 stdin 读密码，否则与后续输入冲突。

---

## 六、坑点 3：内核 6.18 移除了 `EXTRA_CFLAGS`，导致找不到 `driver-config.h`

### 现象

用 `vmware-modconfig --console --install-all` 或手动编译模块时报错：

```
linux/driver.c:20:10: fatal error: driver-config.h: No such file or directory
```

根因：vmmon/vmnet 的 `Makefile.kernel` 使用 `EXTRA_CFLAGS` 传递 `-I` 头文件路径，
但 **内核 6.18 已移除 `EXTRA_CFLAGS` 支持**（`scripts/Makefile.build` / `Makefile.lib` 中已无此变量），
导致头文件路径没传进去。

### 解决方案

把模块源码里的 `EXTRA_CFLAGS` 改为 `ccflags-y`（两者语义一致，`ccflags-y` 是现代写法）。

- `/tmp/vmmod/vmmon-only/Makefile.kernel`
- `/tmp/vmmod/vmnet-only/Makefile.kernel`

```make
# 修改前
EXTRA_CFLAGS := $(CC_OPTS) $(INCLUDE)
# 修改后
ccflags-y := $(CC_OPTS) $(INCLUDE)
```

---

## 七、坑点 4：`del_timer_sync()` 被移除 → `timer_delete_sync()`

### 现象

编译 vmmon 时（补丁 3 之后）报错：

```
linux/driver.c:349:4: error: implicit declaration of function 'del_timer_sync' [-Werror=implicit-function-declaration]
```

根因：内核 6.18 移除了 `del_timer_sync`，改名为 `timer_delete_sync`。

### 解决方案

把 vmmon 源码中的调用全部替换：

```bash
sed -i 's/del_timer_sync(/timer_delete_sync(/g' \
  /tmp/vmmod/vmmon-only/linux/hostif.c \
  /tmp/vmmod/vmmon-only/linux/driver.c
```

涉及位置：
- `linux/driver.c:349` → `timer_delete_sync(&tscTimer);`
- `linux/hostif.c:1998` → `timer_delete_sync(&uptimeState.timer);`

---

## 八、坑点 5：`rdmsrl_safe()` 被移除 → `rdmsrq_safe()`

### 现象

编译 vmmon 时报错：

```
linux/hostif.c:3413:10: error: implicit declaration of function 'rdmsrl_safe'; did you mean 'rdmsrq_safe'?
```

根因：内核 6.18 将 `rdmsrl_safe` 改名为 `rdmsrq_safe`。

### 解决方案

```bash
sed -i 's/rdmsrl_safe(/rdmsrq_safe(/g' /tmp/vmmod/vmmon-only/linux/hostif.c
```

---

## 九、坑点 6：vmnet 使用旧式 `init_module` 导出名，6.18 不再接受

### 现象

编译 vmnet 时报 objtool 错误（旧式符号 `init_module` 被标记 deprecated）：

```
objtool: Magic init_module() function name is deprecated
```

内核 6.18 要求模块必须通过 `module_init()/module_exit()` 宏注册入口/出口函数，
不再接受裸符号名 `init_module` / `cleanup_module`。

### 解决方案

修改 `/tmp/vmmod/vmnet-only/driver.c`：

1. 函数定义改名：

```c
int
vmnet_init_module(void)        // 原：init_module(void)
{
   ...
}

void
vmnet_cleanup_module(void)     // 原：cleanup_module(void)
{
   ...
}
```

2. 文件末尾（`MODULE_INFO(supported, "external");` 之后）追加宏注册：

```c
module_init(vmnet_init_module);
module_exit(vmnet_cleanup_module);
```

> 可选：建议同时把函数声明为 `static int __init` / `static void __exit`，更规范。

---

## 十、模块编译、安装与加载

### 编译（在官方自带的模块源码上打补丁后进行）

模块源码解包自 `/usr/lib/vmware/modules/source/vmmon.tar` 和 `vmnet.tar`：

```bash
mkdir -p /tmp/vmmod && cd /tmp/vmmod
tar xf /usr/lib/vmware/modules/source/vmmon.tar
tar xf /usr/lib/vmware/modules/source/vmnet.tar

# ... 依次应用坑点 3/4/5/6 的补丁 ...

make -C /tmp/vmmod/vmmon-only       # 生成 vmmon.ko
make -C /tmp/vmmod/vmnet-only       # 生成 vmnet.ko
```

> 编译时若出现 `Skipping BTF generation ... due to unavailability of vmlinux` 仅为提示，可忽略。
> 如果 vmnet 编译出现 `warning: objtool: ... csum_partial_copy_nocheck() with UACCESS enabled`，
> 只是 objtool 警告，不影响生成 `.ko`。

### 安装到内核模块目录并加载

```bash
sudo mkdir -p /lib/modules/$(uname -r)/misc
sudo cp /tmp/vmmod/vmmon-only/vmmon.ko /tmp/vmmod/vmnet-only/vmnet.ko \
     /lib/modules/$(uname -r)/misc/
sudo depmod -a
sudo modprobe vmmon
sudo modprobe vmnet
```

> `/lib` 在 Debian 上是 `/usr` 的符号链接，因 `/usr` 已可写所以可以直接写入。

### 创建设备节点

vmnet 模块用 `register_chrdev(119, "vmnet", ...)` 注册主设备号 119，但**不会自动创建 `/dev/vmnet*`**，
需要手动创建（权限 666，次要设备号 0–8）：

```bash
for i in 0 1 2 3 4 5 6 7 8; do
  sudo mknod -m 666 /dev/vmnet$i c 119 $i
done
```

> 开机后这些节点由 `/etc/init.d/vmware`（S01vmware）中的 vnetlib 自动重建，无需手动持久化。

### 验证

```bash
lsmod | grep -E 'vmmon|vmnet'     # 应看到两个模块
ls -la /dev/vmmon /dev/vmnet0     # 应存在设备节点
vmware -v                          # 输出：VMware Workstation 17.6.4 build-24832109
```

---

## 十一、FAQ / 提示

- **为何不用 mkubecek/vmware-host-modules？** 该仓库最高仅支持 workstation-17.5.1，不支持 17.6.x；
  且其 17.5.1 分支在 6.18 内核上同样因 `EXTRA_CFLAGS` 被移除而失败。
- **补丁参考顺序**：先改 `ccflags-y`（否则连头文件都找不到），再改两个已移除/改名的 API，
  最后处理 vmnet 的 init_module 问题。
- **遇到内核更新**：如果内核升级到新版本，需要在新版本目录下重复“编译→安装→depmod”步骤，
  旧的 `.ko` 按内核版本隔离，不会冲突。
- **卸载/清理**：可用 `sudo vmware-installer -u vmware-workstation`，或删除
  `/lib/modules/$(uname -r)/misc/vm{mon,net}.ko` 后 `sudo depmod -a`。

---

## 十二、附：本次实际执行的完整命令序列

```bash
# 1. 开启 /usr 可写（磐石系统）
sudo deepin-immutable-writable enable -d /usr
sudo reboot

# 2. 安装 bundle（自动应答 EULA）
# 先进入 bundle 文件所在目录，再执行（目录按实际下载位置而定）
cd /path/to/bundle/
{ printf '当前机器的root密码\n'; for i in $(seq 1 40); do echo yes; done; } \
  | sudo -S env PAGER=cat \
      ./VMware-Workstation-Full-17.6.4-24832109.x86_64.bundle --console

# 3. 解包模块源码
mkdir -p /tmp/vmmod && cd /tmp/vmmod
tar xf /usr/lib/vmware/modules/source/vmmon.tar
tar xf /usr/lib/vmware/modules/source/vmnet.tar

# 4. 补丁：EXTRA_CFLAGS -> ccflags-y（vmmon、vmnet 的 Makefile.kernel）
# 5. 补丁：del_timer_sync -> timer_delete_sync（vmmon）
# 6. 补丁：rdmsrl_safe -> rdmsrq_safe（vmmon）
# 7. 补丁：init_module 重命名 + module_init/module_exit（vmnet driver.c）

# 8. 编译
make -C /tmp/vmmod/vmmon-only
make -C /tmp/vmmod/vmnet-only

# 9. 安装与加载
sudo mkdir -p /lib/modules/$(uname -r)/misc
sudo cp /tmp/vmmod/vmmon-only/vmmon.ko /tmp/vmmod/vmnet-only/vmnet.ko \
     /lib/modules/$(uname -r)/misc/
sudo depmod -a
sudo modprobe vmmon && sudo modprobe vmnet

# 10. 创建设备节点
for i in 0 1 2 3 4 5 6 7 8; do
  sudo mknod -m 666 /dev/vmnet$i c 119 $i
done

# 11. 启动验证
vmware -v
vmware &
```
