Friday, 13 May 2016

How To's and Gotchas in grub2 menus

These are my notes below, which may be of interest to those of you writing grub2 (or even grub4dos) menus.

Most of these are concerned with directly booting from a linux ISO file using 'cheat codes'.

If that does not interest you, then don't read any more.

Note: I have been learning and experimenting with grub2 for a week now, so I am by no means an expert!


You can look at the Easy2Boot UEFI_GRUB2_PTN2 project download and study the grub2 menu files (.cfg and .grub2) if you want to find specific linux iso-boot examples.

As I come across more tidbits, I will add them to these notes!

grub2 manual
How to write grub2 modules

Replacing a CDLABEL cheat code

You may find a kernel/linux command line which includes a CDLABEL reference - e.g. Network Security Toolkit uses root=CDLABEL=nst-22-7334.i686 amongst it kernel parameters. This is the label of the CD or ISO file that you are booting to.

However, if you want to be able to boot a slightly different version, it means you have to make another menu and change the text to match the label of the new ISO every time. This is one reason why many multiboot solutions don't work if you use a slightly different version of the ISO. For the Network Security Toolkit, I got round it by dynamically finding the UUID of the ISO volume like this:

menuentry "Network Security Toolkit live ISO GUI" --unrestricted --class nts {
   set isoname=nst.iso ; CHECK_MNU_FOLDER
   set root=$root2
   loopback loop $isofile
   probe -u (loop) --set=loopuuid
   set id=UUID=$loopuuid
   #set graphics mode for UEFI boot or else no display!
   set gfxpayload=1024x768,800x600
   linux (loop)/isolinux/vmlinuz0 iso-scan/filename=$isofile root=UUID=$loopuuid           rootfstype=auto ro rd.live.image rd.luks=0 rd.md=0 rd.dm=0 audit=0 systemd.unit=graphical.target nstrelocate=false
   initrd (loop)/isolinux/initrd0.img
   boot
}

Booting via UEFI

One of the great advantages of grub2 over grub4dos is that it can be booted via UEFI.

Once booted to grub2 from UEFI, we can often boot from ISOs that contain no UEFI boot files. This is because we have used grub2 as a boot manager and we can load the linux kernel directly from grub2. If the linux kernel code is written in a 'nice' way, it will not use any BIOS calls because the kernel and initial ramdrive (initrd) have already been loaded by grub2. Thus it does not need to use any UEFI or BIOS calls before 'switching' to it's own kernel and hardware drivers.

I tried this with many linux ISOs and often found they would successfully UEFI-boot when they were not supposed to (i.e. the ISO did not contain any UEFI boot files)!

However, a few payloads, such as Network Security Toolkit, Parrot, Tails, ophcrack, systemrescuecd, etc. needed the display to be set to a graphicsmode before booting (see example code above).

Some options are:
set gfxpayload=1024x768,800x600     - before booting, set graphics mode to first available mode
set gfxpayload=1024x768x32              - use 32-bit colour 1024x768
set gfxpayload=keep                            - keep current graphics mode (resolution)
set gfxpayload=text                              - before booting switch to text mode display

The graphics switch takes place after the grub2 boot command (may be implicit) has been executed.

A typical symptom if you don't use the gfxpayload setting when UEFI-booting, was that there would be no linux boot text messages and no linux Desktop - just the last screen left by grub2!

The AVG Rescue ISO seemed to need  gfxpayload=keep for UEFI-booting and gfxpayload=text for Legacy\MBR booting.

Using the 'boot' command

In grub4dos and grub2, after a list of menu commands, the 'boot' command is implicit and does not need to be specified, EXCEPT:...

I found that some grub2 menus displayed a 'Press any key to continue...' prompt before booting. I still do not know why this happens (perhaps an error in one of the menu command lines?), but I found that if you added 'boot' to the end of the menu, then you no longer got this annoying prompt!

Scrolling/Paging and special variables used by grub2

If you use 'set pager=0' then this will turn paging off, which means you don't get the annoying 'MORE' prompts on occasion (which are not always at the bottom of the screen!). It is supposed to be off by default anyway. Other useful grub2 special variables are debug and timeout.

The prefix and root variables

The $root variable value has no parentheses surrounding it (e.g. it is in the form hd0,msdos1), but device names need to be in the form of (hd0,msdos1), i.e. enclosed by parentheses. So when using this variable, don't forget to add the parentheses!

The prefix variable is used by grub to find it's  'working' directory where it can find and load any .mod modules that it needs. There are typically three (x86) module folders, i386-efi, i386-pc and x86_64-efi which are located under \boot\grub.

For instance, if you are in UEFI-64-bit mode and issue the command  ls / , then grub2 will need to load the ls.mod module from the x86_64-efi folder before it can execute the ls command. Alternatively, you can pre-load the ls.mod module by using the insmod ls command.

Now, typically, the first line in the grub.cfg file (which is the first file executed by grub2) is:

set prefix=/boot/grub

This seems to me to be incorrect because consider this sequence of commands:

set prefix=/boot/grub
set root=(hd0,msdos2)
ls /

This will cause an error:
error: file '/boot/grub/i386-pc/ls/mod' not found.

because grub2 is looking on hd0,msdos2 for the module file!

So to fix this and make it far more robust, the correct value for prefix should be set using:

set prefix=($root)/boot/grub

Look for valid 'cheat codes' inside the ISO

