1. change some directory structure for the build script

2. add build script and document
   see DOC/BuildVentoyFromSource.txt for detail
This commit is contained in:
longpanda
2020-05-20 22:36:27 +08:00
parent 965417970b
commit 2aae096c2a
97 changed files with 6298 additions and 67 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,545 @@
/* -*-Asm-*- */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001,2002,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/symbol.h>
#include <grub/machine/boot.h>
/*
* defines for the code go here
*/
/* Print message string */
#define MSG(x) movw $x, %si; call LOCAL(message)
#define ERR(x) movw $x, %si; jmp LOCAL(error_message)
.macro floppy
part_start:
LOCAL(probe_values):
.byte 36, 18, 15, 9, 0
LOCAL(floppy_probe):
pushw %dx
/*
* Perform floppy probe.
*/
#ifdef __APPLE__
LOCAL(probe_values_minus_one) = LOCAL(probe_values) - 1
movw MACRO_DOLLAR(LOCAL(probe_values_minus_one)), %si
#else
movw MACRO_DOLLAR(LOCAL(probe_values)) - 1, %si
#endif
LOCAL(probe_loop):
/* reset floppy controller INT 13h AH=0 */
xorw %ax, %ax
int MACRO_DOLLAR(0x13)
incw %si
movb (%si), %cl
/* if number of sectors is 0, display error and die */
testb %cl, %cl
jnz 1f
/*
* Floppy disk probe failure.
*/
MSG(fd_probe_error_string)
jmp LOCAL(general_error)
/* "Floppy" */
fd_probe_error_string: .asciz "Floppy"
1:
/* perform read */
movw MACRO_DOLLAR(GRUB_BOOT_MACHINE_BUFFER_SEG), %bx
movw %bx, %es
xorw %bx, %bx
movw MACRO_DOLLAR(0x201), %ax
movb MACRO_DOLLAR(0), %ch
movb MACRO_DOLLAR(0), %dh
int MACRO_DOLLAR(0x13)
/* if error, jump to "LOCAL(probe_loop)" */
jc LOCAL(probe_loop)
/* %cl is already the correct value! */
movb MACRO_DOLLAR(1), %dh
movb MACRO_DOLLAR(79), %ch
jmp LOCAL(final_init)
.endm
.macro scratch
/* scratch space */
mode:
.byte 0
disk_address_packet:
sectors:
.long 0
heads:
.long 0
cylinders:
.word 0
sector_start:
.byte 0
head_start:
.byte 0
cylinder_start:
.word 0
/* more space... */
.endm
.file "boot.S"
.text
/* Tell GAS to generate 16-bit instructions so that this code works
in real mode. */
.code16
.globl _start, start;
_start:
start:
/*
* _start is loaded at 0x7c00 and is jumped to with CS:IP 0:0x7c00
*/
/*
* Beginning of the sector is compatible with the FAT/HPFS BIOS
* parameter block.
*/
jmp LOCAL(after_BPB)
nop /* do I care about this ??? */
#ifdef HYBRID_BOOT
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
jmp LOCAL(after_BPB)
#else
/*
* This space is for the BIOS parameter block!!!! Don't change
* the first jump, nor start the code anywhere but right after
* this area.
*/
.org GRUB_BOOT_MACHINE_BPB_START
.org 4
#endif
#ifdef HYBRID_BOOT
floppy
#else
scratch
#endif
.org GRUB_BOOT_MACHINE_BPB_END
/*
* End of BIOS parameter block.
*/
LOCAL(kernel_address):
.word GRUB_BOOT_MACHINE_KERNEL_ADDR
#ifndef HYBRID_BOOT
.org GRUB_BOOT_MACHINE_KERNEL_SECTOR
LOCAL(kernel_sector):
.long 1
LOCAL(kernel_sector_high):
.long 0
#endif
.org GRUB_BOOT_MACHINE_BOOT_DRIVE
boot_drive:
.byte 0xff /* the disk to load kernel from */
/* 0xff means use the boot drive */
LOCAL(after_BPB):
/* general setup */
cli /* we're not safe here! */
/*
* This is a workaround for buggy BIOSes which don't pass boot
* drive correctly. If GRUB is installed into a HDD, check if
* DL is masked correctly. If not, assume that the BIOS passed
* a bogus value and set DL to 0x80, since this is the only
* possible boot drive. If GRUB is installed into a floppy,
* this does nothing (only jump).
*/
.org GRUB_BOOT_MACHINE_DRIVE_CHECK
boot_drive_check:
jmp 3f /* grub-setup may overwrite this jump */
testb $0x80, %dl
jz 2f
3:
/* Ignore %dl different from 0-0x0f and 0x80-0x8f. */
testb $0x70, %dl
jz 1f
2:
movb $0x80, %dl
1:
/*
* ljmp to the next instruction because some bogus BIOSes
* jump to 07C0:0000 instead of 0000:7C00.
*/
ljmp $0, $real_start
real_start:
/* set up %ds and %ss as offset from 0 */
xorw %ax, %ax
movw %ax, %ds
movw %ax, %ss
/* set up the REAL stack */
movw $GRUB_BOOT_MACHINE_STACK_SEG, %sp
sti /* we're safe again */
/*
* Check if we have a forced disk reference here
*/
movb boot_drive, %al
cmpb $0xff, %al
je 1f
movb %al, %dl
1:
/* save drive reference first thing! */
pushw %dx
/* print a notification message on the screen */
MSG(notification_string)
/* set %si to the disk address packet */
movw $disk_address_packet, %si
/* check if LBA is supported */
movb $0x41, %ah
movw $0x55aa, %bx
int $0x13
/*
* %dl may have been clobbered by INT 13, AH=41H.
* This happens, for example, with AST BIOS 1.04.
*/
popw %dx
pushw %dx
/* use CHS if fails */
jc LOCAL(chs_mode)
cmpw $0xaa55, %bx
jne LOCAL(chs_mode)
andw $1, %cx
jz LOCAL(chs_mode)
LOCAL(lba_mode):
xorw %ax, %ax
movw %ax, 4(%si)
incw %ax
/* set the mode to non-zero */
movb %al, -1(%si)
/* the blocks */
movw %ax, 2(%si)
/* the size and the reserved byte */
movw $0x0010, (%si)
/* the absolute address */
movl LOCAL(kernel_sector), %ebx
movl %ebx, 8(%si)
movl LOCAL(kernel_sector_high), %ebx
movl %ebx, 12(%si)
/* the segment of buffer address */
movw $GRUB_BOOT_MACHINE_BUFFER_SEG, 6(%si)
/*
* BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory
* Call with %ah = 0x42
* %dl = drive number
* %ds:%si = segment:offset of disk address packet
* Return:
* %al = 0x0 on success; err code on failure
*/
movb $0x42, %ah
int $0x13
/* LBA read is not supported, so fallback to CHS. */
jc LOCAL(chs_mode)
movw $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
jmp LOCAL(copy_buffer)
LOCAL(chs_mode):
/*
* Determine the hard disk geometry from the BIOS!
* We do this first, so that LS-120 IDE floppies work correctly.
*/
movb $8, %ah
int $0x13
jnc LOCAL(final_init)
popw %dx
/*
* The call failed, so maybe use the floppy probe instead.
*/
testb %dl, %dl
jnb LOCAL(floppy_probe)
/* Nope, we definitely have a hard disk, and we're screwed. */
ERR(hd_probe_error_string)
LOCAL(final_init):
/* set the mode to zero */
movzbl %dh, %eax
movb %ah, -1(%si)
/* save number of heads */
incw %ax
movl %eax, 4(%si)
movzbw %cl, %dx
shlw $2, %dx
movb %ch, %al
movb %dh, %ah
/* save number of cylinders */
incw %ax
movw %ax, 8(%si)
movzbw %dl, %ax
shrb $2, %al
/* save number of sectors */
movl %eax, (%si)
setup_sectors:
/* load logical sector start (top half) */
movl LOCAL(kernel_sector_high), %eax
orl %eax, %eax
jnz LOCAL(geometry_error)
/* load logical sector start (bottom half) */
movl LOCAL(kernel_sector), %eax
/* zero %edx */
xorl %edx, %edx
/* divide by number of sectors */
divl (%si)
/* save sector start */
movb %dl, %cl
xorw %dx, %dx /* zero %edx */
divl 4(%si) /* divide by number of heads */
/* do we need too many cylinders? */
cmpw 8(%si), %ax
jge LOCAL(geometry_error)
/* normalize sector start (1-based) */
incb %cl
/* low bits of cylinder start */
movb %al, %ch
/* high bits of cylinder start */
xorb %al, %al
shrw $2, %ax
orb %al, %cl
/* save head start */
movb %dl, %al
/* restore %dl */
popw %dx
/* head start */
movb %al, %dh
/*
* BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
* Call with %ah = 0x2
* %al = number of sectors
* %ch = cylinder
* %cl = sector (bits 6-7 are high bits of "cylinder")
* %dh = head
* %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
* %es:%bx = segment:offset of buffer
* Return:
* %al = 0x0 on success; err code on failure
*/
movw $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
movw %bx, %es /* load %es segment with disk buffer */
xorw %bx, %bx /* %bx = 0, put it at 0 in the segment */
movw $0x0201, %ax /* function 2 */
int $0x13
jc LOCAL(read_error)
movw %es, %bx
LOCAL(copy_buffer):
/*
* We need to save %cx and %si because the startup code in
* kernel uses them without initializing them.
*/
pusha
pushw %ds
movw $0x100, %cx
movw %bx, %ds
xorw %si, %si
movw $GRUB_BOOT_MACHINE_KERNEL_ADDR, %di
movw %si, %es
cld
rep
movsw
popw %ds
popa
/* boot kernel */
jmp *(LOCAL(kernel_address))
/* END OF MAIN LOOP */
/*
* BIOS Geometry translation error (past the end of the disk geometry!).
*/
LOCAL(geometry_error):
ERR(geometry_error_string)
/*
* Read error on the disk.
*/
LOCAL(read_error):
movw $read_error_string, %si
LOCAL(error_message):
call LOCAL(message)
LOCAL(general_error):
MSG(general_error_string)
/* go here when you need to stop the machine hard after an error condition */
/* tell the BIOS a boot failure, which may result in no effect */
int $0x18
LOCAL(stop):
jmp LOCAL(stop)
ventoy_uuid: .ascii "XXXXXXXXXXXXXXXX"
notification_string: .asciz "GR"
geometry_error_string: .asciz "Ge"
hd_probe_error_string: .asciz "HD"
read_error_string: .asciz "Rd"
general_error_string: .asciz " Er\r\n"
/*
* message: write the string pointed to by %si
*
* WARNING: trashes %si, %ax, and %bx
*/
/*
* Use BIOS "int 10H Function 0Eh" to write character in teletype mode
* %ah = 0xe %al = character
* %bh = page %bl = foreground color (graphics modes)
*/
1:
movw $0x0001, %bx
movb $0xe, %ah
int $0x10 /* display a byte */
LOCAL(message):
lodsb
cmpb $0, %al
jne 1b /* if not end of string, jmp to display */
ret
/*
* Windows NT breaks compatibility by embedding a magic
* number here.
*/
#ifdef HYBRID_BOOT
.org 0x1b0
LOCAL(kernel_sector):
.long 1
LOCAL(kernel_sector_high):
.long 0
#endif
.org GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC
nt_magic:
.long 0
.word 0
/*
* This is where an MBR would go if on a hard disk. The code
* here isn't even referenced unless we're on a floppy. Kinda
* sneaky, huh?
*/
.org GRUB_BOOT_MACHINE_PART_START
#ifndef HYBRID_BOOT
floppy
#else
scratch
#endif
.org GRUB_BOOT_MACHINE_PART_END
/* the last 2 bytes in the sector 0 contain the signature */
.word GRUB_BOOT_MACHINE_SIGNATURE

View File

@@ -0,0 +1,162 @@
/* blocklist.c - print the block list of a file */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2006,2007 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/dl.h>
#include <grub/misc.h>
#include <grub/file.h>
#include <grub/mm.h>
#include <grub/disk.h>
#include <grub/partition.h>
#include <grub/command.h>
#include <grub/i18n.h>
GRUB_MOD_LICENSE ("GPLv3+");
/* Context for grub_cmd_blocklist. */
struct blocklist_ctx
{
unsigned long start_sector;
unsigned num_sectors;
int num_entries;
grub_disk_addr_t part_start;
};
/* Helper for grub_cmd_blocklist. */
static void
print_blocklist (grub_disk_addr_t sector, unsigned num,
unsigned offset, unsigned length, struct blocklist_ctx *ctx)
{
if (ctx->num_entries++)
grub_printf (",");
grub_printf ("%llu", (unsigned long long) (sector - ctx->part_start));
if (num > 0)
grub_printf ("+%u", num);
if (offset != 0 || length != 0)
grub_printf ("[%u-%u]", offset, offset + length);
}
/* Helper for grub_cmd_blocklist. */
static void
read_blocklist (grub_disk_addr_t sector, unsigned offset, unsigned length,
void *data)
{
struct blocklist_ctx *ctx = data;
if (ctx->num_sectors > 0)
{
if (ctx->start_sector + ctx->num_sectors == sector
&& offset == 0 && length >= GRUB_DISK_SECTOR_SIZE)
{
ctx->num_sectors += length >> GRUB_DISK_SECTOR_BITS;
sector += length >> GRUB_DISK_SECTOR_BITS;
length &= (GRUB_DISK_SECTOR_SIZE - 1);
}
if (!length)
return;
print_blocklist (ctx->start_sector, ctx->num_sectors, 0, 0, ctx);
ctx->num_sectors = 0;
}
if (offset)
{
unsigned l = length + offset;
l &= (GRUB_DISK_SECTOR_SIZE - 1);
l -= offset;
print_blocklist (sector, 0, offset, l, ctx);
length -= l;
sector++;
offset = 0;
}
if (!length)
return;
if (length & (GRUB_DISK_SECTOR_SIZE - 1))
{
if (length >> GRUB_DISK_SECTOR_BITS)
{
print_blocklist (sector, length >> GRUB_DISK_SECTOR_BITS, 0, 0, ctx);
sector += length >> GRUB_DISK_SECTOR_BITS;
}
print_blocklist (sector, 0, 0, length & (GRUB_DISK_SECTOR_SIZE - 1), ctx);
}
else
{
ctx->start_sector = sector;
ctx->num_sectors = length >> GRUB_DISK_SECTOR_BITS;
}
}
static grub_err_t
grub_cmd_blocklist (grub_command_t cmd __attribute__ ((unused)),
int argc, char **args)
{
grub_file_t file;
char buf[GRUB_DISK_SECTOR_SIZE];
struct blocklist_ctx ctx = {
.start_sector = 0,
.num_sectors = 0,
.num_entries = 0,
.part_start = 0
};
if (argc < 1)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
file = grub_file_open (args[0], GRUB_FILE_TYPE_PRINT_BLOCKLIST
| GRUB_FILE_TYPE_NO_DECOMPRESS);
if (! file)
return grub_errno;
if (! file->device->disk)
return grub_error (GRUB_ERR_BAD_DEVICE,
"this command is available only for disk devices");
ctx.part_start = grub_partition_get_start (file->device->disk->partition);
file->read_hook = read_blocklist;
file->read_hook_data = &ctx;
while (grub_file_read (file, buf, sizeof (buf)) > 0)
;
if (ctx.num_sectors > 0)
print_blocklist (ctx.start_sector, ctx.num_sectors, 0, 0, &ctx);
grub_printf("\nentry number:%d \n", ctx.num_entries);
grub_file_close (file);
return grub_errno;
}
static grub_command_t cmd;
GRUB_MOD_INIT(blocklist)
{
cmd = grub_register_command ("blocklist", grub_cmd_blocklist,
N_("FILE"), N_("Print a block list."));
}
GRUB_MOD_FINI(blocklist)
{
grub_unregister_command (cmd);
}

View File

