schierlm/usb-modboot

In MBR mode on 64-bit system, $grub_cpu is i386 not x84_64

steve6375 opened this issue · 4 comments

grub_cpu returns "i386" on a 64-bit system, I suggest setting up some variables for the user to use in his .cfg or .inc files to make it easier, e.g.

# DEFINE VARIABLES FOR MBR\EFI and 32/64-bit
set BIT64=false; set BIT32=false; set MBR=false; set EFI=false; set EFI64=false; set EFI32=false; set MBR32=false; set MBR64=false
if cpuid -l; then set BIT64=true; else set BIT32=true; fi
if [ "${grub_platform}" == "pc" ]; then set MBR=true; fi
if [ "${grub_platform}" == "efi" ]; then set EFI=true; fi
if [ "${grub_cpu}" == "x86_64" and $EFI = true ]; then set EFI64=true; fi
if [ "${grub_cpu}" == "i386" and $EFI = true ]; then set EFI32=true; fi
if [ $BIT64 = true and $MBR = true ]; then set MBR64=true; fi
if [ $BIT32 = true and $MBR = true ]; then set MBR32=true; fi
export MBR EFI MBR32 MBR64 EFI32 EFI64 BIT32 BIT64
#Examples
#if [ $BIT64 = true and $MBR = true ] ; then echo 64-bit MBR ; fi
#if [ $BIT64 = true and $EFI = true ] ; then echo 64-bit EFI ; fi
#if $BIT64 ; then echo 64-bit ; fi
#if $BIT32 ; then echo 32-bit ; fi

ISO files can be placed in a sub-folder and then .inc menus can be added such as

	if [ -f "/usb-modboot/ISO/ubuntu-18.04-desktop-amd64.iso" -a $BIT64 ]; then
			menuentry "Ubuntu amd64" "ubuntu.iso" {
				set iso_path="/usb-modboot/ISO/ubuntu-18.04-desktop-amd64.iso"
				export iso_path
				loopback loopdev_cfg "${iso_path}"
				set orig_root="$root"
				set root=(loopdev_cfg)
				configfile /boot/grub/loopback.cfg
				loopback -d loopdev_cfg
				set root="$orig_root"
				terminal_output $terminal
			}
	fi



I agree that it is useful to have a variable that captures whether the cpu is 64-bit capable, since it is impossible in GRUB to check both command output and variables in one if statement . Something like:

if cpuid -l; then set SUPPORTS_64_BIT=1; fi

I have already defined a variable $FIRMWARE_NAME which is either "BIOS" or "UEFI" and can therefore be used to decide between BIOS and EFI (just like using grub_platform).

However, I am not sure whether the other variables are actually beneficial or if they only confuse and use up variable space that might be needed better for advanced scripting in your include files.

First of all, your 4 variables EFI32 EFI64 MBR32 MBR64 suggest that there are actually only four environments somebody writing scripts may care about. But actually there are five. Among Windows 10 tablets and Chromebooks there are many devices with 64-bit capable Atom CPUs which ship with a 32-bit EFI environment. On those environments, it is possible to boot Windows 64-bit (if you use 32-bit bootmgr.efi), and it is also possible to boot recent 64-bit Linux kernels (if you use linux loader and not linuxefi loader). Therefore, I think that the decision between 64-bit EFI ("$grub_cpu" = "x86_64") and 64-bit processor support (cpuid -l) should not be mixed into one variable.

Second, GRUB's scripting does not really support boolean values. In your example,

if [ -f "/usb-modboot/ISO/ubuntu-18.04-desktop-amd64.iso" -a $BIT64 ]; then

will also be entered on 32-bit system, since "false" as a string is considered to be true (only empty string is false). You'd have to write

if [ -f "/usb-modboot/ISO/ubuntu-18.04-desktop-amd64.iso" -a "$BIT64" = "true" ]; then

which makes me try to avoid having variables that have string values that look like boolean values true and false. I usually prefer either descriptive values like "BIOS" and "UEFI", or using 1 vs empty string (so you can use -z and -n flags to decide).

That being said, you can of course define your own variables in your scripts and use them afterwards.

And if you believe there are stronger arguments for including those variables that I missed, feel free to comment again :)

sorry about the bad code of -a - I should have tested it first!..

With my variables, BIT64=true means a 64-bit capable CPU. EFI64 means it booted using a 64-bit UEFI BIOS.
I actually use, e.g.

if $BIT64; then 

(menuentry here)

fi

in my code for menuentries which works and makes it really easy.

For instance, enclosing a menuentry with if $BIT32 is useful so that it does not display 64-bit ISOs in the menu on a 32-bit CPU system.
In the case of a 64-bit Atom CPU which has a UEFI32 BIOS, it can run a 32-bit ISO if $EFI64 is false (or $MBR and $EFI32 is true), so 32-bit ISOs which can MBR and UEFI-boot should test for if ! $EFI64. So both $BIT64 and $EFI64 are useful.
Some ISOs do not support UEFI, so $MBR32 and $MBR64 are useful (you could have both a 64-bit linux ISO and the same linux version in 32-bit form but only want to have one listed in the menu depending on your CPU type).
ISOs which do not support UEFI-booting but do support booting on 32-bit and 64-bit CPUs can use $MBR. Same for $MBR64 or $MBR32 where the ISO only support that type of system.
Knoppix ISOs work for MBR32, MBR64 and EFI64 but not EFI32, so using ! $EFI32 is also useful.

