Thursday 5 January 2023

Run grub4dos commands from the Ventoy (or any grub2) menu system

Ventoy is based on grub2 and although it is very good at booting most legacy payloads, it is not that good at booting some DOS floppy images and some other legacy BIOS payloads.

If you need to boot using grub4dos, you can define your own grub2 external user menu (\ventoy\ventoy_grub.cfg) which can be run from Ventoy by pressing F6 in the Ventoy menu system.

My eBook 'Getting started with Ventoy' gives a few examples of how to do this.

You can run grub.exe (the grub4dos DOS/grub2 executable) from Ventoy (grub2) using a menu entry such as:

menuentry "Test A B\t.iso" --class=custom {
set cmd="set I=/ISO/t.iso; find --set-root %I%; map %I% (0xff);map --hook;chainloader (0xff);boot"
linux16 (hd0,1)/grub.exe --config-file=${cmd};

The --config-file can be a string of commands (as in this case) or a  grub4dos menu.lst file.

There are some issues however when calling grub4dos from grub2. 

grub2 can use device names of (msdos0,1) or (hd0,1) for the first legacy disk and partition, but grub4dos expects the partition number to start from 0, so we must convert this to (hd0,0) for grub4dos.

Also, grub2 can use path names and filenames which contain spaces quite happily, but grub4dos requires that each space is preceded by a \ symbol, so a path of /A B/t.iso becomes /A\ B/t.iso.

This may be a problem where you need to use a path with a specific device and partition and the filename contains spaces.

We can overcome this by running a grub4dos batch file which will convert the grub2-compatible filename into a grub4dos compatible filename like this:

menuentry "Test iso with spaces \A B\t.iso" --class=custom {
set "ISO=(msdos0,1)/A B/t.iso"
set cmd="set P=${ISO}; call /grubpath.g4b; find --set-root %P%; map %P% (0xff); map --hook; chainloader (0xff); boot"
linux16 (hd0,1)/grub.exe --config-file=${cmd};

The path is held in the %P% grub4dos variable and converted into the correct format for grub4dos. The device name part is optional.
A device name, e.g. (msdos0,1) (if present), will be converted to (hd0,0) and the pathname will be converted to (hd0,0)/A\ B/t.iso by the grubpath.g4b batch file.

Example showing path conversion before booting a iso

The grub4dos conversion file, grubpath.g4b is shown below:

# Usage:  call grubpath.g4b
# Convert grub2 path in variable P to a valid grub4dos path (add \ before all spaces) - decrement partition number by 1 - convert (msdosx,y) to (hdx,y-1)
# Result in %P%

# Example use in a grub2 menuentry which runs grub.exe - assume this file and grub.exe are in root of drive
#menuentry "Test iso with spaces /A B/t.iso" --class=custom {
#set "ISO=(hd0,1)/A B/t.iso"
#set cmd="set P=${ISO}; call /grubpath.g4b; find --set-root %P%; map %P% (0xff); map --hook; chainloader (0xff); boot"
#linux16 (hd0,1)/grub.exe --config-file=${cmd};

debug on
errorcheck on
echo P=%P%
if "%P%"=="" pause Variable P is not defined && goto :EOF
# Add \ before all spaces
set ST=0
echo -n %P% > (md)0x226+1
cat --locate=\x20 --skip=%ST% --number=1 (md)0x226+1 > nul || goto :FINx
set /a L=%?% > nul
call set P1=%^P:~0,%L%%%
set /a ST=%L%+2 > nul
set /a L=%L%+1 > nul
call set "P2=%^P:~%L%,255%" > nul
call set "P=%^P1%\ %^P2%" > nul
goto :loop
# finish if does not have device name
if not "%P:~0,1%"=="(" goto :fend

#convert to (hdx,y) - result (hd9,8)
echo -n %P% > (md)0x226+1
if "%P:~0,6%"=="(msdos" set d=6
if "%P:~0,3%"=="(hd" set d=3
if "%d%"=="" goto :fend
cat --number=1 --locatei=, (md)0x226+1 > nul || goto :fend
set /a p=%?%+1 > nul
set /a dl=%p%-%d%-1 > nul
cat --number=1 --locatei=) (md)0x226+1 > nul || goto :fend
set /a pl=%?% > nul
set p3s=%pl%
set /a pl=%pl%-%p% > nul
set P1= ;; set P2= ;; set P3=
call set P1=%^P:~%d%,%dl%%%
call set P2=%^P:~%p%,%pl%%%
# grub2 partition number cannot be 0
if "%P2%"=="0" goto :fend
set /a P2=%P2%-1 > nul
call set "P3=%^P:~%p3s%,255%" > nul
call set "P=(hd%^P1%,%^P2%%%^P3%" > nul

endlocal && set P=%P%
pause --wait=2 P=%P%

The latest Beta of EasyBoot also contains this file in the /_ISO/e2b/grub/ folder.

The batch file turns debug on, so use the command 'debug off' in the cmd string after it runs the batch file if you want to suppress some of the grub4dos messages...

set cmd="set P=${ISO}; call /grubpath.g4b; debug off; clear; map %P% (0xff); map --hook; chainloader (0xff); boot"

No comments:

Post a Comment