Как я научился перезагружаться в нужную ОС через UEFI

Добрый вечер, Habrahabr,
Сегодня мы научимся перезагружаться из Linux прямиком в Windows и обратно всего за один (двойной) клик.
image


Дано:


  • Два диска с GPT с разными ОС
  • Материнская плата с UEFI и отключеным Legacy Mode
  • Windows, которую бережно поставили с полной поддержкой UEFI
  • Linux (у меня Manjaro), который бережно поставили, выпилив любое упоминание GRUB и прочих старомодных вещей
  • rEFInd — красивый менеджер загрузки (нет, не загрузчик)


Надо:


  • Написать два скрипта на *sh и cmd (bat), которые позволят перезагрузиться в нужную ОС


Примечания:


  • Рабочие варианты обозначены, как Решение, остальное — мои рассуждения и описание пути к цели.
  • Я сознательно опускаю описание тех деталей, которые не относятся к сути настройки, либо легко варьируются. Упоминаются же эти детали потому, что для меня они не были очевидны на момент настройки, поэтому статья становится более понятной для неискушенных читателей, которые не будут видеть в ней инструкцию по рисованию совы.


Linux:


Linux is your bro
Linux — прекрасная база для работы с компьютером, поэтому всё, что нам нужно уже есть в репозиториях, а коллективный разум сообщества знает всё и всегда готов помочь. Поэтому, после недолгого изучения интернета, ставим efibootmgr:


У меня это было так
sudo pacman -S efibootmgr


Отлично, теперь запускаем:


sudo efibootmgr


Видим что-то вроде этого
Timeout: 1 seconds
BootOrder: 0001,0000
Boot0000* Windows Boot Manager
Boot0001* rEFInd Boot Manager


Внимательные читатели уже наверняка заметили, что в выводе что-то не так, но тогда я был окрылён мыслью о том, что через 15 минут буду летать между операционками без проблем, и не обратил на это должного внимания.
Ок, 3 минуты на документацию, и мы находим нужный параметр »-n», который выставляет кастомый порядок загрузки ровно на один раз. Пробуем выполнить эту команду:
Решение


sudo efibootmgr -n 0000 && sync && reboot