I don't know what the environment space is in your grub2 build, so it is up to you what variables you define. These are just suggestions which I have found very useful when making menuentries and where I want to limit the number of entries in a grub menu.

I think if you used .inc files for some of your other modules, e.g. netboot, grml, etc. with a nice menuentry title and relevant 'if' statements, then you could just include all modules in a single download and the user would see only the menu entries in the main menu if the payload file was present.

Also, it might be helpful to include a sample_menu.ini file and a sample_menu.module.inc file which demonstrated to the user how to add menus and also could list what pre-defined environment variables were available to use?

These are just suggestions I am throwing at you to make it easier to use and more user friendly, please feel free to ignore them if you wish, I am just trying to be helpful :-)

P.S. It would be nice to have sub-menus, e.g. 'addfolder xxxx', in menu.ini to add a new menu entry in the first menu which would take you to a xxxx menu which lists all modules in the \usb-modboot\xxxx folder ?

sorry about the bad code of -a - I should have tested it first!..

No problem - it was just a good way to me to illustrate that it is a bad idea to have boolean variables in GRUB.

With my variables, BIT64=true means a 64-bit capable CPU. EFI64 means it booted using a 64-bit UEFI BIOS.

Yes, but EFI64 refers to the "bitness" of the firmware, while MBR64 refers to the "bitness" of the CPU. For consistency, it should be MBR16 only (since MBR firmware is always 16-bit) :-)

I actually use, e.g.

if $BIT64; then 

(menuentry here)

fi

in my code for menuentries which works and makes it really easy.

Yes, that form works, since it will call the "true" and "false" commands. But since GRUB does not support && or || in commands, any attempt to combine two such variables will fail.

For instance, enclosing a menuentry with if $BIT32 is useful so that it does not display 64-bit ISOs in the menu on a 32-bit CPU system.

But if ! cpuid -l works equally well...

In the case of a 64-bit Atom CPU which has a UEFI32 BIOS, it can run a 32-bit ISO if $EFI64 is false (or $MBR and $EFI32 is true), so 32-bit ISOs which can MBR and UEFI-boot should test for if ! $EFI64. So both $BIT64 and $EFI64 are useful.

However, in case of an ISO that supports both MBR and EFI32 boot I would not test for ! $EFI64, since that (again) assumes that the list of options is exhaustive. If later I add support for 64-bit Coreboot (for example) to USB-ModBoot, the module would break (assuming it cannot boot on coreboot either). Therefore, I'd rather use something like if ["$MBR" = "true" -o "$EFI32" = "true" ] instead of using a variable that just happens to work correctly "by accident".

Some ISOs do not support UEFI, so $MBR32 and $MBR64 are useful (you could have both a 64-bit linux ISO and the same linux version in 32-bit form but only want to have one listed in the menu depending on your CPU type).

I would want to have both listed when booting on a 64-bit cpu, since when my machine has only 2 GB or less of RAM, I'd often prefer booting the 32-bit version over the 64-bit one.

ISOs which do not support UEFI-booting but do support booting on 32-bit and 64-bit CPUs can use $MBR. Same for $MBR64 or $MBR32 where the ISO only support that type of system.

Out of curiosity: Did you ever encounter an ISO (or other boot image) that works in MBR mode on a 32-bit CPU but not on a 64-bit CPU? The only reason I could think of is licensing, since 64-bit CPUs are supposed to be 100% backwards compatible (even down to the 16-bit 8086 CPU).

I think if you used .inc files for some of your other modules, e.g. netboot, grml, etc. with a nice menuentry title and relevant 'if' statements, then you could just include all modules in a single download and the user would see only the menu entries in the main menu if the payload file was present.

Then I could also include the logic for all "modules" into the main config file. It would be equally slow, but with less cluttered files on the filesystem. Or I could have stayed with the predecessor of usb-modboot (which is still on GitHub and used such a static system). Or I could have used any of the other static systems made by other people (like you :D) where I can also add scripts myself, but should be aware that whenever I try to upgrade the main system, some of my scripts will break.

In other words, my modules should only be examples, not in any way exhaustive (they are just what I needed for my own needs). On the other hand, the defined variables and functions should (barring fixed bugs in GRUB itself) be backwards compatible so if you write your own modules, you should be able to extract a newer version of USB-ModBoot on top of them and everything still works.

(There are also two modules that are dynamic on the name of the module file, so you can add them more than once for multiple "payload" ISOs).

Also, it might be helpful to include a sample_menu.ini file and a sample_menu.module.inc file which demonstrated to the user how to add menus and also could list what pre-defined environment variables were available to use?

Yes, documentation can surely be improved :-)

These are just suggestions I am throwing at you to make it easier to use and more user friendly, please feel free to ignore them if you wish, I am just trying to be helpful :-)

Yeah, I understand that. But I want to understand the rationale of some of those suggestions, too, so I tend to ask back. (Feel free to ignore my questions if you don't think that your answers might change my opinion :D)

P.S. It would be nice to have sub-menus, e.g. 'addfolder xxxx', in menu.ini to add a new menu entry in the first menu which would take you to a xxxx menu which lists all modules in the \usb-modboot\xxxx folder ?

Yes, submenus is a good idea. I'd rather make it automatic, i.e. if a directory contains no grub.cfg and no grub.inc, it becomes a submenu with its contents. You can still use addmodule to add such a directory to the menu manually, but you can also just have a directory structure and menus will come to life automatically.

I'll have to refactor my script a bit to do that (in particular, pass a path to the menuentries as third parameter).

core-module.dat

Documentation improvements still pending.