在折腾上一篇文章的时候,发现了一条有趣的折腾分支,在这台老设备上运行 Windows 操作系统。

看起来应该蛮有趣的,那么就折腾一下吧。

写在前面

最早知道 WoA(Windows on ARM) 项目,是 2020 看到这篇报道:在 Lumia 950XL 上运行 Windows 10 ARM64 版。不过,当时的项目完善程度、和我的设备匹配度,都没有那么高。

在写完上篇文章《Docker 加持的 安卓手机:随身携带的知识库(一)》后,发现了 K20 Pro 芯片(Snapdragon 855)对应的 WoA 项目:woa-msmnile,这是一个聚集了将 “woa” 移植到 SM8150 和其他较新芯片的开源项目组织。

翻阅项目相关动态,发现项目的完善度还蛮高的,这不得试试看嘛!

本篇文章,有参考和改进社区另外一个项目 woa-raphael/woa-raphael中的文档(2024 年),当文章完全写完后,我会对项目发起“完善 PR”。

以及,参考了更早时候(2021 年)的 edk2-porting 开源项目的社区用户 Chr_ 的实践过程

清晰又愉快的复现之路

在这篇文章里,我会尽量将其中琐碎的、容易疏忽的坑一并记录下来,选择使用稳定可复现的环境来进行,明确每一步命令的因果联系,希望对喜欢折腾的你有用。

关于 WoA msmnile

项目组织中有非常多的仓库和文档,但是任何文档(包括文档站)都没有主要的项目 woa-msmnile/msmnilePkg 中更新的快。所以,在折腾的时候,我们可以始终以它为准。

在这个主要项目的文档中,我们可以发现它主要支持了四类芯片的手机,其中支持 DSDT、设备支持度好的有:

  • 2019 年出品的 Qualcomm Snapdragon 855,代号:SM8150。
    • 比如:ASUS ROG2、LG G8 和 V50 系列、Nubia Mini 5G、OnePlus 7 系列、OPPO Reno ACE、Realme X3、三星 S10 和 Fold、三星 Tab S6 系列、小米 9、小米 K20 Pro、小米 Mix3、小米 Pad 5,以及小米 Poco X3 Pro、高通 QRD 855。
  • 2020 年出品的 Qualcomm Snapdragon 720G,代号:SM7125。
    • 包含:小米 Note 9S、高通 QRD 778。
  • 2021 年出品的 Qualcomm Snapdragon 888,代号:SM8350。
    • 包含:三星 Z Fold 3 5G、ZTE A31 Pro、高通 MTP 888。
  • 2021 和 2022 年出品的 Qualcomm Snapdragon 778G/778G+/782G, 代号:SM7325。
    • 包含:高通 QRD 778。

本文验证设备 K20Pro 的相关开源项目的主要维护者,分别是来自摩洛哥的 @degdag (将适配硬件的 Linux 主线项目 degdag/sm8150-mainline 维护到了 6.0 版本)和来自福建的 @sunflower2333woa-msmnile/msmnilePkg项目的主要维护者),感谢他们的无私付出。

准备工作

因为每个人的设备状况会有不同,如果你已经做了下面的一些工作,部分步骤就绪,可以选择性的略过。

在执行命令的时候,请胆大心细,勤做备份。我个人建议在折腾的时候,一条一条命令地执行,等待必要的执行结果返回,再执行下一条命令。

安卓开发工具和手机引导环境

我们所需要的安卓开发调试工具(ADB)和 Fastboot 相关的资源,可以在安卓开发者官方网站找到,platform-tools

根据你的具体操作系统环境,下载对应的工具版本即可。本文中,我是在 macOS 上进行操作,选择的是 macOS 软件版本。

为手机刷入 TWRP 恢复环境

这次刷机的时候,当我再次查看 TWRP 的 K20 Pro 子项目页面时,发现这台设备的引导程序竟然还有新版本

K20 Pro 硬件规格

TWRP Raphael 的 GitHub 项目页面中,除了能够看到这台手机设备的硬件详情之外,能够发现目前这个引导程序已经能够支持 Android 12、13、14 啦。

不过,如果你的 K20 Pro 安装的是低于 Android 12 的操作系统,那么你需要使用下载页面中后缀是 9-0-raphael.img 的引导文件。

这里,我以高版本的引导文件为例:

引导对于折腾手机非常重要,所以建议在下载后,进行必要的 Hash 校验,确保下载文件是完整、有效的。