И оказываемся в Windows, как того и желали. Теперь эту команду записываем в скрипт/alias/*.desktop-файл и радуемся тому, как всё здорово получилось.


Windows:


Typical Windows
Началось всё с поиска аналога efibootmgr для Windows, которого в чистом виде, конечно же, нет. Для успокоения совести я даже попытался использовать Linux Subsystem, но это, конечно же, не сработало.
Беглый поиск по интернетам показал, что схожим функционалом по модификации NVRAM обладает утилита bcdedit. Отлично, думаю я, запускаю PowerShell из-под Администратора и пишу


bcdedit /enum firmware


Вот что я увидел

Firmware Boot Manager
---------------------
identifier              {fwbootmgr}
displayorder            {6893bb38-946b-11e7-b175-9301bd8a88f4}
                        {bootmgr}
timeout                 1

Windows Boot Manager
--------------------
identifier              {bootmgr}
device                  partition=\Device\HarddiskVolume2
path                    \EFI\Microsoft\Boot\bootmgfw.efi
description             Windows Boot Manager
locale                  ru-RU
inherit                 {globalsettings}
default                 {current}
resumeobject            {6893bb40-946b-11e7-b175-9301bd8a88f4}
toolsdisplayorder       {memdiag}
timeout                 30

Firmware Application (101fffff)
-------------------------------
identifier              {6893bb38-946b-11e7-b175-9301bd8a88f4}
device                  partition=\Device\HarddiskVolume2
path                    \EFI\REFIND\REFIND_X64.EFI
description             rEFInd Boot Manager


Где Linux? Куда грузиться? Ненавижу винду
На самом деле оказалось, что виновата не Windows, а я (да-да, тот самый момент для внимательных пользователей), и вот почему: rEFInd — прекрасная утилита, которая обычно, для корректной работы, требует только установить себя. Она умеет подхватывать все .efi файлы, разные дистрибутивы с разными ядрами, сама подставляет иконки. Прелесть, а не инструмент. Но это сыграло со мной злую шутку, так как оказалось, что UEFI ничего не знает про Linux, так как отсутствует соответствующий ему .efi-файл.
Поэтому презагружаемся обратно в Linux, конфигурируем systemd-boot (bootctl). Теперь всё выглядит вот так:


Timeout: 1 seconds
BootOrder: 0001,0003,0000,0002
Boot0000* Windows Boot Manager
Boot0001* rEFInd Boot Manager
Boot0002* Linux Boot Manager
Boot0003* Manjaro


Возвращаемся обратно и снова запускаем.


bcdedit /enum firmware


Вот что я увидел теперь

Firmware Boot Manager
---------------------
identifier              {fwbootmgr}
displayorder            {6893bb38-946b-11e7-b175-9301bd8a88f4}
                        {bootmgr}
                        {ff0bc716-c088-11e7-bf74-000acd2dac7d}
                        {ff0bc716-c088-11e7-bf74-000acd2dac7d}
timeout                 1

Windows Boot Manager
--------------------
identifier              {bootmgr}
device                  partition=\Device\HarddiskVolume2
path                    \EFI\Microsoft\Boot\bootmgfw.efi
description             Windows Boot Manager
locale                  ru-RU
inherit                 {globalsettings}
default                 {current}
resumeobject            {6893bb40-946b-11e7-b175-9301bd8a88f4}
toolsdisplayorder       {memdiag}
timeout                 30

Firmware Application (101fffff)
-------------------------------
identifier              {6893bb38-946b-11e7-b175-9301bd8a88f4}
device                  partition=\Device\HarddiskVolume2
path                    \EFI\REFIND\REFIND_X64.EFI
description             rEFInd Boot Manager

Firmware Application (101fffff)
-------------------------------
identifier              {ff0bc716-c088-11e7-bf74-000acd2dac7d}
device                  partition=\Device\HarddiskVolume2
path                    \EFI\SYSTEMD\SYSTEMD-BOOTX64.EFI
description             Linux Boot Manager

Firmware Application (101fffff)
-------------------------------
identifier              {ff0bc717-c088-11e7-bf74-000acd2dac7d}
device                  partition=\Device\HarddiskVolume1
path                    \EFI\manjaro\vmlinuz-4.13-x86_64
description             Manjaro


Тут стоит упомянуть, что проблему мне помогали решать пользователи reddit. Именно благодаря им мы имеем следующий шаг:


bcdedit /bootsequence {ff0bc717-c088-11e7-bf74-000acd2dac7d}


Где аргументом является identifier необходимого варианта — Linux Boot Manager.


Troubleshooting

Powershell не даёт нормально выполнить эту команду, ругаясь на


The entry list data is not valid as specified.

Всё из-за того, что Microsoft периодически любит что-нибудь сломать. Решение просто и элегантно — вызываем классический CMD и работаем в нём. Это можно сделать командой


cmd


Перезагружаемся и видим, что порядок загрузки не изменился, а мы оказались в первом элементе в BootOrder (у меня это был rEFInd), выбираем Windows и видим страшный привет из времен DOS, который говорит нам, что \EFI\SYSTEMD\SYSTEMD-BOOTX64.EFI не найден. Да, мы изменили параметры загрузчика Windows, но не UEFI.
Борьба с этой ошибкой заняла у меня все праздники, но ничего путного не получалось. Я уже подумывал написать на C++ небольшую программку для этого (то, что это возможно, следует из существования такого софта, как EasyUEFI).
Но тут на очередном сайте я обнаружил вот такую конструкцию


 bcdedit /set {bootmgr} path ....


И тут меня осенило, что можно прямо сказать bcdedit что и куда писать. Дальше стоило только проверить догадку:
Решение


bcdedit.exe /set {fwbootmgr} bootsequence {ff0bc716-c088-11e7-bf74-000acd2dac7d} /addfirst


Тут важно, что мы явно сказали писать не в {bootmgr} (как он, видимо, делает по-умолчанию), а в {fwbootmgr}, что и является нашими настройками UEFI.
Перезагружаемся, и всё работает так, как мы этого хотели. Сохраняем это дело в bat/lnk, дописываем


shutdown /r /t 0


Выставляем запуск из-под администратора и готово!
Спасибо за внимание! Буду крайне рад замечаниям по технической части в комментариях, замечаниям по оформлению — в ЛС.

© Habrahabr.ru