@@ -0,0 +1,764 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/machine/biosdisk.h>
#include <grub/machine/kernel.h>
#include <grub/machine/memory.h>
#include <grub/machine/int.h>
#include <grub/disk.h>
#include <grub/dl.h>
#include <grub/mm.h>
#include <grub/types.h>
#include <grub/misc.h>
#include <grub/err.h>
#include <grub/term.h>
#include <grub/i18n.h>
GRUB_MOD_LICENSE ("GPLv3+");
static int cd_drive = 0;
static int grub_biosdisk_rw_int13_extensions (int ah, int drive, void *dap);
static int grub_biosdisk_get_num_floppies (void)
{
struct grub_bios_int_registers regs;
int drive;
/* reset the disk system first */
regs.eax = 0;
regs.edx = 0;
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
grub_bios_interrupt (0x13, &regs);
for (drive = 0; drive < 2; drive++)
{
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT | GRUB_CPU_INT_FLAGS_CARRY;
regs.edx = drive;
/* call GET DISK TYPE */
regs.eax = 0x1500;
grub_bios_interrupt (0x13, &regs);
if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY)
break;
/* check if this drive exists */
if (!(regs.eax & 0x300))
break;
}
return drive;
}
/*
* Call IBM/MS INT13 Extensions (int 13 %ah=AH) for DRIVE. DAP
* is passed for disk address packet. If an error occurs, return
* non-zero, otherwise zero.
*/
static int
grub_biosdisk_rw_int13_extensions (int ah, int drive, void *dap)
{
struct grub_bios_int_registers regs;
regs.eax = ah << 8;
/* compute the address of disk_address_packet */
regs.ds = (((grub_addr_t) dap) & 0xffff0000) >> 4;
regs.esi = (((grub_addr_t) dap) & 0xffff);
regs.edx = drive;
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
grub_bios_interrupt (0x13, &regs);
return (regs.eax >> 8) & 0xff;
}
/*
* Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write
* NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs,
* return non-zero, otherwise zero.
*/
static int
grub_biosdisk_rw_standard (int ah, int drive, int coff, int hoff,
int soff, int nsec, int segment)
{
int ret, i;
/* Try 3 times. */
for (i = 0; i < 3; i++)
{
struct grub_bios_int_registers regs;
/* set up CHS information */
/* set %ch to low eight bits of cylinder */
regs.ecx = (coff << 8) & 0xff00;
/* set bits 6-7 of %cl to high two bits of cylinder */
regs.ecx |= (coff >> 2) & 0xc0;
/* set bits 0-5 of %cl to sector */
regs.ecx |= soff & 0x3f;
/* set %dh to head and %dl to drive */
regs.edx = (drive & 0xff) | ((hoff << 8) & 0xff00);
/* set %ah to AH */
regs.eax = (ah << 8) & 0xff00;
/* set %al to NSEC */
regs.eax |= nsec & 0xff;
regs.ebx = 0;
regs.es = segment;
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
grub_bios_interrupt (0x13, &regs);
/* check if successful */
if (!(regs.flags & GRUB_CPU_INT_FLAGS_CARRY))
return 0;
/* save return value */
ret = regs.eax >> 8;
/* if fail, reset the disk system */
regs.eax = 0;
regs.edx = (drive & 0xff);
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
grub_bios_interrupt (0x13, &regs);
}
return ret;
}
/*
* Check if LBA is supported for DRIVE. If it is supported, then return
* the major version of extensions, otherwise zero.
*/
static int
grub_biosdisk_check_int13_extensions (int drive)
{
struct grub_bios_int_registers regs;
regs.edx = drive & 0xff;
regs.eax = 0x4100;
regs.ebx = 0x55aa;
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
grub_bios_interrupt (0x13, &regs);
if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY)
return 0;
if ((regs.ebx & 0xffff) != 0xaa55)
return 0;
/* check if AH=0x42 is supported */
if (!(regs.ecx & 1))
return 0;
return (regs.eax >> 8) & 0xff;
}
/*
* Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
* error occurs, then return non-zero, otherwise zero.
*/
static int
grub_biosdisk_get_diskinfo_standard (int drive,
unsigned long *cylinders,
unsigned long *heads,
unsigned long *sectors)
{
struct grub_bios_int_registers regs;
regs.eax = 0x0800;
regs.edx = drive & 0xff;
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
grub_bios_interrupt (0x13, &regs);
/* Check if unsuccessful. Ignore return value if carry isn't set to
workaround some buggy BIOSes. */
if ((regs.flags & GRUB_CPU_INT_FLAGS_CARRY) && ((regs.eax & 0xff00) != 0))
return (regs.eax & 0xff00) >> 8;
/* bogus BIOSes may not return an error number */
/* 0 sectors means no disk */
if (!(regs.ecx & 0x3f))
/* XXX 0x60 is one of the unused error numbers */
return 0x60;
/* the number of heads is counted from zero */
*heads = ((regs.edx >> 8) & 0xff) + 1;
*cylinders = (((regs.ecx >> 8) & 0xff) | ((regs.ecx << 2) & 0x0300)) + 1;
*sectors = regs.ecx & 0x3f;
return 0;
}
static int
grub_biosdisk_get_diskinfo_real (int drive, void *drp, grub_uint16_t ax)
{
struct grub_bios_int_registers regs;
regs.eax = ax;
/* compute the address of drive parameters */
regs.esi = ((grub_addr_t) drp) & 0xf;
regs.ds = ((grub_addr_t) drp) >> 4;
regs.edx = drive & 0xff;
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
grub_bios_interrupt (0x13, &regs);
/* Check if unsuccessful. Ignore return value if carry isn't set to
workaround some buggy BIOSes. */
if ((regs.flags & GRUB_CPU_INT_FLAGS_CARRY) && ((regs.eax & 0xff00) != 0))
return (regs.eax & 0xff00) >> 8;
return 0;
}
/*
* Return the cdrom information of DRIVE in CDRP. If an error occurs,
* then return non-zero, otherwise zero.
*/
static int
grub_biosdisk_get_cdinfo_int13_extensions (int drive, void *cdrp)
{
return grub_biosdisk_get_diskinfo_real (drive, cdrp, 0x4b01);
}
/*
* Return the geometry of DRIVE in a drive parameters, DRP. If an error
* occurs, then return non-zero, otherwise zero.
*/
static int
grub_biosdisk_get_diskinfo_int13_extensions (int drive, void *drp)
{
return grub_biosdisk_get_diskinfo_real (drive, drp, 0x4800);
}
static int
grub_biosdisk_get_drive (const char *name)
{
unsigned long drive;
if (name[0] == 'c' && name[1] == 'd' && name[2] == 0 && cd_drive)
return cd_drive;
if ((name[0] != 'f' && name[0] != 'h') || name[1] != 'd')
goto fail;
drive = grub_strtoul (name + 2, 0, 10);
if (grub_errno != GRUB_ERR_NONE)
goto fail;
if (name[0] == 'h')
drive += 0x80;
return (int) drive ;
fail:
grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a biosdisk");
return -1;
}
static int
grub_biosdisk_call_hook (grub_disk_dev_iterate_hook_t hook, void *hook_data,
int drive)
{
char name[10];
if (cd_drive && drive == cd_drive)
return hook ("cd", hook_data);
grub_snprintf (name, sizeof (name),
(drive & 0x80) ? "hd%d" : "fd%d", drive & (~0x80));
return hook (name, hook_data);
}
static int
grub_biosdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data,
grub_disk_pull_t pull)
{
int num_floppies;
int drive;
/* For hard disks, attempt to read the MBR. */
switch (pull)
{
case GRUB_DISK_PULL_NONE:
for (drive = 0x80; drive < 0x90; drive++)
{
if (grub_biosdisk_rw_standard (0x02, drive, 0, 0, 1, 1,
GRUB_MEMORY_MACHINE_SCRATCH_SEG) != 0)
{
grub_dprintf ("disk", "Read error when probing drive 0x%2x\n", drive);
break;
}
if (grub_biosdisk_call_hook (hook, hook_data, drive))
return 1;
}
return 0;
case GRUB_DISK_PULL_REMOVABLE:
if (cd_drive)
{
if (grub_biosdisk_call_hook (hook, hook_data, cd_drive))
return 1;
}
/* For floppy disks, we can get the number safely. */
num_floppies = grub_biosdisk_get_num_floppies ();
for (drive = 0; drive < num_floppies; drive++)
if (grub_biosdisk_call_hook (hook, hook_data, drive))
return 1;
return 0;
default:
return 0;
}
return 0;
}
#pragma pack(1)
typedef struct ventoy_part_table
{
grub_uint8_t Active; // 0x00 0x80
grub_uint8_t StartHead;
grub_uint16_t StartSector : 6;
grub_uint16_t StartCylinder : 10;
grub_uint8_t FsFlag;
grub_uint8_t EndHead;
grub_uint16_t EndSector : 6;
grub_uint16_t EndCylinder : 10;
grub_uint32_t StartSectorId;
grub_uint32_t SectorCount;
}ventoy_part_table;
typedef struct ventoy_mbr_head
{
grub_uint8_t BootCode[446];
ventoy_part_table PartTbl[4];
grub_uint8_t Byte55;
grub_uint8_t ByteAA;
}ventoy_mbr_head;
#pragma pack()
static grub_err_t
grub_biosdisk_rw (int cmd, grub_disk_t disk,
grub_disk_addr_t sector, grub_size_t size,
unsigned segment);
static int ventoy_is_mbr_match(ventoy_mbr_head *head)
{
grub_uint32_t PartStartSector;
if (head->Byte55 != 0x55 || head->ByteAA != 0xAA) {
return 0;
}
if (head->PartTbl[2].SectorCount > 0 || head->PartTbl[3].SectorCount > 0) {
return 0;
}
if (head->PartTbl[0].StartSectorId != 2048) {
return 0;
}
if (head->PartTbl[1].Active != 0x80 || head->PartTbl[1].FsFlag != 0xEF) {
return 0;
}
PartStartSector = head->PartTbl[0].StartSectorId + head->PartTbl[0].SectorCount;
if (head->PartTbl[1].StartSectorId != PartStartSector || head->PartTbl[1].SectorCount != 65536) {
return 0;
}
return 1;
}
static grub_err_t
grub_biosdisk_open (const char *name, grub_disk_t disk)
{
grub_uint64_t total_sectors = 0;
int drive;
struct grub_biosdisk_data *data;
drive = grub_biosdisk_get_drive (name);
if (drive < 0)
return grub_errno;
disk->id = drive;
data = (struct grub_biosdisk_data *) grub_zalloc (sizeof (*data));
if (! data)
return grub_errno;
data->drive = drive;
if ((cd_drive) && (drive == cd_drive))
{
data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM;
data->sectors = 8;
disk->log_sector_size = 11;
/* TODO: get the correct size. */
total_sectors = GRUB_DISK_SIZE_UNKNOWN;
}
else
{
/* HDD */
int version;
disk->log_sector_size = 9;
version = grub_biosdisk_check_int13_extensions (drive);
if (version)
{
struct grub_biosdisk_drp *drp
= (struct grub_biosdisk_drp *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
/* Clear out the DRP. */
grub_memset (drp, 0, sizeof (*drp));
drp->size = sizeof (*drp);
if (! grub_biosdisk_get_diskinfo_int13_extensions (drive, drp))
{
data->flags = GRUB_BIOSDISK_FLAG_LBA;
if (drp->total_sectors)
total_sectors = drp->total_sectors;
else
/* Some buggy BIOSes doesn't return the total sectors
correctly but returns zero. So if it is zero, compute
it by C/H/S returned by the LBA BIOS call. */
total_sectors = ((grub_uint64_t) drp->cylinders)
* drp->heads * drp->sectors;
if (drp->bytes_per_sector
&& !(drp->bytes_per_sector & (drp->bytes_per_sector - 1))
&& drp->bytes_per_sector >= 512
&& drp->bytes_per_sector <= 16384)
{
for (disk->log_sector_size = 0;
(1 << disk->log_sector_size) < drp->bytes_per_sector;
disk->log_sector_size++);
}
}
}
}
if (! (data->flags & GRUB_BIOSDISK_FLAG_CDROM))
{
if (grub_biosdisk_get_diskinfo_standard (drive,
&data->cylinders,
&data->heads,
&data->sectors) != 0)
{
if (total_sectors && (data->flags & GRUB_BIOSDISK_FLAG_LBA))
{
data->sectors = 63;
data->heads = 255;
data->cylinders
= grub_divmod64 (total_sectors
+ data->heads * data->sectors - 1,
data->heads * data->sectors, 0);
}
else
{
grub_free (data);
return grub_error (GRUB_ERR_BAD_DEVICE, "%s cannot get C/H/S values", disk->name);
}
}
if (data->sectors == 0)
data->sectors = 63;
if (data->heads == 0)
data->heads = 255;
if (! total_sectors)
total_sectors = ((grub_uint64_t) data->cylinders)
* data->heads * data->sectors;
}
disk->total_sectors = total_sectors;
/* Limit the max to 0x7f because of Phoenix EDD. */
disk->max_agglomerate = 0x7f >> GRUB_DISK_CACHE_BITS;
COMPILE_TIME_ASSERT ((0x7f >> GRUB_DISK_CACHE_BITS
<< (GRUB_DISK_SECTOR_BITS + GRUB_DISK_CACHE_BITS))
+ sizeof (struct grub_biosdisk_dap)
< GRUB_MEMORY_MACHINE_SCRATCH_SIZE);
disk->data = data;
//fixup some buggy bios
if (total_sectors > (16434495 - 2097152) && total_sectors < (16434495 + 2097152) &&
(data->flags & GRUB_BIOSDISK_FLAG_LBA) > 0 && (data->flags & GRUB_BIOSDISK_FLAG_CDROM) == 0) {
if (grub_biosdisk_rw(0, disk, 0, 1, GRUB_MEMORY_MACHINE_SCRATCH_SEG) == 0) {
ventoy_mbr_head *mbr = (ventoy_mbr_head *)GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
if (ventoy_is_mbr_match(mbr)) {
total_sectors = mbr->PartTbl[1].StartSectorId + mbr->PartTbl[1].SectorCount + 1;
if (disk->total_sectors < total_sectors) {
disk->total_sectors = total_sectors;
}
}
}
}
return GRUB_ERR_NONE;
}
static void
grub_biosdisk_close (grub_disk_t disk)
{
grub_free (disk->data);
}
/* For readability. */
#define GRUB_BIOSDISK_READ 0
#define GRUB_BIOSDISK_WRITE 1
#define GRUB_BIOSDISK_CDROM_RETRY_COUNT 3
static grub_err_t
grub_biosdisk_rw (int cmd, grub_disk_t disk,
grub_disk_addr_t sector, grub_size_t size,
unsigned segment)
{
struct grub_biosdisk_data *data = disk->data;
/* VirtualBox fails with sectors above 2T on CDs.
Since even BD-ROMS are never that big anyway, return error. */
if ((data->flags & GRUB_BIOSDISK_FLAG_CDROM)
&& (sector >> 32))
return grub_error (GRUB_ERR_OUT_OF_RANGE,
N_("attempt to read or write outside of disk `%s'"),
disk->name);
if (data->flags & GRUB_BIOSDISK_FLAG_LBA)
{
struct grub_biosdisk_dap *dap;
dap = (struct grub_biosdisk_dap *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR
+ (data->sectors
<< disk->log_sector_size));
dap->length = sizeof (*dap);
dap->reserved = 0;
dap->blocks = size;
dap->buffer = segment << 16; /* The format SEGMENT:ADDRESS. */
dap->block = sector;
if (data->flags & GRUB_BIOSDISK_FLAG_CDROM)
{
int i;
if (cmd)
return grub_error (GRUB_ERR_WRITE_ERROR, N_("cannot write to CD-ROM"));
for (i = 0; i < GRUB_BIOSDISK_CDROM_RETRY_COUNT; i++)
if (! grub_biosdisk_rw_int13_extensions (0x42, data->drive, dap))
break;
if (i == GRUB_BIOSDISK_CDROM_RETRY_COUNT)
return grub_error (GRUB_ERR_READ_ERROR, N_("failure reading sector 0x%llx "
"from `%s'"),
(unsigned long long) sector,
disk->name);
}
else
if (grub_biosdisk_rw_int13_extensions (cmd + 0x42, data->drive, dap))
{
/* Fall back to the CHS mode. */
data->flags &= ~GRUB_BIOSDISK_FLAG_LBA;
disk->total_sectors = data->cylinders * data->heads * data->sectors;
return grub_biosdisk_rw (cmd, disk, sector, size, segment);
}
}
else
{
unsigned coff, hoff, soff;
unsigned head;
/* It is impossible to reach over 8064 MiB (a bit less than LBA24) with
the traditional CHS access. */
if (sector >
1024 /* cylinders */ *
256 /* heads */ *
63 /* spt */)
return grub_error (GRUB_ERR_OUT_OF_RANGE,
N_("attempt to read or write outside of disk `%s'"),
disk->name);
soff = ((grub_uint32_t) sector) % data->sectors + 1;
head = ((grub_uint32_t) sector) / data->sectors;
hoff = head % data->heads;
coff = head / data->heads;
if (coff >= data->cylinders)
return grub_error (GRUB_ERR_OUT_OF_RANGE,
N_("attempt to read or write outside of disk `%s'"),
disk->name);
if (grub_biosdisk_rw_standard (cmd + 0x02, data->drive,
coff, hoff, soff, size, segment))
{
switch (cmd)
{
case GRUB_BIOSDISK_READ:
return grub_error (GRUB_ERR_READ_ERROR, N_("failure reading sector 0x%llx "
"from `%s'"),
(unsigned long long) sector,
disk->name);
case GRUB_BIOSDISK_WRITE:
return grub_error (GRUB_ERR_WRITE_ERROR, N_("failure writing sector 0x%llx "
"to `%s'"),
(unsigned long long) sector,
disk->name);
}
}
}
return GRUB_ERR_NONE;
}
/* Return the number of sectors which can be read safely at a time. */
static grub_size_t
get_safe_sectors (grub_disk_t disk, grub_disk_addr_t sector)
{
grub_size_t size;
grub_uint64_t offset;
struct grub_biosdisk_data *data = disk->data;
grub_uint32_t sectors = data->sectors;
/* OFFSET = SECTOR % SECTORS */
grub_divmod64 (sector, sectors, &offset);
size = sectors - offset;
return size;
}
static grub_err_t
grub_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
grub_size_t size, char *buf)
{
while (size)
{
grub_size_t len;
len = get_safe_sectors (disk, sector);
if (len > size)
len = size;
if (grub_biosdisk_rw (GRUB_BIOSDISK_READ, disk, sector, len,
GRUB_MEMORY_MACHINE_SCRATCH_SEG))
return grub_errno;
grub_memcpy (buf, (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR,
len << disk->log_sector_size);
buf += len << disk->log_sector_size;
sector += len;
size -= len;
}
return grub_errno;
}
static grub_err_t
grub_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
grub_size_t size, const char *buf)
{
struct grub_biosdisk_data *data = disk->data;
if (data->flags & GRUB_BIOSDISK_FLAG_CDROM)
return grub_error (GRUB_ERR_IO, N_("cannot write to CD-ROM"));
while (size)
{
grub_size_t len;
len = get_safe_sectors (disk, sector);
if (len > size)
len = size;
grub_memcpy ((void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, buf,
len << disk->log_sector_size);
if (grub_biosdisk_rw (GRUB_BIOSDISK_WRITE, disk, sector, len,
GRUB_MEMORY_MACHINE_SCRATCH_SEG))
return grub_errno;
buf += len << disk->log_sector_size;
sector += len;
size -= len;
}
return grub_errno;
}
static struct grub_disk_dev grub_biosdisk_dev =
{
.name = "biosdisk",
.id = GRUB_DISK_DEVICE_BIOSDISK_ID,
.disk_iterate = grub_biosdisk_iterate,
.disk_open = grub_biosdisk_open,
.disk_close = grub_biosdisk_close,
.disk_read = grub_biosdisk_read,
.disk_write = grub_biosdisk_write,
.next = 0
};
static void
grub_disk_biosdisk_fini (void)
{
grub_disk_dev_unregister (&grub_biosdisk_dev);
}
GRUB_MOD_INIT(biosdisk)
{
struct grub_biosdisk_cdrp *cdrp
= (struct grub_biosdisk_cdrp *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
grub_uint8_t boot_drive;
if (grub_disk_firmware_is_tainted)
{
grub_puts_ (N_("Native disk drivers are in use. "
"Refusing to use firmware disk interface."));
return;
}
grub_disk_firmware_fini = grub_disk_biosdisk_fini;
grub_memset (cdrp, 0, sizeof (*cdrp));
cdrp->size = sizeof (*cdrp);
cdrp->media_type = 0xFF;
boot_drive = (grub_boot_device >> 24);
if ((! grub_biosdisk_get_cdinfo_int13_extensions (boot_drive, cdrp))
&& ((cdrp->media_type & GRUB_BIOSDISK_CDTYPE_MASK)
== GRUB_BIOSDISK_CDTYPE_NO_EMUL))
cd_drive = cdrp->drive_no;
/* Since diskboot.S rejects devices over 0x90 it must be a CD booted with
cdboot.S
*/
if (boot_drive >= 0x90)
cd_drive = boot_drive;
grub_disk_dev_register (&grub_biosdisk_dev);
}
GRUB_MOD_FINI(biosdisk)
{
grub_disk_biosdisk_fini ();
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,161 @@
/* gfxmenu.c - Graphical menu interface controller. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2008 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/types.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/err.h>
#include <grub/dl.h>
#include <grub/command.h>
#include <grub/video.h>
#include <grub/gfxterm.h>
#include <grub/bitmap.h>
#include <grub/bitmap_scale.h>
#include <grub/term.h>
#include <grub/env.h>
#include <grub/normal.h>
#include <grub/gfxwidgets.h>
#include <grub/menu.h>
#include <grub/menu_viewer.h>
#include <grub/gfxmenu_model.h>
#include <grub/gfxmenu_view.h>
#include <grub/time.h>
#include <grub/env.h>
#include <grub/i18n.h>
GRUB_MOD_LICENSE ("GPLv3+");
extern int g_ventoy_menu_refresh;
static grub_gfxmenu_view_t cached_view;
static void
grub_gfxmenu_viewer_fini (void *data __attribute__ ((unused)))
{
}
/* FIXME: Previously 't' changed to text menu is it necessary? */
static grub_err_t
grub_gfxmenu_try (int entry, grub_menu_t menu, int nested)
{
int force_refresh = 0;
grub_gfxmenu_view_t view = NULL;
const char *theme_path;
char *full_theme_path = 0;
struct grub_menu_viewer *instance;
grub_err_t err;
struct grub_video_mode_info mode_info;
theme_path = grub_env_get ("theme");
if (! theme_path)
return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"),
"theme");
err = grub_video_get_info (&mode_info);
if (err)
return err;
instance = grub_zalloc (sizeof (*instance));
if (!instance)
return grub_errno;
if (theme_path[0] != '/' && theme_path[0] != '(')
{
const char *prefix;
prefix = grub_env_get ("prefix");
full_theme_path = grub_xasprintf ("%s/themes/%s",
prefix,
theme_path);
}
if (g_ventoy_menu_refresh)
{
g_ventoy_menu_refresh = 0;
force_refresh = 1;
}
if (force_refresh ||
!cached_view || grub_strcmp (cached_view->theme_path,
full_theme_path ? : theme_path) != 0
|| cached_view->screen.width != mode_info.width
|| cached_view->screen.height != mode_info.height)
{
grub_gfxmenu_view_destroy (cached_view);
/* Create the view. */
cached_view = grub_gfxmenu_view_new (full_theme_path ? : theme_path,
mode_info.width,
mode_info.height);
}
grub_free (full_theme_path);
if (! cached_view)
{
grub_free (instance);
return grub_errno;
}
view = cached_view;
view->double_repaint = (mode_info.mode_type
& GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED)
&& !(mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_UPDATING_SWAP);
view->selected = entry;
view->menu = menu;
view->nested = nested;
view->first_timeout = -1;
grub_video_set_viewport (0, 0, mode_info.width, mode_info.height);
if (view->double_repaint)
{
grub_video_swap_buffers ();
grub_video_set_viewport (0, 0, mode_info.width, mode_info.height);
}
grub_gfxmenu_view_draw (view);
instance->data = view;
instance->set_chosen_entry = grub_gfxmenu_set_chosen_entry;
instance->fini = grub_gfxmenu_viewer_fini;
instance->print_timeout = grub_gfxmenu_print_timeout;
instance->clear_timeout = grub_gfxmenu_clear_timeout;
grub_menu_register_viewer (instance);
return GRUB_ERR_NONE;
}
GRUB_MOD_INIT (gfxmenu)
{
struct grub_term_output *term;
FOR_ACTIVE_TERM_OUTPUTS(term)
if (grub_gfxmenu_try_hook && term->fullscreen)
{
term->fullscreen ();
break;
}
grub_gfxmenu_try_hook = grub_gfxmenu_try;
}
GRUB_MOD_FINI (gfxmenu)
{
grub_gfxmenu_view_destroy (cached_view);
grub_gfxmenu_try_hook = NULL;
}

View File

@@ -0,0 +1,295 @@
/* gui_label.c - GUI component to display a line of text. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2008,2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/gui.h>
#include <grub/font.h>
#include <grub/gui_string_util.h>
#include <grub/i18n.h>
#include <grub/color.h>
#include <grub/env.h>
extern int g_ventoy_memdisk_mode;
extern int g_ventoy_iso_raw;
extern int g_ventoy_iso_uefi_drv;
static const char *align_options[] =
{
"left",
"center",
"right",
0
};
enum align_mode {
align_left,
align_center,
align_right
};
struct grub_gui_label
{
struct grub_gui_component comp;
grub_gui_container_t parent;
grub_video_rect_t bounds;
char *id;
int visible;
char *text;
char *template;
grub_font_t font;
grub_video_rgba_color_t color;
int value;
enum align_mode align;
};
typedef struct grub_gui_label *grub_gui_label_t;
static void
label_destroy (void *vself)
{
grub_gui_label_t self = vself;
grub_gfxmenu_timeout_unregister ((grub_gui_component_t) self);
grub_free (self->text);
grub_free (self->template);
grub_free (self);
}
static const char *
label_get_id (void *vself)
{
grub_gui_label_t self = vself;
return self->id;
}
static int
label_is_instance (void *vself __attribute__((unused)), const char *type)
{
return grub_strcmp (type, "component") == 0;
}
static void
label_paint (void *vself, const grub_video_rect_t *region)
{
grub_gui_label_t self = vself;
if (! self->visible)
return;
if (!grub_video_have_common_points (region, &self->bounds))
return;
/* Calculate the starting x coordinate. */
int left_x;
if (self->align == align_left)
left_x = 0;
else if (self->align == align_center)
left_x = (self->bounds.width
- grub_font_get_string_width (self->font, self->text)) / 2;
else if (self->align == align_right)
left_x = (self->bounds.width
- grub_font_get_string_width (self->font, self->text));
else
return; /* Invalid alignment. */
if (left_x < 0 || left_x > (int) self->bounds.width)
left_x = 0;
grub_video_rect_t vpsave;
grub_gui_set_viewport (&self->bounds, &vpsave);
grub_font_draw_string (self->text,
self->font,
grub_video_map_rgba_color (self->color),
left_x,
grub_font_get_ascent (self->font));
grub_gui_restore_viewport (&vpsave);
}
static void
label_set_parent (void *vself, grub_gui_container_t parent)
{
grub_gui_label_t self = vself;
self->parent = parent;
}
static grub_gui_container_t
label_get_parent (void *vself)
{
grub_gui_label_t self = vself;
return self->parent;
}
static void
label_set_bounds (void *vself, const grub_video_rect_t *bounds)
{
grub_gui_label_t self = vself;
self->bounds = *bounds;
}
static void
label_get_bounds (void *vself, grub_video_rect_t *bounds)
{
grub_gui_label_t self = vself;
*bounds = self->bounds;
}
static void
label_get_minimal_size (void *vself, unsigned *width, unsigned *height)
{
grub_gui_label_t self = vself;
*width = grub_font_get_string_width (self->font, self->text);
*height = (grub_font_get_ascent (self->font)
+ grub_font_get_descent (self->font));
}
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
static void
label_set_state (void *vself, int visible, int start __attribute__ ((unused)),
int current, int end __attribute__ ((unused)))
{
grub_gui_label_t self = vself;
self->value = -current;
self->visible = visible;
grub_free (self->text);
self->text = grub_xasprintf (self->template ? : "%d", self->value);
}
static grub_err_t
label_set_property (void *vself, const char *name, const char *value)
{
grub_gui_label_t self = vself;
if (grub_strcmp (name, "text") == 0)
{
grub_free (self->text);
grub_free (self->template);
if (! value)
{
self->template = NULL;
self->text = grub_strdup ("");
}
else
{
if (grub_strcmp (value, "@KEYMAP_LONG@") == 0)
value = _("Press enter to boot the selected OS, "
"`e' to edit the commands before booting "
"or `c' for a command-line. ESC to return previous menu.");
else if (grub_strcmp (value, "@KEYMAP_MIDDLE@") == 0)
value = _("Press enter to boot the selected OS, "
"`e' to edit the commands before booting "
"or `c' for a command-line.");
else if (grub_strcmp (value, "@KEYMAP_SHORT@") == 0)
value = _("enter: boot, `e': options, `c': cmd-line");
/* FIXME: Add more templates here if needed. */
else if (grub_strcmp (value, "@VTOY_MEM_DISK@") == 0) {
value = g_ventoy_memdisk_mode ? grub_env_get("VTOY_MEM_DISK_STR") : " ";
}
else if (grub_strcmp (value, "@VTOY_ISO_RAW@") == 0) {
value = g_ventoy_iso_raw ? grub_env_get("VTOY_ISO_RAW_STR") : " ";
}
else if (grub_strcmp (value, "@VTOY_ISO_UEFI_DRV@") == 0) {
value = g_ventoy_iso_uefi_drv ? grub_env_get("VTOY_ISO_UEFI_DRV_STR") : " ";
}
else if (grub_strcmp (value, "@VTOY_HOTKEY_TIP@") == 0) {
value = grub_env_get("VTOY_HOTKEY_TIP");
if (value == NULL) {
value = _(" ");
}
}
self->template = grub_strdup (value);
self->text = grub_xasprintf (value, self->value);
}
}
else if (grub_strcmp (name, "font") == 0)
{
self->font = grub_font_get (value);
}
else if (grub_strcmp (name, "color") == 0)
{
grub_video_parse_color (value, &self->color);
}
else if (grub_strcmp (name, "align") == 0)
{
int i;
for (i = 0; align_options[i]; i++)
{
if (grub_strcmp (align_options[i], value) == 0)
{
self->align = i; /* Set the alignment mode. */
break;
}
}
}
else if (grub_strcmp (name, "visible") == 0)
{
self->visible = grub_strcmp (value, "false") != 0;
}
else if (grub_strcmp (name, "id") == 0)
{
grub_gfxmenu_timeout_unregister ((grub_gui_component_t) self);
grub_free (self->id);
if (value)
self->id = grub_strdup (value);
else
self->id = 0;
if (self->id && grub_strcmp (self->id, GRUB_GFXMENU_TIMEOUT_COMPONENT_ID)
== 0)
grub_gfxmenu_timeout_register ((grub_gui_component_t) self,
label_set_state);
}
return GRUB_ERR_NONE;
}
#pragma GCC diagnostic error "-Wformat-nonliteral"
static struct grub_gui_component_ops label_ops =
{
.destroy = label_destroy,
.get_id = label_get_id,
.is_instance = label_is_instance,
.paint = label_paint,
.set_parent = label_set_parent,
.get_parent = label_get_parent,
.set_bounds = label_set_bounds,
.get_bounds = label_get_bounds,
.get_minimal_size = label_get_minimal_size,
.set_property = label_set_property
};
grub_gui_component_t
grub_gui_label_new (void)
{
grub_gui_label_t label;
label = grub_zalloc (sizeof (*label));
if (! label)
return 0;
label->comp.ops = &label_ops;
label->visible = 1;
label->text = grub_strdup ("");
label->font = grub_font_get ("Unknown Regular 16");
label->color.red = 0;
label->color.green = 0;
label->color.blue = 0;
label->color.alpha = 255;
label->align = align_left;
return (grub_gui_component_t) label;
}

View File

@@ -0,0 +1,600 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2002,2003,2004,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/disk.h>
#include <grub/err.h>
#include <grub/mm.h>
#include <grub/types.h>
#include <grub/partition.h>
#include <grub/misc.h>
#include <grub/time.h>
#include <grub/file.h>
#include <grub/i18n.h>
#include <grub/ventoy.h>
#define GRUB_CACHE_TIMEOUT 2
/* The last time the disk was used. */
static grub_uint64_t grub_last_time = 0;
struct grub_disk_cache grub_disk_cache_table[GRUB_DISK_CACHE_NUM];
void (*grub_disk_firmware_fini) (void);
int grub_disk_firmware_is_tainted;
#if DISK_CACHE_STATS
static unsigned long grub_disk_cache_hits;
static unsigned long grub_disk_cache_misses;
void
grub_disk_cache_get_performance (unsigned long *hits, unsigned long *misses)
{
*hits = grub_disk_cache_hits;
*misses = grub_disk_cache_misses;
}
#endif
grub_err_t (*grub_disk_write_weak) (grub_disk_t disk,
grub_disk_addr_t sector,
grub_off_t offset,
grub_size_t size,
const void *buf);
#include "disk_common.c"
void
grub_disk_cache_invalidate_all (void)
{
unsigned i;
for (i = 0; i < GRUB_DISK_CACHE_NUM; i++)
{
struct grub_disk_cache *cache = grub_disk_cache_table + i;
if (cache->data && ! cache->lock)
{
grub_free (cache->data);
cache->data = 0;
}
}
}
static char *
grub_disk_cache_fetch (unsigned long dev_id, unsigned long disk_id,
grub_disk_addr_t sector)
{
struct grub_disk_cache *cache;
unsigned cache_index;
cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector);
cache = grub_disk_cache_table + cache_index;
if (cache->dev_id == dev_id && cache->disk_id == disk_id
&& cache->sector == sector)
{
cache->lock = 1;
#if DISK_CACHE_STATS
grub_disk_cache_hits++;
#endif
return cache->data;
}
#if DISK_CACHE_STATS
grub_disk_cache_misses++;
#endif
return 0;
}
static void
grub_disk_cache_unlock (unsigned long dev_id, unsigned long disk_id,
grub_disk_addr_t sector)
{
struct grub_disk_cache *cache;
unsigned cache_index;
cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector);
cache = grub_disk_cache_table + cache_index;
if (cache->dev_id == dev_id && cache->disk_id == disk_id
&& cache->sector == sector)
cache->lock = 0;
}
static grub_err_t
grub_disk_cache_store (unsigned long dev_id, unsigned long disk_id,
grub_disk_addr_t sector, const char *data)
{
unsigned cache_index;
struct grub_disk_cache *cache;
cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector);
cache = grub_disk_cache_table + cache_index;
cache->lock = 1;
grub_free (cache->data);
cache->data = 0;
cache->lock = 0;
cache->data = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
if (! cache->data)
return grub_errno;
grub_memcpy (cache->data, data,
GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
cache->dev_id = dev_id;
cache->disk_id = disk_id;
cache->sector = sector;
return GRUB_ERR_NONE;
}
grub_disk_dev_t grub_disk_dev_list;
void
grub_disk_dev_register (grub_disk_dev_t dev)
{
dev->next = grub_disk_dev_list;
grub_disk_dev_list = dev;
}
void
grub_disk_dev_unregister (grub_disk_dev_t dev)
{
grub_disk_dev_t *p, q;
for (p = &grub_disk_dev_list, q = *p; q; p = &(q->next), q = q->next)
if (q == dev)
{
*p = q->next;
break;
}
}
/* Return the location of the first ',', if any, which is not
escaped by a '\'. */
static const char *
find_part_sep (const char *name)
{
const char *p = name;
char c;
while ((c = *p++) != '\0')
{
if (c == '\\' && *p == ',')
p++;
else if (c == ',')
return p - 1;
}
return NULL;
}
grub_disk_t
grub_disk_open (const char *name)
{
const char *p;
grub_disk_t disk;
grub_disk_dev_t dev;
char *raw = (char *) name;
grub_uint64_t current_time;
grub_dprintf ("disk", "Opening `%s'...\n", name);
disk = (grub_disk_t) grub_zalloc (sizeof (*disk));
if (! disk)
return 0;
disk->log_sector_size = GRUB_DISK_SECTOR_BITS;
/* Default 1MiB of maximum agglomerate. */
disk->max_agglomerate = 1048576 >> (GRUB_DISK_SECTOR_BITS
+ GRUB_DISK_CACHE_BITS);
p = find_part_sep (name);
if (p)
{
grub_size_t len = p - name;
raw = grub_malloc (len + 1);
if (! raw)
goto fail;
grub_memcpy (raw, name, len);
raw[len] = '\0';
disk->name = grub_strdup (raw);
}
else
disk->name = grub_strdup (name);
if (! disk->name)
goto fail;
for (dev = grub_disk_dev_list; dev; dev = dev->next)
{
if ((dev->disk_open) (raw, disk) == GRUB_ERR_NONE)
break;
else if (grub_errno == GRUB_ERR_UNKNOWN_DEVICE)
grub_errno = GRUB_ERR_NONE;
else
goto fail;
}
if (! dev)
{
grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' not found"),
name);
goto fail;
}
if (disk->log_sector_size > GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS
|| disk->log_sector_size < GRUB_DISK_SECTOR_BITS)
{
grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"sector sizes of %d bytes aren't supported yet",
(1 << disk->log_sector_size));
goto fail;
}
disk->dev = dev;
if (p)
{
disk->partition = grub_partition_probe (disk, p + 1);
if (! disk->partition)
{
/* TRANSLATORS: It means that the specified partition e.g.
hd0,msdos1=/dev/sda1 doesn't exist. */
grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("no such partition"));
goto fail;
}
}
/* The cache will be invalidated about 2 seconds after a device was
closed. */
current_time = grub_get_time_ms ();
if (current_time > (grub_last_time
+ GRUB_CACHE_TIMEOUT * 1000))
grub_disk_cache_invalidate_all ();
grub_last_time = current_time;
fail:
if (raw && raw != name)
grub_free (raw);
if (grub_errno != GRUB_ERR_NONE)
{
grub_error_push ();
grub_dprintf ("disk", "Opening `%s' failed.\n", name);
grub_error_pop ();
grub_disk_close (disk);
return 0;
}
return disk;
}
void
grub_disk_close (grub_disk_t disk)
{
grub_partition_t part;
grub_dprintf ("disk", "Closing `%s'.\n", disk->name);
if (disk->dev && disk->dev->disk_close)
(disk->dev->disk_close) (disk);
/* Reset the timer. */
grub_last_time = grub_get_time_ms ();
while (disk->partition)
{
part = disk->partition->parent;
grub_free (disk->partition);
disk->partition = part;
}
grub_free ((void *) disk->name);
grub_free (disk);
}
/* Small read (less than cache size and not pass across cache unit boundaries).
sector is already adjusted and is divisible by cache unit size.
*/
static grub_err_t
grub_disk_read_small_real (grub_disk_t disk, grub_disk_addr_t sector,
grub_off_t offset, grub_size_t size, void *buf)
{
char *data;
char *tmp_buf;
/* Fetch the cache. */
data = grub_disk_cache_fetch (disk->dev->id, disk->id, sector);
if (data)
{
/* Just copy it! */
grub_memcpy (buf, data + offset, size);
grub_disk_cache_unlock (disk->dev->id, disk->id, sector);
return GRUB_ERR_NONE;
}
/* Allocate a temporary buffer. */
tmp_buf = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
if (! tmp_buf)
return grub_errno;
/* Otherwise read data from the disk actually. */
if (disk->total_sectors == GRUB_DISK_SIZE_UNKNOWN
|| sector + GRUB_DISK_CACHE_SIZE
< (disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)))
{
grub_err_t err;
err = (disk->dev->disk_read) (disk, transform_sector (disk, sector),
1U << (GRUB_DISK_CACHE_BITS
+ GRUB_DISK_SECTOR_BITS
- disk->log_sector_size), tmp_buf);
if (!err)
{
/* Copy it and store it in the disk cache. */
grub_memcpy (buf, tmp_buf + offset, size);
grub_disk_cache_store (disk->dev->id, disk->id,
sector, tmp_buf);
grub_free (tmp_buf);
return GRUB_ERR_NONE;
}
}
grub_free (tmp_buf);
grub_errno = GRUB_ERR_NONE;
{
/* Uggh... Failed. Instead, just read necessary data. */
unsigned num;
grub_disk_addr_t aligned_sector;
sector += (offset >> GRUB_DISK_SECTOR_BITS);
offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1);
aligned_sector = (sector & ~((1ULL << (disk->log_sector_size
- GRUB_DISK_SECTOR_BITS))
- 1));
offset += ((sector - aligned_sector) << GRUB_DISK_SECTOR_BITS);
num = ((size + offset + (1ULL << (disk->log_sector_size))
- 1) >> (disk->log_sector_size));
tmp_buf = grub_malloc (num << disk->log_sector_size);
if (!tmp_buf)
return grub_errno;
if ((disk->dev->disk_read) (disk, transform_sector (disk, aligned_sector),
num, tmp_buf))
{
grub_error_push ();
grub_dprintf ("disk", "%s read failed\n", disk->name);
grub_error_pop ();
grub_free (tmp_buf);
return grub_errno;
}
grub_memcpy (buf, tmp_buf + offset, size);
grub_free (tmp_buf);
return GRUB_ERR_NONE;
}
}
static grub_err_t
grub_disk_read_small (grub_disk_t disk, grub_disk_addr_t sector,
grub_off_t offset, grub_size_t size, void *buf)
{
grub_err_t err;
err = grub_disk_read_small_real (disk, sector, offset, size, buf);
if (err)
return err;
if (disk->read_hook)
(disk->read_hook) (sector + (offset >> GRUB_DISK_SECTOR_BITS),
offset & (GRUB_DISK_SECTOR_SIZE - 1),
size, disk->read_hook_data);
return GRUB_ERR_NONE;
}
grub_err_t grub_disk_blocklist_read(void *chunklist, grub_uint64_t sector,
grub_uint64_t size, grub_uint32_t log_sector_size)
{
ventoy_img_chunk *last_chunk = NULL;
ventoy_img_chunk *new_chunk = NULL;
ventoy_img_chunk_list *chunk_list = (ventoy_img_chunk_list *)chunklist;
if (chunk_list->cur_chunk == 0)
{
chunk_list->chunk[0].img_start_sector = 0;
chunk_list->chunk[0].img_end_sector = (size >> 11) - 1;
chunk_list->chunk[0].disk_start_sector = sector;
chunk_list->chunk[0].disk_end_sector = sector + (size >> log_sector_size) - 1;
chunk_list->cur_chunk = 1;
return 0;
}
last_chunk = chunk_list->chunk + chunk_list->cur_chunk - 1;
if (last_chunk->disk_end_sector + 1 == sector)
{
last_chunk->img_end_sector += (size >> 11);
last_chunk->disk_end_sector += (size >> log_sector_size);
return 0;
}
if (chunk_list->cur_chunk == chunk_list->max_chunk)
{
new_chunk = grub_realloc(chunk_list->chunk, chunk_list->max_chunk * 2 * sizeof(ventoy_img_chunk));
if (NULL == new_chunk)
{
return -1;
}
chunk_list->chunk = new_chunk;
chunk_list->max_chunk *= 2;
/* issue: update last_chunk */
last_chunk = chunk_list->chunk + chunk_list->cur_chunk - 1;
}
new_chunk = chunk_list->chunk + chunk_list->cur_chunk;
new_chunk->img_start_sector = last_chunk->img_end_sector + 1;
new_chunk->img_end_sector = new_chunk->img_start_sector + (size >> 11) - 1;
new_chunk->disk_start_sector = sector;
new_chunk->disk_end_sector = sector + (size >> log_sector_size) - 1;
chunk_list->cur_chunk++;
return 0;
}
/* Read data from the disk. */
grub_err_t
grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
grub_off_t offset, grub_size_t size, void *buf)
{
if (disk->read_hook == (grub_disk_read_hook_t)grub_disk_blocklist_read)
{
return grub_disk_blocklist_read((ventoy_img_chunk_list *)disk->read_hook_data, sector, size, disk->log_sector_size);
}
/* First of all, check if the region is within the disk. */
if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE)
{
grub_error_push ();
grub_dprintf ("disk", "Read out of range: sector 0x%llx (%s).\n",
(unsigned long long) sector, grub_errmsg);
grub_error_pop ();
return grub_errno;
}
/* First read until first cache boundary. */
if (offset || (sector & (GRUB_DISK_CACHE_SIZE - 1)))
{
grub_disk_addr_t start_sector;
grub_size_t pos;
grub_err_t err;
grub_size_t len;
start_sector = sector & ~((grub_disk_addr_t) GRUB_DISK_CACHE_SIZE - 1);
pos = (sector - start_sector) << GRUB_DISK_SECTOR_BITS;
len = ((GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS)
- pos - offset);
if (len > size)
len = size;
err = grub_disk_read_small (disk, start_sector,
offset + pos, len, buf);
if (err)
return err;
buf = (char *) buf + len;
size -= len;
offset += len;
sector += (offset >> GRUB_DISK_SECTOR_BITS);
offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1);
}
/* Until SIZE is zero... */
while (size >= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS))
{
char *data = NULL;
grub_disk_addr_t agglomerate;
grub_err_t err;
/* agglomerate read until we find a first cached entry. */
for (agglomerate = 0; agglomerate
< (size >> (GRUB_DISK_SECTOR_BITS + GRUB_DISK_CACHE_BITS))
&& agglomerate < disk->max_agglomerate;
agglomerate++)
{
data = grub_disk_cache_fetch (disk->dev->id, disk->id,
sector + (agglomerate
<< GRUB_DISK_CACHE_BITS));
if (data)
break;
}
if (data)
{
grub_memcpy ((char *) buf
+ (agglomerate << (GRUB_DISK_CACHE_BITS
+ GRUB_DISK_SECTOR_BITS)),
data, GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
grub_disk_cache_unlock (disk->dev->id, disk->id,
sector + (agglomerate
<< GRUB_DISK_CACHE_BITS));
}
if (agglomerate)
{
grub_disk_addr_t i;
err = (disk->dev->disk_read) (disk, transform_sector (disk, sector),
agglomerate << (GRUB_DISK_CACHE_BITS
+ GRUB_DISK_SECTOR_BITS
- disk->log_sector_size),
buf);
if (err)
return err;
for (i = 0; i < agglomerate; i ++)
grub_disk_cache_store (disk->dev->id, disk->id,
sector + (i << GRUB_DISK_CACHE_BITS),
(char *) buf
+ (i << (GRUB_DISK_CACHE_BITS
+ GRUB_DISK_SECTOR_BITS)));
if (disk->read_hook)
(disk->read_hook) (sector, 0, agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS),
disk->read_hook_data);
sector += agglomerate << GRUB_DISK_CACHE_BITS;
size -= agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS);
buf = (char *) buf
+ (agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS));
}
if (data)
{
if (disk->read_hook)
(disk->read_hook) (sector, 0, (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS),
disk->read_hook_data);
sector += GRUB_DISK_CACHE_SIZE;
buf = (char *) buf + (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
size -= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
}
}
/* And now read the last part. */
if (size)
{
grub_err_t err;
err = grub_disk_read_small (disk, sector, 0, size, buf);
if (err)
return err;
}
return grub_errno;
}
grub_uint64_t
grub_disk_get_size (grub_disk_t disk)
{
if (disk->partition)
return grub_partition_get_len (disk->partition);
else if (disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN)
return disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS);
else
return GRUB_DISK_SIZE_UNKNOWN;
}

View File