# 查看官方提供的校验值
# cat twrp-3.7.1_12-0-raphael.img.sha256
483a448620146aaa79c30ca1cdc4c2de2172c00401a5e93616d6f76c12bb2480  twrp-3.7.1_12-0-raphael.img

# 计算下载文件的校验值
# shasum -a 256 twrp-3.7.1_12-0-raphael.img
483a448620146aaa79c30ca1cdc4c2de2172c00401a5e93616d6f76c12bb2480  twrp-3.7.1_12-0-raphael.img

将手机和电脑使用数据线连在一起,先执行命令将手机切换到 fastboot 模式:

adb reboot bootloader

接着,将下载的引导镜像刷入手机:

# fastboot flash recovery twrp-3.7.1_12-0-raphael.img
Sending 'recovery' (65536 KB)                      OKAY [  1.663s]
Writing 'recovery'                                 OKAY [  0.259s]
Finished. Total time: 1.944s

然后,重启手机即可开始验证 TWRP 是否刷写正确:

# fastboot reboot
Rebooting                                          OKAY [  0.002s]
Finished. Total time: 0.002s

根据需求安装你需要的 TWRP 版本

最后,长按关机键盘和音量键上,我们就能够看到你刷入的指定版本的 TWRP 啦。

备份手机数据和固件数据

我个人不推荐使用正在使用的手机来折腾,如果你一定要这么做,请先妥善的备份这台手机上的数据,以免过程中出现意外,后悔莫及。

关闭手机,长按音量上键和电源键,进入手机的 TWRP 模式,然后点击备份,我个人建议至少备份 bootModemEFS 目录。

使用 TWRP 备份手机固件数据

当备份结束后,我们找到备份日志里的目录,将备份文件下载到本地电脑里以防万一,比如 TWRP 备份路径是这个:

/data/media/0/TWRP/BACKUPS/42cba029/2024-05-04--03-47-15_QQ3A200805001/ 

那么,我们可以用下面的命令,来一键从手机上拖下来各种备份文件:

# adb shell "ls /data/media/0/TWRP/BACKUPS/42cba029/2024-05-04--03-47-15_QQ3A200805001/" | xargs -I {} adb pull /data/media/0/TWRP/BACKUPS/42cba029/2024-05-04--03-47-15_QQ3A200805001/{} .


/data/media/0/TWRP/BACKUPS/42cba029/2024-05-04--03-47-15_QQ3A200805001/bluetooth.emmc.win: 1 file pulled, 0 skipped. 33.8 MB/s (1048576 bytes in 0.030s)
/data/media/0/TWRP/BACKUPS/42cba029/2024-05-04--03-47-15_QQ3A200805001/bluetooth.emmc.win.sha2: 1 file pulled, 0 skipped. 0.1 MB/s (85 bytes in 0.002s)
/data/media/0/TWRP/BACKUPS/42cba029/2024-05-04--03-47-15_QQ3A200805001/boot.emmc.win: 1 file pulled, 0 skipped. 41.1 MB/s (134217728 bytes in 3.112s)
...

备份完毕手机固件后,我们就可以甩开膀子折腾了。

下载适配手机的 UEFI 镜像

截止我这篇文章写完,目前 UEFI 项目最新的稳定版本是 releases/2402.86。这篇文章使用的 K20 Pro 对应的 UEFI 镜像非常小,只有 3MB:xiaomi-raphael_NOSB.img

下载这个文件到本地,我们稍后使用。

下载一个用于初步验证的 ARM PE

今年一月份,远景上有一位网友出于 ARM 环境没有可以用于维护系统的 PE,开始发布并迭代 CNBYDJ PE,截止上个月,版本已经更新到了 v1.4,我们可以在 PE 网站下载到最新版本的 ISO 镜像文件。

下载“CNBYDJ PE-V1.4.iso”到本地,同样稍后使用。

实践开始

接下来,我们首先来完成 Windows 安装环境的初始化准备,以及确保手机可以被引导至 Windows 环境(PE环境)。

第一步:调整分区表

我们首先需要使用 sgdisk(环境内置,无需下载),来调整分区表,来解锁默认分区的限制。

当然,任何时候,都不建议“摸黑”操作,先执行来了解下设备的分区表状况:

adb shell "sgdisk --print /dev/block/sda"

命令执行完毕,我们将看到类似下面的结果:

