Router OS 7 on UEFI

Hi!

How to install ROS7.1.3 on Hyper-V under Gen2 (UEFI Boot)?
Failed to download smoothly.
Need to convert an image file maybe? Or does the Host Server require a BIOS setup?

I experienced same problem.
It is easy to just instalI RouterOS 7 x86 from iso on uefi capable virtual machine but I wanted to boot use CHR v7 as LXD virtual machine.
It seems that CHR has everything needed to boot via uefi, but its partition table is some kind of Frankenstein between GPT and MBR. Also partition that has efi files on it is formatted as ext2 so it is not in line with UEFI standard which require that EFI files are stored on FAT/16/32 partition. Bellow is a script to correct this issues. I was able to boot it on LXD, qemu/KVM and hyper-v gen2 via uefi. Secure boot is not possible, efi file is not signed by Microsoft.
My script require that you have linux with installed following packages so it can operate and has to be executed with root privileges.
wget
unzip
qemu-img
qemu-nbd
rsync
gdisk

#!/bin/bash
wget --no-check-certificate https://download.mikrotik.com/routeros/7.3beta40/chr-7.3beta40.img.zip -O /tmp/chr.img.zip
unzip -p /tmp/chr.img.zip > /tmp/chr.img
rm -rf  chr.qcow2
qemu-img convert -f raw -O qcow2 /tmp/chr.img chr.qcow2
rm -rf /tmp/chr.im*
modprobe nbd
qemu-nbd -c /dev/nbd0 chr.qcow2
rm -rf /tmp/tmp*
mkdir /tmp/tmpmount/
mkdir /tmp/tmpefipart/
mount /dev/nbd0p1 /tmp/tmpmount/
rsync -a /tmp/tmpmount/ /tmp/tmpefipart/
umount /dev/nbd0p1
mkfs -t fat /dev/nbd0p1
mount /dev/nbd0p1 /tmp/tmpmount/
rsync -a /tmp/tmpefipart/ /tmp/tmpmount/
umount /dev/nbd0p1
rm -rf /tmp/tmp*
(
echo 2 # use GPT
echo t # change partition code
echo 1 # select first partition
echo 8300 # change code to Linux filesystem 8300
echo r # Recovery/transformation
echo h # Hybrid MBR
echo 1 2 # partitions added to the hybrid MBR
echo n # Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? (Y/N)
echo   # Enter an MBR hex code (default 83)
echo y # Set the bootable flag? (Y/N)
echo   # Enter an MBR hex code (default 83)
echo n # Set the bootable flag? (Y/N)
echo n # Unused partition space(s) found. Use one to protect more partitions? (Y/N)
echo w # write changes to disk
echo y # confirm
) | gdisk /dev/nbd0
qemu-nbd -d /dev/nbd0
echo "script finished, created file chr.qcow2"

to format it to hyper-v format just issue

qemu-img convert -f qcow2 -O vhdx chr.qcow2 chr.vhdx

@kriszos: thanks for a good piece of engineering, it’s a keeper!

@Marciboy, just curious but what’s the reason do you need gen2?

+1

the original solution.
but there are some problems as a result of using it: when you try to boot into hyper-v gen2, it loads, and even there is external access (mac winbox), but the local console keyboard is not available.
it does not respond to clicks.
in case something goes wrong with the network, it will be impossible to restore via the console

all modern operating systems can be loaded into uefi, consider this one of the stages of standardization: let all virtual operating systems be loaded into uefi, if possible.
there is no reason not to make boot support in uefi.

Huge thanks. It helped me out as I’ve just hit some improvement at a cloud provider where only UEFI boot became possible since I’ve installed a CHR there the last time. Even better, I’ve installed CHR 7.14.3 using the script and the console works just fine.

Would some kind person explain to those of us far less knowledgeable what exactly is the situation or use-case for installing ROS in this way?

Do I understand correctly that we are talking about installing ROS on a PC (x86 architecture)?

CHR is intended for deployment as a virtual machine - where you need a virtualized router you are familiar with rather than a bare Linux for production, or where you need to simulate some complicated setups, or where you just need a Mikrotik router running on a public IP for some training, which was my particular reason to deploy two CHRs today.

The virtualization host may be your Proxmox or another virtualization platform runninng on your old PC at home or on a bare metal in a data center, but you may also install CHR as a virtual server at some cloud provider, where you share the hardware with other customers. In all these cases, the virtualization system emulates also the boot environment; some of them allow both “legacy” BIOS mode and UEFI mode, some only one of them.