Often you need to specify a special cheat code to make the kernel find and load the ISO file as a CD filesystem. You must tell it the path and name of the ISO file and it will typically search all devices for it (though in some cases you must specify the device by using it's volume label or UUID or device name).

For more info on why this cheat code is needed and some examples of many different ones, e.g. iso-scan/filename, fromiso, isofrom, findiso, etc. (all meaning the same thing!) used by different linux distros, see my previous blog 'Why is it so difficult to boot ISO files from a USB drive?'

Often, you can find out what cheat codes are being looked for, by looking at the linux shell scripts inside the initrd file. Here is what I do:

1. I use 7Zip to view the ISO contents
2. Double-click on the initrd file inside 7Zip (the file name may vary depending on distro) it will usually be mentioned in the isolinux.cfg file or grub.cfg file)
3. If the initrd file was compressed, you may need to repeat step 2 again
4. Finally, you should see some files and folders. Extract all these to a temporary folder on your hard disk - e.g. C:\temp\initrd.
5. Now I use a find tool (e.g. Find&Replace fnr.exe) to find all occurrences of a word (e.g. 'iso') in all the files I just extracted. Typically you will find it in a file like \casper\casper or casper\preload. If not look for some other string like 'cmdline' which is the variable used for the commandline string.
6. Once I have found the files which look like the relevant scripts, you can dissect them to see what cheat codes they are looking for. See my previous blog on Dr.Web for an example.

grub4dos 'map' command equivalent

When you boot from a CD or floppy disk, the internal hard disk will be BIOS disk 0, however when you boot from a USB drive, the USB drive is BIOS disk 0 and the internal hard disk will be BIOS disk 1.

So if you boot from a USB drive, then load a floppy disk or ISO image (for instance), and then boot to it, any code which uses the BIOS will expect BIOS disk 0 to be the internal hard disk.

In grub4dos, we can use the map command to swap the BIOS disks over - e.g.

map (hd0) (hd1)
map (hd1) (hd0)
map --hook

So for the KonBoot floppy disk image, we need to use this type of grub2 menu:

menuentry "Konboot (BIOS)"  {
insmod linux16
drivemap -s (hd0) (hd1)
linux16 /boot/grub/memdisk floppy
initrd16 /konboot.img
boot
}

where drivemap -s = swap over

Error: 'Please provide a name for this Disk, such as 'Debian 5.0.3 Disk 1'

Linux is looking for the \.disk folder (usually a hidden folder). You may need to copy it to the root of the drive.

It can be made to continue by typing ALT+F1 - enter some random text e.g. "xxx" - press the [ENTER] key and it should continue to boot (press ALT+F7 to return to message console if required).


Booting via grub4dos

Sometimes it is easier to run grub4dos if grub2 lacks some function or other, or if the payload contains a grub4dos menu system, we can launch grub4dos and then run the payload's own grub4dos menu .cfg file:
# Hirens extracted to a FAT32 partition
menuentry "HIRENS TOOLS" {  
linux16 /grub.exe —config-file="find —set-root /HBCD/menu.lst; configfile /HBCD/menu.lst" 
}
grub.exe can be obtained from a 'good' grub4dos download package (use the same version date that E2B uses for \grldr) - e.g. grub4dos 2016-04-26.

Testing under a VM (e.g. VBox)

Even though 99% of linux ISOs work when tested under a VM, there is always the odd one (like Avira Rescue System ISO) that doesn't! So if it looks like it should work, but hangs under a VM, try a real system.


P.S. If I have got any of this wrong or slightly inaccurate, please let me know, as I said, I am a newbie in grub2!

Scripting (notes)

Useful scripting 'test' examples here

echo $"Press escape to "
sleep --interruptible 9       - waits 9 secs or press ESC

#get device part of a path
insmod regexp
regexp -s "dev_name" '^\((.*)\).*$' "$prefix"
rmmod regexp

#get drive number from $root
regexp -s devnum 'hd([0-9]+)' $root

  # Set pager=1 so ls output doesn't scroll past the top of the screen
  # but restore $pager to its previous value when finished
  set oldpager="${pager}"
  set pager=1

#useful instr function
function strcontains {
set str="$1"
set pattern="$2"
if regexp ".*${pattern}.*" "$str"; then
return 0;
else
return 1;
fi
}


You dont always need to specify an initramfs initrd line - e.g.
menuentry 'Fedora Linux, no initramfs' { set root='hd0,msdos1' linux /vmlinuz-3.3.4-5.fc17.i686.PAE rootfstype=ext4 ,fat,ntfs root=/dev/sda2 rd.md=0 rd.lvm=0 rd.dm=0 SYSFONT=True KEYTABLE=us rd.luks=0 LANG=en_US.UTF-8 }

Read more: http://blog.fpmurphy.com/2013/08/boot-linux-without-an-initramfs-2.html#ixzz4JsKckwf4



lsmod - list installed modules

set debug=all    - full debug - use just before linux and initrd lines

grub2 multiboot system

Useful scripting examples can be found in this project:
http://www.sitecuatui.com/aio-boot-v0-9/

I found many bugs and menus that did not work with many payloads however.


Secure Boot and shimx64.efi

You can secure boot to grub2 by  renaming shimx64.efi to bootx64.efi and  the grub2 bootx64.efi to grubx64.efi.

\EFI\boot\bootx64.efi   (shimx64.efi)
\EFI\boot\grubx64.efi   (grub2 efi file)

However, when you secure boot to grub2, many modules (xxx.mod) will not load (for good security). You can boot to .efi files but you cannot use loopback or chainloader, etc. Also regular expressions module, ls, some filesystem search modules, etc. won't load because they are not signed/allowed. Also, you cannot access a different partition other than the boot partition.

This means that the multiboot functionality of grub2 is crippled when you secure-boot.


The method used by E2B is far more robust because it simply 'switches in' an EFI-bootable partition.

See also this grub2 tutorial here.