Disk /dev/block/sda: 123365376 sectors, 470.6 GiB
Logical sector size: 4096 bytes
Disk identifier (GUID): A5701557-F489-826C-FA44-357608497E4A
Partition table holds up to 32 entries
First usable sector is 6, last usable sector is 123365370
Partitions will be aligned on 2-sector boundaries
Total free space is 0 sectors (0 bytes)

Number  Start (sector)    End (sector)  Size       Code  Name
   1               6               7   8.0 KiB     FFFF  switch
   2               8              15   32.0 KiB    FFFF  ssd
   3              16              23   32.0 KiB    FFFF  dbg
   4              24              31   32.0 KiB    FFFF  bk01
   5              32              63   128.0 KiB   FFFF  bk02
   6              64             127   256.0 KiB   FFFF  bk03
   7             128             255   512.0 KiB   FFFF  bk04
   8             256             383   512.0 KiB   FFFF  keystore
   9             384             511   512.0 KiB   FFFF  frp
  10             512            1023   2.0 MiB     FFFF  bk05
  11            1024            2047   4.0 MiB     FFFF  misc
  12            2048            3071   4.0 MiB     FFFF  vm-data
  13            3072            4095   4.0 MiB     FFFF  bk06
  14            4096            6143   8.0 MiB     FFFF  logfs
  15            6144            8191   8.0 MiB     FFFF  bk07
  16            8192           12287   16.0 MiB    FFFF  oops
  17           12288           16383   16.0 MiB    FFFF  devinfo
  18           16384           20479   16.0 MiB    FFFF  oem_misc1
  19           20480           24575   16.0 MiB    FFFF  metadata
  20           24576           32603   31.4 MiB    FFFF  bk08
  21           32604           40959   32.6 MiB    FFFF  splash
  22           40960           49151   32.0 MiB    FFFF  bk09
  23           49152           65535   64.0 MiB    FFFF  persist
  24           65536           81919   64.0 MiB    FFFF  persistbak
  25           81920           98303   64.0 MiB    FFFF  logdump
  26           98304          131071   128.0 MiB   FFFF  minidump
  27          131072          163839   128.0 MiB   FFFF  rawdump
  28          163840          180223   64.0 MiB    FFFF  recovery
  29          180224          245759   256.0 MiB   FFFF  cache
  30          245760          507903   1024.0 MiB  FFFF  cust
  31          507904       123365370   468.7 GiB   FFFF  userdata

顺手做一个分区表备份,并将备份的结果保存到本地计算机:

# adb shell "sgdisk --backup=sgdisk-sda.bin /dev/block/sda"
The operation has completed successfully.

# adb pull sgdisk-sda.bin .
sgdisk-sda.bin: 1 file pulled, 0 skipped. 2.5 MB/s (5632 bytes in 0.002s)

我们可以先执行下面的命令,来扩大允许的分区表数量,避免后续执行过程中出现异常情况(比如允许 128 个分区项目存在):

adb shell sgdisk --resize-table=128 /dev/block/sda

命令执行完毕,我们将看到下面的日志结果:

# adb shell sgdisk --resize-table=128 /dev/block/sda
Warning: The kernel is still using the old partition table.
The new table will be used at the next reboot.
The operation has completed successfully.

完成了分区表的调整和备份后,就可以开始对磁盘分区进行调整啦。

第二步:对手机进行分区调整

我们可以借助 Parted,来在手机上进行分区操作,来准备 Windows 的安装分区,raphael-partitioning/parted

默认情况下,手机的 sbin 目录只有 bashsh

# adb shell "ls /sbin/"
bash
sh

我们通过 adb push 将下载好的分区工具传到手机里:

# 上传工具到手机里
# adb push ./parted /sbin/
./parted: 1 file pushed, 0 skipped. 162.7 MB/s (922984 bytes in 0.005s)

# 赋予可执行权限
# adb shell "chmod +x /sbin/parted"

# 再次查看目录,就有 `parted` 工具啦
# adb shell "ls /sbin/"
bash
parted
sh

接下来,我们使用 parted 对现有的分区进行调整,先进入交互式命令行中:

adb shell parted /dev/block/sda

输入 print,回车,我们能够得到磁盘的当前状况:

# print
Model: SAMSUNG KLUFG8R1EM-B0C1 (scsi)
Disk /dev/block/sda: 505GB
Sector size (logical/physical): 4096B/4096B
Partition Table: gpt
Disk Flags: 