@@ -0,0 +1,966 @@
/* efi.c - generic EFI support */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2006,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/misc.h>
#include <grub/charset.h>
#include <grub/efi/api.h>
#include <grub/efi/efi.h>
#include <grub/efi/console_control.h>
#include <grub/efi/pe32.h>
#include <grub/time.h>
#include <grub/term.h>
#include <grub/kernel.h>
#include <grub/mm.h>
#include <grub/loader.h>
/* The handle of GRUB itself. Filled in by the startup code. */
grub_efi_handle_t grub_efi_image_handle;
/* The pointer to a system table. Filled in by the startup code. */
grub_efi_system_table_t *grub_efi_system_table;
static grub_efi_guid_t console_control_guid = GRUB_EFI_CONSOLE_CONTROL_GUID;
static grub_efi_guid_t loaded_image_guid = GRUB_EFI_LOADED_IMAGE_GUID;
static grub_efi_guid_t device_path_guid = GRUB_EFI_DEVICE_PATH_GUID;
void *
grub_efi_locate_protocol (grub_efi_guid_t *protocol, void *registration)
{
void *interface;
grub_efi_status_t status;
status = efi_call_3 (grub_efi_system_table->boot_services->locate_protocol,
protocol, registration, &interface);
if (status != GRUB_EFI_SUCCESS)
return 0;
return interface;
}
/* Return the array of handles which meet the requirement. If successful,
the number of handles is stored in NUM_HANDLES. The array is allocated
from the heap. */
grub_efi_handle_t *
grub_efi_locate_handle (grub_efi_locate_search_type_t search_type,
grub_efi_guid_t *protocol,
void *search_key,
grub_efi_uintn_t *num_handles)
{
grub_efi_boot_services_t *b;
grub_efi_status_t status;
grub_efi_handle_t *buffer;
grub_efi_uintn_t buffer_size = 8 * sizeof (grub_efi_handle_t);
buffer = grub_malloc (buffer_size);
if (! buffer)
return 0;
b = grub_efi_system_table->boot_services;
status = efi_call_5 (b->locate_handle, search_type, protocol, search_key,
&buffer_size, buffer);
if (status == GRUB_EFI_BUFFER_TOO_SMALL)
{
grub_free (buffer);
buffer = grub_malloc (buffer_size);
if (! buffer)
return 0;
status = efi_call_5 (b->locate_handle, search_type, protocol, search_key,
&buffer_size, buffer);
}
if (status != GRUB_EFI_SUCCESS)
{
grub_free (buffer);
return 0;
}
*num_handles = buffer_size / sizeof (grub_efi_handle_t);
return buffer;
}
void *
grub_efi_open_protocol (grub_efi_handle_t handle,
grub_efi_guid_t *protocol,
grub_efi_uint32_t attributes)
{
grub_efi_boot_services_t *b;
grub_efi_status_t status;
void *interface;
b = grub_efi_system_table->boot_services;
status = efi_call_6 (b->open_protocol, handle,
protocol,
&interface,
grub_efi_image_handle,
0,
attributes);
if (status != GRUB_EFI_SUCCESS)
return 0;
return interface;
}
int
grub_efi_set_text_mode (int on)
{
grub_efi_console_control_protocol_t *c;
grub_efi_screen_mode_t mode, new_mode;
c = grub_efi_locate_protocol (&console_control_guid, 0);
if (! c)
/* No console control protocol instance available, assume it is
already in text mode. */
return 1;
if (efi_call_4 (c->get_mode, c, &mode, 0, 0) != GRUB_EFI_SUCCESS)
return 0;
new_mode = on ? GRUB_EFI_SCREEN_TEXT : GRUB_EFI_SCREEN_GRAPHICS;
if (mode != new_mode)
if (efi_call_2 (c->set_mode, c, new_mode) != GRUB_EFI_SUCCESS)
return 0;
return 1;
}
void
grub_efi_stall (grub_efi_uintn_t microseconds)
{
efi_call_1 (grub_efi_system_table->boot_services->stall, microseconds);
}
grub_efi_loaded_image_t *
grub_efi_get_loaded_image (grub_efi_handle_t image_handle)
{
return grub_efi_open_protocol (image_handle,
&loaded_image_guid,
GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
}
void
grub_reboot (void)
{
grub_machine_fini (GRUB_LOADER_FLAG_NORETURN);
efi_call_4 (grub_efi_system_table->runtime_services->reset_system,
GRUB_EFI_RESET_COLD, GRUB_EFI_SUCCESS, 0, NULL);
for (;;) ;
}
void
grub_exit (void)
{
grub_machine_fini (GRUB_LOADER_FLAG_NORETURN);
efi_call_4 (grub_efi_system_table->boot_services->exit,
grub_efi_image_handle, GRUB_EFI_SUCCESS, 0, 0);
for (;;) ;
}
grub_err_t
grub_efi_set_virtual_address_map (grub_efi_uintn_t memory_map_size,
grub_efi_uintn_t descriptor_size,
grub_efi_uint32_t descriptor_version,
grub_efi_memory_descriptor_t *virtual_map)
{
grub_efi_runtime_services_t *r;
grub_efi_status_t status;
r = grub_efi_system_table->runtime_services;
status = efi_call_4 (r->set_virtual_address_map, memory_map_size,
descriptor_size, descriptor_version, virtual_map);
if (status == GRUB_EFI_SUCCESS)
return GRUB_ERR_NONE;
return grub_error (GRUB_ERR_IO, "set_virtual_address_map failed");
}
grub_err_t
grub_efi_set_variable(const char *var, const grub_efi_guid_t *guid,
void *data, grub_size_t datasize)
{
grub_efi_status_t status;
grub_efi_runtime_services_t *r;
grub_efi_char16_t *var16;
grub_size_t len, len16;
len = grub_strlen (var);
len16 = len * GRUB_MAX_UTF16_PER_UTF8;
var16 = grub_malloc ((len16 + 1) * sizeof (var16[0]));
if (!var16)
return grub_errno;
len16 = grub_utf8_to_utf16 (var16, len16, (grub_uint8_t *) var, len, NULL);
var16[len16] = 0;
r = grub_efi_system_table->runtime_services;
status = efi_call_5 (r->set_variable, var16, guid,
(GRUB_EFI_VARIABLE_NON_VOLATILE
| GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS
| GRUB_EFI_VARIABLE_RUNTIME_ACCESS),
datasize, data);
grub_free (var16);
if (status == GRUB_EFI_SUCCESS)
return GRUB_ERR_NONE;
return grub_error (GRUB_ERR_IO, "could not set EFI variable `%s'", var);
}
void *
grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid,
grub_size_t *datasize_out)
{
grub_efi_status_t status;
grub_efi_uintn_t datasize = 0;
grub_efi_runtime_services_t *r;
grub_efi_char16_t *var16;
void *data;
grub_size_t len, len16;
*datasize_out = 0;
len = grub_strlen (var);
len16 = len * GRUB_MAX_UTF16_PER_UTF8;
var16 = grub_malloc ((len16 + 1) * sizeof (var16[0]));
if (!var16)
return NULL;
len16 = grub_utf8_to_utf16 (var16, len16, (grub_uint8_t *) var, len, NULL);
var16[len16] = 0;
r = grub_efi_system_table->runtime_services;
status = efi_call_5 (r->get_variable, var16, guid, NULL, &datasize, NULL);
if (status != GRUB_EFI_BUFFER_TOO_SMALL || !datasize)
{
grub_free (var16);
return NULL;
}
data = grub_malloc (datasize);
if (!data)
{
grub_free (var16);
return NULL;
}
status = efi_call_5 (r->get_variable, var16, guid, NULL, &datasize, data);
grub_free (var16);
if (status == GRUB_EFI_SUCCESS)
{
*datasize_out = datasize;
return data;
}
grub_free (data);
return NULL;
}
#pragma GCC diagnostic ignored "-Wcast-align"
/* Search the mods section from the PE32/PE32+ image. This code uses
a PE32 header, but should work with PE32+ as well. */
grub_addr_t
grub_efi_modules_addr (void)
{
grub_efi_loaded_image_t *image;
struct grub_pe32_header *header;
struct grub_pe32_coff_header *coff_header;
struct grub_pe32_section_table *sections;
struct grub_pe32_section_table *section;
struct grub_module_info *info;
grub_uint16_t i;
image = grub_efi_get_loaded_image (grub_efi_image_handle);
if (! image)
return 0;
header = image->image_base;
coff_header = &(header->coff_header);
sections
= (struct grub_pe32_section_table *) ((char *) coff_header
+ sizeof (*coff_header)
+ coff_header->optional_header_size);
for (i = 0, section = sections;
i < coff_header->num_sections;
i++, section++)
{
if (grub_strcmp (section->name, "mods") == 0)
break;
}
if (i == coff_header->num_sections)
return 0;
info = (struct grub_module_info *) ((char *) image->image_base
+ section->virtual_address);
if (info->magic != GRUB_MODULE_MAGIC)
return 0;
return (grub_addr_t) info;
}
#pragma GCC diagnostic error "-Wcast-align"
char *
grub_efi_get_filename (grub_efi_device_path_t *dp0)
{
char *name = 0, *p, *pi;
grub_size_t filesize = 0;
grub_efi_device_path_t *dp;
if (!dp0)
return NULL;
dp = dp0;
while (1)
{
grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
if (type == GRUB_EFI_END_DEVICE_PATH_TYPE)
break;
if (type == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
&& subtype == GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE)
{
grub_efi_uint16_t len;
len = ((GRUB_EFI_DEVICE_PATH_LENGTH (dp) - 4)
/ sizeof (grub_efi_char16_t));
filesize += GRUB_MAX_UTF8_PER_UTF16 * len + 2;
}
dp = GRUB_EFI_NEXT_DEVICE_PATH (dp);
}
if (!filesize)
return NULL;
dp = dp0;
p = name = grub_malloc (filesize);
if (!name)
return NULL;
while (1)
{
grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
if (type == GRUB_EFI_END_DEVICE_PATH_TYPE)
break;
else if (type == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
&& subtype == GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE)
{
grub_efi_file_path_device_path_t *fp;
grub_efi_uint16_t len;
grub_efi_char16_t *dup_name;
*p++ = '/';
len = ((GRUB_EFI_DEVICE_PATH_LENGTH (dp) - 4)
/ sizeof (grub_efi_char16_t));
fp = (grub_efi_file_path_device_path_t *) dp;
/* According to EFI spec Path Name is NULL terminated */
while (len > 0 && fp->path_name[len - 1] == 0)
len--;
dup_name = grub_malloc (len * sizeof (*dup_name));
if (!dup_name)
{
grub_free (name);
return NULL;
}
p = (char *) grub_utf16_to_utf8 ((unsigned char *) p,
grub_memcpy (dup_name, fp->path_name, len * sizeof (*dup_name)),
len);
grub_free (dup_name);
}
dp = GRUB_EFI_NEXT_DEVICE_PATH (dp);
}
*p = '\0';
for (pi = name, p = name; *pi;)
{
/* EFI breaks paths with backslashes. */
if (*pi == '\\' || *pi == '/')
{
*p++ = '/';
while (*pi == '\\' || *pi == '/')
pi++;
continue;
}
*p++ = *pi++;
}
*p = '\0';
return name;
}
grub_efi_device_path_t *
grub_efi_get_device_path (grub_efi_handle_t handle)
{
return grub_efi_open_protocol (handle, &device_path_guid,
GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
}
/* Return the device path node right before the end node. */
grub_efi_device_path_t *
grub_efi_find_last_device_path (const grub_efi_device_path_t *dp)
{
grub_efi_device_path_t *next, *p;
if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp))
return 0;
for (p = (grub_efi_device_path_t *) dp, next = GRUB_EFI_NEXT_DEVICE_PATH (p);
! GRUB_EFI_END_ENTIRE_DEVICE_PATH (next);
p = next, next = GRUB_EFI_NEXT_DEVICE_PATH (next))
;
return p;
}
/* Duplicate a device path. */
grub_efi_device_path_t *
grub_efi_duplicate_device_path (const grub_efi_device_path_t *dp)
{
grub_efi_device_path_t *p;
grub_size_t total_size = 0;
for (p = (grub_efi_device_path_t *) dp;
;
p = GRUB_EFI_NEXT_DEVICE_PATH (p))
{
total_size += GRUB_EFI_DEVICE_PATH_LENGTH (p);
if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (p))
break;
}
p = grub_malloc (total_size);
if (! p)
return 0;
grub_memcpy (p, dp, total_size);
return p;
}
static void
dump_vendor_path (const char *type, grub_efi_vendor_device_path_t *vendor)
{
grub_uint32_t vendor_data_len = vendor->header.length - sizeof (*vendor);
grub_printf ("/%sVendor(%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x)[%x: ",
type,
(unsigned) vendor->vendor_guid.data1,
(unsigned) vendor->vendor_guid.data2,
(unsigned) vendor->vendor_guid.data3,
(unsigned) vendor->vendor_guid.data4[0],
(unsigned) vendor->vendor_guid.data4[1],
(unsigned) vendor->vendor_guid.data4[2],
(unsigned) vendor->vendor_guid.data4[3],
(unsigned) vendor->vendor_guid.data4[4],
(unsigned) vendor->vendor_guid.data4[5],
(unsigned) vendor->vendor_guid.data4[6],
(unsigned) vendor->vendor_guid.data4[7],
vendor_data_len);
if (vendor->header.length > sizeof (*vendor))
{
grub_uint32_t i;
for (i = 0; i < vendor_data_len; i++)
grub_printf ("%02x ", vendor->vendor_defined_data[i]);
}
grub_printf ("]");
}
/* Print the chain of Device Path nodes. This is mainly for debugging. */
void
grub_efi_print_device_path (grub_efi_device_path_t *dp)
{
while (1)
{
grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp);
switch (type)
{
case GRUB_EFI_END_DEVICE_PATH_TYPE:
switch (subtype)
{
case GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE:
grub_printf ("/EndEntire\n");
//grub_putchar ('\n');
break;
case GRUB_EFI_END_THIS_DEVICE_PATH_SUBTYPE:
grub_printf ("/EndThis\n");
//grub_putchar ('\n');
break;
default:
grub_printf ("/EndUnknown(%x)\n", (unsigned) subtype);
break;
}
break;
case GRUB_EFI_HARDWARE_DEVICE_PATH_TYPE:
switch (subtype)
{
case GRUB_EFI_PCI_DEVICE_PATH_SUBTYPE:
{
grub_efi_pci_device_path_t *pci
= (grub_efi_pci_device_path_t *) dp;
grub_printf ("/PCI(%x,%x)",
(unsigned) pci->function, (unsigned) pci->device);
}
break;
case GRUB_EFI_PCCARD_DEVICE_PATH_SUBTYPE:
{
grub_efi_pccard_device_path_t *pccard
= (grub_efi_pccard_device_path_t *) dp;
grub_printf ("/PCCARD(%x)",
(unsigned) pccard->function);
}
break;
case GRUB_EFI_MEMORY_MAPPED_DEVICE_PATH_SUBTYPE:
{
grub_efi_memory_mapped_device_path_t *mmapped
= (grub_efi_memory_mapped_device_path_t *) dp;
grub_printf ("/MMap(%x,%llx,%llx)",
(unsigned) mmapped->memory_type,
(unsigned long long) mmapped->start_address,
(unsigned long long) mmapped->end_address);
}
break;
case GRUB_EFI_VENDOR_DEVICE_PATH_SUBTYPE:
dump_vendor_path ("Hardware",
(grub_efi_vendor_device_path_t *) dp);
break;
case GRUB_EFI_CONTROLLER_DEVICE_PATH_SUBTYPE:
{
grub_efi_controller_device_path_t *controller
= (grub_efi_controller_device_path_t *) dp;
grub_printf ("/Ctrl(%x)",
(unsigned) controller->controller_number);
}
break;
default:
grub_printf ("/UnknownHW(%x)", (unsigned) subtype);
break;
}
break;
case GRUB_EFI_ACPI_DEVICE_PATH_TYPE:
switch (subtype)
{
case GRUB_EFI_ACPI_DEVICE_PATH_SUBTYPE:
{
grub_efi_acpi_device_path_t *acpi
= (grub_efi_acpi_device_path_t *) dp;
grub_printf ("/ACPI(%x,%x)",
(unsigned) acpi->hid,
(unsigned) acpi->uid);
}
break;
case GRUB_EFI_EXPANDED_ACPI_DEVICE_PATH_SUBTYPE:
{
grub_efi_expanded_acpi_device_path_t *eacpi
= (grub_efi_expanded_acpi_device_path_t *) dp;
grub_printf ("/ACPI(");
if (GRUB_EFI_EXPANDED_ACPI_HIDSTR (dp)[0] == '\0')
grub_printf ("%x,", (unsigned) eacpi->hid);
else
grub_printf ("%s,", GRUB_EFI_EXPANDED_ACPI_HIDSTR (dp));
if (GRUB_EFI_EXPANDED_ACPI_UIDSTR (dp)[0] == '\0')
grub_printf ("%x,", (unsigned) eacpi->uid);
else
grub_printf ("%s,", GRUB_EFI_EXPANDED_ACPI_UIDSTR (dp));
if (GRUB_EFI_EXPANDED_ACPI_CIDSTR (dp)[0] == '\0')
grub_printf ("%x)", (unsigned) eacpi->cid);
else
grub_printf ("%s)", GRUB_EFI_EXPANDED_ACPI_CIDSTR (dp));
}
break;
default:
grub_printf ("/UnknownACPI(%x)", (unsigned) subtype);
break;
}
break;
case GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE:
switch (subtype)
{
case GRUB_EFI_ATAPI_DEVICE_PATH_SUBTYPE:
{
grub_efi_atapi_device_path_t *atapi
= (grub_efi_atapi_device_path_t *) dp;
grub_printf ("/ATAPI(%x,%x,%x)",
(unsigned) atapi->primary_secondary,
(unsigned) atapi->slave_master,
(unsigned) atapi->lun);
}
break;
case GRUB_EFI_SCSI_DEVICE_PATH_SUBTYPE:
{
grub_efi_scsi_device_path_t *scsi
= (grub_efi_scsi_device_path_t *) dp;
grub_printf ("/SCSI(%x,%x)",
(unsigned) scsi->pun,
(unsigned) scsi->lun);
}
break;
case GRUB_EFI_FIBRE_CHANNEL_DEVICE_PATH_SUBTYPE:
{
grub_efi_fibre_channel_device_path_t *fc
= (grub_efi_fibre_channel_device_path_t *) dp;
grub_printf ("/FibreChannel(%llx,%llx)",
(unsigned long long) fc->wwn,
(unsigned long long) fc->lun);
}
break;
case GRUB_EFI_1394_DEVICE_PATH_SUBTYPE:
{
grub_efi_1394_device_path_t *firewire
= (grub_efi_1394_device_path_t *) dp;
grub_printf ("/1394(%llx)",
(unsigned long long) firewire->guid);
}
break;
case GRUB_EFI_USB_DEVICE_PATH_SUBTYPE:
{
grub_efi_usb_device_path_t *usb
= (grub_efi_usb_device_path_t *) dp;
grub_printf ("/USB(%x,%x)",
(unsigned) usb->parent_port_number,
(unsigned) usb->usb_interface);
}
break;
case GRUB_EFI_USB_CLASS_DEVICE_PATH_SUBTYPE:
{
grub_efi_usb_class_device_path_t *usb_class
= (grub_efi_usb_class_device_path_t *) dp;
grub_printf ("/USBClass(%x,%x,%x,%x,%x)",
(unsigned) usb_class->vendor_id,
(unsigned) usb_class->product_id,
(unsigned) usb_class->device_class,
(unsigned) usb_class->device_subclass,
(unsigned) usb_class->device_protocol);
}
break;
case GRUB_EFI_I2O_DEVICE_PATH_SUBTYPE:
{
grub_efi_i2o_device_path_t *i2o
= (grub_efi_i2o_device_path_t *) dp;
grub_printf ("/I2O(%x)", (unsigned) i2o->tid);
}
break;
case GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE:
{
grub_efi_mac_address_device_path_t *mac
= (grub_efi_mac_address_device_path_t *) dp;
grub_printf ("/MacAddr(%02x:%02x:%02x:%02x:%02x:%02x,%x)",
(unsigned) mac->mac_address[0],
(unsigned) mac->mac_address[1],
(unsigned) mac->mac_address[2],
(unsigned) mac->mac_address[3],
(unsigned) mac->mac_address[4],
(unsigned) mac->mac_address[5],
(unsigned) mac->if_type);
}
break;
case GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE:
{
grub_efi_ipv4_device_path_t *ipv4
= (grub_efi_ipv4_device_path_t *) dp;
grub_printf ("/IPv4(%u.%u.%u.%u,%u.%u.%u.%u,%u,%u,%x,%x)",
(unsigned) ipv4->local_ip_address[0],
(unsigned) ipv4->local_ip_address[1],
(unsigned) ipv4->local_ip_address[2],
(unsigned) ipv4->local_ip_address[3],
(unsigned) ipv4->remote_ip_address[0],
(unsigned) ipv4->remote_ip_address[1],
(unsigned) ipv4->remote_ip_address[2],
(unsigned) ipv4->remote_ip_address[3],
(unsigned) ipv4->local_port,
(unsigned) ipv4->remote_port,
(unsigned) ipv4->protocol,
(unsigned) ipv4->static_ip_address);
}
break;
case GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE:
{
grub_efi_ipv6_device_path_t *ipv6
= (grub_efi_ipv6_device_path_t *) dp;
grub_printf ("/IPv6(%x:%x:%x:%x:%x:%x:%x:%x,%x:%x:%x:%x:%x:%x:%x:%x,%u,%u,%x,%x)",
(unsigned) ipv6->local_ip_address[0],
(unsigned) ipv6->local_ip_address[1],
(unsigned) ipv6->local_ip_address[2],
(unsigned) ipv6->local_ip_address[3],
(unsigned) ipv6->local_ip_address[4],
(unsigned) ipv6->local_ip_address[5],
(unsigned) ipv6->local_ip_address[6],
(unsigned) ipv6->local_ip_address[7],
(unsigned) ipv6->remote_ip_address[0],
(unsigned) ipv6->remote_ip_address[1],
(unsigned) ipv6->remote_ip_address[2],
(unsigned) ipv6->remote_ip_address[3],
(unsigned) ipv6->remote_ip_address[4],
(unsigned) ipv6->remote_ip_address[5],
(unsigned) ipv6->remote_ip_address[6],
(unsigned) ipv6->remote_ip_address[7],
(unsigned) ipv6->local_port,
(unsigned) ipv6->remote_port,
(unsigned) ipv6->protocol,
(unsigned) ipv6->static_ip_address);
}
break;
case GRUB_EFI_INFINIBAND_DEVICE_PATH_SUBTYPE:
{
grub_efi_infiniband_device_path_t *ib
= (grub_efi_infiniband_device_path_t *) dp;
grub_printf ("/InfiniBand(%x,%llx,%llx,%llx)",
(unsigned) ib->port_gid[0], /* XXX */
(unsigned long long) ib->remote_id,
(unsigned long long) ib->target_port_id,
(unsigned long long) ib->device_id);
}
break;
case GRUB_EFI_UART_DEVICE_PATH_SUBTYPE:
{
grub_efi_uart_device_path_t *uart
= (grub_efi_uart_device_path_t *) dp;
grub_printf ("/UART(%llu,%u,%x,%x)",
(unsigned long long) uart->baud_rate,
uart->data_bits,
uart->parity,
uart->stop_bits);
}
break;
case GRUB_EFI_SATA_DEVICE_PATH_SUBTYPE:
{
grub_efi_sata_device_path_t *sata;
sata = (grub_efi_sata_device_path_t *) dp;
grub_printf ("/Sata(%x,%x,%x)",
sata->hba_port,
sata->multiplier_port,
sata->lun);
}
break;
case GRUB_EFI_VENDOR_MESSAGING_DEVICE_PATH_SUBTYPE:
dump_vendor_path ("Messaging",
(grub_efi_vendor_device_path_t *) dp);
break;
default:
grub_printf ("/UnknownMessaging(%x)", (unsigned) subtype);
break;
}
break;
case GRUB_EFI_MEDIA_DEVICE_PATH_TYPE:
switch (subtype)
{
case GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE:
{
grub_efi_hard_drive_device_path_t *hd = (grub_efi_hard_drive_device_path_t *) dp;
grub_printf ("/HD(%u,%llx,%llx,%02x%02x%02x%02x%02x%02x%02x%02x,%x,%x)",
hd->partition_number,
(unsigned long long) hd->partition_start,
(unsigned long long) hd->partition_size,
(unsigned) hd->partition_signature[0],
(unsigned) hd->partition_signature[1],
(unsigned) hd->partition_signature[2],
(unsigned) hd->partition_signature[3],
(unsigned) hd->partition_signature[4],
(unsigned) hd->partition_signature[5],
(unsigned) hd->partition_signature[6],
(unsigned) hd->partition_signature[7],
(unsigned) hd->partmap_type,
(unsigned) hd->signature_type);
}
break;
case GRUB_EFI_CDROM_DEVICE_PATH_SUBTYPE:
{
grub_efi_cdrom_device_path_t *cd
= (grub_efi_cdrom_device_path_t *) dp;
grub_printf ("/CD(%u,%llx,%llx)",
cd->boot_entry,
(unsigned long long) cd->partition_start,
(unsigned long long) cd->partition_size);
}
break;
case GRUB_EFI_VENDOR_MEDIA_DEVICE_PATH_SUBTYPE:
dump_vendor_path ("Media",
(grub_efi_vendor_device_path_t *) dp);
break;
case GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE:
{
grub_efi_file_path_device_path_t *fp;
grub_uint8_t *buf;
fp = (grub_efi_file_path_device_path_t *) dp;
buf = grub_malloc ((len - 4) * 2 + 1);
if (buf)
{
grub_efi_char16_t *dup_name = grub_malloc (len - 4);
if (!dup_name)
{
grub_errno = GRUB_ERR_NONE;
grub_printf ("/File((null))");
grub_free (buf);
break;
}
*grub_utf16_to_utf8 (buf, grub_memcpy (dup_name, fp->path_name, len - 4),
(len - 4) / sizeof (grub_efi_char16_t))
= '\0';
grub_free (dup_name);
}
else
grub_errno = GRUB_ERR_NONE;
grub_printf ("/File(%s)", buf);
grub_free (buf);
}
break;
case GRUB_EFI_PROTOCOL_DEVICE_PATH_SUBTYPE:
{
grub_efi_protocol_device_path_t *proto
= (grub_efi_protocol_device_path_t *) dp;
grub_printf ("/Protocol(%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x)",
(unsigned) proto->guid.data1,
(unsigned) proto->guid.data2,
(unsigned) proto->guid.data3,
(unsigned) proto->guid.data4[0],
(unsigned) proto->guid.data4[1],
(unsigned) proto->guid.data4[2],
(unsigned) proto->guid.data4[3],
(unsigned) proto->guid.data4[4],
(unsigned) proto->guid.data4[5],
(unsigned) proto->guid.data4[6],
(unsigned) proto->guid.data4[7]);
}
break;
default:
grub_printf ("/UnknownMedia(%x)", (unsigned) subtype);
break;
}
break;
case GRUB_EFI_BIOS_DEVICE_PATH_TYPE:
switch (subtype)
{
case GRUB_EFI_BIOS_DEVICE_PATH_SUBTYPE:
{
grub_efi_bios_device_path_t *bios
= (grub_efi_bios_device_path_t *) dp;
grub_printf ("/BIOS(%x,%x,%s)",
(unsigned) bios->device_type,
(unsigned) bios->status_flags,
(char *) (dp + 1));
}
break;
default:
grub_printf ("/UnknownBIOS(%x)", (unsigned) subtype);
break;
}
break;
default:
grub_printf ("/UnknownType(%x,%x)\n",
(unsigned) type,
(unsigned) subtype);
return;
break;
}
if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp))
break;
dp = (grub_efi_device_path_t *) ((char *) dp + len);
}
}
/* Compare device paths. */
int
grub_efi_compare_device_paths (const grub_efi_device_path_t *dp1,
const grub_efi_device_path_t *dp2)
{
if (! dp1 || ! dp2)
/* Return non-zero. */
return 1;
while (1)
{
grub_efi_uint8_t type1, type2;
grub_efi_uint8_t subtype1, subtype2;
grub_efi_uint16_t len1, len2;
int ret;
type1 = GRUB_EFI_DEVICE_PATH_TYPE (dp1);
type2 = GRUB_EFI_DEVICE_PATH_TYPE (dp2);
if (type1 != type2)
return (int) type2 - (int) type1;
subtype1 = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp1);
subtype2 = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp2);
if (subtype1 != subtype2)
return (int) subtype1 - (int) subtype2;
len1 = GRUB_EFI_DEVICE_PATH_LENGTH (dp1);
len2 = GRUB_EFI_DEVICE_PATH_LENGTH (dp2);
if (len1 != len2)
return (int) len1 - (int) len2;
ret = grub_memcmp (dp1, dp2, len1);
if (ret != 0)
return ret;
if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp1))
break;
dp1 = (grub_efi_device_path_t *) ((char *) dp1 + len1);
dp2 = (grub_efi_device_path_t *) ((char *) dp2 + len2);
}
return 0;
}
void * grub_efi_allocate_iso_buf(grub_uint64_t size)
{
grub_efi_boot_services_t *b;
grub_efi_status_t status;
grub_efi_physical_address_t address = 0;
grub_efi_uintn_t pages = GRUB_EFI_BYTES_TO_PAGES(size);
b = grub_efi_system_table->boot_services;
status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_ANY_PAGES, GRUB_EFI_RUNTIME_SERVICES_DATA, pages, &address);
if (status != GRUB_EFI_SUCCESS)
{
return NULL;
}
return (void *)(unsigned long)address;
}

View File

@@ -0,0 +1,256 @@
/* file.c - file I/O functions */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2002,2006,2007,2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/misc.h>
#include <grub/err.h>
#include <grub/file.h>
#include <grub/net.h>
#include <grub/mm.h>
#include <grub/fs.h>
#include <grub/device.h>
#include <grub/i18n.h>
void (*EXPORT_VAR (grub_grubnet_fini)) (void);
grub_file_filter_t grub_file_filters[GRUB_FILE_FILTER_MAX];
/* Get the device part of the filename NAME. It is enclosed by parentheses. */
char *
grub_file_get_device_name (const char *name)
{
if (name[0] == '(')
{
char *p = grub_strchr (name, ')');
char *ret;
if (! p)
{
grub_error (GRUB_ERR_BAD_FILENAME, N_("missing `%c' symbol"), ')');
return 0;
}
ret = (char *) grub_malloc (p - name);
if (! ret)
return 0;
grub_memcpy (ret, name + 1, p - name - 1);
ret[p - name - 1] = '\0';
return ret;
}
return 0;
}
/* Support mem:xxx:size:xxx format in chainloader */
grub_file_t grub_memfile_open(const char *name);
#define GRUB_MEMFILE_MEM "mem:"
#define GRUB_MEMFILE_SIZE "size:"
grub_file_t grub_memfile_open(const char *name)
{
char *size = NULL;
grub_file_t file = 0;
file = (grub_file_t)grub_zalloc(sizeof(*file));
if (NULL == file)
{
return 0;
}
file->name = grub_strdup(name);
file->data = (void *)grub_strtoul(name + grub_strlen(GRUB_MEMFILE_MEM), NULL, 0);
size = grub_strstr(name, GRUB_MEMFILE_SIZE);
file->size = (grub_off_t)grub_strtoul(size + grub_strlen(GRUB_MEMFILE_SIZE), NULL, 0);
grub_errno = GRUB_ERR_NONE;
return file;
}
grub_file_t
grub_file_open (const char *name, enum grub_file_type type)
{
grub_device_t device = 0;
grub_file_t file = 0, last_file = 0;
char *device_name;
const char *file_name;
grub_file_filter_id_t filter;
/* <DESC> : mem:xxx:size:xxx format in chainloader */
if (grub_strncmp(name, GRUB_MEMFILE_MEM, grub_strlen(GRUB_MEMFILE_MEM)) == 0) {
return grub_memfile_open(name);
}
device_name = grub_file_get_device_name (name);
if (grub_errno)
goto fail;
/* Get the file part of NAME. */
file_name = (name[0] == '(') ? grub_strchr (name, ')') : NULL;
if (file_name)
file_name++;
else
file_name = name;
device = grub_device_open (device_name);
grub_free (device_name);
if (! device)
goto fail;
file = (grub_file_t) grub_zalloc (sizeof (*file));
if (! file)
goto fail;
file->device = device;
/* In case of relative pathnames and non-Unix systems (like Windows)
* name of host files may not start with `/'. Blocklists for host files
* are meaningless as well (for a start, host disk does not allow any direct
* access - it is just a marker). So skip host disk in this case.
*/
if (device->disk && file_name[0] != '/'
#if defined(GRUB_UTIL) || defined(GRUB_MACHINE_EMU)
&& grub_strcmp (device->disk->name, "host")
#endif
)
/* This is a block list. */
file->fs = &grub_fs_blocklist;
else
{
file->fs = grub_fs_probe (device);
if (! file->fs)
goto fail;
}
if ((file->fs->fs_open) (file, file_name) != GRUB_ERR_NONE)
goto fail;
file->name = grub_strdup (name);
grub_errno = GRUB_ERR_NONE;
for (filter = 0; file && filter < ARRAY_SIZE (grub_file_filters);
filter++)
if (grub_file_filters[filter])
{
last_file = file;
file = grub_file_filters[filter] (file, type);
if (file && file != last_file)
{
file->name = grub_strdup (name);
grub_errno = GRUB_ERR_NONE;
}
}
if (!file)
grub_file_close (last_file);
return file;
fail:
if (device)
grub_device_close (device);
/* if (net) grub_net_close (net); */
grub_free (file);
return 0;
}
grub_disk_read_hook_t grub_file_progress_hook;
grub_ssize_t
grub_file_read (grub_file_t file, void *buf, grub_size_t len)
{
grub_ssize_t res;
grub_disk_read_hook_t read_hook;
void *read_hook_data;
if (file->offset > file->size)
{
grub_error (GRUB_ERR_OUT_OF_RANGE,
N_("attempt to read past the end of file"));
return -1;
}
if (len == 0)
return 0;
if (len > file->size - file->offset)
len = file->size - file->offset;
/* Prevent an overflow. */
if ((grub_ssize_t) len < 0)
len >>= 1;
if (len == 0)
return 0;
if (grub_strncmp(file->name, GRUB_MEMFILE_MEM, grub_strlen(GRUB_MEMFILE_MEM)) == 0) {
grub_memcpy(buf, (grub_uint8_t *)(file->data) + file->offset, len);
file->offset += len;
return len;
}
read_hook = file->read_hook;
read_hook_data = file->read_hook_data;
if (!file->read_hook)
{
file->read_hook = grub_file_progress_hook;
file->read_hook_data = file;
file->progress_offset = file->offset;
}
res = (file->fs->fs_read) (file, buf, len);
file->read_hook = read_hook;
file->read_hook_data = read_hook_data;
if (res > 0)
file->offset += res;
return res;
}
grub_err_t
grub_file_close (grub_file_t file)
{
if (file->fs && file->fs->fs_close)
(file->fs->fs_close) (file);
if (file->device)
grub_device_close (file->device);
grub_free (file->name);
grub_free (file);
return grub_errno;
}
grub_off_t
grub_file_seek (grub_file_t file, grub_off_t offset)
{
grub_off_t old;
if (offset > file->size)
{
grub_error (GRUB_ERR_OUT_OF_RANGE,
N_("attempt to seek outside of the file"));
return -1;
}
old = file->offset;
file->offset = offset;
return old;
}

View File

@@ -0,0 +1,304 @@
/* fs.c - filesystem manager */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2002,2005,2007 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/disk.h>
#include <grub/net.h>
#include <grub/fs.h>
#include <grub/file.h>
#include <grub/err.h>
#include <grub/misc.h>
#include <grub/types.h>
#include <grub/mm.h>
#include <grub/term.h>
#include <grub/i18n.h>
#include <grub/env.h>
grub_fs_t grub_fs_list = 0;
grub_fs_autoload_hook_t grub_fs_autoload_hook = 0;
/* Helper for grub_fs_probe. */
static int
probe_dummy_iter (const char *filename __attribute__ ((unused)),
const struct grub_dirhook_info *info __attribute__ ((unused)),
void *data __attribute__ ((unused)))
{
return 1;
}
grub_fs_t
grub_fs_probe (grub_device_t device)
{
grub_fs_t p;
const char *first_probe;
grub_size_t len;
if (device->disk)
{
/* Make it sure not to have an infinite recursive calls. */
static int count = 0;
first_probe = grub_env_get("ventoy_fs_probe");
if (!first_probe)
{
first_probe = "iso9660";
}
len = grub_strlen(first_probe);
/* use iso9660 first */
for (p = grub_fs_list; p; p = p->next)
{
if (grub_strncmp(p->name, first_probe, len) == 0)
{
break;
}
}
if (p)
{
grub_dprintf ("fs", "Detecting %s...\n", p->name);
/* This is evil: newly-created just mounted BtrFS after copying all
GRUB files has a very peculiar unrecoverable corruption which
will be fixed at sync but we'd rather not do a global sync and
syncing just files doesn't seem to help. Relax the check for
this time. */
#ifdef GRUB_UTIL
if (grub_strcmp (p->name, "btrfs") == 0)
{
char *label = 0;
p->fs_uuid (device, &label);
if (label)
grub_free (label);
}
else
#endif
(p->fs_dir) (device, "/", probe_dummy_iter, NULL);
if (grub_errno == GRUB_ERR_NONE)
return p;
grub_error_push ();
grub_dprintf ("fs", "%s detection failed.\n", p->name);
grub_error_pop ();
if (grub_errno != GRUB_ERR_BAD_FS
&& grub_errno != GRUB_ERR_OUT_OF_RANGE)
return 0;
grub_errno = GRUB_ERR_NONE;
}
for (p = grub_fs_list; p; p = p->next)
{
grub_dprintf ("fs", "Detecting %s...\n", p->name);
/* This is evil: newly-created just mounted BtrFS after copying all
GRUB files has a very peculiar unrecoverable corruption which
will be fixed at sync but we'd rather not do a global sync and
syncing just files doesn't seem to help. Relax the check for
this time. */
#ifdef GRUB_UTIL
if (grub_strcmp (p->name, "btrfs") == 0)
{
char *label = 0;
p->fs_uuid (device, &label);
if (label)
grub_free (label);
}
else
#endif
(p->fs_dir) (device, "/", probe_dummy_iter, NULL);
if (grub_errno == GRUB_ERR_NONE)
return p;
grub_error_push ();
grub_dprintf ("fs", "%s detection failed.\n", p->name);
grub_error_pop ();
if (grub_errno != GRUB_ERR_BAD_FS
&& grub_errno != GRUB_ERR_OUT_OF_RANGE)
return 0;
grub_errno = GRUB_ERR_NONE;
}
/* Let's load modules automatically. */
if (grub_fs_autoload_hook && count == 0)
{
count++;
while (grub_fs_autoload_hook ())
{
p = grub_fs_list;
(p->fs_dir) (device, "/", probe_dummy_iter, NULL);
if (grub_errno == GRUB_ERR_NONE)
{
count--;
return p;
}
if (grub_errno != GRUB_ERR_BAD_FS
&& grub_errno != GRUB_ERR_OUT_OF_RANGE)
{
count--;
return 0;
}
grub_errno = GRUB_ERR_NONE;
}
count--;
}
}
else if (device->net && device->net->fs)
return device->net->fs;
grub_error (GRUB_ERR_UNKNOWN_FS, N_("unknown filesystem"));
return 0;
}
/* Block list support routines. */
struct grub_fs_block
{
grub_disk_addr_t offset;
unsigned long length;
};
static grub_err_t
grub_fs_blocklist_open (grub_file_t file, const char *name)
{
char *p = (char *) name;
unsigned num = 0;
unsigned i;
grub_disk_t disk = file->device->disk;
struct grub_fs_block *blocks;
/* First, count the number of blocks. */
do
{
num++;
p = grub_strchr (p, ',');
if (p)
p++;
}
while (p);
/* Allocate a block list. */
blocks = grub_zalloc (sizeof (struct grub_fs_block) * (num + 1));
if (! blocks)
return 0;
file->size = 0;
p = (char *) name;
for (i = 0; i < num; i++)
{
if (*p != '+')
{
blocks[i].offset = grub_strtoull (p, &p, 0);
if (grub_errno != GRUB_ERR_NONE || *p != '+')
{
grub_error (GRUB_ERR_BAD_FILENAME,
N_("invalid file name `%s'"), name);
goto fail;
}
}
p++;
blocks[i].length = grub_strtoul (p, &p, 0);
if (grub_errno != GRUB_ERR_NONE
|| blocks[i].length == 0
|| (*p && *p != ',' && ! grub_isspace (*p)))
{
grub_error (GRUB_ERR_BAD_FILENAME,
N_("invalid file name `%s'"), name);
goto fail;
}
if (disk->total_sectors < blocks[i].offset + blocks[i].length)
{
grub_error (GRUB_ERR_BAD_FILENAME, "beyond the total sectors");
goto fail;
}
file->size += (blocks[i].length << GRUB_DISK_SECTOR_BITS);
p++;
}
file->data = blocks;
return GRUB_ERR_NONE;
fail:
grub_free (blocks);
return grub_errno;
}
static grub_ssize_t
grub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len)
{
struct grub_fs_block *p;
grub_disk_addr_t sector;
grub_off_t offset;
grub_ssize_t ret = 0;
if (len > file->size - file->offset)
len = file->size - file->offset;
sector = (file->offset >> GRUB_DISK_SECTOR_BITS);
offset = (file->offset & (GRUB_DISK_SECTOR_SIZE - 1));
for (p = file->data; p->length && len > 0; p++)
{
if (sector < p->length)
{
grub_size_t size;
size = len;
if (((size + offset + GRUB_DISK_SECTOR_SIZE - 1)
>> GRUB_DISK_SECTOR_BITS) > p->length - sector)
size = ((p->length - sector) << GRUB_DISK_SECTOR_BITS) - offset;
if (grub_disk_read (file->device->disk, p->offset + sector, offset,
size, buf) != GRUB_ERR_NONE)
return -1;
ret += size;
len -= size;
sector -= ((size + offset) >> GRUB_DISK_SECTOR_BITS);
offset = ((size + offset) & (GRUB_DISK_SECTOR_SIZE - 1));
}
else
sector -= p->length;
}
return ret;
}
struct grub_fs grub_fs_blocklist =
{
.name = "blocklist",
.fs_dir = 0,
.fs_open = grub_fs_blocklist_open,
.fs_read = grub_fs_blocklist_read,
.fs_close = 0,
.next = 0
};

View File

@@ -0,0 +1,312 @@
/* main.c - the kernel main routine */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2002,2003,2005,2006,2008,2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/kernel.h>
#include <grub/misc.h>
#include <grub/symbol.h>
#include <grub/dl.h>
#include <grub/term.h>
#include <grub/file.h>
#include <grub/device.h>
#include <grub/env.h>
#include <grub/mm.h>
#include <grub/command.h>
#include <grub/reader.h>
#include <grub/parser.h>
#ifdef GRUB_MACHINE_PCBIOS
#include <grub/machine/memory.h>
#endif
grub_addr_t
grub_modules_get_end (void)
{
struct grub_module_info *modinfo;
modinfo = (struct grub_module_info *) grub_modbase;
/* Check if there are any modules. */
if ((modinfo == 0) || modinfo->magic != GRUB_MODULE_MAGIC)
return grub_modbase;
return grub_modbase + modinfo->size;
}
/* Load all modules in core. */
static void
grub_load_modules (void)
{
struct grub_module_header *header;
FOR_MODULES (header)
{
/* Not an ELF module, skip. */
if (header->type != OBJ_TYPE_ELF)
continue;
if (! grub_dl_load_core ((char *) header + sizeof (struct grub_module_header),
(header->size - sizeof (struct grub_module_header))))
grub_fatal ("%s", grub_errmsg);
if (grub_errno)
grub_print_error ();
}
}
static char *load_config;
static void
grub_load_config (void)
{
struct grub_module_header *header;
FOR_MODULES (header)
{
/* Not an embedded config, skip. */
if (header->type != OBJ_TYPE_CONFIG)
continue;
load_config = grub_malloc (header->size - sizeof (struct grub_module_header) + 1);
if (!load_config)
{
grub_print_error ();
break;
}
grub_memcpy (load_config, (char *) header +
sizeof (struct grub_module_header),
header->size - sizeof (struct grub_module_header));
load_config[header->size - sizeof (struct grub_module_header)] = 0;
break;
}
}
/* Write hook for the environment variables of root. Remove surrounding
parentheses, if any. */
static char *
grub_env_write_root (struct grub_env_var *var __attribute__ ((unused)),
const char *val)
{
/* XXX Is it better to check the existence of the device? */
grub_size_t len = grub_strlen (val);
if (val[0] == '(' && val[len - 1] == ')')
return grub_strndup (val + 1, len - 2);
return grub_strdup (val);
}
static void
grub_set_prefix_and_root (void)
{
char *device = NULL;
char *path = NULL;
char *fwdevice = NULL;
char *fwpath = NULL;
char *prefix = NULL;
struct grub_module_header *header;
FOR_MODULES (header)
if (header->type == OBJ_TYPE_PREFIX)
prefix = (char *) header + sizeof (struct grub_module_header);
grub_register_variable_hook ("root", 0, grub_env_write_root);
grub_machine_get_bootlocation (&fwdevice, &fwpath);
if (fwdevice)
{
char *cmdpath;
cmdpath = grub_xasprintf ("(%s)%s", fwdevice, fwpath ? : "");
if (cmdpath)
{
grub_env_set ("cmdpath", cmdpath);
grub_env_export ("cmdpath");
grub_free (cmdpath);
}
}
if (prefix)
{
char *pptr = NULL;
if (prefix[0] == '(')
{
pptr = grub_strrchr (prefix, ')');
if (pptr)
{
device = grub_strndup (prefix + 1, pptr - prefix - 1);
pptr++;
}
}
if (!pptr)
pptr = prefix;
if (pptr[0])
path = grub_strdup (pptr);
}
if (!device && fwdevice)
device = fwdevice;
else if (fwdevice && (device[0] == ',' || !device[0]))
{
/* We have a partition, but still need to fill in the drive. */
char *comma, *new_device;
for (comma = fwdevice; *comma; )
{
if (comma[0] == '\\' && comma[1] == ',')
{
comma += 2;
continue;
}
if (*comma == ',')
break;
comma++;
}
if (*comma)
{
char *drive = grub_strndup (fwdevice, comma - fwdevice);
new_device = grub_xasprintf ("%s%s", drive, device);
grub_free (drive);
}
else
new_device = grub_xasprintf ("%s%s", fwdevice, device);
grub_free (fwdevice);
grub_free (device);
device = new_device;
}
else
grub_free (fwdevice);
if (fwpath && !path)
{
grub_size_t len = grub_strlen (fwpath);
while (len > 1 && fwpath[len - 1] == '/')
fwpath[--len] = 0;
if (len >= sizeof (GRUB_TARGET_CPU "-" GRUB_PLATFORM) - 1
&& grub_memcmp (fwpath + len - (sizeof (GRUB_TARGET_CPU "-" GRUB_PLATFORM) - 1), GRUB_TARGET_CPU "-" GRUB_PLATFORM,
sizeof (GRUB_TARGET_CPU "-" GRUB_PLATFORM) - 1) == 0)
fwpath[len - (sizeof (GRUB_TARGET_CPU "-" GRUB_PLATFORM) - 1)] = 0;
path = fwpath;
}
else
grub_free (fwpath);
if (device)
{
char *prefix_set;
prefix_set = grub_xasprintf ("(%s)%s", device, path ? : "");
if (prefix_set)
{
grub_env_set ("prefix", prefix_set);
grub_free (prefix_set);
}
grub_env_set ("root", device);
}
grub_free (device);
grub_free (path);
grub_print_error ();
}
/* Load the normal mode module and execute the normal mode if possible. */
static void
grub_load_normal_mode (void)
{
/* Load the module. */
grub_dl_load ("normal");
/* Print errors if any. */
grub_print_error ();
grub_errno = 0;
grub_command_execute ("normal", 0, 0);
}
static void
reclaim_module_space (void)
{
grub_addr_t modstart, modend;
if (!grub_modbase)
return;
#ifdef GRUB_MACHINE_PCBIOS
modstart = GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR;
#else
modstart = grub_modbase;
#endif
modend = grub_modules_get_end ();
grub_modbase = 0;
#if GRUB_KERNEL_PRELOAD_SPACE_REUSABLE
grub_mm_init_region ((void *) modstart, modend - modstart);
#else
(void) modstart;
(void) modend;
#endif
}
/* The main routine. */
void __attribute__ ((noreturn))
grub_main (void)
{
/* First of all, initialize the machine. */
grub_machine_init ();
grub_boot_time ("After machine init.");
/* Hello. */
grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT);
//grub_printf ("Welcome to GRUB!\n\n");
grub_setcolorstate (GRUB_TERM_COLOR_STANDARD);
grub_load_config ();
grub_boot_time ("Before loading embedded modules.");
/* Load pre-loaded modules and free the space. */
grub_register_exported_symbols ();
#ifdef GRUB_LINKER_HAVE_INIT
grub_arch_dl_init_linker ();
#endif
grub_load_modules ();
grub_boot_time ("After loading embedded modules.");
/* It is better to set the root device as soon as possible,
for convenience. */
grub_set_prefix_and_root ();
grub_env_export ("root");
grub_env_export ("prefix");
/* Reclaim space used for modules. */
reclaim_module_space ();
grub_boot_time ("After reclaiming module space.");
grub_register_core_commands ();
grub_boot_time ("Before execution of embedded config.");
if (load_config)
grub_parser_execute (load_config);
grub_boot_time ("After execution of embedded config. Attempt to go to normal mode");
grub_load_normal_mode ();
grub_rescue_run ();
}

View File