As of 7.15.3, the raw image of the CHR you can download from Mikrotik pages is not compatible with the UEFI mode as-is, so the clever script above is required to convert it to a compatible format before deploying it.

FWIW, I packaged @kriszos into a GitHub project that run it run a GitHub Action, see here: https://github.com/tikoci/fat-chr

With the GitHub re-packaged CHR disk image with UEFI support going to the “Releases” section on the project. For example, 7.15.3:
https://github.com/tikoci/fat-chr/releases/tag/Build10185466371-custom

But as @sindy notes most platforms do support “Legacy BIOS” options. And, even then… UEFI will work on some platforms — just some EUFI BIOS software cannot use Linux file system in Mikrotik’s “stock” disk images.

Wow, very cool!

Do I understand correclty that I can take a regular x86 PC, put a few NICs in it and run a virtualized instance of ROS making the entire box a router (or firewall)?

Indeed. Mikrotik recommends exactly this approach (a virtualization platform and a CHR on it even if the CHR would be the only VM running there) over the “x86” product that runs on bare metal but may be a bit behind with the network card drivers etc.

Yep, essentially the posted bash script makes use of gdisk (a version of it can run also on Windows, if you are not running Linux) by Roderick Smith, which is the defacto standard for checking/correcting/modifying MBR and GPT images or disks:
https://www.rodsbooks.com/gdisk/
and can - among other things - create hybrid MBR’s, with - in the words of the Author - are “flaky and dangerous”, the related page is aptly titled:
Hybrid MBRs: The Good, the Bad, and the So Ugly You’ll Tear Your Eyes Out
https://www.rodsbooks.com/gdisk/hybrid.html
but sometimes they just work. :slight_smile:


GPT specs are nicely published in the form of a pdf with over 2.200 pages (last time I checked), it is not a surprise that this (or that) manufacturer or software developer managed to create either non-standard setups or - on the opposite - provisions to allow booting from non-standard setups.
In other words when your OS actually boots that can happen for one of these three main reasons:

  1. both the image/disk AND the BIOS/UEFI are perfectly standard
  2. the image/disk is out of standard BUT the BIOS/UEFI is also allowing some slack or has added features implemented to allow that kind of non-standard
  3. the image/disk is out of standard BUT has some added features implemented that workarounds the (standard or non-standard) behaviour of the BIOS/UEFI

Hybrid MBR’s is essentially #3 above (non-standard image), but somehow the UEFI manages to boot from a 0x83 partition, so it is at the same time also #2.

The issue here is not complicated, @kriszos explains it:

So it not really the “hybrid” MBR/GPT disk partitioning that’s the issue here… the bigger issue is the disk formatting — basically ext2 file system is not readable by some EUFI BIOSes. See https://uefi.org/specs/UEFI/2.10/13_Protocols_Media_Access.html#file-system-format

Yes and no :open_mouth: , that may (or may not) be the issue (that then could be solved by only creating a FAT32 volume with the boot loader on it), but the script additionally attempts to solve the non-bootable issue by using a hybrid MBR alright, besides the FAT formatting:

(
echo 2 # use GPT
echo t # change partition code
echo 1 # select first partition
echo 8300 # change code to Linux filesystem 8300
echo r # Recovery/transformation
echo h # Hybrid MBR
echo 1 2 # partitions added to the hybrid MBR
echo n # Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? (Y/N)
echo   # Enter an MBR hex code (default 83)
echo y # Set the bootable flag? (Y/N)
echo   # Enter an MBR hex code (default 83)
echo n # Set the bootable flag? (Y/N)
echo n # Unused partition space(s) found. Use one to protect more partitions? (Y/N)
echo w # write changes to disk
echo y # confirm
) | gdisk /dev/nbd0

It is a workaround, and of the kind “So Ugly You’ll Tear Your Eyes Out”, but if it works, it works :slight_smile: .

The current RAW disk image (chr-7.15.3.img.zip) is however seemingly ALREADY (sort of) hybridized, with two 0x83 partitions in the MBR (WITHOUT the protective 0xEE one that GPT prescribes and that gdisk would create as third partition entry) and some BIOS boot code, so there should be no point in running gdisk on it (anymore).

BTW the partitioning is “wrong”, gdisk sees an overlap between the (GPT, secondary) partition table and the second partition AND a 1 sector overlap between the first and second partition, so it won’t allow writing to it until the issues are fixed. (the MBR partitioning is seemingly just fine)

It is possible that the good Mikrotik guys made these overlaps intentionally to prevent people fiddling with gdisk on it (or maybe they are using some different software that produces this overlap accidentally).