Number  Start   End     Size    File system  Name        Flags
 1      24.6kB  32.8kB  8192B                switch
 2      32.8kB  65.5kB  32.8kB               ssd
 3      65.5kB  98.3kB  32.8kB               dbg
 4      98.3kB  131kB   32.8kB               bk01
 5      131kB   262kB   131kB                bk02
 6      262kB   524kB   262kB                bk03
 7      524kB   1049kB  524kB                bk04
 8      1049kB  1573kB  524kB                keystore
 9      1573kB  2097kB  524kB                frp
10      2097kB  4194kB  2097kB               bk05
11      4194kB  8389kB  4194kB               misc
12      8389kB  12.6MB  4194kB               vm-data
13      12.6MB  16.8MB  4194kB               bk06
14      16.8MB  25.2MB  8389kB               logfs
15      25.2MB  33.6MB  8389kB               bk07
16      33.6MB  50.3MB  16.8MB               oops
17      50.3MB  67.1MB  16.8MB               devinfo
18      67.1MB  83.9MB  16.8MB               oem_misc1
19      83.9MB  101MB   16.8MB  ext4         metadata
20      101MB   134MB   32.9MB               bk08
21      134MB   168MB   34.2MB               splash
22      168MB   201MB   33.6MB               bk09
23      201MB   268MB   67.1MB  ext4         persist
24      268MB   336MB   67.1MB  ext4         persistbak
25      336MB   403MB   67.1MB               logdump
26      403MB   537MB   134MB                minidump
27      537MB   671MB   134MB                rawdump
28      671MB   738MB   67.1MB               recovery
29      738MB   1007MB  268MB   ext4         cache
30      1007MB  2080MB  1074MB  ext4         cust
31      2080MB  505GB   503GB   ext4         userdata

和上面使用 sgdisk 获取的数据类似,但是尺寸的计算方式有些差别,导致了展示大小有些差异。与此同时,我们也能够清晰的看到,我们这台设备使用的是一块 UFS 2.1 规格的三星硬盘:KLUFG8R1EM-B0C1,建议最高运行温度是不超过 85 度。未来实际使用的时候,我们需要尽量避免手机温度过高。

言归正传,通常情况,userdata 分区将会是列表中最后一个分区。我们需要短暂的记录下 userdata 分区的编号,稍后要使用,我这里展示的编号是 31

我们在手机中的 TWRP 界面选择“挂载”功能,确保没有挂载 Data 分区,然后执行命令,调整分区尺寸:

# 31 替换为你的 UserData 分区编号
resizepart 31

命令正确执行后,分区工具会询问想要调整的尺寸:

End?  [505GB]?

如果你没有看到上面的对话提示,那么你需要在 TWRP 中卸载“Data”分区的挂载,或者手动输入命令,卸载分区,然后再次执行上面的命令:

adb shell umount /data/

因为我的不打算在这台设备上再深度使用 Android,就和社区文档推荐的保持一致,输入 32GB 并按下回车。当软件再次向我们确认的时候,输入 Yes 确认我们的选择即可。

#End?  [505GB]? 32GB                                                       
32GB
Warning: Shrinking a partition can cause data loss, are you sure you want to
continue?
Yes/No? Yes                                                               
# Yes

我们再次输入 p free 查看分区大小,可以发现 userdata 就已经听话的缩容到 32GB 了。并且,在这个分区后,我们有了一个 473GB 空闲磁盘空间,可以用于安装 Windows 系统。

31      2080MB  32.0GB  29.9GB  ext4         userdata
        32.0GB  505GB   473GB   Free Space

接下来,来创建一个紧贴着 userdata 分区的 esp 分区。因为 userdata 分区尺寸是 32GB(磁盘结束位置),所以,我们可以使用下面的命令,来创建一个尺寸为 500MB 的小巧 ESP 分区:

mkpart esp fat32 32GB 32.5GB

ESP 分区紧贴着的上一个分区 userdata 分区编号为 31,所以我们给 ESP 分区编号的时候,就需要设置为 32

set 32 esp on

再次使用 p feee 命令查看分区状况,能够看到我们上面命令的执行都生效了:

31      2080MB  32.0GB  29.9GB  ext4         userdata
        32.0GB  32.0GB  438kB   Free Space
32      32.0GB  32.5GB  499MB   fat32        esp         boot, esp
        32.5GB  505GB   473GB   Free Space

我们继续操作,来创建存放 Windows 的磁盘。结合上面的 esp 分区的结束位置(32.5GB)和整个磁盘大小505GB

mkpart win ntfs 32.5GB 485GB

这里我没有选择完整的占满整个磁盘,而是预留了 20GB 给后续 PE 维护系统使用(505GB-20GB=485GB)。