@@ -0,0 +1,118 @@
/* cmdline.c - linux command line handling */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2010 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/lib/cmdline.h>
#include <grub/misc.h>
static unsigned int check_arg (char *c, int *has_space)
{
int space = 0;
unsigned int size = 0;
while (*c)
{
if (*c == '\\' || *c == '\'' || *c == '"')
size++;
else if (*c == ' ')
space = 1;
size++;
c++;
}
if (space)
size += 2;
if (has_space)
*has_space = space;
return size;
}
unsigned int grub_loader_cmdline_size (int argc, char *argv[])
{
int i;
unsigned int size = 0;
for (i = 0; i < argc; i++)
{
size += check_arg (argv[i], 0);
size++; /* Separator space or NULL. */
}
if (size == 0)
size = 1;
return size;
}
grub_err_t
grub_create_loader_cmdline (int argc, char *argv[], char *buf,
grub_size_t size, enum grub_verify_string_type type)
{
int i, space;
unsigned int arg_size;
char *c, *orig_buf = buf;
for (i = 0; i < argc; i++)
{
c = argv[i];
arg_size = check_arg(argv[i], &space);
arg_size++; /* Separator space or NULL. */
if (size < arg_size)
break;
size -= arg_size;
if (space)
*buf++ = '"';
while (*c)
{
if (*c == '\\' && *(c+1) == 'x' &&
grub_isxdigit(*(c+2)) && grub_isxdigit(*(c+3)))
{
*buf++ = *c++;
*buf++ = *c++;
*buf++ = *c++;
*buf++ = *c++;
continue;
}
else if (*c == '\\' || *c == '\'' || *c == '"')
*buf++ = '\\';
*buf++ = *c;
c++;
}
if (space)
*buf++ = '"';
*buf++ = ' ';
}
/* Replace last space with null. */
if (i)
buf--;
*buf = 0;
return grub_verify_string (orig_buf, type);
}

View File

@@ -0,0 +1,428 @@
/* chainloader.c - boot another boot loader */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2002,2004,2006,2007,2008 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
/* TODO: support load options. */
#include <grub/loader.h>
#include <grub/file.h>
#include <grub/err.h>
#include <grub/device.h>
#include <grub/disk.h>
#include <grub/misc.h>
#include <grub/charset.h>
#include <grub/mm.h>
#include <grub/types.h>
#include <grub/dl.h>
#include <grub/efi/api.h>
#include <grub/efi/efi.h>
#include <grub/efi/disk.h>
#include <grub/command.h>
#include <grub/i18n.h>
#include <grub/net.h>
#if defined (__i386__) || defined (__x86_64__)
#include <grub/macho.h>
#include <grub/i386/macho.h>
#endif
GRUB_MOD_LICENSE ("GPLv3+");
static grub_dl_t my_mod;
static grub_efi_physical_address_t address;
static grub_efi_uintn_t pages;
static grub_efi_device_path_t *file_path;
static grub_efi_handle_t image_handle;
static grub_efi_char16_t *cmdline;
static grub_err_t
grub_chainloader_unload (void)
{
grub_efi_boot_services_t *b;
b = grub_efi_system_table->boot_services;
efi_call_1 (b->unload_image, image_handle);
efi_call_2 (b->free_pages, address, pages);
grub_free (file_path);
grub_free (cmdline);
cmdline = 0;
file_path = 0;
grub_dl_unref (my_mod);
return GRUB_ERR_NONE;
}
static grub_err_t
grub_chainloader_boot (void)
{
grub_efi_boot_services_t *b;
grub_efi_status_t status;
grub_efi_uintn_t exit_data_size;
grub_efi_char16_t *exit_data = NULL;
b = grub_efi_system_table->boot_services;
status = efi_call_3 (b->start_image, image_handle, &exit_data_size, &exit_data);
if (status != GRUB_EFI_SUCCESS)
{
if (exit_data)
{
char *buf;
buf = grub_malloc (exit_data_size * 4 + 1);
if (buf)
{
*grub_utf16_to_utf8 ((grub_uint8_t *) buf,
exit_data, exit_data_size) = 0;
grub_error (GRUB_ERR_BAD_OS, buf);
grub_free (buf);
}
}
else
grub_error (GRUB_ERR_BAD_OS, "unknown error");
}
if (exit_data)
efi_call_1 (b->free_pool, exit_data);
grub_loader_unset ();
return grub_errno;
}
static void
copy_file_path (grub_efi_file_path_device_path_t *fp,
const char *str, grub_efi_uint16_t len)
{
grub_efi_char16_t *p, *path_name;
grub_efi_uint16_t size;
fp->header.type = GRUB_EFI_MEDIA_DEVICE_PATH_TYPE;
fp->header.subtype = GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE;
path_name = grub_malloc (len * GRUB_MAX_UTF16_PER_UTF8 * sizeof (*path_name));
if (!path_name)
return;
size = grub_utf8_to_utf16 (path_name, len * GRUB_MAX_UTF16_PER_UTF8,
(const grub_uint8_t *) str, len, 0);
for (p = path_name; p < path_name + size; p++)
if (*p == '/')
*p = '\\';
grub_memcpy (fp->path_name, path_name, size * sizeof (*fp->path_name));
/* File Path is NULL terminated */
fp->path_name[size++] = '\0';
fp->header.length = size * sizeof (grub_efi_char16_t) + sizeof (*fp);
grub_free (path_name);
}
static grub_efi_device_path_t *
make_file_path (grub_efi_device_path_t *dp, const char *filename)
{
char *dir_start;
char *dir_end;
grub_size_t size;
grub_efi_device_path_t *d;
dir_start = grub_strchr (filename, ')');
if (! dir_start)
dir_start = (char *) filename;
else
dir_start++;
dir_end = grub_strrchr (dir_start, '/');
if (! dir_end)
{
grub_error (GRUB_ERR_BAD_FILENAME, "invalid EFI file path");
return 0;
}
size = 0;
d = dp;
while (1)
{
size += GRUB_EFI_DEVICE_PATH_LENGTH (d);
if ((GRUB_EFI_END_ENTIRE_DEVICE_PATH (d)))
break;
d = GRUB_EFI_NEXT_DEVICE_PATH (d);
}
/* File Path is NULL terminated. Allocate space for 2 extra characters */
/* FIXME why we split path in two components? */
file_path = grub_malloc (size
+ ((grub_strlen (dir_start) + 2)
* GRUB_MAX_UTF16_PER_UTF8
* sizeof (grub_efi_char16_t))
+ sizeof (grub_efi_file_path_device_path_t) * 2);
if (! file_path)
return 0;
grub_memcpy (file_path, dp, size);
/* Fill the file path for the directory. */
d = (grub_efi_device_path_t *) ((char *) file_path
+ ((char *) d - (char *) dp));
//grub_efi_print_device_path (d);
copy_file_path ((grub_efi_file_path_device_path_t *) d,
dir_start, dir_end - dir_start);
/* Fill the file path for the file. */
d = GRUB_EFI_NEXT_DEVICE_PATH (d);
copy_file_path ((grub_efi_file_path_device_path_t *) d,
dir_end + 1, grub_strlen (dir_end + 1));
/* Fill the end of device path nodes. */
d = GRUB_EFI_NEXT_DEVICE_PATH (d);
d->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
d->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
d->length = sizeof (*d);
return file_path;
}
static grub_err_t
grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
grub_file_t file = 0;
grub_ssize_t size;
grub_efi_status_t status;
grub_efi_boot_services_t *b;
grub_device_t dev = 0;
grub_efi_device_path_t *dp = 0;
grub_efi_loaded_image_t *loaded_image;
char *filename;
void *boot_image = 0;
grub_efi_handle_t dev_handle = 0;
if (argc == 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
filename = argv[0];
grub_dl_ref (my_mod);
/* Initialize some global variables. */
address = 0;
image_handle = 0;
file_path = 0;
b = grub_efi_system_table->boot_services;
file = grub_file_open (filename, GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE);
if (! file)
goto fail;
/* Get the root device's device path. */
dev = grub_device_open (0);
if (! dev)
goto fail;
if (dev->disk)
dev_handle = grub_efidisk_get_device_handle (dev->disk);
else if (dev->net && dev->net->server)
{
grub_net_network_level_address_t addr;
struct grub_net_network_level_interface *inf;
grub_net_network_level_address_t gateway;
grub_err_t err;
err = grub_net_resolve_address (dev->net->server, &addr);
if (err)
goto fail;
err = grub_net_route_address (addr, &gateway, &inf);
if (err)
goto fail;
dev_handle = grub_efinet_get_device_handle (inf->card);
}
if (dev_handle)
dp = grub_efi_get_device_path (dev_handle);
if (! dp)
{
grub_error (GRUB_ERR_BAD_DEVICE, "not a valid root device");
goto fail;
}
file_path = make_file_path (dp, filename);
if (! file_path)
goto fail;
//grub_printf ("file path: ");
//grub_efi_print_device_path (file_path);
size = grub_file_size (file);
if (!size)
{
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
filename);
goto fail;
}
pages = (((grub_efi_uintn_t) size + ((1 << 12) - 1)) >> 12);
status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_ANY_PAGES,
GRUB_EFI_LOADER_CODE,
pages, &address);
if (status != GRUB_EFI_SUCCESS)
{
grub_dprintf ("chain", "Failed to allocate %u pages\n",
(unsigned int) pages);
grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
goto fail;
}
boot_image = (void *) ((grub_addr_t) address);
if (grub_file_read (file, boot_image, size) != size)
{
if (grub_errno == GRUB_ERR_NONE)
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
filename);
goto fail;
}
#if defined (__i386__) || defined (__x86_64__)
if (size >= (grub_ssize_t) sizeof (struct grub_macho_fat_header))
{
struct grub_macho_fat_header *head = boot_image;
if (head->magic
== grub_cpu_to_le32_compile_time (GRUB_MACHO_FAT_EFI_MAGIC))
{
grub_uint32_t i;
struct grub_macho_fat_arch *archs
= (struct grub_macho_fat_arch *) (head + 1);
for (i = 0; i < grub_cpu_to_le32 (head->nfat_arch); i++)
{
if (GRUB_MACHO_CPUTYPE_IS_HOST_CURRENT (archs[i].cputype))
break;
}
if (i == grub_cpu_to_le32 (head->nfat_arch))
{
grub_error (GRUB_ERR_BAD_OS, "no compatible arch found");
goto fail;
}
if (grub_cpu_to_le32 (archs[i].offset)
> ~grub_cpu_to_le32 (archs[i].size)
|| grub_cpu_to_le32 (archs[i].offset)
+ grub_cpu_to_le32 (archs[i].size)
> (grub_size_t) size)
{
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
filename);
goto fail;
}
boot_image = (char *) boot_image + grub_cpu_to_le32 (archs[i].offset);
size = grub_cpu_to_le32 (archs[i].size);
}
}
#endif
status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path,
boot_image, size,
&image_handle);
if (status != GRUB_EFI_SUCCESS)
{
if (status == GRUB_EFI_OUT_OF_RESOURCES)
grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of resources");
else
grub_error (GRUB_ERR_BAD_OS, "cannot load image");
goto fail;
}
/* LoadImage does not set a device handler when the image is
loaded from memory, so it is necessary to set it explicitly here.
This is a mess. */
loaded_image = grub_efi_get_loaded_image (image_handle);
if (! loaded_image)
{
grub_error (GRUB_ERR_BAD_OS, "no loaded image available");
goto fail;
}
loaded_image->device_handle = dev_handle;
if (argc > 1)
{
int i, len;
grub_efi_char16_t *p16;
for (i = 1, len = 0; i < argc; i++)
len += grub_strlen (argv[i]) + 1;
len *= sizeof (grub_efi_char16_t);
cmdline = p16 = grub_malloc (len);
if (! cmdline)
goto fail;
for (i = 1; i < argc; i++)
{
char *p8;
p8 = argv[i];
while (*p8)
*(p16++) = *(p8++);
*(p16++) = ' ';
}
*(--p16) = 0;
loaded_image->load_options = cmdline;
loaded_image->load_options_size = len;
}
grub_file_close (file);
grub_device_close (dev);
grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);
return 0;
fail:
if (dev)
grub_device_close (dev);
if (file)
grub_file_close (file);
grub_free (file_path);
if (address)
efi_call_2 (b->free_pages, address, pages);
grub_dl_unref (my_mod);
return grub_errno;
}
static grub_command_t cmd;
GRUB_MOD_INIT(chainloader)
{
cmd = grub_register_command ("chainloader", grub_cmd_chainloader,
0, N_("Load another boot loader."));
my_mod = mod;
}
GRUB_MOD_FINI(chainloader)
{
grub_unregister_command (cmd);
}

View File

@@ -0,0 +1,214 @@
/* env.c - Environment variables */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2003,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/env.h>
#include <grub/env_private.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/command.h>
#include <grub/normal.h>
#include <grub/i18n.h>
struct menu_pointer
{
grub_menu_t menu;
struct menu_pointer *prev;
};
static struct menu_pointer initial_menu;
static struct menu_pointer *current_menu = &initial_menu;
void
grub_env_unset_menu (void)
{
current_menu->menu = NULL;
}
grub_menu_t
grub_env_get_menu (void)
{
return current_menu->menu;
}
void
grub_env_set_menu (grub_menu_t nmenu)
{
current_menu->menu = nmenu;
}
static grub_err_t
grub_env_new_context (int export_all)
{
struct grub_env_context *context;
int i;
struct menu_pointer *menu;
context = grub_zalloc (sizeof (*context));
if (! context)
return grub_errno;
menu = grub_zalloc (sizeof (*menu));
if (! menu)
{
grub_free (context);
return grub_errno;
}
context->prev = grub_current_context;
grub_current_context = context;
menu->prev = current_menu;
current_menu = menu;
/* Copy exported variables. */
for (i = 0; i < HASHSZ; i++)
{
struct grub_env_var *var;
for (var = context->prev->vars[i]; var; var = var->next)
if (var->global || export_all)
{
if (grub_env_set (var->name, var->value) != GRUB_ERR_NONE)
{
grub_env_context_close ();
return grub_errno;
}
grub_env_export (var->name);
grub_register_variable_hook (var->name, var->read_hook, var->write_hook);
}
}
return GRUB_ERR_NONE;
}
grub_err_t
grub_env_context_open (void)
{
return grub_env_new_context (1);
}
int grub_extractor_level = 0;
grub_err_t
grub_env_extractor_open (int source)
{
grub_extractor_level++;
return grub_env_new_context (source);
}
grub_err_t
grub_env_context_close (void)
{
struct grub_env_context *context;
int i;
struct menu_pointer *menu;
if (! grub_current_context->prev)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"cannot close the initial context");
/* Free the variables associated with this context. */
for (i = 0; i < HASHSZ; i++)
{
struct grub_env_var *p, *q;
for (p = grub_current_context->vars[i]; p; p = q)
{
q = p->next;
grub_free (p->name);
grub_free (p->value);
grub_free (p);
}
}
/* Restore the previous context. */
context = grub_current_context->prev;
grub_free (grub_current_context);
grub_current_context = context;
menu = current_menu->prev;
if (current_menu->menu)
grub_normal_free_menu (current_menu->menu);
grub_free (current_menu);
current_menu = menu;
return GRUB_ERR_NONE;
}
grub_err_t
grub_env_extractor_close (int source)
{
grub_menu_t menu = NULL;
grub_menu_entry_t *last;
grub_err_t err;
if (source)
{
menu = grub_env_get_menu ();
grub_env_unset_menu ();
}
err = grub_env_context_close ();
if (source && menu)
{
grub_menu_t menu2;
menu2 = grub_env_get_menu ();
last = &menu2->entry_list;
while (*last)
last = &(*last)->next;
*last = menu->entry_list;
menu2->size += menu->size;
}
grub_extractor_level--;
return err;
}
static grub_command_t export_cmd;
static grub_err_t
grub_cmd_export (struct grub_command *cmd __attribute__ ((unused)),
int argc, char **args)
{
int i;
if (argc < 1)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("one argument expected"));
for (i = 0; i < argc; i++)
grub_env_export (args[i]);
return 0;
}
void
grub_context_init (void)
{
export_cmd = grub_register_command ("export", grub_cmd_export,
N_("ENVVAR [ENVVAR] ..."),
N_("Export variables."));
}
void
grub_context_fini (void)
{
grub_unregister_command (export_cmd);
}

View File

@@ -0,0 +1,998 @@
/* menu.c - General supporting functionality for menus. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2003,2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/normal.h>
#include <grub/misc.h>
#include <grub/loader.h>
#include <grub/mm.h>
#include <grub/time.h>
#include <grub/env.h>
#include <grub/menu_viewer.h>
#include <grub/command.h>
#include <grub/parser.h>
#include <grub/auth.h>
#include <grub/i18n.h>
#include <grub/term.h>
#include <grub/script_sh.h>
#include <grub/gfxterm.h>
#include <grub/dl.h>
#include <grub/env.h>
int g_ventoy_menu_refresh = 0;
int g_ventoy_memdisk_mode = 0;
int g_ventoy_iso_raw = 0;
int g_ventoy_iso_uefi_drv = 0;
int g_ventoy_last_entry = 0;
/* Time to delay after displaying an error message about a default/fallback
entry failing to boot. */
#define DEFAULT_ENTRY_ERROR_DELAY_MS 2500
grub_err_t (*grub_gfxmenu_try_hook) (int entry, grub_menu_t menu,
int nested) = NULL;
enum timeout_style {
TIMEOUT_STYLE_MENU,
TIMEOUT_STYLE_COUNTDOWN,
TIMEOUT_STYLE_HIDDEN
};
struct timeout_style_name {
const char *name;
enum timeout_style style;
} timeout_style_names[] = {
{"menu", TIMEOUT_STYLE_MENU},
{"countdown", TIMEOUT_STYLE_COUNTDOWN},
{"hidden", TIMEOUT_STYLE_HIDDEN},
{NULL, 0}
};
/* Wait until the user pushes any key so that the user
can see what happened. */
void
grub_wait_after_message (void)
{
grub_uint64_t endtime;
grub_xputs ("\n");
grub_printf_ (N_("Press any key to continue..."));
grub_refresh ();
endtime = grub_get_time_ms () + 10000;
while (grub_get_time_ms () < endtime
&& grub_getkey_noblock () == GRUB_TERM_NO_KEY);
grub_xputs ("\n");
}
/* Get a menu entry by its index in the entry list. */
grub_menu_entry_t
grub_menu_get_entry (grub_menu_t menu, int no)
{
grub_menu_entry_t e;
for (e = menu->entry_list; e && no > 0; e = e->next, no--)
;
return e;
}
/* Get the index of a menu entry associated with a given hotkey, or -1. */
static int
get_entry_index_by_hotkey (grub_menu_t menu, int hotkey)
{
grub_menu_entry_t entry;
int i;
for (i = 0, entry = menu->entry_list; i < menu->size;
i++, entry = entry->next)
if (entry->hotkey == hotkey)
return i;
return -1;
}
/* Return the timeout style. If the variable "timeout_style" is not set or
invalid, default to TIMEOUT_STYLE_MENU. */
static enum timeout_style
get_timeout_style (void)
{
const char *val;
struct timeout_style_name *style_name;
val = grub_env_get ("timeout_style");
if (!val)
return TIMEOUT_STYLE_MENU;
for (style_name = timeout_style_names; style_name->name; style_name++)
if (grub_strcmp (style_name->name, val) == 0)
return style_name->style;
return TIMEOUT_STYLE_MENU;
}
/* Return the current timeout. If the variable "timeout" is not set or
invalid, return -1. */
int
grub_menu_get_timeout (void)
{
const char *val;
int timeout;
val = grub_env_get ("timeout");
if (! val)
return -1;
grub_error_push ();
timeout = (int) grub_strtoul (val, 0, 0);
/* If the value is invalid, unset the variable. */
if (grub_errno != GRUB_ERR_NONE)
{
grub_env_unset ("timeout");
grub_errno = GRUB_ERR_NONE;
timeout = -1;
}
grub_error_pop ();
return timeout;
}
/* Set current timeout in the variable "timeout". */
void
grub_menu_set_timeout (int timeout)
{
/* Ignore TIMEOUT if it is zero, because it will be unset really soon. */
if (timeout > 0)
{
char buf[16];
grub_snprintf (buf, sizeof (buf), "%d", timeout);
grub_env_set ("timeout", buf);
}
}
/* Get the first entry number from the value of the environment variable NAME,
which is a space-separated list of non-negative integers. The entry number
which is returned is stripped from the value of NAME. If no entry number
can be found, -1 is returned. */
static int
get_and_remove_first_entry_number (const char *name)
{
const char *val;
char *tail;
int entry;
val = grub_env_get (name);
if (! val)
return -1;
grub_error_push ();
entry = (int) grub_strtoul (val, &tail, 0);
if (grub_errno == GRUB_ERR_NONE)
{
/* Skip whitespace to find the next digit. */
while (*tail && grub_isspace (*tail))
tail++;
grub_env_set (name, tail);
}
else
{
grub_env_unset (name);
grub_errno = GRUB_ERR_NONE;
entry = -1;
}
grub_error_pop ();
return entry;
}
/* Run a menu entry. */
static void
grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot)
{
grub_err_t err = GRUB_ERR_NONE;
int errs_before;
grub_menu_t menu = NULL;
char *optr, *buf, *oldchosen = NULL, *olddefault = NULL;
const char *ptr, *chosen, *def;
grub_size_t sz = 0;
if (entry->restricted)
err = grub_auth_check_authentication (entry->users);
if (err)
{
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
return;
}
errs_before = grub_err_printed_errors;
chosen = grub_env_get ("chosen");
def = grub_env_get ("default");
if (entry->submenu)
{
grub_env_context_open ();
menu = grub_zalloc (sizeof (*menu));
if (! menu)
return;
grub_env_set_menu (menu);
if (auto_boot)
grub_env_set ("timeout", "0");
}
for (ptr = entry->id; *ptr; ptr++)
sz += (*ptr == '>') ? 2 : 1;
if (chosen)
{
oldchosen = grub_strdup (chosen);
if (!oldchosen)
grub_print_error ();
}
if (def)
{
olddefault = grub_strdup (def);
if (!olddefault)
grub_print_error ();
}
sz++;
if (chosen)
sz += grub_strlen (chosen);
sz++;
buf = grub_malloc (sz);
if (!buf)
grub_print_error ();
else
{
optr = buf;
if (chosen)
{
optr = grub_stpcpy (optr, chosen);
*optr++ = '>';
}
for (ptr = entry->id; *ptr; ptr++)
{
if (*ptr == '>')
*optr++ = '>';
*optr++ = *ptr;
}
*optr = 0;
grub_env_set ("chosen", buf);
grub_env_export ("chosen");
grub_free (buf);
}
for (ptr = def; ptr && *ptr; ptr++)
{
if (ptr[0] == '>' && ptr[1] == '>')
{
ptr++;
continue;
}
if (ptr[0] == '>')
break;
}
if (ptr && ptr[0] && ptr[1])
grub_env_set ("default", ptr + 1);
else
grub_env_unset ("default");
grub_script_execute_new_scope (entry->sourcecode, entry->argc, entry->args);
if (errs_before != grub_err_printed_errors)
grub_wait_after_message ();
errs_before = grub_err_printed_errors;
if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
/* Implicit execution of boot, only if something is loaded. */
grub_command_execute ("boot", 0, 0);
if (errs_before != grub_err_printed_errors)
grub_wait_after_message ();
if (entry->submenu)
{
if (menu && menu->size)
{
grub_show_menu (menu, 1, auto_boot);
grub_normal_free_menu (menu);
}
grub_env_context_close ();
}
if (oldchosen)
grub_env_set ("chosen", oldchosen);
else
grub_env_unset ("chosen");
if (olddefault)
grub_env_set ("default", olddefault);
else
grub_env_unset ("default");
grub_env_unset ("timeout");
}
/* Execute ENTRY from the menu MENU, falling back to entries specified
in the environment variable "fallback" if it fails. CALLBACK is a
pointer to a struct of function pointers which are used to allow the
caller provide feedback to the user. */
static void
grub_menu_execute_with_fallback (grub_menu_t menu,
grub_menu_entry_t entry,
int autobooted,
grub_menu_execute_callback_t callback,
void *callback_data)
{
int fallback_entry;
callback->notify_booting (entry, callback_data);
grub_menu_execute_entry (entry, 1);
/* Deal with fallback entries. */
while ((fallback_entry = get_and_remove_first_entry_number ("fallback"))
>= 0)
{
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
entry = grub_menu_get_entry (menu, fallback_entry);
callback->notify_fallback (entry, callback_data);
grub_menu_execute_entry (entry, 1);
/* If the function call to execute the entry returns at all, then this is
taken to indicate a boot failure. For menu entries that do something
other than actually boot an operating system, this could assume
incorrectly that something failed. */
}
if (!autobooted)
callback->notify_failure (callback_data);
}
static struct grub_menu_viewer *viewers;
static void
menu_set_chosen_entry (int entry)
{
struct grub_menu_viewer *cur;
for (cur = viewers; cur; cur = cur->next)
cur->set_chosen_entry (entry, cur->data);
}
static void
menu_print_timeout (int timeout)
{
struct grub_menu_viewer *cur;
for (cur = viewers; cur; cur = cur->next)
cur->print_timeout (timeout, cur->data);
}
static void
menu_fini (void)
{
struct grub_menu_viewer *cur, *next;
for (cur = viewers; cur; cur = next)
{
next = cur->next;
cur->fini (cur->data);
grub_free (cur);
}
viewers = NULL;
}
static void
menu_init (int entry, grub_menu_t menu, int nested)
{
struct grub_term_output *term;
int gfxmenu = 0;
FOR_ACTIVE_TERM_OUTPUTS(term)
if (term->fullscreen)
{
if (grub_env_get ("theme"))
{
if (!grub_gfxmenu_try_hook)
{
grub_dl_load ("gfxmenu");
grub_print_error ();
}
if (grub_gfxmenu_try_hook)
{
grub_err_t err;
err = grub_gfxmenu_try_hook (entry, menu, nested);
if(!err)
{
gfxmenu = 1;
break;
}
}
else
grub_error (GRUB_ERR_BAD_MODULE,
N_("module `%s' isn't loaded"),
"gfxmenu");
grub_print_error ();
grub_wait_after_message ();
}
grub_errno = GRUB_ERR_NONE;
term->fullscreen ();
break;
}
FOR_ACTIVE_TERM_OUTPUTS(term)
{
grub_err_t err;
if (grub_strcmp (term->name, "gfxterm") == 0 && gfxmenu)
continue;
err = grub_menu_try_text (term, entry, menu, nested);
if(!err)
continue;
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
}
}
static void
clear_timeout (void)
{
struct grub_menu_viewer *cur;
for (cur = viewers; cur; cur = cur->next)
cur->clear_timeout (cur->data);
}
void
grub_menu_register_viewer (struct grub_menu_viewer *viewer)
{
viewer->next = viewers;
viewers = viewer;
}
static int
menuentry_eq (const char *id, const char *spec)
{
const char *ptr1, *ptr2;
ptr1 = id;
ptr2 = spec;
while (1)
{
if (*ptr2 == '>' && ptr2[1] != '>' && *ptr1 == 0)
return 1;
if (*ptr2 == '>' && ptr2[1] != '>')
return 0;
if (*ptr2 == '>')
ptr2++;
if (*ptr1 != *ptr2)
return 0;
if (*ptr1 == 0)
return 1;
ptr1++;
ptr2++;
}
}
/* Get the entry number from the variable NAME. */
static int
get_entry_number (grub_menu_t menu, const char *name)
{
const char *val;
int entry;
val = grub_env_get (name);
if (! val)
return -1;
grub_error_push ();
entry = (int) grub_strtoul (val, 0, 0);
if (grub_errno == GRUB_ERR_BAD_NUMBER)
{
/* See if the variable matches the title of a menu entry. */
grub_menu_entry_t e = menu->entry_list;
int i;
grub_errno = GRUB_ERR_NONE;
for (i = 0; e; i++)
{
if (menuentry_eq (e->title, val)
|| menuentry_eq (e->id, val))
{
entry = i;
break;
}
e = e->next;
}
if (! e)
entry = -1;
}
if (grub_errno != GRUB_ERR_NONE)
{
grub_errno = GRUB_ERR_NONE;
entry = -1;
}
grub_error_pop ();
return entry;
}
/* Check whether a second has elapsed since the last tick. If so, adjust
the timer and return 1; otherwise, return 0. */
static int
has_second_elapsed (grub_uint64_t *saved_time)
{
grub_uint64_t current_time;
current_time = grub_get_time_ms ();
if (current_time - *saved_time >= 1000)
{
*saved_time = current_time;
return 1;
}
else
return 0;
}
static void
print_countdown (struct grub_term_coordinate *pos, int n)
{
grub_term_restore_pos (pos);
/* NOTE: Do not remove the trailing space characters.
They are required to clear the line. */
grub_printf ("%d ", n);
grub_refresh ();
}
#define GRUB_MENU_PAGE_SIZE 10
/* Show the menu and handle menu entry selection. Returns the menu entry
index that should be executed or -1 if no entry should be executed (e.g.,
Esc pressed to exit a sub-menu or switching menu viewers).
If the return value is not -1, then *AUTO_BOOT is nonzero iff the menu
entry to be executed is a result of an automatic default selection because
of the timeout. */
static int
run_menu (grub_menu_t menu, int nested, int *auto_boot)
{
const char *cmdstr;
grub_uint64_t saved_time;
int default_entry,current_entry;
int timeout;
enum timeout_style timeout_style;
default_entry = get_entry_number (menu, "default");
if (g_ventoy_last_entry >= 0 && g_ventoy_last_entry < menu->size) {
default_entry = g_ventoy_last_entry;
}
/* If DEFAULT_ENTRY is not within the menu entries, fall back to
the first entry. */
else if (default_entry < 0 || default_entry >= menu->size)
default_entry = 0;
timeout = grub_menu_get_timeout ();
if (timeout < 0)
/* If there is no timeout, the "countdown" and "hidden" styles result in
the system doing nothing and providing no or very little indication
why. Technically this is what the user asked for, but it's not very
useful and likely to be a source of confusion, so we disallow this. */
grub_env_unset ("timeout_style");
timeout_style = get_timeout_style ();
if (timeout_style == TIMEOUT_STYLE_COUNTDOWN
|| timeout_style == TIMEOUT_STYLE_HIDDEN)
{
static struct grub_term_coordinate *pos;
int entry = -1;
if (timeout_style == TIMEOUT_STYLE_COUNTDOWN && timeout)
{
pos = grub_term_save_pos ();
print_countdown (pos, timeout);
}
/* Enter interruptible sleep until Escape or a menu hotkey is pressed,
or the timeout expires. */
saved_time = grub_get_time_ms ();
while (1)
{
int key;
key = grub_getkey_noblock ();
if (key != GRUB_TERM_NO_KEY)
{
entry = get_entry_index_by_hotkey (menu, key);
if (entry >= 0)
break;
}
if (key == GRUB_TERM_ESC)
{
timeout = -1;
break;
}
if (timeout > 0 && has_second_elapsed (&saved_time))
{
timeout--;
if (timeout_style == TIMEOUT_STYLE_COUNTDOWN)
print_countdown (pos, timeout);
}
if (timeout == 0)
/* We will fall through to auto-booting the default entry. */
break;
}
grub_env_unset ("timeout");
grub_env_unset ("timeout_style");
if (entry >= 0)
{
*auto_boot = 0;
return entry;
}
}
/* If timeout is 0, drawing is pointless (and ugly). */
if (timeout == 0)
{
*auto_boot = 1;
return default_entry;
}
current_entry = default_entry;
refresh:
menu_init (current_entry, menu, nested);
/* Initialize the time. */
saved_time = grub_get_time_ms ();
timeout = grub_menu_get_timeout ();
if (timeout > 0)
menu_print_timeout (timeout);
else
clear_timeout ();
while (1)
{
int c;
timeout = grub_menu_get_timeout ();
if (grub_normal_exit_level)
return -1;
if (timeout > 0 && has_second_elapsed (&saved_time))
{
timeout--;
grub_menu_set_timeout (timeout);
menu_print_timeout (timeout);
}
if (timeout == 0)
{
grub_env_unset ("timeout");
*auto_boot = 1;
menu_fini ();
return default_entry;
}
c = grub_getkey_noblock ();
/* Negative values are returned on error. */
if ((c != GRUB_TERM_NO_KEY) && (c > 0))
{
if (timeout >= 0)
{
grub_env_unset ("timeout");
grub_env_unset ("fallback");
clear_timeout ();
}
switch (c)
{
case GRUB_TERM_KEY_HOME:
case GRUB_TERM_CTRL | 'a':
current_entry = 0;
menu_set_chosen_entry (current_entry);
break;
case GRUB_TERM_KEY_END:
case GRUB_TERM_CTRL | 'e':
current_entry = menu->size - 1;
menu_set_chosen_entry (current_entry);
break;
case GRUB_TERM_KEY_UP:
case GRUB_TERM_CTRL | 'p':
case '^':
if (current_entry > 0)
current_entry--;
menu_set_chosen_entry (current_entry);
break;
case GRUB_TERM_CTRL | 'n':
case GRUB_TERM_KEY_DOWN:
case 'v':
if (current_entry < menu->size - 1)
current_entry++;
menu_set_chosen_entry (current_entry);
break;
case GRUB_TERM_CTRL | 'g':
case GRUB_TERM_KEY_PPAGE:
if (current_entry < GRUB_MENU_PAGE_SIZE)
current_entry = 0;
else
current_entry -= GRUB_MENU_PAGE_SIZE;
menu_set_chosen_entry (current_entry);
break;
case GRUB_TERM_CTRL | 'c':
case GRUB_TERM_KEY_NPAGE:
if (current_entry + GRUB_MENU_PAGE_SIZE < menu->size)
current_entry += GRUB_MENU_PAGE_SIZE;
else
current_entry = menu->size - 1;
menu_set_chosen_entry (current_entry);
break;
case '\n':
case '\r':
// case GRUB_TERM_KEY_RIGHT:
case GRUB_TERM_CTRL | 'f':
menu_fini ();
*auto_boot = 0;
return current_entry;
case GRUB_TERM_ESC:
if (nested)
{
menu_fini ();
return -1;
}
break;
case 'c':
menu_fini ();
grub_cmdline_run (1, 0);
goto refresh;
case 'e':
menu_fini ();
{
grub_menu_entry_t e = grub_menu_get_entry (menu, current_entry);
if (e)
grub_menu_entry_run (e);
}
goto refresh;
case GRUB_TERM_KEY_F2:
cmdstr = grub_env_get("VTOY_F2_CMD");
if (cmdstr)
{
menu_fini ();
grub_script_execute_sourcecode(cmdstr);
goto refresh;
}
break;
case GRUB_TERM_KEY_F3:
cmdstr = grub_env_get("VTOY_F3_CMD");
if (cmdstr)
{
menu_fini ();
grub_script_execute_sourcecode(cmdstr);
goto refresh;
}
break;
case GRUB_TERM_KEY_F4:
cmdstr = grub_env_get("VTOY_F4_CMD");
if (cmdstr)
{
menu_fini ();
grub_script_execute_sourcecode(cmdstr);
goto refresh;
}
break;
case GRUB_TERM_KEY_F5:
cmdstr = grub_env_get("VTOY_F5_CMD");
if (cmdstr)
{
menu_fini ();
grub_script_execute_sourcecode(cmdstr);
goto refresh;
}
break;
case GRUB_TERM_KEY_F6:
cmdstr = grub_env_get("VTOY_F6_CMD");
if (cmdstr)
{
menu_fini ();
grub_script_execute_sourcecode(cmdstr);
goto refresh;
}
break;
case GRUB_TERM_KEY_F7:
cmdstr = grub_env_get("VTOY_F7_CMD");
if (cmdstr)
{
menu_fini ();
grub_script_execute_sourcecode(cmdstr);
goto refresh;
}
break;
case GRUB_TERM_KEY_F1:
menu_fini ();
g_ventoy_memdisk_mode = 1 - g_ventoy_memdisk_mode;
g_ventoy_menu_refresh = 1;
goto refresh;
case (GRUB_TERM_CTRL | 'i'):
menu_fini ();
g_ventoy_iso_raw = 1 - g_ventoy_iso_raw;
g_ventoy_menu_refresh = 1;
goto refresh;
case (GRUB_TERM_CTRL | 'u'):
menu_fini ();
g_ventoy_iso_uefi_drv = 1 - g_ventoy_iso_uefi_drv;
g_ventoy_menu_refresh = 1;
goto refresh;
default:
{
int entry;
entry = get_entry_index_by_hotkey (menu, c);
if (entry >= 0)
{
menu_fini ();
*auto_boot = 0;
return entry;
}
}
break;
}
}
}
/* Never reach here. */
}
/* Callback invoked immediately before a menu entry is executed. */
static void
notify_booting (grub_menu_entry_t entry,
void *userdata __attribute__((unused)))
{
grub_printf (" ");
grub_printf_ (N_("Booting `%s'"), entry->title);
grub_printf ("\n\n");
}
/* Callback invoked when a default menu entry executed because of a timeout
has failed and an attempt will be made to execute the next fallback
entry, ENTRY. */
static void
notify_fallback (grub_menu_entry_t entry,
void *userdata __attribute__((unused)))
{
grub_printf ("\n ");
grub_printf_ (N_("Falling back to `%s'"), entry->title);
grub_printf ("\n\n");
grub_millisleep (DEFAULT_ENTRY_ERROR_DELAY_MS);
}
/* Callback invoked when a menu entry has failed and there is no remaining
fallback entry to attempt. */
static void
notify_execution_failure (void *userdata __attribute__((unused)))
{
if (grub_errno != GRUB_ERR_NONE)
{
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
}
grub_printf ("\n ");
grub_printf_ (N_("Failed to boot both default and fallback entries.\n"));
grub_wait_after_message ();
}
/* Callbacks used by the text menu to provide user feedback when menu entries
are executed. */
static struct grub_menu_execute_callback execution_callback =
{
.notify_booting = notify_booting,
.notify_fallback = notify_fallback,
.notify_failure = notify_execution_failure
};
static grub_err_t
show_menu (grub_menu_t menu, int nested, int autobooted)
{
while (1)
{
int boot_entry;
grub_menu_entry_t e;
int auto_boot;
boot_entry = run_menu (menu, nested, &auto_boot);
if (boot_entry < 0)
break;
g_ventoy_last_entry = boot_entry;
e = grub_menu_get_entry (menu, boot_entry);
if (! e)
continue; /* Menu is empty. */
grub_cls ();
if (auto_boot)
grub_menu_execute_with_fallback (menu, e, autobooted,
&execution_callback, 0);
else
grub_menu_execute_entry (e, 0);
if (autobooted)
break;
}
return GRUB_ERR_NONE;
}
grub_err_t
grub_show_menu (grub_menu_t menu, int nested, int autoboot)
{
grub_err_t err1, err2;
while (1)
{
err1 = show_menu (menu, nested, autoboot);
autoboot = 0;
grub_print_error ();
if (grub_normal_exit_level)
break;
err2 = grub_auth_check_authentication (NULL);
if (err2)
{
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
continue;
}
break;
}
return err1;
}