Anyway the “RouterOS Boot” partition is of type “EFI system” with Attribute flag 4 (set field 2, Bios bootable).

But there is no conversion to FAT or FAT32.

It has to be tested if the change of filesystem alone would solve the problem, as the gdisk part of the script won’t likely work correctly anymore.

The (GPT) EF (EFI System type) is “filesystem agnostic” and the 0x83 partition type in the MBR should also be irrelevant, as it is a "protective ID, telling Windows “here be lions”, as well not connected to the filesystem used.

P.S.: I just found the older 7.14.3 image, and that one is “good” (hence the script can work on it):

7.14.3 (good)

Command (? for help): v

No problems found. 0 free sectors (0 bytes) available in 0
segments, the largest of which is 0 (0 bytes) in size.

Command (? for help): i
Partition number (1-2): 1
Partition GUID code: C12A7328-F81F-11D2-BA4B-00A0C93EC93B (EFI System)
Partition unique GUID: 7009F6C9-F6E7-884F-B847-90C15779364A
First sector: 34 (at 17.0 KiB)
Last sector: 65569 (at 32.0 MiB)
Partition size: 65536 sectors (32.0 MiB)
Attribute flags: 0000000000000004
Partition name: ‘RouterOS Boot’

Command (? for help): i
Partition number (1-2): 2
Partition GUID code: 0FC63DAF-8483-4772-8E79-3D69D8477DE4 (Linux filesystem)
Partition unique GUID: 3E1AF678-B766-9E4E-BF6D-A84F74F7DB80
First sector: 65570 (at 32.0 MiB)
Last sector: 258047 (at 126.0 MiB)
Partition size: 192478 sectors (94.0 MiB)
Attribute flags: 0000000000000000
Partition name: ‘RouterOS’

vs.

7.15.3 (bad)

Command (? for help): v

Problem: partitions 2 and 1 overlap:
Partition 2: 65570 to 258048
Partition 1: 34 to 65570

Warning! Secondary partition table overlaps the last partition by
1 blocks!
Try reducing the partition table size by 4 entries.
(Use the ‘s’ item on the experts’ menu.)

Identified 2 problems!

Command (? for help): i
Partition number (1-2): 1
Partition GUID code: C12A7328-F81F-11D2-BA4B-00A0C93EC93B (EFI System)
Partition unique GUID: 530D7DB7-E875-CC44-9ABD-7F5CAEC90E75
First sector: 34 (at 17.0 KiB)
Last sector: 65570 (at 32.0 MiB)
Partition size: 65537 sectors (32.0 MiB)
Attribute flags: 0000000000000004
Partition name: ‘RouterOS Boot’

Command (? for help): i
Partition number (1-2): 2
Partition GUID code: 0FC63DAF-8483-4772-8E79-3D69D8477DE4 (Linux filesystem)
Partition unique GUID: 37D0E30B-6AA9-EF4E-A67A-4770FC37A330
First sector: 65570 (at 32.0 MiB)
Last sector: 258048 (at 126.0 MiB)
Partition size: 192479 sectors (94.0 MiB)
Attribute flags: 0000000000000000
Partition name: ‘RouterOS’

P.P.S.: As a curiosity only, the 7.14.3 image has a disk signature (which should mean that it has been mounted/accessed on a Windows system) and “botched” CHS values in the two partitions in the MBR.
These (wrong CHS) is fixed by gdisk when it writes the hybrid MBR (and it also seemingly deletes the disk signature), so it could be part of the reason why the image modified by the script worked (besides the addition of the protective 0xEE partition), it is very rare nowadays that a system would use CHS data, but you never know, on small disks such as this image is, it is a possibility.

The good news for those who don’t want to dive that deep (respect, @jaclaz!) is that an upgrade of an already installed CHR apparently doesn’t affect the boot partition. So installing an image of 7.14.3 that has been made acceptable for UEFI using the script (whichever part of it is the actual reason that it works) and then upgrading to 7.15.3 is an easy, field-proven way to work around the 1-sector overlap.

Good to know.
I believe that also using the 7.14.3 image as base and replacing/resyncing the files on the two partitions with the 7.15.3 ones should work.