当所有命令都执行就绪,输入 quit 或者按下 CTRL + D 组合键来退出 parted 交互环境。

接着,在手机上的 TWRP 中选择“清除”、“格式化 Data”分区。然后重启手机,等待手机“恢复出厂设置”完毕,进入手机将能看到一台容量只有 32GB 的手机。

手机内置存储已经调整至 32GB

至此,基础的分区调整就结束啦。

第三步:安装 ARM PE 环境

因为安装 Windows 系统,不可避免的要使用 Windows 环境下的程序,为了便宜行事,我们再次重启手机到 TWRP 环境,折腾 PE 系统使用的磁盘分区,来获得一个能够安装 Windows 使用的操作环境。

依旧是参考上文中的方法,展开对 /dev/block/sda 磁盘的攻势:

adb push ./parted /sbin/
adb shell "chmod +x /sbin/parted"
adb shell parted /dev/block/sda

输入 p free 能够看到分区状况如下:

31      2080MB  32.0GB  29.9GB  ext4         userdata
        32.0GB  32.0GB  438kB   Free Space
32      32.0GB  32.5GB  499MB   fat32        esp         boot, esp
33      32.5GB  485GB   453GB   ntfs         win         msftdata
        485GB   505GB   20.3GB  Free Space

执行命令,创建一个分区给 ARM PE 使用:

mkpart armpe fat32 485GB 505GB

再次使用 p free 查看,磁盘分区就都准备就绪啦:

32      32.0GB  32.5GB  499MB                esp         boot, esp
33      32.5GB  485GB   453GB                win         msftdata
34      485GB   505GB   20.3GB  fat32        armpe       msftdata
        505GB   505GB   1028kB  Free Space

重启手机,再次进入 TWRP 环境后,在 adb shell 环境中,我们将 ARM PE 分区初始化为 fat32 格式,将 PE 分区挂入手机系统。

# 格式化磁盘
# mkfs.fat -F32 -s1 /dev/block/by-name/armpe                                                                                                                                       
mkfs.fat 3.0.28 (2015-05-16)

# 挂载到设备中
mkdir -p /armpe
mount /dev/block/by-name/armpe /armpe/

然后,将上文中我们下载好的 ARM PE 的 .iso 文件中的内容解压缩到本地计算机,并上传到这个分区内:

# 挂载 ISO 文件内容
# hdiutil attach CNBYDJ\ PE-V1.4.iso 
/dev/disk4          	                               	/Volumes/CNBYDJ PE-V1.4

# 切换工作目录
# cd /Volumes/CNBYDJ\ PE-V1.4

# 查看光盘内容
# ls
CNBYDJ.ico  autorun.inf boot        bootmgr.efi efi         sources

# 将光盘内容推送到 PE 分区
adb push boot /armpe/
adb push bootmgr.efi /armpe/
adb push efi /armpe/
adb push sources /armpe/

# 切换工作目录
cd -

# 弹出光盘
hdiutil detach disk4

将 PE 镜像中的内容上传到手机之后,PE 系统的安装就完成了一半啦。

第四步:更换手机启动引导方式为 UEFI

重启设备到 Fastboot 模式

我们在手机的 TWRP 中选择“重启”,重启到 “Fastboot” 模式。或者手机关机状态下,选择长按“音量减”和“电源按键”,启动到 “Fastboot” 模式。

接着,执行 fastboot flash 命令,完成手机的“启动管理器”的更新:

# fastboot flash boot /Users/soulteary/Downloads/xiaomi-raphael_NOSB.img 
Sending 'boot' (3088 KB)                           OKAY [  0.077s]
Writing 'boot'                                     OKAY [  0.030s]
Finished. Total time: 0.131s

当刷新完毕,执行 fastboot reboot 或者手动重启手机,手机重新启动后,UEFI 引导开始工作,我们就能够看到熟悉的“Windows 启动界面”啦。

设备正在引导至 Windows 系统(PE)

稍等片刻,系统将引导至 PE 环境,我们将能够看到 Windows PE 的桌面环境。

切换到 PE 环境的手机

最后

3月份的时候,媒体上有报道《突发,Windows再也不能安装安卓应用了》,微软官方停止了“适用于 Android 的 Windows 子系统”的支持,虽然 Windows 不能再安装 Android 应用了,但是 Android 手机可以偷家呀(哈哈)。

好了,就先写到这里,下一篇相关的文章我们将继续,进行正式的 Windows 环境的安装。

–EOF