View File

@@ -0,0 +1,605 @@
/* menu_text.c - Basic text menu implementation. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/normal.h>
#include <grub/term.h>
#include <grub/misc.h>
#include <grub/loader.h>
#include <grub/mm.h>
#include <grub/time.h>
#include <grub/env.h>
#include <grub/menu_viewer.h>
#include <grub/i18n.h>
#include <grub/charset.h>
static grub_uint8_t grub_color_menu_normal;
static grub_uint8_t grub_color_menu_highlight;
struct menu_viewer_data
{
int first, offset;
struct grub_term_screen_geometry geo;
enum {
TIMEOUT_UNKNOWN,
TIMEOUT_NORMAL,
TIMEOUT_TERSE,
TIMEOUT_TERSE_NO_MARGIN
} timeout_msg;
grub_menu_t menu;
struct grub_term_output *term;
};
static inline int
grub_term_cursor_x (const struct grub_term_screen_geometry *geo)
{
return (geo->first_entry_x + geo->entry_width);
}
grub_size_t
grub_getstringwidth (grub_uint32_t * str, const grub_uint32_t * last_position,
struct grub_term_output *term)
{
grub_ssize_t width = 0;
while (str < last_position)
{
struct grub_unicode_glyph glyph;
glyph.ncomb = 0;
str += grub_unicode_aglomerate_comb (str, last_position - str, &glyph);
width += grub_term_getcharwidth (term, &glyph);
grub_unicode_destroy_glyph (&glyph);
}
return width;
}
static int
grub_print_message_indented_real (const char *msg, int margin_left,
int margin_right,
struct grub_term_output *term, int dry_run)
{
grub_uint32_t *unicode_msg;
grub_uint32_t *last_position;
grub_size_t msg_len = grub_strlen (msg) + 2;
int ret = 0;
unicode_msg = grub_malloc (msg_len * sizeof (grub_uint32_t));
if (!unicode_msg)
return 0;
msg_len = grub_utf8_to_ucs4 (unicode_msg, msg_len,
(grub_uint8_t *) msg, -1, 0);
last_position = unicode_msg + msg_len;
*last_position = 0;
if (dry_run)
ret = grub_ucs4_count_lines (unicode_msg, last_position, margin_left,
margin_right, term);
else
grub_print_ucs4_menu (unicode_msg, last_position, margin_left,
margin_right, term, 0, -1, 0, 0);
grub_free (unicode_msg);
return ret;
}
void
grub_print_message_indented (const char *msg, int margin_left, int margin_right,
struct grub_term_output *term)
{
grub_print_message_indented_real (msg, margin_left, margin_right, term, 0);
}
static void
draw_border (struct grub_term_output *term, const struct grub_term_screen_geometry *geo)
{
int i;
grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
grub_term_gotoxy (term, (struct grub_term_coordinate) { geo->first_entry_x - 1,
geo->first_entry_y - 1 });
grub_putcode (GRUB_UNICODE_CORNER_UL, term);
for (i = 0; i < geo->entry_width + 1; i++)
grub_putcode (GRUB_UNICODE_HLINE, term);
grub_putcode (GRUB_UNICODE_CORNER_UR, term);
for (i = 0; i < geo->num_entries; i++)
{
grub_term_gotoxy (term, (struct grub_term_coordinate) { geo->first_entry_x - 1,
geo->first_entry_y + i });
grub_putcode (GRUB_UNICODE_VLINE, term);
grub_term_gotoxy (term,
(struct grub_term_coordinate) { geo->first_entry_x + geo->entry_width + 1,
geo->first_entry_y + i });
grub_putcode (GRUB_UNICODE_VLINE, term);
}
grub_term_gotoxy (term,
(struct grub_term_coordinate) { geo->first_entry_x - 1,
geo->first_entry_y - 1 + geo->num_entries + 1 });
grub_putcode (GRUB_UNICODE_CORNER_LL, term);
for (i = 0; i < geo->entry_width + 1; i++)
grub_putcode (GRUB_UNICODE_HLINE, term);
grub_putcode (GRUB_UNICODE_CORNER_LR, term);
grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
grub_term_gotoxy (term,
(struct grub_term_coordinate) { geo->first_entry_x - 1,
(geo->first_entry_y - 1 + geo->num_entries
+ GRUB_TERM_MARGIN + 1) });
}
static int
print_message (int nested, int edit, struct grub_term_output *term, int dry_run)
{
int ret = 0;
grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
if (edit)
{
ret += grub_print_message_indented_real (_("Minimum Emacs-like screen editing is \
supported. TAB lists completions. Press Ctrl-x or F10 to boot, Ctrl-c or F2 for a \
command-line or ESC to discard edits and return to the GRUB menu."),
STANDARD_MARGIN, STANDARD_MARGIN,
term, dry_run);
}
else
{
char *msg_translated;
msg_translated = grub_xasprintf (_("Use the %C and %C keys to select which "
"entry is highlighted."),
GRUB_UNICODE_UPARROW,
GRUB_UNICODE_DOWNARROW);
if (!msg_translated)
return 0;
ret += grub_print_message_indented_real (msg_translated, STANDARD_MARGIN,
STANDARD_MARGIN, term, dry_run);
grub_free (msg_translated);
if (nested)
{
ret += grub_print_message_indented_real
(_("Press enter to boot the selected OS, "
"`e' to edit the commands before booting "
"or `c' for a command-line. ESC to return previous menu."),
STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run);
}
else
{
ret += grub_print_message_indented_real("\n", STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run);
ret += grub_print_message_indented_real(grub_env_get("VTOY_TEXT_MENU_VER"),
STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run);
ret += grub_print_message_indented_real("\n", STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run);
ret += grub_print_message_indented_real(grub_env_get("VTOY_HOTKEY_TIP"),
STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run);
}
}
return ret;
}
static void
print_entry (int y, int highlight, grub_menu_entry_t entry,
const struct menu_viewer_data *data)
{
const char *title;
grub_size_t title_len;
grub_ssize_t len;
grub_uint32_t *unicode_title;
grub_ssize_t i;
grub_uint8_t old_color_normal, old_color_highlight;
title = entry ? entry->title : "";
title_len = grub_strlen (title);
unicode_title = grub_malloc (title_len * sizeof (*unicode_title));
if (! unicode_title)
/* XXX How to show this error? */
return;
len = grub_utf8_to_ucs4 (unicode_title, title_len,
(grub_uint8_t *) title, -1, 0);
if (len < 0)
{
/* It is an invalid sequence. */
grub_free (unicode_title);
return;
}
old_color_normal = grub_term_normal_color;
old_color_highlight = grub_term_highlight_color;
grub_term_normal_color = grub_color_menu_normal;
grub_term_highlight_color = grub_color_menu_highlight;
grub_term_setcolorstate (data->term, highlight
? GRUB_TERM_COLOR_HIGHLIGHT
: GRUB_TERM_COLOR_NORMAL);
grub_term_gotoxy (data->term, (struct grub_term_coordinate) {
data->geo.first_entry_x, y });
for (i = 0; i < len; i++)
if (unicode_title[i] == '\n' || unicode_title[i] == '\b'
|| unicode_title[i] == '\r' || unicode_title[i] == '\e')
unicode_title[i] = ' ';
if (data->geo.num_entries > 1)
grub_putcode (highlight ? '*' : ' ', data->term);
grub_print_ucs4_menu (unicode_title,
unicode_title + len,
0,
data->geo.right_margin,
data->term, 0, 1,
GRUB_UNICODE_RIGHTARROW, 0);
grub_term_setcolorstate (data->term, GRUB_TERM_COLOR_NORMAL);
grub_term_gotoxy (data->term,
(struct grub_term_coordinate) {
grub_term_cursor_x (&data->geo), y });
grub_term_normal_color = old_color_normal;
grub_term_highlight_color = old_color_highlight;
grub_term_setcolorstate (data->term, GRUB_TERM_COLOR_NORMAL);
grub_free (unicode_title);
}
static void
print_entries (grub_menu_t menu, const struct menu_viewer_data *data)
{
grub_menu_entry_t e;
int i;
grub_term_gotoxy (data->term,
(struct grub_term_coordinate) {
data->geo.first_entry_x + data->geo.entry_width
+ data->geo.border + 1,
data->geo.first_entry_y });
if (data->geo.num_entries != 1)
{
if (data->first)
grub_putcode (GRUB_UNICODE_UPARROW, data->term);
else
grub_putcode (' ', data->term);
}
e = grub_menu_get_entry (menu, data->first);
for (i = 0; i < data->geo.num_entries; i++)
{
print_entry (data->geo.first_entry_y + i, data->offset == i,
e, data);
if (e)
e = e->next;
}
grub_term_gotoxy (data->term,
(struct grub_term_coordinate) { data->geo.first_entry_x + data->geo.entry_width
+ data->geo.border + 1,
data->geo.first_entry_y + data->geo.num_entries - 1 });
if (data->geo.num_entries == 1)
{
if (data->first && e)
grub_putcode (GRUB_UNICODE_UPDOWNARROW, data->term);
else if (data->first)
grub_putcode (GRUB_UNICODE_UPARROW, data->term);
else if (e)
grub_putcode (GRUB_UNICODE_DOWNARROW, data->term);
else
grub_putcode (' ', data->term);
}
else
{
if (e)
grub_putcode (GRUB_UNICODE_DOWNARROW, data->term);
else
grub_putcode (' ', data->term);
}
grub_term_gotoxy (data->term,
(struct grub_term_coordinate) { grub_term_cursor_x (&data->geo),
data->geo.first_entry_y + data->offset });
}
/* Initialize the screen. If NESTED is non-zero, assume that this menu
is run from another menu or a command-line. If EDIT is non-zero, show
a message for the menu entry editor. */
void
grub_menu_init_page (int nested, int edit,
struct grub_term_screen_geometry *geo,
struct grub_term_output *term)
{
grub_uint8_t old_color_normal, old_color_highlight;
int msg_num_lines;
int bottom_message = 1;
int empty_lines = 1;
int version_msg = 1;
geo->border = 1;
geo->first_entry_x = 1 /* margin */ + 1 /* border */;
geo->entry_width = grub_term_width (term) - 5;
geo->first_entry_y = 2 /* two empty lines*/
+ 1 /* GNU GRUB version text */ + 1 /* top border */;
geo->timeout_lines = 2;
/* 3 lines for timeout message and bottom margin. 2 lines for the border. */
geo->num_entries = grub_term_height (term) - geo->first_entry_y
- 1 /* bottom border */
- 1 /* empty line before info message*/
- geo->timeout_lines /* timeout */
- 1 /* empty final line */;
msg_num_lines = print_message (nested, edit, term, 1);
if (geo->num_entries - msg_num_lines < 3
|| geo->entry_width < 10)
{
geo->num_entries += 4;
geo->first_entry_y -= 2;
empty_lines = 0;
geo->first_entry_x -= 1;
geo->entry_width += 1;
}
if (geo->num_entries - msg_num_lines < 3
|| geo->entry_width < 10)
{
geo->num_entries += 2;
geo->first_entry_y -= 1;
geo->first_entry_x -= 1;
geo->entry_width += 2;
geo->border = 0;
}
if (geo->entry_width <= 0)
geo->entry_width = 1;
if (geo->num_entries - msg_num_lines < 3
&& geo->timeout_lines == 2)
{
geo->timeout_lines = 1;
geo->num_entries++;
}
if (geo->num_entries - msg_num_lines < 3)
{
geo->num_entries += 1;
geo->first_entry_y -= 1;
version_msg = 0;
}
if (geo->num_entries - msg_num_lines >= 2)
geo->num_entries -= msg_num_lines;
else
bottom_message = 0;
/* By default, use the same colors for the menu. */
old_color_normal = grub_term_normal_color;
old_color_highlight = grub_term_highlight_color;
grub_color_menu_normal = grub_term_normal_color;
grub_color_menu_highlight = grub_term_highlight_color;
/* Then give user a chance to replace them. */
grub_parse_color_name_pair (&grub_color_menu_normal,
grub_env_get ("menu_color_normal"));
grub_parse_color_name_pair (&grub_color_menu_highlight,
grub_env_get ("menu_color_highlight"));
if (version_msg)
grub_normal_init_page (term, empty_lines);
else
grub_term_cls (term);
grub_term_normal_color = grub_color_menu_normal;
grub_term_highlight_color = grub_color_menu_highlight;
if (geo->border)
draw_border (term, geo);
grub_term_normal_color = old_color_normal;
grub_term_highlight_color = old_color_highlight;
geo->timeout_y = geo->first_entry_y + geo->num_entries
+ geo->border + empty_lines;
if (bottom_message)
{
grub_term_gotoxy (term,
(struct grub_term_coordinate) { GRUB_TERM_MARGIN,
geo->timeout_y });
print_message (nested, edit, term, 0);
geo->timeout_y += msg_num_lines;
}
geo->right_margin = grub_term_width (term)
- geo->first_entry_x
- geo->entry_width - 1;
}
static void
menu_text_print_timeout (int timeout, void *dataptr)
{
struct menu_viewer_data *data = dataptr;
char *msg_translated = 0;
grub_term_gotoxy (data->term,
(struct grub_term_coordinate) { 0, data->geo.timeout_y });
if (data->timeout_msg == TIMEOUT_TERSE
|| data->timeout_msg == TIMEOUT_TERSE_NO_MARGIN)
msg_translated = grub_xasprintf (_("%ds"), timeout);
else
msg_translated = grub_xasprintf (_("The highlighted entry will be executed automatically in %ds."), timeout);
if (!msg_translated)
{
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
return;
}
if (data->timeout_msg == TIMEOUT_UNKNOWN)
{
data->timeout_msg = grub_print_message_indented_real (msg_translated,
3, 1, data->term, 1)
<= data->geo.timeout_lines ? TIMEOUT_NORMAL : TIMEOUT_TERSE;
if (data->timeout_msg == TIMEOUT_TERSE)
{
grub_free (msg_translated);
msg_translated = grub_xasprintf (_("%ds"), timeout);
if (grub_term_width (data->term) < 10)
data->timeout_msg = TIMEOUT_TERSE_NO_MARGIN;
}
}
grub_print_message_indented (msg_translated,
data->timeout_msg == TIMEOUT_TERSE_NO_MARGIN ? 0 : 3,
data->timeout_msg == TIMEOUT_TERSE_NO_MARGIN ? 0 : 1,
data->term);
grub_free (msg_translated);
grub_term_gotoxy (data->term,
(struct grub_term_coordinate) {
grub_term_cursor_x (&data->geo),
data->geo.first_entry_y + data->offset });
grub_term_refresh (data->term);
}
static void
menu_text_set_chosen_entry (int entry, void *dataptr)
{
struct menu_viewer_data *data = dataptr;
int oldoffset = data->offset;
int complete_redraw = 0;
data->offset = entry - data->first;
if (data->offset > data->geo.num_entries - 1)
{
data->first = entry - (data->geo.num_entries - 1);
data->offset = data->geo.num_entries - 1;
complete_redraw = 1;
}
if (data->offset < 0)
{
data->offset = 0;
data->first = entry;
complete_redraw = 1;
}
if (complete_redraw)
print_entries (data->menu, data);
else
{
print_entry (data->geo.first_entry_y + oldoffset, 0,
grub_menu_get_entry (data->menu, data->first + oldoffset),
data);
print_entry (data->geo.first_entry_y + data->offset, 1,
grub_menu_get_entry (data->menu, data->first + data->offset),
data);
}
grub_term_refresh (data->term);
}
static void
menu_text_fini (void *dataptr)
{
struct menu_viewer_data *data = dataptr;
grub_term_setcursor (data->term, 1);
grub_term_cls (data->term);
grub_free (data);
}
static void
menu_text_clear_timeout (void *dataptr)
{
struct menu_viewer_data *data = dataptr;
int i;
for (i = 0; i < data->geo.timeout_lines;i++)
{
grub_term_gotoxy (data->term, (struct grub_term_coordinate) {
0, data->geo.timeout_y + i });
grub_print_spaces (data->term, grub_term_width (data->term) - 1);
}
if (data->geo.num_entries <= 5 && !data->geo.border)
{
grub_term_gotoxy (data->term,
(struct grub_term_coordinate) {
data->geo.first_entry_x + data->geo.entry_width
+ data->geo.border + 1,
data->geo.first_entry_y + data->geo.num_entries - 1
});
grub_putcode (' ', data->term);
data->geo.timeout_lines = 0;
data->geo.num_entries++;
print_entries (data->menu, data);
}
grub_term_gotoxy (data->term,
(struct grub_term_coordinate) {
grub_term_cursor_x (&data->geo),
data->geo.first_entry_y + data->offset });
grub_term_refresh (data->term);
}
grub_err_t
grub_menu_try_text (struct grub_term_output *term,
int entry, grub_menu_t menu, int nested)
{
struct menu_viewer_data *data;
struct grub_menu_viewer *instance;
instance = grub_zalloc (sizeof (*instance));
if (!instance)
return grub_errno;
data = grub_zalloc (sizeof (*data));
if (!data)
{
grub_free (instance);
return grub_errno;
}
data->term = term;
instance->data = data;
instance->set_chosen_entry = menu_text_set_chosen_entry;
instance->print_timeout = menu_text_print_timeout;
instance->clear_timeout = menu_text_clear_timeout;
instance->fini = menu_text_fini;
data->menu = menu;
data->offset = entry;
data->first = 0;
grub_term_setcursor (data->term, 0);
grub_menu_init_page (nested, 0, &data->geo, data->term);
if (data->offset > data->geo.num_entries - 1)
{
data->first = data->offset - (data->geo.num_entries - 1);
data->offset = data->geo.num_entries - 1;
}
print_entries (menu, data);
grub_term_refresh (data->term);
grub_menu_register_viewer (instance);
return GRUB_ERR_NONE;
}

View File

@@ -0,0 +1,194 @@
/* misc.c - miscellaneous functions */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2007,2008,2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/normal.h>
#include <grub/disk.h>
#include <grub/fs.h>
#include <grub/err.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/datetime.h>
#include <grub/term.h>
#include <grub/i18n.h>
#include <grub/partition.h>
static const char *grub_human_sizes[3][6] =
{
/* This algorithm in reality would work only up to (2^64) / 100 B = 81 PiB.
Put here all possible suffixes it can produce so no array bounds check
is needed.
*/
/* TRANSLATORS: that's the list of binary unit prefixes. */
{ N_("B"), N_("KiB"), N_("MiB"), N_("GiB"), N_("TiB"), N_("PiB")},
/* TRANSLATORS: that's the list of binary unit prefixes. */
{ "", N_("KB"), N_("MB"), N_("GB"), N_("TB"), N_("PB") },
/* TRANSLATORS: that's the list of binary unit prefixes. */
{ N_("B/s"), N_("KiB/s"), N_("MiB/s"), N_("GiB/s"), N_("TiB/s"), N_("PiB/s"), },
};
const char *
grub_get_human_size (grub_uint64_t size, enum grub_human_size_type type)
{
grub_uint64_t fsize;
unsigned units = 0;
static char buf[30];
const char *umsg;
if (type != GRUB_HUMAN_SIZE_SPEED)
fsize = size * 100ULL;
else
fsize = size;
/* Since 2^64 / 1024^5 < 102400, this can give at most 5 iterations.
So units <=5, so impossible to go past the end of array.
*/
while (fsize >= 102400)
{
fsize = (fsize + 512) / 1024;
units++;
}
umsg = _(grub_human_sizes[type][units]);
if (units || type == GRUB_HUMAN_SIZE_SPEED)
{
grub_uint64_t whole, fraction;
whole = grub_divmod64 (fsize, 100, &fraction);
grub_snprintf (buf, sizeof (buf),
"%" PRIuGRUB_UINT64_T
".%02" PRIuGRUB_UINT64_T "%s", whole, fraction,
umsg);
}
else
grub_snprintf (buf, sizeof (buf), "%llu%s", (unsigned long long) size,
umsg);
return buf;
}
/* Print the information on the device NAME. */
grub_err_t
grub_normal_print_device_info (const char *name)
{
grub_device_t dev;
char *p;
p = grub_strchr (name, ',');
if (p)
{
grub_xputs ("\t");
grub_printf_ (N_("Partition %s:"), name);
grub_xputs (" ");
}
else
{
grub_printf_ (N_("Device %s:"), name);
grub_xputs (" ");
}
dev = grub_device_open (name);
if (! dev)
grub_printf ("%s", _("Filesystem cannot be accessed"));
else if (dev->disk)
{
grub_fs_t fs;
fs = grub_fs_probe (dev);
/* Ignore all errors. */
grub_errno = 0;
if (fs)
{
const char *fsname = fs->name;
if (grub_strcmp (fsname, "ext2") == 0)
fsname = "ext*";
grub_printf_ (N_("Filesystem type %s"), fsname);
if (fs->fs_label)
{
char *label;
(fs->fs_label) (dev, &label);
if (grub_errno == GRUB_ERR_NONE)
{
if (label && grub_strlen (label))
{
grub_xputs (" ");
grub_printf_ (N_("- Label `%s'"), label);
}
grub_free (label);
}
grub_errno = GRUB_ERR_NONE;
}
if (fs->fs_mtime)
{
grub_int32_t tm;
struct grub_datetime datetime;
(fs->fs_mtime) (dev, &tm);
if (grub_errno == GRUB_ERR_NONE)
{
grub_unixtime2datetime (tm, &datetime);
grub_xputs (" ");
/* TRANSLATORS: Arguments are year, month, day, hour, minute,
second, day of the week (translated). */
grub_printf_ (N_("- Last modification time %d-%02d-%02d "
"%02d:%02d:%02d %s"),
datetime.year, datetime.month, datetime.day,
datetime.hour, datetime.minute, datetime.second,
grub_get_weekday_name (&datetime));
}
grub_errno = GRUB_ERR_NONE;
}
if (fs->fs_uuid)
{
char *uuid;
(fs->fs_uuid) (dev, &uuid);
if (grub_errno == GRUB_ERR_NONE)
{
if (uuid && grub_strlen (uuid))
grub_printf (", UUID %s", uuid);
grub_free (uuid);
}
grub_errno = GRUB_ERR_NONE;
}
}
else
grub_printf ("%s", _("No known filesystem detected"));
if (dev->disk->partition)
grub_printf (_(" - Partition start at %llu%sKiB"),
(unsigned long long) (grub_partition_get_start (dev->disk->partition) >> 1),
(grub_partition_get_start (dev->disk->partition) & 1) ? ".5" : "" );
else
grub_printf_ (N_(" - Sector size %uB"), 1 << dev->disk->log_sector_size);
if (grub_disk_get_size (dev->disk) == GRUB_DISK_SIZE_UNKNOWN)
grub_puts_ (N_(" - Total size unknown"));
else
grub_printf (_(" - Total size %llu%sKiB"),
(unsigned long long) (grub_disk_get_size (dev->disk) >> 1),
/* TRANSLATORS: Replace dot with appropriate decimal separator for
your language. */
(grub_disk_get_size (dev->disk) & 1) ? _(".5") : "");
}
if (dev)
grub_device_close (dev);
grub_xputs ("\n");
return grub_errno;
}

View File

@@ -0,0 +1,207 @@
/*
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* Huffman alphabets
*
*/
#include "wimboot.h"
#include "huffman.h"
/**
* Transcribe binary value (for debugging)
*
* @v value Value
* @v bits Length of value (in bits)
* @ret string Transcribed value
*/
const char * huffman_bin ( unsigned long value, unsigned int bits ) {
static char buf[ ( 8 * sizeof ( value ) ) + 1 /* NUL */ ];
char *out = buf;
/* Sanity check */
assert ( bits < sizeof ( buf ) );
/* Transcribe value */
while ( bits-- )
*(out++) = ( ( value & ( 1 << bits ) ) ? '1' : '0' );
*out = '\0';
return buf;
}
/**
* Dump Huffman alphabet (for debugging)
*
* @v alphabet Huffman alphabet
*/
static void __attribute__ (( unused ))
huffman_dump_alphabet ( struct huffman_alphabet *alphabet ) {
struct huffman_symbols *sym;
unsigned int bits;
unsigned int huf;
unsigned int i;
(void)huf;
/* Dump symbol table for each utilised length */
for ( bits = 1 ; bits <= ( sizeof ( alphabet->huf ) /
sizeof ( alphabet->huf[0] ) ) ; bits++ ) {
sym = &alphabet->huf[ bits - 1 ];
if ( sym->freq == 0 )
continue;
huf = ( sym->start >> sym->shift );
DBG ( "Huffman length %d start \"%s\" freq %d:", bits,
huffman_bin ( huf, sym->bits ), sym->freq );
for ( i = 0 ; i < sym->freq ; i++ ) {
DBG ( " %03x", sym->raw[ huf + i ] );
}
DBG ( "\n" );
}
/* Dump quick lookup table */
DBG ( "Huffman quick lookup:" );
for ( i = 0 ; i < ( sizeof ( alphabet->lookup ) /
sizeof ( alphabet->lookup[0] ) ) ; i++ ) {
DBG ( " %d", ( alphabet->lookup[i] + 1 ) );
}
DBG ( "\n" );
}
/**
* Construct Huffman alphabet
*
* @v alphabet Huffman alphabet
* @v lengths Symbol length table
* @v count Number of symbols
* @ret rc Return status code
*/
int huffman_alphabet ( struct huffman_alphabet *alphabet,
uint8_t *lengths, unsigned int count ) {
struct huffman_symbols *sym;
unsigned int huf;
unsigned int cum_freq;
unsigned int bits;
unsigned int raw;
unsigned int adjustment;
unsigned int prefix;
int empty;
int complete;
/* Clear symbol table */
memset ( alphabet->huf, 0, sizeof ( alphabet->huf ) );
/* Count number of symbols with each Huffman-coded length */
empty = 1;
for ( raw = 0 ; raw < count ; raw++ ) {
bits = lengths[raw];
if ( bits ) {
alphabet->huf[ bits - 1 ].freq++;
empty = 0;
}
}
/* In the degenerate case of having no symbols (i.e. an unused
* alphabet), generate a trivial alphabet with exactly two
* single-bit codes. This allows callers to avoid having to
* check for this special case.
*/
if ( empty )
alphabet->huf[0].freq = 2;
/* Populate Huffman-coded symbol table */
huf = 0;
cum_freq = 0;
for ( bits = 1 ; bits <= ( sizeof ( alphabet->huf ) /
sizeof ( alphabet->huf[0] ) ) ; bits++ ) {
sym = &alphabet->huf[ bits - 1 ];
sym->bits = bits;
sym->shift = ( HUFFMAN_BITS - bits );
sym->start = ( huf << sym->shift );
sym->raw = &alphabet->raw[cum_freq];
huf += sym->freq;
if ( huf > ( 1U << bits ) ) {
DBG ( "Huffman alphabet has too many symbols with "
"lengths <=%d\n", bits );
return -1;
}
huf <<= 1;
cum_freq += sym->freq;
}
complete = ( huf == ( 1U << bits ) );
/* Populate raw symbol table */
for ( raw = 0 ; raw < count ; raw++ ) {
bits = lengths[raw];
if ( bits ) {
sym = &alphabet->huf[ bits - 1 ];
*(sym->raw++) = raw;
}
}
/* Adjust Huffman-coded symbol table raw pointers and populate
* quick lookup table.
*/
for ( bits = 1 ; bits <= ( sizeof ( alphabet->huf ) /
sizeof ( alphabet->huf[0] ) ) ; bits++ ) {
sym = &alphabet->huf[ bits - 1 ];
/* Adjust raw pointer */
sym->raw -= sym->freq; /* Reset to first symbol */
adjustment = ( sym->start >> sym->shift );
sym->raw -= adjustment; /* Adjust for quick indexing */
/* Populate quick lookup table */
for ( prefix = ( sym->start >> HUFFMAN_QL_SHIFT ) ;
prefix < ( 1 << HUFFMAN_QL_BITS ) ; prefix++ ) {
alphabet->lookup[prefix] = ( bits - 1 );
}
}
/* Check that there are no invalid codes */
if ( ! complete ) {
DBG ( "Huffman alphabet is incomplete\n" );
return -1;
}
return 0;
}
/**
* Get Huffman symbol set
*
* @v alphabet Huffman alphabet
* @v huf Raw input value (normalised to HUFFMAN_BITS bits)
* @ret sym Huffman symbol set
*/
struct huffman_symbols * huffman_sym ( struct huffman_alphabet *alphabet,
unsigned int huf ) {
struct huffman_symbols *sym;
unsigned int lookup_index;
/* Find symbol set for this length */
lookup_index = ( huf >> HUFFMAN_QL_SHIFT );
sym = &alphabet->huf[ alphabet->lookup[ lookup_index ] ];
while ( huf < sym->start )
sym--;
return sym;
}

View File

@@ -0,0 +1,108 @@
#ifndef _HUFFMAN_H
#define _HUFFMAN_H
/*
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* Huffman alphabets
*
*/
/** Maximum length of a Huffman symbol (in bits) */
#define HUFFMAN_BITS 16
/** Raw huffman symbol */
typedef uint16_t huffman_raw_symbol_t;
/** Quick lookup length for a Huffman symbol (in bits)
*
* This is a policy decision.
*/
#define HUFFMAN_QL_BITS 7
/** Quick lookup shift */
#define HUFFMAN_QL_SHIFT ( HUFFMAN_BITS - HUFFMAN_QL_BITS )
/** A Huffman-coded set of symbols of a given length */
struct huffman_symbols {
/** Length of Huffman-coded symbols (in bits) */
uint8_t bits;
/** Shift to normalise symbols of this length to HUFFMAN_BITS bits */
uint8_t shift;
/** Number of Huffman-coded symbols having this length */
uint16_t freq;
/** First symbol of this length (normalised to HUFFMAN_BITS bits)
*
* Stored as a 32-bit value to allow the value
* (1<<HUFFMAN_BITS ) to be used for empty sets of symbols
* longer than the maximum utilised length.
*/
uint32_t start;
/** Raw symbols having this length */
huffman_raw_symbol_t *raw;
};
/** A Huffman-coded alphabet */
struct huffman_alphabet {
/** Huffman-coded symbol set for each length */
struct huffman_symbols huf[HUFFMAN_BITS];
/** Quick lookup table */
uint8_t lookup[ 1 << HUFFMAN_QL_BITS ];
/** Raw symbols
*
* Ordered by Huffman-coded symbol length, then by symbol
* value. This field has a variable length.
*/
huffman_raw_symbol_t raw[0];
};
/**
* Get Huffman symbol length
*
* @v sym Huffman symbol set
* @ret len Length (in bits)
*/
static inline __attribute__ (( always_inline )) unsigned int
huffman_len ( struct huffman_symbols *sym ) {
return sym->bits;
}
/**
* Get Huffman symbol value
*
* @v sym Huffman symbol set
* @v huf Raw input value (normalised to HUFFMAN_BITS bits)
* @ret raw Raw symbol value
*/
static inline __attribute__ (( always_inline )) huffman_raw_symbol_t
huffman_raw ( struct huffman_symbols *sym, unsigned int huf ) {
return sym->raw[ huf >> sym->shift ];
}
extern int huffman_alphabet ( struct huffman_alphabet *alphabet,
uint8_t *lengths, unsigned int count );
extern struct huffman_symbols *
huffman_sym ( struct huffman_alphabet *alphabet, unsigned int huf );
#endif /* _HUFFMAN_H */

View File