Is there a free environment where the image bootability can be tested?
I made a few tests and the image for 7.15.3 should be fixable through a slightly more complex set of gdisk commands (without needing other more complex approaches like a Hex editor), basically the two partition entries in the MBR are correct (in their LBA part), so it should be possible to use gdisk to translate from the MBR to the GPT, then it is just a matter of correcting the partition type and flags of the first GPT partition and (to be picky/strict) rename the two partitions to their original RouterOS Boot and RouterOS labels.
But the result would need to be tested, there might still be the need of changing manually a few bytes or - maybe easier for scripting - adding a few dd commands.

It depends on the local meaning of the symbolic address “there” :smiley:

On Proxmox, you can choose between BIOS and UEFI boot.

According to the OP, a “Gen2” machine in Hyper V means that UEFI boot is used; strictly speaking Hyper-V is not free, but if you already have it, there is no additional cost associated with testing the image.

Similarly, if you create a VPS in a paid environment for a short enough time to just deploy the machine and test whether it boots, if will not be “free” but it will likely cost less than the coffee you’ll power yourself up with during the process.

I can offer testing the images on both Proxmox and Hyper-V and, once that proves successful, even in the paid environment, but I suspect the logistics might be a bit complicated.

If you have the time/will to test (before the images), the actual gdisk script on a Linux machine, it’s fine, no need to exchange large files.

I am attaching a small Excel spreadsheet with the Original gdisk script, a modified one (with the end result that should be the same as the original script) and a second modified one (that should IMHO be more “correct”, as there are a few things that the original script does not do strictly speaking “right”, but that shouldn’t prevent the bootability).

The backup partition table of a GPT disk should be in theory at the end, not right after the last partition, the 1st script moves it one sector forward and then it places it back where it was (and where it is also on the 7.14.3 image), the 2nd one places it at the end of the disk.

Personally I would move the gdisk script part earlier in the whole script, before the making of the FAT filesystem, i.e. between the
qemu-nbd -c /dev/nbd0 chr.qcow2
and the
rm -rf /tmp/tmp*
as I cannot say if when mounting the qemu-nbd will use the MBR or the GPT, if it uses the first there should be not any difference, if it uses the second the newly created FAT filesystem might have 1 excess sector.

To be even more on the safer side, one could unmount and re-mount the nbd0, to make sure that when the mkfs/rsync happens the mounted image is already corrected.

On one hand (if the re-partitioning from MBR data script works) we are lucky that the good Mikrotik guys made those MBR entries, on the other hand, that image won’t ever boot in BIOS as there is not any bootsector/loader, so it makes no sense to have not just a protective MBR EE partition entry for the whole disk, let alone the MBR boot code.

So, I added a third script, that gets rid of the hybrid setup, which is simply not needed (unless there is some UEFI that uses MBR partitions, which is very, very unlikely)

Real purists may want to 00 out the first 180 bytes of the MBR.
gdiskscript.xls (18.5 KB)

In this context, I cannot see any benefit in testing 50 incremental scenarios just to find the one that makes it work with the minimum number of changes. The amount of CPU that has to be spent on a Linux machine to make the resulting layout “the most correct one” is not a limiting factor here. So if it is scenario #3 that creates such a “most correct” layout, let’s use that one alone. So please post one script as a plain text, I’ll feed it with the 7.15.3 image and we’ll see the outcome.

Ah. OK.

In any case the three different scripts may come useful in case there is the need to experiment, should the chosen one fail (for this or that reason).

The script that I believe creates the most correct output is this one (the one called "MODIFIED SCRIPT3 (pure EFI/GPT, NO Hybrid)
" in the spreadsheet):

(
echo 2 # use GPT
echo x # extra functionality
echo e # relocate backup data structures to the end of the disk
echo r # Recovery/transformation
echo f # load MBR and build fresh GPT from it
echo y # Warning! This will destroy the currently defined partitions! Proceed? (Y/N):
echo x # extra functionality
echo a # set attributes
echo 1 #  Partition number (1-2):
echo 2 # Toggle which attribute field (0-63, 64 or <Enter> to exit):
echo   # Toggle which attribute field (0-63, 64 or <Enter> to exit):
echo m # return to main menu
echo t # change partition code
echo 1 # select first partition
echo EF00 # Hex code or GUID (L to show codes, Enter = EF00):
echo c # change a partition's name
echo 1 #  Partition number (1-2):
echo RouterOS Boot # Enter name:
echo c # change a partition's name
echo 2 #  Partition number (1-2):
echo RouterOS # Enter name:
echo w # write changes to disk
echo y # confirm
) | gdisk /dev/nbd0

Features:

  1. single 0xEE protective entry in the MBR spanning the whole disk
  2. backup partition table at the end of the disk
  3. corrected partition sizes
  4. partition names and flags made the same as the original