@@ -0,0 +1,666 @@
/*
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* LZX decompression
*
* This algorithm is derived jointly from the document "[MS-PATCH]:
* LZX DELTA Compression and Decompression", available from
*
* http://msdn.microsoft.com/en-us/library/cc483133.aspx
*
* and from the file lzx-decompress.c in the wimlib source code.
*
*/
#include "wimboot.h"
#include "huffman.h"
#include "lzx.h"
/** Base positions, indexed by position slot */
static unsigned int lzx_position_base[LZX_POSITION_SLOTS];
/**
* Attempt to accumulate bits from LZX bitstream
*
* @v lzx Decompressor
* @v bits Number of bits to accumulate
* @v norm_value Accumulated value (normalised to 16 bits)
*
* Note that there may not be sufficient accumulated bits in the
* bitstream; callers must check that sufficient bits are available
* before using the value.
*/
static int lzx_accumulate ( struct lzx *lzx, unsigned int bits ) {
const uint16_t *src16;
/* Accumulate more bits if required */
if ( ( lzx->bits < bits ) &&
( lzx->input.offset < lzx->input.len ) ) {
src16 = (const uint16_t *)( ( char * ) lzx->input.data + lzx->input.offset );
lzx->input.offset += sizeof ( *src16 );
lzx->accumulator |= ( *src16 << ( 16 - lzx->bits ) );
lzx->bits += 16;
}
return ( lzx->accumulator >> 16 );
}
/**
* Consume accumulated bits from LZX bitstream
*
* @v lzx Decompressor
* @v bits Number of bits to consume
* @ret rc Return status code
*/
static int lzx_consume ( struct lzx *lzx, unsigned int bits ) {
/* Fail if insufficient bits are available */
if ( lzx->bits < bits ) {
DBG ( "LZX input overrun in %#zx/%#zx out %#zx)\n",
lzx->input.offset, lzx->input.len, lzx->output.offset );
return -1;
}
/* Consume bits */
lzx->accumulator <<= bits;
lzx->bits -= bits;
return 0;
}
/**
* Get bits from LZX bitstream
*
* @v lzx Decompressor
* @v bits Number of bits to fetch
* @ret value Value, or negative error
*/
static int lzx_getbits ( struct lzx *lzx, unsigned int bits ) {
int norm_value;
int rc;
/* Accumulate more bits if required */
norm_value = lzx_accumulate ( lzx, bits );
/* Consume bits */
if ( ( rc = lzx_consume ( lzx, bits ) ) != 0 )
return rc;
return ( norm_value >> ( 16 - bits ) );
}
/**
* Align LZX bitstream for byte access
*
* @v lzx Decompressor
* @v bits Minimum number of padding bits
* @ret rc Return status code
*/
static int lzx_align ( struct lzx *lzx, unsigned int bits ) {
int pad;
/* Get padding bits */
pad = lzx_getbits ( lzx, bits );
if ( pad < 0 )
return pad;
/* Consume all accumulated bits */
lzx_consume ( lzx, lzx->bits );
return 0;
}
/**
* Get bytes from LZX bitstream
*
* @v lzx Decompressor
* @v data Data buffer, or NULL
* @v len Length of data buffer
* @ret rc Return status code
*/
static int lzx_getbytes ( struct lzx *lzx, void *data, size_t len ) {
/* Sanity check */
if ( ( lzx->input.offset + len ) > lzx->input.len ) {
DBG ( "LZX input overrun in %#zx/%#zx out %#zx)\n",
lzx->input.offset, lzx->input.len, lzx->output.offset );
return -1;
}
/* Copy data */
if ( data )
memcpy ( data, ( lzx->input.data + lzx->input.offset ), len );
lzx->input.offset += len;
return 0;
}
/**
* Decode LZX Huffman-coded symbol
*
* @v lzx Decompressor
* @v alphabet Huffman alphabet
* @ret raw Raw symbol, or negative error
*/
static int lzx_decode ( struct lzx *lzx, struct huffman_alphabet *alphabet ) {
struct huffman_symbols *sym;
int huf;
int rc;
/* Accumulate sufficient bits */
huf = lzx_accumulate ( lzx, HUFFMAN_BITS );
if ( huf < 0 )
return huf;
/* Decode symbol */
sym = huffman_sym ( alphabet, huf );
/* Consume bits */
if ( ( rc = lzx_consume ( lzx, huffman_len ( sym ) ) ) != 0 )
return rc;
return huffman_raw ( sym, huf );
}
/**
* Generate Huffman alphabet from raw length table
*
* @v lzx Decompressor
* @v count Number of symbols
* @v bits Length of each length (in bits)
* @v lengths Lengths table to fill in
* @v alphabet Huffman alphabet to fill in
* @ret rc Return status code
*/
static int lzx_raw_alphabet ( struct lzx *lzx, unsigned int count,
unsigned int bits, uint8_t *lengths,
struct huffman_alphabet *alphabet ) {
unsigned int i;
int len;
int rc;
/* Read lengths */
for ( i = 0 ; i < count ; i++ ) {
len = lzx_getbits ( lzx, bits );
if ( len < 0 )
return len;
lengths[i] = len;
}
/* Generate Huffman alphabet */
if ( ( rc = huffman_alphabet ( alphabet, lengths, count ) ) != 0 )
return rc;
return 0;
}
/**
* Generate pretree
*
* @v lzx Decompressor
* @v count Number of symbols
* @v lengths Lengths table to fill in
* @ret rc Return status code
*/
static int lzx_pretree ( struct lzx *lzx, unsigned int count,
uint8_t *lengths ) {
unsigned int i;
unsigned int length;
int dup = 0;
int code;
int rc;
/* Generate pretree alphabet */
if ( ( rc = lzx_raw_alphabet ( lzx, LZX_PRETREE_CODES,
LZX_PRETREE_BITS, lzx->pretree_lengths,
&lzx->pretree ) ) != 0 )
return rc;
/* Read lengths */
for ( i = 0 ; i < count ; i++ ) {
if ( dup ) {
/* Duplicate previous length */
lengths[i] = lengths[ i - 1 ];
dup--;
} else {
/* Get next code */
code = lzx_decode ( lzx, &lzx->pretree );
if ( code < 0 )
return code;
/* Interpret code */
if ( code <= 16 ) {
length = ( ( lengths[i] - code + 17 ) % 17 );
} else if ( code == 17 ) {
length = 0;
dup = lzx_getbits ( lzx, 4 );
if ( dup < 0 )
return dup;
dup += 3;
} else if ( code == 18 ) {
length = 0;
dup = lzx_getbits ( lzx, 5 );
if ( dup < 0 )
return dup;
dup += 19;
} else if ( code == 19 ) {
length = 0;
dup = lzx_getbits ( lzx, 1 );
if ( dup < 0 )
return dup;
dup += 3;
code = lzx_decode ( lzx, &lzx->pretree );
if ( code < 0 )
return code;
length = ( ( lengths[i] - code + 17 ) % 17 );
} else {
DBG ( "Unrecognised pretree code %d\n", code );
return -1;
}
lengths[i] = length;
}
}
/* Sanity check */
if ( dup ) {
DBG ( "Pretree duplicate overrun\n" );
return -1;
}
return 0;
}
/**
* Generate aligned offset Huffman alphabet
*
* @v lzx Decompressor
* @ret rc Return status code
*/
static int lzx_alignoffset_alphabet ( struct lzx *lzx ) {
int rc;
/* Generate aligned offset alphabet */
if ( ( rc = lzx_raw_alphabet ( lzx, LZX_ALIGNOFFSET_CODES,
LZX_ALIGNOFFSET_BITS,
lzx->alignoffset_lengths,
&lzx->alignoffset ) ) != 0 )
return rc;
return 0;
}
/**
* Generate main Huffman alphabet
*
* @v lzx Decompressor
* @ret rc Return status code
*/
static int lzx_main_alphabet ( struct lzx *lzx ) {
int rc;
/* Generate literal symbols pretree */
if ( ( rc = lzx_pretree ( lzx, LZX_MAIN_LIT_CODES,
lzx->main_lengths.literals ) ) != 0 ) {
DBG ( "Could not construct main literal pretree\n" );
return rc;
}
/* Generate remaining symbols pretree */
if ( ( rc = lzx_pretree ( lzx, ( LZX_MAIN_CODES - LZX_MAIN_LIT_CODES ),
lzx->main_lengths.remainder ) ) != 0 ) {
DBG ( "Could not construct main remainder pretree\n" );
return rc;
}
/* Generate Huffman alphabet */
if ( ( rc = huffman_alphabet ( &lzx->main, lzx->main_lengths.literals,
LZX_MAIN_CODES ) ) != 0 ) {
DBG ( "Could not generate main alphabet\n" );
return rc;
}
return 0;
}
/**
* Generate length Huffman alphabet
*
* @v lzx Decompressor
* @ret rc Return status code
*/
static int lzx_length_alphabet ( struct lzx *lzx ) {
int rc;
/* Generate pretree */
if ( ( rc = lzx_pretree ( lzx, LZX_LENGTH_CODES,
lzx->length_lengths ) ) != 0 ) {
DBG ( "Could not generate length pretree\n" );
return rc;
}
/* Generate Huffman alphabet */
if ( ( rc = huffman_alphabet ( &lzx->length, lzx->length_lengths,
LZX_LENGTH_CODES ) ) != 0 ) {
DBG ( "Could not generate length alphabet\n" );
return rc;
}
return 0;
}
/**
* Process LZX block header
*
* @v lzx Decompressor
* @ret rc Return status code
*/
static int lzx_block_header ( struct lzx *lzx ) {
size_t block_len;
int block_type;
int default_len;
int len_high;
int len_low;
int rc;
/* Get block type */
block_type = lzx_getbits ( lzx, LZX_BLOCK_TYPE_BITS );
if ( block_type < 0 )
return block_type;
lzx->block_type = block_type;
/* Check block length */
default_len = lzx_getbits ( lzx, 1 );
if ( default_len < 0 )
return default_len;
if ( default_len ) {
block_len = LZX_DEFAULT_BLOCK_LEN;
} else {
len_high = lzx_getbits ( lzx, 8 );
if ( len_high < 0 )
return len_high;
len_low = lzx_getbits ( lzx, 8 );
if ( len_low < 0 )
return len_low;
block_len = ( ( len_high << 8 ) | len_low );
}
lzx->output.threshold = ( lzx->output.offset + block_len );
/* Handle block type */
switch ( block_type ) {
case LZX_BLOCK_ALIGNOFFSET :
/* Generated aligned offset alphabet */
if ( ( rc = lzx_alignoffset_alphabet ( lzx ) ) != 0 )
return rc;
/* Fall through */
case LZX_BLOCK_VERBATIM :
/* Generate main alphabet */
if ( ( rc = lzx_main_alphabet ( lzx ) ) != 0 )
return rc;
/* Generate lengths alphabet */
if ( ( rc = lzx_length_alphabet ( lzx ) ) != 0 )
return rc;
break;
case LZX_BLOCK_UNCOMPRESSED :
/* Align input stream */
if ( ( rc = lzx_align ( lzx, 1 ) ) != 0 )
return rc;
/* Read new repeated offsets */
if ( ( rc = lzx_getbytes ( lzx, &lzx->repeated_offset,
sizeof ( lzx->repeated_offset )))!=0)
return rc;
break;
default:
DBG ( "Unrecognised block type %d\n", block_type );
return -1;
}
return 0;
}
/**
* Process uncompressed data
*
* @v lzx Decompressor
* @ret rc Return status code
*/
static int lzx_uncompressed ( struct lzx *lzx ) {
void *data;
size_t len;
int rc;
/* Copy bytes */
data = ( lzx->output.data ?
( lzx->output.data + lzx->output.offset ) : NULL );
len = ( lzx->output.threshold - lzx->output.offset );
if ( ( rc = lzx_getbytes ( lzx, data, len ) ) != 0 )
return rc;
/* Align input stream */
if ( len % 2 )
lzx->input.offset++;
return 0;
}
/**
* Process an LZX token
*
* @v lzx Decompressor
* @ret rc Return status code
*
* Variable names are chosen to match the LZX specification
* pseudo-code.
*/
static int lzx_token ( struct lzx *lzx ) {
unsigned int length_header;
unsigned int position_slot;
unsigned int offset_bits;
unsigned int i;
size_t match_offset;
size_t match_length;
int verbatim_bits;
int aligned_bits;
int maindata;
int length;
uint8_t *copy;
/* Get maindata symelse*/
maindata = lzx_decode ( lzx, &lzx->main );
if ( maindata < 0 )
return maindata;
/* Check for literals */
if ( maindata < LZX_MAIN_LIT_CODES ) {
if ( lzx->output.data )
lzx->output.data[lzx->output.offset] = maindata;
lzx->output.offset++;
return 0;
}
maindata -= LZX_MAIN_LIT_CODES;
/* Calculate the match length */
length_header = ( maindata & 7 );
if ( length_header == 7 ) {
length = lzx_decode ( lzx, &lzx->length );
if ( length < 0 )
return length;
} else {
length = 0;
}
match_length = ( length_header + 2 + length );
/* Calculate the position slot */
position_slot = ( maindata >> 3 );
if ( position_slot < LZX_REPEATED_OFFSETS ) {
/* Repeated offset */
match_offset = lzx->repeated_offset[position_slot];
lzx->repeated_offset[position_slot] = lzx->repeated_offset[0];
lzx->repeated_offset[0] = match_offset;
} else {
/* Non-repeated offset */
offset_bits = lzx_footer_bits ( position_slot );
if ( ( lzx->block_type == LZX_BLOCK_ALIGNOFFSET ) &&
( offset_bits >= 3 ) ) {
verbatim_bits = lzx_getbits ( lzx, ( offset_bits - 3 ));
if ( verbatim_bits < 0 )
return verbatim_bits;
verbatim_bits <<= 3;
aligned_bits = lzx_decode ( lzx, &lzx->alignoffset );
if ( aligned_bits < 0 )
return aligned_bits;
} else {
verbatim_bits = lzx_getbits ( lzx, offset_bits );
if ( verbatim_bits < 0 )
return verbatim_bits;
aligned_bits = 0;
}
match_offset = ( lzx_position_base[position_slot] +
verbatim_bits + aligned_bits - 2 );
/* Update repeated offset list */
for ( i = ( LZX_REPEATED_OFFSETS - 1 ) ; i > 0 ; i-- )
lzx->repeated_offset[i] = lzx->repeated_offset[ i - 1 ];
lzx->repeated_offset[0] = match_offset;
}
/* Copy data */
if ( match_offset > lzx->output.offset ) {
DBG ( "LZX match underrun out 0x%x offset 0x%x len 0x%x\n",
lzx->output.offset, match_offset, match_length );
return -1;
}
if ( lzx->output.data ) {
copy = &lzx->output.data[lzx->output.offset];
for ( i = 0 ; i < match_length ; i++ )
copy[i] = copy[ i - match_offset ];
}
lzx->output.offset += match_length;
return 0;
}
/**
* Translate E8 jump addresses
*
* @v lzx Decompressor
*/
static void lzx_translate_jumps ( struct lzx *lzx ) {
size_t offset;
int32_t *target;
/* Sanity check */
if ( lzx->output.offset < 10 )
return;
/* Scan for jump instructions */
for ( offset = 0 ; offset < ( lzx->output.offset - 10 ) ; offset++ ) {
/* Check for jump instruction */
if ( lzx->output.data[offset] != 0xe8 )
continue;
/* Translate jump target */
target = ( ( int32_t * ) &lzx->output.data[ offset + 1 ] );
if ( *target >= 0 ) {
if ( *target < LZX_WIM_MAGIC_FILESIZE )
*target -= offset;
} else {
if ( *target >= -( ( int32_t ) offset ) )
*target += LZX_WIM_MAGIC_FILESIZE;
}
offset += sizeof ( *target );
}
}
/**
* Decompress LZX-compressed data
*
* @v data Compressed data
* @v len Length of compressed data
* @v buf Decompression buffer, or NULL
* @ret out_len Length of decompressed data, or negative error
*/
ssize_t lzx_decompress ( const void *data, size_t len, void *buf ) {
struct lzx lzx;
unsigned int i;
int rc;
/* Sanity check */
if ( len % 2 ) {
DBG ( "LZX cannot handle odd-length input data\n" );
return -1;
}
/* Initialise global state, if required */
if ( ! lzx_position_base[ LZX_POSITION_SLOTS - 1 ] ) {
for ( i = 1 ; i < LZX_POSITION_SLOTS ; i++ ) {
lzx_position_base[i] =
( lzx_position_base[i-1] +
( 1 << lzx_footer_bits ( i - 1 ) ) );
}
}
/* Initialise decompressor */
memset ( &lzx, 0, sizeof ( lzx ) );
lzx.input.data = data;
lzx.input.len = len;
lzx.output.data = buf;
for ( i = 0 ; i < LZX_REPEATED_OFFSETS ; i++ )
lzx.repeated_offset[i] = 1;
/* Process blocks */
while ( lzx.input.offset < lzx.input.len ) {
/* Process block header */
if ( ( rc = lzx_block_header ( &lzx ) ) != 0 )
return rc;
/* Process block contents */
if ( lzx.block_type == LZX_BLOCK_UNCOMPRESSED ) {
/* Copy uncompressed data */
if ( ( rc = lzx_uncompressed ( &lzx ) ) != 0 )
return rc;
} else {
/* Process token stream */
while ( lzx.output.offset < lzx.output.threshold ) {
if ( ( rc = lzx_token ( &lzx ) ) != 0 )
return rc;
}
}
}
/* Postprocess to undo E8 jump compression */
if ( lzx.output.data )
lzx_translate_jumps ( &lzx );
return lzx.output.offset;
}

View File

@@ -0,0 +1,179 @@
#ifndef _LZX_H
#define _LZX_H
/*
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* LZX decompression
*
*/
#include "huffman.h"
/** Number of aligned offset codes */
#define LZX_ALIGNOFFSET_CODES 8
/** Aligned offset code length (in bits) */
#define LZX_ALIGNOFFSET_BITS 3
/** Number of pretree codes */
#define LZX_PRETREE_CODES 20
/** Pretree code length (in bits) */
#define LZX_PRETREE_BITS 4
/** Number of literal main codes */
#define LZX_MAIN_LIT_CODES 256
/** Number of position slots */
#define LZX_POSITION_SLOTS 30
/** Number of main codes */
#define LZX_MAIN_CODES ( LZX_MAIN_LIT_CODES + ( 8 * LZX_POSITION_SLOTS ) )
/** Number of length codes */
#define LZX_LENGTH_CODES 249
/** Block type length (in bits) */
#define LZX_BLOCK_TYPE_BITS 3
/** Default block length */
#define LZX_DEFAULT_BLOCK_LEN 32768
/** Number of repeated offsets */
#define LZX_REPEATED_OFFSETS 3
/** Don't ask */
#define LZX_WIM_MAGIC_FILESIZE 12000000
/** Block types */
enum lzx_block_type {
/** Verbatim block */
LZX_BLOCK_VERBATIM = 1,
/** Aligned offset block */
LZX_BLOCK_ALIGNOFFSET = 2,
/** Uncompressed block */
LZX_BLOCK_UNCOMPRESSED = 3,
};
/** An LZX input stream */
struct lzx_input_stream {
/** Data */
const uint8_t *data;
/** Length */
size_t len;
/** Offset within stream */
size_t offset;
};
/** An LZX output stream */
struct lzx_output_stream {
/** Data, or NULL */
uint8_t *data;
/** Offset within stream */
size_t offset;
/** End of current block within stream */
size_t threshold;
};
/** LZX decompressor */
struct lzx {
/** Input stream */
struct lzx_input_stream input;
/** Output stream */
struct lzx_output_stream output;
/** Accumulator */
uint32_t accumulator;
/** Number of bits in accumulator */
unsigned int bits;
/** Block type */
enum lzx_block_type block_type;
/** Repeated offsets */
unsigned int repeated_offset[LZX_REPEATED_OFFSETS];
/** Aligned offset Huffman alphabet */
struct huffman_alphabet alignoffset;
/** Aligned offset raw symbols
*
* Must immediately follow the aligned offset Huffman
* alphabet.
*/
huffman_raw_symbol_t alignoffset_raw[LZX_ALIGNOFFSET_CODES];
/** Aligned offset code lengths */
uint8_t alignoffset_lengths[LZX_ALIGNOFFSET_CODES];
/** Pretree Huffman alphabet */
struct huffman_alphabet pretree;
/** Pretree raw symbols
*
* Must immediately follow the pretree Huffman alphabet.
*/
huffman_raw_symbol_t pretree_raw[LZX_PRETREE_CODES];
/** Preetree code lengths */
uint8_t pretree_lengths[LZX_PRETREE_CODES];
/** Main Huffman alphabet */
struct huffman_alphabet main;
/** Main raw symbols
*
* Must immediately follow the main Huffman alphabet.
*/
huffman_raw_symbol_t main_raw[LZX_MAIN_CODES];
/** Main code lengths */
struct {
/** Literals */
uint8_t literals[LZX_MAIN_LIT_CODES];
/** Remaining symbols */
uint8_t remainder[ LZX_MAIN_CODES - LZX_MAIN_LIT_CODES ];
} __attribute__ (( packed )) main_lengths;
/** Length Huffman alphabet */
struct huffman_alphabet length;
/** Length raw symbols
*
* Must immediately follow the length Huffman alphabet.
*/
huffman_raw_symbol_t length_raw[LZX_LENGTH_CODES];
/** Length code lengths */
uint8_t length_lengths[LZX_LENGTH_CODES];
};
/**
* Calculate number of footer bits for a given position slot
*
* @v position_slot Position slot
* @ret footer_bits Number of footer bits
*/
static inline unsigned int lzx_footer_bits ( unsigned int position_slot ) {
if ( position_slot < 2 ) {
return 0;
} else if ( position_slot < 38 ) {
return ( ( position_slot / 2 ) - 1 );
} else {
return 17;
}
}
extern ssize_t lzx_decompress ( const void *data, size_t len, void *buf );
#endif /* _LZX_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,577 @@
/******************************************************************************
* ventoy_def.h
*
* Copyright (c) 2020, longpanda <admin@ventoy.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef __VENTOY_DEF_H__
#define __VENTOY_DEF_H__
#define VTOY_MAX_SCRIPT_BUF (4 * 1024 * 1024)
#define VTOY_SIZE_1GB 1073741824
#define JSON_SUCCESS 0
#define JSON_FAILED 1
#define JSON_NOT_FOUND 2
#define ulong unsigned long
#define ulonglong unsigned long long
#define vtoy_to_upper(c) (((char)(c) >= 'a' && (char)(c) <= 'z') ? ((char)(c) - 'a' + 'A') : (char)(c))
#define VENTOY_CMD_RETURN(err) grub_errno = (err); return (err)
#define VENTOY_FILE_TYPE (GRUB_FILE_TYPE_NO_DECOMPRESS | GRUB_FILE_TYPE_LINUX_INITRD)
#define ventoy_env_op1(op, a) grub_env_##op(a)
#define ventoy_env_op2(op, a, b) grub_env_##op((a), (b))
#define ventoy_get_env(key) ventoy_env_op1(get, key)
#define ventoy_set_env(key, val) ventoy_env_op2(set, key, val)
typedef struct ventoy_initrd_ctx
{
const char *path_prefix;
const char *dir_prefix;
}ventoy_initrd_ctx;
typedef struct cmd_para
{
const char *name;
grub_extcmd_func_t func;
grub_command_flags_t flags;
const struct grub_arg_option *parser;
const char *summary;
const char *description;
grub_extcmd_t cmd;
}cmd_para;
#define ventoy_align(value, align) (((value) + ((align) - 1)) & (~((align) - 1)))
#pragma pack(1)
typedef struct cpio_newc_header
{
char c_magic[6];
char c_ino[8];
char c_mode[8];
char c_uid[8];
char c_gid[8];
char c_nlink[8];
char c_mtime[8];
char c_filesize[8];
char c_devmajor[8];
char c_devminor[8];
char c_rdevmajor[8];
char c_rdevminor[8];
char c_namesize[8];
char c_check[8];
}cpio_newc_header;
#pragma pack()
#define cmd_raw_name ctxt->extcmd->cmd->name
#define check_free(p, func) if (p) { func(p); p = NULL; }
typedef int (*grub_char_check_func)(int c);
#define ventoy_is_decimal(str) ventoy_string_check(str, grub_isdigit)
// El Torito Boot Record Volume Descriptor
#pragma pack(1)
typedef struct eltorito_descriptor
{
grub_uint8_t type;
grub_uint8_t id[5];
grub_uint8_t version;
grub_uint8_t system_id[32];
grub_uint8_t reserved[32];
grub_uint32_t sector;
}eltorito_descriptor;
typedef struct ventoy_iso9660_override
{
grub_uint32_t first_sector;
grub_uint32_t first_sector_be;
grub_uint32_t size;
grub_uint32_t size_be;
}ventoy_iso9660_override;
typedef struct ventoy_udf_override
{
grub_uint32_t length;
grub_uint32_t position;
}ventoy_udf_override;
#pragma pack()
typedef struct img_info
{
char path[512];
char name[256];
int id;
grub_uint64_t size;
int select;
void *parent;
struct img_info *next;
struct img_info *prev;
}img_info;
typedef struct img_iterator_node
{
struct img_iterator_node *next;
img_info **tail;
char dir[400];
int dirlen;
int isocnt;
int done;
int select;
struct img_iterator_node *parent;
struct img_iterator_node *firstchild;
void *firstiso;
}img_iterator_node;
typedef struct initrd_info
{
char name[256];
grub_uint64_t offset;
grub_uint64_t size;
grub_uint8_t iso_type; // 0: iso9660 1:udf
grub_uint32_t udf_start_block;
grub_uint64_t override_offset;
grub_uint32_t override_length;
char override_data[32];
struct initrd_info *next;
struct initrd_info *prev;
}initrd_info;
extern initrd_info *g_initrd_img_list;
extern initrd_info *g_initrd_img_tail;
extern int g_initrd_img_count;
extern int g_valid_initrd_count;
extern img_info *g_ventoy_img_list;
extern int g_ventoy_img_count;
extern grub_uint8_t *g_ventoy_cpio_buf;
extern grub_uint32_t g_ventoy_cpio_size;
extern cpio_newc_header *g_ventoy_initrd_head;
extern grub_uint8_t *g_ventoy_runtime_buf;
extern ventoy_guid g_ventoy_guid;
extern ventoy_img_chunk_list g_img_chunk_list;
extern int g_ventoy_debug;
void ventoy_debug(const char *fmt, ...);
#define debug(fmt, ...) if (g_ventoy_debug) ventoy_debug("[VTOY]: "fmt, __VA_ARGS__)
#define FLAG_HEADER_RESERVED 0x00000001
#define FLAG_HEADER_COMPRESSION 0x00000002
#define FLAG_HEADER_READONLY 0x00000004
#define FLAG_HEADER_SPANNED 0x00000008
#define FLAG_HEADER_RESOURCE_ONLY 0x00000010
#define FLAG_HEADER_METADATA_ONLY 0x00000020
#define FLAG_HEADER_WRITE_IN_PROGRESS 0x00000040
#define FLAG_HEADER_RP_FIX 0x00000080 // reparse point fixup
#define FLAG_HEADER_COMPRESS_RESERVED 0x00010000
#define FLAG_HEADER_COMPRESS_XPRESS 0x00020000
#define FLAG_HEADER_COMPRESS_LZX 0x00040000
#define RESHDR_FLAG_FREE 0x01
#define RESHDR_FLAG_METADATA 0x02
#define RESHDR_FLAG_COMPRESSED 0x04
#define RESHDR_FLAG_SPANNED 0x08
#pragma pack(1)
/* A WIM resource header */
typedef struct wim_resource_header
{
grub_uint64_t size_in_wim:56; /* Compressed length */
grub_uint64_t flags:8; /* flags */
grub_uint64_t offset; /* Offset */
grub_uint64_t raw_size; /* Uncompressed length */
}wim_resource_header;
/* WIM resource header length mask */
#define WIM_RESHDR_ZLEN_MASK 0x00ffffffffffffffULL
/* WIM resource header flags */
typedef enum wim_resource_header_flags
{
WIM_RESHDR_METADATA = ( 0x02ULL << 56 ), /* Resource contains metadata */
WIM_RESHDR_COMPRESSED = ( 0x04ULL << 56 ), /* Resource is compressed */
WIM_RESHDR_PACKED_STREAMS = ( 0x10ULL << 56 ), /* Resource is compressed using packed streams */
}wim_resource_header_flags;
#define WIM_HEAD_SIGNATURE "MSWIM\0\0"
/* WIM header */
typedef struct wim_header
{
grub_uint8_t signature[8]; /* Signature */
grub_uint32_t header_len; /* Header length */
grub_uint32_t version; /* Verson */
grub_uint32_t flags; /* Flags */
grub_uint32_t chunk_len; /* Chunk length */
grub_uint8_t guid[16]; /* GUID */
grub_uint16_t part; /* Part number */
grub_uint16_t parts; /* Total number of parts */
grub_uint32_t images; /* number of images */
wim_resource_header lookup; /* Lookup table */
wim_resource_header xml; /* XML data */
wim_resource_header metadata; /* Boot metadata */
grub_uint32_t boot_index; /* Boot index */
wim_resource_header integrity; /* Integrity table */
grub_uint8_t reserved[60]; /* Reserved */
} wim_header;
/* WIM header flags */
typedef enum wim_header_flags
{
WIM_HDR_XPRESS = 0x00020000, /* WIM uses Xpress compresson */
WIM_HDR_LZX = 0x00040000, /* WIM uses LZX compression */
}wim_header_flags;
/* A WIM file hash */
typedef struct wim_hash
{
/* SHA-1 hash */
grub_uint8_t sha1[20];
}wim_hash;
/* A WIM lookup table entry */
typedef struct wim_lookup_entry
{
wim_resource_header resource; /* Resource header */
grub_uint16_t part; /* Part number */
grub_uint32_t refcnt; /* Reference count */
wim_hash hash; /* Hash */
}wim_lookup_entry;
/* WIM chunk length */
#define WIM_CHUNK_LEN 32768
/* A WIM chunk buffer */
typedef struct wim_chunk_buffer
{
grub_uint8_t data[WIM_CHUNK_LEN]; /*Data */
}wim_chunk_buffer;
/* Security data */
typedef struct wim_security_header
{
grub_uint32_t len; /* Length */
grub_uint32_t count; /* Number of entries */
}wim_security_header;
/* Directory entry */
typedef struct wim_directory_entry
{
grub_uint64_t len; /* Length */
grub_uint32_t attributes; /* Attributes */
grub_uint32_t security; /* Security ID */
grub_uint64_t subdir; /* Subdirectory offset */
grub_uint8_t reserved1[16]; /* Reserved */
grub_uint64_t created; /* Creation time */
grub_uint64_t accessed; /* Last access time */
grub_uint64_t written; /* Last written time */
wim_hash hash; /* Hash */
grub_uint8_t reserved2[12]; /* Reserved */
grub_uint16_t streams; /* Streams */
grub_uint16_t short_name_len; /* Short name length */
grub_uint16_t name_len; /* Name length */
}wim_directory_entry;
/** Normal file */
#define WIM_ATTR_NORMAL 0x00000080UL
/** No security information exists for this file */
#define WIM_NO_SECURITY 0xffffffffUL
#pragma pack()
typedef struct wim_tail
{
grub_uint32_t wim_raw_size;
grub_uint32_t wim_align_size;
grub_uint8_t iso_type;
grub_uint64_t file_offset;
grub_uint32_t udf_start_block;
grub_uint64_t fe_entry_size_offset;
grub_uint64_t override_offset;
grub_uint32_t override_len;
grub_uint8_t override_data[32];
wim_header wim_header;
wim_hash bin_hash;
grub_uint32_t jump_exe_len;
grub_uint8_t *jump_bin_data;
grub_uint32_t bin_raw_len;
grub_uint32_t bin_align_len;
grub_uint8_t *new_meta_data;
grub_uint32_t new_meta_len;
grub_uint32_t new_meta_align_len;
grub_uint8_t *new_lookup_data;
grub_uint32_t new_lookup_len;
grub_uint32_t new_lookup_align_len;
}wim_tail;
typedef enum _JSON_TYPE
{
JSON_TYPE_NUMBER = 0,
JSON_TYPE_STRING,
JSON_TYPE_BOOL,
JSON_TYPE_ARRAY,
JSON_TYPE_OBJECT,
JSON_TYPE_NULL,
JSON_TYPE_BUTT
}JSON_TYPE;
typedef struct _VTOY_JSON
{
struct _VTOY_JSON *pstPrev;
struct _VTOY_JSON *pstNext;
struct _VTOY_JSON *pstChild;
JSON_TYPE enDataType;
union
{
char *pcStrVal;
int iNumVal;
grub_uint64_t lValue;
}unData;
char *pcName;
}VTOY_JSON;
typedef struct _JSON_PARSE
{
char *pcKey;
void *pDataBuf;
grub_uint32_t uiBufSize;
}JSON_PARSE;
#define JSON_NEW_ITEM(pstJson, ret) \
{ \
(pstJson) = (VTOY_JSON *)grub_zalloc(sizeof(VTOY_JSON)); \
if (NULL == (pstJson)) \
{ \
json_debug("Failed to alloc memory for json.\n"); \
return (ret); \
} \
}
typedef int (*ventoy_plugin_entry_pf)(VTOY_JSON *json, const char *isodisk);
typedef struct plugin_entry
{
const char *key;
ventoy_plugin_entry_pf entryfunc;
}plugin_entry;
void ventoy_fill_os_param(grub_file_t file, ventoy_os_param *param);
grub_err_t ventoy_cmd_isolinux_initrd_collect(grub_extcmd_context_t ctxt, int argc, char **args);
grub_err_t ventoy_cmd_grub_initrd_collect(grub_extcmd_context_t ctxt, int argc, char **args);
grub_err_t ventoy_cmd_specify_initrd_file(grub_extcmd_context_t ctxt, int argc, char **args);
grub_err_t ventoy_cmd_dump_initrd_list(grub_extcmd_context_t ctxt, int argc, char **args);
grub_err_t ventoy_cmd_clear_initrd_list(grub_extcmd_context_t ctxt, int argc, char **args);
grub_uint32_t ventoy_get_iso_boot_catlog(grub_file_t file);
int ventoy_has_efi_eltorito(grub_file_t file, grub_uint32_t sector);
grub_err_t ventoy_cmd_linux_chain_data(grub_extcmd_context_t ctxt, int argc, char **args);
grub_err_t ventoy_cmd_linux_locate_initrd(grub_extcmd_context_t ctxt, int argc, char **args);
grub_err_t ventoy_cmd_initrd_count(grub_extcmd_context_t ctxt, int argc, char **args);
grub_err_t ventoy_cmd_valid_initrd_count(grub_extcmd_context_t ctxt, int argc, char **args);
grub_err_t ventoy_cmd_load_cpio(grub_extcmd_context_t ctxt, int argc, char **args);
int ventoy_cpio_newc_fill_head(void *buf, int filesize, const void *filedata, const char *name);
grub_file_t ventoy_grub_file_open(enum grub_file_type type, const char *fmt, ...);
int ventoy_is_file_exist(const char *fmt, ...);
int ventoy_fill_data(grub_uint32_t buflen, char *buffer);
grub_err_t ventoy_cmd_load_plugin(grub_extcmd_context_t ctxt, int argc, char **args);
grub_err_t ventoy_cmd_wimdows_reset(grub_extcmd_context_t ctxt, int argc, char **args);
grub_err_t ventoy_cmd_wimdows_locate_wim(grub_extcmd_context_t ctxt, int argc, char **args);
grub_err_t ventoy_cmd_windows_chain_data(grub_extcmd_context_t ctxt, int argc, char **args);
VTOY_JSON *vtoy_json_find_item
(
VTOY_JSON *pstJson,
JSON_TYPE enDataType,
const char *szKey
);
int vtoy_json_parse_value
(
char *pcNewStart,
char *pcRawStart,
VTOY_JSON *pstJson,
const char *pcData,
const char **ppcEnd
);
VTOY_JSON * vtoy_json_create(void);
int vtoy_json_parse(VTOY_JSON *pstJson, const char *szJsonData);
int vtoy_json_scan_parse
(
const VTOY_JSON *pstJson,
grub_uint32_t uiParseNum,
JSON_PARSE *pstJsonParse
);
int vtoy_json_scan_array
(
VTOY_JSON *pstJson,
const char *szKey,
VTOY_JSON **ppstArrayItem
);
int vtoy_json_scan_array_ex
(
VTOY_JSON *pstJson,
const char *szKey,
VTOY_JSON **ppstArrayItem
);
int vtoy_json_scan_object
(
VTOY_JSON *pstJson,
const char *szKey,
VTOY_JSON **ppstObjectItem
);
int vtoy_json_get_int
(
VTOY_JSON *pstJson,
const char *szKey,
int *piValue
);
int vtoy_json_get_uint
(
VTOY_JSON *pstJson,
const char *szKey,
grub_uint32_t *puiValue
);
int vtoy_json_get_uint64
(
VTOY_JSON *pstJson,
const char *szKey,
grub_uint64_t *pui64Value
);
int vtoy_json_get_bool
(
VTOY_JSON *pstJson,
const char *szKey,
grub_uint8_t *pbValue
);
int vtoy_json_get_string
(
VTOY_JSON *pstJson,
const char *szKey,
grub_uint32_t uiBufLen,
char *pcBuf
);
const char * vtoy_json_get_string_ex(VTOY_JSON *pstJson, const char *szKey);
int vtoy_json_destroy(VTOY_JSON *pstJson);
grub_uint32_t CalculateCrc32
(
const void *Buffer,
grub_uint32_t Length,
grub_uint32_t InitValue
);
static inline int ventoy_isspace (int c)
{
return (c == '\n' || c == '\r' || c == ' ' || c == '\t');
}
static inline int ventoy_is_word_end(int c)
{
return (c == 0 || c == ',' || ventoy_isspace(c));
}
#pragma pack(1)
typedef struct ventoy_part_table
{
grub_uint8_t Active; // 0x00 0x80
grub_uint8_t StartHead;
grub_uint16_t StartSector : 6;
grub_uint16_t StartCylinder : 10;
grub_uint8_t FsFlag;
grub_uint8_t EndHead;
grub_uint16_t EndSector : 6;
grub_uint16_t EndCylinder : 10;
grub_uint32_t StartSectorId;
grub_uint32_t SectorCount;
}ventoy_part_table;
typedef struct ventoy_mbr_head
{
grub_uint8_t BootCode[446];
ventoy_part_table PartTbl[4];
grub_uint8_t Byte55;
grub_uint8_t ByteAA;
}ventoy_mbr_head;
#pragma pack()
typedef struct install_template
{
char isopath[256];
char templatepath[256];
struct install_template *next;
}install_template;
extern int g_ventoy_last_entry;
extern int g_ventoy_memdisk_mode;
extern int g_ventoy_iso_raw;
extern int g_ventoy_iso_uefi_drv;
int ventoy_cmp_img(img_info *img1, img_info *img2);
void ventoy_swap_img(img_info *img1, img_info *img2);
char * ventoy_plugin_get_install_template(const char *isopath);
void ventoy_plugin_dump_auto_install(void);
int ventoy_fill_windows_rtdata(void *buf, char *isopath);
#endif /* __VENTOY_DEF_H__ */

View File

@@ -0,0 +1,736 @@
/******************************************************************************
* ventoy_json.c
*
* Copyright (c) 2020, longpanda <admin@ventoy.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include <grub/types.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/err.h>
#include <grub/dl.h>
#include <grub/disk.h>
#include <grub/device.h>
#include <grub/term.h>
#include <grub/partition.h>
#include <grub/file.h>
#include <grub/normal.h>
#include <grub/extcmd.h>
#include <grub/datetime.h>
#include <grub/i18n.h>
#include <grub/net.h>
#include <grub/time.h>
#include <grub/ventoy.h>
#include "ventoy_def.h"
GRUB_MOD_LICENSE ("GPLv3+");
static void json_debug(const char *fmt, ...)
{
va_list args;
va_start (args, fmt);
grub_vprintf (fmt, args);
va_end (args);
grub_printf("\n");
}
static void vtoy_json_free(VTOY_JSON *pstJsonHead)
{
VTOY_JSON *pstNext = NULL;
while (NULL != pstJsonHead)
{
pstNext = pstJsonHead->pstNext;
if ((pstJsonHead->enDataType < JSON_TYPE_BUTT) && (NULL != pstJsonHead->pstChild))
{
vtoy_json_free(pstJsonHead->pstChild);
}
grub_free(pstJsonHead);
pstJsonHead = pstNext;
}
return;
}
static char *vtoy_json_skip(const char *pcData)
{
while ((NULL != pcData) && ('\0' != *pcData) && (*pcData <= 32))
{
pcData++;
}
return (char *)pcData;
}
VTOY_JSON *vtoy_json_find_item
(
VTOY_JSON *pstJson,
JSON_TYPE enDataType,
const char *szKey
)
{
while (NULL != pstJson)
{
if ((enDataType == pstJson->enDataType) &&
(0 == grub_strcmp(szKey, pstJson->pcName)))
{
return pstJson;
}
pstJson = pstJson->pstNext;
}
return NULL;
}
static int vtoy_json_parse_number
(
VTOY_JSON *pstJson,
const char *pcData,
const char **ppcEnd
)
{
unsigned long Value;
Value = grub_strtoul(pcData, (char **)ppcEnd, 10);
if (*ppcEnd == pcData)
{
json_debug("Failed to parse json number %s.", pcData);
return JSON_FAILED;
}
pstJson->enDataType = JSON_TYPE_NUMBER;
pstJson->unData.lValue = Value;
return JSON_SUCCESS;
}
static int vtoy_json_parse_string
(
char *pcNewStart,
char *pcRawStart,
VTOY_JSON *pstJson,
const char *pcData,
const char **ppcEnd
)
{
grub_uint32_t uiLen = 0;
const char *pcPos = NULL;
const char *pcTmp = pcData + 1;
*ppcEnd = pcData;
if ('\"' != *pcData)
{
return JSON_FAILED;
}
pcPos = grub_strchr(pcTmp, '\"');
if ((NULL == pcPos) || (pcPos < pcTmp))
{
json_debug("Invalid string %s.", pcData);
return JSON_FAILED;
}
*ppcEnd = pcPos + 1;
uiLen = (grub_uint32_t)(unsigned long)(pcPos - pcTmp);
pstJson->enDataType = JSON_TYPE_STRING;
pstJson->unData.pcStrVal = pcNewStart + (pcTmp - pcRawStart);
pstJson->unData.pcStrVal[uiLen] = '\0';
return JSON_SUCCESS;
}
static int vtoy_json_parse_array
(
char *pcNewStart,
char *pcRawStart,
VTOY_JSON *pstJson,
const char *pcData,
const char **ppcEnd
)
{
int Ret = JSON_SUCCESS;
VTOY_JSON *pstJsonChild = NULL;
VTOY_JSON *pstJsonItem = NULL;
const char *pcTmp = pcData + 1;
*ppcEnd = pcData;
pstJson->enDataType = JSON_TYPE_ARRAY;
if ('[' != *pcData)
{
return JSON_FAILED;
}
pcTmp = vtoy_json_skip(pcTmp);
if (']' == *pcTmp)
{
*ppcEnd = pcTmp + 1;
return JSON_SUCCESS;
}
JSON_NEW_ITEM(pstJson->pstChild, JSON_FAILED);
Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJson->pstChild, pcTmp, ppcEnd);
if (JSON_SUCCESS != Ret)
{
json_debug("Failed to parse array child.");
return JSON_FAILED;
}
pstJsonChild = pstJson->pstChild;
pcTmp = vtoy_json_skip(*ppcEnd);
while ((NULL != pcTmp) && (',' == *pcTmp))
{
JSON_NEW_ITEM(pstJsonItem, JSON_FAILED);
pstJsonChild->pstNext = pstJsonItem;
pstJsonItem->pstPrev = pstJsonChild;
pstJsonChild = pstJsonItem;
Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);
if (JSON_SUCCESS != Ret)
{
json_debug("Failed to parse array child.");
return JSON_FAILED;
}
pcTmp = vtoy_json_skip(*ppcEnd);
}
if ((NULL != pcTmp) && (']' == *pcTmp))
{
*ppcEnd = pcTmp + 1;
return JSON_SUCCESS;
}
else
{
*ppcEnd = pcTmp;
return JSON_FAILED;
}
}
static int vtoy_json_parse_object
(
char *pcNewStart,
char *pcRawStart,
VTOY_JSON *pstJson,
const char *pcData,
const char **ppcEnd
)
{
int Ret = JSON_SUCCESS;
VTOY_JSON *pstJsonChild = NULL;
VTOY_JSON *pstJsonItem = NULL;
const char *pcTmp = pcData + 1;
*ppcEnd = pcData;
pstJson->enDataType = JSON_TYPE_OBJECT;
if ('{' != *pcData)
{
return JSON_FAILED;
}
pcTmp = vtoy_json_skip(pcTmp);
if ('}' == *pcTmp)
{
*ppcEnd = pcTmp + 1;
return JSON_SUCCESS;
}
JSON_NEW_ITEM(pstJson->pstChild, JSON_FAILED);
Ret = vtoy_json_parse_string(pcNewStart, pcRawStart, pstJson->pstChild, pcTmp, ppcEnd);
if (JSON_SUCCESS != Ret)
{
json_debug("Failed to parse array child.");
return JSON_FAILED;
}
pstJsonChild = pstJson->pstChild;
pstJsonChild->pcName = pstJsonChild->unData.pcStrVal;
pstJsonChild->unData.pcStrVal = NULL;
pcTmp = vtoy_json_skip(*ppcEnd);
if ((NULL == pcTmp) || (':' != *pcTmp))
{
*ppcEnd = pcTmp;
return JSON_FAILED;
}
Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);
if (JSON_SUCCESS != Ret)
{
json_debug("Failed to parse array child.");
return JSON_FAILED;
}
pcTmp = vtoy_json_skip(*ppcEnd);
while ((NULL != pcTmp) && (',' == *pcTmp))
{
JSON_NEW_ITEM(pstJsonItem, JSON_FAILED);
pstJsonChild->pstNext = pstJsonItem;
pstJsonItem->pstPrev = pstJsonChild;
pstJsonChild = pstJsonItem;
Ret = vtoy_json_parse_string(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);
if (JSON_SUCCESS != Ret)
{
json_debug("Failed to parse array child.");
return JSON_FAILED;
}
pcTmp = vtoy_json_skip(*ppcEnd);
pstJsonChild->pcName = pstJsonChild->unData.pcStrVal;
pstJsonChild->unData.pcStrVal = NULL;
if ((NULL == pcTmp) || (':' != *pcTmp))
{
*ppcEnd = pcTmp;
return JSON_FAILED;
}
Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);
if (JSON_SUCCESS != Ret)
{
json_debug("Failed to parse array child.");
return JSON_FAILED;
}
pcTmp = vtoy_json_skip(*ppcEnd);
}
if ((NULL != pcTmp) && ('}' == *pcTmp))
{
*ppcEnd = pcTmp + 1;
return JSON_SUCCESS;
}
else
{
*ppcEnd = pcTmp;
return JSON_FAILED;
}
}
int vtoy_json_parse_value
(
char *pcNewStart,
char *pcRawStart,
VTOY_JSON *pstJson,
const char *pcData,
const char **ppcEnd
)
{
pcData = vtoy_json_skip(pcData);
switch (*pcData)
{
case 'n':
{
if (0 == grub_strncmp(pcData, "null", 4))
{
pstJson->enDataType = JSON_TYPE_NULL;
*ppcEnd = pcData + 4;
return JSON_SUCCESS;
}
break;
}
case 'f':
{
if (0 == grub_strncmp(pcData, "false", 5))
{
pstJson->enDataType = JSON_TYPE_BOOL;
pstJson->unData.lValue = 0;
*ppcEnd = pcData + 5;
return JSON_SUCCESS;
}
break;
}
case 't':
{
if (0 == grub_strncmp(pcData, "true", 4))
{
pstJson->enDataType = JSON_TYPE_BOOL;
pstJson->unData.lValue = 1;
*ppcEnd = pcData + 4;
return JSON_SUCCESS;
}
break;
}
case '\"':
{
return vtoy_json_parse_string(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd);
}
case '[':
{
return vtoy_json_parse_array(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd);
}
case '{':
{
return vtoy_json_parse_object(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd);
}
case '-':
{
return vtoy_json_parse_number(pstJson, pcData, ppcEnd);
}
default :
{
if (*pcData >= '0' && *pcData <= '9')
{
return vtoy_json_parse_number(pstJson, pcData, ppcEnd);
}
}
}
*ppcEnd = pcData;
json_debug("Invalid json data %u.", (grub_uint8_t)(*pcData));
return JSON_FAILED;
}
VTOY_JSON * vtoy_json_create(void)
{
VTOY_JSON *pstJson = NULL;
pstJson = (VTOY_JSON *)grub_zalloc(sizeof(VTOY_JSON));
if (NULL == pstJson)
{
return NULL;
}
return pstJson;
}
int vtoy_json_parse(VTOY_JSON *pstJson, const char *szJsonData)
{
grub_uint32_t uiMemSize = 0;
int Ret = JSON_SUCCESS;
char *pcNewBuf = NULL;
const char *pcEnd = NULL;
uiMemSize = grub_strlen(szJsonData) + 1;
pcNewBuf = (char *)grub_malloc(uiMemSize);
if (NULL == pcNewBuf)
{
json_debug("Failed to alloc new buf.");
return JSON_FAILED;
}
grub_memcpy(pcNewBuf, szJsonData, uiMemSize);
pcNewBuf[uiMemSize - 1] = 0;
Ret = vtoy_json_parse_value(pcNewBuf, (char *)szJsonData, pstJson, szJsonData, &pcEnd);
if (JSON_SUCCESS != Ret)
{
json_debug("Failed to parse json data %s start=%p, end=%p:%s.",
szJsonData, szJsonData, pcEnd, pcEnd);
return JSON_FAILED;
}
return JSON_SUCCESS;
}
int vtoy_json_scan_parse
(
const VTOY_JSON *pstJson,
grub_uint32_t uiParseNum,
JSON_PARSE *pstJsonParse
)
{
grub_uint32_t i = 0;
const VTOY_JSON *pstJsonCur = NULL;
JSON_PARSE *pstCurParse = NULL;
for (pstJsonCur = pstJson; NULL != pstJsonCur; pstJsonCur = pstJsonCur->pstNext)
{
if ((JSON_TYPE_OBJECT == pstJsonCur->enDataType) ||
(JSON_TYPE_ARRAY == pstJsonCur->enDataType))
{
continue;
}
for (i = 0, pstCurParse = NULL; i < uiParseNum; i++)
{
if (0 == grub_strcmp(pstJsonParse[i].pcKey, pstJsonCur->pcName))
{
pstCurParse = pstJsonParse + i;
break;
}
}
if (NULL == pstCurParse)
{
continue;
}
switch (pstJsonCur->enDataType)
{
case JSON_TYPE_NUMBER:
{
if (sizeof(grub_uint32_t) == pstCurParse->uiBufSize)
{
*(grub_uint32_t *)(pstCurParse->pDataBuf) = (grub_uint32_t)pstJsonCur->unData.lValue;
}
else if (sizeof(grub_uint16_t) == pstCurParse->uiBufSize)
{
*(grub_uint16_t *)(pstCurParse->pDataBuf) = (grub_uint16_t)pstJsonCur->unData.lValue;
}
else if (sizeof(grub_uint8_t) == pstCurParse->uiBufSize)
{
*(grub_uint8_t *)(pstCurParse->pDataBuf) = (grub_uint8_t)pstJsonCur->unData.lValue;
}
else if ((pstCurParse->uiBufSize > sizeof(grub_uint64_t)))
{
grub_snprintf((char *)pstCurParse->pDataBuf, pstCurParse->uiBufSize, "%llu",
(unsigned long long)(pstJsonCur->unData.lValue));
}
else
{
json_debug("Invalid number data buf size %u.", pstCurParse->uiBufSize);
}
break;
}
case JSON_TYPE_STRING:
{
grub_strncpy((char *)pstCurParse->pDataBuf, pstJsonCur->unData.pcStrVal, pstCurParse->uiBufSize);
break;
}
case JSON_TYPE_BOOL:
{
*(grub_uint8_t *)(pstCurParse->pDataBuf) = (pstJsonCur->unData.lValue) > 0 ? 1 : 0;
break;
}
default :
{
break;
}
}
}
return JSON_SUCCESS;
}
int vtoy_json_scan_array
(
VTOY_JSON *pstJson,
const char *szKey,
VTOY_JSON **ppstArrayItem
)
{
VTOY_JSON *pstJsonItem = NULL;
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_ARRAY, szKey);
if (NULL == pstJsonItem)
{
json_debug("Key %s is not found in json data.", szKey);
return JSON_NOT_FOUND;
}
*ppstArrayItem = pstJsonItem;
return JSON_SUCCESS;
}
int vtoy_json_scan_array_ex
(
VTOY_JSON *pstJson,
const char *szKey,
VTOY_JSON **ppstArrayItem
)
{
VTOY_JSON *pstJsonItem = NULL;
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_ARRAY, szKey);
if (NULL == pstJsonItem)
{
json_debug("Key %s is not found in json data.", szKey);
return JSON_NOT_FOUND;
}
*ppstArrayItem = pstJsonItem->pstChild;
return JSON_SUCCESS;
}
int vtoy_json_scan_object
(
VTOY_JSON *pstJson,
const char *szKey,
VTOY_JSON **ppstObjectItem
)
{
VTOY_JSON *pstJsonItem = NULL;
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_OBJECT, szKey);
if (NULL == pstJsonItem)
{
json_debug("Key %s is not found in json data.", szKey);
return JSON_NOT_FOUND;
}
*ppstObjectItem = pstJsonItem;
return JSON_SUCCESS;
}
int vtoy_json_get_int
(
VTOY_JSON *pstJson,
const char *szKey,
int *piValue
)
{
VTOY_JSON *pstJsonItem = NULL;
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey);
if (NULL == pstJsonItem)
{
json_debug("Key %s is not found in json data.", szKey);
return JSON_NOT_FOUND;
}
*piValue = (int)pstJsonItem->unData.lValue;
return JSON_SUCCESS;
}
int vtoy_json_get_uint
(
VTOY_JSON *pstJson,
const char *szKey,
grub_uint32_t *puiValue
)
{
VTOY_JSON *pstJsonItem = NULL;
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey);
if (NULL == pstJsonItem)
{
json_debug("Key %s is not found in json data.", szKey);
return JSON_NOT_FOUND;
}
*puiValue = (grub_uint32_t)pstJsonItem->unData.lValue;
return JSON_SUCCESS;
}
int vtoy_json_get_uint64
(
VTOY_JSON *pstJson,
const char *szKey,
grub_uint64_t *pui64Value
)
{
VTOY_JSON *pstJsonItem = NULL;
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey);
if (NULL == pstJsonItem)
{
json_debug("Key %s is not found in json data.", szKey);
return JSON_NOT_FOUND;
}
*pui64Value = (grub_uint64_t)pstJsonItem->unData.lValue;
return JSON_SUCCESS;
}
int vtoy_json_get_bool
(
VTOY_JSON *pstJson,
const char *szKey,
grub_uint8_t *pbValue
)
{
VTOY_JSON *pstJsonItem = NULL;
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_BOOL, szKey);
if (NULL == pstJsonItem)
{
json_debug("Key %s is not found in json data.", szKey);
return JSON_NOT_FOUND;
}
*pbValue = pstJsonItem->unData.lValue > 0 ? 1 : 0;
return JSON_SUCCESS;
}
int vtoy_json_get_string
(
VTOY_JSON *pstJson,
const char *szKey,
grub_uint32_t uiBufLen,
char *pcBuf
)
{
VTOY_JSON *pstJsonItem = NULL;
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_STRING, szKey);
if (NULL == pstJsonItem)
{
json_debug("Key %s is not found in json data.", szKey);
return JSON_NOT_FOUND;
}
grub_strncpy(pcBuf, pstJsonItem->unData.pcStrVal, uiBufLen);
return JSON_SUCCESS;
}
const char * vtoy_json_get_string_ex(VTOY_JSON *pstJson, const char *szKey)
{
VTOY_JSON *pstJsonItem = NULL;
if ((NULL == pstJson) || (NULL == szKey))
{
return NULL;
}
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_STRING, szKey);
if (NULL == pstJsonItem)
{
json_debug("Key %s is not found in json data.", szKey);
return NULL;
}
return pstJsonItem->unData.pcStrVal;
}
int vtoy_json_destroy(VTOY_JSON *pstJson)
{
if (NULL == pstJson)
{
return JSON_SUCCESS;
}
if (NULL != pstJson->pstChild)
{
vtoy_json_free(pstJson->pstChild);
}
if (NULL != pstJson->pstNext)
{
vtoy_json_free(pstJson->pstNext);
}
grub_free(pstJson);
return JSON_SUCCESS;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,278 @@
/******************************************************************************
* ventoy_plugin.c
*
* Copyright (c) 2020, longpanda <admin@ventoy.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include <grub/types.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/err.h>
#include <grub/dl.h>
#include <grub/disk.h>
#include <grub/device.h>
#include <grub/term.h>
#include <grub/partition.h>
#include <grub/file.h>
#include <grub/normal.h>
#include <grub/extcmd.h>
#include <grub/datetime.h>
#include <grub/i18n.h>
#include <grub/net.h>
#include <grub/time.h>
#include <grub/ventoy.h>
#include "ventoy_def.h"
GRUB_MOD_LICENSE ("GPLv3+");
static install_template *g_install_template_head = NULL;
static int ventoy_plugin_control_entry(VTOY_JSON *json, const char *isodisk)
{
VTOY_JSON *pNode = NULL;
VTOY_JSON *pChild = NULL;
(void)isodisk;
if (json->enDataType != JSON_TYPE_ARRAY)
{
debug("Not array %d\n", json->enDataType);
return 0;
}
for (pNode = json->pstChild; pNode; pNode = pNode->pstNext)
{
if (pNode->enDataType == JSON_TYPE_OBJECT)
{
pChild = pNode->pstChild;
if (pChild->enDataType == JSON_TYPE_STRING && pChild->pcName && pChild->unData.pcStrVal)
{
ventoy_set_env(pChild->pcName, pChild->unData.pcStrVal);
}
}
}
return 0;
}
static int ventoy_plugin_theme_entry(VTOY_JSON *json, const char *isodisk)
{
const char *value;
char filepath[256];
value = vtoy_json_get_string_ex(json->pstChild, "file");
if (value)
{
if (value[0] == '/')
{
grub_snprintf(filepath, sizeof(filepath), "%s%s", isodisk, value);
}
else
{
grub_snprintf(filepath, sizeof(filepath), "%s/ventoy/%s", isodisk, value);
}
if (ventoy_is_file_exist(filepath) == 0)
{
debug("Theme file %s does not exist\n", filepath);
return 0;
}
debug("vtoy_theme %s\n", filepath);
grub_env_set("vtoy_theme", filepath);
}
value = vtoy_json_get_string_ex(json->pstChild, "gfxmode");
if (value)
{
debug("vtoy_gfxmode %s\n", value);
grub_env_set("vtoy_gfxmode", value);
}
return 0;
}
static int ventoy_plugin_auto_install_entry(VTOY_JSON *json, const char *isodisk)
{
const char *iso = NULL;
const char *script = NULL;
VTOY_JSON *pNode = NULL;
install_template *node = NULL;
install_template *next = NULL;
(void)isodisk;
if (json->enDataType != JSON_TYPE_ARRAY)
{
debug("Not array %d\n", json->enDataType);
return 0;
}
if (g_install_template_head)
{
for (node = g_install_template_head; node; node = next)
{
next = node->next;
grub_free(node);
}
g_install_template_head = NULL;
}
for (pNode = json->pstChild; pNode; pNode = pNode->pstNext)
{
iso = vtoy_json_get_string_ex(pNode->pstChild, "image");
if (iso && iso[0] == '/')
{
script = vtoy_json_get_string_ex(pNode->pstChild, "template");
if (script && script[0] == '/')
{
node = grub_zalloc(sizeof(install_template));
if (node)
{
grub_snprintf(node->isopath, sizeof(node->isopath), "%s", iso);
grub_snprintf(node->templatepath, sizeof(node->templatepath), "%s", script);
if (g_install_template_head)
{
node->next = g_install_template_head;
}
g_install_template_head = node;
}
}
}
}
return 0;
}
static plugin_entry g_plugin_entries[] =
{
{ "control", ventoy_plugin_control_entry },
{ "theme", ventoy_plugin_theme_entry },
{ "auto_install", ventoy_plugin_auto_install_entry },
};
static int ventoy_parse_plugin_config(VTOY_JSON *json, const char *isodisk)
{
int i;
VTOY_JSON *cur = json;
while (cur)
{
for (i = 0; i < (int)ARRAY_SIZE(g_plugin_entries); i++)
{
if (grub_strcmp(g_plugin_entries[i].key, cur->pcName) == 0)
{
debug("Plugin entry for %s\n", g_plugin_entries[i].key);
g_plugin_entries[i].entryfunc(cur, isodisk);
break;
}
}
cur = cur->pstNext;
}
return 0;
}
grub_err_t ventoy_cmd_load_plugin(grub_extcmd_context_t ctxt, int argc, char **args)
{
int ret = 0;
char *buf = NULL;
grub_file_t file;
VTOY_JSON *json = NULL;
(void)ctxt;
(void)argc;
file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s/ventoy/ventoy.json", args[0]);
if (!file)
{
return GRUB_ERR_NONE;
}
debug("json configuration file size %d\n", (int)file->size);
buf = grub_malloc(file->size + 1);
if (!buf)
{
grub_file_close(file);
return 1;
}
buf[file->size] = 0;
grub_file_read(file, buf, file->size);
grub_file_close(file);
json = vtoy_json_create();
if (!json)
{
return 1;
}
ret = vtoy_json_parse(json, buf);
if (ret)
{
debug("Failed to parse json string %d\n", ret);
grub_free(buf);
return 1;
}
ventoy_parse_plugin_config(json->pstChild, args[0]);
vtoy_json_destroy(json);
grub_free(buf);
VENTOY_CMD_RETURN(GRUB_ERR_NONE);
}
void ventoy_plugin_dump_auto_install(void)
{
install_template *node = NULL;
for (node = g_install_template_head; node; node = node->next)
{
grub_printf("IMAGE:<%s>\n", node->isopath);
grub_printf("SCRIPT:<%s>\n\n", node->templatepath);
}
return;
}
char * ventoy_plugin_get_install_template(const char *isopath)
{
install_template *node = NULL;
for (node = g_install_template_head; node; node = node->next)
{
if (grub_strcmp(node->isopath, isopath) == 0)
{
return node->templatepath;
}
}
return NULL;
}

View File

@@ -0,0 +1,959 @@
/******************************************************************************
* ventoy_windows.c
*
* Copyright (c) 2020, longpanda <admin@ventoy.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include <grub/types.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/err.h>
#include <grub/dl.h>
#include <grub/disk.h>
#include <grub/device.h>
#include <grub/term.h>
#include <grub/partition.h>
#include <grub/file.h>
#include <grub/normal.h>
#include <grub/extcmd.h>
#include <grub/datetime.h>
#include <grub/i18n.h>
#include <grub/net.h>
#include <grub/time.h>
#include <grub/crypto.h>
#include <grub/ventoy.h>
#include "ventoy_def.h"
GRUB_MOD_LICENSE ("GPLv3+");
wim_hash g_old_hash;
wim_tail g_wim_data;
static wim_lookup_entry *g_replace_look = NULL;
grub_ssize_t lzx_decompress ( const void *data, grub_size_t len, void *buf );
static int wim_name_cmp(const char *search, grub_uint16_t *name, grub_uint16_t namelen)
{
char c1 = vtoy_to_upper(*search);
char c2 = vtoy_to_upper(*name);
while (namelen > 0 && (c1 == c2))
{
search++;
name++;
namelen--;
c1 = vtoy_to_upper(*search);
c2 = vtoy_to_upper(*name);
}
if (namelen == 0 && *search == 0)
{
return 0;
}
return 1;
}
static int ventoy_is_pe64(grub_uint8_t *buffer)
{
grub_uint32_t pe_off;
if (buffer[0] != 'M' || buffer[1] != 'Z')
{
return 0;
}
pe_off = *(grub_uint32_t *)(buffer + 60);
if (buffer[pe_off] != 'P' || buffer[pe_off + 1] != 'E')
{
return 0;
}
if (*(grub_uint16_t *)(buffer + pe_off + 24) == 0x020b)
{
return 1;
}
return 0;
}
grub_err_t ventoy_cmd_wimdows_reset(grub_extcmd_context_t ctxt, int argc, char **args)
{
(void)ctxt;
(void)argc;
(void)args;
check_free(g_wim_data.jump_bin_data, grub_free);
check_free(g_wim_data.new_meta_data, grub_free);
check_free(g_wim_data.new_lookup_data, grub_free);
grub_memset(&g_wim_data, 0, sizeof(g_wim_data));
return 0;
}
static int ventoy_load_jump_exe(const char *path, grub_uint8_t **data, grub_uint32_t *size, wim_hash *hash)
{
grub_uint32_t i;
grub_uint32_t align;
grub_file_t file;
debug("windows load jump %s\n", path);
file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", path);
if (!file)
{
debug("Can't open file %s\n", path);
return 1;
}
align = ventoy_align((int)file->size, 2048);
debug("file %s size:%d align:%u\n", path, (int)file->size, align);
*size = (grub_uint32_t)file->size;
*data = (grub_uint8_t *)grub_malloc(align);
if ((*data) == NULL)
{
debug("Failed to alloc memory size %u\n", align);
goto end;
}
grub_file_read(file, (*data), file->size);
if (hash)
{
grub_crypto_hash(GRUB_MD_SHA1, hash->sha1, (*data), file->size);
if (g_ventoy_debug)
{
debug("%s", "jump bin 64 hash: ");
for (i = 0; i < sizeof(hash->sha1); i++)
{
ventoy_debug("%02x ", hash->sha1[i]);
}
ventoy_debug("\n");
}
}
end:
grub_file_close(file);
return 0;
}
static int ventoy_get_override_info(grub_file_t file)
{
grub_uint32_t start_block;
grub_uint64_t file_offset;
grub_uint64_t override_offset;
grub_uint32_t override_len;
grub_uint64_t fe_entry_size_offset;
if (grub_strcmp(file->fs->name, "iso9660") == 0)
{
g_wim_data.iso_type = 0;
override_len = sizeof(ventoy_iso9660_override);
override_offset = grub_iso9660_get_last_file_dirent_pos(file) + 2;
grub_file_read(file, &start_block, 1); // just read for hook trigger
file_offset = grub_iso9660_get_last_read_pos(file);
debug("iso9660 wim size:%llu override_offset:%llu file_offset:%llu\n",
(ulonglong)file->size, (ulonglong)override_offset, (ulonglong)file_offset);
}
else
{
g_wim_data.iso_type = 1;
override_len = sizeof(ventoy_udf_override);
override_offset = grub_udf_get_last_file_attr_offset(file, &start_block, &fe_entry_size_offset);
file_offset = grub_udf_get_file_offset(file);
debug("UDF wim size:%llu override_offset:%llu file_offset:%llu start_block=%u\n",
(ulonglong)file->size, (ulonglong)override_offset, (ulonglong)file_offset, start_block);
}
g_wim_data.file_offset = file_offset;
g_wim_data.udf_start_block = start_block;
g_wim_data.fe_entry_size_offset = fe_entry_size_offset;
g_wim_data.override_offset = override_offset;
g_wim_data.override_len = override_len;
return 0;
}
static int ventoy_read_resource(grub_file_t fp, wim_resource_header *head, void **buffer)
{
int decompress_len = 0;
int total_decompress = 0;
grub_uint32_t i = 0;
grub_uint32_t chunk_num = 0;
grub_uint32_t chunk_size = 0;
grub_uint32_t last_chunk_size = 0;
grub_uint32_t last_decompress_size = 0;
grub_uint32_t cur_offset = 0;
grub_uint8_t *cur_dst = NULL;
grub_uint8_t *buffer_compress = NULL;
grub_uint8_t *buffer_decompress = NULL;
grub_uint32_t *chunk_offset = NULL;
buffer_decompress = (grub_uint8_t *)grub_malloc(head->raw_size + head->size_in_wim);
if (NULL == buffer_decompress)
{
return 0;
}
grub_file_seek(fp, head->offset);
if (head->size_in_wim == head->raw_size)
{
grub_file_read(fp, buffer_decompress, head->size_in_wim);
*buffer = buffer_decompress;
return 0;
}
buffer_compress = buffer_decompress + head->raw_size;
grub_file_read(fp, buffer_compress, head->size_in_wim);
chunk_num = (head->raw_size + WIM_CHUNK_LEN - 1) / WIM_CHUNK_LEN;
cur_offset = (chunk_num - 1) * 4;
chunk_offset = (grub_uint32_t *)buffer_compress;
cur_dst = buffer_decompress;
for (i = 0; i < chunk_num - 1; i++)
{
chunk_size = (i == 0) ? chunk_offset[i] : chunk_offset[i] - chunk_offset[i - 1];
if (WIM_CHUNK_LEN == chunk_size)
{
grub_memcpy(cur_dst, buffer_compress + cur_offset, chunk_size);
decompress_len = (int)chunk_size;
}
else
{
decompress_len = (int)lzx_decompress(buffer_compress + cur_offset, chunk_size, cur_dst);
}
//debug("chunk_size:%u decompresslen:%d\n", chunk_size, decompress_len);
total_decompress += decompress_len;
cur_dst += decompress_len;
cur_offset += chunk_size;
}
/* last chunk */
last_chunk_size = (grub_uint32_t)(head->size_in_wim - cur_offset);
last_decompress_size = head->raw_size - total_decompress;
if (last_chunk_size < WIM_CHUNK_LEN && last_chunk_size == last_decompress_size)
{
debug("Last chunk %u uncompressed\n", last_chunk_size);
grub_memcpy(cur_dst, buffer_compress + cur_offset, last_chunk_size);
decompress_len = (int)last_chunk_size;
}
else
{
decompress_len = (int)lzx_decompress(buffer_compress + cur_offset, head->size_in_wim - cur_offset, cur_dst);
}
cur_dst += decompress_len;
total_decompress += decompress_len;
if (cur_dst != buffer_decompress + head->raw_size)
{
debug("head->size_in_wim:%llu head->raw_size:%llu cur_dst:%p buffer_decompress:%p total_decompress:%d\n",
(ulonglong)head->size_in_wim, (ulonglong)head->raw_size, cur_dst, buffer_decompress, total_decompress);
grub_free(buffer_decompress);
return 1;
}
*buffer = buffer_decompress;
return 0;
}
static wim_directory_entry * search_wim_dirent(wim_directory_entry *dir, const char *search_name)
{
do
{
if (dir->len && dir->name_len)
{
if (wim_name_cmp(search_name, (grub_uint16_t *)(dir + 1), dir->name_len / 2) == 0)
{
return dir;
}
}
dir = (wim_directory_entry *)((grub_uint8_t *)dir + dir->len);
} while(dir->len);
return NULL;
}
static wim_directory_entry * search_full_wim_dirent
(
void *meta_data,
wim_directory_entry *dir,
const char **path
)
{
wim_directory_entry *subdir = NULL;
wim_directory_entry *search = dir;
while (*path)
{
subdir = (wim_directory_entry *)((char *)meta_data + search->subdir);
search = search_wim_dirent(subdir, *path);
if (!search)
{
debug("%s search failed\n", *path);
}
path++;
}
return search;
}
static wim_directory_entry * search_replace_wim_dirent(void *meta_data, wim_directory_entry *dir)
{
wim_directory_entry *wim_dirent = NULL;
const char *winpeshl_path[] = { "Windows", "System32", "winpeshl.exe", NULL };
const char *pecmd_path[] = { "Windows", "System32", "PECMD.exe", NULL };
wim_dirent = search_full_wim_dirent(meta_data, dir, winpeshl_path);
if (wim_dirent)
{
return wim_dirent;
}
wim_dirent = search_full_wim_dirent(meta_data, dir, pecmd_path);
if (wim_dirent)
{
return wim_dirent;
}
return NULL;
}
static wim_lookup_entry * ventoy_find_look_entry(wim_header *header, wim_lookup_entry *lookup, wim_hash *hash)
{
grub_uint32_t i = 0;
for (i = 0; i < (grub_uint32_t)header->lookup.raw_size / sizeof(wim_lookup_entry); i++)
{
if (grub_memcmp(&lookup[i].hash, hash, sizeof(wim_hash)) == 0)
{
return lookup + i;
}
}
return NULL;
}
static wim_lookup_entry * ventoy_find_meta_entry(wim_header *header, wim_lookup_entry *lookup)
{
grub_uint32_t i = 0;
grub_uint32_t index = 0;;
if ((header == NULL) || (lookup == NULL))
{
return NULL;
}
for (i = 0; i < (grub_uint32_t)header->lookup.raw_size / sizeof(wim_lookup_entry); i++)
{
if (lookup[i].resource.flags & RESHDR_FLAG_METADATA)
{
index++;
if (index == header->boot_index)
{
return lookup + i;
}
}
}
return NULL;
}
static int ventoy_update_all_hash(void *meta_data, wim_directory_entry *dir)
{
if ((meta_data == NULL) || (dir == NULL))
{
return 0;
}
if (dir->len < sizeof(wim_directory_entry))
{
return 0;
}
do
{
if (dir->subdir == 0 && grub_memcmp(dir->hash.sha1, g_old_hash.sha1, sizeof(wim_hash)) == 0)
{
debug("find target file, name_len:%u upadte hash\n", dir->name_len);
grub_memcpy(dir->hash.sha1, &(g_wim_data.bin_hash), sizeof(wim_hash));
}
if (dir->subdir)
{
ventoy_update_all_hash(meta_data, (wim_directory_entry *)((char *)meta_data + dir->subdir));
}
dir = (wim_directory_entry *)((char *)dir + dir->len);
} while (dir->len >= sizeof(wim_directory_entry));
return 0;
}
static int ventoy_cat_exe_file_data(grub_uint32_t exe_len, grub_uint8_t *exe_data)
{
int pe64 = 0;
char file[256];
grub_uint32_t jump_len = 0;
grub_uint32_t jump_align = 0;
grub_uint8_t *jump_data = NULL;
pe64 = ventoy_is_pe64(exe_data);
grub_snprintf(file, sizeof(file), "%s/vtoyjump%d.exe", grub_env_get("vtoy_path"), pe64 ? 64 : 32);
ventoy_load_jump_exe(file, &jump_data, &jump_len, NULL);
jump_align = ventoy_align(jump_len, 16);
g_wim_data.jump_exe_len = jump_len;
g_wim_data.bin_raw_len = jump_align + sizeof(ventoy_os_param) + sizeof(ventoy_windows_data) + exe_len;
g_wim_data.bin_align_len = ventoy_align(g_wim_data.bin_raw_len, 2048);
g_wim_data.jump_bin_data = grub_malloc(g_wim_data.bin_align_len);
if (g_wim_data.jump_bin_data)
{
grub_memcpy(g_wim_data.jump_bin_data, jump_data, jump_len);
grub_memcpy(g_wim_data.jump_bin_data + jump_align + sizeof(ventoy_os_param) + sizeof(ventoy_windows_data), exe_data, exe_len);
}
debug("jump_exe_len:%u bin_raw_len:%u bin_align_len:%u\n",
g_wim_data.jump_exe_len, g_wim_data.bin_raw_len, g_wim_data.bin_align_len);
return 0;
}
int ventoy_fill_windows_rtdata(void *buf, char *isopath)
{
char *pos = NULL;
char *script = NULL;
ventoy_windows_data *data = (ventoy_windows_data *)buf;
grub_memset(data, 0, sizeof(ventoy_windows_data));
pos = grub_strstr(isopath, "/");
if (!pos)
{
return 1;
}
script = ventoy_plugin_get_install_template(pos);
if (script)
{
debug("auto install script <%s>\n", script);
grub_snprintf(data->auto_install_script, sizeof(data->auto_install_script) - 1, "%s", script);
}
else
{
debug("auto install script not found %p\n", pos);
}
return 0;
}
static int ventoy_update_before_chain(ventoy_os_param *param, char *isopath)
{
grub_uint32_t jump_align = 0;
wim_lookup_entry *meta_look = NULL;
wim_security_header *security = NULL;
wim_directory_entry *rootdir = NULL;
wim_header *head = &(g_wim_data.wim_header);
wim_lookup_entry *lookup = (wim_lookup_entry *)g_wim_data.new_lookup_data;
jump_align = ventoy_align(g_wim_data.jump_exe_len, 16);
if (g_wim_data.jump_bin_data)
{
grub_memcpy(g_wim_data.jump_bin_data + jump_align, param, sizeof(ventoy_os_param));
ventoy_fill_windows_rtdata(g_wim_data.jump_bin_data + jump_align + sizeof(ventoy_os_param), isopath);
}
grub_crypto_hash(GRUB_MD_SHA1, g_wim_data.bin_hash.sha1, g_wim_data.jump_bin_data, g_wim_data.bin_raw_len);
security = (wim_security_header *)g_wim_data.new_meta_data;
rootdir = (wim_directory_entry *)(g_wim_data.new_meta_data + ((security->len + 7) & 0xFFFFFFF8U));
/* update all winpeshl.exe dirent entry's hash */
ventoy_update_all_hash(g_wim_data.new_meta_data, rootdir);
/* update winpeshl.exe lookup entry data (hash/offset/length) */
if (g_replace_look)
{
debug("update replace lookup entry_id:%ld\n", ((long)g_replace_look - (long)lookup) / sizeof(wim_lookup_entry));
g_replace_look->resource.raw_size = g_wim_data.bin_raw_len;
g_replace_look->resource.size_in_wim = g_wim_data.bin_raw_len;
g_replace_look->resource.flags = 0;
g_replace_look->resource.offset = g_wim_data.wim_align_size;
grub_memcpy(g_replace_look->hash.sha1, g_wim_data.bin_hash.sha1, sizeof(wim_hash));
}
/* update metadata's hash */
meta_look = ventoy_find_meta_entry(head, lookup);
if (meta_look)
{
debug("find meta lookup entry_id:%ld\n", ((long)meta_look - (long)lookup) / sizeof(wim_lookup_entry));
grub_memcpy(&meta_look->resource, &head->metadata, sizeof(wim_resource_header));
grub_crypto_hash(GRUB_MD_SHA1, meta_look->hash.sha1, g_wim_data.new_meta_data, g_wim_data.new_meta_len);
}
return 0;
}
grub_err_t ventoy_cmd_wimdows_locate_wim(grub_extcmd_context_t ctxt, int argc, char **args)
{
int rc;
grub_file_t file;
grub_uint32_t exe_len;
grub_uint8_t *exe_data = NULL;
grub_uint8_t *decompress_data = NULL;
wim_lookup_entry *lookup = NULL;
wim_security_header *security = NULL;
wim_directory_entry *rootdir = NULL;
wim_directory_entry *search = NULL;
wim_header *head = &(g_wim_data.wim_header);
(void)ctxt;
(void)argc;
debug("windows locate wim start %s\n", args[0]);
file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
if (!file)
{
return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't open file %s\n", args[0]);
}
ventoy_get_override_info(file);
grub_file_seek(file, 0);
grub_file_read(file, head, sizeof(wim_header));
if (grub_memcmp(head->signature, WIM_HEAD_SIGNATURE, sizeof(head->signature)))
{
debug("Not a valid wim file %s\n", (char *)head->signature);
grub_file_close(file);
return 1;
}
if (head->flags & FLAG_HEADER_COMPRESS_XPRESS)
{
debug("Xpress compress is not supported 0x%x\n", head->flags);
grub_file_close(file);
return 1;
}
rc = ventoy_read_resource(file, &head->metadata, (void **)&decompress_data);
if (rc)
{
grub_printf("failed to read meta data %d\n", rc);
grub_file_close(file);
return 1;
}
security = (wim_security_header *)decompress_data;
rootdir = (wim_directory_entry *)(decompress_data + ((security->len + 7) & 0xFFFFFFF8U));
/* search winpeshl.exe dirent entry */
search = search_replace_wim_dirent(decompress_data, rootdir);
if (!search)
{
debug("Failed to find replace file %p\n", search);
grub_file_close(file);
return 1;
}
debug("find replace file at %p\n", search);
grub_memcpy(&g_old_hash, search->hash.sha1, sizeof(wim_hash));
debug("read lookup offset:%llu size:%llu\n", (ulonglong)head->lookup.offset, (ulonglong)head->lookup.raw_size);
lookup = grub_malloc(head->lookup.raw_size);
grub_file_seek(file, head->lookup.offset);
grub_file_read(file, lookup, head->lookup.raw_size);
/* find and extact winpeshl.exe */
g_replace_look = ventoy_find_look_entry(head, lookup, &g_old_hash);
if (g_replace_look)
{
exe_len = (grub_uint32_t)g_replace_look->resource.raw_size;
debug("find replace lookup entry_id:%ld raw_size:%u\n",
((long)g_replace_look - (long)lookup) / sizeof(wim_lookup_entry), exe_len);
if (0 == ventoy_read_resource(file, &(g_replace_look->resource), (void **)&(exe_data)))
{
ventoy_cat_exe_file_data(exe_len, exe_data);
grub_free(exe_data);
}
else
{
debug("failed to read replace file meta data %u\n", exe_len);
}
}
else
{
debug("failed to find lookup entry for replace file 0x%02x 0x%02x\n", g_old_hash.sha1[0], g_old_hash.sha1[1]);
}
g_wim_data.wim_raw_size = (grub_uint32_t)file->size;
g_wim_data.wim_align_size = ventoy_align(g_wim_data.wim_raw_size, 2048);
check_free(g_wim_data.new_meta_data, grub_free);
g_wim_data.new_meta_data = decompress_data;
g_wim_data.new_meta_len = head->metadata.raw_size;
g_wim_data.new_meta_align_len = ventoy_align(g_wim_data.new_meta_len, 2048);
check_free(g_wim_data.new_lookup_data, grub_free);
g_wim_data.new_lookup_data = (grub_uint8_t *)lookup;
g_wim_data.new_lookup_len = (grub_uint32_t)head->lookup.raw_size;
g_wim_data.new_lookup_align_len = ventoy_align(g_wim_data.new_lookup_len, 2048);
head->metadata.flags = RESHDR_FLAG_METADATA;
head->metadata.offset = g_wim_data.wim_align_size + g_wim_data.bin_align_len;
head->metadata.size_in_wim = g_wim_data.new_meta_len;
head->metadata.raw_size = g_wim_data.new_meta_len;
head->lookup.flags = 0;
head->lookup.offset = head->metadata.offset + g_wim_data.new_meta_align_len;
head->lookup.size_in_wim = g_wim_data.new_lookup_len;
head->lookup.raw_size = g_wim_data.new_lookup_len;
grub_file_close(file);
debug("%s", "windows locate wim finish\n");
VENTOY_CMD_RETURN(GRUB_ERR_NONE);
}
static grub_uint32_t ventoy_get_override_chunk_num(void)
{
/* 1: block count in Partition Descriptor */
/* 2: file_size in file_entry or extend_file_entry */
/* 3: data_size and position in extend data short ad */
/* 4: new wim file header */
return 4;
}
static void ventoy_windows_fill_override_data( grub_uint64_t isosize, void *override)
{
grub_uint32_t data32;
grub_uint64_t data64;
grub_uint64_t sector;
grub_uint32_t new_wim_size;
ventoy_override_chunk *cur;
sector = (isosize + 2047) / 2048;
cur = (ventoy_override_chunk *)override;
new_wim_size = g_wim_data.wim_align_size + g_wim_data.bin_align_len +
g_wim_data.new_meta_align_len + g_wim_data.new_lookup_align_len;
if (g_wim_data.iso_type == 0)
{
ventoy_iso9660_override *dirent = (ventoy_iso9660_override *)g_wim_data.override_data;
dirent->first_sector = (grub_uint32_t)sector;
dirent->size = new_wim_size;
dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector);
dirent->size_be = grub_swap_bytes32(dirent->size);
}
else
{
ventoy_udf_override *udf = (ventoy_udf_override *)g_wim_data.override_data;
udf->length = new_wim_size;
udf->position = (grub_uint32_t)sector - g_wim_data.udf_start_block;
}
//override 1: sector number in pd data
cur->img_offset = grub_udf_get_last_pd_size_offset();
cur->override_size = 4;
data32 = sector - g_wim_data.udf_start_block + (new_wim_size / 2048);
grub_memcpy(cur->override_data, &(data32), 4);
//override 2: filesize in file_entry
cur++;
cur->img_offset = g_wim_data.fe_entry_size_offset;
cur->override_size = 8;
data64 = new_wim_size;
grub_memcpy(cur->override_data, &(data64), 8);
/* override 3: position and length in extend data */
cur++;
cur->img_offset = g_wim_data.override_offset;
cur->override_size = g_wim_data.override_len;
grub_memcpy(cur->override_data, g_wim_data.override_data, cur->override_size);
/* override 4: new wim file header */
cur++;
cur->img_offset = g_wim_data.file_offset;
cur->override_size = sizeof(wim_header);
grub_memcpy(cur->override_data, &(g_wim_data.wim_header), cur->override_size);
return;
}
static void ventoy_windows_fill_virt_data( grub_uint64_t isosize, ventoy_chain_head *chain)
{
grub_uint64_t sector;
grub_uint32_t offset;
grub_uint32_t wim_secs;
grub_uint32_t mem_secs;
char *override = NULL;
ventoy_virt_chunk *cur = NULL;
sector = (isosize + 2047) / 2048;
offset = sizeof(ventoy_virt_chunk);
wim_secs = g_wim_data.wim_align_size / 2048;
mem_secs = (g_wim_data.bin_align_len + g_wim_data.new_meta_align_len + g_wim_data.new_lookup_align_len) / 2048;
override = (char *)chain + chain->virt_chunk_offset;
cur = (ventoy_virt_chunk *)override;
cur->remap_sector_start = sector;
cur->remap_sector_end = cur->remap_sector_start + wim_secs;
cur->org_sector_start = (grub_uint32_t)(g_wim_data.file_offset / 2048);
cur->mem_sector_start = cur->remap_sector_end;
cur->mem_sector_end = cur->mem_sector_start + mem_secs;
cur->mem_sector_offset = offset;
grub_memcpy(override + offset, g_wim_data.jump_bin_data, g_wim_data.bin_raw_len);
offset += g_wim_data.bin_align_len;
grub_memcpy(override + offset, g_wim_data.new_meta_data, g_wim_data.new_meta_len);
offset += g_wim_data.new_meta_align_len;
grub_memcpy(override + offset, g_wim_data.new_lookup_data, g_wim_data.new_lookup_len);
offset += g_wim_data.new_lookup_align_len;
chain->virt_img_size_in_bytes += g_wim_data.wim_align_size +
g_wim_data.bin_align_len +
g_wim_data.new_meta_align_len +
g_wim_data.new_lookup_align_len;
return;
}
static int ventoy_windows_drive_map(ventoy_chain_head *chain)
{
grub_disk_t disk;
debug("drive map begin <%p> ...\n", chain);
if (chain->disk_drive == 0x80)
{
disk = grub_disk_open("hd1");
if (disk)
{
grub_disk_close(disk);
debug("drive map needed %p\n", disk);
chain->drive_map = 0x81;
}
else
{
debug("failed to open disk %s\n", "hd1");
}
}
else
{
debug("no need to map 0x%x\n", chain->disk_drive);
}
return 0;
}
grub_err_t ventoy_cmd_windows_chain_data(grub_extcmd_context_t ctxt, int argc, char **args)
{
int unknown_image = 0;
int ventoy_compatible = 0;
grub_uint32_t size = 0;
grub_uint64_t isosize = 0;
grub_uint32_t boot_catlog = 0;
grub_uint32_t img_chunk_size = 0;
grub_uint32_t override_size = 0;
grub_uint32_t virt_chunk_size = 0;
grub_file_t file;
grub_disk_t disk;
const char *pLastChain = NULL;
const char *compatible;
ventoy_chain_head *chain;
char envbuf[64];
(void)ctxt;
(void)argc;
debug("chain data begin <%s> ...\n", args[0]);
compatible = grub_env_get("ventoy_compatible");
if (compatible && compatible[0] == 'Y')
{
ventoy_compatible = 1;
}
if (NULL == g_img_chunk_list.chunk)
{
grub_printf("ventoy not ready\n");
return 1;
}
if (0 == ventoy_compatible && g_wim_data.new_meta_data == NULL)
{
unknown_image = 1;
debug("Warning: %s was not recognized by Ventoy\n", args[0]);
}
file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
if (!file)
{
return 1;
}
isosize = file->size;
boot_catlog = ventoy_get_iso_boot_catlog(file);
if (boot_catlog)
{
if (ventoy_is_efi_os() && (!ventoy_has_efi_eltorito(file, boot_catlog)))
{
grub_env_set("LoadIsoEfiDriver", "on");
}
}
else
{
if (ventoy_is_efi_os())
{
grub_env_set("LoadIsoEfiDriver", "on");
}
else
{
return grub_error(GRUB_ERR_BAD_ARGUMENT, "File %s is not bootable", args[0]);
}
}
img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
if (ventoy_compatible || unknown_image)
{
size = sizeof(ventoy_chain_head) + img_chunk_size;
}
else
{
override_size = ventoy_get_override_chunk_num() * sizeof(ventoy_override_chunk);
virt_chunk_size = sizeof(ventoy_virt_chunk) + g_wim_data.bin_align_len +
g_wim_data.new_meta_align_len + g_wim_data.new_lookup_align_len;;
size = sizeof(ventoy_chain_head) + img_chunk_size + override_size + virt_chunk_size;
}
pLastChain = grub_env_get("vtoy_chain_mem_addr");
if (pLastChain)
{
chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16);
if (chain)
{
debug("free last chain memory %p\n", chain);
grub_free(chain);
}
}
chain = grub_malloc(size);
if (!chain)
{
grub_printf("Failed to alloc chain memory size %u\n", size);
grub_file_close(file);
return 1;
}
grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)chain);
grub_env_set("vtoy_chain_mem_addr", envbuf);
grub_snprintf(envbuf, sizeof(envbuf), "%u", size);
grub_env_set("vtoy_chain_mem_size", envbuf);
grub_memset(chain, 0, sizeof(ventoy_chain_head));
/* part 1: os parameter */
ventoy_fill_os_param(file, &(chain->os_param));
if (g_wim_data.jump_bin_data && g_wim_data.new_meta_data)
{
ventoy_update_before_chain(&(chain->os_param), args[0]);
}
/* part 2: chain head */
disk = file->device->disk;
chain->disk_drive = disk->id;
chain->disk_sector_size = (1 << disk->log_sector_size);
chain->real_img_size_in_bytes = file->size;
chain->virt_img_size_in_bytes = (file->size + 2047) / 2048 * 2048;
chain->boot_catalog = boot_catlog;
if (!ventoy_is_efi_os())
{
grub_file_seek(file, boot_catlog * 2048);
grub_file_read(file, chain->boot_catalog_sector, sizeof(chain->boot_catalog_sector));
}
/* part 3: image chunk */
chain->img_chunk_offset = sizeof(ventoy_chain_head);
chain->img_chunk_num = g_img_chunk_list.cur_chunk;
grub_memcpy((char *)chain + chain->img_chunk_offset, g_img_chunk_list.chunk, img_chunk_size);
if (ventoy_compatible || unknown_image)
{
return 0;
}
if (g_wim_data.new_meta_data == NULL)
{
return 0;
}
/* part 4: override chunk */
chain->override_chunk_offset = chain->img_chunk_offset + img_chunk_size;
chain->override_chunk_num = ventoy_get_override_chunk_num();
ventoy_windows_fill_override_data(isosize, (char *)chain + chain->override_chunk_offset);
/* part 5: virt chunk */
chain->virt_chunk_offset = chain->override_chunk_offset + override_size;
chain->virt_chunk_num = 1;
ventoy_windows_fill_virt_data(isosize, chain);
if (ventoy_is_efi_os() == 0)
{
ventoy_windows_drive_map(chain);
}
VENTOY_CMD_RETURN(GRUB_ERR_NONE);
}

View File

@@ -0,0 +1,65 @@
/******************************************************************************
* wimboot.h
*
* Copyright (c) 2020, longpanda <admin@ventoy.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef __WIMBOOT_H__
#define __WIMBOOT_H__
#include <grub/types.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/err.h>
#include <grub/dl.h>
#include <grub/disk.h>
#include <grub/device.h>
#include <grub/term.h>
#include <grub/partition.h>
#include <grub/file.h>
#include <grub/normal.h>
#include <grub/extcmd.h>
#include <grub/datetime.h>
#include <grub/i18n.h>
#include <grub/net.h>
#include <grub/time.h>
#include <grub/crypto.h>
#include <grub/ventoy.h>
#include "ventoy_def.h"
#define size_t grub_size_t
#define ssize_t grub_ssize_t
#define memset grub_memset
#define memcpy grub_memcpy
#define uint8_t grub_uint8_t
#define uint16_t grub_uint16_t
#define uint32_t grub_uint32_t
#define uint64_t grub_uint64_t
#define int32_t grub_int32_t
#define assert(exp)
//#define DBG grub_printf
#define DBG(fmt, ...)
const char * huffman_bin ( unsigned long value, unsigned int bits );
#endif

View File

@@ -0,0 +1,265 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GRUB_DISK_HEADER
#define GRUB_DISK_HEADER 1
#include <config.h>
#include <grub/symbol.h>
#include <grub/err.h>
#include <grub/types.h>
#include <grub/device.h>
/* For NULL. */
#include <grub/mm.h>
/* These are used to set a device id. When you add a new disk device,
you must define a new id for it here. */
enum grub_disk_dev_id
{
GRUB_DISK_DEVICE_BIOSDISK_ID,
GRUB_DISK_DEVICE_OFDISK_ID,
GRUB_DISK_DEVICE_LOOPBACK_ID,
GRUB_DISK_DEVICE_EFIDISK_ID,
GRUB_DISK_DEVICE_DISKFILTER_ID,
GRUB_DISK_DEVICE_HOST_ID,
GRUB_DISK_DEVICE_ATA_ID,
GRUB_DISK_DEVICE_MEMDISK_ID,
GRUB_DISK_DEVICE_NAND_ID,
GRUB_DISK_DEVICE_SCSI_ID,
GRUB_DISK_DEVICE_CRYPTODISK_ID,
GRUB_DISK_DEVICE_ARCDISK_ID,
GRUB_DISK_DEVICE_HOSTDISK_ID,
GRUB_DISK_DEVICE_PROCFS_ID,
GRUB_DISK_DEVICE_CBFSDISK_ID,
GRUB_DISK_DEVICE_UBOOTDISK_ID,
GRUB_DISK_DEVICE_XEN,
GRUB_DISK_DEVICE_OBDISK_ID,
};
struct grub_disk;
#ifdef GRUB_UTIL
struct grub_disk_memberlist;
#endif
typedef enum
{
GRUB_DISK_PULL_NONE,
GRUB_DISK_PULL_REMOVABLE,
GRUB_DISK_PULL_RESCAN,
GRUB_DISK_PULL_MAX
} grub_disk_pull_t;
typedef int (*grub_disk_dev_iterate_hook_t) (const char *name, void *data);
/* Disk device. */
struct grub_disk_dev
{
/* The device name. */
const char *name;
/* The device id used by the cache manager. */
enum grub_disk_dev_id id;
/* Call HOOK with each device name, until HOOK returns non-zero. */
int (*disk_iterate) (grub_disk_dev_iterate_hook_t hook, void *hook_data,
grub_disk_pull_t pull);
/* Open the device named NAME, and set up DISK. */
grub_err_t (*disk_open) (const char *name, struct grub_disk *disk);
/* Close the disk DISK. */
void (*disk_close) (struct grub_disk *disk);
/* Read SIZE sectors from the sector SECTOR of the disk DISK into BUF. */
grub_err_t (*disk_read) (struct grub_disk *disk, grub_disk_addr_t sector,
grub_size_t size, char *buf);
/* Write SIZE sectors from BUF into the sector SECTOR of the disk DISK. */
grub_err_t (*disk_write) (struct grub_disk *disk, grub_disk_addr_t sector,
grub_size_t size, const char *buf);
#ifdef GRUB_UTIL
struct grub_disk_memberlist *(*disk_memberlist) (struct grub_disk *disk);
const char * (*disk_raidname) (struct grub_disk *disk);
#endif
/* The next disk device. */
struct grub_disk_dev *next;
};
typedef struct grub_disk_dev *grub_disk_dev_t;
extern grub_disk_dev_t EXPORT_VAR (grub_disk_dev_list);
struct grub_partition;
typedef void (*grub_disk_read_hook_t) (grub_disk_addr_t sector,
unsigned offset, unsigned length,
void *data);
/* Disk. */
struct grub_disk
{
/* The disk name. */
const char *name;
/* The underlying disk device. */
grub_disk_dev_t dev;
/* The total number of sectors. */
grub_uint64_t total_sectors;
/* Logarithm of sector size. */
unsigned int log_sector_size;
/* Maximum number of sectors read divided by GRUB_DISK_CACHE_SIZE. */
unsigned int max_agglomerate;
/* The id used by the disk cache manager. */
unsigned long id;
/* The partition information. This is machine-specific. */
struct grub_partition *partition;
/* Called when a sector was read. OFFSET is between 0 and
the sector size minus 1, and LENGTH is between 0 and the sector size. */
grub_disk_read_hook_t read_hook;
/* Caller-specific data passed to the read hook. */
void *read_hook_data;
/* Device-specific data. */
void *data;
};
typedef struct grub_disk *grub_disk_t;
#ifdef GRUB_UTIL
struct grub_disk_memberlist
{
grub_disk_t disk;
struct grub_disk_memberlist *next;
};
typedef struct grub_disk_memberlist *grub_disk_memberlist_t;
#endif
/* The sector size. */
#define GRUB_DISK_SECTOR_SIZE 0x200
#define GRUB_DISK_SECTOR_BITS 9
/* The maximum number of disk caches. */
#define GRUB_DISK_CACHE_NUM 1021
/* The size of a disk cache in 512B units. Must be at least as big as the
largest supported sector size, currently 16K. */
#define GRUB_DISK_CACHE_BITS 6
#define GRUB_DISK_CACHE_SIZE (1 << GRUB_DISK_CACHE_BITS)
#define GRUB_DISK_MAX_MAX_AGGLOMERATE ((1 << (30 - GRUB_DISK_CACHE_BITS - GRUB_DISK_SECTOR_BITS)) - 1)
/* Return value of grub_disk_get_size() in case disk size is unknown. */
#define GRUB_DISK_SIZE_UNKNOWN 0xffffffffffffffffULL
/* This is called from the memory manager. */
void grub_disk_cache_invalidate_all (void);
void EXPORT_FUNC(grub_disk_dev_register) (grub_disk_dev_t dev);
void EXPORT_FUNC(grub_disk_dev_unregister) (grub_disk_dev_t dev);
static inline int
grub_disk_dev_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data)
{
grub_disk_dev_t p;
grub_disk_pull_t pull;
for (pull = 0; pull < GRUB_DISK_PULL_MAX; pull++)
for (p = grub_disk_dev_list; p; p = p->next)
if (p->disk_iterate && (p->disk_iterate) (hook, hook_data, pull))
return 1;
return 0;
}
grub_disk_t EXPORT_FUNC(grub_disk_open) (const char *name);
void EXPORT_FUNC(grub_disk_close) (grub_disk_t disk);
grub_err_t EXPORT_FUNC(grub_disk_blocklist_read)(void *chunklist, grub_uint64_t sector,
grub_uint64_t size, grub_uint32_t log_sector_size);
grub_err_t EXPORT_FUNC(grub_disk_read) (grub_disk_t disk,
grub_disk_addr_t sector,
grub_off_t offset,
grub_size_t size,
void *buf);
grub_err_t grub_disk_write (grub_disk_t disk,
grub_disk_addr_t sector,
grub_off_t offset,
grub_size_t size,
const void *buf);
extern grub_err_t (*EXPORT_VAR(grub_disk_write_weak)) (grub_disk_t disk,
grub_disk_addr_t sector,
grub_off_t offset,
grub_size_t size,
const void *buf);
grub_uint64_t EXPORT_FUNC(grub_disk_get_size) (grub_disk_t disk);
#if DISK_CACHE_STATS
void
EXPORT_FUNC(grub_disk_cache_get_performance) (unsigned long *hits, unsigned long *misses);
#endif
extern void (* EXPORT_VAR(grub_disk_firmware_fini)) (void);
extern int EXPORT_VAR(grub_disk_firmware_is_tainted);
static inline void
grub_stop_disk_firmware (void)
{
/* To prevent two drivers operating on the same disks. */
grub_disk_firmware_is_tainted = 1;
if (grub_disk_firmware_fini)
{
grub_disk_firmware_fini ();
grub_disk_firmware_fini = NULL;
}
}
/* Disk cache. */
struct grub_disk_cache
{
enum grub_disk_dev_id dev_id;
unsigned long disk_id;
grub_disk_addr_t sector;
char *data;
int lock;
};
extern struct grub_disk_cache EXPORT_VAR(grub_disk_cache_table)[GRUB_DISK_CACHE_NUM];
#if defined (GRUB_UTIL)
void grub_lvm_init (void);
void grub_ldm_init (void);
void grub_mdraid09_init (void);
void grub_mdraid1x_init (void);
void grub_diskfilter_init (void);
void grub_lvm_fini (void);
void grub_ldm_fini (void);
void grub_mdraid09_fini (void);
void grub_mdraid1x_fini (void);
void grub_diskfilter_fini (void);
#endif
#endif /* ! GRUB_DISK_HEADER */

View File

@@ -0,0 +1,124 @@
/* efi.h - declare variables and functions for EFI support */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2006,2007,2008,2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GRUB_EFI_EFI_HEADER
#define GRUB_EFI_EFI_HEADER 1
#include <grub/types.h>
#include <grub/dl.h>
#include <grub/efi/api.h>
/* Functions. */
void *EXPORT_FUNC(grub_efi_locate_protocol) (grub_efi_guid_t *protocol,
void *registration);
grub_efi_handle_t *
EXPORT_FUNC(grub_efi_locate_handle) (grub_efi_locate_search_type_t search_type,
grub_efi_guid_t *protocol,
void *search_key,
grub_efi_uintn_t *num_handles);
void *EXPORT_FUNC(grub_efi_open_protocol) (grub_efi_handle_t handle,
grub_efi_guid_t *protocol,
grub_efi_uint32_t attributes);
int EXPORT_FUNC(grub_efi_set_text_mode) (int on);
void EXPORT_FUNC(grub_efi_stall) (grub_efi_uintn_t microseconds);
void *
EXPORT_FUNC(grub_efi_allocate_pages_real) (grub_efi_physical_address_t address,
grub_efi_uintn_t pages,
grub_efi_allocate_type_t alloctype,
grub_efi_memory_type_t memtype);
void *
EXPORT_FUNC(grub_efi_allocate_fixed) (grub_efi_physical_address_t address,
grub_efi_uintn_t pages);
void *
EXPORT_FUNC(grub_efi_allocate_any_pages) (grub_efi_uintn_t pages);
void EXPORT_FUNC(grub_efi_free_pages) (grub_efi_physical_address_t address,
grub_efi_uintn_t pages);
grub_efi_uintn_t EXPORT_FUNC(grub_efi_find_mmap_size) (void);
int
EXPORT_FUNC(grub_efi_get_memory_map) (grub_efi_uintn_t *memory_map_size,
grub_efi_memory_descriptor_t *memory_map,
grub_efi_uintn_t *map_key,
grub_efi_uintn_t *descriptor_size,
grub_efi_uint32_t *descriptor_version);
void grub_efi_memory_fini (void);
grub_efi_loaded_image_t *EXPORT_FUNC(grub_efi_get_loaded_image) (grub_efi_handle_t image_handle);
void EXPORT_FUNC(grub_efi_print_device_path) (grub_efi_device_path_t *dp);
char *EXPORT_FUNC(grub_efi_get_filename) (grub_efi_device_path_t *dp);
grub_efi_device_path_t *
EXPORT_FUNC(grub_efi_get_device_path) (grub_efi_handle_t handle);
grub_efi_device_path_t *
EXPORT_FUNC(grub_efi_find_last_device_path) (const grub_efi_device_path_t *dp);
grub_efi_device_path_t *
EXPORT_FUNC(grub_efi_duplicate_device_path) (const grub_efi_device_path_t *dp);
grub_err_t EXPORT_FUNC (grub_efi_finish_boot_services) (grub_efi_uintn_t *outbuf_size, void *outbuf,
grub_efi_uintn_t *map_key,
grub_efi_uintn_t *efi_desc_size,
grub_efi_uint32_t *efi_desc_version);
grub_err_t EXPORT_FUNC (grub_efi_set_virtual_address_map) (grub_efi_uintn_t memory_map_size,
grub_efi_uintn_t descriptor_size,
grub_efi_uint32_t descriptor_version,
grub_efi_memory_descriptor_t *virtual_map);
void *EXPORT_FUNC (grub_efi_get_variable) (const char *variable,
const grub_efi_guid_t *guid,
grub_size_t *datasize_out);
grub_err_t
EXPORT_FUNC (grub_efi_set_variable) (const char *var,
const grub_efi_guid_t *guid,
void *data,
grub_size_t datasize);
int
EXPORT_FUNC (grub_efi_compare_device_paths) (const grub_efi_device_path_t *dp1,
const grub_efi_device_path_t *dp2);
void * EXPORT_FUNC (grub_efi_allocate_iso_buf) (grub_uint64_t size);
extern void (*EXPORT_VAR(grub_efi_net_config)) (grub_efi_handle_t hnd,
char **device,
char **path);
#if defined(__arm__) || defined(__aarch64__) || defined(__riscv)
void *EXPORT_FUNC(grub_efi_get_firmware_fdt)(void);
grub_err_t EXPORT_FUNC(grub_efi_get_ram_base)(grub_addr_t *);
#include <grub/cpu/linux.h>
grub_err_t grub_arch_efi_linux_check_image(struct linux_arch_kernel_header *lh);
grub_err_t grub_arch_efi_linux_boot_image(grub_addr_t addr, grub_size_t size,
char *args);
#endif
grub_addr_t grub_efi_modules_addr (void);
void grub_efi_mm_init (void);
void grub_efi_mm_fini (void);
void grub_efi_init (void);
void grub_efi_fini (void);
void grub_efi_set_prefix (void);
/* Variables. */
extern grub_efi_system_table_t *EXPORT_VAR(grub_efi_system_table);
extern grub_efi_handle_t EXPORT_VAR(grub_efi_image_handle);
extern int EXPORT_VAR(grub_efi_is_finished);
struct grub_net_card;
grub_efi_handle_t
grub_efinet_get_device_handle (struct grub_net_card *card);
#endif /* ! GRUB_EFI_EFI_HEADER */

View File

@@ -0,0 +1,132 @@
/* fs.h - filesystem manager */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2002,2003,2004,2007,2008,2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GRUB_FS_HEADER
#define GRUB_FS_HEADER 1
#include <grub/device.h>
#include <grub/symbol.h>
#include <grub/types.h>
#include <grub/list.h>
/* For embedding types. */
#ifdef GRUB_UTIL
#include <grub/partition.h>
#endif
/* Forward declaration is required, because of mutual reference. */
struct grub_file;
struct grub_dirhook_info
{
unsigned dir:1;
unsigned mtimeset:1;
unsigned case_insensitive:1;
unsigned inodeset:1;
grub_int32_t mtime;
grub_uint64_t inode;
grub_uint64_t size;
};
typedef int (*grub_fs_dir_hook_t) (const char *filename,
const struct grub_dirhook_info *info,
void *data);
/* Filesystem descriptor. */
struct grub_fs
{
/* The next filesystem. */
struct grub_fs *next;
struct grub_fs **prev;
/* My name. */
const char *name;
/* Call HOOK with each file under DIR. */
grub_err_t (*fs_dir) (grub_device_t device, const char *path,
grub_fs_dir_hook_t hook, void *hook_data);
/* Open a file named NAME and initialize FILE. */
grub_err_t (*fs_open) (struct grub_file *file, const char *name);
/* Read LEN bytes data from FILE into BUF. */
grub_ssize_t (*fs_read) (struct grub_file *file, char *buf, grub_size_t len);
/* Close the file FILE. */
grub_err_t (*fs_close) (struct grub_file *file);
/* Return the label of the device DEVICE in LABEL. The label is
returned in a grub_malloc'ed buffer and should be freed by the
caller. */
grub_err_t (*fs_label) (grub_device_t device, char **label);
/* Return the uuid of the device DEVICE in UUID. The uuid is
returned in a grub_malloc'ed buffer and should be freed by the
caller. */
grub_err_t (*fs_uuid) (grub_device_t device, char **uuid);
/* Get writing time of filesystem. */
grub_err_t (*fs_mtime) (grub_device_t device, grub_int32_t *timebuf);
#ifdef GRUB_UTIL
/* Determine sectors available for embedding. */
grub_err_t (*fs_embed) (grub_device_t device, unsigned int *nsectors,
unsigned int max_nsectors,
grub_embed_type_t embed_type,
grub_disk_addr_t **sectors);
/* Whether this filesystem reserves first sector for DOS-style boot. */
int reserved_first_sector;
/* Whether blocklist installs have a chance to work. */
int blocklist_install;
#endif
};
typedef struct grub_fs *grub_fs_t;
/* This is special, because block lists are not files in usual sense. */
extern struct grub_fs grub_fs_blocklist;
/* This hook is used to automatically load filesystem modules.
If this hook loads a module, return non-zero. Otherwise return zero.
The newly loaded filesystem is assumed to be inserted into the head of
the linked list GRUB_FS_LIST through the function grub_fs_register. */
typedef int (*grub_fs_autoload_hook_t) (void);
extern grub_fs_autoload_hook_t EXPORT_VAR(grub_fs_autoload_hook);
extern grub_fs_t EXPORT_VAR (grub_fs_list);
#ifndef GRUB_LST_GENERATOR
static inline void
grub_fs_register (grub_fs_t fs)
{
grub_list_push (GRUB_AS_LIST_P (&grub_fs_list), GRUB_AS_LIST (fs));
}
#endif
static inline void
grub_fs_unregister (grub_fs_t fs)
{
grub_list_remove (GRUB_AS_LIST (fs));
}
#define FOR_FILESYSTEMS(var) FOR_LIST_ELEMENTS((var), (grub_fs_list))
grub_fs_t EXPORT_FUNC(grub_fs_probe) (grub_device_t device);
#endif /* ! GRUB_FS_HEADER */

View File

@@ -0,0 +1,242 @@
/******************************************************************************
* ventoy.h
*
* Copyright (c) 2020, longpanda <admin@ventoy.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef __VENTOY_H__
#define __VENTOY_H__
#define COMPILE_ASSERT(expr) extern char __compile_assert[(expr) ? 1 : -1]
#define VENTOY_COMPATIBLE_STR "VENTOY COMPATIBLE"
#define VENTOY_COMPATIBLE_STR_LEN 17
#define VENTOY_GUID { 0x77772020, 0x2e77, 0x6576, { 0x6e, 0x74, 0x6f, 0x79, 0x2e, 0x6e, 0x65, 0x74 }}
typedef enum ventoy_fs_type
{
ventoy_fs_exfat = 0, /* 0: exfat */
ventoy_fs_ntfs, /* 1: NTFS */
ventoy_fs_ext, /* 2: ext2/ext3/ext4 */
ventoy_fs_xfs, /* 3: XFS */
ventoy_fs_udf, /* 4: UDF */
ventoy_fs_max
}ventoy_fs_type;
#pragma pack(1)
typedef struct ventoy_guid
{
grub_uint32_t data1;
grub_uint16_t data2;
grub_uint16_t data3;
grub_uint8_t data4[8];
}ventoy_guid;
typedef struct ventoy_image_disk_region
{
grub_uint32_t image_sector_count; /* image sectors contained in this region (in 2048) */
grub_uint32_t image_start_sector; /* image sector start (in 2048) */
grub_uint64_t disk_start_sector; /* disk sector start (in 512) */
}ventoy_image_disk_region;
typedef struct ventoy_image_location
{
ventoy_guid guid;
/* image sector size, currently this value is always 2048 */
grub_uint32_t image_sector_size;
/* disk sector size, normally the value is 512 */
grub_uint32_t disk_sector_size;
grub_uint32_t region_count;
/*
* disk region data (region_count)
* If the image file has more than one fragments in disk,
* there will be more than one region data here.
*
*/
ventoy_image_disk_region regions[1];
/* ventoy_image_disk_region regions[2~region_count-1] */
}ventoy_image_location;
typedef struct ventoy_os_param
{
ventoy_guid guid; // VENTOY_GUID
grub_uint8_t chksum; // checksum
grub_uint8_t vtoy_disk_guid[16];
grub_uint64_t vtoy_disk_size; // disk size in bytes
grub_uint16_t vtoy_disk_part_id; // begin with 1
grub_uint16_t vtoy_disk_part_type; // 0:exfat 1:ntfs other: reserved
char vtoy_img_path[384]; // It seems to be enough, utf-8 format
grub_uint64_t vtoy_img_size; // image file size in bytes
/*
* Ventoy will write a copy of ventoy_image_location data into runtime memory
* this is the physically address and length of that memory.
* Address 0 means no such data exist.
* Address will be aligned by 4KB.
*
*/
grub_uint64_t vtoy_img_location_addr;
grub_uint32_t vtoy_img_location_len;
/*
* These 32 bytes are reserved by ventoy.
*
* vtoy_reserved[0]: vtoy_break_level
* vtoy_reserved[1]: vtoy_debug_level
*
*/
grub_uint8_t vtoy_reserved[32]; // Internal use by ventoy
grub_uint8_t reserved[31];
}ventoy_os_param;
typedef struct ventoy_windows_data
{
char auto_install_script[384];
grub_uint8_t reserved[128];
}ventoy_windows_data;
#pragma pack()
// compile assert check : sizeof(ventoy_os_param) must be 512
COMPILE_ASSERT(sizeof(ventoy_os_param) == 512);
#pragma pack(4)
typedef struct ventoy_chain_head
{
ventoy_os_param os_param;
grub_uint32_t disk_drive;
grub_uint32_t drive_map;
grub_uint32_t disk_sector_size;
grub_uint64_t real_img_size_in_bytes;
grub_uint64_t virt_img_size_in_bytes;
grub_uint32_t boot_catalog;
grub_uint8_t boot_catalog_sector[2048];
grub_uint32_t img_chunk_offset;
grub_uint32_t img_chunk_num;
grub_uint32_t override_chunk_offset;
grub_uint32_t override_chunk_num;
grub_uint32_t virt_chunk_offset;
grub_uint32_t virt_chunk_num;
}ventoy_chain_head;
typedef struct ventoy_img_chunk
{
grub_uint32_t img_start_sector; // sector size: 2KB
grub_uint32_t img_end_sector; // included
grub_uint64_t disk_start_sector; // in disk_sector_size
grub_uint64_t disk_end_sector; // included
}ventoy_img_chunk;
typedef struct ventoy_override_chunk
{
grub_uint64_t img_offset;
grub_uint32_t override_size;
grub_uint8_t override_data[512];
}ventoy_override_chunk;
typedef struct ventoy_virt_chunk
{
grub_uint32_t mem_sector_start;
grub_uint32_t mem_sector_end;
grub_uint32_t mem_sector_offset;
grub_uint32_t remap_sector_start;
grub_uint32_t remap_sector_end;
grub_uint32_t org_sector_start;
}ventoy_virt_chunk;
#define DEFAULT_CHUNK_NUM 1024
typedef struct ventoy_img_chunk_list
{
grub_uint32_t max_chunk;
grub_uint32_t cur_chunk;
ventoy_img_chunk *chunk;
}ventoy_img_chunk_list;
#pragma pack()
#define ventoy_filt_register grub_file_filter_register
typedef const char * (*grub_env_get_pf)(const char *name);
#pragma pack(1)
#define GRUB_FILE_REPLACE_MAGIC 0x1258BEEF
typedef struct ventoy_grub_param_file_replace
{
grub_uint32_t magic;
char old_file_name[4][256];
grub_uint32_t old_name_cnt;
grub_uint32_t new_file_virtual_id;
}ventoy_grub_param_file_replace;
typedef struct ventoy_grub_param
{
grub_env_get_pf grub_env_get;
ventoy_grub_param_file_replace file_replace;
}ventoy_grub_param;
#pragma pack()
int grub_fat_get_file_chunk(grub_uint64_t part_start, grub_file_t file, ventoy_img_chunk_list *chunk_list);
grub_uint64_t grub_iso9660_get_last_read_pos(grub_file_t file);
grub_uint64_t grub_iso9660_get_last_file_dirent_pos(grub_file_t file);
grub_uint64_t grub_udf_get_file_offset(grub_file_t file);
grub_uint64_t grub_udf_get_last_pd_size_offset(void);
grub_uint64_t grub_udf_get_last_file_attr_offset
(
grub_file_t file,
grub_uint32_t *startBlock,
grub_uint64_t *fe_entry_size_offset
);
int ventoy_is_efi_os(void);
#endif /* __VENTOY_H__ */

View File

@@ -0,0 +1,43 @@
#!/bin/bash
VT_DIR=$PWD/../../..
rm -rf $VT_DIR/GRUB2/INSTALL
rm -rf $VT_DIR/GRUB2/PXE
mkdir -p $VT_DIR/GRUB2/INSTALL
mkdir -p $VT_DIR/GRUB2/PXE
make install
PATH=$PATH:$VT_DIR/GRUB2/INSTALL/bin/:$VT_DIR/GRUB2/INSTALL/sbin/
net_modules_legacy="net tftp http"
all_modules_legacy="date drivemap blocklist ext2 xfs ventoy chain read halt iso9660 linux16 test true sleep reboot echo video_colors video_cirrus video_bochs vga vbe video_fb font video gettext extcmd terminal linux minicmd help configfile tr trig boot biosdisk disk ls tar squash4 password_pbkdf2 all_video png jpeg part_msdos fat exfat ntfs loopback gzio normal udf gfxmenu gfxterm gfxterm_background gfxterm_menu"
net_modules_uefi="efinet net tftp http"
all_modules_uefi="blocklist ventoy test ext2 xfs read halt sleep serial terminfo png password_pbkdf2 gcry_sha512 pbkdf2 part_gpt part_msdos ls tar squash4 loopback part_apple minicmd diskfilter linux relocator jpeg iso9660 udf hfsplus halt acpi mmap gfxmenu video_colors trig bitmap_scale gfxterm bitmap font fat exfat ntfs fshelp efifwsetup reboot echo configfile normal terminal gettext chain priority_queue bufio datetime cat extcmd crypto gzio boot all_video efi_gop efi_uga video_bochs video_cirrus video video_fb gfxterm_background gfxterm_menu"
if [ "$1" = "uefi" ]; then
all_modules="$net_modules_uefi $all_modules_uefi"
grub-mkimage -v --directory "$VT_DIR/GRUB2/INSTALL/lib/grub/x86_64-efi" --prefix '(,msdos2)/grub' --output "$VT_DIR/INSTALL/EFI/BOOT/grubx64_real.efi" --format 'x86_64-efi' --compression 'auto' $all_modules_uefi 'fat' 'part_msdos'
else
all_modules="$net_modules_legacy $all_modules_legacy"
grub-mkimage -v --directory "$VT_DIR/GRUB2/INSTALL/lib/grub/i386-pc" --prefix '(,msdos2)/grub' --output "$VT_DIR/INSTALL/grub/i386-pc/core.img" --format 'i386-pc' --compression 'auto' $all_modules_legacy 'fat' 'part_msdos' 'biosdisk'
fi
grub-mknetdir --modules="$all_modules" --net-directory=$VT_DIR/GRUB2/PXE --subdir=grub2 --locales=en@quot || exit 1
if [ "$1" = "uefi" ]; then
rm -f $VT_DIR/GRUB2/NBP/core.efi
cp -a $VT_DIR/GRUB2/PXE/grub2/x86_64-efi/core.efi $VT_DIR/GRUB2/NBP/core.efi || exit 1
rm -f $VT_DIR/INSTALL/grub/x86_64-efi/normal.mod
cp -a $VT_DIR/GRUB2/PXE/grub2/x86_64-efi/normal.mod $VT_DIR/INSTALL/grub/x86_64-efi/normal.mod || exit 1
else
rm -f $VT_DIR/GRUB2/NBP/core.0
cp -a $VT_DIR/GRUB2/PXE/grub2/i386-pc/core.0 $VT_DIR/GRUB2/NBP/core.0 || exit 1
rm -f $VT_DIR/INSTALL/grub/i386-pc/boot.img
cp -a $VT_DIR/GRUB2/INSTALL/lib/grub/i386-pc/boot.img $VT_DIR/INSTALL/grub/i386-pc/boot.img || exit 1
fi