Initial commit from Ventoy@7cbdc5c

This commit is contained in:
2026-08-23 00:38:40 -04:00
commit e9a7a16826
2848 changed files with 439164 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+909
View File
@@ -0,0 +1,909 @@
#! /usr/bin/python
# GRUB -- GRand Unified Bootloader
# Copyright (C) 2010,2011,2012,2013 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/>.
from __future__ import print_function
__metaclass__ = type
from optparse import OptionParser
import re
#
# This is the python script used to generate Makefile.*.am
#
GRUB_PLATFORMS = [ "emu", "i386_pc", "i386_efi", "i386_qemu", "i386_coreboot",
"i386_multiboot", "i386_ieee1275", "x86_64_efi",
"i386_xen", "x86_64_xen", "i386_xen_pvh",
"mips_loongson", "mips64_efi", "sparc64_ieee1275",
"powerpc_ieee1275", "mips_arc", "ia64_efi",
"mips_qemu_mips", "arm_uboot", "arm_efi", "arm64_efi",
"arm_coreboot", "riscv32_efi", "riscv64_efi" ]
GROUPS = {}
GROUPS["common"] = GRUB_PLATFORMS[:]
# Groups based on CPU
GROUPS["i386"] = [ "i386_pc", "i386_efi", "i386_qemu", "i386_coreboot", "i386_multiboot", "i386_ieee1275" ]
GROUPS["x86_64"] = [ "x86_64_efi" ]
GROUPS["x86"] = GROUPS["i386"] + GROUPS["x86_64"]
GROUPS["mips"] = [ "mips_loongson", "mips_qemu_mips", "mips_arc" ]
GROUPS["mips64"] = [ "mips64_efi" ]
GROUPS["sparc64"] = [ "sparc64_ieee1275" ]
GROUPS["powerpc"] = [ "powerpc_ieee1275" ]
GROUPS["arm"] = [ "arm_uboot", "arm_efi", "arm_coreboot" ]
GROUPS["arm64"] = [ "arm64_efi" ]
GROUPS["riscv32"] = [ "riscv32_efi" ]
GROUPS["riscv64"] = [ "riscv64_efi" ]
# Groups based on firmware
GROUPS["efi"] = [ "i386_efi", "x86_64_efi", "ia64_efi", "arm_efi", "arm64_efi", "mips64_efi",
"riscv32_efi", "riscv64_efi" ]
GROUPS["ieee1275"] = [ "i386_ieee1275", "sparc64_ieee1275", "powerpc_ieee1275" ]
GROUPS["uboot"] = [ "arm_uboot" ]
GROUPS["xen"] = [ "i386_xen", "x86_64_xen" ]
GROUPS["coreboot"] = [ "i386_coreboot", "arm_coreboot" ]
# emu is a special case so many core functionality isn't needed on this platform
GROUPS["noemu"] = GRUB_PLATFORMS[:]; GROUPS["noemu"].remove("emu")
# Groups based on hardware features
GROUPS["cmos"] = GROUPS["x86"][:] + ["mips_loongson", "mips_qemu_mips",
"sparc64_ieee1275", "powerpc_ieee1275"]
GROUPS["cmos"].remove("i386_efi"); GROUPS["cmos"].remove("x86_64_efi");
GROUPS["pci"] = GROUPS["x86"] + ["mips_loongson"]
GROUPS["usb"] = GROUPS["pci"] + ["arm_coreboot"]
# If gfxterm is main output console integrate it into kernel
GROUPS["videoinkernel"] = ["mips_loongson", "i386_coreboot", "arm_coreboot" ]
GROUPS["videomodules"] = GRUB_PLATFORMS[:];
for i in GROUPS["videoinkernel"]: GROUPS["videomodules"].remove(i)
# Similar for terminfo
GROUPS["terminfoinkernel"] = [ "emu", "mips_loongson", "mips_arc", "mips_qemu_mips", "i386_xen_pvh" ] + GROUPS["xen"] + GROUPS["ieee1275"] + GROUPS["uboot"];
GROUPS["terminfomodule"] = GRUB_PLATFORMS[:];
for i in GROUPS["terminfoinkernel"]: GROUPS["terminfomodule"].remove(i)
# Flattened Device Trees (FDT)
GROUPS["fdt"] = [ "arm64_efi", "arm_uboot", "arm_efi", "riscv32_efi", "riscv64_efi" ]
# Needs software helpers for division
# Must match GRUB_DIVISION_IN_SOFTWARE in misc.h
GROUPS["softdiv"] = GROUPS["arm"] + ["ia64_efi"] + GROUPS["riscv32"]
GROUPS["no_softdiv"] = GRUB_PLATFORMS[:]
for i in GROUPS["softdiv"]: GROUPS["no_softdiv"].remove(i)
# Miscellaneous groups scheduled to disappear in future
GROUPS["i386_coreboot_multiboot_qemu"] = ["i386_coreboot", "i386_multiboot", "i386_qemu"]
GROUPS["nopc"] = GRUB_PLATFORMS[:]; GROUPS["nopc"].remove("i386_pc")
#
# Create platform => groups reverse map, where groups covering that
# platform are ordered by their sizes
#
RMAP = {}
for platform in GRUB_PLATFORMS:
# initialize with platform itself as a group
RMAP[platform] = [ platform ]
for k in GROUPS.keys():
v = GROUPS[k]
# skip groups that don't cover this platform
if platform not in v: continue
bigger = []
smaller = []
# partition currently known groups based on their size
for group in RMAP[platform]:
if group in GRUB_PLATFORMS: smaller.append(group)
elif len(GROUPS[group]) < len(v): smaller.append(group)
else: bigger.append(group)
# insert in the middle
RMAP[platform] = smaller + [ k ] + bigger
#
# Input
#
# We support a subset of the AutoGen definitions file syntax. Specifically,
# compound names are disallowed; some preprocessing directives are
# disallowed (though #if/#endif are allowed; note that, like AutoGen, #if
# skips everything to the next #endif regardless of the value of the
# conditional); and shell-generated strings, Scheme-generated strings, and
# here strings are disallowed.
class AutogenToken:
(autogen, definitions, eof, var_name, other_name, string, number,
semicolon, equals, comma, lbrace, rbrace, lbracket, rbracket) = range(14)
class AutogenState:
(init, need_def, need_tpl, need_semi, need_name, have_name, need_value,
need_idx, need_rbracket, indx_name, have_value, done) = range(12)
class AutogenParseError(Exception):
def __init__(self, message, path, line):
super(AutogenParseError, self).__init__(message)
self.path = path
self.line = line
def __str__(self):
return (
super(AutogenParseError, self).__str__() +
" at file %s line %d" % (self.path, self.line))
class AutogenDefinition(list):
def __getitem__(self, key):
try:
return super(AutogenDefinition, self).__getitem__(key)
except TypeError:
for name, value in self:
if name == key:
return value
def __contains__(self, key):
for name, value in self:
if name == key:
return True
return False
def get(self, key, default):
for name, value in self:
if name == key:
return value
else:
return default
def find_all(self, key):
for name, value in self:
if name == key:
yield value
class AutogenParser:
def __init__(self):
self.definitions = AutogenDefinition()
self.def_stack = [("", self.definitions)]
self.curdef = None
self.new_name = None
self.cur_path = None
self.cur_line = 0
@staticmethod
def is_unquotable_char(c):
return (ord(c) in range(ord("!"), ord("~") + 1) and
c not in "#,;<=>[\\]`{}?*'\"()")
@staticmethod
def is_value_name_char(c):
return c in ":^-_" or c.isalnum()
def error(self, message):
raise AutogenParseError(message, self.cur_file, self.cur_line)
def read_tokens(self, f):
data = f.read()
end = len(data)
offset = 0
while offset < end:
while offset < end and data[offset].isspace():
if data[offset] == "\n":
self.cur_line += 1
offset += 1
if offset >= end:
break
c = data[offset]
if c == "#":
offset += 1
try:
end_directive = data.index("\n", offset)
directive = data[offset:end_directive]
offset = end_directive
except ValueError:
directive = data[offset:]
offset = end
name, value = directive.split(None, 1)
if name == "if":
try:
end_if = data.index("\n#endif", offset)
new_offset = end_if + len("\n#endif")
self.cur_line += data[offset:new_offset].count("\n")
offset = new_offset
except ValueError:
self.error("#if without matching #endif")
else:
self.error("Unhandled directive '#%s'" % name)
elif c == "{":
yield AutogenToken.lbrace, c
offset += 1
elif c == "=":
yield AutogenToken.equals, c
offset += 1
elif c == "}":
yield AutogenToken.rbrace, c
offset += 1
elif c == "[":
yield AutogenToken.lbracket, c
offset += 1
elif c == "]":
yield AutogenToken.rbracket, c
offset += 1
elif c == ";":
yield AutogenToken.semicolon, c
offset += 1
elif c == ",":
yield AutogenToken.comma, c
offset += 1
elif c in ("'", '"'):
s = []
while True:
offset += 1
if offset >= end:
self.error("EOF in quoted string")
if data[offset] == "\n":
self.cur_line += 1
if data[offset] == "\\":
offset += 1
if offset >= end:
self.error("EOF in quoted string")
if data[offset] == "\n":
self.cur_line += 1
# Proper escaping unimplemented; this can be filled
# out if needed.
s.append("\\")
s.append(data[offset])
elif data[offset] == c:
offset += 1
break
else:
s.append(data[offset])
yield AutogenToken.string, "".join(s)
elif c == "/":
offset += 1
if data[offset] == "*":
offset += 1
try:
end_comment = data.index("*/", offset)
new_offset = end_comment + len("*/")
self.cur_line += data[offset:new_offset].count("\n")
offset = new_offset
except ValueError:
self.error("/* without matching */")
elif data[offset] == "/":
try:
offset = data.index("\n", offset)
except ValueError:
pass
elif (c.isdigit() or
(c == "-" and offset < end - 1 and
data[offset + 1].isdigit())):
end_number = offset + 1
while end_number < end and data[end_number].isdigit():
end_number += 1
yield AutogenToken.number, data[offset:end_number]
offset = end_number
elif self.is_unquotable_char(c):
end_name = offset
while (end_name < end and
self.is_value_name_char(data[end_name])):
end_name += 1
if end_name < end and self.is_unquotable_char(data[end_name]):
while (end_name < end and
self.is_unquotable_char(data[end_name])):
end_name += 1
yield AutogenToken.other_name, data[offset:end_name]
offset = end_name
else:
s = data[offset:end_name]
if s.lower() == "autogen":
yield AutogenToken.autogen, s
elif s.lower() == "definitions":
yield AutogenToken.definitions, s
else:
yield AutogenToken.var_name, s
offset = end_name
else:
self.error("Invalid input character '%s'" % c)
yield AutogenToken.eof, None
def do_need_name_end(self, token):
if len(self.def_stack) > 1:
self.error("Definition blocks were left open")
def do_need_name_var_name(self, token):
self.new_name = token
def do_end_block(self, token):
if len(self.def_stack) <= 1:
self.error("Too many close braces")
new_name, parent_def = self.def_stack.pop()
parent_def.append((new_name, self.curdef))
self.curdef = parent_def
def do_empty_val(self, token):
self.curdef.append((self.new_name, ""))
def do_str_value(self, token):
self.curdef.append((self.new_name, token))
def do_start_block(self, token):
self.def_stack.append((self.new_name, self.curdef))
self.curdef = AutogenDefinition()
def do_indexed_name(self, token):
self.new_name = token
def read_definitions_file(self, f):
self.curdef = self.definitions
self.cur_line = 0
state = AutogenState.init
# The following transition table was reduced from the Autogen
# documentation:
# info -f autogen -n 'Full Syntax'
transitions = {
AutogenState.init: {
AutogenToken.autogen: (AutogenState.need_def, None),
},
AutogenState.need_def: {
AutogenToken.definitions: (AutogenState.need_tpl, None),
},
AutogenState.need_tpl: {
AutogenToken.var_name: (AutogenState.need_semi, None),
AutogenToken.other_name: (AutogenState.need_semi, None),
AutogenToken.string: (AutogenState.need_semi, None),
},
AutogenState.need_semi: {
AutogenToken.semicolon: (AutogenState.need_name, None),
},
AutogenState.need_name: {
AutogenToken.autogen: (AutogenState.need_def, None),
AutogenToken.eof: (AutogenState.done, self.do_need_name_end),
AutogenToken.var_name: (
AutogenState.have_name, self.do_need_name_var_name),
AutogenToken.rbrace: (
AutogenState.have_value, self.do_end_block),
},
AutogenState.have_name: {
AutogenToken.semicolon: (
AutogenState.need_name, self.do_empty_val),
AutogenToken.equals: (AutogenState.need_value, None),
AutogenToken.lbracket: (AutogenState.need_idx, None),
},
AutogenState.need_value: {
AutogenToken.var_name: (
AutogenState.have_value, self.do_str_value),
AutogenToken.other_name: (
AutogenState.have_value, self.do_str_value),
AutogenToken.string: (
AutogenState.have_value, self.do_str_value),
AutogenToken.number: (
AutogenState.have_value, self.do_str_value),
AutogenToken.lbrace: (
AutogenState.need_name, self.do_start_block),
},
AutogenState.need_idx: {
AutogenToken.var_name: (
AutogenState.need_rbracket, self.do_indexed_name),
AutogenToken.number: (
AutogenState.need_rbracket, self.do_indexed_name),
},
AutogenState.need_rbracket: {
AutogenToken.rbracket: (AutogenState.indx_name, None),
},
AutogenState.indx_name: {
AutogenToken.semicolon: (
AutogenState.need_name, self.do_empty_val),
AutogenToken.equals: (AutogenState.need_value, None),
},
AutogenState.have_value: {
AutogenToken.semicolon: (AutogenState.need_name, None),
AutogenToken.comma: (AutogenState.need_value, None),
},
}
for code, token in self.read_tokens(f):
if code in transitions[state]:
state, handler = transitions[state][code]
if handler is not None:
handler(token)
else:
self.error(
"Parse error in state %s: unexpected token '%s'" % (
state, token))
if state == AutogenState.done:
break
def read_definitions(self, path):
self.cur_file = path
with open(path) as f:
self.read_definitions_file(f)
defparser = AutogenParser()
#
# Output
#
outputs = {}
def output(s, section=''):
if s == "":
return
outputs.setdefault(section, [])
outputs[section].append(s)
def write_output(section=''):
for s in outputs.get(section, []):
print(s, end='')
#
# Global variables
#
def gvar_add(var, value):
output(var + " += " + value + "\n")
#
# Per PROGRAM/SCRIPT variables
#
seen_vars = set()
def vars_init(defn, *var_list):
name = defn['name']
if name not in seen_target and name not in seen_vars:
for var in var_list:
output(var + " = \n", section='decl')
seen_vars.add(name)
def var_set(var, value):
output(var + " = " + value + "\n")
def var_add(var, value):
output(var + " += " + value + "\n")
#
# Variable names and rules
#
canonical_name_re = re.compile(r'[^0-9A-Za-z@_]')
canonical_name_suffix = ""
def set_canonical_name_suffix(suffix):
global canonical_name_suffix
canonical_name_suffix = suffix
def cname(defn):
return canonical_name_re.sub('_', defn['name'] + canonical_name_suffix)
def rule(target, source, cmd):
if cmd[0] == "\n":
output("\n" + target + ": " + source + cmd.replace("\n", "\n\t") + "\n")
else:
output("\n" + target + ": " + source + "\n\t" + cmd.replace("\n", "\n\t") + "\n")
#
# Handle keys with platform names as values, for example:
#
# kernel = {
# nostrip = emu;
# ...
# }
#
def platform_tagged(defn, platform, tag):
for value in defn.find_all(tag):
for group in RMAP[platform]:
if value == group:
return True
return False
def if_platform_tagged(defn, platform, tag, snippet_if, snippet_else=None):
if platform_tagged(defn, platform, tag):
return snippet_if
elif snippet_else is not None:
return snippet_else
#
# Handle tagged values
#
# module = {
# extra_dist = ...
# extra_dist = ...
# ...
# };
#
def foreach_value(defn, tag, closure):
r = []
for value in defn.find_all(tag):
r.append(closure(value))
return ''.join(r)
#
# Handle best matched values for a platform, for example:
#
# module = {
# cflags = '-Wall';
# emu_cflags = '-Wall -DGRUB_EMU=1';
# ...
# }
#
def foreach_platform_specific_value(defn, platform, suffix, nonetag, closure):
r = []
for group in RMAP[platform]:
values = list(defn.find_all(group + suffix))
if values:
for value in values:
r.append(closure(value))
break
else:
for value in defn.find_all(nonetag):
r.append(closure(value))
return ''.join(r)
#
# Handle values from sum of all groups for a platform, for example:
#
# module = {
# common = kern/misc.c;
# emu = kern/emu/misc.c;
# ...
# }
#
def foreach_platform_value(defn, platform, suffix, closure):
r = []
for group in RMAP[platform]:
for value in defn.find_all(group + suffix):
r.append(closure(value))
return ''.join(r)
def platform_conditional(platform, closure):
output("\nif COND_" + platform + "\n")
closure(platform)
output("endif\n")
#
# Handle guarding with platform-specific "enable" keys, for example:
#
# module = {
# name = pci;
# noemu = bus/pci.c;
# emu = bus/emu/pci.c;
# emu = commands/lspci.c;
#
# enable = emu;
# enable = i386_pc;
# enable = x86_efi;
# enable = i386_ieee1275;
# enable = i386_coreboot;
# };
#
def foreach_enabled_platform(defn, closure):
if 'enable' in defn:
for platform in GRUB_PLATFORMS:
if platform_tagged(defn, platform, "enable"):
platform_conditional(platform, closure)
else:
for platform in GRUB_PLATFORMS:
platform_conditional(platform, closure)
#
# Handle guarding with platform-specific automake conditionals, for example:
#
# module = {
# name = usb;
# common = bus/usb/usb.c;
# noemu = bus/usb/usbtrans.c;
# noemu = bus/usb/usbhub.c;
# enable = emu;
# enable = i386;
# enable = mips_loongson;
# emu_condition = COND_GRUB_EMU_SDL;
# };
#
def under_platform_specific_conditionals(defn, platform, closure):
output(foreach_platform_specific_value(defn, platform, "_condition", "condition", lambda cond: "if " + cond + "\n"))
closure(defn, platform)
output(foreach_platform_specific_value(defn, platform, "_condition", "condition", lambda cond: "endif " + cond + "\n"))
def platform_specific_values(defn, platform, suffix, nonetag):
return foreach_platform_specific_value(defn, platform, suffix, nonetag,
lambda value: value + " ")
def platform_values(defn, platform, suffix):
return foreach_platform_value(defn, platform, suffix, lambda value: value + " ")
def extra_dist(defn):
return foreach_value(defn, "extra_dist", lambda value: value + " ")
def platform_sources(defn, p): return platform_values(defn, p, "")
def platform_nodist_sources(defn, p): return platform_values(defn, p, "_nodist")
def platform_startup(defn, p): return platform_specific_values(defn, p, "_startup", "startup")
def platform_ldadd(defn, p): return platform_specific_values(defn, p, "_ldadd", "ldadd")
def platform_dependencies(defn, p): return platform_specific_values(defn, p, "_dependencies", "dependencies")
def platform_cflags(defn, p): return platform_specific_values(defn, p, "_cflags", "cflags")
def platform_ldflags(defn, p): return platform_specific_values(defn, p, "_ldflags", "ldflags")
def platform_cppflags(defn, p): return platform_specific_values(defn, p, "_cppflags", "cppflags")
def platform_ccasflags(defn, p): return platform_specific_values(defn, p, "_ccasflags", "ccasflags")
def platform_stripflags(defn, p): return platform_specific_values(defn, p, "_stripflags", "stripflags")
def platform_objcopyflags(defn, p): return platform_specific_values(defn, p, "_objcopyflags", "objcopyflags")
#
# Emit snippet only the first time through for the current name.
#
seen_target = set()
def first_time(defn, snippet):
if defn['name'] not in seen_target:
return snippet
return ''
def is_platform_independent(defn):
if 'enable' in defn:
return False
for suffix in [ "", "_nodist" ]:
template = platform_values(defn, GRUB_PLATFORMS[0], suffix)
for platform in GRUB_PLATFORMS[1:]:
if template != platform_values(defn, platform, suffix):
return False
for suffix in [ "startup", "ldadd", "dependencies", "cflags", "ldflags", "cppflags", "ccasflags", "stripflags", "objcopyflags", "condition" ]:
template = platform_specific_values(defn, GRUB_PLATFORMS[0], "_" + suffix, suffix)
for platform in GRUB_PLATFORMS[1:]:
if template != platform_specific_values(defn, platform, "_" + suffix, suffix):
return False
for tag in [ "nostrip" ]:
template = platform_tagged(defn, GRUB_PLATFORMS[0], tag)
for platform in GRUB_PLATFORMS[1:]:
if template != platform_tagged(defn, platform, tag):
return False
return True
def module(defn, platform):
name = defn['name']
set_canonical_name_suffix(".module")
gvar_add("platform_PROGRAMS", name + ".module")
gvar_add("MODULE_FILES", name + ".module$(EXEEXT)")
var_set(cname(defn) + "_SOURCES", platform_sources(defn, platform) + " ## platform sources")
var_set("nodist_" + cname(defn) + "_SOURCES", platform_nodist_sources(defn, platform) + " ## platform nodist sources")
var_set(cname(defn) + "_LDADD", platform_ldadd(defn, platform))
var_set(cname(defn) + "_CFLAGS", "$(AM_CFLAGS) $(CFLAGS_MODULE) " + platform_cflags(defn, platform))
var_set(cname(defn) + "_LDFLAGS", "$(AM_LDFLAGS) $(LDFLAGS_MODULE) " + platform_ldflags(defn, platform))
var_set(cname(defn) + "_CPPFLAGS", "$(AM_CPPFLAGS) $(CPPFLAGS_MODULE) " + platform_cppflags(defn, platform))
var_set(cname(defn) + "_CCASFLAGS", "$(AM_CCASFLAGS) $(CCASFLAGS_MODULE) " + platform_ccasflags(defn, platform))
var_set(cname(defn) + "_DEPENDENCIES", "$(TARGET_OBJ2ELF) " + platform_dependencies(defn, platform))
gvar_add("dist_noinst_DATA", extra_dist(defn))
gvar_add("BUILT_SOURCES", "$(nodist_" + cname(defn) + "_SOURCES)")
gvar_add("CLEANFILES", "$(nodist_" + cname(defn) + "_SOURCES)")
gvar_add("MOD_FILES", name + ".mod")
gvar_add("MARKER_FILES", name + ".marker")
gvar_add("CLEANFILES", name + ".marker")
output("""
""" + name + """.marker: $(""" + cname(defn) + """_SOURCES) $(nodist_""" + cname(defn) + """_SOURCES)
$(TARGET_CPP) -DGRUB_LST_GENERATOR $(CPPFLAGS_MARKER) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(""" + cname(defn) + """_CPPFLAGS) $(CPPFLAGS) $^ > [email protected] || (rm -f $@; exit 1)
grep 'MARKER' [email protected] > $@; rm -f [email protected]
""")
def kernel(defn, platform):
name = defn['name']
set_canonical_name_suffix(".exec")
gvar_add("platform_PROGRAMS", name + ".exec")
var_set(cname(defn) + "_SOURCES", platform_startup(defn, platform))
var_add(cname(defn) + "_SOURCES", platform_sources(defn, platform))
var_set("nodist_" + cname(defn) + "_SOURCES", platform_nodist_sources(defn, platform) + " ## platform nodist sources")
var_set(cname(defn) + "_LDADD", platform_ldadd(defn, platform))
var_set(cname(defn) + "_CFLAGS", "$(AM_CFLAGS) $(CFLAGS_KERNEL) " + platform_cflags(defn, platform))
var_set(cname(defn) + "_LDFLAGS", "$(AM_LDFLAGS) $(LDFLAGS_KERNEL) " + platform_ldflags(defn, platform))
var_set(cname(defn) + "_CPPFLAGS", "$(AM_CPPFLAGS) $(CPPFLAGS_KERNEL) " + platform_cppflags(defn, platform))
var_set(cname(defn) + "_CCASFLAGS", "$(AM_CCASFLAGS) $(CCASFLAGS_KERNEL) " + platform_ccasflags(defn, platform))
var_set(cname(defn) + "_STRIPFLAGS", "$(AM_STRIPFLAGS) $(STRIPFLAGS_KERNEL) " + platform_stripflags(defn, platform))
var_set(cname(defn) + "_DEPENDENCIES", "$(TARGET_OBJ2ELF)")
gvar_add("dist_noinst_DATA", extra_dist(defn))
gvar_add("BUILT_SOURCES", "$(nodist_" + cname(defn) + "_SOURCES)")
gvar_add("CLEANFILES", "$(nodist_" + cname(defn) + "_SOURCES)")
gvar_add("platform_DATA", name + ".img")
gvar_add("CLEANFILES", name + ".img")
rule(name + ".img", name + ".exec$(EXEEXT)",
if_platform_tagged(defn, platform, "nostrip",
"""if test x$(TARGET_APPLE_LINKER) = x1; then \
$(TARGET_OBJCONV) -f$(TARGET_MODULE_FORMAT) -nr:_grub_mod_init:grub_mod_init -nr:_grub_mod_fini:grub_mod_fini -ed2022 -wd1106 -nu -nd $< $@; \
elif test ! -z '$(TARGET_OBJ2ELF)'; then \
$(TARGET_OBJ2ELF) $< $@ || (rm -f $@; exit 1); \
else cp $< $@; fi""",
"""if test x$(TARGET_APPLE_LINKER) = x1; then \
$(TARGET_STRIP) -S -x $(""" + cname(defn) + """) -o [email protected] $<; \
$(TARGET_OBJCONV) -f$(TARGET_MODULE_FORMAT) -nr:_grub_mod_init:grub_mod_init -nr:_grub_mod_fini:grub_mod_fini -ed2022 -ed2016 -wd1106 -nu -nd [email protected] $@; \
rm -f [email protected]; \
elif test ! -z '$(TARGET_OBJ2ELF)'; then \
""" + "$(TARGET_STRIP) $(" + cname(defn) + "_STRIPFLAGS) -o [email protected] $< && \
$(TARGET_OBJ2ELF) [email protected] $@ || (rm -f $@; rm -f [email protected]; exit 1); \
rm -f [email protected]; \
else """ + "$(TARGET_STRIP) $(" + cname(defn) + "_STRIPFLAGS) -o $@ $<; \
fi"""))
def image(defn, platform):
name = defn['name']
set_canonical_name_suffix(".image")
gvar_add("platform_PROGRAMS", name + ".image")
var_set(cname(defn) + "_SOURCES", platform_sources(defn, platform))
var_set("nodist_" + cname(defn) + "_SOURCES", platform_nodist_sources(defn, platform) + "## platform nodist sources")
var_set(cname(defn) + "_LDADD", platform_ldadd(defn, platform))
var_set(cname(defn) + "_CFLAGS", "$(AM_CFLAGS) $(CFLAGS_IMAGE) " + platform_cflags(defn, platform))
var_set(cname(defn) + "_LDFLAGS", "$(AM_LDFLAGS) $(LDFLAGS_IMAGE) " + platform_ldflags(defn, platform))
var_set(cname(defn) + "_CPPFLAGS", "$(AM_CPPFLAGS) $(CPPFLAGS_IMAGE) " + platform_cppflags(defn, platform))
var_set(cname(defn) + "_CCASFLAGS", "$(AM_CCASFLAGS) $(CCASFLAGS_IMAGE) " + platform_ccasflags(defn, platform))
var_set(cname(defn) + "_OBJCOPYFLAGS", "$(OBJCOPYFLAGS_IMAGE) " + platform_objcopyflags(defn, platform))
# var_set(cname(defn) + "_DEPENDENCIES", platform_dependencies(defn, platform) + " " + platform_ldadd(defn, platform))
gvar_add("dist_noinst_DATA", extra_dist(defn))
gvar_add("BUILT_SOURCES", "$(nodist_" + cname(defn) + "_SOURCES)")
gvar_add("CLEANFILES", "$(nodist_" + cname(defn) + "_SOURCES)")
gvar_add("platform_DATA", name + ".img")
gvar_add("CLEANFILES", name + ".img")
rule(name + ".img", name + ".image$(EXEEXT)", """
if test x$(TARGET_APPLE_LINKER) = x1; then \
$(MACHO2IMG) $< $@; \
else \
$(TARGET_OBJCOPY) $(""" + cname(defn) + """_OBJCOPYFLAGS) --strip-unneeded -R .note -R .comment -R .note.gnu.build-id -R .MIPS.abiflags -R .reginfo -R .rel.dyn -R .note.gnu.gold-version -R .ARM.exidx $< $@; \
fi
""")
def library(defn, platform):
name = defn['name']
set_canonical_name_suffix("")
vars_init(defn,
cname(defn) + "_SOURCES",
"nodist_" + cname(defn) + "_SOURCES",
cname(defn) + "_CFLAGS",
cname(defn) + "_CPPFLAGS",
cname(defn) + "_CCASFLAGS")
# cname(defn) + "_DEPENDENCIES")
if name not in seen_target:
gvar_add("noinst_LIBRARIES", name)
var_add(cname(defn) + "_SOURCES", platform_sources(defn, platform))
var_add("nodist_" + cname(defn) + "_SOURCES", platform_nodist_sources(defn, platform))
var_add(cname(defn) + "_CFLAGS", first_time(defn, "$(AM_CFLAGS) $(CFLAGS_LIBRARY) ") + platform_cflags(defn, platform))
var_add(cname(defn) + "_CPPFLAGS", first_time(defn, "$(AM_CPPFLAGS) $(CPPFLAGS_LIBRARY) ") + platform_cppflags(defn, platform))
var_add(cname(defn) + "_CCASFLAGS", first_time(defn, "$(AM_CCASFLAGS) $(CCASFLAGS_LIBRARY) ") + platform_ccasflags(defn, platform))
# var_add(cname(defn) + "_DEPENDENCIES", platform_dependencies(defn, platform) + " " + platform_ldadd(defn, platform))
gvar_add("dist_noinst_DATA", extra_dist(defn))
if name not in seen_target:
gvar_add("BUILT_SOURCES", "$(nodist_" + cname(defn) + "_SOURCES)")
gvar_add("CLEANFILES", "$(nodist_" + cname(defn) + "_SOURCES)")
def installdir(defn, default="bin"):
return defn.get('installdir', default)
def manpage(defn, adddeps):
name = defn['name']
mansection = defn['mansection']
output("if COND_MAN_PAGES\n")
gvar_add("man_MANS", name + "." + mansection)
rule(name + "." + mansection, name + " " + adddeps, """
chmod a+x """ + name + """
PATH=$(builddir):$$PATH pkgdatadir=$(builddir) $(HELP2MAN) --section=""" + mansection + """ -i $(top_srcdir)/docs/man/""" + name + """.h2m -o $@ """ + name + """
""")
gvar_add("CLEANFILES", name + "." + mansection)
output("endif\n")
def program(defn, platform, test=False):
name = defn['name']
set_canonical_name_suffix("")
if 'testcase' in defn:
gvar_add("check_PROGRAMS", name)
gvar_add("TESTS", name)
else:
var_add(installdir(defn) + "_PROGRAMS", name)
if 'mansection' in defn:
manpage(defn, "")
var_set(cname(defn) + "_SOURCES", platform_sources(defn, platform))
var_set("nodist_" + cname(defn) + "_SOURCES", platform_nodist_sources(defn, platform))
var_set(cname(defn) + "_LDADD", platform_ldadd(defn, platform))
var_set(cname(defn) + "_CFLAGS", "$(AM_CFLAGS) $(CFLAGS_PROGRAM) " + platform_cflags(defn, platform))
var_set(cname(defn) + "_LDFLAGS", "$(AM_LDFLAGS) $(LDFLAGS_PROGRAM) " + platform_ldflags(defn, platform))
var_set(cname(defn) + "_CPPFLAGS", "$(AM_CPPFLAGS) $(CPPFLAGS_PROGRAM) " + platform_cppflags(defn, platform))
var_set(cname(defn) + "_CCASFLAGS", "$(AM_CCASFLAGS) $(CCASFLAGS_PROGRAM) " + platform_ccasflags(defn, platform))
# var_set(cname(defn) + "_DEPENDENCIES", platform_dependencies(defn, platform) + " " + platform_ldadd(defn, platform))
gvar_add("dist_noinst_DATA", extra_dist(defn))
gvar_add("BUILT_SOURCES", "$(nodist_" + cname(defn) + "_SOURCES)")
gvar_add("CLEANFILES", "$(nodist_" + cname(defn) + "_SOURCES)")
def data(defn, platform):
var_add("dist_" + installdir(defn) + "_DATA", platform_sources(defn, platform))
gvar_add("dist_noinst_DATA", extra_dist(defn))
def transform_data(defn, platform):
name = defn['name']
var_add(installdir(defn) + "_DATA", name)
rule(name, "$(top_builddir)/config.status " + platform_sources(defn, platform) + platform_dependencies(defn, platform), """
(for x in """ + platform_sources(defn, platform) + """; do cat $(srcdir)/"$$x"; done) | $(top_builddir)/config.status --file=$@:-
chmod a+x """ + name + """
""")
gvar_add("CLEANFILES", name)
gvar_add("EXTRA_DIST", extra_dist(defn))
gvar_add("dist_noinst_DATA", platform_sources(defn, platform))
def script(defn, platform):
name = defn['name']
if 'testcase' in defn:
gvar_add("check_SCRIPTS", name)
gvar_add ("TESTS", name)
else:
var_add(installdir(defn) + "_SCRIPTS", name)
if 'mansection' in defn:
manpage(defn, "grub-mkconfig_lib")
rule(name, "$(top_builddir)/config.status " + platform_sources(defn, platform) + platform_dependencies(defn, platform), """
(for x in """ + platform_sources(defn, platform) + """; do cat $(srcdir)/"$$x"; done) | $(top_builddir)/config.status --file=$@:-
chmod a+x """ + name + """
""")
gvar_add("CLEANFILES", name)
gvar_add("EXTRA_DIST", extra_dist(defn))
gvar_add("dist_noinst_DATA", platform_sources(defn, platform))
def rules(target, closure):
seen_target.clear()
seen_vars.clear()
for defn in defparser.definitions.find_all(target):
if is_platform_independent(defn):
under_platform_specific_conditionals(defn, GRUB_PLATFORMS[0], closure)
else:
foreach_enabled_platform(
defn,
lambda p: under_platform_specific_conditionals(defn, p, closure))
# Remember that we've seen this target.
seen_target.add(defn['name'])
parser = OptionParser(usage="%prog DEFINITION-FILES")
_, args = parser.parse_args()
for arg in args:
defparser.read_definitions(arg)
rules("module", module)
rules("kernel", kernel)
rules("image", image)
rules("library", library)
rules("program", program)
rules("script", script)
rules("data", data)
rules("transform_data", transform_data)
write_output(section='decl')
write_output()
@@ -0,0 +1,507 @@
AUTOMAKE_OPTIONS = subdir-objects -Wno-portability
DEPDIR=.deps-core
include $(top_srcdir)/conf/Makefile.common
CC=$(TARGET_CC)
CPP=$(TARGET_CC)
CCAS=$(TARGET_CC)
RANLIB=$(TARGET_RANLIB)
STRIP=$(TARGET_STRIP)
MACHO2IMG=$(top_builddir)/grub-macho2img
AM_CFLAGS = $(TARGET_CFLAGS)
AM_LDFLAGS = $(TARGET_LDFLAGS)
AM_CPPFLAGS = $(TARGET_CPPFLAGS) $(CPPFLAGS_DEFAULT)
AM_CCASFLAGS = $(TARGET_CCASFLAGS) $(CCASFLAGS_DEFAULT)
CFLAGS_PROGRAM += $(CFLAGS_PLATFORM)
LDFLAGS_PROGRAM += $(LDFLAGS_PLATFORM)
CPPFLAGS_PROGRAM += $(CPPFLAGS_PLATFORM)
CCASFLAGS_PROGRAM += $(CCASFLAGS_PLATFORM)
CFLAGS_LIBRARY += $(CFLAGS_PLATFORM) -fno-builtin
CPPFLAGS_LIBRARY += $(CPPFLAGS_PLATFORM)
CCASFLAGS_LIBRARY += $(CCASFLAGS_PLATFORM)
build-grub-pep2elf$(BUILD_EXEEXT): $(top_srcdir)/util/grub-pe2elf.c $(top_srcdir)/grub-core/kern/emu/misc.c $(top_srcdir)/util/misc.c
$(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) -DGRUB_BUILD=1 -DGRUB_TARGET_WORDSIZE=64 -DGRUB_UTIL=1 -DGRUB_BUILD_PROGRAM_NAME=\"build-grub-pep2elf\" $^
CLEANFILES += build-grub-pep2elf$(BUILD_EXEEXT)
build-grub-pe2elf$(BUILD_EXEEXT): $(top_srcdir)/util/grub-pe2elf.c $(top_srcdir)/grub-core/kern/emu/misc.c $(top_srcdir)/util/misc.c
$(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) -DGRUB_BUILD=1 -DGRUB_TARGET_WORDSIZE=32 -DGRUB_UTIL=1 -DGRUB_BUILD_PROGRAM_NAME=\"build-grub-pe2elf\" $^
CLEANFILES += build-grub-pe2elf$(BUILD_EXEEXT)
# gentrigtables
gentrigtables$(BUILD_EXEEXT): gentrigtables.c
$(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) $< $(BUILD_LIBM)
CLEANFILES += gentrigtables$(BUILD_EXEEXT)
build-grub-module-verifier$(BUILD_EXEEXT): $(top_srcdir)/util/grub-module-verifier.c $(top_srcdir)/util/grub-module-verifier32.c $(top_srcdir)/util/grub-module-verifier64.c $(top_srcdir)/grub-core/kern/emu/misc.c $(top_srcdir)/util/misc.c
$(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) -DGRUB_BUILD=1 -DGRUB_UTIL=1 -DGRUB_BUILD_PROGRAM_NAME=\"build-grub-module-verifier\" $^
CLEANFILES += build-grub-module-verifier$(BUILD_EXEEXT)
# trigtables.c
trigtables.c: gentrigtables$(BUILD_EXEEXT) gentrigtables.c $(top_srcdir)/configure.ac
./gentrigtables$(BUILD_EXEEXT) > $@
CLEANFILES += trigtables.c
# XXX Use Automake's LEX & YACC support
grub_script.tab.h: script/parser.y
$(YACC) -d -p grub_script_yy -b grub_script $<
grub_script.tab.c: grub_script.tab.h
CLEANFILES += grub_script.tab.c grub_script.tab.h
# For the lexer.
grub_script.yy.h: script/yylex.l
$(LEX) -o grub_script.yy.c --header-file=grub_script.yy.h $<
grub_script.yy.c: grub_script.yy.h
rs_decoder.h: $(srcdir)/lib/reed_solomon.c
$(TARGET_CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) -Os -I$(top_builddir) -S -DSTANDALONE -o $@ $< -g0 -mregparm=3 -ffreestanding
CLEANFILES += grub_script.yy.c grub_script.yy.h
include $(srcdir)/Makefile.core.am
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/cache.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/command.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/device.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/disk.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/dl.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/env.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/env_private.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/err.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/file.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/fs.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i18n.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/kernel.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/list.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/misc.h
if COND_emu
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/compiler-rt-emu.h
else
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/compiler-rt.h
endif
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/mm.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/parser.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/partition.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/term.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/time.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/mm_private.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/net.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/memory.h
if COND_i386_pc
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/pxe.h
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/int.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h
endif
if COND_i386_xen_pvh
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/int.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/loader.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/xen.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/xen/hypercall.h
endif
if COND_i386_efi
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pmtimer.h
endif
if COND_i386_coreboot
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/coreboot/lbio.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/video.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/video_fb.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/gfxterm.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/font.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/bufio.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h
endif
if COND_i386_multiboot
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h
endif
if COND_i386_qemu
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h
endif
if COND_i386_ieee1275
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ieee1275.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h
endif
if COND_i386_xen
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/xen.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/xen/hypercall.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/loader.h
endif
if COND_x86_64_xen
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/xen.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/x86_64/xen/hypercall.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/loader.h
endif
if COND_x86_64_efi
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pmtimer.h
endif
if COND_ia64_efi
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h
endif
if COND_mips
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/cpu/kernel.h
endif
if COND_mips_arc
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/arc/arc.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h
endif
if COND_mips_qemu_mips
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/keyboard_layouts.h
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/serial.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/loader.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h
endif
if COND_mips_loongson
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/keyboard_layouts.h
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/time.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/video.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/video_fb.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/gfxterm.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/font.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/bufio.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/cs5536.h
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/pci.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/serial.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/loader.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h
endif
if COND_mips_qemu_mips
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/memory.h
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h
endif
if COND_mips64_efi
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/loongson.h
endif
if COND_powerpc_ieee1275
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ieee1275.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h
endif
if COND_sparc64_ieee1275
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ieee1275.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/sparc64/ieee1275/ieee1275.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h
endif
if COND_arm_uboot
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/uboot/uboot.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/uboot/disk.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/arm/system.h
endif
if COND_arm_coreboot
KERNEL_HEADER_FILES += $(top_builddir)/include/grub/keyboard_layouts.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/arm/system.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/video.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/video_fb.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/gfxterm.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/font.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/bufio.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/fdt.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/dma.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/arm/coreboot/kernel.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/fdtbus.h
endif
if COND_arm_efi
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/arm/system.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h
endif
if COND_arm64_efi
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h
endif
if COND_riscv32_efi
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h
endif
if COND_riscv64_efi
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h
endif
if COND_emu
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/datetime.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/misc.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/net.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/hostdisk.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/hostfile.h
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h
if COND_GRUB_EMU_SDL
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/sdl.h
endif
if COND_GRUB_EMU_PCI
KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/libpciaccess.h
endif
endif
symlist.h: $(top_builddir)/config.h $(KERNEL_HEADER_FILES)
@list='$^'; \
for p in $$list; do \
echo "#include <$$p>" >> $@ || (rm -f $@; exit 1); \
done
CLEANFILES += symlist.h
BUILT_SOURCES += symlist.h
symlist.c: symlist.h gensymlist.sh
$(TARGET_CPP) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS_KERNEL) $(CPPFLAGS) -DGRUB_SYMBOL_GENERATOR=1 symlist.h > symlist.p || (rm -f symlist.p; exit 1)
cat symlist.p | $(SHELL) $(srcdir)/gensymlist.sh $(top_builddir)/config.h $(KERNEL_HEADER_FILES) >$@ || (rm -f $@; exit 1)
rm -f symlist.p
CLEANFILES += symlist.c
BUILT_SOURCES += symlist.c
if COND_HAVE_ASM_USCORE
ASM_PREFIX=_
else
ASM_PREFIX=
endif
noinst_DATA += kernel_syms.lst
kernel_syms.lst: $(KERNEL_HEADER_FILES) $(top_builddir)/config.h
$(TARGET_CPP) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS_KERNEL) $(CPPFLAGS) $(CFLAGS) -DGRUB_SYMBOL_GENERATOR=1 $^ >kernel_syms.input
cat kernel_syms.input | grep -v '^#' | sed -n \
-e '/EXPORT_FUNC *([a-zA-Z0-9_]*)/{s/.*EXPORT_FUNC *(\([a-zA-Z0-9_]*\)).*/defined kernel '"$(ASM_PREFIX)"'\1/;p;}' \
-e '/EXPORT_VAR *([a-zA-Z0-9_]*)/{s/.*EXPORT_VAR *(\([a-zA-Z0-9_]*\)).*/defined kernel '"$(ASM_PREFIX)"'\1/;p;}' \
| sort -u >$@
rm -f kernel_syms.input
CLEANFILES += kernel_syms.lst
if COND_emu
kern/emu/grub_emu-main.$(OBJEXT):grub_emu_init.h
grub_emu-grub_emu_init.$(OBJEXT):grub_emu_init.h
kern/emu/grub_emu_dyn-main.$(OBJEXT):grub_emu_init.h
grub_emu_dyn-grub_emu_init.$(OBJEXT):grub_emu_init.h
grub_emu_init.h: genemuinitheader.sh $(MODULE_FILES)
rm -f $@; echo $(MODULE_FILES) | sh $(srcdir)/genemuinitheader.sh $(TARGET_NM) > $@
CLEANFILES += grub_emu_init.h
grub_emu_init.c: grub_emu_init.h genemuinit.sh $(MODULE_FILES)
rm -f $@; echo $(MODULE_FILES) | sh $(srcdir)/genemuinit.sh $(TARGET_NM) > $@
CLEANFILES += grub_emu_init.c
endif
# List files
fs.lst: $(MARKER_FILES)
(for pp in $^; do \
b=`basename $$pp .marker`; \
if grep 'FS_LIST_MARKER' $$pp >/dev/null 2>&1; then \
echo $$b; \
fi; \
done) | sort -u > $@
platform_DATA += fs.lst
CLEANFILES += fs.lst
command.lst: $(MARKER_FILES)
(for pp in $^; do \
b=`basename $$pp .marker`; \
sed -n \
-e "/EXTCOMMAND_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/*\1: $$b/;p;}" \
-e "/P1COMMAND_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/*\1: $$b/;p;}" \
-e "/COMMAND_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/\1: $$b/;p;}" $$pp; \
done) | sort -u > $@
platform_DATA += command.lst
CLEANFILES += command.lst
partmap.lst: $(MARKER_FILES)
(for pp in $^; do \
b=`basename $$pp .marker`; \
if grep 'PARTMAP_LIST_MARKER' $$pp >/dev/null 2>&1; then \
echo $$b; \
fi; \
done) | sort -u > $@
platform_DATA += partmap.lst
CLEANFILES += partmap.lst
terminal.lst: $(MARKER_FILES)
(for pp in $^; do \
b=`basename $$pp .marker`; \
sed -n \
-e "/INPUT_TERMINAL_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/i\1: $$b/;p;}" \
-e "/OUTPUT_TERMINAL_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/o\1: $$b/;p;}" $$pp; \
done) | sort -u > $@
platform_DATA += terminal.lst
CLEANFILES += terminal.lst
fdt.lst: $(MARKER_FILES)
(for pp in $^; do \
b=`basename $$pp .marker`; \
sed -n \
-e "/FDT_DRIVER_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/i\1: $$b/;p;}" \
-e "/FDT_DRIVER_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/o\1: $$b/;p;}" $$pp; \
done) | sort -u > $@
platform_DATA += fdt.lst
CLEANFILES += fdt.lst
parttool.lst: $(MARKER_FILES)
(for pp in $^; do \
b=`basename $$pp .marker`; \
sed -n \
-e "/PARTTOOL_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/\1: $$b/;p;}" $$pp; \
done) | sort -u > $@
platform_DATA += parttool.lst
CLEANFILES += parttool.lst
video.lst: $(MARKER_FILES)
(for pp in $^; do \
b=`basename $$pp .marker`; \
if grep 'VIDEO_LIST_MARKER' $$pp >/dev/null 2>&1; then \
echo $$b; \
fi; \
done) | sort -u > $@
platform_DATA += video.lst
CLEANFILES += video.lst
# but, crypto.lst is simply copied
crypto.lst: $(srcdir)/lib/libgcrypt-grub/cipher/crypto.lst
cp $^ $@
platform_DATA += crypto.lst
CLEANFILES += crypto.lst
syminfo.lst: gensyminfo.sh kernel_syms.lst $(MODULE_FILES)
cat kernel_syms.lst > $@.new
for m in $(MODULE_FILES); do \
sh $< $$m >> $@.new || exit 1; \
done
mv $@.new $@
# generate global module dependencies list
moddep.lst: syminfo.lst genmoddep.awk video.lst
cat $< | sort | $(AWK) -f $(srcdir)/genmoddep.awk > $@ || (rm -f $@; exit 1)
platform_DATA += moddep.lst
CLEANFILES += config.log syminfo.lst moddep.lst
$(MOD_FILES): %.mod : genmod.sh moddep.lst %.module$(EXEEXT) build-grub-module-verifier$(BUILD_EXEEXT)
TARGET_OBJ2ELF=@TARGET_OBJ2ELF@ sh $^ $@
platform_DATA += $(MOD_FILES)
platform_DATA += modinfo.sh
CLEANFILES += $(MOD_FILES)
if COND_ENABLE_EFIEMU
efiemu32.o: efiemu/runtime/efiemu.c $(TARGET_OBJ2ELF)
-rm -f $@
-rm -f $@.bin
$(TARGET_CC) $(DEFS) $(INCLUDES) $(CPPFLAGS_EFIEMU) $(CPPFLAGS_DEFAULT) -m32 -Wall -Werror -nostdlib -static -O2 -c -o $@.bin $<
if test "x$(TARGET_APPLE_LINKER)" = x1; then \
$(TARGET_OBJCONV) -felf32 -nu -nd $@.bin $@ || exit 1; \
rm -f $@.bin ; \
elif test ! -z "$(TARGET_OBJ2ELF)"; then \
$(TARGET_OBJ2ELF) $@.bin || (rm -f $@.bin; exit 1); \
mv $@.bin $@ ; \
else \
mv $@.bin $@ ; \
fi
# Link format -arch,x86_64 means Apple linker
efiemu64_c.o: efiemu/runtime/efiemu.c
$(TARGET_CC) $(DEFS) $(INCLUDES) $(CPPFLAGS_EFIEMU) $(CPPFLAGS_DEFAULT) -m64 -nostdlib -Wall -Werror -O2 -mcmodel=large -mno-red-zone -c -o $@ $<
efiemu64_s.o: efiemu/runtime/efiemu.S
$(TARGET_CC) $(DEFS) $(INCLUDES) $(CPPFLAGS_EFIEMU) $(CPPFLAGS_DEFAULT) -m64 -Wall -Werror -nostdlib -O2 -mcmodel=large -mno-red-zone -c -o $@ $<
efiemu64.o: efiemu64_c.o efiemu64_s.o $(TARGET_OBJ2ELEF)
-rm -f $@
-rm -f $@.bin
$(TARGET_CC) -m64 $(EFIEMU64_LINK_FORMAT) -nostdlib -static -Wl,-r -o $@.bin $^
if test "x$(EFIEMU64_LINK_FORMAT)" = x-arch,x86_64; then \
$(TARGET_OBJCONV) -felf64 -nu -nd $@.bin $@ || exit 1; \
rm -f $@.bin; \
else \
mv $@.bin $@ ; \
fi
platform_DATA += efiemu32.o efiemu64.o
CLEANFILES += efiemu32.o efiemu64.o efiemu64_c.o efiemu64_s.o
endif
windowsdir=$(top_builddir)/$(PACKAGE)-$(VERSION)-for-windows
windowsdir: $(PROGRAMS) $(starfield_DATA) $(platform_DATA)
test -d $(windowsdir)/$(target_cpu)-$(platform) || mkdir $(windowsdir)/$(target_cpu)-$(platform)
for x in $(platform_DATA); do \
cp -fp $$x $(windowsdir)/$(target_cpu)-$(platform)/$$x; \
done
File diff suppressed because it is too large Load Diff
@@ -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 "VT"
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
@@ -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);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,205 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2022 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/efi/api.h>
#include <grub/efi/efi.h>
#include <grub/err.h>
#include <grub/extcmd.h>
#include <grub/file.h>
#include <grub/i18n.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/types.h>
GRUB_MOD_LICENSE ("GPLv3+");
static grub_efi_guid_t loaded_image_guid = GRUB_EFI_LOADED_IMAGE_GUID;
static grub_efi_status_t
grub_efi_connect_all (void)
{
grub_efi_status_t status;
grub_efi_uintn_t handle_count;
grub_efi_handle_t *handle_buffer;
grub_efi_uintn_t index;
grub_efi_boot_services_t *b;
grub_dprintf ("efi", "Connecting ...\n");
b = grub_efi_system_table->boot_services;
status = efi_call_5 (b->locate_handle_buffer,
GRUB_EFI_ALL_HANDLES, NULL, NULL,
&handle_count, &handle_buffer);
if (status != GRUB_EFI_SUCCESS)
return status;
for (index = 0; index < handle_count; index++)
{
status = efi_call_4 (b->connect_controller,
handle_buffer[index], NULL, NULL, 1);
}
if (handle_buffer)
{
efi_call_1 (b->free_pool, handle_buffer);
}
return GRUB_EFI_SUCCESS;
}
static grub_err_t
grub_efi_load_driver (grub_size_t size, void *boot_image, int connect)
{
grub_efi_status_t status;
grub_efi_handle_t driver_handle;
grub_efi_boot_services_t *b;
grub_efi_loaded_image_t *loaded_image;
b = grub_efi_system_table->boot_services;
status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, NULL,
boot_image, size, &driver_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;
}
loaded_image = grub_efi_get_loaded_image (driver_handle);
if (! loaded_image)
{
grub_error (GRUB_ERR_BAD_OS, "no loaded image available");
goto fail;
}
grub_dprintf ("efi", "Registering loaded image\n");
status = efi_call_3 (b->handle_protocol, driver_handle,
&loaded_image_guid, (void **)&loaded_image);
if (status != GRUB_EFI_SUCCESS)
{
grub_error (GRUB_ERR_BAD_OS, "not a dirver");
goto fail;
}
grub_dprintf ("efi", "StartImage: %p\n", boot_image);
status = efi_call_3 (b->start_image, driver_handle, NULL, NULL);
if (status != GRUB_EFI_SUCCESS)
{
grub_error (GRUB_ERR_BAD_OS, "StartImage failed");
goto fail;
}
if (connect)
{
status = grub_efi_connect_all ();
if (status != GRUB_EFI_SUCCESS)
{
grub_error (GRUB_ERR_BAD_OS, "cannot connect controllers\n");
goto fail;
}
}
grub_dprintf ("efi", "Driver installed\n");
return 0;
fail:
return grub_errno;
}
static const struct grub_arg_option options_fwload[] =
{
{"nc", 'n', 0, N_("Loads the driver, but does not connect the driver."), 0, 0},
{0, 0, 0, 0, 0, 0}
};
static grub_err_t
grub_cmd_fwload (grub_extcmd_context_t ctxt, int argc, char **args)
{
struct grub_arg_list *state = ctxt->state;
int connect = 1;
grub_file_t file = 0;
grub_efi_boot_services_t *b;
grub_efi_status_t status;
grub_efi_uintn_t pages = 0;
grub_ssize_t size;
grub_efi_physical_address_t address;
void *boot_image = 0;
b = grub_efi_system_table->boot_services;
if (argc != 1)
goto fail;
file = grub_file_open (args[0], GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE);
if (! file)
goto fail;
size = grub_file_size (file);
if (!size)
{
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), args[0]);
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_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"), args[0]);
goto fail;
}
grub_file_close (file);
if (state[0].set)
connect = 0;
if (grub_efi_load_driver (size, boot_image, connect))
goto fail;
return GRUB_ERR_NONE;
fail:
if (file)
grub_file_close (file);
if (address)
efi_call_2 (b->free_pages, address, pages);
return grub_errno;
}
static grub_err_t
grub_cmd_fwconnect (grub_extcmd_context_t ctxt __attribute__ ((unused)),
int argc __attribute__ ((unused)),
char **args __attribute__ ((unused)))
{
grub_efi_connect_all ();
return GRUB_ERR_NONE;
}
static grub_extcmd_t cmd_fwload, cmd_fwconnect;
GRUB_MOD_INIT(fwload)
{
cmd_fwload = grub_register_extcmd ("fwload", grub_cmd_fwload, 0, N_("FILE"),
N_("Install UEFI driver."), options_fwload);
cmd_fwconnect = grub_register_extcmd ("fwconnect", grub_cmd_fwconnect, 0,
NULL, N_("Connect drivers."), 0);
}
GRUB_MOD_FINI(fwload)
{
grub_unregister_extcmd (cmd_fwload);
grub_unregister_extcmd (cmd_fwconnect);
}
@@ -0,0 +1,61 @@
/* smbios.c - get smbios tables. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2019 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/smbios.h>
#include <grub/misc.h>
#include <grub/efi/efi.h>
#include <grub/efi/api.h>
struct grub_smbios_eps *
grub_machine_smbios_get_eps (void)
{
unsigned i;
static grub_efi_packed_guid_t smbios_guid = GRUB_EFI_SMBIOS_TABLE_GUID;
for (i = 0; i < grub_efi_system_table->num_table_entries; i++)
{
grub_efi_packed_guid_t *guid =
&grub_efi_system_table->configuration_table[i].vendor_guid;
if (! grub_memcmp (guid, &smbios_guid, sizeof (grub_efi_packed_guid_t)))
return (struct grub_smbios_eps *)
grub_efi_system_table->configuration_table[i].vendor_table;
}
return 0;
}
struct grub_smbios_eps3 *
grub_machine_smbios_get_eps3 (void)
{
unsigned i;
static grub_efi_packed_guid_t smbios3_guid = GRUB_EFI_SMBIOS3_TABLE_GUID;
for (i = 0; i < grub_efi_system_table->num_table_entries; i++)
{
grub_efi_packed_guid_t *guid =
&grub_efi_system_table->configuration_table[i].vendor_guid;
if (! grub_memcmp (guid, &smbios3_guid, sizeof (grub_efi_packed_guid_t)))
return (struct grub_smbios_eps3 *)
grub_efi_system_table->configuration_table[i].vendor_table;
}
return 0;
}
@@ -0,0 +1,351 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 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/dl.h>
#include <grub/extcmd.h>
#include <grub/file.h>
#include <grub/disk.h>
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/crypto.h>
#include <grub/normal.h>
#include <grub/i18n.h>
GRUB_MOD_LICENSE ("GPLv3+");
static const struct grub_arg_option options[] = {
{"hash", 'h', 0, N_("Specify hash to use."), N_("HASH"), ARG_TYPE_STRING},
{"check", 'c', 0, N_("Check hashes of files with hash list FILE."),
N_("FILE"), ARG_TYPE_STRING},
{"prefix", 'p', 0, N_("Base directory for hash list."), N_("DIR"),
ARG_TYPE_STRING},
{"keep-going", 'k', 0, N_("Don't stop after first error."), 0, 0},
{"uncompress", 'u', 0, N_("Uncompress file before checksumming."), 0, 0},
{0, 0, 0, 0, 0, 0}
};
static struct { const char *name; const char *hashname; } aliases[] =
{
{"sha256sum", "sha256"},
{"sha512sum", "sha512"},
{"sha1sum", "sha1"},
{"md5sum", "md5"},
{"crc", "crc32"},
};
static inline int
hextoval (char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -1;
}
static grub_err_t
hash_file (grub_file_t file, const gcry_md_spec_t *hash, void *result)
{
int progress = 0;
grub_uint64_t ro = 0;
grub_uint64_t div = 0;
grub_uint64_t total = 0;
void *context;
grub_uint8_t *readbuf;
#define BUF_SIZE 1024 * 1024
readbuf = grub_malloc (BUF_SIZE);
if (!readbuf)
return grub_errno;
context = grub_zalloc (hash->contextsize);
if (!readbuf || !context)
goto fail;
if (file->size > 16 * 1024 * 1024)
progress = 1;
hash->init (context);
while (1)
{
grub_ssize_t r;
r = grub_file_read (file, readbuf, BUF_SIZE);
if (r < 0)
goto fail;
if (r == 0)
break;
hash->write (context, readbuf, r);
if (progress)
{
total += r;
div = grub_divmod64(total * 100, (grub_uint64_t)file->size, &ro);
grub_printf("\rCalculating %s %d%% ", hash->name, (int)div);
grub_refresh();
}
}
hash->final (context);
grub_memcpy (result, hash->read (context), hash->mdlen);
grub_free (readbuf);
grub_free (context);
if (progress)
{
grub_printf("\rCalculating %s 100%% \n\r\n", hash->name);
grub_refresh();
}
return GRUB_ERR_NONE;
fail:
grub_free (readbuf);
grub_free (context);
return grub_errno;
}
static grub_err_t
check_list (const gcry_md_spec_t *hash, const char *hashfilename,
const char *prefix, int keep, int uncompress)
{
grub_file_t hashlist, file;
char *buf = NULL;
grub_uint8_t expected[GRUB_CRYPTO_MAX_MDLEN];
grub_uint8_t actual[GRUB_CRYPTO_MAX_MDLEN];
grub_err_t err;
unsigned i;
unsigned unread = 0, mismatch = 0;
if (hash->mdlen > GRUB_CRYPTO_MAX_MDLEN)
return grub_error (GRUB_ERR_BUG, "mdlen is too long");
hashlist = grub_file_open (hashfilename, GRUB_FILE_TYPE_HASHLIST);
if (!hashlist)
return grub_errno;
while (grub_free (buf), (buf = grub_file_getline (hashlist)))
{
const char *p = buf;
while (grub_isspace (p[0]))
p++;
for (i = 0; i < hash->mdlen; i++)
{
int high, low;
high = hextoval (*p++);
low = hextoval (*p++);
if (high < 0 || low < 0)
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid hash list");
expected[i] = (high << 4) | low;
}
if ((p[0] != ' ' && p[0] != '\t') || (p[1] != ' ' && p[1] != '\t'))
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid hash list");
p += 2;
if (prefix)
{
char *filename;
filename = grub_xasprintf ("%s/%s", prefix, p);
if (!filename)
return grub_errno;
file = grub_file_open (filename, GRUB_FILE_TYPE_TO_HASH
| (!uncompress ? GRUB_FILE_TYPE_NO_DECOMPRESS
: GRUB_FILE_TYPE_NONE));
grub_free (filename);
}
else
file = grub_file_open (p, GRUB_FILE_TYPE_TO_HASH
| (!uncompress ? GRUB_FILE_TYPE_NO_DECOMPRESS
: GRUB_FILE_TYPE_NONE));
if (!file)
{
grub_file_close (hashlist);
grub_free (buf);
return grub_errno;
}
err = hash_file (file, hash, actual);
grub_file_close (file);
if (err)
{
grub_printf_ (N_("%s: READ ERROR\n"), p);
if (!keep)
{
grub_file_close (hashlist);
grub_free (buf);
return err;
}
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
unread++;
continue;
}
if (grub_crypto_memcmp (expected, actual, hash->mdlen) != 0)
{
grub_printf_ (N_("%s: HASH MISMATCH\n"), p);
if (!keep)
{
grub_file_close (hashlist);
grub_free (buf);
return grub_error (GRUB_ERR_TEST_FAILURE,
"hash of '%s' mismatches", p);
}
mismatch++;
continue;
}
grub_printf_ (N_("%s: OK\n"), p);
}
if (mismatch || unread)
return grub_error (GRUB_ERR_TEST_FAILURE,
"%d files couldn't be read and hash "
"of %d files mismatches", unread, mismatch);
return GRUB_ERR_NONE;
}
static grub_err_t
grub_cmd_hashsum (struct grub_extcmd_context *ctxt,
int argc, char **args)
{
struct grub_arg_list *state = ctxt->state;
const char *hashname = NULL;
const char *prefix = NULL;
const gcry_md_spec_t *hash;
unsigned i;
int keep = state[3].set;
int uncompress = state[4].set;
unsigned unread = 0;
int len = 0;
char hashsum[256];
for (i = 0; i < ARRAY_SIZE (aliases); i++)
if (grub_strcmp (ctxt->extcmd->cmd->name, aliases[i].name) == 0)
hashname = aliases[i].hashname;
if (state[0].set)
hashname = state[0].arg;
if (!hashname)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "no hash specified");
hash = grub_crypto_lookup_md_by_name (hashname);
if (!hash)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown hash");
if (hash->mdlen > GRUB_CRYPTO_MAX_MDLEN)
return grub_error (GRUB_ERR_BUG, "mdlen is too long");
if (state[2].set)
prefix = state[2].arg;
if (state[1].set)
{
if (argc != 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"--check is incompatible with file list");
return check_list (hash, state[1].arg, prefix, keep, uncompress);
}
for (i = 0; i < (unsigned) argc; i++)
{
GRUB_PROPERLY_ALIGNED_ARRAY (result, GRUB_CRYPTO_MAX_MDLEN);
grub_file_t file;
grub_err_t err;
unsigned j;
int vlnk = 0;
file = grub_file_open (args[i], GRUB_FILE_TYPE_TO_HASH
| (!uncompress ? GRUB_FILE_TYPE_NO_DECOMPRESS
: GRUB_FILE_TYPE_NONE));
if (!file)
{
if (!keep)
return grub_errno;
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
unread++;
continue;
}
vlnk = file->vlnk;
err = hash_file (file, hash, result);
grub_file_close (file);
if (err)
{
if (!keep)
return err;
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
unread++;
continue;
}
for (j = 0; j < hash->mdlen; j++)
{
grub_printf ("%02x", ((grub_uint8_t *) result)[j]);
len += grub_snprintf(hashsum + len, sizeof(hashsum) - len, "%02x", ((grub_uint8_t *) result)[j]);
}
grub_printf (" %s\n", vlnk ? grub_file_get_vlnk(args[i], NULL) : args[i]);
grub_env_set("VT_LAST_CHECK_SUM", hashsum);
}
if (unread)
return grub_error (GRUB_ERR_TEST_FAILURE, "%d files couldn't be read",
unread);
return GRUB_ERR_NONE;
}
static grub_extcmd_t cmd, cmd_md5, cmd_sha1, cmd_sha256, cmd_sha512, cmd_crc;
GRUB_MOD_INIT(hashsum)
{
cmd = grub_register_extcmd ("hashsum", grub_cmd_hashsum, 0,
N_("-h HASH [-c FILE [-p PREFIX]] "
"[FILE1 [FILE2 ...]]"),
/* TRANSLATORS: "hash checksum" is just to
be a bit more precise, you can treat it as
just "hash". */
N_("Compute or check hash checksum."),
options);
cmd_md5 = grub_register_extcmd ("md5sum", grub_cmd_hashsum, 0,
N_("[-c FILE [-p PREFIX]] "
"[FILE1 [FILE2 ...]]"),
N_("Compute or check hash checksum."),
options);
cmd_sha1 = grub_register_extcmd ("sha1sum", grub_cmd_hashsum, 0,
N_("[-c FILE [-p PREFIX]] "
"[FILE1 [FILE2 ...]]"),
N_("Compute or check hash checksum."),
options);
cmd_sha256 = grub_register_extcmd ("sha256sum", grub_cmd_hashsum, 0,
N_("[-c FILE [-p PREFIX]] "
"[FILE1 [FILE2 ...]]"),
N_("Compute or check hash checksum."),
options);
cmd_sha512 = grub_register_extcmd ("sha512sum", grub_cmd_hashsum, 0,
N_("[-c FILE [-p PREFIX]] "
"[FILE1 [FILE2 ...]]"),
N_("Compute or check hash checksum."),
options);
cmd_crc = grub_register_extcmd ("crc", grub_cmd_hashsum, 0,
N_("[-c FILE [-p PREFIX]] "
"[FILE1 [FILE2 ...]]"),
N_("Compute or check hash checksum."),
options);
}
GRUB_MOD_FINI(hashsum)
{
grub_unregister_extcmd (cmd);
grub_unregister_extcmd (cmd_md5);
grub_unregister_extcmd (cmd_sha1);
grub_unregister_extcmd (cmd_sha256);
grub_unregister_extcmd (cmd_sha512);
grub_unregister_extcmd (cmd_crc);
}
@@ -0,0 +1,52 @@
/* smbios.c - get smbios tables. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2019 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/acpi.h>
#include <grub/smbios.h>
#include <grub/misc.h>
struct grub_smbios_eps *
grub_machine_smbios_get_eps (void)
{
grub_uint8_t *ptr;
grub_dprintf ("smbios", "Looking for SMBIOS EPS. Scanning BIOS\n");
for (ptr = (grub_uint8_t *) 0xf0000; ptr < (grub_uint8_t *) 0x100000; ptr += 16)
if (grub_memcmp (ptr, "_SM_", 4) == 0
&& grub_byte_checksum (ptr, sizeof (struct grub_smbios_eps)) == 0)
return (struct grub_smbios_eps *) ptr;
return 0;
}
struct grub_smbios_eps3 *
grub_machine_smbios_get_eps3 (void)
{
grub_uint8_t *ptr;
grub_dprintf ("smbios", "Looking for SMBIOS3 EPS. Scanning BIOS\n");
for (ptr = (grub_uint8_t *) 0xf0000; ptr < (grub_uint8_t *) 0x100000; ptr += 16)
if (grub_memcmp (ptr, "_SM3_", 5) == 0
&& grub_byte_checksum (ptr, sizeof (struct grub_smbios_eps3)) == 0)
return (struct grub_smbios_eps3 *) ptr;
return 0;
}
@@ -0,0 +1,894 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2000, 2001, 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/types.h>
#include <grub/misc.h>
#include <grub/command.h>
#include <grub/mm.h>
#include <grub/err.h>
#include <grub/dl.h>
#include <grub/file.h>
#include <grub/normal.h>
#include <grub/script_sh.h>
#include <grub/i18n.h>
#include <grub/term.h>
#include <grub/legacy_parse.h>
#include <grub/crypto.h>
#include <grub/auth.h>
#include <grub/disk.h>
#include <grub/partition.h>
GRUB_MOD_LICENSE ("GPLv3+");
/* Helper for legacy_file. */
static grub_err_t
legacy_file_getline (char **line, int cont __attribute__ ((unused)),
void *data __attribute__ ((unused)))
{
*line = 0;
return GRUB_ERR_NONE;
}
static grub_err_t
legacy_file (const char *filename)
{
grub_file_t file;
char *entryname = NULL, *entrysrc = NULL;
grub_menu_t menu;
char *suffix = grub_strdup ("");
if (!suffix)
return grub_errno;
file = grub_file_open (filename, GRUB_FILE_TYPE_CONFIG);
if (! file)
{
grub_free (suffix);
return grub_errno;
}
menu = grub_env_get_menu ();
if (! menu)
{
menu = grub_zalloc (sizeof (*menu));
if (! menu)
{
grub_free (suffix);
return grub_errno;
}
grub_env_set_menu (menu);
}
while (1)
{
char *buf = grub_file_getline (file);
char *parsed = NULL;
if (!buf && grub_errno)
{
grub_file_close (file);
grub_free (suffix);
return grub_errno;
}
if (!buf)
break;
{
char *oldname = NULL;
char *newsuffix;
char *ptr;
for (ptr = buf; *ptr && grub_isspace (*ptr); ptr++);
oldname = entryname;
parsed = grub_legacy_parse (ptr, &entryname, &newsuffix);
grub_free (buf);
buf = NULL;
if (newsuffix)
{
char *t;
t = suffix;
suffix = grub_realloc (suffix, grub_strlen (suffix)
+ grub_strlen (newsuffix) + 1);
if (!suffix)
{
grub_free (t);
grub_free (entrysrc);
grub_free (parsed);
grub_free (newsuffix);
grub_free (suffix);
return grub_errno;
}
grub_memcpy (suffix + grub_strlen (suffix), newsuffix,
grub_strlen (newsuffix) + 1);
grub_free (newsuffix);
newsuffix = NULL;
}
if (oldname != entryname && oldname)
{
const char **args = grub_malloc (sizeof (args[0]));
if (!args)
{
grub_file_close (file);
return grub_errno;
}
args[0] = oldname;
grub_normal_add_menu_entry (1, args, NULL, NULL, "legacy",
NULL, NULL,
entrysrc, 0, NULL, NULL);
grub_free (args);
entrysrc[0] = 0;
grub_free (oldname);
}
}
if (parsed && !entryname)
{
grub_normal_parse_line (parsed, legacy_file_getline, NULL);
grub_print_error ();
grub_free (parsed);
parsed = NULL;
}
else if (parsed)
{
if (!entrysrc)
entrysrc = parsed;
else
{
char *t;
t = entrysrc;
entrysrc = grub_realloc (entrysrc, grub_strlen (entrysrc)
+ grub_strlen (parsed) + 1);
if (!entrysrc)
{
grub_free (t);
grub_free (parsed);
grub_free (suffix);
return grub_errno;
}
grub_memcpy (entrysrc + grub_strlen (entrysrc), parsed,
grub_strlen (parsed) + 1);
grub_free (parsed);
parsed = NULL;
}
}
}
grub_file_close (file);
if (entryname)
{
const char **args = grub_malloc (sizeof (args[0]));
if (!args)
{
grub_file_close (file);
grub_free (suffix);
grub_free (entrysrc);
return grub_errno;
}
args[0] = entryname;
grub_normal_add_menu_entry (1, args, NULL, NULL, NULL,
NULL, NULL, entrysrc, 0, NULL,
NULL);
grub_free (args);
}
grub_normal_parse_line (suffix, legacy_file_getline, NULL);
grub_print_error ();
grub_free (suffix);
grub_free (entrysrc);
return GRUB_ERR_NONE;
}
static grub_err_t
grub_cmd_legacy_source (struct grub_command *cmd,
int argc, char **args)
{
int new_env, extractor;
grub_err_t ret;
if (argc != 1)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
extractor = (cmd->name[0] == 'e');
new_env = (cmd->name[extractor ? (sizeof ("extract_legacy_entries_") - 1)
: (sizeof ("legacy_") - 1)] == 'c');
if (new_env)
grub_cls ();
if (new_env && !extractor)
grub_env_context_open ();
if (extractor)
grub_env_extractor_open (!new_env);
ret = legacy_file (args[0]);
if (new_env)
{
grub_menu_t menu;
menu = grub_env_get_menu ();
if (menu && menu->size)
grub_show_menu (menu, 1, 0);
if (!extractor)
grub_env_context_close ();
}
if (extractor)
grub_env_extractor_close (!new_env);
return ret;
}
static enum
{
GUESS_IT, LINUX, MULTIBOOT, KFREEBSD, KNETBSD, KOPENBSD
} kernel_type;
static grub_err_t
grub_cmd_legacy_kernel (struct grub_command *mycmd __attribute__ ((unused)),
int argc, char **args)
{
int i;
#ifdef TODO
int no_mem_option = 0;
#endif
struct grub_command *cmd;
char **cutargs;
int cutargc;
grub_err_t err = GRUB_ERR_NONE;
for (i = 0; i < 2; i++)
{
/* FIXME: really support this. */
if (argc >= 1 && grub_strcmp (args[0], "--no-mem-option") == 0)
{
#ifdef TODO
no_mem_option = 1;
#endif
argc--;
args++;
continue;
}
/* linux16 handles both zImages and bzImages. */
if (argc >= 1 && (grub_strcmp (args[0], "--type=linux") == 0
|| grub_strcmp (args[0], "--type=biglinux") == 0))
{
kernel_type = LINUX;
argc--;
args++;
continue;
}
if (argc >= 1 && grub_strcmp (args[0], "--type=multiboot") == 0)
{
kernel_type = MULTIBOOT;
argc--;
args++;
continue;
}
if (argc >= 1 && grub_strcmp (args[0], "--type=freebsd") == 0)
{
kernel_type = KFREEBSD;
argc--;
args++;
continue;
}
if (argc >= 1 && grub_strcmp (args[0], "--type=openbsd") == 0)
{
kernel_type = KOPENBSD;
argc--;
args++;
continue;
}
if (argc >= 1 && grub_strcmp (args[0], "--type=netbsd") == 0)
{
kernel_type = KNETBSD;
argc--;
args++;
continue;
}
}
if (argc < 2)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
cutargs = grub_malloc (sizeof (cutargs[0]) * (argc - 1));
if (!cutargs)
return grub_errno;
cutargc = argc - 1;
grub_memcpy (cutargs + 1, args + 2, sizeof (cutargs[0]) * (argc - 2));
cutargs[0] = args[0];
do
{
/* First try Linux. */
if (kernel_type == GUESS_IT || kernel_type == LINUX)
{
#ifdef GRUB_MACHINE_PCBIOS
cmd = grub_command_find ("linux16");
#else
cmd = grub_command_find ("linux");
#endif
if (cmd)
{
if (!(cmd->func) (cmd, cutargc, cutargs))
{
kernel_type = LINUX;
goto out;
}
}
grub_errno = GRUB_ERR_NONE;
}
/* Then multiboot. */
if (kernel_type == GUESS_IT || kernel_type == MULTIBOOT)
{
cmd = grub_command_find ("multiboot");
if (cmd)
{
if (!(cmd->func) (cmd, argc, args))
{
kernel_type = MULTIBOOT;
goto out;
}
}
grub_errno = GRUB_ERR_NONE;
}
{
int bsd_device = -1;
int bsd_slice = -1;
int bsd_part = -1;
{
grub_device_t dev;
const char *hdbiasstr;
int hdbias = 0;
hdbiasstr = grub_env_get ("legacy_hdbias");
if (hdbiasstr)
{
hdbias = grub_strtoul (hdbiasstr, 0, 0);
grub_errno = GRUB_ERR_NONE;
}
dev = grub_device_open (0);
if (dev && dev->disk
&& dev->disk->dev->id == GRUB_DISK_DEVICE_BIOSDISK_ID
&& dev->disk->id >= 0x80 && dev->disk->id <= 0x90)
{
struct grub_partition *part = dev->disk->partition;
bsd_device = dev->disk->id - 0x80 - hdbias;
if (part && (grub_strcmp (part->partmap->name, "netbsd") == 0
|| grub_strcmp (part->partmap->name, "openbsd") == 0
|| grub_strcmp (part->partmap->name, "bsd") == 0))
{
bsd_part = part->number;
part = part->parent;
}
if (part && grub_strcmp (part->partmap->name, "msdos") == 0)
bsd_slice = part->number;
}
if (dev)
grub_device_close (dev);
}
/* k*BSD didn't really work well with grub-legacy. */
if (kernel_type == GUESS_IT || kernel_type == KFREEBSD)
{
char buf[sizeof("adXXXXXXXXXXXXsXXXXXXXXXXXXYYY")];
if (bsd_device != -1)
{
if (bsd_slice != -1 && bsd_part != -1)
grub_snprintf(buf, sizeof(buf), "ad%ds%d%c", bsd_device,
bsd_slice, 'a' + bsd_part);
else if (bsd_slice != -1)
grub_snprintf(buf, sizeof(buf), "ad%ds%d", bsd_device,
bsd_slice);
else
grub_snprintf(buf, sizeof(buf), "ad%d", bsd_device);
grub_env_set ("kFreeBSD.vfs.root.mountfrom", buf);
}
else
grub_env_unset ("kFreeBSD.vfs.root.mountfrom");
cmd = grub_command_find ("kfreebsd");
if (cmd)
{
if (!(cmd->func) (cmd, cutargc, cutargs))
{
kernel_type = KFREEBSD;
goto out;
}
}
grub_errno = GRUB_ERR_NONE;
}
{
char **bsdargs;
int bsdargc;
char bsddevname[sizeof ("wdXXXXXXXXXXXXY")];
int found = 0;
if (bsd_device == -1)
{
bsdargs = cutargs;
bsdargc = cutargc;
}
else
{
char rbuf[3] = "-r";
bsdargc = cutargc + 2;
bsdargs = grub_malloc (sizeof (bsdargs[0]) * bsdargc);
if (!bsdargs)
{
err = grub_errno;
goto out;
}
grub_memcpy (bsdargs, args, argc * sizeof (bsdargs[0]));
bsdargs[argc] = rbuf;
bsdargs[argc + 1] = bsddevname;
grub_snprintf (bsddevname, sizeof (bsddevname),
"wd%d%c", bsd_device,
bsd_part != -1 ? bsd_part + 'a' : 'c');
}
if (kernel_type == GUESS_IT || kernel_type == KNETBSD)
{
cmd = grub_command_find ("knetbsd");
if (cmd)
{
if (!(cmd->func) (cmd, bsdargc, bsdargs))
{
kernel_type = KNETBSD;
found = 1;
goto free_bsdargs;
}
}
grub_errno = GRUB_ERR_NONE;
}
if (kernel_type == GUESS_IT || kernel_type == KOPENBSD)
{
cmd = grub_command_find ("kopenbsd");
if (cmd)
{
if (!(cmd->func) (cmd, bsdargc, bsdargs))
{
kernel_type = KOPENBSD;
found = 1;
goto free_bsdargs;
}
}
grub_errno = GRUB_ERR_NONE;
}
free_bsdargs:
if (bsdargs != cutargs)
grub_free (bsdargs);
if (found)
goto out;
}
}
}
while (0);
err = grub_error (GRUB_ERR_BAD_OS, "couldn't load file %s",
args[0]);
out:
grub_free (cutargs);
return err;
}
static grub_err_t
grub_cmd_legacy_initrd (struct grub_command *mycmd __attribute__ ((unused)),
int argc, char **args)
{
struct grub_command *cmd;
if (kernel_type == LINUX)
{
#ifdef GRUB_MACHINE_PCBIOS
cmd = grub_command_find ("initrd16");
#else
cmd = grub_command_find ("initrd");
#endif
if (!cmd)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("can't find command `%s'"),
#ifdef GRUB_MACHINE_PCBIOS
"initrd16"
#else
"initrd"
#endif
);
return cmd->func (cmd, argc ? 1 : 0, args);
}
if (kernel_type == MULTIBOOT)
{
cmd = grub_command_find ("module");
if (!cmd)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("can't find command `%s'"),
"module");
return cmd->func (cmd, argc, args);
}
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("you need to load the kernel first"));
}
static grub_err_t
grub_cmd_legacy_initrdnounzip (struct grub_command *mycmd __attribute__ ((unused)),
int argc, char **args)
{
struct grub_command *cmd;
if (kernel_type == LINUX)
{
cmd = grub_command_find ("initrd16");
if (!cmd)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("can't find command `%s'"),
"initrd16");
return cmd->func (cmd, argc, args);
}
if (kernel_type == MULTIBOOT)
{
char **newargs;
grub_err_t err;
char nounzipbuf[10] = "--nounzip";
cmd = grub_command_find ("module");
if (!cmd)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("can't find command `%s'"),
"module");
newargs = grub_malloc ((argc + 1) * sizeof (newargs[0]));
if (!newargs)
return grub_errno;
grub_memcpy (newargs + 1, args, argc * sizeof (newargs[0]));
newargs[0] = nounzipbuf;
err = cmd->func (cmd, argc + 1, newargs);
grub_free (newargs);
return err;
}
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("you need to load the kernel first"));
}
static grub_err_t
check_password_deny (const char *user __attribute__ ((unused)),
const char *entered __attribute__ ((unused)),
void *password __attribute__ ((unused)))
{
return GRUB_ACCESS_DENIED;
}
#define MD5_HASHLEN 16
struct legacy_md5_password
{
grub_uint8_t *salt;
int saltlen;
grub_uint8_t hash[MD5_HASHLEN];
};
static int
check_password_md5_real (const char *entered,
struct legacy_md5_password *pw)
{
grub_size_t enteredlen = grub_strlen (entered);
unsigned char alt_result[MD5_HASHLEN];
unsigned char *digest;
grub_uint8_t *ctx;
grub_size_t i;
int ret;
ctx = grub_zalloc (GRUB_MD_MD5->contextsize);
if (!ctx)
return 0;
GRUB_MD_MD5->init (ctx);
GRUB_MD_MD5->write (ctx, entered, enteredlen);
GRUB_MD_MD5->write (ctx, pw->salt + 3, pw->saltlen - 3);
GRUB_MD_MD5->write (ctx, entered, enteredlen);
digest = GRUB_MD_MD5->read (ctx);
GRUB_MD_MD5->final (ctx);
grub_memcpy (alt_result, digest, MD5_HASHLEN);
GRUB_MD_MD5->init (ctx);
GRUB_MD_MD5->write (ctx, entered, enteredlen);
GRUB_MD_MD5->write (ctx, pw->salt, pw->saltlen); /* include the $1$ header */
for (i = enteredlen; i > 16; i -= 16)
GRUB_MD_MD5->write (ctx, alt_result, 16);
GRUB_MD_MD5->write (ctx, alt_result, i);
for (i = enteredlen; i > 0; i >>= 1)
GRUB_MD_MD5->write (ctx, entered + ((i & 1) ? enteredlen : 0), 1);
digest = GRUB_MD_MD5->read (ctx);
GRUB_MD_MD5->final (ctx);
for (i = 0; i < 1000; i++)
{
grub_memcpy (alt_result, digest, 16);
GRUB_MD_MD5->init (ctx);
if ((i & 1) != 0)
GRUB_MD_MD5->write (ctx, entered, enteredlen);
else
GRUB_MD_MD5->write (ctx, alt_result, 16);
if (i % 3 != 0)
GRUB_MD_MD5->write (ctx, pw->salt + 3, pw->saltlen - 3);
if (i % 7 != 0)
GRUB_MD_MD5->write (ctx, entered, enteredlen);
if ((i & 1) != 0)
GRUB_MD_MD5->write (ctx, alt_result, 16);
else
GRUB_MD_MD5->write (ctx, entered, enteredlen);
digest = GRUB_MD_MD5->read (ctx);
GRUB_MD_MD5->final (ctx);
}
ret = (grub_crypto_memcmp (digest, pw->hash, MD5_HASHLEN) == 0);
grub_free (ctx);
return ret;
}
static grub_err_t
check_password_md5 (const char *user,
const char *entered,
void *password)
{
if (!check_password_md5_real (entered, password))
return GRUB_ACCESS_DENIED;
grub_auth_authenticate (user);
return GRUB_ERR_NONE;
}
static inline int
ib64t (char c)
{
if (c == '.')
return 0;
if (c == '/')
return 1;
if (c >= '0' && c <= '9')
return c - '0' + 2;
if (c >= 'A' && c <= 'Z')
return c - 'A' + 12;
if (c >= 'a' && c <= 'z')
return c - 'a' + 38;
return -1;
}
static struct legacy_md5_password *
parse_legacy_md5 (int argc, char **args)
{
const char *salt, *saltend;
struct legacy_md5_password *pw = NULL;
int i;
const char *p;
if (grub_memcmp (args[0], "--md5", sizeof ("--md5")) != 0)
goto fail;
if (argc == 1)
goto fail;
if (grub_strlen(args[1]) <= 3)
goto fail;
salt = args[1];
saltend = grub_strchr (salt + 3, '$');
if (!saltend)
goto fail;
pw = grub_malloc (sizeof (*pw));
if (!pw)
goto fail;
p = saltend + 1;
for (i = 0; i < 5; i++)
{
int n;
grub_uint32_t w = 0;
for (n = 0; n < 4; n++)
{
int ww = ib64t(*p++);
if (ww == -1)
goto fail;
w |= ww << (n * 6);
}
pw->hash[i == 4 ? 5 : 12+i] = w & 0xff;
pw->hash[6+i] = (w >> 8) & 0xff;
pw->hash[i] = (w >> 16) & 0xff;
}
{
int n;
grub_uint32_t w = 0;
for (n = 0; n < 2; n++)
{
int ww = ib64t(*p++);
if (ww == -1)
goto fail;
w |= ww << (6 * n);
}
if (w >= 0x100)
goto fail;
pw->hash[11] = w;
}
pw->saltlen = saltend - salt;
pw->salt = (grub_uint8_t *) grub_strndup (salt, pw->saltlen);
if (!pw->salt)
goto fail;
return pw;
fail:
grub_free (pw);
return NULL;
}
static grub_err_t
grub_cmd_legacy_password (struct grub_command *mycmd __attribute__ ((unused)),
int argc, char **args)
{
struct legacy_md5_password *pw = NULL;
if (argc == 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
if (args[0][0] != '-' || args[0][1] != '-')
return grub_normal_set_password ("legacy", args[0]);
pw = parse_legacy_md5 (argc, args);
if (pw)
return grub_auth_register_authentication ("legacy", check_password_md5, pw);
else
/* This is to imitate minor difference between grub-legacy in GRUB2.
If 2 password commands are executed in a row and second one fails
on GRUB2 the password of first one is used, whereas in grub-legacy
authenthication is denied. In case of no password command was executed
early both versions deny any access. */
return grub_auth_register_authentication ("legacy", check_password_deny,
NULL);
}
int
grub_legacy_check_md5_password (int argc, char **args,
char *entered)
{
struct legacy_md5_password *pw = NULL;
int ret;
if (args[0][0] != '-' || args[0][1] != '-')
{
char correct[GRUB_AUTH_MAX_PASSLEN];
grub_memset (correct, 0, sizeof (correct));
grub_strncpy (correct, args[0], sizeof (correct));
return grub_crypto_memcmp (entered, correct, GRUB_AUTH_MAX_PASSLEN) == 0;
}
pw = parse_legacy_md5 (argc, args);
if (!pw)
return 0;
ret = check_password_md5_real (entered, pw);
grub_free (pw);
return ret;
}
static grub_err_t
grub_cmd_legacy_check_password (struct grub_command *mycmd __attribute__ ((unused)),
int argc, char **args)
{
char entered[GRUB_AUTH_MAX_PASSLEN];
if (argc == 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
grub_puts_ (N_("Enter password: "));
if (!grub_password_get (entered, GRUB_AUTH_MAX_PASSLEN))
return GRUB_ACCESS_DENIED;
if (!grub_legacy_check_md5_password (argc, args,
entered))
return GRUB_ACCESS_DENIED;
return GRUB_ERR_NONE;
}
static grub_command_t cmd_source, cmd_configfile;
static grub_command_t cmd_source_extract, cmd_configfile_extract;
static grub_command_t cmd_kernel, cmd_initrd, cmd_initrdnounzip;
static grub_command_t cmd_password, cmd_check_password;
GRUB_MOD_INIT(legacycfg)
{
cmd_source
= grub_register_command ("legacy_source",
grub_cmd_legacy_source,
N_("FILE"),
/* TRANSLATORS: "legacy config" means
"config as used by grub-legacy". */
N_("Parse legacy config in same context"));
cmd_configfile
= grub_register_command ("legacy_configfile",
grub_cmd_legacy_source,
N_("FILE"),
N_("Parse legacy config in new context"));
cmd_source_extract
= grub_register_command ("extract_legacy_entries_source",
grub_cmd_legacy_source,
N_("FILE"),
N_("Parse legacy config in same context taking only menu entries"));
cmd_configfile_extract
= grub_register_command ("extract_legacy_entries_configfile",
grub_cmd_legacy_source,
N_("FILE"),
N_("Parse legacy config in new context taking only menu entries"));
cmd_kernel = grub_register_command ("legacy_kernel",
grub_cmd_legacy_kernel,
N_("[--no-mem-option] [--type=TYPE] FILE [ARG ...]"),
N_("Simulate grub-legacy `kernel' command"));
cmd_initrd = grub_register_command ("legacy_initrd",
grub_cmd_legacy_initrd,
N_("FILE [ARG ...]"),
N_("Simulate grub-legacy `initrd' command"));
cmd_initrdnounzip = grub_register_command ("legacy_initrd_nounzip",
grub_cmd_legacy_initrdnounzip,
N_("FILE [ARG ...]"),
N_("Simulate grub-legacy `modulenounzip' command"));
cmd_password = grub_register_command ("legacy_password",
grub_cmd_legacy_password,
N_("[--md5] PASSWD [FILE]"),
N_("Simulate grub-legacy `password' command"));
cmd_check_password = grub_register_command ("legacy_check_password",
grub_cmd_legacy_check_password,
N_("[--md5] PASSWD [FILE]"),
N_("Simulate grub-legacy `password' command in menu entry mode"));
}
GRUB_MOD_FINI(legacycfg)
{
grub_unregister_command (cmd_source);
grub_unregister_command (cmd_configfile);
grub_unregister_command (cmd_source_extract);
grub_unregister_command (cmd_configfile_extract);
grub_unregister_command (cmd_kernel);
grub_unregister_command (cmd_initrd);
grub_unregister_command (cmd_initrdnounzip);
grub_unregister_command (cmd_password);
grub_unregister_command (cmd_check_password);
}
@@ -0,0 +1,397 @@
/* loadenv.c - command to load/save environment variable. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 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/dl.h>
#include <grub/mm.h>
#include <grub/file.h>
#include <grub/disk.h>
#include <grub/misc.h>
#include <grub/env.h>
#include <grub/partition.h>
#include <grub/lib/envblk.h>
#include <grub/extcmd.h>
#include <grub/i18n.h>
#include "loadenv.h"
GRUB_MOD_LICENSE ("GPLv3+");
static const struct grub_arg_option options[] =
{
/* TRANSLATORS: This option is used to override default filename
for loading and storing environment. */
{"file", 'f', 0, N_("Specify filename."), 0, ARG_TYPE_PATHNAME},
{"skip-sig", 's', 0,
N_("Skip signature-checking of the environment file."), 0, ARG_TYPE_NONE},
{0, 0, 0, 0, 0, 0}
};
/* Opens 'filename' with compression filters disabled. Optionally disables the
PUBKEY filter (that insists upon properly signed files) as well. PUBKEY
filter is restored before the function returns. */
static grub_file_t
open_envblk_file (char *filename,
enum grub_file_type type)
{
grub_file_t file;
char *buf = 0;
if (! filename)
{
const char *prefix;
int len;
prefix = grub_env_get ("prefix");
if (! prefix)
{
grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "prefix");
return 0;
}
len = grub_strlen (prefix);
buf = grub_malloc (len + 1 + sizeof (GRUB_ENVBLK_DEFCFG));
if (! buf)
return 0;
filename = buf;
grub_strcpy (filename, prefix);
filename[len] = '/';
grub_strcpy (filename + len + 1, GRUB_ENVBLK_DEFCFG);
}
file = grub_file_open (filename, type);
grub_free (buf);
return file;
}
static grub_err_t
grub_cmd_load_env (grub_extcmd_context_t ctxt, int argc, char **args)
{
struct grub_arg_list *state = ctxt->state;
grub_file_t file;
grub_envblk_t envblk;
grub_env_whitelist_t whitelist;
whitelist.len = argc;
whitelist.list = args;
/* state[0] is the -f flag; state[1] is the --skip-sig flag */
file = open_envblk_file ((state[0].set) ? state[0].arg : 0,
GRUB_FILE_TYPE_LOADENV
| (state[1].set
? GRUB_FILE_TYPE_SKIP_SIGNATURE : GRUB_FILE_TYPE_NONE));
if (! file)
return grub_errno;
envblk = read_envblk_file (file);
if (! envblk)
goto fail;
/* argc > 0 indicates caller provided a whitelist of variables to read. */
grub_envblk_iterate (envblk, argc > 0 ? &whitelist : 0, set_var);
grub_envblk_close (envblk);
fail:
grub_file_close (file);
return grub_errno;
}
/* Print all variables in current context. */
static int
print_var (const char *name, const char *value,
void *hook_data __attribute__ ((unused)))
{
grub_printf ("%s=%s\n", name, value);
return 0;
}
static grub_err_t
grub_cmd_list_env (grub_extcmd_context_t ctxt,
int argc __attribute__ ((unused)),
char **args __attribute__ ((unused)))
{
struct grub_arg_list *state = ctxt->state;
grub_file_t file;
grub_envblk_t envblk;
file = open_envblk_file ((state[0].set) ? state[0].arg : 0,
GRUB_FILE_TYPE_LOADENV
| (state[1].set
? GRUB_FILE_TYPE_SKIP_SIGNATURE : GRUB_FILE_TYPE_NONE));
if (! file)
return grub_errno;
envblk = read_envblk_file (file);
if (! envblk)
goto fail;
grub_envblk_iterate (envblk, NULL, print_var);
grub_envblk_close (envblk);
fail:
grub_file_close (file);
return grub_errno;
}
/* Used to maintain a variable length of blocklists internally. */
struct blocklist
{
grub_disk_addr_t sector;
unsigned offset;
unsigned length;
struct blocklist *next;
};
static void
free_blocklists (struct blocklist *p)
{
struct blocklist *q;
for (; p; p = q)
{
q = p->next;
grub_free (p);
}
}
static grub_err_t
check_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
grub_file_t file)
{
grub_size_t total_length;
grub_size_t index;
grub_disk_t disk;
grub_disk_addr_t part_start;
struct blocklist *p;
char *buf;
/* Sanity checks. */
total_length = 0;
for (p = blocklists; p; p = p->next)
{
struct blocklist *q;
/* Check if any pair of blocks overlap. */
for (q = p->next; q; q = q->next)
{
grub_disk_addr_t s1, s2;
grub_disk_addr_t e1, e2;
s1 = p->sector;
e1 = s1 + ((p->length + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS);
s2 = q->sector;
e2 = s2 + ((q->length + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS);
if (s1 < e2 && s2 < e1)
{
/* This might be actually valid, but it is unbelievable that
any filesystem makes such a silly allocation. */
return grub_error (GRUB_ERR_BAD_FS, "malformed file");
}
}
total_length += p->length;
}
if (total_length != grub_file_size (file))
{
/* Maybe sparse, unallocated sectors. No way in GRUB. */
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "sparse file not allowed");
}
/* One more sanity check. Re-read all sectors by blocklists, and compare
those with the data read via a file. */
disk = file->device->disk;
part_start = grub_partition_get_start (disk->partition);
buf = grub_envblk_buffer (envblk);
char *blockbuf = NULL;
grub_size_t blockbuf_len = 0;
for (p = blocklists, index = 0; p; index += p->length, p = p->next)
{
if (p->length > blockbuf_len)
{
grub_free (blockbuf);
blockbuf_len = 2 * p->length;
blockbuf = grub_malloc (blockbuf_len);
if (!blockbuf)
return grub_errno;
}
if (grub_disk_read (disk, p->sector - part_start,
p->offset, p->length, blockbuf))
return grub_errno;
if (grub_memcmp (buf + index, blockbuf, p->length) != 0)
return grub_error (GRUB_ERR_FILE_READ_ERROR, "invalid blocklist");
}
return GRUB_ERR_NONE;
}
static int
write_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
grub_file_t file)
{
char *buf;
grub_disk_t disk;
grub_disk_addr_t part_start;
struct blocklist *p;
grub_size_t index;
buf = grub_envblk_buffer (envblk);
disk = file->device->disk;
part_start = grub_partition_get_start (disk->partition);
index = 0;
for (p = blocklists; p; index += p->length, p = p->next)
{
if (grub_disk_write (disk, p->sector - part_start,
p->offset, p->length, buf + index))
return 0;
}
return 1;
}
/* Context for grub_cmd_save_env. */
struct grub_cmd_save_env_ctx
{
struct blocklist *head, *tail;
};
/* Store blocklists in a linked list. */
static void
save_env_read_hook (grub_disk_addr_t sector, unsigned offset, unsigned length,
void *data)
{
struct grub_cmd_save_env_ctx *ctx = data;
struct blocklist *block;
block = grub_malloc (sizeof (*block));
if (! block)
return;
block->sector = sector;
block->offset = offset;
block->length = length;
/* Slightly complicated, because the list should be FIFO. */
block->next = 0;
if (ctx->tail)
ctx->tail->next = block;
ctx->tail = block;
if (! ctx->head)
ctx->head = block;
}
static grub_err_t
grub_cmd_save_env (grub_extcmd_context_t ctxt, int argc, char **args)
{
struct grub_arg_list *state = ctxt->state;
grub_file_t file;
grub_envblk_t envblk;
struct grub_cmd_save_env_ctx ctx = {
.head = 0,
.tail = 0
};
if (! argc)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "no variable is specified");
file = open_envblk_file ((state[0].set) ? state[0].arg : 0,
GRUB_FILE_TYPE_SAVEENV
| GRUB_FILE_TYPE_SKIP_SIGNATURE);
if (! file)
return grub_errno;
if (! file->device->disk)
{
grub_file_close (file);
return grub_error (GRUB_ERR_BAD_DEVICE, "disk device required");
}
file->read_hook = save_env_read_hook;
file->read_hook_data = &ctx;
envblk = read_envblk_file (file);
file->read_hook = 0;
if (! envblk)
goto fail;
if (check_blocklists (envblk, ctx.head, file))
goto fail;
while (argc)
{
const char *value;
value = grub_env_get (args[0]);
if (value)
{
if (! grub_envblk_set (envblk, args[0], value))
{
grub_error (GRUB_ERR_BAD_ARGUMENT, "environment block too small");
goto fail;
}
}
else
grub_envblk_delete (envblk, args[0]);
argc--;
args++;
}
write_blocklists (envblk, ctx.head, file);
fail:
if (envblk)
grub_envblk_close (envblk);
free_blocklists (ctx.head);
grub_file_close (file);
return grub_errno;
}
static grub_extcmd_t cmd_load, cmd_list, cmd_save;
GRUB_MOD_INIT(loadenv)
{
cmd_load =
grub_register_extcmd ("load_env", grub_cmd_load_env, 0,
N_("[-f FILE] [-s|--skip-sig] [variable_name_to_whitelist] [...]"),
N_("Load variables from environment block file."),
options);
cmd_list =
grub_register_extcmd ("list_env", grub_cmd_list_env, 0, N_("[-f FILE]"),
N_("List variables from environment block file."),
options);
cmd_save =
grub_register_extcmd ("save_env", grub_cmd_save_env, 0,
N_("[-f FILE] variable_name [...]"),
N_("Save variables to environment block file."),
options);
}
GRUB_MOD_FINI(loadenv)
{
grub_unregister_extcmd (cmd_load);
grub_unregister_extcmd (cmd_list);
grub_unregister_extcmd (cmd_save);
}
@@ -0,0 +1,93 @@
/* loadenv.c - command to load/save environment variable. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 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/>.
*/
static grub_envblk_t UNUSED
read_envblk_file (grub_file_t file)
{
grub_off_t offset = 0;
char *buf;
grub_size_t size = grub_file_size (file);
grub_envblk_t envblk;
buf = grub_malloc (size);
if (! buf)
return 0;
while (size > 0)
{
grub_ssize_t ret;
ret = grub_file_read (file, buf + offset, size);
if (ret <= 0)
{
grub_free (buf);
return 0;
}
size -= ret;
offset += ret;
}
envblk = grub_envblk_open (buf, offset);
if (! envblk)
{
grub_free (buf);
grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block");
return 0;
}
return envblk;
}
struct grub_env_whitelist
{
grub_size_t len;
char **list;
};
typedef struct grub_env_whitelist grub_env_whitelist_t;
static int UNUSED
test_whitelist_membership (const char* name,
const grub_env_whitelist_t* whitelist)
{
grub_size_t i;
for (i = 0; i < whitelist->len; i++)
if (grub_strcmp (name, whitelist->list[i]) == 0)
return 1; /* found it */
return 0; /* not found */
}
/* Helper for grub_cmd_load_env. */
static int UNUSED
set_var (const char *name, const char *value, void *whitelist)
{
if (! whitelist)
{
grub_env_set (name, value);
return 0;
}
if (test_whitelist_membership (name,
(const grub_env_whitelist_t *) whitelist))
grub_env_set (name, value);
return 0;
}
@@ -0,0 +1,349 @@
/* menuentry.c - menuentry command */
/*
* 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/types.h>
#include <grub/misc.h>
#include <grub/err.h>
#include <grub/dl.h>
#include <grub/extcmd.h>
#include <grub/i18n.h>
#include <grub/normal.h>
static const struct grub_arg_option options[] =
{
{"class", 1, GRUB_ARG_OPTION_REPEATABLE,
N_("Menu entry type."), N_("STRING"), ARG_TYPE_STRING},
{"users", 2, 0,
N_("List of users allowed to boot this entry."), N_("USERNAME[,USERNAME]"),
ARG_TYPE_STRING},
{"hotkey", 3, 0,
N_("Keyboard key to quickly boot this entry."), N_("KEYBOARD_KEY"), ARG_TYPE_STRING},
{"source", 4, 0,
N_("Use STRING as menu entry body."), N_("STRING"), ARG_TYPE_STRING},
{"id", 0, 0, N_("Menu entry identifier."), N_("STRING"), ARG_TYPE_STRING},
/* TRANSLATORS: menu entry can either be bootable by anyone or only by
handful of users. By default when security is active only superusers can
boot a given menu entry. With --unrestricted (this option)
anyone can boot it. */
{"unrestricted", 0, 0, N_("This entry can be booted by any user."),
0, ARG_TYPE_NONE},
{0, 0, 0, 0, 0, 0}
};
static struct
{
const char *name;
int key;
} hotkey_aliases[] =
{
{"backspace", GRUB_TERM_BACKSPACE},
{"tab", GRUB_TERM_TAB},
{"delete", GRUB_TERM_KEY_DC},
{"insert", GRUB_TERM_KEY_INSERT},
{"f1", GRUB_TERM_KEY_F1},
{"f2", GRUB_TERM_KEY_F2},
{"f3", GRUB_TERM_KEY_F3},
{"f4", GRUB_TERM_KEY_F4},
{"f5", GRUB_TERM_KEY_F5},
{"f6", GRUB_TERM_KEY_F6},
{"f7", GRUB_TERM_KEY_F7},
{"f8", GRUB_TERM_KEY_F8},
{"f9", GRUB_TERM_KEY_F9},
{"f10", GRUB_TERM_KEY_F10},
{"f11", GRUB_TERM_KEY_F11},
{"f12", GRUB_TERM_KEY_F12},
};
/* Add a menu entry to the current menu context (as given by the environment
variable data slot `menu'). As the configuration file is read, the script
parser calls this when a menu entry is to be created. */
grub_err_t
grub_normal_add_menu_entry (int argc, const char **args,
char **classes, const char *id,
const char *users, const char *hotkey,
const char *prefix, const char *sourcecode,
int submenu, int *index, struct bls_entry *bls)
{
int menu_hotkey = 0;
char **menu_args = NULL;
char *menu_users = NULL;
char *menu_title = NULL;
char *menu_sourcecode = NULL;
char *menu_id = NULL;
struct grub_menu_entry_class *menu_classes = NULL;
grub_menu_t menu;
grub_menu_entry_t *last;
menu = grub_env_get_menu ();
if (! menu)
return grub_error (GRUB_ERR_MENU, "no menu context");
last = &menu->entry_list;
menu_sourcecode = grub_xasprintf ("%s%s", prefix ?: "", sourcecode);
if (! menu_sourcecode)
return grub_errno;
if (classes && classes[0])
{
int i;
for (i = 0; classes[i]; i++); /* count # of menuentry classes */
menu_classes = grub_zalloc (sizeof (struct grub_menu_entry_class)
* (i + 1));
if (! menu_classes)
goto fail;
for (i = 0; classes[i]; i++)
{
menu_classes[i].name = grub_strdup (classes[i]);
if (! menu_classes[i].name)
goto fail;
menu_classes[i].next = classes[i + 1] ? &menu_classes[i + 1] : NULL;
}
}
if (users)
{
menu_users = grub_strdup (users);
if (! menu_users)
goto fail;
}
if (hotkey)
{
unsigned i;
for (i = 0; i < ARRAY_SIZE (hotkey_aliases); i++)
if (grub_strcmp (hotkey, hotkey_aliases[i].name) == 0)
{
menu_hotkey = hotkey_aliases[i].key;
break;
}
if (i == ARRAY_SIZE (hotkey_aliases))
menu_hotkey = hotkey[0];
}
if (! argc)
{
grub_error (GRUB_ERR_MENU, "menuentry is missing title");
goto fail;
}
menu_title = grub_strdup (args[0]);
if (! menu_title)
goto fail;
grub_dprintf ("menu", "id:\"%s\"\n", id);
grub_dprintf ("menu", "title:\"%s\"\n", menu_title);
menu_id = grub_strdup (id ? : menu_title);
if (! menu_id)
goto fail;
grub_dprintf ("menu", "menu_id:\"%s\"\n", menu_id);
/* Save argc, args to pass as parameters to block arg later. */
menu_args = grub_malloc (sizeof (char*) * (argc + 1));
if (! menu_args)
goto fail;
{
int i;
for (i = 0; i < argc; i++)
{
menu_args[i] = grub_strdup (args[i]);
if (! menu_args[i])
goto fail;
}
menu_args[argc] = NULL;
}
/* Add the menu entry at the end of the list. */
int ind=0;
while (*last)
{
ind++;
last = &(*last)->next;
}
*last = grub_zalloc (sizeof (**last));
if (! *last)
goto fail;
(*last)->title = menu_title;
(*last)->id = menu_id;
(*last)->hotkey = menu_hotkey;
(*last)->classes = menu_classes;
if (menu_users)
(*last)->restricted = 1;
(*last)->users = menu_users;
(*last)->argc = argc;
(*last)->args = menu_args;
(*last)->sourcecode = menu_sourcecode;
(*last)->submenu = submenu;
(*last)->bls = bls;
menu->size++;
if (index)
*index = ind;
return GRUB_ERR_NONE;
fail:
grub_free (menu_sourcecode);
{
int i;
for (i = 0; menu_classes && menu_classes[i].name; i++)
grub_free (menu_classes[i].name);
grub_free (menu_classes);
}
{
int i;
for (i = 0; menu_args && menu_args[i]; i++)
grub_free (menu_args[i]);
grub_free (menu_args);
}
grub_free (menu_users);
grub_free (menu_title);
grub_free (menu_id);
return grub_errno;
}
static char *
setparams_prefix (int argc, char **args)
{
int i;
int j;
char *p;
char *result;
grub_size_t len = 10;
/* Count resulting string length */
for (i = 0; i < argc; i++)
{
len += 3; /* 3 = 1 space + 2 quotes */
p = args[i];
while (*p)
len += (*p++ == '\'' ? 3 : 1);
}
result = grub_malloc (len + 2);
if (! result)
return 0;
grub_strcpy (result, "setparams");
p = result + 9;
for (j = 0; j < argc; j++)
{
*p++ = ' ';
*p++ = '\'';
p = grub_strchrsub (p, args[j], '\'', "'\\''");
*p++ = '\'';
}
*p++ = '\n';
*p = '\0';
return result;
}
static grub_err_t
grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
{
char ch;
char *src;
char *prefix;
unsigned len;
grub_err_t r;
const char *users;
if (! argc)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing arguments");
if (ctxt->state[3].set && ctxt->script)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "multiple menuentry definitions");
if (! ctxt->state[3].set && ! ctxt->script)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "no menuentry definition");
if (ctxt->state[1].set)
users = ctxt->state[1].arg;
else if (ctxt->state[5].set)
users = NULL;
else
users = "";
if (! ctxt->script)
return grub_normal_add_menu_entry (argc, (const char **) args,
(ctxt->state[0].set ? ctxt->state[0].args
: NULL),
ctxt->state[4].arg,
users,
ctxt->state[2].arg, 0,
ctxt->state[3].arg,
ctxt->extcmd->cmd->name[0] == 's',
NULL, NULL);
src = args[argc - 1];
args[argc - 1] = NULL;
len = grub_strlen(src);
ch = src[len - 1];
src[len - 1] = '\0';
prefix = setparams_prefix (argc - 1, args);
if (! prefix)
return grub_errno;
r = grub_normal_add_menu_entry (argc - 1, (const char **) args,
ctxt->state[0].args, ctxt->state[4].arg,
users,
ctxt->state[2].arg, prefix, src + 1,
ctxt->extcmd->cmd->name[0] == 's', NULL,
NULL);
src[len - 1] = ch;
args[argc - 1] = src;
grub_free (prefix);
return r;
}
static grub_extcmd_t cmd, cmd_sub;
void
grub_menu_init (void)
{
cmd = grub_register_extcmd ("menuentry", grub_cmd_menuentry,
GRUB_COMMAND_FLAG_BLOCKS
| GRUB_COMMAND_ACCEPT_DASH
| GRUB_COMMAND_FLAG_EXTRACTOR,
N_("BLOCK"), N_("Define a menu entry."), options);
cmd_sub = grub_register_extcmd ("submenu", grub_cmd_menuentry,
GRUB_COMMAND_FLAG_BLOCKS
| GRUB_COMMAND_ACCEPT_DASH
| GRUB_COMMAND_FLAG_EXTRACTOR,
N_("BLOCK"), N_("Define a submenu."),
options);
}
void
grub_menu_fini (void)
{
grub_unregister_extcmd (cmd);
grub_unregister_extcmd (cmd_sub);
}
@@ -0,0 +1,353 @@
/* search.c - search devices based on a file or a filesystem label */
/*
* 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/types.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/err.h>
#include <grub/dl.h>
#include <grub/device.h>
#include <grub/file.h>
#include <grub/env.h>
#include <grub/command.h>
#include <grub/search.h>
#include <grub/i18n.h>
#include <grub/disk.h>
#include <grub/partition.h>
GRUB_MOD_LICENSE ("GPLv3+");
static int g_no_vtoyefi_part = 0;
static char g_vtoyefi_dosname[64];
static char g_vtoyefi_gptname[64];
struct cache_entry
{
struct cache_entry *next;
char *key;
char *value;
};
static struct cache_entry *cache;
/* Context for FUNC_NAME. */
struct search_ctx
{
const char *key;
const char *var;
int no_floppy;
char **hints;
unsigned nhints;
int count;
int is_cache;
};
/* Helper for FUNC_NAME. */
static int
iterate_device (const char *name, void *data)
{
struct search_ctx *ctx = data;
int found = 0;
/* Skip floppy drives when requested. */
if (ctx->no_floppy &&
name[0] == 'f' && name[1] == 'd' && name[2] >= '0' && name[2] <= '9')
return 0;
if (g_no_vtoyefi_part && (grub_strcmp(name, g_vtoyefi_dosname) == 0 || grub_strcmp(name, g_vtoyefi_gptname) == 0)) {
return 0;
}
#ifdef DO_SEARCH_FS_UUID
#define compare_fn grub_strcasecmp
#else
#define compare_fn grub_strcmp
#endif
#ifdef DO_SEARCH_FILE
{
char *buf;
grub_file_t file;
buf = grub_xasprintf ("(%s)%s", name, ctx->key);
if (! buf)
return 1;
file = grub_file_open (buf, GRUB_FILE_TYPE_FS_SEARCH
| GRUB_FILE_TYPE_NO_DECOMPRESS);
if (file)
{
found = 1;
grub_file_close (file);
}
grub_free (buf);
}
#else
{
/* SEARCH_FS_UUID or SEARCH_LABEL */
grub_device_t dev;
grub_fs_t fs;
char *quid;
dev = grub_device_open (name);
if (dev)
{
fs = grub_fs_probe (dev);
#ifdef DO_SEARCH_FS_UUID
#define read_fn fs_uuid
#else
#define read_fn fs_label
#endif
if (fs && fs->read_fn)
{
fs->read_fn (dev, &quid);
if (grub_errno == GRUB_ERR_NONE && quid)
{
if (compare_fn (quid, ctx->key) == 0)
found = 1;
grub_free (quid);
}
}
grub_device_close (dev);
}
}
#endif
if (!ctx->is_cache && found && ctx->count == 0)
{
struct cache_entry *cache_ent;
cache_ent = grub_malloc (sizeof (*cache_ent));
if (cache_ent)
{
cache_ent->key = grub_strdup (ctx->key);
cache_ent->value = grub_strdup (name);
if (cache_ent->value && cache_ent->key)
{
cache_ent->next = cache;
cache = cache_ent;
}
else
{
grub_free (cache_ent->value);
grub_free (cache_ent->key);
grub_free (cache_ent);
grub_errno = GRUB_ERR_NONE;
}
}
else
grub_errno = GRUB_ERR_NONE;
}
if (found)
{
ctx->count++;
if (ctx->var)
grub_env_set (ctx->var, name);
else
grub_printf (" %s", name);
}
grub_errno = GRUB_ERR_NONE;
return (found && ctx->var);
}
/* Helper for FUNC_NAME. */
static int
part_hook (grub_disk_t disk, const grub_partition_t partition, void *data)
{
struct search_ctx *ctx = data;
char *partition_name, *devname;
int ret;
partition_name = grub_partition_get_name (partition);
if (! partition_name)
return 1;
devname = grub_xasprintf ("%s,%s", disk->name, partition_name);
grub_free (partition_name);
if (!devname)
return 1;
ret = iterate_device (devname, ctx);
grub_free (devname);
return ret;
}
/* Helper for FUNC_NAME. */
static void
try (struct search_ctx *ctx)
{
unsigned i;
struct cache_entry **prev;
struct cache_entry *cache_ent;
for (prev = &cache, cache_ent = *prev; cache_ent;
prev = &cache_ent->next, cache_ent = *prev)
if (compare_fn (cache_ent->key, ctx->key) == 0)
break;
if (cache_ent)
{
ctx->is_cache = 1;
if (iterate_device (cache_ent->value, ctx))
{
ctx->is_cache = 0;
return;
}
ctx->is_cache = 0;
/* Cache entry was outdated. Remove it. */
if (!ctx->count)
{
*prev = cache_ent->next;
grub_free (cache_ent->key);
grub_free (cache_ent->value);
grub_free (cache_ent);
}
}
for (i = 0; i < ctx->nhints; i++)
{
char *end;
if (!ctx->hints[i][0])
continue;
end = ctx->hints[i] + grub_strlen (ctx->hints[i]) - 1;
if (*end == ',')
*end = 0;
if (iterate_device (ctx->hints[i], ctx))
{
if (!*end)
*end = ',';
return;
}
if (!*end)
{
grub_device_t dev;
int ret;
dev = grub_device_open (ctx->hints[i]);
if (!dev)
{
if (!*end)
*end = ',';
continue;
}
if (!dev->disk)
{
grub_device_close (dev);
if (!*end)
*end = ',';
continue;
}
ret = grub_partition_iterate (dev->disk, part_hook, ctx);
if (!*end)
*end = ',';
grub_device_close (dev);
if (ret)
return;
}
}
grub_device_iterate (iterate_device, ctx);
}
void
FUNC_NAME (const char *key, const char *var, int no_floppy,
char **hints, unsigned nhints)
{
struct search_ctx ctx = {
.key = key,
.var = var,
.no_floppy = no_floppy,
.hints = hints,
.nhints = nhints,
.count = 0,
.is_cache = 0
};
grub_fs_autoload_hook_t saved_autoload;
g_no_vtoyefi_part = 0;
if (grub_env_get("VTOY_SEARCH_NO_VTOYEFI"))
{
grub_snprintf(g_vtoyefi_dosname, sizeof(g_vtoyefi_dosname), "%s,msdos2", grub_env_get("vtoydev"));
grub_snprintf(g_vtoyefi_gptname, sizeof(g_vtoyefi_gptname), "%s,gpt2", grub_env_get("vtoydev"));
g_no_vtoyefi_part = 1;
}
/* First try without autoloading if we're setting variable. */
if (var)
{
saved_autoload = grub_fs_autoload_hook;
grub_fs_autoload_hook = 0;
try (&ctx);
/* Restore autoload hook. */
grub_fs_autoload_hook = saved_autoload;
/* Retry with autoload if nothing found. */
if (grub_errno == GRUB_ERR_NONE && ctx.count == 0)
try (&ctx);
}
else
try (&ctx);
if (grub_errno == GRUB_ERR_NONE && ctx.count == 0)
grub_error (GRUB_ERR_FILE_NOT_FOUND, "no such device: %s", key);
}
static grub_err_t
grub_cmd_do_search (grub_command_t cmd __attribute__ ((unused)), int argc,
char **args)
{
if (argc == 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
FUNC_NAME (args[0], argc == 1 ? 0 : args[1], 0, (args + 2),
argc > 2 ? argc - 2 : 0);
return grub_errno;
}
static grub_command_t cmd;
#ifdef DO_SEARCH_FILE
GRUB_MOD_INIT(search_fs_file)
#elif defined (DO_SEARCH_FS_UUID)
GRUB_MOD_INIT(search_fs_uuid)
#else
GRUB_MOD_INIT(search_label)
#endif
{
cmd =
grub_register_command (COMMAND_NAME, grub_cmd_do_search,
N_("NAME [VARIABLE] [HINTS]"),
HELP_MESSAGE);
}
#ifdef DO_SEARCH_FILE
GRUB_MOD_FINI(search_fs_file)
#elif defined (DO_SEARCH_FS_UUID)
GRUB_MOD_FINI(search_fs_uuid)
#else
GRUB_MOD_FINI(search_label)
#endif
{
grub_unregister_command (cmd);
}
@@ -0,0 +1,399 @@
/* smbios.c - retrieve smbios information. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2019 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/env.h>
#include <grub/extcmd.h>
#include <grub/i18n.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/smbios.h>
GRUB_MOD_LICENSE ("GPLv3+");
/* Abstract useful values found in either the SMBIOS3 or SMBIOS EPS. */
static struct {
grub_addr_t start;
grub_addr_t end;
grub_uint16_t structures;
} table_desc;
static grub_extcmd_t cmd;
/* Locate the SMBIOS entry point structure depending on the hardware. */
struct grub_smbios_eps *
grub_smbios_get_eps (void)
{
static struct grub_smbios_eps *eps = NULL;
if (eps != NULL)
return eps;
eps = grub_machine_smbios_get_eps ();
return eps;
}
/* Locate the SMBIOS3 entry point structure depending on the hardware. */
static struct grub_smbios_eps3 *
grub_smbios_get_eps3 (void)
{
static struct grub_smbios_eps3 *eps = NULL;
if (eps != NULL)
return eps;
eps = grub_machine_smbios_get_eps3 ();
return eps;
}
static char *
linux_string (const char *value)
{
char *out = grub_malloc( grub_strlen (value) + 1);
const char *src = value;
char *dst = out;
for (; *src; src++)
if (*src > ' ' && *src < 127 && *src != ':')
*dst++ = *src;
*dst = 0;
return out;
}
/*
* These functions convert values from the various SMBIOS structure field types
* into a string formatted to be returned to the user. They expect that the
* structure and offset were already validated. When the requested data is
* successfully retrieved and formatted, the pointer to the string is returned;
* otherwise, NULL is returned on failure. Don't free the result.
*/
static const char *
grub_smbios_format_byte (const grub_uint8_t *structure, grub_uint8_t offset)
{
static char buffer[sizeof ("255")];
grub_snprintf (buffer, sizeof (buffer), "%u", structure[offset]);
return (const char *)buffer;
}
static const char *
grub_smbios_format_word (const grub_uint8_t *structure, grub_uint8_t offset)
{
static char buffer[sizeof ("65535")];
grub_uint16_t value = grub_get_unaligned16 (structure + offset);
grub_snprintf (buffer, sizeof (buffer), "%u", value);
return (const char *)buffer;
}
static const char *
grub_smbios_format_dword (const grub_uint8_t *structure, grub_uint8_t offset)
{
static char buffer[sizeof ("4294967295")];
grub_uint32_t value = grub_get_unaligned32 (structure + offset);
grub_snprintf (buffer, sizeof (buffer), "%" PRIuGRUB_UINT32_T, value);
return (const char *)buffer;
}
static const char *
grub_smbios_format_qword (const grub_uint8_t *structure, grub_uint8_t offset)
{
static char buffer[sizeof ("18446744073709551615")];
grub_uint64_t value = grub_get_unaligned64 (structure + offset);
grub_snprintf (buffer, sizeof (buffer), "%" PRIuGRUB_UINT64_T, value);
return (const char *)buffer;
}
static const char *
grub_smbios_get_string (const grub_uint8_t *structure, grub_uint8_t offset)
{
const grub_uint8_t *ptr = structure + structure[1];
const grub_uint8_t *table_end = (const grub_uint8_t *)table_desc.end;
const grub_uint8_t referenced_string_number = structure[offset];
grub_uint8_t i;
/* A string referenced with zero is interpreted as unset. */
if (referenced_string_number == 0)
return NULL;
/* Search the string set. */
for (i = 1; *ptr != 0 && ptr < table_end; i++)
if (i == referenced_string_number)
{
const char *str = (const char *)ptr;
while (*ptr++ != 0)
if (ptr >= table_end)
return NULL; /* The string isn't terminated. */
return str;
}
else
while (*ptr++ != 0 && ptr < table_end);
/* The string number is greater than the number of strings in the set. */
return NULL;
}
static const char *
grub_smbios_format_uuid (const grub_uint8_t *structure, grub_uint8_t offset)
{
static char buffer[sizeof ("ffffffff-ffff-ffff-ffff-ffffffffffff")];
const grub_uint8_t *f = structure + offset; /* little-endian fields */
const grub_uint8_t *g = f + 8; /* byte-by-byte fields */
grub_snprintf (buffer, sizeof (buffer),
"%02x%02x%02x%02x-%02x%02x-%02x%02x-"
"%02x%02x-%02x%02x%02x%02x%02x%02x",
f[3], f[2], f[1], f[0], f[5], f[4], f[7], f[6],
g[0], g[1], g[2], g[3], g[4], g[5], g[6], g[7]);
return (const char *)buffer;
}
/* List the field formatting functions and the number of bytes they need. */
static const struct {
const char *(*format) (const grub_uint8_t *structure, grub_uint8_t offset);
grub_uint8_t field_length;
} field_extractors[] = {
{grub_smbios_format_byte, 1},
{grub_smbios_format_word, 2},
{grub_smbios_format_dword, 4},
{grub_smbios_format_qword, 8},
{grub_smbios_get_string, 1},
{grub_smbios_format_uuid, 16}
};
/* List command options, with structure field getters ordered as above. */
#define FIRST_GETTER_OPT (3)
#define SETTER_OPT (FIRST_GETTER_OPT + ARRAY_SIZE(field_extractors))
#define LINUX_OPT (FIRST_GETTER_OPT + ARRAY_SIZE(field_extractors) + 1)
static const struct grub_arg_option options[] = {
{"type", 't', 0, N_("Match structures with the given type."),
N_("type"), ARG_TYPE_INT},
{"handle", 'h', 0, N_("Match structures with the given handle."),
N_("handle"), ARG_TYPE_INT},
{"match", 'm', 0, N_("Select a structure when several match."),
N_("match"), ARG_TYPE_INT},
{"get-byte", 'b', 0, N_("Get the byte's value at the given offset."),
N_("offset"), ARG_TYPE_INT},
{"get-word", 'w', 0, N_("Get two bytes' value at the given offset."),
N_("offset"), ARG_TYPE_INT},
{"get-dword", 'd', 0, N_("Get four bytes' value at the given offset."),
N_("offset"), ARG_TYPE_INT},
{"get-qword", 'q', 0, N_("Get eight bytes' value at the given offset."),
N_("offset"), ARG_TYPE_INT},
{"get-string", 's', 0, N_("Get the string specified at the given offset."),
N_("offset"), ARG_TYPE_INT},
{"get-uuid", 'u', 0, N_("Get the UUID's value at the given offset."),
N_("offset"), ARG_TYPE_INT},
{"set", '\0', 0, N_("Store the value in the given variable name."),
N_("variable"), ARG_TYPE_STRING},
{"linux", '\0', 0, N_("Filter the result like linux does."),
N_("variable"), ARG_TYPE_NONE},
{0, 0, 0, 0, 0, 0}
};
/*
* Return a matching SMBIOS structure.
*
* This method can use up to three criteria for selecting a structure:
* - The "type" field (use -1 to ignore)
* - The "handle" field (use -1 to ignore)
* - Which to return if several match (use 0 to ignore)
*
* The return value is a pointer to the first matching structure. If no
* structures match the given parameters, NULL is returned.
*/
static const grub_uint8_t *
grub_smbios_match_structure (const grub_int16_t type,
const grub_int32_t handle,
const grub_uint16_t match)
{
const grub_uint8_t *ptr = (const grub_uint8_t *)table_desc.start;
const grub_uint8_t *table_end = (const grub_uint8_t *)table_desc.end;
grub_uint16_t structures = table_desc.structures;
grub_uint16_t structure_count = 0;
grub_uint16_t matches = 0;
while (ptr < table_end
&& ptr[1] >= 4 /* Valid structures include the 4-byte header. */
&& (structure_count++ < structures || structures == 0))
{
grub_uint16_t structure_handle = grub_get_unaligned16 (ptr + 2);
grub_uint8_t structure_type = ptr[0];
if ((handle < 0 || handle == structure_handle)
&& (type < 0 || type == structure_type)
&& (match == 0 || match == ++matches))
return ptr;
else
{
ptr += ptr[1];
while ((*ptr++ != 0 || *ptr++ != 0) && ptr < table_end);
}
if (structure_type == GRUB_SMBIOS_TYPE_END_OF_TABLE)
break;
}
return NULL;
}
static grub_err_t
grub_cmd_smbios (grub_extcmd_context_t ctxt,
int argc __attribute__ ((unused)),
char **argv __attribute__ ((unused)))
{
struct grub_arg_list *state = ctxt->state;
grub_int16_t type = -1;
grub_int32_t handle = -1;
grub_uint16_t match = 0;
grub_uint8_t offset = 0;
const grub_uint8_t *structure;
const char *value;
char *modified_value = NULL;
grub_int32_t option;
grub_int8_t field_type = -1;
grub_uint8_t i;
if (table_desc.start == 0)
return grub_error (GRUB_ERR_IO,
N_("the SMBIOS entry point structure was not found"));
/* Read the given filtering options. */
if (state[0].set)
{
option = grub_strtol (state[0].arg, NULL, 0);
if (option < 0 || option > 255)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("the type must be between 0 and 255"));
type = (grub_int16_t)option;
}
if (state[1].set)
{
option = grub_strtol (state[1].arg, NULL, 0);
if (option < 0 || option > 65535)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("the handle must be between 0 and 65535"));
handle = (grub_int32_t)option;
}
if (state[2].set)
{
option = grub_strtol (state[2].arg, NULL, 0);
if (option <= 0 || option > 65535)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("the match must be a positive integer"));
match = (grub_uint16_t)option;
}
/* Determine the data type of the structure field to retrieve. */
for (i = 0; i < ARRAY_SIZE(field_extractors); i++)
if (state[FIRST_GETTER_OPT + i].set)
{
if (field_type >= 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("only one --get option is usable at a time"));
field_type = i;
}
/* Require a choice of a structure field to return. */
if (field_type < 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("one of the --get options is required"));
/* Locate a matching SMBIOS structure. */
structure = grub_smbios_match_structure (type, handle, match);
if (structure == NULL)
return grub_error (GRUB_ERR_IO,
N_("no structure matched the given options"));
/* Ensure the requested byte offset is inside the structure. */
option = grub_strtol (state[FIRST_GETTER_OPT + field_type].arg, NULL, 0);
if (option < 0 || option >= structure[1])
return grub_error (GRUB_ERR_OUT_OF_RANGE,
N_("the given offset is outside the structure"));
/* Ensure the requested data type at the offset is inside the structure. */
offset = (grub_uint8_t)option;
if (offset + field_extractors[field_type].field_length > structure[1])
return grub_error (GRUB_ERR_OUT_OF_RANGE,
N_("the field ends outside the structure"));
/* Format the requested structure field into a readable string. */
value = field_extractors[field_type].format (structure, offset);
if (value == NULL)
return grub_error (GRUB_ERR_IO,
N_("failed to retrieve the structure field"));
if (state[LINUX_OPT].set)
value = modified_value = linux_string (value);
/* Store or print the formatted value. */
if (state[SETTER_OPT].set)
grub_env_set (state[SETTER_OPT].arg, value);
else
grub_printf ("%s\n", value);
grub_free(modified_value);
return GRUB_ERR_NONE;
}
GRUB_MOD_INIT(smbios)
{
struct grub_smbios_eps3 *eps3;
struct grub_smbios_eps *eps;
if ((eps3 = grub_smbios_get_eps3 ()))
{
table_desc.start = (grub_addr_t)eps3->table_address;
table_desc.end = table_desc.start + eps3->maximum_table_length;
table_desc.structures = 0; /* SMBIOS3 drops the structure count. */
}
else if ((eps = grub_smbios_get_eps ()))
{
table_desc.start = (grub_addr_t)eps->intermediate.table_address;
table_desc.end = table_desc.start + eps->intermediate.table_length;
table_desc.structures = eps->intermediate.structures;
}
cmd = grub_register_extcmd ("smbios", grub_cmd_smbios, 0,
N_("[-t type] [-h handle] [-m match] "
"(-b|-w|-d|-q|-s|-u) offset "
"[--set variable]"),
N_("Retrieve SMBIOS information."), options);
}
GRUB_MOD_FINI(smbios)
{
grub_unregister_extcmd (cmd);
}
@@ -0,0 +1,533 @@
/* test.c -- The test command.. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,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/dl.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/env.h>
#include <grub/fs.h>
#include <grub/device.h>
#include <grub/file.h>
#include <grub/command.h>
#include <grub/i18n.h>
GRUB_MOD_LICENSE ("GPLv3+");
static int g_test_case_insensitive = 0;
/* A simple implementation for signed numbers. */
static int
grub_strtosl (char *arg, char **end, int base)
{
if (arg[0] == '-')
return -grub_strtoul (arg + 1, end, base);
return grub_strtoul (arg, end, base);
}
/* Context for test_parse. */
struct test_parse_ctx
{
int invert;
int or, and;
int file_exists;
struct grub_dirhook_info file_info;
char *filename;
};
/* Take care of discarding and inverting. */
static void
update_val (int val, struct test_parse_ctx *ctx)
{
ctx->and = ctx->and && (ctx->invert ? ! val : val);
ctx->invert = 0;
}
/* A hook for iterating directories. */
static int
find_file (const char *cur_filename, const struct grub_dirhook_info *info,
void *data)
{
int case_insensitive = 0;
struct test_parse_ctx *ctx = data;
if (g_test_case_insensitive || info->case_insensitive)
case_insensitive = 1;
if ((case_insensitive ? grub_strcasecmp (cur_filename, ctx->filename)
: grub_strcmp (cur_filename, ctx->filename)) == 0)
{
ctx->file_info = *info;
ctx->file_exists = 1;
return 1;
}
return 0;
}
/* Check if file exists and fetch its information. */
static void
get_fileinfo (char *path, struct test_parse_ctx *ctx)
{
char *pathname;
char *device_name;
grub_fs_t fs;
grub_device_t dev;
ctx->file_exists = 0;
device_name = grub_file_get_device_name (path);
dev = grub_device_open (device_name);
if (! dev)
{
grub_free (device_name);
return;
}
fs = grub_fs_probe (dev);
if (! fs)
{
grub_free (device_name);
grub_device_close (dev);
return;
}
pathname = grub_strchr (path, ')');
if (! pathname)
pathname = path;
else
pathname++;
/* Remove trailing '/'. */
while (*pathname && pathname[grub_strlen (pathname) - 1] == '/')
pathname[grub_strlen (pathname) - 1] = 0;
/* Split into path and filename. */
ctx->filename = grub_strrchr (pathname, '/');
if (! ctx->filename)
{
path = grub_strdup ("/");
ctx->filename = pathname;
}
else
{
ctx->filename++;
path = grub_strdup (pathname);
path[ctx->filename - pathname] = 0;
}
/* It's the whole device. */
if (! *pathname)
{
ctx->file_exists = 1;
grub_memset (&ctx->file_info, 0, sizeof (ctx->file_info));
/* Root is always a directory. */
ctx->file_info.dir = 1;
/* Fetch writing time. */
ctx->file_info.mtimeset = 0;
if (fs->fs_mtime)
{
if (! fs->fs_mtime (dev, &ctx->file_info.mtime))
ctx->file_info.mtimeset = 1;
grub_errno = GRUB_ERR_NONE;
}
}
else
(fs->fs_dir) (dev, path, find_file, ctx);
grub_device_close (dev);
grub_free (path);
grub_free (device_name);
}
/* Parse a test expression starting from *argn. */
static int
test_parse (char **args, int *argn, int argc)
{
struct test_parse_ctx ctx = {
.and = 1,
.or = 0,
.invert = 0
};
/* Here we have the real parsing. */
while (*argn < argc)
{
/* First try 3 argument tests. */
if (*argn + 2 < argc)
{
/* String tests. */
if (grub_strcmp (args[*argn + 1], "=") == 0
|| grub_strcmp (args[*argn + 1], "==") == 0)
{
update_val (grub_strcmp (args[*argn], args[*argn + 2]) == 0,
&ctx);
(*argn) += 3;
continue;
}
if (grub_strcmp (args[*argn + 1], "!=") == 0)
{
update_val (grub_strcmp (args[*argn], args[*argn + 2]) != 0,
&ctx);
(*argn) += 3;
continue;
}
/* GRUB extension: lexicographical sorting. */
if (grub_strcmp (args[*argn + 1], "<") == 0)
{
update_val (grub_strcmp (args[*argn], args[*argn + 2]) < 0,
&ctx);
(*argn) += 3;
continue;
}
if (grub_strcmp (args[*argn + 1], "<=") == 0)
{
update_val (grub_strcmp (args[*argn], args[*argn + 2]) <= 0,
&ctx);
(*argn) += 3;
continue;
}
if (grub_strcmp (args[*argn + 1], ">") == 0)
{
update_val (grub_strcmp (args[*argn], args[*argn + 2]) > 0,
&ctx);
(*argn) += 3;
continue;
}
if (grub_strcmp (args[*argn + 1], ">=") == 0)
{
update_val (grub_strcmp (args[*argn], args[*argn + 2]) >= 0,
&ctx);
(*argn) += 3;
continue;
}
/* Number tests. */
if (grub_strcmp (args[*argn + 1], "-eq") == 0)
{
update_val (grub_strtosl (args[*argn], 0, 0)
== grub_strtosl (args[*argn + 2], 0, 0), &ctx);
(*argn) += 3;
continue;
}
if (grub_strcmp (args[*argn + 1], "-EQ") == 0)
{
update_val (grub_strtoull (args[*argn], 0, 0)
== grub_strtoull (args[*argn + 2], 0, 0), &ctx);
(*argn) += 3;
continue;
}
if (grub_strcmp (args[*argn + 1], "-ge") == 0)
{
update_val (grub_strtosl (args[*argn], 0, 0)
>= grub_strtosl (args[*argn + 2], 0, 0), &ctx);
(*argn) += 3;
continue;
}
if (grub_strcmp (args[*argn + 1], "-GE") == 0)
{
update_val (grub_strtoull (args[*argn], 0, 0)
>= grub_strtoull (args[*argn + 2], 0, 0), &ctx);
(*argn) += 3;
continue;
}
if (grub_strcmp (args[*argn + 1], "-gt") == 0)
{
update_val (grub_strtosl (args[*argn], 0, 0)
> grub_strtosl (args[*argn + 2], 0, 0), &ctx);
(*argn) += 3;
continue;
}
if (grub_strcmp (args[*argn + 1], "-GT") == 0)
{
update_val (grub_strtoull (args[*argn], 0, 0)
> grub_strtoull (args[*argn + 2], 0, 0), &ctx);
(*argn) += 3;
continue;
}
if (grub_strcmp (args[*argn + 1], "-le") == 0)
{
update_val (grub_strtosl (args[*argn], 0, 0)
<= grub_strtosl (args[*argn + 2], 0, 0), &ctx);
(*argn) += 3;
continue;
}
if (grub_strcmp (args[*argn + 1], "-LE") == 0)
{
update_val (grub_strtoull (args[*argn], 0, 0)
<= grub_strtoull (args[*argn + 2], 0, 0), &ctx);
(*argn) += 3;
continue;
}
if (grub_strcmp (args[*argn + 1], "-lt") == 0)
{
update_val (grub_strtosl (args[*argn], 0, 0)
< grub_strtosl (args[*argn + 2], 0, 0), &ctx);
(*argn) += 3;
continue;
}
if (grub_strcmp (args[*argn + 1], "-LT") == 0)
{
update_val (grub_strtoull (args[*argn], 0, 0)
< grub_strtoull (args[*argn + 2], 0, 0), &ctx);
(*argn) += 3;
continue;
}
if (grub_strcmp (args[*argn + 1], "-ne") == 0)
{
update_val (grub_strtosl (args[*argn], 0, 0)
!= grub_strtosl (args[*argn + 2], 0, 0), &ctx);
(*argn) += 3;
continue;
}
if (grub_strcmp (args[*argn + 1], "-NE") == 0)
{
update_val (grub_strtoull (args[*argn], 0, 0)
!= grub_strtoull (args[*argn + 2], 0, 0), &ctx);
(*argn) += 3;
continue;
}
/* GRUB extension: compare numbers skipping prefixes.
Useful for comparing versions. E.g. vmlinuz-2 -plt vmlinuz-11. */
if (grub_strcmp (args[*argn + 1], "-pgt") == 0
|| grub_strcmp (args[*argn + 1], "-plt") == 0)
{
int i;
/* Skip common prefix. */
for (i = 0; args[*argn][i] == args[*argn + 2][i]
&& args[*argn][i]; i++);
/* Go the digits back. */
i--;
while (grub_isdigit (args[*argn][i]) && i > 0)
i--;
i++;
if (grub_strcmp (args[*argn + 1], "-pgt") == 0)
update_val (grub_strtoul (args[*argn] + i, 0, 0)
> grub_strtoul (args[*argn + 2] + i, 0, 0), &ctx);
else
update_val (grub_strtoul (args[*argn] + i, 0, 0)
< grub_strtoul (args[*argn + 2] + i, 0, 0), &ctx);
(*argn) += 3;
continue;
}
/* -nt and -ot tests. GRUB extension: when doing -?t<bias> bias
will be added to the first mtime. */
if (grub_memcmp (args[*argn + 1], "-nt", 3) == 0
|| grub_memcmp (args[*argn + 1], "-ot", 3) == 0)
{
struct grub_dirhook_info file1;
int file1exists;
int bias = 0;
/* Fetch fileinfo. */
get_fileinfo (args[*argn], &ctx);
file1 = ctx.file_info;
file1exists = ctx.file_exists;
get_fileinfo (args[*argn + 2], &ctx);
if (args[*argn + 1][3])
bias = grub_strtosl (args[*argn + 1] + 3, 0, 0);
if (grub_memcmp (args[*argn + 1], "-nt", 3) == 0)
update_val ((file1exists && ! ctx.file_exists)
|| (file1.mtimeset && ctx.file_info.mtimeset
&& file1.mtime + bias > ctx.file_info.mtime),
&ctx);
else
update_val ((! file1exists && ctx.file_exists)
|| (file1.mtimeset && ctx.file_info.mtimeset
&& file1.mtime + bias < ctx.file_info.mtime),
&ctx);
(*argn) += 3;
continue;
}
}
/* Two-argument tests. */
if (*argn + 1 < argc)
{
/* File tests. */
if (grub_strcmp (args[*argn], "-d") == 0)
{
get_fileinfo (args[*argn + 1], &ctx);
update_val (ctx.file_exists && ctx.file_info.dir, &ctx);
(*argn) += 2;
continue;
}
if (grub_strcmp (args[*argn], "-D") == 0)
{
g_test_case_insensitive = 1;
get_fileinfo (args[*argn + 1], &ctx);
g_test_case_insensitive = 0;
update_val (ctx.file_exists && ctx.file_info.dir, &ctx);
(*argn) += 2;
continue;
}
if (grub_strcmp (args[*argn], "-e") == 0)
{
get_fileinfo (args[*argn + 1], &ctx);
update_val (ctx.file_exists, &ctx);
(*argn) += 2;
continue;
}
if (grub_strcmp (args[*argn], "-E") == 0)
{
g_test_case_insensitive = 1;
get_fileinfo (args[*argn + 1], &ctx);
g_test_case_insensitive = 0;
update_val (ctx.file_exists, &ctx);
(*argn) += 2;
continue;
}
if (grub_strcmp (args[*argn], "-f") == 0)
{
get_fileinfo (args[*argn + 1], &ctx);
/* FIXME: check for other types. */
update_val (ctx.file_exists && ! ctx.file_info.dir, &ctx);
(*argn) += 2;
continue;
}
if (grub_strcmp (args[*argn], "-F") == 0)
{
g_test_case_insensitive = 1;
get_fileinfo (args[*argn + 1], &ctx);
g_test_case_insensitive = 0;
/* FIXME: check for other types. */
update_val (ctx.file_exists && ! ctx.file_info.dir, &ctx);
(*argn) += 2;
continue;
}
if (grub_strcmp (args[*argn], "-s") == 0)
{
grub_file_t file;
file = grub_file_open (args[*argn + 1], GRUB_FILE_TYPE_GET_SIZE
| GRUB_FILE_TYPE_NO_DECOMPRESS);
update_val (file && (grub_file_size (file) != 0), &ctx);
if (file)
grub_file_close (file);
grub_errno = GRUB_ERR_NONE;
(*argn) += 2;
continue;
}
/* String tests. */
if (grub_strcmp (args[*argn], "-n") == 0)
{
update_val (args[*argn + 1][0], &ctx);
(*argn) += 2;
continue;
}
if (grub_strcmp (args[*argn], "-z") == 0)
{
update_val (! args[*argn + 1][0], &ctx);
(*argn) += 2;
continue;
}
}
/* Special modifiers. */
/* End of expression. return to parent. */
if (grub_strcmp (args[*argn], ")") == 0)
{
(*argn)++;
return ctx.or || ctx.and;
}
/* Recursively invoke if parenthesis. */
if (grub_strcmp (args[*argn], "(") == 0)
{
(*argn)++;
update_val (test_parse (args, argn, argc), &ctx);
continue;
}
if (grub_strcmp (args[*argn], "!") == 0)
{
ctx.invert = ! ctx.invert;
(*argn)++;
continue;
}
if (grub_strcmp (args[*argn], "-a") == 0)
{
(*argn)++;
continue;
}
if (grub_strcmp (args[*argn], "-o") == 0)
{
ctx.or = ctx.or || ctx.and;
ctx.and = 1;
(*argn)++;
continue;
}
/* No test found. Interpret if as just a string. */
update_val (args[*argn][0], &ctx);
(*argn)++;
}
return ctx.or || ctx.and;
}
static grub_err_t
grub_cmd_test (grub_command_t cmd __attribute__ ((unused)),
int argc, char **args)
{
int argn = 0;
if (argc >= 1 && grub_strcmp (args[argc - 1], "]") == 0)
argc--;
return test_parse (args, &argn, argc) ? GRUB_ERR_NONE
: grub_error (GRUB_ERR_TEST_FAILURE, N_("false"));
}
static grub_command_t cmd_1, cmd_2;
GRUB_MOD_INIT(test)
{
cmd_1 = grub_register_command ("[", grub_cmd_test,
N_("EXPRESSION ]"), N_("Evaluate an expression."));
cmd_1->flags |= GRUB_COMMAND_FLAG_EXTRACTOR;
cmd_2 = grub_register_command ("test", grub_cmd_test,
N_("EXPRESSION"), N_("Evaluate an expression."));
cmd_2->flags |= GRUB_COMMAND_FLAG_EXTRACTOR;
}
GRUB_MOD_FINI(test)
{
grub_unregister_command (cmd_1);
grub_unregister_command (cmd_2);
}
@@ -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 ();
}
@@ -0,0 +1,257 @@
/* loopback.c - command to add loopback devices. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,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/disk.h>
#include <grub/mm.h>
#include <grub/extcmd.h>
#include <grub/i18n.h>
GRUB_MOD_LICENSE ("GPLv3+");
struct grub_loopback
{
char *devname;
grub_file_t file;
struct grub_loopback *next;
unsigned long id;
grub_off_t skip;
};
static struct grub_loopback *loopback_list;
static unsigned long last_id = 0;
static const struct grub_arg_option options[] =
{
/* TRANSLATORS: The disk is simply removed from the list of available ones,
not wiped, avoid to scare user. */
{"delete", 'd', 0, N_("Delete the specified loopback drive."), 0, 0},
{"skip", 's', 0, "skip sectors of the file.", "SECTORS", ARG_TYPE_INT },
{0, 0, 0, 0, 0, 0}
};
/* Delete the loopback device NAME. */
static grub_err_t
delete_loopback (const char *name)
{
struct grub_loopback *dev;
struct grub_loopback **prev;
/* Search for the device. */
for (dev = loopback_list, prev = &loopback_list;
dev;
prev = &dev->next, dev = dev->next)
if (grub_strcmp (dev->devname, name) == 0)
break;
if (! dev)
return grub_error (GRUB_ERR_BAD_DEVICE, "device not found");
/* Remove the device from the list. */
*prev = dev->next;
grub_free (dev->devname);
grub_file_close (dev->file);
grub_free (dev);
return 0;
}
/* The command to add and remove loopback devices. */
static grub_err_t
grub_cmd_loopback (grub_extcmd_context_t ctxt, int argc, char **args)
{
struct grub_arg_list *state = ctxt->state;
grub_file_t file;
struct grub_loopback *newdev;
grub_err_t ret;
grub_off_t skip = 0;
if (argc < 1)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required");
/* Check if `-d' was used. */
if (state[0].set)
return delete_loopback (args[0]);
if (state[1].set)
skip = (grub_off_t)grub_strtoull(state[1].arg, NULL, 10);
if (argc < 2)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
file = grub_file_open (args[1], GRUB_FILE_TYPE_LOOPBACK
| GRUB_FILE_TYPE_NO_DECOMPRESS);
if (! file)
return grub_errno;
/* First try to replace the old device. */
for (newdev = loopback_list; newdev; newdev = newdev->next)
if (grub_strcmp (newdev->devname, args[0]) == 0)
break;
if (newdev)
{
grub_file_close (newdev->file);
newdev->file = file;
newdev->skip = skip;
return 0;
}
/* Unable to replace it, make a new entry. */
newdev = grub_malloc (sizeof (struct grub_loopback));
if (! newdev)
goto fail;
newdev->devname = grub_strdup (args[0]);
if (! newdev->devname)
{
grub_free (newdev);
goto fail;
}
newdev->file = file;
newdev->skip = skip;
newdev->id = last_id++;
/* Add the new entry to the list. */
newdev->next = loopback_list;
loopback_list = newdev;
return 0;
fail:
ret = grub_errno;
grub_file_close (file);
return ret;
}
static int
grub_loopback_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data,
grub_disk_pull_t pull)
{
struct grub_loopback *d;
if (pull != GRUB_DISK_PULL_NONE)
return 0;
for (d = loopback_list; d; d = d->next)
{
if (hook (d->devname, hook_data))
return 1;
}
return 0;
}
static grub_err_t
grub_loopback_open (const char *name, grub_disk_t disk)
{
struct grub_loopback *dev;
for (dev = loopback_list; dev; dev = dev->next)
if (grub_strcmp (dev->devname, name) == 0)
break;
if (! dev)
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device");
/* Use the filesize for the disk size, round up to a complete sector. */
if (dev->file->size != GRUB_FILE_SIZE_UNKNOWN)
disk->total_sectors = ((dev->file->size + GRUB_DISK_SECTOR_SIZE - 1)
/ GRUB_DISK_SECTOR_SIZE);
else
disk->total_sectors = GRUB_DISK_SIZE_UNKNOWN;
/* Avoid reading more than 512M. */
disk->max_agglomerate = 1 << (29 - GRUB_DISK_SECTOR_BITS
- GRUB_DISK_CACHE_BITS);
disk->id = dev->id;
disk->data = dev;
return 0;
}
static grub_err_t
grub_loopback_read (grub_disk_t disk, grub_disk_addr_t sector,
grub_size_t size, char *buf)
{
grub_file_t file = ((struct grub_loopback *) disk->data)->file;
grub_off_t skip = ((struct grub_loopback *) disk->data)->skip;
grub_off_t pos;
grub_file_seek (file, (sector + skip) << GRUB_DISK_SECTOR_BITS);
grub_file_read (file, buf, size << GRUB_DISK_SECTOR_BITS);
if (grub_errno)
return grub_errno;
/* In case there is more data read than there is available, in case
of files that are not a multiple of GRUB_DISK_SECTOR_SIZE, fill
the rest with zeros. */
pos = (sector + skip + size) << GRUB_DISK_SECTOR_BITS;
if (pos > file->size)
{
grub_size_t amount = pos - file->size;
grub_memset (buf + (size << GRUB_DISK_SECTOR_BITS) - amount, 0, amount);
}
return 0;
}
static grub_err_t
grub_loopback_write (grub_disk_t disk __attribute ((unused)),
grub_disk_addr_t sector __attribute ((unused)),
grub_size_t size __attribute ((unused)),
const char *buf __attribute ((unused)))
{
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"loopback write is not supported");
}
static struct grub_disk_dev grub_loopback_dev =
{
.name = "loopback",
.id = GRUB_DISK_DEVICE_LOOPBACK_ID,
.disk_iterate = grub_loopback_iterate,
.disk_open = grub_loopback_open,
.disk_read = grub_loopback_read,
.disk_write = grub_loopback_write,
.next = 0
};
static grub_extcmd_t cmd;
GRUB_MOD_INIT(loopback)
{
cmd = grub_register_extcmd ("loopback", grub_cmd_loopback, 0,
N_("[-d] DEVICENAME FILE."),
/* TRANSLATORS: The file itself is not destroyed
or transformed into drive. */
N_("Make a virtual drive from a file."), options);
grub_disk_dev_register (&grub_loopback_dev);
}
GRUB_MOD_FINI(loopback)
{
grub_unregister_extcmd (cmd);
grub_disk_dev_unregister (&grub_loopback_dev);
}
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
@@ -0,0 +1,436 @@
/* fshelp.c -- Filesystem helper functions */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2004,2005,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/>.
*/
#include <grub/err.h>
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/disk.h>
#include <grub/fshelp.h>
#include <grub/dl.h>
#include <grub/i18n.h>
GRUB_MOD_LICENSE ("GPLv3+");
typedef int (*iterate_dir_func) (grub_fshelp_node_t dir,
grub_fshelp_iterate_dir_hook_t hook,
void *data);
typedef grub_err_t (*lookup_file_func) (grub_fshelp_node_t dir,
const char *name,
grub_fshelp_node_t *foundnode,
enum grub_fshelp_filetype *foundtype);
typedef char *(*read_symlink_func) (grub_fshelp_node_t node);
struct stack_element {
struct stack_element *parent;
grub_fshelp_node_t node;
enum grub_fshelp_filetype type;
};
/* Context for grub_fshelp_find_file. */
struct grub_fshelp_find_file_ctx
{
/* Inputs. */
const char *path;
grub_fshelp_node_t rootnode;
/* Global options. */
int symlinknest;
/* Current file being traversed and its parents. */
struct stack_element *currnode;
};
/* Helper for find_file_iter. */
static void
free_node (grub_fshelp_node_t node, struct grub_fshelp_find_file_ctx *ctx)
{
if (node != ctx->rootnode)
grub_free (node);
}
static void
pop_element (struct grub_fshelp_find_file_ctx *ctx)
{
struct stack_element *el;
el = ctx->currnode;
ctx->currnode = el->parent;
free_node (el->node, ctx);
grub_free (el);
}
static void
free_stack (struct grub_fshelp_find_file_ctx *ctx)
{
while (ctx->currnode)
pop_element (ctx);
}
static void
go_up_a_level (struct grub_fshelp_find_file_ctx *ctx)
{
if (!ctx->currnode->parent)
return;
pop_element (ctx);
}
static grub_err_t
push_node (struct grub_fshelp_find_file_ctx *ctx, grub_fshelp_node_t node, enum grub_fshelp_filetype filetype)
{
struct stack_element *nst;
nst = grub_malloc (sizeof (*nst));
if (!nst)
return grub_errno;
nst->node = node;
nst->type = filetype & ~GRUB_FSHELP_CASE_INSENSITIVE;
nst->parent = ctx->currnode;
ctx->currnode = nst;
return GRUB_ERR_NONE;
}
static grub_err_t
go_to_root (struct grub_fshelp_find_file_ctx *ctx)
{
free_stack (ctx);
return push_node (ctx, ctx->rootnode, GRUB_FSHELP_DIR);
}
struct grub_fshelp_find_file_iter_ctx
{
const char *name;
grub_fshelp_node_t *foundnode;
enum grub_fshelp_filetype *foundtype;
};
int g_ventoy_case_insensitive = 0;
/* Helper for grub_fshelp_find_file. */
static int
find_file_iter (const char *filename, enum grub_fshelp_filetype filetype,
grub_fshelp_node_t node, void *data)
{
struct grub_fshelp_find_file_iter_ctx *ctx = data;
if (g_ventoy_case_insensitive)
{
filetype |= GRUB_FSHELP_CASE_INSENSITIVE;
}
if (filetype == GRUB_FSHELP_UNKNOWN ||
((filetype & GRUB_FSHELP_CASE_INSENSITIVE)
? grub_strcasecmp (ctx->name, filename)
: grub_strcmp (ctx->name, filename)))
{
grub_free (node);
return 0;
}
/* The node is found, stop iterating over the nodes. */
*ctx->foundnode = node;
*ctx->foundtype = filetype;
return 1;
}
static grub_err_t
directory_find_file (grub_fshelp_node_t node, const char *name, grub_fshelp_node_t *foundnode,
enum grub_fshelp_filetype *foundtype, iterate_dir_func iterate_dir)
{
int found;
struct grub_fshelp_find_file_iter_ctx ctx = {
.foundnode = foundnode,
.foundtype = foundtype,
.name = name
};
found = iterate_dir (node, find_file_iter, &ctx);
if (! found)
{
if (grub_errno)
return grub_errno;
}
return GRUB_ERR_NONE;
}
static grub_err_t
find_file (char *currpath,
iterate_dir_func iterate_dir, lookup_file_func lookup_file,
read_symlink_func read_symlink,
struct grub_fshelp_find_file_ctx *ctx)
{
char *name, *next;
grub_err_t err;
for (name = currpath; ; name = next)
{
char c;
grub_fshelp_node_t foundnode = NULL;
enum grub_fshelp_filetype foundtype = 0;
/* Remove all leading slashes. */
while (*name == '/')
name++;
/* Found the node! */
if (! *name)
return 0;
/* Extract the actual part from the pathname. */
for (next = name; *next && *next != '/'; next++);
/* At this point it is expected that the current node is a
directory, check if this is true. */
if (ctx->currnode->type != GRUB_FSHELP_DIR)
return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
/* Don't rely on fs providing actual . in the listing. */
if (next - name == 1 && name[0] == '.')
continue;
/* Don't rely on fs providing actual .. in the listing. */
if (next - name == 2 && name[0] == '.' && name[1] == '.')
{
go_up_a_level (ctx);
continue;
}
/* Iterate over the directory. */
c = *next;
*next = '\0';
if (lookup_file)
err = lookup_file (ctx->currnode->node, name, &foundnode, &foundtype);
else
err = directory_find_file (ctx->currnode->node, name, &foundnode, &foundtype, iterate_dir);
*next = c;
if (err)
return err;
if (!foundnode)
break;
push_node (ctx, foundnode, foundtype);
/* Read in the symlink and follow it. */
if (ctx->currnode->type == GRUB_FSHELP_SYMLINK)
{
char *symlink;
/* Test if the symlink does not loop. */
if (++ctx->symlinknest == 8)
return grub_error (GRUB_ERR_SYMLINK_LOOP,
N_("too deep nesting of symlinks"));
symlink = read_symlink (ctx->currnode->node);
if (!symlink)
return grub_errno;
/* The symlink is an absolute path, go back to the root inode. */
if (symlink[0] == '/')
{
err = go_to_root (ctx);
if (err)
return err;
}
else
{
/* Get from symlink to containing directory. */
go_up_a_level (ctx);
}
/* Lookup the node the symlink points to. */
find_file (symlink, iterate_dir, lookup_file, read_symlink, ctx);
grub_free (symlink);
if (grub_errno)
return grub_errno;
}
}
return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"),
ctx->path);
}
static grub_err_t
grub_fshelp_find_file_real (const char *path, grub_fshelp_node_t rootnode,
grub_fshelp_node_t *foundnode,
iterate_dir_func iterate_dir,
lookup_file_func lookup_file,
read_symlink_func read_symlink,
enum grub_fshelp_filetype expecttype)
{
struct grub_fshelp_find_file_ctx ctx = {
.path = path,
.rootnode = rootnode,
.symlinknest = 0,
.currnode = 0
};
grub_err_t err;
enum grub_fshelp_filetype foundtype;
char *duppath;
if (!path || path[0] != '/')
{
return grub_error (GRUB_ERR_BAD_FILENAME, N_("invalid file name `%s'"), path);
}
err = go_to_root (&ctx);
if (err)
return err;
duppath = grub_strdup (path);
if (!duppath)
return grub_errno;
err = find_file (duppath, iterate_dir, lookup_file, read_symlink, &ctx);
grub_free (duppath);
if (err)
{
free_stack (&ctx);
return err;
}
*foundnode = ctx.currnode->node;
foundtype = ctx.currnode->type;
/* Avoid the node being freed. */
ctx.currnode->node = 0;
free_stack (&ctx);
/* Check if the node that was found was of the expected type. */
if (expecttype == GRUB_FSHELP_REG && foundtype != expecttype)
return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file"));
else if (expecttype == GRUB_FSHELP_DIR && foundtype != expecttype)
return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
return 0;
}
/* Lookup the node PATH. The node ROOTNODE describes the root of the
directory tree. The node found is returned in FOUNDNODE, which is
either a ROOTNODE or a new malloc'ed node. ITERATE_DIR is used to
iterate over all directory entries in the current node.
READ_SYMLINK is used to read the symlink if a node is a symlink.
EXPECTTYPE is the type node that is expected by the called, an
error is generated if the node is not of the expected type. */
grub_err_t
grub_fshelp_find_file (const char *path, grub_fshelp_node_t rootnode,
grub_fshelp_node_t *foundnode,
iterate_dir_func iterate_dir,
read_symlink_func read_symlink,
enum grub_fshelp_filetype expecttype)
{
return grub_fshelp_find_file_real (path, rootnode, foundnode,
iterate_dir, NULL,
read_symlink, expecttype);
}
grub_err_t
grub_fshelp_find_file_lookup (const char *path, grub_fshelp_node_t rootnode,
grub_fshelp_node_t *foundnode,
lookup_file_func lookup_file,
read_symlink_func read_symlink,
enum grub_fshelp_filetype expecttype)
{
return grub_fshelp_find_file_real (path, rootnode, foundnode,
NULL, lookup_file,
read_symlink, expecttype);
}
/* Read LEN bytes from the file NODE on disk DISK into the buffer BUF,
beginning with the block POS. READ_HOOK should be set before
reading a block from the file. READ_HOOK_DATA is passed through as
the DATA argument to READ_HOOK. GET_BLOCK is used to translate
file blocks to disk blocks. The file is FILESIZE bytes big and the
blocks have a size of LOG2BLOCKSIZE (in log2). */
grub_ssize_t
grub_fshelp_read_file (grub_disk_t disk, grub_fshelp_node_t node,
grub_disk_read_hook_t read_hook, void *read_hook_data,
grub_off_t pos, grub_size_t len, char *buf,
grub_disk_addr_t (*get_block) (grub_fshelp_node_t node,
grub_disk_addr_t block),
grub_off_t filesize, int log2blocksize,
grub_disk_addr_t blocks_start)
{
grub_disk_addr_t i, blockcnt;
int blocksize = 1 << (log2blocksize + GRUB_DISK_SECTOR_BITS);
if (pos > filesize)
{
grub_error (GRUB_ERR_OUT_OF_RANGE,
N_("attempt to read past the end of file"));
return -1;
}
/* Adjust LEN so it we can't read past the end of the file. */
if (pos + len > filesize)
len = filesize - pos;
blockcnt = ((len + pos) + blocksize - 1) >> (log2blocksize + GRUB_DISK_SECTOR_BITS);
for (i = pos >> (log2blocksize + GRUB_DISK_SECTOR_BITS); i < blockcnt; i++)
{
grub_disk_addr_t blknr;
int blockoff = pos & (blocksize - 1);
int blockend = blocksize;
int skipfirst = 0;
blknr = get_block (node, i);
if (grub_errno)
return -1;
blknr = blknr << log2blocksize;
/* Last block. */
if (i == blockcnt - 1)
{
blockend = (len + pos) & (blocksize - 1);
/* The last portion is exactly blocksize. */
if (! blockend)
blockend = blocksize;
}
/* First block. */
if (i == (pos >> (log2blocksize + GRUB_DISK_SECTOR_BITS)))
{
skipfirst = blockoff;
blockend -= skipfirst;
}
/* If the block number is 0 this block is not stored on disk but
is zero filled instead. */
if (blknr)
{
disk->read_hook = read_hook;
disk->read_hook_data = read_hook_data;
grub_disk_read (disk, blknr + blocks_start, skipfirst,
blockend, buf);
disk->read_hook = 0;
if (grub_errno)
return -1;
}
else if (read_hook != (grub_disk_read_hook_t)(void *)grub_disk_blocklist_read)
grub_memset (buf, 0, blockend);
buf += blocksize - skipfirst;
}
return len;
}
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
@@ -0,0 +1,285 @@
/*
* LZ4 - Fast LZ compression algorithm
* Header File
* Copyright (C) 2011-2013, Yann Collet.
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You can contact the author at :
* - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
* - LZ4 source repository : http://code.google.com/p/lz4/
*/
#include <grub/err.h>
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/types.h>
int LZ4_uncompress_unknownOutputSize(const char *source, char *dest,
int isize, int maxOutputSize);
/*
* CPU Feature Detection
*/
/* 32 or 64 bits ? */
#if (GRUB_CPU_SIZEOF_VOID_P == 8)
#define LZ4_ARCH64 1
#else
#define LZ4_ARCH64 0
#endif
/*
* Compiler Options
*/
#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
#if (GCC_VERSION >= 302) || (defined (__INTEL_COMPILER) && __INTEL_COMPILER >= 800) || defined(__clang__)
#define expect(expr, value) (__builtin_expect((expr), (value)))
#else
#define expect(expr, value) (expr)
#endif
#define likely(expr) expect((expr) != 0, 1)
#define unlikely(expr) expect((expr) != 0, 0)
/* Basic types */
#define BYTE grub_uint8_t
#define U16 grub_uint16_t
#define U32 grub_uint32_t
#define S32 grub_int32_t
#define U64 grub_uint64_t
typedef struct _U16_S {
U16 v;
} GRUB_PACKED U16_S;
typedef struct _U32_S {
U32 v;
} GRUB_PACKED U32_S;
typedef struct _U64_S {
U64 v;
} GRUB_PACKED U64_S;
#define A64(x) (((U64_S *)(x))->v)
#define A32(x) (((U32_S *)(x))->v)
#define A16(x) (((U16_S *)(x))->v)
/*
* Constants
*/
#define MINMATCH 4
#define COPYLENGTH 8
#define LASTLITERALS 5
#define ML_BITS 4
#define ML_MASK ((1U<<ML_BITS)-1)
#define RUN_BITS (8-ML_BITS)
#define RUN_MASK ((1U<<RUN_BITS)-1)
/*
* Architecture-specific macros
*/
#if LZ4_ARCH64
#define STEPSIZE 8
#define UARCH U64
#define AARCH A64
#define LZ4_COPYSTEP(s, d) A64(d) = A64(s); d += 8; s += 8;
#define LZ4_COPYPACKET(s, d) LZ4_COPYSTEP(s, d)
#define LZ4_SECURECOPY(s, d, e) if (d < e) LZ4_WILDCOPY(s, d, e)
#define HTYPE U32
#define INITBASE(base) const BYTE* const base = ip
#else
#define STEPSIZE 4
#define UARCH U32
#define AARCH A32
#define LZ4_COPYSTEP(s, d) A32(d) = A32(s); d += 4; s += 4;
#define LZ4_COPYPACKET(s, d) LZ4_COPYSTEP(s, d); LZ4_COPYSTEP(s, d);
#define LZ4_SECURECOPY LZ4_WILDCOPY
#define HTYPE const BYTE*
#define INITBASE(base) const int base = 0
#endif
#define LZ4_READ_LITTLEENDIAN_16(d, s, p) { d = (s) - grub_le_to_cpu16 (A16 (p)); }
#define LZ4_WRITE_LITTLEENDIAN_16(p, v) { A16(p) = grub_cpu_to_le16 (v); p += 2; }
/* Macros */
#define LZ4_WILDCOPY(s, d, e) do { LZ4_COPYPACKET(s, d) } while (d < e);
/* Decompression functions */
grub_err_t
lz4_decompress(void *s_start, void *d_start, grub_size_t s_len, grub_size_t d_len);
grub_err_t
lz4_decompress(void *s_start, void *d_start, grub_size_t s_len, grub_size_t d_len)
{
const BYTE *src = s_start;
U32 bufsiz = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) |
src[3];
/* invalid compressed buffer size encoded at start */
if (bufsiz + 4 > s_len)
return grub_error(GRUB_ERR_BAD_FS,"lz4 decompression failed.");
/*
* Returns 0 on success (decompression function returned non-negative)
* and appropriate error on failure (decompression function returned negative).
*/
return (LZ4_uncompress_unknownOutputSize((char*)s_start + 4, d_start, bufsiz,
d_len) < 0)?grub_error(GRUB_ERR_BAD_FS,"lz4 decompression failed."):0;
}
int
LZ4_uncompress_unknownOutputSize(const char *source,
char *dest, int isize, int maxOutputSize)
{
/* Local Variables */
const BYTE * ip = (const BYTE *) source;
const BYTE *const iend = ip + isize;
const BYTE * ref;
BYTE * op = (BYTE *) dest;
BYTE *const oend = op + maxOutputSize;
BYTE *cpy;
grub_size_t dec[] = { 0, 3, 2, 3, 0, 0, 0, 0 };
/* Main Loop */
while (ip < iend) {
BYTE token;
int length;
/* get runlength */
token = *ip++;
if ((length = (token >> ML_BITS)) == RUN_MASK) {
int s = 255;
while ((ip < iend) && (s == 255)) {
s = *ip++;
length += s;
}
}
/* copy literals */
if ((grub_addr_t) length > ~(grub_addr_t)op)
goto _output_error;
cpy = op + length;
if ((cpy > oend - COPYLENGTH) ||
(ip + length > iend - COPYLENGTH)) {
if (cpy > oend)
/*
* Error: request to write beyond destination
* buffer.
*/
goto _output_error;
if (ip + length > iend)
/*
* Error : request to read beyond source
* buffer.
*/
goto _output_error;
grub_memcpy(op, ip, length);
op += length;
ip += length;
if (ip < iend)
/* Error : LZ4 format violation */
goto _output_error;
/* Necessarily EOF, due to parsing restrictions. */
break;
}
LZ4_WILDCOPY(ip, op, cpy);
ip -= (op - cpy);
op = cpy;
/* get offset */
LZ4_READ_LITTLEENDIAN_16(ref, cpy, ip);
ip += 2;
if (ref < (BYTE * const) dest)
/*
* Error: offset creates reference outside of
* destination buffer.
*/
goto _output_error;
/* get matchlength */
if ((length = (token & ML_MASK)) == ML_MASK) {
while (ip < iend) {
int s = *ip++;
length += s;
if (s == 255)
continue;
break;
}
}
/* copy repeated sequence */
if unlikely(op - ref < STEPSIZE) {
#if LZ4_ARCH64
grub_size_t dec2table[] = { 0, 0, 0, -1, 0, 1, 2, 3 };
grub_size_t dec2 = dec2table[op - ref];
#else
const int dec2 = 0;
#endif
*op++ = *ref++;
*op++ = *ref++;
*op++ = *ref++;
*op++ = *ref++;
ref -= dec[op - ref];
A32(op) = A32(ref);
op += STEPSIZE - 4;
ref -= dec2;
} else {
LZ4_COPYSTEP(ref, op);
}
cpy = op + length - (STEPSIZE - 4);
if (cpy > oend - COPYLENGTH) {
if (cpy > oend)
/*
* Error: request to write outside of
* destination buffer.
*/
goto _output_error;
LZ4_SECURECOPY(ref, op, (oend - COPYLENGTH));
while (op < cpy)
*op++ = *ref++;
op = cpy;
if (op == oend)
/*
* Check EOF (should never happen, since last
* 5 bytes are supposed to be literals).
*/
break;
continue;
}
LZ4_SECURECOPY(ref, op, cpy);
op = cpy; /* correction */
}
/* end of decoding */
return (int)(((char *)op) - dest);
/* write overflow error detected */
_output_error:
return (int)(-(((char *)ip) - source));
}
@@ -0,0 +1,165 @@
/* 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;
if (menu->size)
view->menu_title_offset = grub_zalloc (sizeof (*view->menu_title_offset) * menu->size);
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;
if (view->menu_title_offset)
instance->scroll_chosen_entry = grub_gfxmenu_scroll_chosen_entry;
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;
}
@@ -0,0 +1,324 @@
/* 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_grub2_mode;
extern int g_ventoy_wimboot_mode;
extern int g_ventoy_iso_uefi_drv;
extern char g_ventoy_hotkey_tip[256];
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;
int vtoytip;
enum align_mode align;
};
typedef struct grub_gui_label *grub_gui_label_t;
extern const char * g_ventoy_tip_msg1;
extern const char * g_ventoy_tip_msg2;
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)
{
const char *text;
grub_gui_label_t self = vself;
if (! self->visible)
return;
if (!grub_video_have_common_points (region, &self->bounds))
return;
if (self->vtoytip == 1) {
text = g_ventoy_tip_msg1 ? g_ventoy_tip_msg1 : "";
} else if (self->vtoytip == 2) {
text = g_ventoy_tip_msg2 ? g_ventoy_tip_msg2 : "";
} else {
text = self->text;
}
/* 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, text)) / 2;
else if (self->align == align_right)
left_x = (self->bounds.width
- grub_font_get_string_width (self->font, 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 (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_GRUB2_MODE@") == 0) {
value = g_ventoy_grub2_mode ? grub_env_get("VTOY_GRUB2_MODE_STR") : " ";
}
else if (grub_strcmp (value, "@VTOY_WIMBOOT_MODE@") == 0) {
value = g_ventoy_wimboot_mode ? grub_env_get("VTOY_WIMBOOT_MODE_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 = g_ventoy_hotkey_tip;
} else if (value[0] == '@' && value[1] == '@' && value[2]) {
value = grub_env_get(value + 2);
if (!value)
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);
if (grub_strcmp(value, "VTOY_MENU_TIP_1") == 0) {
self->vtoytip = 1;
} else if (grub_strcmp(value, "VTOY_MENU_TIP_2") == 0) {
self->vtoytip = 2;
}
}
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;
}
@@ -0,0 +1,961 @@
/* gui_list.c - GUI component to display a selectable list of items. */
/*
* 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/gui_string_util.h>
#include <grub/gfxmenu_view.h>
#include <grub/gfxwidgets.h>
#include <grub/color.h>
#include <grub/charset.h>
enum scrollbar_slice_mode {
SCROLLBAR_SLICE_WEST,
SCROLLBAR_SLICE_CENTER,
SCROLLBAR_SLICE_EAST
};
struct grub_gui_list_impl
{
struct grub_gui_list list;
grub_gui_container_t parent;
grub_video_rect_t bounds;
char *id;
int visible;
int icon_width;
int icon_height;
int item_height;
int item_padding;
int item_icon_space;
int item_spacing;
grub_font_t item_font;
int selected_item_font_inherit;
grub_font_t selected_item_font;
grub_video_rgba_color_t item_color;
int selected_item_color_inherit;
grub_video_rgba_color_t selected_item_color;
int draw_scrollbar;
int need_to_recreate_scrollbar;
char *scrollbar_frame_pattern;
char *scrollbar_thumb_pattern;
grub_gfxmenu_box_t scrollbar_frame;
grub_gfxmenu_box_t scrollbar_thumb;
int scrollbar_thumb_overlay;
int scrollbar_width;
enum scrollbar_slice_mode scrollbar_slice;
int scrollbar_left_pad;
int scrollbar_right_pad;
int scrollbar_top_pad;
int scrollbar_bottom_pad;
int first_shown_index;
int need_to_recreate_boxes;
char *theme_dir;
char *menu_box_pattern;
char *item_box_pattern;
int selected_item_box_pattern_inherit;
char *selected_item_box_pattern;
grub_gfxmenu_box_t menu_box;
grub_gfxmenu_box_t selected_item_box;
grub_gfxmenu_box_t item_box;
grub_gfxmenu_icon_manager_t icon_manager;
grub_gfxmenu_view_t view;
};
typedef struct grub_gui_list_impl *list_impl_t;
static void
list_destroy (void *vself)
{
list_impl_t self = vself;
grub_free (self->theme_dir);
grub_free (self->menu_box_pattern);
grub_free (self->item_box_pattern);
grub_free (self->selected_item_box_pattern);
if (self->menu_box)
self->menu_box->destroy (self->menu_box);
if (self->item_box)
self->item_box->destroy (self->item_box);
if (self->selected_item_box)
self->selected_item_box->destroy (self->selected_item_box);
if (self->icon_manager)
grub_gfxmenu_icon_manager_destroy (self->icon_manager);
if (self->scrollbar_thumb)
self->scrollbar_thumb->destroy (self->scrollbar_thumb);
if (self->scrollbar_frame)
self->scrollbar_frame->destroy (self->scrollbar_frame);
grub_free (self->scrollbar_thumb_pattern);
grub_free (self->scrollbar_frame_pattern);
grub_free (self);
}
static int
get_num_shown_items (list_impl_t self)
{
int boxpad = self->item_padding;
int item_vspace = self->item_spacing;
int item_height = self->item_height;
grub_gfxmenu_box_t box = self->menu_box;
int box_top_pad = box->get_top_pad (box);
int box_bottom_pad = box->get_bottom_pad (box);
grub_gfxmenu_box_t itembox = self->item_box;
grub_gfxmenu_box_t selbox = self->selected_item_box;
int item_top_pad = itembox->get_top_pad (itembox);
int item_bottom_pad = itembox->get_bottom_pad (itembox);
int sel_top_pad = selbox->get_top_pad (selbox);
int sel_bottom_pad = selbox->get_bottom_pad (selbox);
int max_top_pad = grub_max (item_top_pad, sel_top_pad);
int max_bottom_pad = grub_max (item_bottom_pad, sel_bottom_pad);
if (item_height + item_vspace <= 0)
return 1;
return (self->bounds.height + item_vspace - 2 * boxpad
- max_top_pad - max_bottom_pad
- box_top_pad - box_bottom_pad) / (item_height + item_vspace);
}
static int
check_boxes (list_impl_t self)
{
if (self->need_to_recreate_boxes)
{
grub_gui_recreate_box (&self->menu_box,
self->menu_box_pattern,
self->theme_dir);
grub_gui_recreate_box (&self->item_box,
self->item_box_pattern,
self->theme_dir);
grub_gui_recreate_box (&self->selected_item_box,
self->selected_item_box_pattern,
self->theme_dir);
self->need_to_recreate_boxes = 0;
}
return (self->menu_box != 0 && self->selected_item_box != 0
&& self->item_box != 0);
}
static int
check_scrollbar (list_impl_t self)
{
if (self->need_to_recreate_scrollbar)
{
grub_gui_recreate_box (&self->scrollbar_frame,
self->scrollbar_frame_pattern,
self->theme_dir);
grub_gui_recreate_box (&self->scrollbar_thumb,
self->scrollbar_thumb_pattern,
self->theme_dir);
self->need_to_recreate_scrollbar = 0;
}
if (self->scrollbar_frame == 0 || self->scrollbar_thumb == 0)
return 0;
/* Sanity checks. */
grub_gfxmenu_box_t frame = self->scrollbar_frame;
grub_gfxmenu_box_t thumb = self->scrollbar_thumb;
grub_gfxmenu_box_t menu = self->menu_box;
int min_width = frame->get_left_pad (frame)
+ frame->get_right_pad (frame);
int min_height = frame->get_top_pad (frame)
+ frame->get_bottom_pad (frame)
+ self->scrollbar_top_pad + self->scrollbar_bottom_pad
+ menu->get_top_pad (menu)
+ menu->get_bottom_pad (menu);
if (!self->scrollbar_thumb_overlay)
{
min_width += thumb->get_left_pad (thumb)
+ thumb->get_right_pad (thumb);
min_height += thumb->get_top_pad (thumb)
+ thumb->get_bottom_pad (thumb);
}
if (min_width <= self->scrollbar_width
&& min_height <= (int) self->bounds.height)
return 1;
/* Unprintable dimenstions. */
self->draw_scrollbar = 0;
return 0;
}
static const char *
list_get_id (void *vself)
{
list_impl_t self = vself;
return self->id;
}
static int
list_is_instance (void *vself __attribute__((unused)), const char *type)
{
return (grub_strcmp (type, "component") == 0
|| grub_strcmp (type, "list") == 0);
}
static struct grub_video_bitmap *
get_item_icon (list_impl_t self, int item_index)
{
grub_menu_entry_t entry;
entry = grub_menu_get_entry (self->view->menu, item_index);
if (! entry)
return 0;
return grub_gfxmenu_icon_manager_get_icon (self->icon_manager, entry);
}
static void
make_selected_item_visible (list_impl_t self)
{
int selected_index = self->view->selected;
if (selected_index < 0)
return; /* No item is selected. */
int num_shown_items = get_num_shown_items (self);
int last_shown_index = self->first_shown_index + (num_shown_items - 1);
if (selected_index < self->first_shown_index)
self->first_shown_index = selected_index;
else if (selected_index > last_shown_index)
self->first_shown_index = selected_index - (num_shown_items - 1);
}
/* Draw a scrollbar on the menu. */
static void
draw_scrollbar (list_impl_t self,
int value, int extent, int min, int max,
int scrollbar_width, int scrollbar_height)
{
unsigned thumby, thumbheight;
grub_gfxmenu_box_t frame = self->scrollbar_frame;
grub_gfxmenu_box_t thumb = self->scrollbar_thumb;
int frame_vertical_pad = (frame->get_top_pad (frame)
+ frame->get_bottom_pad (frame));
int frame_horizontal_pad = (frame->get_left_pad (frame)
+ frame->get_right_pad (frame));
unsigned thumb_vertical_pad = (thumb->get_top_pad (thumb)
+ thumb->get_bottom_pad (thumb));
int thumb_horizontal_pad = (thumb->get_left_pad (thumb)
+ thumb->get_right_pad (thumb));
int tracktop = frame->get_top_pad (frame);
unsigned tracklen;
if (scrollbar_height <= frame_vertical_pad)
tracklen = 0;
else
tracklen = scrollbar_height - frame_vertical_pad;
frame->set_content_size (frame,
scrollbar_width - frame_horizontal_pad,
tracklen);
if (self->scrollbar_thumb_overlay)
{
tracklen += thumb_vertical_pad;
tracktop -= thumb->get_top_pad (thumb);
}
if (value <= min || max <= min)
thumby = 0;
else
thumby = ((unsigned) tracklen * (value - min))
/ ((unsigned) (max - min));
if (max <= min)
thumbheight = 1;
else
thumbheight = ((unsigned) (tracklen * extent)
/ ((unsigned) (max - min))) + 1;
/* Rare occasion: too many entries or too low height. */
if (thumbheight < thumb_vertical_pad)
{
thumbheight = thumb_vertical_pad;
if (value <= min || max <= extent
|| tracklen <= thumb_vertical_pad)
thumby = 0;
else
thumby = ((unsigned) ((tracklen - thumb_vertical_pad) * (value - min))
/ ((unsigned)(max - extent)));
}
thumby += tracktop;
int thumbx = frame->get_left_pad (frame);
int thumbwidth = scrollbar_width - frame_horizontal_pad;
if (!self->scrollbar_thumb_overlay)
thumbwidth -= thumb_horizontal_pad;
else
thumbx -= thumb->get_left_pad (thumb);
thumb->set_content_size (thumb, thumbwidth,
thumbheight - thumb_vertical_pad);
frame->draw (frame, 0, 0);
thumb->draw (thumb, thumbx, thumby);
}
/* Draw the list of items. */
static void
draw_menu (list_impl_t self, int num_shown_items)
{
if (! self->menu_box || ! self->selected_item_box || ! self->item_box)
return;
int boxpad = self->item_padding;
int icon_text_space = self->item_icon_space;
int item_vspace = self->item_spacing;
int ascent = grub_font_get_ascent (self->item_font);
int descent = grub_font_get_descent (self->item_font);
int selected_ascent = grub_font_get_ascent (self->selected_item_font);
int selected_descent = grub_font_get_descent (self->selected_item_font);
int text_box_height = self->item_height;
make_selected_item_visible (self);
grub_gfxmenu_box_t itembox = self->item_box;
grub_gfxmenu_box_t selbox = self->selected_item_box;
int item_leftpad = itembox->get_left_pad (itembox);
int item_rightpad = itembox->get_right_pad (itembox);
int item_border_width = item_leftpad + item_rightpad;
int item_toppad = itembox->get_top_pad (itembox);
int sel_leftpad = selbox->get_left_pad (selbox);
int sel_rightpad = selbox->get_right_pad (selbox);
int sel_border_width = sel_leftpad + sel_rightpad;
int sel_toppad = selbox->get_top_pad (selbox);
int max_leftpad = grub_max (item_leftpad, sel_leftpad);
int max_toppad = grub_max (item_toppad, sel_toppad);
int item_top = 0;
int menu_index;
int visible_index;
struct grub_video_rect oviewport;
grub_video_get_viewport (&oviewport.x, &oviewport.y,
&oviewport.width, &oviewport.height);
grub_video_set_viewport (oviewport.x + boxpad,
oviewport.y + boxpad,
oviewport.width - 2 * boxpad,
oviewport.height - 2 * boxpad);
int cwidth = oviewport.width - 2 * boxpad;
itembox->set_content_size (itembox, cwidth - item_border_width,
text_box_height);
selbox->set_content_size (selbox, cwidth - sel_border_width,
text_box_height);
int text_left_offset = self->icon_width + icon_text_space;
int item_text_top_offset = (text_box_height - (ascent + descent)) / 2 + ascent;
int sel_text_top_offset = (text_box_height - (selected_ascent
+ selected_descent)) / 2
+ selected_ascent;
grub_video_rect_t svpsave, sviewport;
sviewport.x = max_leftpad + text_left_offset;
int text_viewport_width = cwidth - sviewport.x;
sviewport.height = text_box_height;
grub_video_color_t item_color;
grub_video_color_t sel_color;
item_color = grub_video_map_rgba_color (self->item_color);
sel_color = grub_video_map_rgba_color (self->selected_item_color);
int item_box_top_offset = max_toppad - item_toppad;
int sel_box_top_offset = max_toppad - sel_toppad;
int item_viewport_width = text_viewport_width - item_rightpad;
int sel_viewport_width = text_viewport_width - sel_rightpad;
int tmp_icon_top_offset = (text_box_height - self->icon_height) / 2;
int item_icon_top_offset = item_toppad + tmp_icon_top_offset;
int sel_icon_top_offset = sel_toppad + tmp_icon_top_offset;
for (visible_index = 0, menu_index = self->first_shown_index;
visible_index < num_shown_items && menu_index < self->view->menu->size;
visible_index++, menu_index++)
{
int is_selected = (menu_index == self->view->selected);
struct grub_video_bitmap *icon;
grub_font_t font;
grub_video_color_t color;
int text_top_offset;
int top_pad;
int icon_top_offset;
int viewport_width;
if (is_selected)
{
selbox->draw (selbox, 0, item_top + sel_box_top_offset);
font = self->selected_item_font;
color = sel_color;
text_top_offset = sel_text_top_offset;
top_pad = sel_toppad;
icon_top_offset = sel_icon_top_offset;
viewport_width = sel_viewport_width;
}
else
{
itembox->draw (itembox, 0, item_top + item_box_top_offset);
font = self->item_font;
color = item_color;
text_top_offset = item_text_top_offset;
top_pad = item_toppad;
icon_top_offset = item_icon_top_offset;
viewport_width = item_viewport_width;
}
icon = get_item_icon (self, menu_index);
if (icon != 0)
grub_video_blit_bitmap (icon, GRUB_VIDEO_BLIT_BLEND,
max_leftpad,
item_top + icon_top_offset,
0, 0, self->icon_width, self->icon_height);
const char *item_title =
grub_menu_get_entry (self->view->menu, menu_index)->title;
int off = self->view->menu_title_offset[menu_index];
const char *scrolled_title =
grub_utf8_offset_code (item_title, grub_strlen (item_title), off);
if (scrolled_title)
item_title = scrolled_title;
sviewport.y = item_top + top_pad;
sviewport.width = viewport_width;
grub_gui_set_viewport (&sviewport, &svpsave);
grub_font_draw_string (item_title,
font,
color,
0,
text_top_offset);
grub_gui_restore_viewport (&svpsave);
item_top += text_box_height + item_vspace;
}
grub_video_set_viewport (oviewport.x,
oviewport.y,
oviewport.width,
oviewport.height);
}
static void
list_paint (void *vself, const grub_video_rect_t *region)
{
list_impl_t self = vself;
grub_video_rect_t vpsave;
if (! self->visible)
return;
if (!grub_video_have_common_points (region, &self->bounds))
return;
check_boxes (self);
if (! self->menu_box || ! self->selected_item_box || ! self->item_box)
return;
grub_gui_set_viewport (&self->bounds, &vpsave);
{
grub_gfxmenu_box_t box = self->menu_box;
int box_left_pad = box->get_left_pad (box);
int box_top_pad = box->get_top_pad (box);
int box_right_pad = box->get_right_pad (box);
int box_bottom_pad = box->get_bottom_pad (box);
grub_video_rect_t vpsave2, content_rect;
int num_shown_items = get_num_shown_items (self);
int drawing_scrollbar = (self->draw_scrollbar
&& (num_shown_items < self->view->menu->size)
&& check_scrollbar (self));
int scrollbar_width = self->scrollbar_width;
content_rect.x = box_left_pad;
content_rect.y = box_top_pad;
content_rect.width = self->bounds.width - box_left_pad - box_right_pad;
content_rect.height = self->bounds.height - box_top_pad - box_bottom_pad;
box->set_content_size (box, content_rect.width, content_rect.height);
box->draw (box, 0, 0);
switch (self->scrollbar_slice)
{
case SCROLLBAR_SLICE_WEST:
content_rect.x += self->scrollbar_right_pad;
content_rect.width -= self->scrollbar_right_pad;
break;
case SCROLLBAR_SLICE_CENTER:
if (drawing_scrollbar)
content_rect.width -= scrollbar_width + self->scrollbar_left_pad
+ self->scrollbar_right_pad;
break;
case SCROLLBAR_SLICE_EAST:
content_rect.width -= self->scrollbar_left_pad;
break;
}
grub_gui_set_viewport (&content_rect, &vpsave2);
draw_menu (self, num_shown_items);
grub_gui_restore_viewport (&vpsave2);
if (drawing_scrollbar)
{
content_rect.y += self->scrollbar_top_pad;
content_rect.height -= self->scrollbar_top_pad
+ self->scrollbar_bottom_pad;
content_rect.width = scrollbar_width;
switch (self->scrollbar_slice)
{
case SCROLLBAR_SLICE_WEST:
if (box_left_pad > scrollbar_width)
{
content_rect.x = box_left_pad - scrollbar_width;
content_rect.width = scrollbar_width;
}
else
{
content_rect.x = 0;
content_rect.width = box_left_pad;
}
break;
case SCROLLBAR_SLICE_CENTER:
content_rect.x = self->bounds.width - box_right_pad
- scrollbar_width - self->scrollbar_right_pad;
content_rect.width = scrollbar_width;
break;
case SCROLLBAR_SLICE_EAST:
content_rect.x = self->bounds.width - box_right_pad;
content_rect.width = box_right_pad;
break;
}
grub_gui_set_viewport (&content_rect, &vpsave2);
draw_scrollbar (self,
self->first_shown_index, num_shown_items,
0, self->view->menu->size,
scrollbar_width,
content_rect.height);
grub_gui_restore_viewport (&vpsave2);
}
}
grub_gui_restore_viewport (&vpsave);
}
static void
list_set_parent (void *vself, grub_gui_container_t parent)
{
list_impl_t self = vself;
self->parent = parent;
}
static grub_gui_container_t
list_get_parent (void *vself)
{
list_impl_t self = vself;
return self->parent;
}
static void
list_set_bounds (void *vself, const grub_video_rect_t *bounds)
{
list_impl_t self = vself;
self->bounds = *bounds;
}
static void
list_get_bounds (void *vself, grub_video_rect_t *bounds)
{
list_impl_t self = vself;
*bounds = self->bounds;
}
static void
list_get_minimal_size (void *vself, unsigned *width, unsigned *height)
{
list_impl_t self = vself;
if (check_boxes (self))
{
int boxpad = self->item_padding;
int item_vspace = self->item_spacing;
int item_height = self->item_height;
int num_items = 3;
grub_gfxmenu_box_t box = self->menu_box;
int box_left_pad = box->get_left_pad (box);
int box_top_pad = box->get_top_pad (box);
int box_right_pad = box->get_right_pad (box);
int box_bottom_pad = box->get_bottom_pad (box);
unsigned width_s;
grub_gfxmenu_box_t selbox = self->selected_item_box;
int sel_top_pad = selbox->get_top_pad (selbox);
int sel_bottom_pad = selbox->get_bottom_pad (selbox);
int sel_left_pad = selbox->get_left_pad (selbox);
int sel_right_pad = selbox->get_right_pad (selbox);
grub_gfxmenu_box_t itembox = self->item_box;
int item_top_pad = itembox->get_top_pad (itembox);
int item_bottom_pad = itembox->get_bottom_pad (itembox);
int item_left_pad = itembox->get_left_pad (itembox);
int item_right_pad = itembox->get_right_pad (itembox);
int max_left_pad = grub_max (item_left_pad, sel_left_pad);
int max_right_pad = grub_max (item_right_pad, sel_right_pad);
int max_top_pad = grub_max (item_top_pad, sel_top_pad);
int max_bottom_pad = grub_max (item_bottom_pad, sel_bottom_pad);
*width = grub_font_get_string_width (self->item_font, "Typical OS");
width_s = grub_font_get_string_width (self->selected_item_font,
"Typical OS");
if (*width < width_s)
*width = width_s;
*width += 2 * boxpad + box_left_pad + box_right_pad
+ max_left_pad + max_right_pad
+ self->item_icon_space + self->icon_width;
switch (self->scrollbar_slice)
{
case SCROLLBAR_SLICE_WEST:
*width += self->scrollbar_right_pad;
break;
case SCROLLBAR_SLICE_CENTER:
*width += self->scrollbar_width + self->scrollbar_left_pad
+ self->scrollbar_right_pad;
break;
case SCROLLBAR_SLICE_EAST:
*width += self->scrollbar_left_pad;
break;
}
/* Set the menu box height to fit the items. */
*height = (item_height * num_items
+ item_vspace * (num_items - 1)
+ 2 * boxpad
+ box_top_pad + box_bottom_pad
+ max_top_pad + max_bottom_pad);
}
else
{
*width = 0;
*height = 0;
}
}
static grub_err_t
list_set_property (void *vself, const char *name, const char *value)
{
list_impl_t self = vself;
if (grub_strcmp (name, "item_font") == 0)
{
self->item_font = grub_font_get (value);
if (self->selected_item_font_inherit)
self->selected_item_font = self->item_font;
}
else if (grub_strcmp (name, "selected_item_font") == 0)
{
if (! value || grub_strcmp (value, "inherit") == 0)
{
self->selected_item_font = self->item_font;
self->selected_item_font_inherit = 1;
}
else
{
self->selected_item_font = grub_font_get (value);
self->selected_item_font_inherit = 0;
}
}
else if (grub_strcmp (name, "item_color") == 0)
{
grub_video_rgba_color_t color;
if (grub_video_parse_color (value, &color) == GRUB_ERR_NONE)
{
self->item_color = color;
if (self->selected_item_color_inherit)
self->selected_item_color = self->item_color;
}
}
else if (grub_strcmp (name, "selected_item_color") == 0)
{
if (! value || grub_strcmp (value, "inherit") == 0)
{
self->selected_item_color = self->item_color;
self->selected_item_color_inherit = 1;
}
else
{
grub_video_rgba_color_t color;
if (grub_video_parse_color (value, &color)
== GRUB_ERR_NONE)
{
self->selected_item_color = color;
self->selected_item_color_inherit = 0;
}
}
}
else if (grub_strcmp (name, "icon_width") == 0)
{
self->icon_width = grub_strtol (value, 0, 10);
grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager,
self->icon_width,
self->icon_height);
}
else if (grub_strcmp (name, "icon_height") == 0)
{
self->icon_height = grub_strtol (value, 0, 10);
grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager,
self->icon_width,
self->icon_height);
}
else if (grub_strcmp (name, "item_height") == 0)
{
self->item_height = grub_strtol (value, 0, 10);
}
else if (grub_strcmp (name, "item_padding") == 0)
{
self->item_padding = grub_strtol (value, 0, 10);
}
else if (grub_strcmp (name, "item_icon_space") == 0)
{
self->item_icon_space = grub_strtol (value, 0, 10);
}
else if (grub_strcmp (name, "item_spacing") == 0)
{
self->item_spacing = grub_strtol (value, 0, 10);
}
else if (grub_strcmp (name, "visible") == 0)
{
self->visible = grub_strcmp (value, "false") != 0;
}
else if (grub_strcmp (name, "menu_pixmap_style") == 0)
{
self->need_to_recreate_boxes = 1;
grub_free (self->menu_box_pattern);
self->menu_box_pattern = value ? grub_strdup (value) : 0;
}
else if (grub_strcmp (name, "item_pixmap_style") == 0)
{
self->need_to_recreate_boxes = 1;
grub_free (self->item_box_pattern);
self->item_box_pattern = value ? grub_strdup (value) : 0;
if (self->selected_item_box_pattern_inherit)
{
grub_free (self->selected_item_box_pattern);
self->selected_item_box_pattern = value ? grub_strdup (value) : 0;
}
}
else if (grub_strcmp (name, "selected_item_pixmap_style") == 0)
{
if (!value || grub_strcmp (value, "inherit") == 0)
{
grub_free (self->selected_item_box_pattern);
char *tmp = self->item_box_pattern;
self->selected_item_box_pattern = tmp ? grub_strdup (tmp) : 0;
self->selected_item_box_pattern_inherit = 1;
}
else
{
self->need_to_recreate_boxes = 1;
grub_free (self->selected_item_box_pattern);
self->selected_item_box_pattern = value ? grub_strdup (value) : 0;
self->selected_item_box_pattern_inherit = 0;
}
}
else if (grub_strcmp (name, "scrollbar_frame") == 0)
{
self->need_to_recreate_scrollbar = 1;
grub_free (self->scrollbar_frame_pattern);
self->scrollbar_frame_pattern = value ? grub_strdup (value) : 0;
}
else if (grub_strcmp (name, "scrollbar_thumb") == 0)
{
self->need_to_recreate_scrollbar = 1;
grub_free (self->scrollbar_thumb_pattern);
self->scrollbar_thumb_pattern = value ? grub_strdup (value) : 0;
}
else if (grub_strcmp (name, "scrollbar_thumb_overlay") == 0)
{
self->scrollbar_thumb_overlay = grub_strcmp (value, "true") == 0;
}
else if (grub_strcmp (name, "scrollbar_width") == 0)
{
self->scrollbar_width = grub_strtol (value, 0, 10);
}
else if (grub_strcmp (name, "scrollbar_slice") == 0)
{
if (grub_strcmp (value, "west") == 0)
self->scrollbar_slice = SCROLLBAR_SLICE_WEST;
else if (grub_strcmp (value, "center") == 0)
self->scrollbar_slice = SCROLLBAR_SLICE_CENTER;
else if (grub_strcmp (value, "east") == 0)
self->scrollbar_slice = SCROLLBAR_SLICE_EAST;
}
else if (grub_strcmp (name, "scrollbar_left_pad") == 0)
{
self->scrollbar_left_pad = grub_strtol (value, 0, 10);
}
else if (grub_strcmp (name, "scrollbar_right_pad") == 0)
{
self->scrollbar_right_pad = grub_strtol (value, 0, 10);
}
else if (grub_strcmp (name, "scrollbar_top_pad") == 0)
{
self->scrollbar_top_pad = grub_strtol (value, 0, 10);
}
else if (grub_strcmp (name, "scrollbar_bottom_pad") == 0)
{
self->scrollbar_bottom_pad = grub_strtol (value, 0, 10);
}
else if (grub_strcmp (name, "scrollbar") == 0)
{
self->draw_scrollbar = grub_strcmp (value, "false") != 0;
}
else if (grub_strcmp (name, "theme_dir") == 0)
{
self->need_to_recreate_boxes = 1;
grub_free (self->theme_dir);
self->theme_dir = value ? grub_strdup (value) : 0;
}
else if (grub_strcmp (name, "id") == 0)
{
grub_free (self->id);
if (value)
self->id = grub_strdup (value);
else
self->id = 0;
}
return grub_errno;
}
/* Set necessary information that the gfxmenu view provides. */
static void
list_set_view_info (void *vself,
grub_gfxmenu_view_t view)
{
list_impl_t self = vself;
grub_gfxmenu_icon_manager_set_theme_path (self->icon_manager,
view->theme_path);
self->view = view;
}
/* Refresh list variables */
static void
list_refresh_info (void *vself,
grub_gfxmenu_view_t view)
{
list_impl_t self = vself;
if (view->nested)
self->first_shown_index = 0;
}
static struct grub_gui_component_ops list_comp_ops =
{
.destroy = list_destroy,
.get_id = list_get_id,
.is_instance = list_is_instance,
.paint = list_paint,
.set_parent = list_set_parent,
.get_parent = list_get_parent,
.set_bounds = list_set_bounds,
.get_bounds = list_get_bounds,
.get_minimal_size = list_get_minimal_size,
.set_property = list_set_property
};
static struct grub_gui_list_ops list_ops =
{
.set_view_info = list_set_view_info,
.refresh_list = list_refresh_info
};
grub_gui_component_t
grub_gui_list_new (void)
{
list_impl_t self;
grub_font_t default_font;
grub_video_rgba_color_t default_fg_color;
self = grub_zalloc (sizeof (*self));
if (! self)
return 0;
self->list.ops = &list_ops;
self->list.component.ops = &list_comp_ops;
self->visible = 1;
default_font = grub_font_get ("Unknown Regular 16");
default_fg_color = grub_video_rgba_color_rgb (0, 0, 0);
self->icon_width = 32;
self->icon_height = 32;
self->item_height = 42;
self->item_padding = 14;
self->item_icon_space = 4;
self->item_spacing = 16;
self->item_font = default_font;
self->selected_item_font_inherit = 1; /* Default to using the item_font. */
self->selected_item_font = default_font;
self->item_color = default_fg_color;
self->selected_item_color_inherit = 1; /* Default to using the item_color. */
self->selected_item_color = default_fg_color;
self->draw_scrollbar = 1;
self->need_to_recreate_scrollbar = 1;
self->scrollbar_frame = 0;
self->scrollbar_thumb = 0;
self->scrollbar_frame_pattern = 0;
self->scrollbar_thumb_pattern = 0;
self->scrollbar_thumb_overlay = 0;
self->scrollbar_width = 16;
self->scrollbar_slice = SCROLLBAR_SLICE_EAST;
self->scrollbar_left_pad = 2;
self->scrollbar_right_pad = 0;
self->scrollbar_top_pad = 0;
self->scrollbar_bottom_pad = 0;
self->first_shown_index = 0;
self->need_to_recreate_boxes = 0;
self->theme_dir = 0;
self->menu_box_pattern = 0;
self->item_box_pattern = 0;
self->selected_item_box_pattern_inherit = 1;/*Default to using the item_box.*/
self->selected_item_box_pattern = 0;
self->menu_box = grub_gfxmenu_create_box (0, 0);
self->item_box = grub_gfxmenu_create_box (0, 0);
self->selected_item_box = grub_gfxmenu_create_box (0, 0);
self->icon_manager = grub_gfxmenu_icon_manager_new ();
if (! self->icon_manager)
{
self->list.component.ops->destroy (self);
return 0;
}
grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager,
self->icon_width,
self->icon_height);
return (grub_gui_component_t) self;
}
@@ -0,0 +1,893 @@
/* theme_loader.c - Theme file loader for gfxmenu. */
/*
* 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/file.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/err.h>
#include <grub/dl.h>
#include <grub/video.h>
#include <grub/gui_string_util.h>
#include <grub/bitmap.h>
#include <grub/bitmap_scale.h>
#include <grub/gfxwidgets.h>
#include <grub/gfxmenu_view.h>
#include <grub/gui.h>
#include <grub/color.h>
#include <grub/env.h>
static grub_err_t
parse_proportional_spec (const char *value, signed *abs, grub_fixed_signed_t *prop);
/* Construct a new box widget using ABSPATTERN to find the pixmap files for
it, storing the new box instance at *BOXPTR.
PATTERN should be of the form: "(hd0,0)/somewhere/style*.png".
The '*' then gets substituted with the various pixmap names that the
box uses. */
static grub_err_t
recreate_box_absolute (grub_gfxmenu_box_t *boxptr, const char *abspattern)
{
char *prefix;
char *suffix;
char *star;
grub_gfxmenu_box_t box;
star = grub_strchr (abspattern, '*');
if (! star)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"missing `*' in box pixmap pattern `%s'", abspattern);
/* Prefix: Get the part before the '*'. */
prefix = grub_malloc (star - abspattern + 1);
if (! prefix)
return grub_errno;
grub_memcpy (prefix, abspattern, star - abspattern);
prefix[star - abspattern] = '\0';
/* Suffix: Everything after the '*' is the suffix. */
suffix = star + 1;
box = grub_gfxmenu_create_box (prefix, suffix);
grub_free (prefix);
if (! box)
return grub_errno;
if (*boxptr)
(*boxptr)->destroy (*boxptr);
*boxptr = box;
return grub_errno;
}
/* Construct a new box widget using PATTERN to find the pixmap files for it,
storing the new widget at *BOXPTR. PATTERN should be of the form:
"somewhere/style*.png". The '*' then gets substituted with the various
pixmap names that the widget uses.
Important! The value of *BOXPTR must be initialized! It must either
(1) Be 0 (a NULL pointer), or
(2) Be a pointer to a valid 'grub_gfxmenu_box_t' instance.
In this case, the previous instance is destroyed. */
grub_err_t
grub_gui_recreate_box (grub_gfxmenu_box_t *boxptr,
const char *pattern, const char *theme_dir)
{
char *abspattern;
/* Check arguments. */
if (! pattern)
{
/* If no pixmap pattern is given, then just create an empty box. */
if (*boxptr)
(*boxptr)->destroy (*boxptr);
*boxptr = grub_gfxmenu_create_box (0, 0);
return grub_errno;
}
if (! theme_dir)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"styled box missing theme directory");
/* Resolve to an absolute path. */
abspattern = grub_resolve_relative_path (theme_dir, pattern);
if (! abspattern)
return grub_errno;
/* Create the box. */
recreate_box_absolute (boxptr, abspattern);
grub_free (abspattern);
return grub_errno;
}
static grub_err_t
theme_get_unsigned_int_from_proportional (const char *value,
unsigned absolute_value,
unsigned int *parsed_value)
{
grub_err_t err;
grub_fixed_signed_t frac;
signed pixels;
err = parse_proportional_spec (value, &pixels, &frac);
if (err != GRUB_ERR_NONE)
return err;
int result = grub_fixed_sfs_multiply (absolute_value, frac) + pixels;
if (result < 0)
result = 0;
*parsed_value = result;
return GRUB_ERR_NONE;
}
/* Set the specified property NAME on the view to the given string VALUE.
The caller is responsible for the lifetimes of NAME and VALUE. */
static grub_err_t
theme_set_string (grub_gfxmenu_view_t view,
const char *name,
const char *value,
const char *theme_dir,
const char *filename,
int line_num,
int col_num)
{
if (! grub_strcmp ("title-font", name))
view->title_font = grub_font_get (value);
else if (! grub_strcmp ("message-font", name))
view->message_font = grub_font_get (value);
else if (! grub_strcmp ("terminal-font", name))
{
grub_free (view->terminal_font_name);
view->terminal_font_name = grub_strdup (value);
if (! view->terminal_font_name)
return grub_errno;
}
else if (! grub_strcmp ("title-color", name))
grub_video_parse_color (value, &view->title_color);
else if (! grub_strcmp ("message-color", name))
grub_video_parse_color (value, &view->message_color);
else if (! grub_strcmp ("message-bg-color", name))
grub_video_parse_color (value, &view->message_bg_color);
else if (! grub_strcmp("menu-tip-left", name))
grub_env_set("VTOY_TIP_LEFT", value);
else if (! grub_strcmp("menu-tip-top", name))
grub_env_set("VTOY_TIP_TOP", value);
else if (! grub_strcmp("menu-tip-color", name))
grub_env_set("VTOY_TIP_COLOR", value);
else if (! grub_strcmp ("desktop-image", name))
{
struct grub_video_bitmap *raw_bitmap;
char *path;
path = grub_resolve_relative_path (theme_dir, value);
if (! path)
return grub_errno;
if (grub_video_bitmap_load (&raw_bitmap, path) != GRUB_ERR_NONE)
{
grub_free (path);
return grub_errno;
}
grub_free(path);
grub_video_bitmap_destroy (view->raw_desktop_image);
view->raw_desktop_image = raw_bitmap;
}
else if (! grub_strcmp ("desktop-image-scale-method", name))
{
if (! value || ! grub_strcmp ("stretch", value))
view->desktop_image_scale_method =
GRUB_VIDEO_BITMAP_SELECTION_METHOD_STRETCH;
else if (! grub_strcmp ("crop", value))
view->desktop_image_scale_method =
GRUB_VIDEO_BITMAP_SELECTION_METHOD_CROP;
else if (! grub_strcmp ("padding", value))
view->desktop_image_scale_method =
GRUB_VIDEO_BITMAP_SELECTION_METHOD_PADDING;
else if (! grub_strcmp ("fitwidth", value))
view->desktop_image_scale_method =
GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITWIDTH;
else if (! grub_strcmp ("fitheight", value))
view->desktop_image_scale_method =
GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITHEIGHT;
else
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"Unsupported scale method: %s",
value);
}
else if (! grub_strcmp ("desktop-image-h-align", name))
{
if (! grub_strcmp ("left", value))
view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_LEFT;
else if (! grub_strcmp ("center", value))
view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_CENTER;
else if (! grub_strcmp ("right", value))
view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_RIGHT;
else
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"Unsupported horizontal align method: %s",
value);
}
else if (! grub_strcmp ("desktop-image-v-align", name))
{
if (! grub_strcmp ("top", value))
view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_TOP;
else if (! grub_strcmp ("center", value))
view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_CENTER;
else if (! grub_strcmp ("bottom", value))
view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_BOTTOM;
else
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"Unsupported vertical align method: %s",
value);
}
else if (! grub_strcmp ("desktop-color", name))
grub_video_parse_color (value, &view->desktop_color);
else if (! grub_strcmp ("terminal-box", name))
{
grub_err_t err;
err = grub_gui_recreate_box (&view->terminal_box, value, theme_dir);
if (err != GRUB_ERR_NONE)
return err;
}
else if (! grub_strcmp ("terminal-border", name))
{
view->terminal_border = grub_strtoul (value, 0, 10);
if (grub_errno)
return grub_errno;
}
else if (! grub_strcmp ("terminal-left", name))
{
unsigned int tmp;
int err = theme_get_unsigned_int_from_proportional (value,
view->screen.width,
&tmp);
if (err != GRUB_ERR_NONE)
return err;
view->terminal_rect.x = tmp;
}
else if (! grub_strcmp ("terminal-top", name))
{
unsigned int tmp;
int err = theme_get_unsigned_int_from_proportional (value,
view->screen.height,
&tmp);
if (err != GRUB_ERR_NONE)
return err;
view->terminal_rect.y = tmp;
}
else if (! grub_strcmp ("terminal-width", name))
{
unsigned int tmp;
int err = theme_get_unsigned_int_from_proportional (value,
view->screen.width,
&tmp);
if (err != GRUB_ERR_NONE)
return err;
view->terminal_rect.width = tmp;
}
else if (! grub_strcmp ("terminal-height", name))
{
unsigned int tmp;
int err = theme_get_unsigned_int_from_proportional (value,
view->screen.height,
&tmp);
if (err != GRUB_ERR_NONE)
return err;
view->terminal_rect.height = tmp;
}
else if (! grub_strcmp ("title-text", name))
{
grub_free (view->title_text);
view->title_text = grub_strdup (value);
if (! view->title_text)
return grub_errno;
}
else if (! grub_strcmp ("ventoy_left_top_color", name))
return grub_errno;
else
{
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"%s:%d:%d unknown property `%s'",
filename, line_num, col_num, name);
}
return grub_errno;
}
struct parsebuf
{
char *buf;
int pos;
int len;
int line_num;
int col_num;
const char *filename;
char *theme_dir;
grub_gfxmenu_view_t view;
};
static int
has_more (struct parsebuf *p)
{
return p->pos < p->len;
}
static int
read_char (struct parsebuf *p)
{
if (has_more (p))
{
char c;
c = p->buf[p->pos++];
if (c == '\n')
{
p->line_num++;
p->col_num = 1;
}
else
{
p->col_num++;
}
return c;
}
else
return -1;
}
static int
peek_char (struct parsebuf *p)
{
if (has_more (p))
return p->buf[p->pos];
else
return -1;
}
static int
is_whitespace (char c)
{
return (c == ' '
|| c == '\t'
|| c == '\r'
|| c == '\n'
|| c == '\f');
}
static void
skip_whitespace (struct parsebuf *p)
{
while (has_more (p) && is_whitespace(peek_char (p)))
read_char (p);
}
static void
advance_to_next_line (struct parsebuf *p)
{
int c;
/* Eat characters up to the newline. */
do
{
c = read_char (p);
}
while (c != -1 && c != '\n');
}
static int
is_identifier_char (int c)
{
return (c != -1
&& (grub_isalpha(c)
|| grub_isdigit(c)
|| c == '_'
|| c == '-'));
}
static char *
read_identifier (struct parsebuf *p)
{
/* Index of the first character of the identifier in p->buf. */
int start;
/* Next index after the last character of the identifer in p->buf. */
int end;
skip_whitespace (p);
/* Capture the start of the identifier. */
start = p->pos;
/* Scan for the end. */
while (is_identifier_char (peek_char (p)))
read_char (p);
end = p->pos;
if (end - start < 1)
return 0;
return grub_new_substring (p->buf, start, end);
}
static char *
read_expression (struct parsebuf *p)
{
int start;
int end;
skip_whitespace (p);
if (peek_char (p) == '"')
{
/* Read as a quoted string.
The quotation marks are not included in the expression value. */
/* Skip opening quotation mark. */
read_char (p);
start = p->pos;
while (has_more (p) && peek_char (p) != '"')
read_char (p);
end = p->pos;
/* Skip the terminating quotation mark. */
read_char (p);
}
else if (peek_char (p) == '(')
{
/* Read as a parenthesized string -- for tuples/coordinates. */
/* The parentheses are included in the expression value. */
int c;
start = p->pos;
do
{
c = read_char (p);
}
while (c != -1 && c != ')');
end = p->pos;
}
else if (has_more (p))
{
/* Read as a single word -- for numeric values or words without
whitespace. */
start = p->pos;
while (has_more (p) && ! is_whitespace (peek_char (p))
&& peek_char (p) != '}')
read_char (p);
end = p->pos;
}
else
{
/* The end of the theme file has been reached. */
grub_error (GRUB_ERR_IO, "%s:%d:%d expression expected in theme file",
p->filename, p->line_num, p->col_num);
return 0;
}
return grub_new_substring (p->buf, start, end);
}
static grub_err_t
parse_proportional_spec (const char *value, signed *abs, grub_fixed_signed_t *prop)
{
signed num;
const char *ptr;
int sig = 0;
*abs = 0;
*prop = 0;
ptr = value;
while (*ptr)
{
sig = 0;
while (*ptr == '-' || *ptr == '+')
{
if (*ptr == '-')
sig = !sig;
ptr++;
}
num = grub_strtoul (ptr, (char **) &ptr, 0);
if (grub_errno)
return grub_errno;
if (sig)
num = -num;
if (*ptr == '%')
{
*prop += grub_fixed_fsf_divide (grub_signed_to_fixed (num), 100);
ptr++;
}
else
*abs += num;
}
return GRUB_ERR_NONE;
}
/* Read a GUI object specification from the theme file.
Any components created will be added to the GUI container PARENT. */
static grub_err_t
read_object (struct parsebuf *p, grub_gui_container_t parent)
{
grub_video_rect_t bounds;
char *name;
name = read_identifier (p);
if (! name)
goto cleanup;
grub_gui_component_t component = 0;
if (grub_strcmp (name, "label") == 0)
{
component = grub_gui_label_new ();
}
else if (grub_strcmp (name, "image") == 0)
{
component = grub_gui_image_new ();
}
else if (grub_strcmp (name, "vbox") == 0)
{
component = (grub_gui_component_t) grub_gui_vbox_new ();
}
else if (grub_strcmp (name, "hbox") == 0)
{
component = (grub_gui_component_t) grub_gui_hbox_new ();
}
else if (grub_strcmp (name, "canvas") == 0)
{
component = (grub_gui_component_t) grub_gui_canvas_new ();
}
else if (grub_strcmp (name, "progress_bar") == 0)
{
component = grub_gui_progress_bar_new ();
}
else if (grub_strcmp (name, "circular_progress") == 0)
{
component = grub_gui_circular_progress_new ();
}
else if (grub_strcmp (name, "boot_menu") == 0)
{
component = grub_gui_list_new ();
}
else
{
/* Unknown type. */
grub_error (GRUB_ERR_IO, "%s:%d:%d unknown object type `%s'",
p->filename, p->line_num, p->col_num, name);
goto cleanup;
}
if (! component)
goto cleanup;
/* Inform the component about the theme so it can find its resources. */
component->ops->set_property (component, "theme_dir", p->theme_dir);
component->ops->set_property (component, "theme_path", p->filename);
/* Add the component as a child of PARENT. */
bounds.x = 0;
bounds.y = 0;
bounds.width = -1;
bounds.height = -1;
component->ops->set_bounds (component, &bounds);
parent->ops->add (parent, component);
skip_whitespace (p);
if (read_char (p) != '{')
{
grub_error (GRUB_ERR_IO,
"%s:%d:%d expected `{' after object type name `%s'",
p->filename, p->line_num, p->col_num, name);
goto cleanup;
}
while (has_more (p))
{
skip_whitespace (p);
/* Check whether the end has been encountered. */
if (peek_char (p) == '}')
{
/* Skip the closing brace. */
read_char (p);
break;
}
if (peek_char (p) == '#')
{
/* Skip comments. */
advance_to_next_line (p);
continue;
}
if (peek_char (p) == '+')
{
/* Skip the '+'. */
read_char (p);
/* Check whether this component is a container. */
if (component->ops->is_instance (component, "container"))
{
/* Read the sub-object recursively and add it as a child. */
if (read_object (p, (grub_gui_container_t) component) != 0)
goto cleanup;
/* After reading the sub-object, resume parsing, expecting
another property assignment or sub-object definition. */
continue;
}
else
{
grub_error (GRUB_ERR_IO,
"%s:%d:%d attempted to add object to non-container",
p->filename, p->line_num, p->col_num);
goto cleanup;
}
}
char *property;
property = read_identifier (p);
if (! property)
{
grub_error (GRUB_ERR_IO, "%s:%d:%d identifier expected in theme file",
p->filename, p->line_num, p->col_num);
goto cleanup;
}
skip_whitespace (p);
if (read_char (p) != '=')
{
grub_error (GRUB_ERR_IO,
"%s:%d:%d expected `=' after property name `%s'",
p->filename, p->line_num, p->col_num, property);
grub_free (property);
goto cleanup;
}
skip_whitespace (p);
char *value;
value = read_expression (p);
if (! value)
{
grub_free (property);
goto cleanup;
}
/* Handle the property value. */
if (grub_strcmp (property, "left") == 0)
parse_proportional_spec (value, &component->x, &component->xfrac);
else if (grub_strcmp (property, "top") == 0)
parse_proportional_spec (value, &component->y, &component->yfrac);
else if (grub_strcmp (property, "width") == 0)
parse_proportional_spec (value, &component->w, &component->wfrac);
else if (grub_strcmp (property, "height") == 0)
parse_proportional_spec (value, &component->h, &component->hfrac);
else
/* General property handling. */
component->ops->set_property (component, property, value);
grub_free (value);
grub_free (property);
if (grub_errno != GRUB_ERR_NONE)
goto cleanup;
}
cleanup:
grub_free (name);
return grub_errno;
}
static grub_err_t
read_property (struct parsebuf *p)
{
char *name;
/* Read the property name. */
name = read_identifier (p);
if (! name)
{
advance_to_next_line (p);
return grub_errno;
}
/* Skip whitespace before separator. */
skip_whitespace (p);
/* Read separator. */
if (read_char (p) != ':')
{
grub_error (GRUB_ERR_IO,
"%s:%d:%d missing separator after property name `%s'",
p->filename, p->line_num, p->col_num, name);
goto done;
}
/* Skip whitespace after separator. */
skip_whitespace (p);
/* Get the value based on its type. */
if (peek_char (p) == '"')
{
/* String value (e.g., '"My string"'). */
char *value = read_expression (p);
if (! value)
{
grub_error (GRUB_ERR_IO, "%s:%d:%d missing property value",
p->filename, p->line_num, p->col_num);
goto done;
}
/* If theme_set_string results in an error, grub_errno will be returned
below. */
theme_set_string (p->view, name, value, p->theme_dir,
p->filename, p->line_num, p->col_num);
grub_free (value);
}
else
{
grub_error (GRUB_ERR_IO,
"%s:%d:%d property value invalid; "
"enclose literal values in quotes (\")",
p->filename, p->line_num, p->col_num);
grub_printf("File: %s\nLine:%d Column:%d\n"
"property value invalid; enclose literal values in quotes (\")\n\n",
p->filename, p->line_num, p->col_num);
goto done;
}
done:
grub_free (name);
return grub_errno;
}
extern int g_menu_update_mode;
/* Set properties on the view based on settings from the specified
theme file. */
grub_err_t
grub_gfxmenu_view_load_theme (grub_gfxmenu_view_t view, const char *theme_path)
{
int flag = 0;
grub_file_t file;
struct parsebuf p;
p.view = view;
p.theme_dir = grub_get_dirname (theme_path);
file = grub_file_open (theme_path, GRUB_FILE_TYPE_THEME);
if (! file)
{
grub_free (p.theme_dir);
return grub_errno;
}
p.len = grub_file_size (file);
p.buf = grub_malloc (p.len + 8192);
p.pos = 0;
p.line_num = 1;
p.col_num = 1;
p.filename = theme_path;
if (! p.buf)
{
grub_file_close (file);
grub_free (p.theme_dir);
return grub_errno;
}
if (grub_file_read (file, p.buf, p.len) != p.len)
{
grub_free (p.buf);
grub_file_close (file);
grub_free (p.theme_dir);
return grub_errno;
}
{
const char *checkret = grub_env_get("VTOY_CHKDEV_RESULT_STRING");
if (checkret == NULL || checkret[0] != '0')
{
p.len += grub_snprintf(p.buf + p.len, 4096, "\n+ hbox{\n left = 1%%\n top = 90%%\n"
" + label {text = \"[Unofficial Ventoy]\" color = \"red\" align = \"left\"}\n"
"}\n");
}
}
if (view->canvas)
view->canvas->component.ops->destroy (view->canvas);
view->canvas = grub_gui_canvas_new ();
if (!view->canvas)
goto fail;
((grub_gui_component_t) view->canvas)
->ops->set_bounds ((grub_gui_component_t) view->canvas,
&view->screen);
parse:
while (has_more (&p))
{
/* Skip comments (lines beginning with #). */
if (peek_char (&p) == '#')
{
advance_to_next_line (&p);
continue;
}
/* Find the first non-whitespace character. */
skip_whitespace (&p);
/* Handle the content. */
if (peek_char (&p) == '+')
{
/* Skip the '+'. */
read_char (&p);
read_object (&p, view->canvas);
}
else
{
read_property (&p);
}
if (grub_errno != GRUB_ERR_NONE)
goto fail;
}
if (flag == 0)
{
const char *tip = grub_env_get("VTOY_MENU_TIP_ENABLE");
if (tip && tip[0] == '1')
{
char tmpmsg[512];
grub_memset(tmpmsg, 'w', 500);
tmpmsg[500] = 0;
g_menu_update_mode = 1;
p.len += grub_snprintf(p.buf + p.len, 4096,
"\n+ vbox{\n left = %s\n top = %s\n"
"+ label { id=\"VTOY_MENU_TIP_1\" text = \"%s\" color = \"%s\" align = \"%s\"}\n"
"+ label { id=\"VTOY_MENU_TIP_2\" text = \"%s\" color = \"%s\" align = \"%s\"}\n"
"}\n",
grub_env_get("VTOY_TIP_LEFT"),
grub_env_get("VTOY_TIP_TOP"),
tmpmsg,
grub_env_get("VTOY_TIP_COLOR"),
grub_env_get("VTOY_TIP_ALIGN"),
tmpmsg,
grub_env_get("VTOY_TIP_COLOR"),
grub_env_get("VTOY_TIP_ALIGN")
);
flag = 1;
goto parse;
}
}
/* Set the new theme path. */
grub_free (view->theme_path);
view->theme_path = grub_strdup (theme_path);
goto cleanup;
fail:
if (view->canvas)
{
view->canvas->component.ops->destroy (view->canvas);
view->canvas = 0;
}
cleanup:
grub_free (p.buf);
grub_file_close (file);
grub_free (p.theme_dir);
return grub_errno;
}
@@ -0,0 +1,694 @@
/* view.c - Graphical menu interface MVC view. */
/*
* 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/file.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/err.h>
#include <grub/dl.h>
#include <grub/normal.h>
#include <grub/video.h>
#include <grub/gfxterm.h>
#include <grub/bitmap.h>
#include <grub/bitmap_scale.h>
#include <grub/term.h>
#include <grub/gfxwidgets.h>
#include <grub/time.h>
#include <grub/menu.h>
#include <grub/menu_viewer.h>
#include <grub/gfxmenu_view.h>
#include <grub/gui_string_util.h>
#include <grub/icon_manager.h>
#include <grub/i18n.h>
#include <grub/charset.h>
static void
init_terminal (grub_gfxmenu_view_t view);
static void
init_background (grub_gfxmenu_view_t view);
static grub_gfxmenu_view_t term_view;
/* Create a new view object, loading the theme specified by THEME_PATH and
associating MODEL with the view. */
grub_gfxmenu_view_t
grub_gfxmenu_view_new (const char *theme_path,
int width, int height)
{
grub_gfxmenu_view_t view;
grub_font_t default_font;
grub_video_rgba_color_t default_fg_color;
grub_video_rgba_color_t default_bg_color;
view = grub_malloc (sizeof (*view));
if (! view)
return 0;
while (grub_gfxmenu_timeout_notifications)
{
struct grub_gfxmenu_timeout_notify *p;
p = grub_gfxmenu_timeout_notifications;
grub_gfxmenu_timeout_notifications = grub_gfxmenu_timeout_notifications->next;
grub_free (p);
}
view->screen.x = 0;
view->screen.y = 0;
view->screen.width = width;
view->screen.height = height;
view->need_to_check_sanity = 1;
view->terminal_border = 3;
view->terminal_rect.width = view->screen.width * 7 / 10;
view->terminal_rect.height = view->screen.height * 7 / 10;
view->terminal_rect.x = view->screen.x + (view->screen.width
- view->terminal_rect.width) / 2;
view->terminal_rect.y = view->screen.y + (view->screen.height
- view->terminal_rect.height) / 2;
default_font = grub_font_get ("Unknown Regular 16");
default_fg_color = grub_video_rgba_color_rgb (0, 0, 0);
default_bg_color = grub_video_rgba_color_rgb (255, 255, 255);
view->canvas = 0;
view->title_font = default_font;
view->message_font = default_font;
view->terminal_font_name = grub_strdup ("Fixed 10");
view->title_color = default_fg_color;
view->message_color = default_bg_color;
view->message_bg_color = default_fg_color;
view->raw_desktop_image = 0;
view->scaled_desktop_image = 0;
view->desktop_image_scale_method = GRUB_VIDEO_BITMAP_SELECTION_METHOD_STRETCH;
view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_CENTER;
view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_CENTER;
view->desktop_color = default_bg_color;
view->terminal_box = grub_gfxmenu_create_box (0, 0);
view->title_text = grub_strdup (_("GRUB Boot Menu"));
view->progress_message_text = 0;
view->theme_path = 0;
/* Set the timeout bar's frame. */
view->progress_message_frame.width = view->screen.width * 4 / 5;
view->progress_message_frame.height = 50;
view->progress_message_frame.x = view->screen.x
+ (view->screen.width - view->progress_message_frame.width) / 2;
view->progress_message_frame.y = view->screen.y
+ view->screen.height - 90 - 20 - view->progress_message_frame.height;
if (grub_gfxmenu_view_load_theme (view, theme_path) != 0)
{
grub_gfxmenu_view_destroy (view);
return 0;
}
return view;
}
/* Destroy the view object. All used memory is freed. */
void
grub_gfxmenu_view_destroy (grub_gfxmenu_view_t view)
{
if (!view)
return;
while (grub_gfxmenu_timeout_notifications)
{
struct grub_gfxmenu_timeout_notify *p;
p = grub_gfxmenu_timeout_notifications;
grub_gfxmenu_timeout_notifications = grub_gfxmenu_timeout_notifications->next;
grub_free (p);
}
grub_video_bitmap_destroy (view->raw_desktop_image);
grub_video_bitmap_destroy (view->scaled_desktop_image);
if (view->terminal_box)
view->terminal_box->destroy (view->terminal_box);
grub_free (view->terminal_font_name);
grub_free (view->title_text);
grub_free (view->progress_message_text);
grub_free (view->theme_path);
if (view->menu_title_offset)
grub_free (view->menu_title_offset);
if (view->canvas)
view->canvas->component.ops->destroy (view->canvas);
grub_free (view);
}
static void
redraw_background (grub_gfxmenu_view_t view,
const grub_video_rect_t *bounds)
{
if (view->scaled_desktop_image)
{
struct grub_video_bitmap *img = view->scaled_desktop_image;
grub_video_blit_bitmap (img, GRUB_VIDEO_BLIT_REPLACE,
bounds->x, bounds->y,
bounds->x - view->screen.x,
bounds->y - view->screen.y,
bounds->width, bounds->height);
}
else
{
grub_video_fill_rect (grub_video_map_rgba_color (view->desktop_color),
bounds->x, bounds->y,
bounds->width, bounds->height);
}
}
static void
draw_title (grub_gfxmenu_view_t view)
{
if (! view->title_text)
return;
/* Center the title. */
int title_width = grub_font_get_string_width (view->title_font,
view->title_text);
int x = (view->screen.width - title_width) / 2;
int y = 40 + grub_font_get_ascent (view->title_font);
grub_font_draw_string (view->title_text,
view->title_font,
grub_video_map_rgba_color (view->title_color),
x, y);
}
struct progress_value_data
{
int visible;
int start;
int end;
int value;
};
struct grub_gfxmenu_timeout_notify *grub_gfxmenu_timeout_notifications;
static void
update_timeouts (int visible, int start, int value, int end)
{
struct grub_gfxmenu_timeout_notify *cur;
for (cur = grub_gfxmenu_timeout_notifications; cur; cur = cur->next)
cur->set_state (cur->self, visible, start, value, end);
}
static void
redraw_timeouts (struct grub_gfxmenu_view *view)
{
struct grub_gfxmenu_timeout_notify *cur;
for (cur = grub_gfxmenu_timeout_notifications; cur; cur = cur->next)
{
grub_video_rect_t bounds;
cur->self->ops->get_bounds (cur->self, &bounds);
grub_video_set_area_status (GRUB_VIDEO_AREA_ENABLED);
grub_gfxmenu_view_redraw (view, &bounds);
}
}
void
grub_gfxmenu_print_timeout (int timeout, void *data)
{
struct grub_gfxmenu_view *view = data;
if (view->first_timeout == -1)
view->first_timeout = timeout;
update_timeouts (1, -view->first_timeout, -timeout, 0);
redraw_timeouts (view);
grub_video_swap_buffers ();
if (view->double_repaint)
redraw_timeouts (view);
}
void
grub_gfxmenu_clear_timeout (void *data)
{
struct grub_gfxmenu_view *view = data;
update_timeouts (0, 1, 0, 0);
redraw_timeouts (view);
grub_video_swap_buffers ();
if (view->double_repaint)
redraw_timeouts (view);
}
static void
update_menu_visit (grub_gui_component_t component,
void *userdata)
{
grub_gfxmenu_view_t view;
view = userdata;
if (component->ops->is_instance (component, "list"))
{
grub_gui_list_t list = (grub_gui_list_t) component;
list->ops->set_view_info (list, view);
}
}
/* Update any boot menu components with the current menu model and
theme path. */
static void
update_menu_components (grub_gfxmenu_view_t view)
{
grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas,
update_menu_visit, view);
}
static void
refresh_menu_visit (grub_gui_component_t component,
void *userdata)
{
grub_gfxmenu_view_t view;
view = userdata;
if (component->ops->is_instance (component, "list"))
{
grub_gui_list_t list = (grub_gui_list_t) component;
list->ops->refresh_list (list, view);
}
}
/* Refresh list information (useful for submenus) */
static void
refresh_menu_components (grub_gfxmenu_view_t view)
{
grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas,
refresh_menu_visit, view);
}
static void
draw_message (grub_gfxmenu_view_t view)
{
char *text = view->progress_message_text;
grub_video_rect_t f = view->progress_message_frame;
if (! text)
return;
grub_font_t font = view->message_font;
grub_video_color_t color = grub_video_map_rgba_color (view->message_color);
/* Border. */
grub_video_fill_rect (color,
f.x-1, f.y-1, f.width+2, f.height+2);
/* Fill. */
grub_video_fill_rect (grub_video_map_rgba_color (view->message_bg_color),
f.x, f.y, f.width, f.height);
/* Center the text. */
int text_width = grub_font_get_string_width (font, text);
int x = f.x + (f.width - text_width) / 2;
int y = (f.y + (f.height - grub_font_get_descent (font)) / 2
+ grub_font_get_ascent (font) / 2);
grub_font_draw_string (text, font, color, x, y);
}
void
grub_gfxmenu_view_redraw (grub_gfxmenu_view_t view,
const grub_video_rect_t *region)
{
if (grub_video_have_common_points (&view->terminal_rect, region))
grub_gfxterm_schedule_repaint ();
grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
grub_video_area_status_t area_status;
grub_video_get_area_status (&area_status);
if (area_status == GRUB_VIDEO_AREA_ENABLED)
grub_video_set_region (region->x, region->y,
region->width, region->height);
redraw_background (view, region);
if (view->canvas)
view->canvas->component.ops->paint (view->canvas, region);
draw_title (view);
if (grub_video_have_common_points (&view->progress_message_frame, region))
draw_message (view);
if (area_status == GRUB_VIDEO_AREA_ENABLED)
grub_video_set_area_status (GRUB_VIDEO_AREA_ENABLED);
}
void
grub_gfxmenu_view_draw (grub_gfxmenu_view_t view)
{
init_terminal (view);
init_background (view);
/* Clear the screen; there may be garbage left over in video memory. */
grub_video_fill_rect (grub_video_map_rgb (0, 0, 0),
view->screen.x, view->screen.y,
view->screen.width, view->screen.height);
grub_video_swap_buffers ();
if (view->double_repaint)
grub_video_fill_rect (grub_video_map_rgb (0, 0, 0),
view->screen.x, view->screen.y,
view->screen.width, view->screen.height);
refresh_menu_components (view);
update_menu_components (view);
grub_video_set_area_status (GRUB_VIDEO_AREA_DISABLED);
grub_gfxmenu_view_redraw (view, &view->screen);
grub_video_swap_buffers ();
if (view->double_repaint)
{
grub_video_set_area_status (GRUB_VIDEO_AREA_DISABLED);
grub_gfxmenu_view_redraw (view, &view->screen);
}
}
static void
redraw_menu_visit (grub_gui_component_t component,
void *userdata)
{
grub_gfxmenu_view_t view;
view = userdata;
if (component->ops->is_instance (component, "list"))
{
grub_video_rect_t bounds;
component->ops->get_bounds (component, &bounds);
grub_video_set_area_status (GRUB_VIDEO_AREA_ENABLED);
grub_gfxmenu_view_redraw (view, &bounds);
}
}
extern int g_menu_update_mode;
static void grub_gfxmenu_update_all(grub_gfxmenu_view_t view)
{
grub_video_set_area_status(GRUB_VIDEO_AREA_DISABLED);
grub_gfxmenu_view_redraw(view, &view->screen);
}
void
grub_gfxmenu_redraw_menu (grub_gfxmenu_view_t view)
{
update_menu_components (view);
if (g_menu_update_mode)
grub_gfxmenu_update_all(view);
else
grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas,
redraw_menu_visit, view);
grub_video_swap_buffers ();
if (view->double_repaint)
{
if (g_menu_update_mode)
grub_gfxmenu_update_all(view);
else
grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas,
redraw_menu_visit, view);
}
}
void
grub_gfxmenu_set_chosen_entry (int entry, void *data)
{
grub_gfxmenu_view_t view = data;
view->selected = entry;
grub_gfxmenu_redraw_menu (view);
}
void
grub_gfxmenu_scroll_chosen_entry (void *data, int diren)
{
grub_gfxmenu_view_t view = data;
const char *item_title;
int off;
int max;
if (!view->menu->size)
return;
item_title = grub_menu_get_entry (view->menu, view->selected)->title;
off = view->menu_title_offset[view->selected] + diren;
max = grub_utf8_get_num_code (item_title, grub_strlen(item_title));
if (diren == 1000000)
off = (max >= 20) ? (max - 20) : 0;
else if (off < 0)
off = 0;
else if (off > max)
off = max;
view->menu_title_offset[view->selected] = off;
grub_gfxmenu_redraw_menu (view);
}
static void
grub_gfxmenu_draw_terminal_box (void)
{
grub_gfxmenu_box_t term_box;
term_box = term_view->terminal_box;
if (!term_box)
return;
grub_video_set_area_status (GRUB_VIDEO_AREA_DISABLED);
term_box->set_content_size (term_box, term_view->terminal_rect.width,
term_view->terminal_rect.height);
term_box->draw (term_box,
term_view->terminal_rect.x - term_box->get_left_pad (term_box),
term_view->terminal_rect.y - term_box->get_top_pad (term_box));
}
static void
get_min_terminal (grub_font_t terminal_font,
unsigned int border_width,
unsigned int *min_terminal_width,
unsigned int *min_terminal_height)
{
struct grub_font_glyph *glyph;
glyph = grub_font_get_glyph (terminal_font, 'M');
*min_terminal_width = (glyph? glyph->device_width : 8) * 80
+ 2 * border_width;
*min_terminal_height = grub_font_get_max_char_height (terminal_font) * 24
+ 2 * border_width;
}
static void
terminal_sanity_check (grub_gfxmenu_view_t view)
{
if (!view->need_to_check_sanity)
return;
/* terminal_font was checked before in the init_terminal function. */
grub_font_t terminal_font = grub_font_get (view->terminal_font_name);
/* Non-negative numbers below. */
int scr_x = view->screen.x;
int scr_y = view->screen.y;
int scr_width = view->screen.width;
int scr_height = view->screen.height;
int term_x = view->terminal_rect.x;
int term_y = view->terminal_rect.y;
int term_width = view->terminal_rect.width;
int term_height = view->terminal_rect.height;
/* Check that border_width isn't too big. */
unsigned int border_width = view->terminal_border;
unsigned int min_terminal_width;
unsigned int min_terminal_height;
get_min_terminal (terminal_font, border_width,
&min_terminal_width, &min_terminal_height);
if (border_width > 3 && ((int) min_terminal_width >= scr_width
|| (int) min_terminal_height >= scr_height))
{
border_width = 3;
get_min_terminal (terminal_font, border_width,
&min_terminal_width, &min_terminal_height);
}
/* Sanity checks. */
if (term_width > scr_width)
term_width = scr_width;
if (term_height > scr_height)
term_height = scr_height;
if (scr_width <= (int) min_terminal_width
|| scr_height <= (int) min_terminal_height)
{
/* The screen resulution is too low. Use all space, except a small border
to show the user, that it is a window. Then center the window. */
term_width = scr_width - 6 * border_width;
term_height = scr_height - 6 * border_width;
term_x = scr_x + (scr_width - term_width) / 2;
term_y = scr_y + (scr_height - term_height) / 2;
}
else if (term_width < (int) min_terminal_width
|| term_height < (int) min_terminal_height)
{
/* The screen resolution is big enough. Make sure, that terminal screen
dimensions aren't less than minimal values. Then center the window. */
term_width = (int) min_terminal_width;
term_height = (int) min_terminal_height;
term_x = scr_x + (scr_width - term_width) / 2;
term_y = scr_y + (scr_height - term_height) / 2;
}
/* At this point w and h are satisfying. */
if (term_x + term_width > scr_width)
term_x = scr_width - term_width;
if (term_y + term_height > scr_height)
term_y = scr_height - term_height;
/* Write down corrected data. */
view->terminal_rect.x = (unsigned int) term_x;
view->terminal_rect.y = (unsigned int) term_y;
view->terminal_rect.width = (unsigned int) term_width;
view->terminal_rect.height = (unsigned int) term_height;
view->terminal_border = border_width;
view->need_to_check_sanity = 0;
}
static void
init_terminal (grub_gfxmenu_view_t view)
{
grub_font_t terminal_font;
terminal_font = grub_font_get (view->terminal_font_name);
if (!terminal_font)
{
grub_error (GRUB_ERR_BAD_FONT, "no font loaded");
return;
}
/* Check that terminal window size and position are sane. */
terminal_sanity_check (view);
term_view = view;
/* Note: currently there is no API for changing the gfxterm font
on the fly, so whatever font the initially loaded theme specifies
will be permanent. */
grub_gfxterm_set_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY,
view->terminal_rect.x,
view->terminal_rect.y,
view->terminal_rect.width,
view->terminal_rect.height,
view->double_repaint,
terminal_font,
view->terminal_border);
grub_gfxterm_decorator_hook = grub_gfxmenu_draw_terminal_box;
}
static void
init_background (grub_gfxmenu_view_t view)
{
if (view->scaled_desktop_image || (!view->raw_desktop_image))
return;
struct grub_video_bitmap *scaled_bitmap;
if (view->desktop_image_scale_method ==
GRUB_VIDEO_BITMAP_SELECTION_METHOD_STRETCH)
grub_video_bitmap_create_scaled (&scaled_bitmap,
view->screen.width,
view->screen.height,
view->raw_desktop_image,
GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
else
grub_video_bitmap_scale_proportional (&scaled_bitmap,
view->screen.width,
view->screen.height,
view->raw_desktop_image,
GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST,
view->desktop_image_scale_method,
view->desktop_image_v_align,
view->desktop_image_h_align);
if (! scaled_bitmap)
return;
view->scaled_desktop_image = scaled_bitmap;
}
/* FIXME: previously notifications were displayed in special case.
Is it necessary?
*/
#if 0
/* Sets MESSAGE as the progress message for the view.
MESSAGE can be 0, in which case no message is displayed. */
static void
set_progress_message (grub_gfxmenu_view_t view, const char *message)
{
grub_free (view->progress_message_text);
if (message)
view->progress_message_text = grub_strdup (message);
else
view->progress_message_text = 0;
}
static void
notify_booting (grub_menu_entry_t entry, void *userdata)
{
grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata;
char *s = grub_malloc (100 + grub_strlen (entry->title));
if (!s)
return;
grub_sprintf (s, "Booting '%s'", entry->title);
set_progress_message (view, s);
grub_free (s);
grub_gfxmenu_view_redraw (view, &view->progress_message_frame);
grub_video_swap_buffers ();
if (view->double_repaint)
grub_gfxmenu_view_redraw (view, &view->progress_message_frame);
}
static void
notify_fallback (grub_menu_entry_t entry, void *userdata)
{
grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata;
char *s = grub_malloc (100 + grub_strlen (entry->title));
if (!s)
return;
grub_sprintf (s, "Falling back to '%s'", entry->title);
set_progress_message (view, s);
grub_free (s);
grub_gfxmenu_view_redraw (view, &view->progress_message_frame);
grub_video_swap_buffers ();
if (view->double_repaint)
grub_gfxmenu_view_redraw (view, &view->progress_message_frame);
}
static void
notify_execution_failure (void *userdata __attribute__ ((unused)))
{
}
static struct grub_menu_execute_callback execute_callback =
{
.notify_booting = notify_booting,
.notify_fallback = notify_fallback,
.notify_failure = notify_execution_failure
};
#endif
@@ -0,0 +1,195 @@
/* corecmd.c - critical commands which are registered in kernel */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 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/dl.h>
#include <grub/err.h>
#include <grub/env.h>
#include <grub/misc.h>
#include <grub/term.h>
#include <grub/file.h>
#include <grub/device.h>
#include <grub/command.h>
#include <grub/i18n.h>
/* set ENVVAR=VALUE */
static grub_err_t
grub_core_cmd_set (struct grub_command *cmd __attribute__ ((unused)),
int argc, char *argv[])
{
char *var;
char *val;
if (argc < 1)
{
struct grub_env_var *env;
FOR_SORTED_ENV (env)
grub_printf ("%s=%s\n", env->name, grub_env_get (env->name));
return 0;
}
var = argv[0];
val = grub_strchr (var, '=');
if (! val)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "not an assignment");
val[0] = 0;
grub_env_set (var, val + 1);
val[0] = '=';
return 0;
}
static grub_err_t
grub_core_cmd_unset (struct grub_command *cmd __attribute__ ((unused)),
int argc, char *argv[])
{
if (argc < 1)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("one argument expected"));
grub_env_unset (argv[0]);
return 0;
}
/* insmod MODULE */
static grub_err_t
grub_core_cmd_insmod (struct grub_command *cmd __attribute__ ((unused)),
int argc, char *argv[])
{
grub_dl_t mod;
if (argc == 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
/* For simple, just disable insmod when SecureBoot is enabled. */
if (g_sys_sb && g_sb_policy == VTOY_SB_POLICY_CHECK)
{
return grub_error (GRUB_ERR_BAD_SIGNATURE, "Cannot insmod when SecureBoot is enabled and Policy is check.");
}
if (argv[0][0] == '/' || argv[0][0] == '(' || argv[0][0] == '+')
mod = grub_dl_load_file (argv[0]);
else
mod = grub_dl_load (argv[0]);
if (mod)
grub_dl_ref (mod);
return 0;
}
static int
grub_mini_print_devices (const char *name, void *data __attribute__ ((unused)))
{
grub_printf ("(%s) ", name);
return 0;
}
static int
grub_mini_print_files (const char *filename,
const struct grub_dirhook_info *info,
void *data __attribute__ ((unused)))
{
grub_printf ("%s%s ", filename, info->dir ? "/" : "");
return 0;
}
/* ls [ARG] */
static grub_err_t
grub_core_cmd_ls (struct grub_command *cmd __attribute__ ((unused)),
int argc, char *argv[])
{
if (argc < 1)
{
grub_device_iterate (grub_mini_print_devices, NULL);
grub_xputs ("\n");
grub_refresh ();
}
else
{
char *device_name;
grub_device_t dev = 0;
grub_fs_t fs;
char *path;
device_name = grub_file_get_device_name (argv[0]);
if (grub_errno)
goto fail;
dev = grub_device_open (device_name);
if (! dev)
goto fail;
fs = grub_fs_probe (dev);
path = grub_strchr (argv[0], ')');
if (! path)
path = argv[0];
else
path++;
if (! *path && ! device_name)
{
grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid argument");
goto fail;
}
if (! *path)
{
if (grub_errno == GRUB_ERR_UNKNOWN_FS)
grub_errno = GRUB_ERR_NONE;
grub_printf ("(%s): Filesystem is %s.\n",
device_name, fs ? fs->name : "unknown");
}
else if (fs)
{
(fs->fs_dir) (dev, path, grub_mini_print_files, NULL);
grub_xputs ("\n");
grub_refresh ();
}
fail:
if (dev)
grub_device_close (dev);
grub_free (device_name);
}
return grub_errno;
}
void
grub_register_core_commands (void)
{
grub_command_t cmd;
cmd = grub_register_command ("set", grub_core_cmd_set,
N_("[ENVVAR=VALUE]"),
N_("Set an environment variable."));
if (cmd)
cmd->flags |= GRUB_COMMAND_FLAG_EXTRACTOR;
grub_register_command ("unset", grub_core_cmd_unset,
N_("ENVVAR"),
N_("Remove an environment variable."));
grub_register_command ("ls", grub_core_cmd_ls,
N_("[ARG]"), N_("List devices or files."));
grub_register_command ("insmod", grub_core_cmd_insmod,
N_("MODULE"), N_("Insert a module."));
}
@@ -0,0 +1,646 @@
/*
* 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 10
/* 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)
{
grub_uint64_t sizeshift;
ventoy_img_chunk *last_chunk = NULL;
ventoy_img_chunk *new_chunk = NULL;
ventoy_img_chunk_list *chunk_list = (ventoy_img_chunk_list *)chunklist;
sizeshift = (size >> log_sector_size);
if (sizeshift == 0)
{
sizeshift = 1;
}
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 + sizeshift - 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 += sizeshift;
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 + sizeshift - 1;
chunk_list->cur_chunk++;
return 0;
}
grub_err_t grub_disk_blocklist_read2(grub_disk_t disk, grub_uint64_t sector,
grub_uint64_t size, char *buf)
{
ventoy_img_chunk_list *chunk_list = (ventoy_img_chunk_list *)(disk->read_hook_data);
if (buf < chunk_list->buf || buf >= chunk_list->buf + VTOY_CHUNK_BUF_SIZE)
{
return 2;
}
if ((chunk_list->buf + chunk_list->last_off) != buf)
{
chunk_list->err_code = VTOY_CHUNK_ERR_NOT_FLAT;
return 0;
}
if (chunk_list->last_off + size > VTOY_CHUNK_BUF_SIZE)
{
chunk_list->err_code = VTOY_CHUNK_ERR_OVER_FLOW;
return 0;
}
chunk_list->last_off += (grub_uint32_t)size;
if (chunk_list->last_off == VTOY_CHUNK_BUF_SIZE)
{
chunk_list->last_off = 0;
}
return grub_disk_blocklist_read(chunk_list, sector, size, disk->log_sector_size);
}
/* 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)(void *)grub_disk_blocklist_read)
{
return grub_disk_blocklist_read((ventoy_img_chunk_list *)disk->read_hook_data, sector, size, disk->log_sector_size);
}
else if (disk->read_hook == (grub_disk_read_hook_t)(void *)grub_disk_blocklist_read2)
{
grub_err_t rv = grub_disk_blocklist_read2(disk, sector, size, (char *)buf);
if (rv != 2)
{
return rv;
}
}
/* 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;
}
@@ -0,0 +1,983 @@
/* 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;
}
void * grub_efi_allocate_chain_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_LOADER_DATA, pages, &address);
if (status != GRUB_EFI_SUCCESS)
{
return NULL;
}
return (void *)(unsigned long)address;
}
@@ -0,0 +1,702 @@
/* mm.c - generic EFI memory management */
/*
* 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/>.
*/
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/efi/api.h>
#include <grub/efi/efi.h>
#include <grub/cpu/efi/memory.h>
#if defined (__i386__) || defined (__x86_64__)
#include <grub/pci.h>
#endif
#define NEXT_MEMORY_DESCRIPTOR(desc, size) \
((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size)))
#define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12)
#define BYTES_TO_PAGES_DOWN(bytes) ((bytes) >> 12)
#define PAGES_TO_BYTES(pages) ((pages) << 12)
/* The size of a memory map obtained from the firmware. This must be
a multiplier of 4KB. */
#define MEMORY_MAP_SIZE 0x3000
/* The minimum and maximum heap size for GRUB itself. */
#define MIN_HEAP_SIZE 0x100000
#define MAX_HEAP_SIZE (1600 * 0x100000)
static void *finish_mmap_buf = 0;
static grub_efi_uintn_t finish_mmap_size = 0;
static grub_efi_uintn_t finish_key = 0;
static grub_efi_uintn_t finish_desc_size;
static grub_efi_uint32_t finish_desc_version;
int grub_efi_is_finished = 0;
/* 160MB 160 * 1024 * 1024 / 4096 */
#define VTOY_CHAIN_MIN_PAGES (160 * 256)
static grub_efi_uint64_t g_total_pages;
static grub_efi_uint64_t g_org_required_pages;
static grub_efi_uint64_t g_new_required_pages;
/*
* We need to roll back EFI allocations on exit. Remember allocations that
* we'll free on exit.
*/
struct efi_allocation;
struct efi_allocation {
grub_efi_physical_address_t address;
grub_efi_uint64_t pages;
struct efi_allocation *next;
};
static struct efi_allocation *efi_allocated_memory;
static void
grub_efi_store_alloc (grub_efi_physical_address_t address,
grub_efi_uintn_t pages)
{
grub_efi_boot_services_t *b;
struct efi_allocation *alloc;
grub_efi_status_t status;
b = grub_efi_system_table->boot_services;
status = efi_call_3 (b->allocate_pool, GRUB_EFI_LOADER_DATA,
sizeof(*alloc), (void**)&alloc);
if (status == GRUB_EFI_SUCCESS)
{
alloc->next = efi_allocated_memory;
alloc->address = address;
alloc->pages = pages;
efi_allocated_memory = alloc;
}
else
grub_printf ("Could not malloc memory to remember EFI allocation. "
"Exiting GRUB won't free all memory.\n");
}
static void
grub_efi_drop_alloc (grub_efi_physical_address_t address,
grub_efi_uintn_t pages)
{
struct efi_allocation *ea, *eap;
grub_efi_boot_services_t *b;
b = grub_efi_system_table->boot_services;
for (eap = NULL, ea = efi_allocated_memory; ea; eap = ea, ea = ea->next)
{
if (ea->address != address || ea->pages != pages)
continue;
/* Remove the current entry from the list. */
if (eap)
eap->next = ea->next;
else
efi_allocated_memory = ea->next;
/* Then free the memory backing it. */
efi_call_1 (b->free_pool, ea);
/* And leave, we're done. */
break;
}
}
/* Allocate pages. Return the pointer to the first of allocated pages. */
void *
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)
{
grub_efi_status_t status;
grub_efi_boot_services_t *b;
/* Limit the memory access to less than 4GB for 32-bit platforms. */
if (address > GRUB_EFI_MAX_USABLE_ADDRESS)
return 0;
b = grub_efi_system_table->boot_services;
status = efi_call_4 (b->allocate_pages, alloctype, memtype, pages, &address);
if (status != GRUB_EFI_SUCCESS)
return 0;
if (address == 0)
{
/* Uggh, the address 0 was allocated... This is too annoying,
so reallocate another one. */
address = GRUB_EFI_MAX_USABLE_ADDRESS;
status = efi_call_4 (b->allocate_pages, alloctype, memtype, pages, &address);
grub_efi_free_pages (0, pages);
if (status != GRUB_EFI_SUCCESS)
return 0;
}
grub_efi_store_alloc (address, pages);
return (void *) ((grub_addr_t) address);
}
void *
grub_efi_allocate_any_pages (grub_efi_uintn_t pages)
{
return grub_efi_allocate_pages_real (GRUB_EFI_MAX_USABLE_ADDRESS,
pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS,
GRUB_EFI_LOADER_DATA);
}
void *
grub_efi_allocate_fixed (grub_efi_physical_address_t address,
grub_efi_uintn_t pages)
{
return grub_efi_allocate_pages_real (address, pages,
GRUB_EFI_ALLOCATE_ADDRESS,
GRUB_EFI_LOADER_DATA);
}
/* Free pages starting from ADDRESS. */
void
grub_efi_free_pages (grub_efi_physical_address_t address,
grub_efi_uintn_t pages)
{
grub_efi_boot_services_t *b;
b = grub_efi_system_table->boot_services;
efi_call_2 (b->free_pages, address, pages);
grub_efi_drop_alloc (address, pages);
}
#if defined (__i386__) || defined (__x86_64__)
/* Helper for stop_broadcom. */
static int
find_card (grub_pci_device_t dev, grub_pci_id_t pciid,
void *data __attribute__ ((unused)))
{
grub_pci_address_t addr;
grub_uint8_t cap;
grub_uint16_t pm_state;
if ((pciid & 0xffff) != GRUB_PCI_VENDOR_BROADCOM)
return 0;
addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
if (grub_pci_read (addr) >> 24 != GRUB_PCI_CLASS_NETWORK)
return 0;
cap = grub_pci_find_capability (dev, GRUB_PCI_CAP_POWER_MANAGEMENT);
if (!cap)
return 0;
addr = grub_pci_make_address (dev, cap + 4);
pm_state = grub_pci_read_word (addr);
pm_state = pm_state | 0x03;
grub_pci_write_word (addr, pm_state);
grub_pci_read_word (addr);
return 0;
}
static void
stop_broadcom (void)
{
grub_pci_iterate (find_card, NULL);
}
#endif
grub_err_t
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_efi_boot_services_t *b;
grub_efi_status_t status;
#if defined (__i386__) || defined (__x86_64__)
const grub_uint16_t apple[] = { 'A', 'p', 'p', 'l', 'e' };
int is_apple;
is_apple = (grub_memcmp (grub_efi_system_table->firmware_vendor,
apple, sizeof (apple)) == 0);
#endif
while (1)
{
if (grub_efi_get_memory_map (&finish_mmap_size, finish_mmap_buf, &finish_key,
&finish_desc_size, &finish_desc_version) < 0)
return grub_error (GRUB_ERR_IO, "couldn't retrieve memory map");
if (outbuf && *outbuf_size < finish_mmap_size)
return grub_error (GRUB_ERR_IO, "memory map buffer is too small");
finish_mmap_buf = grub_malloc (finish_mmap_size);
if (!finish_mmap_buf)
return grub_errno;
if (grub_efi_get_memory_map (&finish_mmap_size, finish_mmap_buf, &finish_key,
&finish_desc_size, &finish_desc_version) <= 0)
{
grub_free (finish_mmap_buf);
return grub_error (GRUB_ERR_IO, "couldn't retrieve memory map");
}
b = grub_efi_system_table->boot_services;
status = efi_call_2 (b->exit_boot_services, grub_efi_image_handle,
finish_key);
if (status == GRUB_EFI_SUCCESS)
break;
if (status != GRUB_EFI_INVALID_PARAMETER)
{
grub_free (finish_mmap_buf);
return grub_error (GRUB_ERR_IO, "couldn't terminate EFI services");
}
grub_free (finish_mmap_buf);
grub_printf ("Trying to terminate EFI services again\n");
}
grub_efi_is_finished = 1;
if (outbuf_size)
*outbuf_size = finish_mmap_size;
if (outbuf)
grub_memcpy (outbuf, finish_mmap_buf, finish_mmap_size);
if (map_key)
*map_key = finish_key;
if (efi_desc_size)
*efi_desc_size = finish_desc_size;
if (efi_desc_version)
*efi_desc_version = finish_desc_version;
#if defined (__i386__) || defined (__x86_64__)
if (is_apple)
stop_broadcom ();
#endif
return GRUB_ERR_NONE;
}
/*
* To obtain the UEFI memory map, we must pass a buffer of sufficient size
* to hold the entire map. This function returns a sane start value for
* buffer size.
*/
grub_efi_uintn_t
grub_efi_find_mmap_size (void)
{
grub_efi_uintn_t mmap_size = 0;
grub_efi_uintn_t desc_size;
if (grub_efi_get_memory_map (&mmap_size, NULL, NULL, &desc_size, 0) < 0)
{
grub_error (GRUB_ERR_IO, "cannot get EFI memory map size");
return 0;
}
/*
* Add an extra page, since UEFI can alter the memory map itself on
* callbacks or explicit calls, including console output.
*/
return ALIGN_UP (mmap_size + GRUB_EFI_PAGE_SIZE, GRUB_EFI_PAGE_SIZE);
}
/* Get the memory map as defined in the EFI spec. Return 1 if successful,
return 0 if partial, or return -1 if an error occurs. */
int
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)
{
grub_efi_status_t status;
grub_efi_boot_services_t *b;
grub_efi_uintn_t key;
grub_efi_uint32_t version;
grub_efi_uintn_t size;
if (grub_efi_is_finished)
{
int ret = 1;
if (*memory_map_size < finish_mmap_size)
{
grub_memcpy (memory_map, finish_mmap_buf, *memory_map_size);
ret = 0;
}
else
{
grub_memcpy (memory_map, finish_mmap_buf, finish_mmap_size);
ret = 1;
}
*memory_map_size = finish_mmap_size;
if (map_key)
*map_key = finish_key;
if (descriptor_size)
*descriptor_size = finish_desc_size;
if (descriptor_version)
*descriptor_version = finish_desc_version;
return ret;
}
/* Allow some parameters to be missing. */
if (! map_key)
map_key = &key;
if (! descriptor_version)
descriptor_version = &version;
if (! descriptor_size)
descriptor_size = &size;
b = grub_efi_system_table->boot_services;
status = efi_call_5 (b->get_memory_map, memory_map_size, memory_map, map_key,
descriptor_size, descriptor_version);
if (*descriptor_size == 0)
*descriptor_size = sizeof (grub_efi_memory_descriptor_t);
if (status == GRUB_EFI_SUCCESS)
return 1;
else if (status == GRUB_EFI_BUFFER_TOO_SMALL)
return 0;
else
return -1;
}
/* Sort the memory map in place. */
static void
sort_memory_map (grub_efi_memory_descriptor_t *memory_map,
grub_efi_uintn_t desc_size,
grub_efi_memory_descriptor_t *memory_map_end)
{
grub_efi_memory_descriptor_t *d1;
grub_efi_memory_descriptor_t *d2;
for (d1 = memory_map;
d1 < memory_map_end;
d1 = NEXT_MEMORY_DESCRIPTOR (d1, desc_size))
{
grub_efi_memory_descriptor_t *max_desc = d1;
for (d2 = NEXT_MEMORY_DESCRIPTOR (d1, desc_size);
d2 < memory_map_end;
d2 = NEXT_MEMORY_DESCRIPTOR (d2, desc_size))
{
if (max_desc->num_pages < d2->num_pages)
max_desc = d2;
}
if (max_desc != d1)
{
grub_efi_memory_descriptor_t tmp;
tmp = *d1;
*d1 = *max_desc;
*max_desc = tmp;
}
}
}
/* Filter the descriptors. GRUB needs only available memory. */
static grub_efi_memory_descriptor_t *
filter_memory_map (grub_efi_memory_descriptor_t *memory_map,
grub_efi_memory_descriptor_t *filtered_memory_map,
grub_efi_uintn_t desc_size,
grub_efi_memory_descriptor_t *memory_map_end)
{
grub_efi_memory_descriptor_t *desc;
grub_efi_memory_descriptor_t *filtered_desc;
for (desc = memory_map, filtered_desc = filtered_memory_map;
desc < memory_map_end;
desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
{
if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY
#if 1
&& desc->physical_start <= GRUB_EFI_MAX_USABLE_ADDRESS
#endif
&& desc->physical_start + PAGES_TO_BYTES (desc->num_pages) > 0x100000
&& desc->num_pages != 0)
{
grub_memcpy (filtered_desc, desc, desc_size);
/* Avoid less than 1MB, because some loaders seem to be confused. */
if (desc->physical_start < 0x100000)
{
desc->num_pages -= BYTES_TO_PAGES (0x100000
- desc->physical_start);
desc->physical_start = 0x100000;
}
#if 1
if (BYTES_TO_PAGES (filtered_desc->physical_start)
+ filtered_desc->num_pages
> BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_USABLE_ADDRESS))
filtered_desc->num_pages
= (BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_USABLE_ADDRESS)
- BYTES_TO_PAGES (filtered_desc->physical_start));
#endif
if (filtered_desc->num_pages == 0)
continue;
filtered_desc = NEXT_MEMORY_DESCRIPTOR (filtered_desc, desc_size);
}
}
return filtered_desc;
}
/* Return the total number of pages. */
static grub_efi_uint64_t
get_total_pages (grub_efi_memory_descriptor_t *memory_map,
grub_efi_uintn_t desc_size,
grub_efi_memory_descriptor_t *memory_map_end)
{
grub_efi_memory_descriptor_t *desc;
grub_efi_uint64_t total = 0;
for (desc = memory_map;
desc < memory_map_end;
desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
total += desc->num_pages;
return total;
}
/* Add memory regions. */
static void
add_memory_regions (grub_efi_memory_descriptor_t *memory_map,
grub_efi_uintn_t desc_size,
grub_efi_memory_descriptor_t *memory_map_end,
grub_efi_uint64_t required_pages)
{
grub_efi_memory_descriptor_t *desc;
for (desc = memory_map;
desc < memory_map_end;
desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
{
grub_efi_uint64_t pages;
grub_efi_physical_address_t start;
void *addr;
start = desc->physical_start;
pages = desc->num_pages;
if (pages > required_pages)
{
start += PAGES_TO_BYTES (pages - required_pages);
pages = required_pages;
}
addr = grub_efi_allocate_pages_real (start, pages,
GRUB_EFI_ALLOCATE_ADDRESS,
GRUB_EFI_LOADER_CODE);
if (! addr)
grub_fatal ("cannot allocate conventional memory %p with %u pages",
(void *) ((grub_addr_t) start),
(unsigned) pages);
grub_mm_init_region (addr, PAGES_TO_BYTES (pages));
required_pages -= pages;
if (required_pages == 0)
break;
}
if (required_pages > 0)
grub_fatal ("too little memory");
}
void
grub_efi_memory_fini (void)
{
/*
* Free all stale allocations. grub_efi_free_pages() will remove
* the found entry from the list and it will always find the first
* list entry (efi_allocated_memory is the list start). Hence we
* remove all entries from the list until none is left altogether.
*/
while (efi_allocated_memory)
grub_efi_free_pages (efi_allocated_memory->address,
efi_allocated_memory->pages);
}
#if 0
/* Print the memory map. */
static void
print_memory_map (grub_efi_memory_descriptor_t *memory_map,
grub_efi_uintn_t desc_size,
grub_efi_memory_descriptor_t *memory_map_end)
{
grub_efi_memory_descriptor_t *desc;
int i;
for (desc = memory_map, i = 0;
desc < memory_map_end;
desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size), i++)
{
grub_printf ("MD: t=%x, p=%llx, v=%llx, n=%llx, a=%llx\n",
desc->type, desc->physical_start, desc->virtual_start,
desc->num_pages, desc->attribute);
}
}
#endif
void
grub_efi_mm_init (void)
{
grub_efi_memory_descriptor_t *memory_map;
grub_efi_memory_descriptor_t *memory_map_end;
grub_efi_memory_descriptor_t *filtered_memory_map;
grub_efi_memory_descriptor_t *filtered_memory_map_end;
grub_efi_uintn_t map_size;
grub_efi_uintn_t desc_size;
grub_efi_uint64_t total_pages;
grub_efi_uint64_t required_pages;
int mm_status;
/* Prepare a memory region to store two memory maps. */
memory_map = grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));
if (! memory_map)
grub_fatal ("cannot allocate memory");
/* Obtain descriptors for available memory. */
map_size = MEMORY_MAP_SIZE;
mm_status = grub_efi_get_memory_map (&map_size, memory_map, 0, &desc_size, 0);
if (mm_status == 0)
{
grub_efi_free_pages
((grub_efi_physical_address_t) ((grub_addr_t) memory_map),
2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));
/* Freeing/allocating operations may increase memory map size. */
map_size += desc_size * 32;
memory_map = grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (map_size));
if (! memory_map)
grub_fatal ("cannot allocate memory");
mm_status = grub_efi_get_memory_map (&map_size, memory_map, 0,
&desc_size, 0);
}
if (mm_status < 0)
grub_fatal ("cannot get memory map");
memory_map_end = NEXT_MEMORY_DESCRIPTOR (memory_map, map_size);
filtered_memory_map = memory_map_end;
filtered_memory_map_end = filter_memory_map (memory_map, filtered_memory_map,
desc_size, memory_map_end);
/* By default, request a quarter of the available memory. */
total_pages = get_total_pages (filtered_memory_map, desc_size,
filtered_memory_map_end);
#if defined (__mips__) && (_MIPS_SIM == _ABI64)
required_pages = (total_pages > 4096) ? (total_pages - 4096) : (total_pages >> 1);
#else
required_pages = (total_pages >> 2);
#endif
if (required_pages < BYTES_TO_PAGES (MIN_HEAP_SIZE))
required_pages = BYTES_TO_PAGES (MIN_HEAP_SIZE);
else if (required_pages > BYTES_TO_PAGES (MAX_HEAP_SIZE))
required_pages = BYTES_TO_PAGES (MAX_HEAP_SIZE);
g_org_required_pages = required_pages;
if (((total_pages - required_pages) >> 2) < VTOY_CHAIN_MIN_PAGES)
{
if (total_pages > (VTOY_CHAIN_MIN_PAGES << 2))
{
g_new_required_pages = total_pages - (VTOY_CHAIN_MIN_PAGES << 2);
if (g_new_required_pages >= 8192)
{
required_pages = g_new_required_pages;
}
}
}
g_total_pages = total_pages;
g_new_required_pages = required_pages;
/* Sort the filtered descriptors, so that GRUB can allocate pages
from smaller regions. */
sort_memory_map (filtered_memory_map, desc_size, filtered_memory_map_end);
/* Allocate memory regions for GRUB's memory management. */
add_memory_regions (filtered_memory_map, desc_size,
filtered_memory_map_end, required_pages);
#if 0
/* For debug. */
map_size = MEMORY_MAP_SIZE;
if (grub_efi_get_memory_map (&map_size, memory_map, 0, &desc_size, 0) < 0)
grub_fatal ("cannot get memory map");
grub_printf ("printing memory map\n");
print_memory_map (memory_map, desc_size,
NEXT_MEMORY_DESCRIPTOR (memory_map, map_size));
grub_fatal ("Debug. ");
#endif
/* Release the memory maps. */
grub_efi_free_pages ((grub_addr_t) memory_map,
2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));
}
#if defined (__aarch64__) || defined (__arm__) || defined (__riscv)
grub_err_t
grub_efi_get_ram_base(grub_addr_t *base_addr)
{
grub_efi_memory_descriptor_t *memory_map, *desc;
grub_efi_uintn_t memory_map_size, desc_size;
int ret;
memory_map_size = grub_efi_find_mmap_size();
memory_map = grub_malloc (memory_map_size);
if (! memory_map)
return GRUB_ERR_OUT_OF_MEMORY;
ret = grub_efi_get_memory_map (&memory_map_size, memory_map, NULL,
&desc_size, NULL);
if (ret < 1)
return GRUB_ERR_BUG;
for (desc = memory_map, *base_addr = GRUB_EFI_MAX_USABLE_ADDRESS;
(grub_addr_t) desc < ((grub_addr_t) memory_map + memory_map_size);
desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
if (desc->attribute & GRUB_EFI_MEMORY_WB)
*base_addr = grub_min (*base_addr, desc->physical_start);
grub_free(memory_map);
return GRUB_ERR_NONE;
}
#endif
void grub_efi_get_reserved_page_num(grub_uint64_t *total, grub_uint64_t *org_required, grub_uint64_t *new_required)
{
*total = g_total_pages;
*org_required = g_org_required_pages;
*new_required = g_new_required_pages;
}
@@ -0,0 +1,253 @@
/* 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>
grub_uint8_t g_sys_sb = 0;
grub_uint8_t g_sb_policy = VTOY_SB_POLICY_BYPASS;
/* The initial context. */
static struct grub_env_context initial_context;
/* The current context. */
struct grub_env_context *grub_current_context = &initial_context;
static grub_env_read_hook_t vtoy_menu_lang_read_hook;
/* Return the hash representation of the string S. */
static unsigned int
grub_env_hashval (const char *s)
{
unsigned int i = 0;
/* XXX: This can be done much more efficiently. */
while (*s)
i += 5 * *(s++);
return i % HASHSZ;
}
static struct grub_env_var *
grub_env_find (const char *name)
{
struct grub_env_var *var;
int idx = grub_env_hashval (name);
/* Look for the variable in the current context. */
for (var = grub_current_context->vars[idx]; var; var = var->next)
if (grub_strcmp (var->name, name) == 0)
return var;
return 0;
}
static void
grub_env_insert (struct grub_env_context *context,
struct grub_env_var *var)
{
int idx = grub_env_hashval (var->name);
/* Insert the variable into the hashtable. */
var->prevp = &context->vars[idx];
var->next = context->vars[idx];
if (var->next)
var->next->prevp = &(var->next);
context->vars[idx] = var;
}
static void
grub_env_remove (struct grub_env_var *var)
{
/* Remove the entry from the variable table. */
*var->prevp = var->next;
if (var->next)
var->next->prevp = var->prevp;
}
grub_err_t
grub_env_set (const char *name, const char *val)
{
struct grub_env_var *var;
/* If the variable does already exist, just update the variable. */
var = grub_env_find (name);
if (var)
{
char *old = var->value;
if (var->write_hook)
var->value = var->write_hook (var, val);
else
var->value = grub_strdup (val);
if (! var->value)
{
var->value = old;
return grub_errno;
}
grub_free (old);
return GRUB_ERR_NONE;
}
/* The variable does not exist, so create a new one. */
var = grub_zalloc (sizeof (*var));
if (! var)
return grub_errno;
var->name = grub_strdup (name);
if (! var->name)
goto fail;
var->value = grub_strdup (val);
if (! var->value)
goto fail;
grub_env_insert (grub_current_context, var);
return GRUB_ERR_NONE;
fail:
grub_free (var->name);
grub_free (var->value);
grub_free (var);
return grub_errno;
}
const char *
grub_env_get (const char *name)
{
struct grub_env_var *var;
if (name && vtoy_menu_lang_read_hook && grub_strncmp(name, "VTLANG_", 7) == 0)
return vtoy_menu_lang_read_hook(NULL, name);
var = grub_env_find (name);
if (! var)
return 0;
if (var->read_hook)
return var->read_hook (var, var->value);
return var->value;
}
void
grub_env_unset (const char *name)
{
struct grub_env_var *var;
var = grub_env_find (name);
if (! var)
return;
if (var->read_hook || var->write_hook)
{
grub_env_set (name, "");
return;
}
grub_env_remove (var);
grub_free (var->name);
grub_free (var->value);
grub_free (var);
}
struct grub_env_var *
grub_env_update_get_sorted (void)
{
struct grub_env_var *sorted_list = 0;
int i;
/* Add variables associated with this context into a sorted list. */
for (i = 0; i < HASHSZ; i++)
{
struct grub_env_var *var;
for (var = grub_current_context->vars[i]; var; var = var->next)
{
struct grub_env_var *p, **q;
for (q = &sorted_list, p = *q; p; q = &((*q)->sorted_next), p = *q)
{
if (grub_strcmp (p->name, var->name) > 0)
break;
}
var->sorted_next = *q;
*q = var;
}
}
return sorted_list;
}
grub_err_t
grub_register_variable_hook (const char *name,
grub_env_read_hook_t read_hook,
grub_env_write_hook_t write_hook)
{
struct grub_env_var *var = grub_env_find (name);
if (! var)
{
if (grub_env_set (name, "") != GRUB_ERR_NONE)
return grub_errno;
var = grub_env_find (name);
/* XXX Insert an assertion? */
}
var->read_hook = read_hook;
var->write_hook = write_hook;
return GRUB_ERR_NONE;
}
grub_err_t
grub_register_vtoy_menu_lang_hook(grub_env_read_hook_t read_hook)
{
vtoy_menu_lang_read_hook = read_hook;
return GRUB_ERR_NONE;
}
grub_err_t
grub_env_export (const char *name)
{
struct grub_env_var *var;
var = grub_env_find (name);
if (! var)
{
grub_err_t err;
err = grub_env_set (name, "");
if (err)
return err;
var = grub_env_find (name);
}
var->global = 1;
return GRUB_ERR_NONE;
}
@@ -0,0 +1,399 @@
/* 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;
}
int ventoy_check_file_exist(const char * fmt, ...)
{
va_list ap;
grub_file_t file;
char fullpath[256] = {0};
va_start (ap, fmt);
grub_vsnprintf(fullpath, 255, fmt, ap);
va_end (ap);
file = grub_file_open(fullpath, GRUB_FILE_TYPE_NONE);
if (!file)
{
grub_errno = 0;
return 0;
}
else
{
grub_file_close(file);
return 1;
}
}
typedef struct grub_vlnk
{
int srclen;
char src[512];
char dst[512];
struct grub_vlnk *next;
}grub_vlnk;
static grub_vlnk g_vtoy_vlnk;
static grub_vlnk *g_vlnk_list;
int grub_file_is_vlnk_suffix(const char *name, int len)
{
grub_uint32_t suffix;
if (len > 9)
{
suffix = *(grub_uint32_t *)(name + len - 4);
if (grub_strncmp(name + len - 9, ".vlnk.", 6) == 0)
{
/* .iso .wim .img .vhd .efi .dat */
if (suffix == 0x6F73692E || suffix == 0x6D69772E ||
suffix == 0x676D692E || suffix == 0x6468762E ||
suffix == 0x6966652E || suffix == 0x7461642E)
{
return 1;
}
}
else if (len > 10 && grub_strncmp(name + len - 10, ".vlnk.", 6) == 0)
{
/* vhdx vtoy */
if (suffix == 0x78646876 || suffix == 0x796F7476)
{
return 1;
}
}
}
return 0;
}
int grub_file_vtoy_vlnk(const char *src, const char *dst)
{
if (src)
{
g_vtoy_vlnk.srclen = (int)grub_strlen(src);
grub_strncpy(g_vtoy_vlnk.src, src, sizeof(g_vtoy_vlnk.src) - 1);
grub_strncpy(g_vtoy_vlnk.dst, dst, sizeof(g_vtoy_vlnk.dst) - 1);
}
else
{
g_vtoy_vlnk.srclen = 0;
g_vtoy_vlnk.src[0] = 0;
g_vtoy_vlnk.dst[0] = 0;
}
return 0;
}
int grub_file_add_vlnk(const char *src, const char *dst)
{
grub_vlnk *node = NULL;
if (src && dst)
{
node = grub_zalloc(sizeof(grub_vlnk));
if (node)
{
node->srclen = (int)grub_strlen(src);
grub_strncpy(node->src, src, sizeof(node->src) - 1);
grub_strncpy(node->dst, dst, sizeof(node->dst) - 1);
node->next = g_vlnk_list;
g_vlnk_list = node;
return 0;
}
}
return 1;
}
const char *grub_file_get_vlnk(const char *name, int *vlnk)
{
int len;
grub_vlnk *node = g_vlnk_list;
len = grub_strlen(name);
if (!grub_file_is_vlnk_suffix(name, len))
{
return name;
}
if (len == g_vtoy_vlnk.srclen && grub_strcmp(name, g_vtoy_vlnk.src) == 0)
{
if (vlnk)
*vlnk = 1;
return g_vtoy_vlnk.dst;
}
while (node)
{
if (node->srclen == len && grub_strcmp(name, node->src) == 0)
{
if (vlnk)
*vlnk = 1;
return node->dst;
}
node = node->next;
}
return name;
}
grub_file_t
grub_file_open (const char *name, enum grub_file_type type)
{
int vlnk = 0;
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 grub_strlen(GRUB_MEMFILE_MEM) */
if (grub_strncmp(name, GRUB_MEMFILE_MEM, 4) == 0) {
return grub_memfile_open(name);
}
if ((g_vlnk_list || g_vtoy_vlnk.srclen) && (type & GRUB_FILE_TYPE_NO_VLNK) == 0)
name = grub_file_get_vlnk(name, &vlnk);
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;
file->vlnk = vlnk;
/* 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 (file->name) {
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;
}
+342
View File
@@ -0,0 +1,342 @@
/* 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_list_probe(grub_device_t device, const char **list)
{
int i;
grub_fs_t p;
if (!device->disk)
return 0;
for (p = grub_fs_list; p; p = p->next)
{
for (i = 0; list[i]; i++)
{
if (grub_strcmp(p->name, list[i]) == 0)
break;
}
if (list[i] == NULL)
continue;
grub_dprintf("fs", "Detecting %s...\n", p->name);
(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;
}
return 0;
}
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
};
@@ -0,0 +1,403 @@
/* 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 int g_ventoy_hook_root = 0;
void ventoy_env_hook_root(int hook)
{
g_ventoy_hook_root = hook;
}
static char *
ventoy_env_write_root (struct grub_env_var *var __attribute__ ((unused)),
const char *val)
{
const char *pos = val;
char buf[256];
if (g_ventoy_hook_root == 0)
{
return grub_env_write_root(var, val);
}
if (pos[0] == '(')
{
pos++;
}
if (grub_strncmp(pos, "vtimghd", 7) == 0)
{
return grub_env_write_root(var, val);
}
pos = grub_strchr(val, ',');
if (!pos)
{
return grub_env_write_root(var, val);
}
if (val[0] == '(')
{
grub_snprintf(buf, sizeof(buf), "(vtimghd%s", pos);
}
else
{
grub_snprintf(buf, sizeof(buf), "vtimghd%s", pos);
}
return grub_env_write_root(var, buf);
}
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, ventoy_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
}
#ifndef GRUB_MACHINE_EFI
static int ventoy_legacy_limit_workaround(void)
{
grub_file_t file;
char *pos, *root;
char buf[128];
root = grub_strdup(grub_env_get("root"));
if (!root)
{
return 1;
}
pos = grub_strchr(root, ',');
if (pos) *pos = 0;
grub_snprintf(buf, sizeof(buf), "(%s,1)/ventoy/ventoy.disk.img.xz", root);
file = grub_file_open(buf, GRUB_FILE_TYPE_NONE);
if (file)
{
pos = grub_malloc(file->size);
if (pos)
{
grub_file_read(file, pos, file->size);
grub_snprintf(buf, sizeof(buf), "loopback ventoydisk mem:0x%lx:size:%lu",
(unsigned long)pos, (unsigned long)file->size);
grub_parser_execute(buf);
grub_env_set("prefix", "(ventoydisk)/grub");
}
grub_file_close(file);
}
grub_free(root);
return 0;
}
#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");
#ifndef GRUB_MACHINE_EFI
if (0 == ventoy_check_file_exist("%s/grub.cfg", grub_env_get("prefix"))) {
ventoy_legacy_limit_workaround();
}
#endif
/* 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 ();
}
@@ -0,0 +1,23 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2009,2017 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>
FUNCTION (grub_arch_sync_caches)
jr.hb $ra
@@ -0,0 +1,150 @@
/* dl-mips64.c - arch-dependent part of loadable module support */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2002,2005,2007,2009,2017 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/elf.h>
#include <grub/misc.h>
#include <grub/err.h>
#include <grub/cpu/types.h>
#include <grub/mm.h>
#include <grub/i18n.h>
/* Check if EHDR is a valid ELF header. */
grub_err_t
grub_arch_dl_check_header (void *ehdr)
{
Elf_Ehdr *e = ehdr;
/* Check the magic numbers. */
#ifdef GRUB_CPU_WORDS_BIGENDIAN
if (e->e_ident[EI_CLASS] != ELFCLASS64
|| e->e_ident[EI_DATA] != ELFDATA2MSB
|| e->e_machine != EM_MIPS)
#else
if (e->e_ident[EI_CLASS] != ELFCLASS64
|| e->e_ident[EI_DATA] != ELFDATA2LSB
|| e->e_machine != EM_MIPS)
#endif
return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic"));
return GRUB_ERR_NONE;
}
#pragma GCC diagnostic ignored "-Wcast-align"
grub_err_t
grub_arch_dl_get_tramp_got_size (const void *ehdr __attribute__ ((unused)),
grub_size_t *tramp, grub_size_t *got)
{
*tramp = 0;
*got = 0;
return GRUB_ERR_NONE;
}
/* Relocate symbols. */
grub_err_t
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
Elf_Shdr *s, grub_dl_segment_t seg)
{
Elf_Ehdr *e = ehdr;
Elf_Rel *rel, *max;
for (rel = (Elf_Rel *) ((char *) e + s->sh_offset),
max = (Elf_Rel *) ((char *) rel + s->sh_size);
rel < max;
rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
{
grub_uint8_t *addr;
Elf_Sym *sym;
Elf_Addr r_info;
grub_uint64_t sym_value;
if (seg->size < rel->r_offset)
return grub_error (GRUB_ERR_BAD_MODULE,
"reloc offset is out of the segment");
r_info = ((grub_uint64_t) rel->r_info << 32) |
(grub_uint32_t) grub_be_to_cpu64 (rel->r_info);
addr = (grub_uint8_t *) ((char *) seg->addr + rel->r_offset);
sym = (Elf_Sym *) ((char *) mod->symtab
+ mod->symsize * ELF_R_SYM (r_info));
sym_value = sym->st_value;
if (s->sh_type == SHT_RELA)
{
sym_value += ((Elf_Rela *) rel)->r_addend;
}
switch (ELF_R_TYPE (r_info))
{
case R_MIPS_64:
*(grub_uint64_t *) addr += sym_value;
break;
case R_MIPS_32:
*(grub_uint32_t *) addr += sym_value;
break;
case R_MIPS_26:
{
grub_uint32_t value;
grub_uint32_t raw;
raw = (*(grub_uint32_t *) addr) & 0x3ffffff;
value = raw << 2;
value += sym_value;
raw = (value >> 2) & 0x3ffffff;
*(grub_uint32_t *) addr =
raw | ((*(grub_uint32_t *) addr) & 0xfc000000);
}
break;
case R_MIPS_LO16:
#ifdef GRUB_CPU_WORDS_BIGENDIAN
addr += 2;
#endif
*(grub_uint16_t *) addr = (grub_int16_t) sym_value;
break;
case R_MIPS_HI16:
#ifdef GRUB_CPU_WORDS_BIGENDIAN
addr += 2;
#endif
*(grub_uint16_t *) addr = (grub_int16_t) ((sym_value + 0x8000UL) >> 16);
break;
case R_MIPS_HIGHER:
#ifdef GRUB_CPU_WORDS_BIGENDIAN
addr += 2;
#endif
*(grub_uint16_t *) addr = (grub_int16_t) ((sym_value + 0x80008000UL) >> 32);
break;
case R_MIPS_HIGHEST:
#ifdef GRUB_CPU_WORDS_BIGENDIAN
addr += 2;
#endif
*(grub_uint16_t *) addr = (grub_uint16_t) ((sym_value + 0x800080008000UL) >> 48);
break;
default:
{
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
N_("relocation 0x%x is not implemented yet"),
ELF_R_TYPE (r_info));
}
break;
}
}
return GRUB_ERR_NONE;
}
@@ -0,0 +1,85 @@
/* init.c - initialize an arm-based EFI system */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2013 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/kernel.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/cpu/time.h>
#include <grub/efi/efi.h>
#include <grub/loader.h>
#include <grub/time.h>
#include <grub/machine/loongson.h>
static grub_uint64_t tmr;
static grub_efi_event_t tmr_evt;
static grub_uint64_t
grub_efi_get_time_ms (void)
{
return tmr;
}
static void
grub_loongson_increment_timer (grub_efi_event_t event __attribute__ ((unused)),
void *context __attribute__ ((unused)))
{
tmr += 10;
}
void
grub_machine_init (void)
{
grub_efi_boot_services_t *b;
grub_efi_init ();
b = grub_efi_system_table->boot_services;
efi_call_5 (b->create_event, GRUB_EFI_EVT_TIMER | GRUB_EFI_EVT_NOTIFY_SIGNAL,
GRUB_EFI_TPL_CALLBACK, grub_loongson_increment_timer, NULL, &tmr_evt);
efi_call_3 (b->set_timer, tmr_evt, GRUB_EFI_TIMER_PERIODIC, 100000);
grub_install_get_time_ms (grub_efi_get_time_ms);
if (grub_efi_is_loongson ())
grub_efi_loongson_init ();
else
/* FIXME: Get cpuclock from EFI. */
grub_timer_init (1000000000U);
}
void
grub_machine_fini (int flags)
{
grub_efi_boot_services_t *b;
if (!(flags & GRUB_LOADER_FLAG_NORETURN))
return;
b = grub_efi_system_table->boot_services;
efi_call_3 (b->set_timer, tmr_evt, GRUB_EFI_TIMER_CANCEL, 0);
efi_call_1 (b->close_event, tmr_evt);
if (grub_efi_is_loongson ())
grub_efi_loongson_fini ();
grub_efi_fini ();
}
@@ -0,0 +1,33 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2017 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/efi/efi.h>
#include <grub/cpu/time.h>
#include <grub/loader.h>
#include <grub/machine/loongson.h>
void
grub_efi_loongson_init (void)
{
}
void
grub_efi_loongson_fini (void)
{
}
@@ -0,0 +1,47 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2013 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>
.file "startup.S"
.text
.set push
.align 4
FUNCTION(_start)
/*
* EFI_SYSTEM_TABLE and EFI_HANDLE are passed in a1/a0.
*/
daddiu $sp, -16
sd $ra, ($sp)
dla $a2, grub_efi_image_handle
sd $a0, ($a2)
dla $a2, grub_efi_system_table
sd $a1, ($a2)
jal grub_main
1:
ld $ra, ($sp)
daddiu $sp, 16
jr $ra
.set pop
@@ -0,0 +1,47 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2009,2017 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/env.h>
#include <grub/time.h>
#include <grub/cpu/mips.h>
grub_uint32_t grub_arch_cpuclock;
/* FIXME: use interrupt to count high. */
grub_uint64_t
grub_get_rtc (void)
{
static grub_uint32_t high = 0;
static grub_uint32_t last = 0;
grub_uint32_t low;
asm volatile ("mfc0 %0, " GRUB_CPU_MIPS_COP0_TIMER_COUNT : "=r" (low));
if (low < last)
high++;
last = low;
return (((grub_uint64_t) high) << 32) | low;
}
void
grub_timer_init (grub_uint32_t cpuclock)
{
grub_arch_cpuclock = cpuclock;
grub_install_get_time_ms (grub_rtc_get_time_ms);
}
@@ -0,0 +1,133 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2002,2003,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/term.h>
#include <grub/err.h>
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/env.h>
#include <grub/time.h>
struct grub_term_output *grub_term_outputs_disabled;
struct grub_term_input *grub_term_inputs_disabled;
struct grub_term_output *grub_term_outputs;
struct grub_term_input *grub_term_inputs;
/* Current color state. */
grub_uint8_t grub_term_normal_color = GRUB_TERM_DEFAULT_NORMAL_COLOR;
grub_uint8_t grub_term_highlight_color = GRUB_TERM_DEFAULT_HIGHLIGHT_COLOR;
void (*grub_term_poll_usb) (int wait_for_completion) = NULL;
void (*grub_net_poll_cards_idle) (void) = NULL;
/* Put a Unicode character. */
static void
grub_putcode_dumb (grub_uint32_t code,
struct grub_term_output *term)
{
struct grub_unicode_glyph c =
{
.base = code,
.variant = 0,
.attributes = 0,
.ncomb = 0,
.estimated_width = 1
};
if (code == '\t' && term->getxy)
{
int n;
n = GRUB_TERM_TAB_WIDTH - ((term->getxy (term).x)
% GRUB_TERM_TAB_WIDTH);
while (n--)
grub_putcode_dumb (' ', term);
return;
}
(term->putchar) (term, &c);
if (code == '\n')
grub_putcode_dumb ('\r', term);
}
static void
grub_xputs_dumb (const char *str)
{
for (; *str; str++)
{
grub_term_output_t term;
grub_uint32_t code = *str;
if (code > 0x7f)
code = '?';
FOR_ACTIVE_TERM_OUTPUTS(term)
grub_putcode_dumb (code, term);
}
}
void (*grub_xputs) (const char *str) = grub_xputs_dumb;
int (*grub_key_remap)(int key) = NULL;
int
grub_getkey_noblock (void)
{
grub_term_input_t term;
if (grub_term_poll_usb)
grub_term_poll_usb (0);
if (grub_net_poll_cards_idle)
grub_net_poll_cards_idle ();
FOR_ACTIVE_TERM_INPUTS(term)
{
int key = term->getkey (term);
if (grub_key_remap)
key = grub_key_remap(key);
if (key != GRUB_TERM_NO_KEY)
return key;
}
return GRUB_TERM_NO_KEY;
}
int
grub_getkey (void)
{
int ret;
grub_refresh ();
while (1)
{
ret = grub_getkey_noblock ();
if (ret != GRUB_TERM_NO_KEY)
return ret;
grub_cpu_idle ();
}
}
void
grub_refresh (void)
{
struct grub_term_output *term;
FOR_ACTIVE_TERM_OUTPUTS(term)
grub_term_refresh (term);
}
@@ -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);
}
@@ -0,0 +1,40 @@
/* 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/efi/api.h>
#include <grub/efi/efi.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/kernel.h>
#include <grub/acpi.h>
#include <grub/loader.h>
void
grub_halt (void)
{
grub_machine_fini (GRUB_LOADER_FLAG_NORETURN);
#if !defined(__ia64__) && !defined(__arm__) && !defined(__aarch64__) && !defined(__mips__) &&\
!defined(__riscv)
grub_acpi_halt ();
#endif
efi_call_4 (grub_efi_system_table->runtime_services->reset_system,
GRUB_EFI_RESET_SHUTDOWN, GRUB_EFI_SUCCESS, 0, NULL);
while (1);
}
@@ -0,0 +1,495 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2017 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/cache.h>
#include <grub/efi/efi.h>
#include <grub/cpu/efi/memory.h>
#include <grub/cpu/memory.h>
#include <grub/machine/loongson.h>
#pragma GCC diagnostic ignored "-Wunused-function"
#define loongson_params (&loongson_boot_params->boot_params.efi.smbios.lp)
#define loongson_boot_params_size ALIGN_UP (sizeof (*loongson_boot_params), 8)
#define loongson_reset_code_size (&grub_efi_loongson_reset_end - &grub_efi_loongson_reset_start)
extern grub_uint8_t grub_efi_loongson_reset_start;
extern grub_uint8_t grub_efi_loongson_reset_end;
static struct
{
grub_efi_loongson_boot_params boot_params;
grub_efi_loongson_memory_map memory_map;
grub_efi_loongson_cpu_info cpu_info;
grub_efi_loongson_system_info system_info;
grub_efi_loongson_irq_src_routing_table irq_src_routing_table;
grub_efi_loongson_interface_info interface_info;
grub_efi_loongson_special_attribute special_attribute;
grub_efi_loongson_board_devices board_devices;
} GRUB_PACKED
* loongson_boot_params;
static void
grub_efi_loongson_init_reset_system (void)
{
grub_efi_loongson_boot_params *boot_params;
grub_uint8_t *reset_code_addr = (grub_uint8_t *) loongson_boot_params +
loongson_boot_params_size;
boot_params = &loongson_boot_params->boot_params;
grub_efi_loongson_reset_system_addr =
(grub_uint64_t) grub_efi_system_table->runtime_services->reset_system;
grub_memcpy (reset_code_addr, &grub_efi_loongson_reset_start, loongson_reset_code_size);
grub_arch_sync_caches (reset_code_addr, loongson_reset_code_size);
boot_params->reset_system.reset_cold = (grub_uint64_t) reset_code_addr +
((grub_uint64_t) &grub_efi_loongson_reset_cold -
(grub_uint64_t) &grub_efi_loongson_reset_start);
boot_params->reset_system.reset_warm = (grub_uint64_t) reset_code_addr +
((grub_uint64_t) &grub_efi_loongson_reset_warm -
(grub_uint64_t) &grub_efi_loongson_reset_start);
boot_params->reset_system.shutdown = (grub_uint64_t) reset_code_addr +
((grub_uint64_t) &grub_efi_loongson_reset_shutdown -
(grub_uint64_t) &grub_efi_loongson_reset_start);
boot_params->reset_system.do_suspend = (grub_uint64_t) reset_code_addr +
((grub_uint64_t) &grub_efi_loongson_reset_suspend -
(grub_uint64_t) &grub_efi_loongson_reset_start);
}
static void
grub_efi_loongson_init_smbios (grub_efi_loongson_smbios_table *smbios_table)
{
grub_efi_loongson_smbios_table *dst = &loongson_boot_params->boot_params.efi.smbios;
dst->vers = smbios_table->vers;
dst->vga_bios = smbios_table->vga_bios;
}
static void
grub_efi_loongson_init_cpu_info (grub_efi_loongson_smbios_table *smbios_table)
{
grub_efi_loongson_cpu_info *src = (void *) smbios_table->lp.cpu_offset;
grub_efi_loongson_cpu_info *dst = &loongson_boot_params->cpu_info;
if (!src)
return;
grub_memcpy (dst, src, sizeof (grub_efi_loongson_cpu_info));
loongson_params->cpu_offset = (grub_uint64_t) dst - (grub_uint64_t) loongson_params;
}
static void
grub_efi_loongson_init_system_info (grub_efi_loongson_smbios_table *smbios_table)
{
grub_efi_loongson_system_info *src = (void *) smbios_table->lp.system_offset;
grub_efi_loongson_system_info *dst = &loongson_boot_params->system_info;
if (!src)
return;
grub_memcpy (dst, src, sizeof (grub_efi_loongson_system_info));
loongson_params->system_offset = (grub_uint64_t) dst - (grub_uint64_t) loongson_params;
}
static void
grub_efi_loongson_init_irq_src_routing_table (grub_efi_loongson_smbios_table *smbios_table)
{
grub_efi_loongson_irq_src_routing_table *src = (void *) smbios_table->lp.irq_offset;
grub_efi_loongson_irq_src_routing_table *dst = &loongson_boot_params->irq_src_routing_table;
if (!src)
return;
grub_memcpy (dst, src, sizeof (grub_efi_loongson_irq_src_routing_table));
loongson_params->irq_offset = (grub_uint64_t) dst - (grub_uint64_t) loongson_params;
}
static void
grub_efi_loongson_init_interface_info (grub_efi_loongson_smbios_table *smbios_table)
{
grub_efi_loongson_interface_info *src = (void *) smbios_table->lp.interface_offset;
grub_efi_loongson_interface_info *dst = &loongson_boot_params->interface_info;
if (!src)
return;
grub_memcpy (dst, src, sizeof (grub_efi_loongson_interface_info));
loongson_params->interface_offset = (grub_uint64_t) dst - (grub_uint64_t) loongson_params;
}
static void
grub_efi_loongson_init_special_attribute (grub_efi_loongson_smbios_table *smbios_table)
{
grub_efi_loongson_special_attribute *src = (void *) smbios_table->lp.special_offset;
grub_efi_loongson_special_attribute *dst = &loongson_boot_params->special_attribute;
if (!src)
return;
grub_memcpy (dst, src, sizeof (grub_efi_loongson_special_attribute));
loongson_params->special_offset = (grub_uint64_t) dst - (grub_uint64_t) loongson_params;
}
static void
grub_efi_loongson_init_board_devices (grub_efi_loongson_smbios_table *smbios_table)
{
grub_efi_loongson_board_devices *src = (void *) smbios_table->lp.boarddev_table_offset;
grub_efi_loongson_board_devices *dst = &loongson_boot_params->board_devices;
if (!src)
return;
grub_memcpy (dst, src, sizeof (grub_efi_loongson_board_devices));
loongson_params->boarddev_table_offset = (grub_uint64_t) dst - (grub_uint64_t) loongson_params;
}
#define ADD_MEMORY_DESCRIPTOR(desc, size) \
((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size)))
static void
grub_efi_loongson_init_memory_map (grub_efi_loongson_smbios_table *smbios_table,
grub_efi_memory_descriptor_t *mmap_buf,
grub_efi_uintn_t mmap_size,
grub_efi_uintn_t desc_size)
{
grub_efi_loongson_memory_map *src = (void *) smbios_table->lp.memory_offset;
grub_efi_loongson_memory_map *dst = &loongson_boot_params->memory_map;
grub_efi_memory_descriptor_t *mmap_end;
grub_efi_memory_descriptor_t *desc;
grub_efi_memory_descriptor_t *desc_next;
grub_efi_uint32_t mem_types_reserved[] =
{
1, // GRUB_EFI_RESERVED_MEMORY_TYPE
0, // GRUB_EFI_LOADER_CODE
0, // GRUB_EFI_LOADER_DATA
0, // GRUB_EFI_BOOT_SERVICES_CODE
0, // GRUB_EFI_BOOT_SERVICES_DATA
1, // GRUB_EFI_RUNTIME_SERVICES_CODE
1, // GRUB_EFI_RUNTIME_SERVICES_DATA
0, // GRUB_EFI_CONVENTIONAL_MEMORY
1, // GRUB_EFI_UNUSABLE_MEMORY
0, // GRUB_EFI_ACPI_RECLAIM_MEMORY
0, // GRUB_EFI_ACPI_MEMORY_NVS
1, // GRUB_EFI_MEMORY_MAPPED_IO
1, // GRUB_EFI_MEMORY_MAPPED_IO_PORT_SPACE
1, // GRUB_EFI_PAL_CODE
1, // GRUB_EFI_PERSISTENT_MEMORY
};
grub_uint32_t need_sort = 1;
if (!src)
return;
dst->vers = src->vers;
dst->nr_map = 0;
dst->mem_freq = src->mem_freq;
loongson_params->memory_offset = (grub_uint64_t) dst - (grub_uint64_t) loongson_params;
if (!mmap_buf || !mmap_size || !desc_size)
return;
mmap_end = ADD_MEMORY_DESCRIPTOR (mmap_buf, mmap_size);
/* drop reserved */
for (desc = mmap_buf,
desc_next = desc;
desc < mmap_end;
desc = ADD_MEMORY_DESCRIPTOR (desc, desc_size))
{
desc->type = mem_types_reserved[desc->type];
if (desc->type)
continue;
if (desc != desc_next)
*desc_next = *desc;
desc_next = ADD_MEMORY_DESCRIPTOR (desc_next, desc_size);
}
mmap_end = desc_next;
/* sort: low->high */
while (need_sort)
{
need_sort = 0;
for (desc = mmap_buf,
desc_next = ADD_MEMORY_DESCRIPTOR (desc, desc_size);
(desc < mmap_end) && (desc_next < mmap_end);
desc = desc_next,
desc_next = ADD_MEMORY_DESCRIPTOR (desc, desc_size))
{
grub_efi_memory_descriptor_t tmp;
if (desc->physical_start <= desc_next->physical_start)
continue;
tmp = *desc;
*desc = *desc_next;
*desc_next = tmp;
need_sort = 1;
}
}
/* combine continuous memory map */
for (desc = mmap_buf,
desc_next = ADD_MEMORY_DESCRIPTOR (desc, desc_size);
desc_next < mmap_end;
desc_next = ADD_MEMORY_DESCRIPTOR (desc_next, desc_size))
{
grub_efi_physical_address_t prev_end = desc->physical_start + (desc->num_pages << 12);
if (prev_end == desc_next->physical_start)
{
desc->num_pages += desc_next->num_pages;
continue;
}
desc = ADD_MEMORY_DESCRIPTOR (desc, desc_size);
grub_memcpy (desc, desc_next, desc_size);
}
mmap_end = ADD_MEMORY_DESCRIPTOR (desc, desc_size);
/* write to loongson memory map */
for (desc = mmap_buf;
desc < mmap_end;
desc = ADD_MEMORY_DESCRIPTOR (desc, desc_size))
{
grub_efi_physical_address_t physical_start = grub_vtop ((void *) desc->physical_start);
grub_efi_physical_address_t physical_end = physical_start + (desc->num_pages << 12);
physical_start = ALIGN_UP (physical_start, 0x100000);
physical_end = ALIGN_DOWN (physical_end, 0x100000);
if (physical_start >= physical_end || (physical_end - physical_start) < 0x100000)
continue;
dst->map[dst->nr_map].node_id = (desc->physical_start >> 44) & 0xf;
dst->map[dst->nr_map].mem_type = (physical_end <= 0x10000000) ?
GRUB_EFI_LOONGSON_SYSTEM_RAM_LOW :
GRUB_EFI_LOONGSON_SYSTEM_RAM_HIGH;
dst->map[dst->nr_map].mem_start = physical_start;
dst->map[dst->nr_map].mem_size = (physical_end - physical_start) >> 20;
grub_dprintf ("loongson", "memory map %03u: 0x%016lx 0x%016lx @ %u\n",
dst->nr_map, physical_start, physical_end - physical_start,
dst->map[dst->nr_map].node_id);
dst->nr_map ++;
}
}
#define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12)
#define SUB_MEMORY_DESCRIPTOR(desc, size) \
((grub_efi_memory_descriptor_t *) ((char *) (desc) - (size)))
void
grub_efi_loongson_alloc_boot_params (void)
{
grub_efi_memory_descriptor_t *mmap_buf;
grub_efi_memory_descriptor_t *mmap_end;
grub_efi_memory_descriptor_t *desc;
grub_efi_uintn_t mmap_size;
grub_efi_uintn_t desc_size;
grub_efi_physical_address_t address;
grub_efi_allocate_type_t type;
grub_efi_uintn_t pages;
grub_efi_status_t status;
grub_efi_boot_services_t *b;
int mm_status;
type = GRUB_EFI_ALLOCATE_ADDRESS;
pages = BYTES_TO_PAGES (loongson_boot_params_size + loongson_reset_code_size);
mmap_size = (1 << 12);
mmap_buf = grub_malloc (mmap_size);
if (!mmap_buf)
grub_fatal ("out of memory!");
mm_status = grub_efi_get_memory_map (&mmap_size, mmap_buf, 0, &desc_size, 0);
if (mm_status == 0)
{
grub_free (mmap_buf);
mmap_size += desc_size * 32;
mmap_buf = grub_malloc (mmap_size);
if (!mmap_buf)
grub_fatal ("out of memory!");
mm_status = grub_efi_get_memory_map (&mmap_size, mmap_buf, 0, &desc_size, 0);
}
if (mm_status < 0)
grub_fatal ("cannot get memory map!");
mmap_end = ADD_MEMORY_DESCRIPTOR (mmap_buf, mmap_size);
for (desc = SUB_MEMORY_DESCRIPTOR (mmap_end, desc_size);
desc >= mmap_buf;
desc = SUB_MEMORY_DESCRIPTOR (desc, desc_size))
{
if (desc->type != GRUB_EFI_CONVENTIONAL_MEMORY)
continue;
if (desc->physical_start >= GRUB_EFI_MAX_USABLE_ADDRESS)
continue;
if (desc->num_pages < pages)
continue;
address = desc->physical_start;
break;
}
grub_free (mmap_buf);
b = grub_efi_system_table->boot_services;
status = efi_call_4 (b->allocate_pages, type, GRUB_EFI_RUNTIME_SERVICES_DATA, pages, &address);
if (status != GRUB_EFI_SUCCESS)
grub_fatal ("cannot allocate Loongson boot parameters!");
loongson_boot_params = (void *) ((grub_addr_t) address);
}
void
grub_efi_loongson_free_boot_params (void)
{
grub_efi_free_pages ((grub_addr_t) loongson_boot_params,
BYTES_TO_PAGES (loongson_boot_params_size + loongson_reset_code_size));
}
void *
grub_efi_loongson_get_smbios_table (void)
{
static grub_efi_loongson_smbios_table *smbios_table;
grub_efi_loongson_boot_params *old_boot_params;
struct bootparamsinterface* boot_params;
void * tmp_boot_params = NULL;
char * p = NULL;
if(smbios_table)
return smbios_table;
tmp_boot_params = grub_efi_loongson_get_boot_params();
if(tmp_boot_params == NULL)
{
grub_dprintf("loongson", "tmp_boot_params is NULL\n");
return tmp_boot_params;
}
boot_params = (struct bootparamsinterface *)tmp_boot_params;
p = (char *)&(boot_params->signature);
if(grub_strncmp(p, "BPI", 3) == 0)
{
grub_dprintf("loongson", "find new bpi\n");
return boot_params ? boot_params : 0;
}
else
{
old_boot_params = (grub_efi_loongson_boot_params *)tmp_boot_params;
return old_boot_params ? &old_boot_params->efi.smbios : 0;
}
}
int
grub_efi_is_loongson (void)
{
return grub_efi_loongson_get_smbios_table () ? 1 : 0;
}
void *
grub_efi_loongson_get_boot_params (void)
{
static void * boot_params = NULL;
grub_efi_configuration_table_t *tables;
grub_efi_guid_t smbios_guid = GRUB_EFI_LOONGSON_SMBIOS_TABLE_GUID;
unsigned int i;
if (boot_params)
return boot_params;
/* Look for Loongson SMBIOS in UEFI config tables. */
tables = grub_efi_system_table->configuration_table;
for (i = 0; i < grub_efi_system_table->num_table_entries; i++)
if (grub_memcmp (&tables[i].vendor_guid, &smbios_guid, sizeof (smbios_guid)) == 0)
{
boot_params= tables[i].vendor_table;
grub_dprintf ("loongson", "found registered SMBIOS @ %p\n", boot_params);
break;
}
return boot_params;
}
grub_uint8_t
grub_efi_loongson_calculatesum8 (const grub_uint8_t *buffer, grub_efi_uintn_t length)
{
grub_uint8_t sum;
grub_efi_uintn_t count;
for (sum = 0, count = 0; count < length; count++)
{
sum = (grub_uint8_t) (sum + *(buffer + count));
}
return sum;
}
grub_uint8_t
grub_efi_loongson_grub_calculatechecksum8 (const grub_uint8_t *buffer, grub_efi_uintn_t length)
{
grub_uint8_t checksum;
checksum = grub_efi_loongson_calculatesum8(buffer, length);
return (grub_uint8_t) (0x100 - checksum);
}
grub_uint32_t
grub_efi_loongson_memmap_sort(struct memmap array[], grub_uint32_t length, mem_map * bpmem, grub_uint32_t index, grub_uint32_t memtype)
{
grub_uint64_t tempmemsize = 0;
grub_uint32_t j = 0;
grub_uint32_t t = 0;
for(j = 0; j < length;)
{
tempmemsize = array[j].memsize;
for(t = j + 1; t < length; t++)
{
if(array[j].memstart + tempmemsize == array[t].memstart)
{
tempmemsize += array[t].memsize;
}
else
{
break;
}
}
bpmem->map[index].memtype = memtype;
bpmem->map[index].memstart = array[j].memstart;
bpmem->map[index].memsize = tempmemsize;
grub_dprintf("loongson", "map[%d]:type %x, start 0x%llx, end 0x%llx\n",
index,
bpmem->map[index].memtype,
(unsigned long long)bpmem->map[index].memstart,
(unsigned long long)bpmem->map[index].memstart+ bpmem->map[index].memsize
);
j = t;
index++;
}
return index;
}
@@ -0,0 +1,61 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2017 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>
.file "loongson.S"
.text
.set push
.set noreorder
.align 4
VARIABLE (grub_efi_loongson_reset_start)
VARIABLE (grub_efi_loongson_reset_system_addr)
.dword 0
reset_system:
bal 1f
move $a1, $zero
1:
ld $t9, -16($ra)
move $a2, $zero
jalr $t9
move $a3, $zero
FUNCTION(grub_efi_loongson_reset_cold)
b reset_system
li $a0, 0
FUNCTION(grub_efi_loongson_reset_warm)
b reset_system
li $a0, 1
FUNCTION(grub_efi_loongson_reset_shutdown)
b reset_system
li $a0, 2
FUNCTION(grub_efi_loongson_reset_suspend)
b reset_system
li $a0, 3
VARIABLE (grub_efi_loongson_reset_end)
.set pop
@@ -0,0 +1,169 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2017 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/types.h>
#include <grub/types.h>
#include <grub/err.h>
#include <grub/cache.h>
#include <grub/mips64/relocator.h>
#include <grub/relocator_private.h>
extern grub_uint8_t grub_relocator_forward_start;
extern grub_uint8_t grub_relocator_forward_end;
extern grub_uint8_t grub_relocator_backward_start;
extern grub_uint8_t grub_relocator_backward_end;
#define REGW_SIZEOF (6 * sizeof (grub_uint32_t))
#define JUMP_SIZEOF (2 * sizeof (grub_uint32_t))
#define RELOCATOR_SRC_SIZEOF(x) (&grub_relocator_##x##_end \
- &grub_relocator_##x##_start)
#define RELOCATOR_SIZEOF(x) (RELOCATOR_SRC_SIZEOF(x) \
+ REGW_SIZEOF * 3)
grub_size_t grub_relocator_align = sizeof (grub_uint64_t);
grub_size_t grub_relocator_forward_size;
grub_size_t grub_relocator_backward_size;
grub_size_t grub_relocator_jumper_size = JUMP_SIZEOF + REGW_SIZEOF;
void
grub_cpu_relocator_init (void)
{
grub_relocator_forward_size = RELOCATOR_SIZEOF(forward);
grub_relocator_backward_size = RELOCATOR_SIZEOF(backward);
}
static void
write_reg (int regn, grub_uint64_t val, void **target)
{
grub_uint32_t lui;
grub_uint32_t ori;
grub_uint32_t dsll;
/* lui $r, 0 */
lui = (0x3c00 | regn) << 16;
/* ori $r, $r, 0 */
ori = (0x3400 | (regn << 5) | regn) << 16;
/* dsll $r, $r, 16 */
dsll = (regn << 16) | (regn << 11) | (16 << 6) | 56;
/* lui $r, val[63:48]. */
*(grub_uint32_t *) *target = lui | (grub_uint16_t) (val >> 48);
*target = ((grub_uint32_t *) *target) + 1;
/* ori $r, val[47:32]. */
*(grub_uint32_t *) *target = ori | (grub_uint16_t) (val >> 32);
*target = ((grub_uint32_t *) *target) + 1;
/* dsll $r, $r, 16 */
*(grub_uint32_t *) *target = dsll;
*target = ((grub_uint32_t *) *target) + 1;
/* ori $r, val[31:16]. */
*(grub_uint32_t *) *target = ori | (grub_uint16_t) (val >> 16);
*target = ((grub_uint32_t *) *target) + 1;
/* dsll $r, $r, 16 */
*(grub_uint32_t *) *target = dsll;
*target = ((grub_uint32_t *) *target) + 1;
/* ori $r, val[15:0]. */
*(grub_uint32_t *) *target = ori | (grub_uint16_t) val;
*target = ((grub_uint32_t *) *target) + 1;
}
static void
write_jump (int regn, void **target)
{
/* j $r. */
*(grub_uint32_t *) *target = (regn << 21) | 0x8;
*target = ((grub_uint32_t *) *target) + 1;
/* nop. */
*(grub_uint32_t *) *target = 0;
*target = ((grub_uint32_t *) *target) + 1;
}
void
grub_cpu_relocator_jumper (void *rels, grub_addr_t addr)
{
write_reg (1, addr, &rels);
write_jump (1, &rels);
}
void
grub_cpu_relocator_backward (void *ptr0, void *src, void *dest,
grub_size_t size)
{
void *ptr = ptr0;
write_reg (8, (grub_uint64_t) src, &ptr);
write_reg (9, (grub_uint64_t) dest, &ptr);
write_reg (10, (grub_uint64_t) size, &ptr);
grub_memcpy (ptr, &grub_relocator_backward_start,
RELOCATOR_SRC_SIZEOF (backward));
}
void
grub_cpu_relocator_forward (void *ptr0, void *src, void *dest,
grub_size_t size)
{
void *ptr = ptr0;
write_reg (8, (grub_uint64_t) src, &ptr);
write_reg (9, (grub_uint64_t) dest, &ptr);
write_reg (10, (grub_uint64_t) size, &ptr);
grub_memcpy (ptr, &grub_relocator_forward_start,
RELOCATOR_SRC_SIZEOF (forward));
}
grub_err_t
grub_relocator64_boot (struct grub_relocator *rel,
struct grub_relocator64_state state)
{
grub_relocator_chunk_t ch;
void *ptr;
grub_err_t err;
void *relst;
grub_size_t relsize;
grub_size_t stateset_size = 31 * REGW_SIZEOF + JUMP_SIZEOF;
unsigned i;
grub_addr_t vtarget;
err = grub_relocator_alloc_chunk_align (rel, &ch, 0,
(0xffffffff - stateset_size)
+ 1, stateset_size,
grub_relocator_align,
GRUB_RELOCATOR_PREFERENCE_NONE, 0);
if (err)
return err;
ptr = get_virtual_current_address (ch);
for (i = 1; i < 32; i++)
write_reg (i, state.gpr[i], &ptr);
write_jump (state.jumpreg, &ptr);
vtarget = (grub_addr_t) grub_map_memory (get_physical_target_address (ch),
stateset_size);
err = grub_relocator_prepare_relocs (rel, vtarget, &relst, &relsize);
if (err)
return err;
grub_arch_sync_caches ((void *) relst, relsize);
((void (*) (void)) relst) ();
/* Not reached. */
return GRUB_ERR_NONE;
}
@@ -0,0 +1,56 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2017 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>
.p2align 4 /* force 16-byte alignment */
.set push
.set noreorder
.set nomacro
VARIABLE (grub_relocator_forward_start)
copycont1:
ld $11,0($8)
sd $11,0($9)
daddiu $8, $8, 8
daddiu $10, $10, -8
bne $10, $0, copycont1
daddiu $9, $9, 8
VARIABLE (grub_relocator_forward_end)
VARIABLE (grub_relocator_backward_start)
daddu $9, $9, $10
daddu $8, $8, $10
/* Backward movsl is implicitly off-by-one. compensate that. */
daddiu $9, $9, -8
daddiu $8, $8, -8
copycont2:
ld $11,0($8)
sd $11,0($9)
daddiu $8, $8, -8
daddiu $10, $10, -8
bne $10, $0, copycont2
daddiu $9, $9, -8
VARIABLE (grub_relocator_backward_end)
.set pop
@@ -0,0 +1,69 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2003,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/symbol.h>
#include <grub/dl.h>
#include <grub/mips64/asm.h>
.file "setjmp.S"
GRUB_MOD_LICENSE "GPLv3+"
.text
/*
* int grub_setjmp (grub_jmp_buf env)
*/
FUNCTION(grub_setjmp)
GRUB_ASM_REG_S $s0, 0($a0)
GRUB_ASM_REG_S $s1, 8($a0)
GRUB_ASM_REG_S $s2, 16($a0)
GRUB_ASM_REG_S $s3, 24($a0)
GRUB_ASM_REG_S $s4, 32($a0)
GRUB_ASM_REG_S $s5, 40($a0)
GRUB_ASM_REG_S $s6, 48($a0)
GRUB_ASM_REG_S $s7, 56($a0)
GRUB_ASM_REG_S $s8, 64($a0)
GRUB_ASM_REG_S $gp, 72($a0)
GRUB_ASM_REG_S $sp, 80($a0)
GRUB_ASM_REG_S $ra, 88($a0)
move $v0, $zero
move $v1, $zero
jr $ra
nop
/*
* int grub_longjmp (grub_jmp_buf env, int val)
*/
FUNCTION(grub_longjmp)
GRUB_ASM_REG_L $s0, 0($a0)
GRUB_ASM_REG_L $s1, 8($a0)
GRUB_ASM_REG_L $s2, 16($a0)
GRUB_ASM_REG_L $s3, 24($a0)
GRUB_ASM_REG_L $s4, 32($a0)
GRUB_ASM_REG_L $s5, 40($a0)
GRUB_ASM_REG_L $s6, 48($a0)
GRUB_ASM_REG_L $s7, 56($a0)
GRUB_ASM_REG_L $s8, 64($a0)
GRUB_ASM_REG_L $gp, 72($a0)
GRUB_ASM_REG_L $sp, 80($a0)
GRUB_ASM_REG_L $ra, 88($a0)
addiu $v0, $zero, 1
movn $v0, $a1, $a1
move $v1, $zero
jr $ra
nop
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,26 @@
#if defined(__i386__)
#include "./i386/setjmp.S"
#elif defined(__x86_64__)
#include "./x86_64/setjmp.S"
#elif defined(__sparc__)
#include "./sparc64/setjmp.S"
#elif defined(__mips__)
#if _MIPS_SIM == _ABI64
#include "./mips64/setjmp.S"
#else
#include "./mips/setjmp.S"
#endif
#elif defined(__powerpc__) || defined(__PPC__)
#include "./powerpc/setjmp.S"
#elif defined(__ia64__)
#include "./ia64/setjmp.S"
#include "./ia64/longjmp.S"
#elif defined(__arm__)
#include "./arm/setjmp.S"
#elif defined(__aarch64__)
#include "./arm64/setjmp.S"
#elif defined(__riscv)
#include "./riscv/setjmp.S"
#else
#error "Unknown target cpu type"
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,859 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2013 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/charset.h>
#include <grub/command.h>
#include <grub/err.h>
#include <grub/file.h>
#include <grub/fdt.h>
#include <grub/linux.h>
#include <grub/loader.h>
#include <grub/mm.h>
#include <grub/types.h>
#include <grub/cpu/linux.h>
#include <grub/efi/efi.h>
#include <grub/efi/fdtload.h>
#include <grub/efi/memory.h>
#include <grub/efi/pe32.h>
#include <grub/i18n.h>
#include <grub/lib/cmdline.h>
#include <grub/verify.h>
#include <grub/term.h>
#include <grub/env.h>
GRUB_MOD_LICENSE ("GPLv3+");
static grub_dl_t my_mod;
static int loaded;
static void *kernel_addr;
static grub_uint64_t kernel_size;
static char *linux_args;
static grub_uint32_t cmdline_size;
static grub_addr_t initrd_start;
static grub_addr_t initrd_end;
#define LINUX_MAX_ARGC 1024
static int ventoy_debug = 0;
static int ventoy_initrd_called = 0;
static int ventoy_linux_argc = 0;
static char **ventoy_linux_args = NULL;
static int ventoy_extra_initrd_num = 0;
static char *ventoy_extra_initrd_list[256];
static grub_err_t
grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[]);
grub_err_t
grub_arch_efi_linux_check_image (struct linux_arch_kernel_header * lh)
{
if (lh->magic != GRUB_LINUX_ARMXX_MAGIC_SIGNATURE)
return grub_error(GRUB_ERR_BAD_OS, "invalid magic number");
if ((lh->code0 & 0xffff) != GRUB_PE32_MAGIC)
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
N_("plain image kernel not supported - rebuild with CONFIG_(U)EFI_STUB enabled"));
grub_dprintf ("linux", "UEFI stub kernel:\n");
grub_dprintf ("linux", "PE/COFF header @ %08x\n", lh->hdr_offset);
return GRUB_ERR_NONE;
}
static grub_err_t
finalize_params_linux (void)
{
int node, retval;
void *fdt;
fdt = grub_fdt_load (GRUB_EFI_LINUX_FDT_EXTRA_SPACE);
if (!fdt)
goto failure;
node = grub_fdt_find_subnode (fdt, 0, "chosen");
if (node < 0)
node = grub_fdt_add_subnode (fdt, 0, "chosen");
if (node < 1)
goto failure;
/* Set initrd info */
if (initrd_start && initrd_end > initrd_start)
{
grub_dprintf ("linux", "Initrd @ %p-%p\n",
(void *) initrd_start, (void *) initrd_end);
retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-start",
initrd_start);
if (retval)
goto failure;
retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-end",
initrd_end);
if (retval)
goto failure;
}
if (grub_fdt_install() != GRUB_ERR_NONE)
goto failure;
return GRUB_ERR_NONE;
failure:
grub_fdt_unload();
return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT");
}
grub_err_t
grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args)
{
grub_efi_memory_mapped_device_path_t *mempath;
grub_efi_handle_t image_handle;
grub_efi_boot_services_t *b;
grub_efi_status_t status;
grub_efi_loaded_image_t *loaded_image;
int len;
mempath = grub_malloc (2 * sizeof (grub_efi_memory_mapped_device_path_t));
if (!mempath)
return grub_errno;
mempath[0].header.type = GRUB_EFI_HARDWARE_DEVICE_PATH_TYPE;
mempath[0].header.subtype = GRUB_EFI_MEMORY_MAPPED_DEVICE_PATH_SUBTYPE;
mempath[0].header.length = grub_cpu_to_le16_compile_time (sizeof (*mempath));
mempath[0].memory_type = GRUB_EFI_LOADER_DATA;
mempath[0].start_address = addr;
mempath[0].end_address = addr + size;
mempath[1].header.type = GRUB_EFI_END_DEVICE_PATH_TYPE;
mempath[1].header.subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
mempath[1].header.length = sizeof (grub_efi_device_path_t);
b = grub_efi_system_table->boot_services;
status = b->load_image (0, grub_efi_image_handle,
(grub_efi_device_path_t *) mempath,
(void *) addr, size, &image_handle);
if (status != GRUB_EFI_SUCCESS)
return grub_error (GRUB_ERR_BAD_OS, "cannot load image");
grub_dprintf ("linux", "linux command line: '%s'\n", args);
/* Convert command line to UCS-2 */
loaded_image = grub_efi_get_loaded_image (image_handle);
loaded_image->load_options_size = len =
(grub_strlen (args) + 1) * sizeof (grub_efi_char16_t);
loaded_image->load_options =
grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
if (!loaded_image->load_options)
return grub_errno;
loaded_image->load_options_size =
2 * grub_utf8_to_utf16 (loaded_image->load_options, len,
(grub_uint8_t *) args, len, NULL);
grub_dprintf ("linux", "starting image %p\n", image_handle);
status = b->start_image (image_handle, 0, NULL);
/* When successful, not reached */
b->unload_image (image_handle);
grub_efi_free_pages ((grub_addr_t) loaded_image->load_options,
GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
return grub_errno;
}
static void ventoy_debug_pause(void)
{
char key;
if (0 == ventoy_debug)
{
return;
}
grub_printf("press Enter to continue ......\n");
while (1)
{
key = grub_getkey();
if (key == '\n' || key == '\r')
{
break;
}
}
}
static int ventoy_preboot(void)
{
int i;
const char *file;
char buf[128];
if (ventoy_debug)
{
grub_printf("ventoy_preboot %d %d\n", ventoy_linux_argc, ventoy_initrd_called);
ventoy_debug_pause();
}
if (ventoy_linux_argc == 0)
{
return 0;
}
if (ventoy_initrd_called)
{
ventoy_initrd_called = 0;
return 0;
}
grub_snprintf(buf, sizeof(buf), "mem:%s:size:%s", grub_env_get("ventoy_cpio_addr"), grub_env_get("ventoy_cpio_size"));
ventoy_extra_initrd_list[ventoy_extra_initrd_num++] = grub_strdup(buf);
file = grub_env_get("vtoy_img_part_file");
if (file)
{
ventoy_extra_initrd_list[ventoy_extra_initrd_num++] = grub_strdup(file);
}
if (ventoy_debug)
{
grub_printf("========== initrd list ==========\n");
for (i = 0; i < ventoy_extra_initrd_num; i++)
{
grub_printf("%s\n", ventoy_extra_initrd_list[i]);
}
grub_printf("=================================\n");
ventoy_debug_pause();
}
grub_cmd_initrd(NULL, ventoy_extra_initrd_num, ventoy_extra_initrd_list);
return 0;
}
static int ventoy_boot_opt_filter(char *opt)
{
if (grub_strcmp(opt, "noinitrd") == 0)
{
return 1;
}
if (grub_strcmp(opt, "vga=current") == 0)
{
return 1;
}
if (grub_strncmp(opt, "rdinit=", 7) == 0)
{
if (grub_strcmp(opt, "rdinit=/vtoy/vtoy") != 0)
{
opt[0] = 'v';
opt[1] = 't';
}
return 0;
}
if (grub_strncmp(opt, "init=", 5) == 0)
{
opt[0] = 'v';
opt[1] = 't';
return 0;
}
if (grub_strncmp(opt, "dm=", 3) == 0)
{
opt[0] = 'D';
opt[1] = 'M';
return 0;
}
if (ventoy_debug)
{
if (grub_strcmp(opt, "quiet") == 0)
{
return 1;
}
if (grub_strncmp(opt, "loglevel=", 9) == 0)
{
return 1;
}
if (grub_strcmp(opt, "splash") == 0)
{
return 1;
}
}
return 0;
}
static int ventoy_bootopt_hook(int argc, char *argv[])
{
int i;
int TmpIdx;
int count = 0;
const char *env;
char c;
char *newenv;
char *last, *pos;
//grub_printf("ventoy_bootopt_hook: %d %d\n", argc, ventoy_linux_argc);
if (ventoy_linux_argc == 0)
{
return 0;
}
/* To avoid --- parameter, we split two parts */
for (TmpIdx = 0; TmpIdx < argc; TmpIdx++)
{
if (ventoy_boot_opt_filter(argv[TmpIdx]))
{
continue;
}
if (grub_strncmp(argv[TmpIdx], "--", 2) == 0)
{
break;
}
ventoy_linux_args[count++] = grub_strdup(argv[TmpIdx]);
}
for (i = 0; i < ventoy_linux_argc; i++)
{
ventoy_linux_args[count] = ventoy_linux_args[i + (LINUX_MAX_ARGC / 2)];
ventoy_linux_args[i + (LINUX_MAX_ARGC / 2)] = NULL;
if (ventoy_linux_args[count][0] == '@')
{
env = grub_env_get(ventoy_linux_args[count] + 1);
if (env)
{
grub_free(ventoy_linux_args[count]);
newenv = grub_strdup(env);
last = newenv;
while (*last)
{
while (*last)
{
if (*last != ' ' && *last != '\t')
{
break;
}
last++;
}
if (*last == 0)
{
break;
}
for (pos = last; *pos; pos++)
{
if (*pos == ' ' || *pos == '\t')
{
c = *pos;
*pos = 0;
if (0 == ventoy_boot_opt_filter(last))
{
ventoy_linux_args[count++] = grub_strdup(last);
}
*pos = c;
break;
}
}
if (*pos == 0)
{
if (0 == ventoy_boot_opt_filter(last))
{
ventoy_linux_args[count++] = grub_strdup(last);
}
break;
}
last = pos + 1;
}
}
else
{
count++;
}
}
else
{
count++;
}
}
while (TmpIdx < argc)
{
if (ventoy_boot_opt_filter(argv[TmpIdx]))
{
continue;
}
ventoy_linux_args[count++] = grub_strdup(argv[TmpIdx]);
TmpIdx++;
}
if (ventoy_debug)
{
ventoy_linux_args[count++] = grub_strdup("loglevel=7");
}
ventoy_linux_argc = count;
if (ventoy_debug)
{
grub_printf("========== bootoption ==========\n");
for (i = 0; i < count; i++)
{
grub_printf("%s ", ventoy_linux_args[i]);
}
grub_printf("\n================================\n");
}
return 0;
}
static grub_err_t
grub_cmd_set_boot_opt (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
int i;
const char *vtdebug;
for (i = 0; i < argc; i++)
{
ventoy_linux_args[ventoy_linux_argc + (LINUX_MAX_ARGC / 2) ] = grub_strdup(argv[i]);
ventoy_linux_argc++;
}
vtdebug = grub_env_get("vtdebug_flag");
if (vtdebug && vtdebug[0])
{
ventoy_debug = 1;
}
if (ventoy_debug) grub_printf("ventoy set boot opt %d\n", ventoy_linux_argc);
return 0;
}
static grub_err_t
grub_cmd_unset_boot_opt (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
int i;
(void)argc;
(void)argv;
for (i = 0; i < LINUX_MAX_ARGC; i++)
{
if (ventoy_linux_args[i])
{
grub_free(ventoy_linux_args[i]);
}
}
ventoy_debug = 0;
ventoy_linux_argc = 0;
ventoy_initrd_called = 0;
grub_memset(ventoy_linux_args, 0, sizeof(char *) * LINUX_MAX_ARGC);
return 0;
}
static grub_err_t
grub_cmd_extra_initrd_append (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
int newclen = 0;
char *pos = NULL;
char *end = NULL;
char buf[256] = {0};
if (argc != 1)
{
return 1;
}
for (pos = argv[0]; *pos; pos++)
{
if (*pos == '/')
{
end = pos;
}
}
if (end)
{
/* grub2 newc bug workaround */
newclen = (int)grub_strlen(end + 1);
if ((110 + newclen) % 4 == 0)
{
grub_snprintf(buf, sizeof(buf), "newc:.%s:%s", end + 1, argv[0]);
}
else
{
grub_snprintf(buf, sizeof(buf), "newc:%s:%s", end + 1, argv[0]);
}
if (ventoy_extra_initrd_num < 256)
{
ventoy_extra_initrd_list[ventoy_extra_initrd_num++] = grub_strdup(buf);
}
}
return 0;
}
static grub_err_t
grub_cmd_extra_initrd_reset (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
int i;
(void)argc;
(void)argv;
for (i = 0; i < ventoy_extra_initrd_num; i++)
{
if (ventoy_extra_initrd_list[i])
{
grub_free(ventoy_extra_initrd_list[i]);
}
}
grub_memset(ventoy_extra_initrd_list, 0, sizeof(ventoy_extra_initrd_list));
return 0;
}
static grub_err_t
grub_linux_boot (void)
{
ventoy_preboot();
if (finalize_params_linux () != GRUB_ERR_NONE)
return grub_errno;
return (grub_arch_efi_linux_boot_image((grub_addr_t)kernel_addr,
kernel_size, linux_args));
}
static grub_err_t
grub_linux_unload (void)
{
grub_dl_unref (my_mod);
loaded = 0;
if (initrd_start)
grub_efi_free_pages ((grub_efi_physical_address_t) initrd_start,
GRUB_EFI_BYTES_TO_PAGES (initrd_end - initrd_start));
initrd_start = initrd_end = 0;
grub_free (linux_args);
if (kernel_addr)
grub_efi_free_pages ((grub_addr_t) kernel_addr,
GRUB_EFI_BYTES_TO_PAGES (kernel_size));
grub_fdt_unload ();
return GRUB_ERR_NONE;
}
/*
* As per linux/Documentation/arm/Booting
* ARM initrd needs to be covered by kernel linear mapping,
* so place it in the first 512MB of DRAM.
*
* As per linux/Documentation/arm64/booting.txt
* ARM64 initrd needs to be contained entirely within a 1GB aligned window
* of up to 32GB of size that covers the kernel image as well.
* Since the EFI stub loader will attempt to load the kernel near start of
* RAM, place the buffer in the first 32GB of RAM.
*/
#ifdef __arm__
#define INITRD_MAX_ADDRESS_OFFSET (512U * 1024 * 1024)
#else /* __aarch64__ */
#define INITRD_MAX_ADDRESS_OFFSET (32ULL * 1024 * 1024 * 1024)
#endif
/*
* This function returns a pointer to a legally allocated initrd buffer,
* or NULL if unsuccessful
*/
static void *
allocate_initrd_mem (int initrd_pages)
{
grub_addr_t max_addr;
if (grub_efi_get_ram_base (&max_addr) != GRUB_ERR_NONE)
return NULL;
max_addr += INITRD_MAX_ADDRESS_OFFSET - 1;
return grub_efi_allocate_pages_real (max_addr, initrd_pages,
GRUB_EFI_ALLOCATE_MAX_ADDRESS,
GRUB_EFI_LOADER_DATA);
}
static grub_err_t
grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
int initrd_size, initrd_pages;
void *initrd_mem = NULL;
if (argc == 0)
{
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
goto fail;
}
if (!loaded)
{
grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("you need to load the kernel first"));
goto fail;
}
if (grub_initrd_init (argc, argv, &initrd_ctx))
goto fail;
initrd_size = grub_get_initrd_size (&initrd_ctx);
grub_dprintf ("linux", "Loading initrd\n");
initrd_pages = (GRUB_EFI_BYTES_TO_PAGES (initrd_size));
initrd_mem = allocate_initrd_mem (initrd_pages);
if (!initrd_mem)
{
grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
goto fail;
}
if (grub_initrd_load (&initrd_ctx, argv, initrd_mem))
goto fail;
initrd_start = (grub_addr_t) initrd_mem;
initrd_end = initrd_start + initrd_size;
grub_dprintf ("linux", "[addr=%p, size=0x%x]\n",
(void *) initrd_start, initrd_size);
fail:
grub_initrd_close (&initrd_ctx);
if (initrd_mem && !initrd_start)
grub_efi_free_pages ((grub_addr_t) initrd_mem, initrd_pages);
return grub_errno;
}
static grub_err_t
grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
grub_file_t file = 0;
struct linux_arch_kernel_header lh;
grub_err_t err;
grub_dl_ref (my_mod);
if (argc == 0)
{
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
goto fail;
}
file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL);
if (!file)
goto fail;
kernel_size = grub_file_size (file);
if (grub_file_read (file, &lh, sizeof (lh)) < (long) sizeof (lh))
return grub_errno;
if (grub_arch_efi_linux_check_image (&lh) != GRUB_ERR_NONE)
goto fail;
grub_loader_unset();
grub_dprintf ("linux", "kernel file size: %lld\n", (long long) kernel_size);
kernel_addr = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (kernel_size));
grub_dprintf ("linux", "kernel numpages: %lld\n",
(long long) GRUB_EFI_BYTES_TO_PAGES (kernel_size));
if (!kernel_addr)
{
grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
goto fail;
}
grub_file_seek (file, 0);
if (grub_file_read (file, kernel_addr, kernel_size)
< (grub_int64_t) kernel_size)
{
if (!grub_errno)
grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]);
goto fail;
}
grub_dprintf ("linux", "kernel @ %p\n", kernel_addr);
cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE);
linux_args = grub_malloc (cmdline_size);
if (!linux_args)
{
grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
goto fail;
}
grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE));
if (ventoy_linux_argc)
{
ventoy_bootopt_hook(argc, argv);
err = grub_create_loader_cmdline (ventoy_linux_argc, ventoy_linux_args,
linux_args + sizeof (LINUX_IMAGE) - 1,
cmdline_size,
GRUB_VERIFY_KERNEL_CMDLINE); }
else
{
err = grub_create_loader_cmdline (argc, argv,
linux_args + sizeof (LINUX_IMAGE) - 1,
cmdline_size,
GRUB_VERIFY_KERNEL_CMDLINE);
}
if (err)
goto fail;
if (grub_errno == GRUB_ERR_NONE)
{
grub_loader_set (grub_linux_boot, grub_linux_unload, 0);
loaded = 1;
}
fail:
if (file)
grub_file_close (file);
if (grub_errno != GRUB_ERR_NONE)
{
grub_dl_unref (my_mod);
loaded = 0;
}
if (linux_args && !loaded)
grub_free (linux_args);
if (kernel_addr && !loaded)
grub_efi_free_pages ((grub_addr_t) kernel_addr,
GRUB_EFI_BYTES_TO_PAGES (kernel_size));
return grub_errno;
}
static grub_err_t
ventoy_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
int argc, char *argv[])
{
int i;
const char *file;
char buf[64];
if (ventoy_debug) grub_printf("ventoy_cmd_initrd %d\n", ventoy_linux_argc);
if (ventoy_linux_argc == 0)
{
return grub_cmd_initrd(cmd, argc, argv);
}
grub_snprintf(buf, sizeof(buf), "mem:%s:size:%s", grub_env_get("ventoy_cpio_addr"), grub_env_get("ventoy_cpio_size"));
if (ventoy_debug) grub_printf("membuf=%s\n", buf);
ventoy_extra_initrd_list[ventoy_extra_initrd_num++] = grub_strdup(buf);
file = grub_env_get("vtoy_img_part_file");
if (file)
{
ventoy_extra_initrd_list[ventoy_extra_initrd_num++] = grub_strdup(file);
}
for (i = 0; i < argc; i++)
{
ventoy_extra_initrd_list[ventoy_extra_initrd_num++] = grub_strdup(argv[i]);
}
ventoy_initrd_called = 1;
if (ventoy_debug)
{
grub_printf("========== initrd list ==========\n");
for (i = 0; i < ventoy_extra_initrd_num; i++)
{
grub_printf("%s\n", ventoy_extra_initrd_list[i]);
}
grub_printf("=================================\n");
}
return grub_cmd_initrd(cmd, ventoy_extra_initrd_num, ventoy_extra_initrd_list);
}
static grub_command_t cmd_linux, cmd_initrd, cmd_linuxefi, cmd_initrdefi;
static grub_command_t cmd_set_bootopt, cmd_unset_bootopt, cmd_extra_initrd_append, cmd_extra_initrd_reset;
GRUB_MOD_INIT (linux)
{
cmd_linux = grub_register_command ("linux", grub_cmd_linux, 0,
N_("Load Linux."));
cmd_initrd = grub_register_command ("initrd", ventoy_cmd_initrd, 0,
N_("Load initrd."));
cmd_linuxefi = grub_register_command ("linuxefi", grub_cmd_linux,
0, N_("Load Linux."));
cmd_initrdefi = grub_register_command ("initrdefi", ventoy_cmd_initrd,
0, N_("Load initrd."));
cmd_set_bootopt = grub_register_command ("vt_set_boot_opt", grub_cmd_set_boot_opt, 0, N_("set ext boot opt"));
cmd_unset_bootopt = grub_register_command ("vt_unset_boot_opt", grub_cmd_unset_boot_opt, 0, N_("unset ext boot opt"));
cmd_extra_initrd_append = grub_register_command ("vt_img_extra_initrd_append", grub_cmd_extra_initrd_append, 0, N_(""));
cmd_extra_initrd_reset = grub_register_command ("vt_img_extra_initrd_reset", grub_cmd_extra_initrd_reset, 0, N_(""));
ventoy_linux_args = grub_zalloc(sizeof(char *) * LINUX_MAX_ARGC);
my_mod = mod;
}
GRUB_MOD_FINI (linux)
{
grub_unregister_command (cmd_linux);
grub_unregister_command (cmd_initrd);
}
@@ -0,0 +1,418 @@
/* 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;
dev = file->device;
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 != NULL)
{
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_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);
return 0;
fail:
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);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,327 @@
#include <grub/types.h>
#include <grub/err.h>
#include <grub/linux.h>
#include <grub/misc.h>
#include <grub/file.h>
#include <grub/mm.h>
#include <grub/env.h>
#include <grub/term.h>
struct newc_head
{
char magic[6];
char ino[8];
char mode[8];
char uid[8];
char gid[8];
char nlink[8];
char mtime[8];
char filesize[8];
char devmajor[8];
char devminor[8];
char rdevmajor[8];
char rdevminor[8];
char namesize[8];
char check[8];
} GRUB_PACKED;
struct grub_linux_initrd_component
{
grub_file_t file;
char *newc_name;
grub_off_t size;
};
struct dir
{
char *name;
struct dir *next;
struct dir *child;
};
static char
hex (grub_uint8_t val)
{
if (val < 10)
return '0' + val;
return 'a' + val - 10;
}
static void
set_field (char *var, grub_uint32_t val)
{
int i;
char *ptr = var;
for (i = 28; i >= 0; i -= 4)
*ptr++ = hex((val >> i) & 0xf);
}
static grub_uint8_t *
make_header (grub_uint8_t *ptr,
const char *name, grub_size_t len,
grub_uint32_t mode,
grub_off_t fsize)
{
struct newc_head *head = (struct newc_head *) ptr;
grub_uint8_t *optr;
grub_size_t oh = 0;
grub_memcpy (head->magic, "070701", 6);
set_field (head->ino, 0);
set_field (head->mode, mode);
set_field (head->uid, 0);
set_field (head->gid, 0);
set_field (head->nlink, 1);
set_field (head->mtime, 0);
set_field (head->filesize, fsize);
set_field (head->devmajor, 0);
set_field (head->devminor, 0);
set_field (head->rdevmajor, 0);
set_field (head->rdevminor, 0);
set_field (head->namesize, len);
set_field (head->check, 0);
optr = ptr;
ptr += sizeof (struct newc_head);
grub_memcpy (ptr, name, len);
ptr += len;
oh = ALIGN_UP_OVERHEAD (ptr - optr, 4);
grub_memset (ptr, 0, oh);
ptr += oh;
return ptr;
}
static void
free_dir (struct dir *root)
{
if (!root)
return;
free_dir (root->next);
free_dir (root->child);
grub_free (root->name);
grub_free (root);
}
static grub_size_t
insert_dir (const char *name, struct dir **root,
grub_uint8_t *ptr)
{
struct dir *cur, **head = root;
const char *cb, *ce = name;
grub_size_t size = 0;
while (1)
{
for (cb = ce; *cb == '/'; cb++);
for (ce = cb; *ce && *ce != '/'; ce++);
if (!*ce)
break;
for (cur = *root; cur; cur = cur->next)
if (grub_memcmp (cur->name, cb, ce - cb)
&& cur->name[ce - cb] == 0)
break;
if (!cur)
{
struct dir *n;
n = grub_zalloc (sizeof (*n));
if (!n)
return 0;
n->next = *head;
n->name = grub_strndup (cb, ce - cb);
if (ptr)
{
/*
* Create the substring with the trailing NUL byte
* to be included in the cpio header.
*/
char *tmp_name = grub_strndup (name, ce - name);
if (!tmp_name) {
grub_free (n->name);
grub_free (n);
return grub_errno;
}
grub_dprintf ("linux", "Creating directory %s, %s\n", name, ce);
ptr = make_header (ptr, tmp_name, ce - name + 1,
040777, 0);
grub_free (tmp_name);
}
size += ALIGN_UP ((ce - (char *) name + 1)
+ sizeof (struct newc_head), 4);
*head = n;
cur = n;
}
root = &cur->next;
}
return size;
}
grub_err_t
grub_initrd_init (int argc, char *argv[],
struct grub_linux_initrd_context *initrd_ctx)
{
int i;
int newc = 0;
struct dir *root = 0;
initrd_ctx->nfiles = 0;
initrd_ctx->components = 0;
initrd_ctx->components = grub_zalloc (argc
* sizeof (initrd_ctx->components[0]));
if (!initrd_ctx->components)
return grub_errno;
initrd_ctx->size = 0;
for (i = 0; i < argc; i++)
{
const char *fname = argv[i];
initrd_ctx->size = ALIGN_UP (initrd_ctx->size, 4);
if (grub_memcmp (argv[i], "newc:", 5) == 0)
{
const char *ptr, *eptr;
ptr = argv[i] + 5;
while (*ptr == '/')
ptr++;
eptr = grub_strchr (ptr, ':');
if (eptr)
{
initrd_ctx->components[i].newc_name = grub_strndup (ptr, eptr - ptr);
if (!initrd_ctx->components[i].newc_name)
{
grub_initrd_close (initrd_ctx);
return grub_errno;
}
initrd_ctx->size
+= ALIGN_UP (sizeof (struct newc_head)
+ grub_strlen (initrd_ctx->components[i].newc_name) + 1,
4);
initrd_ctx->size += insert_dir (initrd_ctx->components[i].newc_name,
&root, 0);
newc = 1;
fname = eptr + 1;
}
}
else if (newc)
{
initrd_ctx->size += ALIGN_UP (sizeof (struct newc_head)
+ sizeof ("TRAILER!!!"), 4);
free_dir (root);
root = 0;
newc = 0;
}
initrd_ctx->components[i].file = grub_file_open (fname,
GRUB_FILE_TYPE_LINUX_INITRD
| GRUB_FILE_TYPE_NO_DECOMPRESS);
if (!initrd_ctx->components[i].file)
{
grub_initrd_close (initrd_ctx);
return grub_errno;
}
initrd_ctx->nfiles++;
initrd_ctx->components[i].size
= grub_file_size (initrd_ctx->components[i].file);
initrd_ctx->size += initrd_ctx->components[i].size;
}
if (newc)
{
initrd_ctx->size = ALIGN_UP (initrd_ctx->size, 4);
initrd_ctx->size += ALIGN_UP (sizeof (struct newc_head)
+ sizeof ("TRAILER!!!"), 4);
free_dir (root);
root = 0;
}
return GRUB_ERR_NONE;
}
grub_size_t
grub_get_initrd_size (struct grub_linux_initrd_context *initrd_ctx)
{
return initrd_ctx->size;
}
void
grub_initrd_close (struct grub_linux_initrd_context *initrd_ctx)
{
int i;
if (!initrd_ctx->components)
return;
for (i = 0; i < initrd_ctx->nfiles; i++)
{
grub_free (initrd_ctx->components[i].newc_name);
grub_file_close (initrd_ctx->components[i].file);
}
grub_free (initrd_ctx->components);
initrd_ctx->components = 0;
}
extern int ventoy_need_prompt_load_file(void);
extern grub_ssize_t ventoy_load_file_with_prompt(grub_file_t file, void *buf, grub_ssize_t size);
grub_err_t
grub_initrd_load (struct grub_linux_initrd_context *initrd_ctx,
char *argv[], void *target)
{
grub_uint8_t *ptr = target;
int i;
int newc = 0;
struct dir *root = 0;
grub_ssize_t cursize = 0;
grub_ssize_t readsize = 0;
for (i = 0; i < initrd_ctx->nfiles; i++)
{
grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4));
ptr += ALIGN_UP_OVERHEAD (cursize, 4);
if (initrd_ctx->components[i].newc_name)
{
ptr += insert_dir (initrd_ctx->components[i].newc_name,
&root, ptr);
ptr = make_header (ptr, initrd_ctx->components[i].newc_name,
grub_strlen (initrd_ctx->components[i].newc_name) + 1,
0100777,
initrd_ctx->components[i].size);
newc = 1;
}
else if (newc)
{
ptr = make_header (ptr, "TRAILER!!!", sizeof ("TRAILER!!!"),
0, 0);
free_dir (root);
root = 0;
newc = 0;
}
cursize = initrd_ctx->components[i].size;
if (ventoy_need_prompt_load_file() && initrd_ctx->components[i].newc_name &&
grub_strcmp(initrd_ctx->components[i].newc_name, "boot.wim") == 0)
{
readsize = ventoy_load_file_with_prompt(initrd_ctx->components[i].file, ptr, cursize);
}
else
{
readsize = grub_file_read (initrd_ctx->components[i].file, ptr, cursize);
}
if (readsize != cursize)
{
if (!grub_errno)
grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
argv[i]);
grub_initrd_close (initrd_ctx);
return grub_errno;
}
ptr += cursize;
}
if (newc)
{
grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4));
ptr += ALIGN_UP_OVERHEAD (cursize, 4);
ptr = make_header (ptr, "TRAILER!!!", sizeof ("TRAILER!!!"), 0, 0);
}
free_dir (root);
root = 0;
return GRUB_ERR_NONE;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -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 (grub_env_get("ventoy_new_context") ? 0 : 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);
}
@@ -0,0 +1,575 @@
/* main.c - the normal mode main routine */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2000,2001,2002,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/kernel.h>
#include <grub/normal.h>
#include <grub/dl.h>
#include <grub/menu.h>
#include <grub/misc.h>
#include <grub/file.h>
#include <grub/mm.h>
#include <grub/term.h>
#include <grub/env.h>
#include <grub/parser.h>
#include <grub/reader.h>
#include <grub/menu_viewer.h>
#include <grub/auth.h>
#include <grub/i18n.h>
#include <grub/charset.h>
#include <grub/script_sh.h>
#include <grub/bufio.h>
GRUB_MOD_LICENSE ("GPLv3+");
#define GRUB_DEFAULT_HISTORY_SIZE 50
static int nested_level = 0;
int grub_normal_exit_level = 0;
void
grub_normal_free_menu (grub_menu_t menu)
{
grub_menu_entry_t entry = menu->entry_list;
while (entry)
{
grub_menu_entry_t next_entry = entry->next;
grub_size_t i;
if (entry->classes)
{
struct grub_menu_entry_class *class;
for (class = entry->classes; class; class = class->next)
grub_free (class->name);
grub_free (entry->classes);
}
if (entry->args)
{
for (i = 0; entry->args[i]; i++)
grub_free (entry->args[i]);
grub_free (entry->args);
}
if (entry->bls)
{
entry->bls->visible = 0;
}
grub_free ((void *) entry->id);
grub_free ((void *) entry->users);
grub_free ((void *) entry->title);
grub_free ((void *) entry->sourcecode);
grub_free (entry);
entry = next_entry;
}
grub_free (menu);
grub_env_unset_menu ();
}
/* Helper for read_config_file. */
static grub_err_t
read_config_file_getline (char **line, int cont __attribute__ ((unused)),
void *data)
{
grub_file_t file = data;
while (1)
{
char *buf;
*line = buf = grub_file_getline (file);
if (! buf)
return grub_errno;
if (buf[0] == '#')
grub_free (*line);
else
break;
}
return GRUB_ERR_NONE;
}
static grub_menu_t
read_config_file (const char *config)
{
grub_file_t rawfile, file;
char *old_file = 0, *old_dir = 0;
char *config_dir, *ptr = 0;
const char *ctmp;
grub_menu_t newmenu;
newmenu = grub_env_get_menu ();
if (! newmenu)
{
newmenu = grub_zalloc (sizeof (*newmenu));
if (! newmenu)
return 0;
grub_env_set_menu (newmenu);
}
/* Try to open the config file. */
rawfile = grub_file_open (config, GRUB_FILE_TYPE_CONFIG);
if (! rawfile)
return 0;
file = grub_bufio_open (rawfile, 0);
if (! file)
{
grub_file_close (rawfile);
return 0;
}
ctmp = grub_env_get ("config_file");
if (ctmp)
old_file = grub_strdup (ctmp);
ctmp = grub_env_get ("config_directory");
if (ctmp)
old_dir = grub_strdup (ctmp);
if (*config == '(')
{
grub_env_set ("config_file", config);
config_dir = grub_strdup (config);
}
else
{
/* $root is guranteed to be defined, otherwise open above would fail */
config_dir = grub_xasprintf ("(%s)%s", grub_env_get ("root"), config);
if (config_dir)
grub_env_set ("config_file", config_dir);
}
if (config_dir)
{
ptr = grub_strrchr (config_dir, '/');
if (ptr)
*ptr = 0;
grub_env_set ("config_directory", config_dir);
grub_free (config_dir);
}
grub_env_export ("config_file");
grub_env_export ("config_directory");
while (1)
{
char *line;
/* Print an error, if any. */
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
if ((read_config_file_getline (&line, 0, file)) || (! line))
break;
grub_normal_parse_line (line, read_config_file_getline, file);
grub_free (line);
}
if (old_file)
grub_env_set ("config_file", old_file);
else
grub_env_unset ("config_file");
if (old_dir)
grub_env_set ("config_directory", old_dir);
else
grub_env_unset ("config_directory");
grub_free (old_file);
grub_free (old_dir);
grub_file_close (file);
return newmenu;
}
/* Initialize the screen. */
void
grub_normal_init_page (struct grub_term_output *term,
int y)
{
grub_ssize_t msg_len;
int posx;
char *msg_formatted;
grub_uint32_t *unicode_msg;
grub_uint32_t *last_position;
grub_term_cls (term);
msg_formatted = grub_xasprintf (_("GNU GRUB version %s"), PACKAGE_VERSION);
if (!msg_formatted)
return;
msg_len = grub_utf8_to_ucs4_alloc (msg_formatted,
&unicode_msg, &last_position);
grub_free (msg_formatted);
if (msg_len < 0)
{
return;
}
posx = grub_getstringwidth (unicode_msg, last_position, term);
posx = ((int) grub_term_width (term) - posx) / 2;
if (posx < 0)
posx = 0;
grub_term_gotoxy (term, (struct grub_term_coordinate) { posx, y });
grub_print_ucs4 (unicode_msg, last_position, 0, 0, term);
grub_putcode ('\n', term);
grub_putcode ('\n', term);
grub_free (unicode_msg);
}
static void
read_lists (const char *val)
{
if (! grub_no_modules)
{
read_command_list (val);
read_fs_list (val);
read_crypto_list (val);
read_terminal_list (val);
}
grub_gettext_reread_prefix (val);
}
static char *
read_lists_hook (struct grub_env_var *var __attribute__ ((unused)),
const char *val)
{
read_lists (val);
return val ? grub_strdup (val) : NULL;
}
/* Read the config file CONFIG and execute the menu interface or
the command line interface if BATCH is false. */
void
grub_normal_execute (const char *config, int nested, int batch)
{
grub_menu_t menu = 0;
const char *prefix;
if (! nested)
{
prefix = grub_env_get ("prefix");
read_lists (prefix);
grub_register_variable_hook ("prefix", NULL, read_lists_hook);
}
grub_boot_time ("Executing config file");
if (config)
{
menu = read_config_file (config);
/* Ignore any error. */
grub_errno = GRUB_ERR_NONE;
}
grub_boot_time ("Executed config file");
if (! batch)
{
if (menu && menu->size)
{
grub_boot_time ("Entering menu");
grub_show_menu (menu, nested, 0);
if (nested)
grub_normal_free_menu (menu);
}
}
}
/* This starts the normal mode. */
void
grub_enter_normal_mode (const char *config)
{
grub_boot_time ("Entering normal mode");
nested_level++;
grub_normal_execute (config, 0, 0);
grub_boot_time ("Entering shell");
grub_cmdline_run (0, 1);
nested_level--;
if (grub_normal_exit_level)
grub_normal_exit_level--;
grub_boot_time ("Exiting normal mode");
}
/* Enter normal mode from rescue mode. */
static grub_err_t
grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)),
int argc, char *argv[])
{
if (argc == 0)
{
/* Guess the config filename. It is necessary to make CONFIG static,
so that it won't get broken by longjmp. */
char *config;
const char *prefix;
prefix = grub_env_get ("prefix");
if (prefix)
{
config = grub_xasprintf ("%s/grub.cfg", prefix);
if (! config)
goto quit;
grub_enter_normal_mode (config);
grub_free (config);
}
else
grub_enter_normal_mode (0);
}
else
grub_enter_normal_mode (argv[0]);
quit:
return 0;
}
/* Exit from normal mode to rescue mode. */
static grub_err_t
grub_cmd_normal_exit (struct grub_command *cmd __attribute__ ((unused)),
int argc __attribute__ ((unused)),
char *argv[] __attribute__ ((unused)))
{
if (nested_level <= grub_normal_exit_level)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "not in normal environment");
grub_normal_exit_level++;
return GRUB_ERR_NONE;
}
static grub_err_t
grub_normal_reader_init (int nested)
{
struct grub_term_output *term;
const char *msg_esc = _("ESC at any time exits.");
char *msg_formatted;
msg_formatted = grub_xasprintf (_("Minimal BASH-like line editing is supported. For "
"the first word, TAB lists possible command completions. Anywhere "
"else TAB lists possible device or file completions. %s"),
nested ? msg_esc : "");
if (!msg_formatted)
return grub_errno;
FOR_ACTIVE_TERM_OUTPUTS(term)
{
grub_normal_init_page (term, 1);
grub_term_setcursor (term, 1);
if (grub_term_width (term) > 3 + STANDARD_MARGIN + 20)
grub_print_message_indented (msg_formatted, 3, STANDARD_MARGIN, term);
else
grub_print_message_indented (msg_formatted, 0, 0, term);
grub_putcode ('\n', term);
grub_putcode ('\n', term);
grub_putcode ('\n', term);
}
grub_free (msg_formatted);
return 0;
}
static grub_err_t
grub_normal_read_line_real (char **line, int cont, int nested)
{
const char *prompt;
if (cont)
/* TRANSLATORS: it's command line prompt. */
prompt = _(">");
else
/* TRANSLATORS: it's command line prompt. */
prompt = _("grub>");
if (!prompt)
return grub_errno;
while (1)
{
*line = grub_cmdline_get (prompt);
if (*line)
return 0;
if (cont || nested)
{
grub_free (*line);
*line = 0;
return grub_errno;
}
}
}
static grub_err_t
grub_normal_read_line (char **line, int cont,
void *data __attribute__ ((unused)))
{
return grub_normal_read_line_real (line, cont, 0);
}
void
grub_cmdline_run (int nested, int force_auth)
{
grub_err_t err = GRUB_ERR_NONE;
do
{
err = grub_auth_check_authentication (NULL);
}
while (err && force_auth);
if (err)
{
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
return;
}
grub_normal_reader_init (nested);
while (1)
{
char *line = NULL;
if (grub_normal_exit_level)
break;
/* Print an error, if any. */
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
grub_normal_read_line_real (&line, 0, nested);
if (! line)
break;
grub_normal_parse_line (line, grub_normal_read_line, NULL);
grub_free (line);
}
}
static char *
grub_env_write_pager (struct grub_env_var *var __attribute__ ((unused)),
const char *val)
{
grub_set_more ((*val == '1'));
return grub_strdup (val);
}
/* clear */
static grub_err_t
grub_mini_cmd_clear (struct grub_command *cmd __attribute__ ((unused)),
int argc __attribute__ ((unused)),
char *argv[] __attribute__ ((unused)))
{
grub_cls ();
return 0;
}
static grub_command_t cmd_clear;
static void (*grub_xputs_saved) (const char *str);
static const char *features[] = {
"feature_chainloader_bpb", "feature_ntldr", "feature_platform_search_hint",
"feature_default_font_path", "feature_all_video_module",
"feature_menuentry_id", "feature_menuentry_options", "feature_200_final",
"feature_nativedisk_cmd", "feature_timeout_style"
};
GRUB_MOD_INIT(normal)
{
unsigned i;
grub_boot_time ("Preparing normal module");
/* Previously many modules depended on gzio. Be nice to user and load it. */
grub_dl_load ("gzio");
grub_errno = 0;
grub_normal_auth_init ();
grub_context_init ();
grub_script_init ();
grub_menu_init ();
grub_xputs_saved = grub_xputs;
grub_xputs = grub_xputs_normal;
/* Normal mode shouldn't be unloaded. */
if (mod)
grub_dl_ref (mod);
cmd_clear =
grub_register_command ("clear", grub_mini_cmd_clear,
0, N_("Clear the screen."));
grub_set_history (GRUB_DEFAULT_HISTORY_SIZE);
grub_register_variable_hook ("pager", 0, grub_env_write_pager);
grub_env_export ("pager");
/* Register a command "normal" for the rescue mode. */
grub_register_command ("normal", grub_cmd_normal,
0, N_("Enter normal mode."));
grub_register_command ("normal_exit", grub_cmd_normal_exit,
0, N_("Exit from normal mode."));
/* Reload terminal colors when these variables are written to. */
grub_register_variable_hook ("color_normal", NULL, grub_env_write_color_normal);
grub_register_variable_hook ("color_highlight", NULL, grub_env_write_color_highlight);
/* Preserve hooks after context changes. */
grub_env_export ("color_normal");
grub_env_export ("color_highlight");
/* Set default color names. */
grub_env_set ("color_normal", "light-gray/black");
grub_env_set ("color_highlight", "black/light-gray");
for (i = 0; i < ARRAY_SIZE (features); i++)
{
grub_env_set (features[i], "y");
grub_env_export (features[i]);
}
grub_env_set ("grub_cpu", GRUB_TARGET_CPU);
grub_env_export ("grub_cpu");
grub_env_set ("grub_platform", GRUB_PLATFORM);
grub_env_export ("grub_platform");
grub_boot_time ("Normal module prepared");
}
GRUB_MOD_FINI(normal)
{
grub_context_fini ();
grub_script_fini ();
grub_menu_fini ();
grub_normal_auth_fini ();
grub_xputs = grub_xputs_saved;
grub_set_history (0);
grub_register_variable_hook ("pager", 0, 0);
grub_fs_autoload_hook = 0;
grub_unregister_command (cmd_clear);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,655 @@
/* 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;
extern char g_ventoy_hotkey_tip[256];
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;
int *menu_title_offset;
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)
{
#if 0
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);
#endif
}
else
{
char szLine[128];
const char *checkret = grub_env_get("VTOY_CHKDEV_RESULT_STRING");
if (checkret == NULL || checkret[0] != '0') {
grub_snprintf(szLine, sizeof(szLine), "%s [Unofficial Ventoy]", grub_env_get("VTOY_TEXT_MENU_VER"));
} else {
grub_snprintf(szLine, sizeof(szLine), "%s", grub_env_get("VTOY_TEXT_MENU_VER"));
}
ret += grub_print_message_indented_real("\n", STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run);
ret += grub_print_message_indented_real(szLine, 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(g_ventoy_hotkey_tip,
3, 6, 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_scroll_chosen_entry (void *dataptr, int diren)
{
struct menu_viewer_data *data = dataptr;
const char *orig_title, *scrolled_title;
int off;
int selected;
grub_menu_entry_t entry;
if (!data->menu->size)
return;
selected = data->first + data->offset;
entry = grub_menu_get_entry (data->menu, selected);
orig_title = entry->title;
off = data->menu_title_offset[selected] + diren;
if (off < 0
|| off > grub_utf8_get_num_code (orig_title, grub_strlen(orig_title)))
return;
scrolled_title =
grub_utf8_offset_code (orig_title, grub_strlen (orig_title), off);
if (scrolled_title)
entry->title = scrolled_title;
print_entry (data->geo.first_entry_y + data->offset, 1, entry, data);
entry->title = orig_title;
data->menu_title_offset[selected] = off;
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);
if (data->menu_title_offset)
grub_free (data->menu_title_offset);
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;
}
if (menu->size)
data->menu_title_offset = grub_zalloc (sizeof (*data->menu_title_offset) * menu->size);
data->term = term;
instance->data = data;
instance->set_chosen_entry = menu_text_set_chosen_entry;
if (data->menu_title_offset)
instance->scroll_chosen_entry = menu_text_scroll_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;
}
@@ -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;
}
@@ -0,0 +1,232 @@
/* gpt.c - Read GUID Partition Tables (GPT). */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2002,2005,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/>.
*/
#include <grub/disk.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/partition.h>
#include <grub/dl.h>
#include <grub/msdos_partition.h>
#include <grub/gpt_partition.h>
#include <grub/i18n.h>
GRUB_MOD_LICENSE ("GPLv3+");
static grub_uint8_t grub_gpt_magic[8] =
{
0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54
};
static const grub_gpt_part_guid_t grub_gpt_partition_type_empty = GRUB_GPT_PARTITION_TYPE_EMPTY;
#ifdef GRUB_UTIL
static const grub_gpt_part_guid_t grub_gpt_partition_type_bios_boot = GRUB_GPT_PARTITION_TYPE_BIOS_BOOT;
#endif
/* 512 << 7 = 65536 byte sectors. */
#define MAX_SECTOR_LOG 7
static struct grub_partition_map grub_gpt_partition_map;
grub_err_t
grub_gpt_partition_map_iterate (grub_disk_t disk,
grub_partition_iterate_hook_t hook,
void *hook_data)
{
struct grub_partition part;
struct grub_gpt_header gpt;
struct grub_gpt_partentry entry;
struct grub_msdos_partition_mbr mbr;
grub_uint64_t entries;
unsigned int i;
int last_offset = 0;
int sector_log = 0;
/* Read the protective MBR. */
if (grub_disk_read (disk, 0, 0, sizeof (mbr), &mbr))
return grub_errno;
/* Check if it is valid. */
if (mbr.signature != grub_cpu_to_le16_compile_time (GRUB_PC_PARTITION_SIGNATURE))
return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature");
/* Make sure the MBR is a protective MBR and not a normal MBR. */
for (i = 0; i < 4; i++)
if (mbr.entries[i].type == GRUB_PC_PARTITION_TYPE_GPT_DISK)
break;
if (i == 4)
return grub_error (GRUB_ERR_BAD_PART_TABLE, "no GPT partition map found");
/* Read the GPT header. */
for (sector_log = 0; sector_log < MAX_SECTOR_LOG; sector_log++)
{
if (grub_disk_read (disk, 1 << sector_log, 0, sizeof (gpt), &gpt))
return grub_errno;
if (grub_memcmp (gpt.magic, grub_gpt_magic, sizeof (grub_gpt_magic)) == 0)
break;
}
if (sector_log == MAX_SECTOR_LOG)
return grub_error (GRUB_ERR_BAD_PART_TABLE, "no valid GPT header");
grub_dprintf ("gpt", "Read a valid GPT header\n");
entries = grub_le_to_cpu64 (gpt.partitions) << sector_log;
for (i = 0; i < grub_le_to_cpu32 (gpt.maxpart); i++)
{
if (grub_disk_read (disk, entries, last_offset,
sizeof (entry), &entry))
return grub_errno;
if (grub_memcmp (&grub_gpt_partition_type_empty, &entry.type,
sizeof (grub_gpt_partition_type_empty)))
{
/* Calculate the first block and the size of the partition. */
part.start = grub_le_to_cpu64 (entry.start) << sector_log;
part.len = (grub_le_to_cpu64 (entry.end)
- grub_le_to_cpu64 (entry.start) + 1) << sector_log;
part.offset = entries;
part.number = i;
part.index = last_offset;
part.partmap = &grub_gpt_partition_map;
part.parent = disk->partition;
part.gpt_attrib = entry.attrib;
grub_dprintf ("gpt", "GPT entry %d: start=%lld, length=%lld\n", i,
(unsigned long long) part.start,
(unsigned long long) part.len);
if (hook (disk, &part, hook_data))
return grub_errno;
}
last_offset += grub_le_to_cpu32 (gpt.partentry_size);
if (last_offset == GRUB_DISK_SECTOR_SIZE)
{
last_offset = 0;
entries++;
}
}
return GRUB_ERR_NONE;
}
#ifdef GRUB_UTIL
/* Context for gpt_partition_map_embed. */
struct gpt_partition_map_embed_ctx
{
grub_disk_addr_t start, len;
};
/* Helper for gpt_partition_map_embed. */
static int
find_usable_region (grub_disk_t disk __attribute__ ((unused)),
const grub_partition_t p, void *data)
{
struct gpt_partition_map_embed_ctx *ctx = data;
struct grub_gpt_partentry gptdata;
grub_partition_t p2;
p2 = disk->partition;
disk->partition = p->parent;
if (grub_disk_read (disk, p->offset, p->index,
sizeof (gptdata), &gptdata))
{
disk->partition = p2;
return 0;
}
disk->partition = p2;
/* If there's an embed region, it is in a dedicated partition. */
if (! grub_memcmp (&gptdata.type, &grub_gpt_partition_type_bios_boot, 16))
{
ctx->start = p->start;
ctx->len = p->len;
return 1;
}
return 0;
}
static grub_err_t
gpt_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors,
unsigned int max_nsectors,
grub_embed_type_t embed_type,
grub_disk_addr_t **sectors)
{
struct gpt_partition_map_embed_ctx ctx = {
.start = 0,
.len = 0
};
unsigned i;
grub_err_t err;
if (embed_type != GRUB_EMBED_PCBIOS)
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"GPT currently supports only PC-BIOS embedding");
err = grub_gpt_partition_map_iterate (disk, find_usable_region, &ctx);
if (err)
return err;
if (ctx.len == 0)
return grub_error (GRUB_ERR_FILE_NOT_FOUND,
N_("this GPT partition label contains no BIOS Boot Partition;"
" embedding won't be possible"));
if (ctx.len < *nsectors)
return grub_error (GRUB_ERR_OUT_OF_RANGE,
N_("your BIOS Boot Partition is too small;"
" embedding won't be possible"));
*nsectors = ctx.len;
if (*nsectors > max_nsectors)
*nsectors = max_nsectors;
*sectors = grub_malloc (*nsectors * sizeof (**sectors));
if (!*sectors)
return grub_errno;
for (i = 0; i < *nsectors; i++)
(*sectors)[i] = ctx.start + i;
return GRUB_ERR_NONE;
}
#endif
/* Partition map type. */
static struct grub_partition_map grub_gpt_partition_map =
{
.name = "gpt",
.iterate = grub_gpt_partition_map_iterate,
#ifdef GRUB_UTIL
.embed = gpt_partition_map_embed
#endif
};
GRUB_MOD_INIT(part_gpt)
{
grub_partition_map_register (&grub_gpt_partition_map);
}
GRUB_MOD_FINI(part_gpt)
{
grub_partition_map_unregister (&grub_gpt_partition_map);
}
@@ -0,0 +1,113 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2007,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/script_sh.h>
#include <grub/parser.h>
#include <grub/mm.h>
#include <grub/charset.h>
grub_script_function_t grub_script_function_list;
grub_script_function_t
grub_script_function_create (struct grub_script_arg *functionname_arg,
struct grub_script *cmd)
{
grub_script_function_t func;
grub_script_function_t *p;
func = (grub_script_function_t) grub_malloc (sizeof (*func));
if (! func)
return 0;
func->name = grub_strdup (functionname_arg->str);
if (! func->name)
{
grub_free (func);
return 0;
}
func->func = cmd;
/* Keep the list sorted for simplicity. */
p = &grub_script_function_list;
while (*p)
{
if (grub_strcmp ((*p)->name, func->name) >= 0)
break;
p = &((*p)->next);
}
/* If the function already exists, overwrite the old function. */
if (*p && grub_strcmp ((*p)->name, func->name) == 0)
{
grub_script_function_t q;
q = *p;
grub_script_free (q->func);
q->func = cmd;
grub_free (func);
func = q;
}
else
{
func->next = *p;
*p = func;
}
return func;
}
void
grub_script_function_remove (const char *name)
{
grub_script_function_t *p, q;
for (p = &grub_script_function_list, q = *p; q; p = &(q->next), q = q->next)
if (grub_strcmp (name, q->name) == 0)
{
*p = q->next;
grub_free (q->name);
grub_script_free (q->func);
grub_free (q);
break;
}
}
grub_script_function_t
grub_script_function_find (char *functionname)
{
grub_script_function_t func;
for (func = grub_script_function_list; func; func = func->next)
if (grub_strcmp (functionname, func->name) == 0)
break;
if (! func)
{
char tmp[64];
grub_strncpy (tmp, functionname, 63);
tmp[63] = 0;
/* Avoid truncating inside UTF-8 character. */
tmp[grub_getend (tmp, tmp + grub_strlen (tmp))] = 0;
grub_error (GRUB_ERR_UNKNOWN_COMMAND, N_("can't find command `%s'"), tmp);
}
return func;
}
@@ -0,0 +1,260 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2022 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/term.h>
#include <grub/misc.h>
#include <grub/types.h>
#include <grub/command.h>
#include <grub/i18n.h>
#include <grub/err.h>
#include <grub/env.h>
#include <grub/efi/efi.h>
#include <grub/efi/api.h>
GRUB_MOD_LICENSE ("GPLv3+");
#define GRUB_EFI_SIMPLE_POINTER_GUID \
{ 0x31878c87, 0x0b75, 0x11d5, \
{ 0x9a, 0x4f, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \
}
typedef struct
{
grub_efi_int32_t x;
grub_efi_int32_t y;
grub_efi_int32_t z;
grub_efi_boolean_t left;
grub_efi_boolean_t right;
} grub_efi_mouse_state;
grub_efi_mouse_state no_move = {0, 0, 0, 0, 0};
typedef struct
{
grub_efi_uint64_t x;
grub_efi_uint64_t y;
grub_efi_uint64_t z;
grub_efi_boolean_t left;
grub_efi_boolean_t right;
} grub_efi_mouse_mode;
struct grub_efi_simple_pointer_protocol
{
grub_efi_status_t (*reset) (struct grub_efi_simple_pointer_protocol *this,
grub_efi_boolean_t extended_verification);
grub_efi_status_t (*get_state) (struct grub_efi_simple_pointer_protocol *this,
grub_efi_mouse_state *state);
grub_efi_event_t *wait_for_input;
grub_efi_mouse_mode *mode;
};
typedef struct grub_efi_simple_pointer_protocol grub_efi_simple_pointer_protocol_t;
typedef struct
{
grub_efi_uintn_t count;
grub_efi_simple_pointer_protocol_t **mouse;
} grub_efi_mouse_prot_t;
static grub_int32_t
mouse_div (grub_int32_t a, grub_uint64_t b)
{
grub_int32_t s = 1, q, ret;
grub_uint64_t n = a;
if (!b)
return 0;
if (a < 0)
{
s = -1;
n = -a;
}
q = grub_divmod64 (n, b, NULL);
ret = s * (q > 0 ? q : -q);
return ret;
}
static grub_efi_mouse_prot_t *
grub_efi_mouse_prot_init (void)
{
grub_efi_status_t status;
grub_efi_guid_t mouse_guid = GRUB_EFI_SIMPLE_POINTER_GUID;
grub_efi_mouse_prot_t *mouse_input = NULL;
grub_efi_boot_services_t *b = grub_efi_system_table->boot_services;
grub_efi_handle_t *buf;
grub_efi_uintn_t count;
grub_efi_uintn_t i;
status = efi_call_5 (b->locate_handle_buffer, GRUB_EFI_BY_PROTOCOL,
&mouse_guid, NULL, &count, &buf);
if (status != GRUB_EFI_SUCCESS)
{
#ifdef MOUSE_DEBUG
grub_printf ("ERROR: SimplePointerProtocol not found.\n");
#endif
return NULL;
}
mouse_input = grub_malloc (sizeof (grub_efi_mouse_prot_t));
if (!mouse_input)
goto end;
mouse_input->mouse = grub_malloc (count
* sizeof (grub_efi_simple_pointer_protocol_t *));
if (!mouse_input->mouse)
{
grub_free (mouse_input);
mouse_input = NULL;
goto end;
}
mouse_input->count = count;
for (i = 0; i < count; i++)
{
efi_call_3 (b->handle_protocol,
buf[i], &mouse_guid, (void **)&mouse_input->mouse[i]);
#ifdef MOUSE_DEBUG
grub_printf ("%d %p ", (int)i, mouse_input->mouse[i]);
#endif
efi_call_2 (mouse_input->mouse[i]->reset, mouse_input->mouse[i], 1);
#ifdef MOUSE_DEBUG
grub_printf
("[%"PRIuGRUB_UINT64_T"] [%"PRIuGRUB_UINT64_T"] [%"PRIuGRUB_UINT64_T"]\n",
mouse_input->mouse[i]->mode->x,
mouse_input->mouse[i]->mode->y, mouse_input->mouse[i]->mode->z);
#endif
}
end:
efi_call_1(b->free_pool, buf);
return mouse_input;
}
static grub_err_t
grub_efi_mouse_input_init (struct grub_term_input *term)
{
grub_efi_mouse_prot_t *mouse_input = NULL;
if (term->data)
return 0;
mouse_input = grub_efi_mouse_prot_init ();
if (!mouse_input)
return GRUB_ERR_BAD_OS;
term->data = (void *)mouse_input;
return 0;
}
static int
grub_mouse_getkey (struct grub_term_input *term)
{
grub_efi_mouse_state cur;
grub_efi_mouse_prot_t *mouse = term->data;
//int x;
int y;
int delta = 0;
const char *env;
grub_efi_uintn_t i;
if (!mouse)
return GRUB_TERM_NO_KEY;
env = grub_env_get("mouse_delta");
if (env)
delta = (int)grub_strtol(env, NULL, 10);
for (i = 0; i < mouse->count; i++)
{
efi_call_2 (mouse->mouse[i]->get_state, mouse->mouse[i], &cur);
if (grub_memcmp (&cur, &no_move, sizeof (grub_efi_mouse_state)) != 0)
{
y = mouse_div (cur.y, mouse->mouse[i]->mode->y);
if (cur.left)
return 0x0d;
if (cur.right)
return GRUB_TERM_ESC;
if (y > delta)
return GRUB_TERM_KEY_DOWN;
if (y < -delta)
return GRUB_TERM_KEY_UP;
}
}
return GRUB_TERM_NO_KEY;
}
#ifdef MOUSE_DEBUG
static grub_err_t
grub_cmd_mouse_test (grub_command_t cmd __attribute__ ((unused)),
int argc __attribute__ ((unused)),
char **args __attribute__ ((unused)))
{
grub_efi_mouse_state cur;
int x = 0, y = 0, z = 0;
grub_efi_uintn_t i;
grub_efi_mouse_prot_t *mouse = NULL;
mouse = grub_efi_mouse_prot_init ();
if (!mouse)
return grub_error (GRUB_ERR_BAD_OS, "mouse not found.\n");
grub_printf ("Press [1] to exit.\n");
while (1)
{
if (grub_getkey_noblock () == '1')
break;
for (i = 0; i < mouse->count; i++)
{
efi_call_2 (mouse->mouse[i]->get_state, mouse->mouse[i], &cur);
if (grub_memcmp (&cur, &no_move, sizeof (grub_efi_mouse_state)) != 0)
{
x = mouse_div (cur.x, mouse->mouse[i]->mode->x);
y = mouse_div (cur.y, mouse->mouse[i]->mode->y);
z = mouse_div (cur.z, mouse->mouse[i]->mode->z);
grub_printf ("[ID=%d] X=%d Y=%d Z=%d L=%d R=%d\n",
(int)i, x, y, z, cur.left, cur.right);
}
}
grub_refresh ();
}
grub_free (mouse->mouse);
grub_free (mouse);
return GRUB_ERR_NONE;
}
static grub_command_t cmd;
#endif
static struct grub_term_input grub_mouse_term_input =
{
.name = "mouse",
.getkey = grub_mouse_getkey,
.init = grub_efi_mouse_input_init,
};
GRUB_MOD_INIT(mouse)
{
grub_term_register_input ("mouse", &grub_mouse_term_input);
#ifdef MOUSE_DEBUG
cmd = grub_register_command ("mouse_test", grub_cmd_mouse_test, 0,
N_("UEFI mouse test."));
#endif
}
GRUB_MOD_FINI(mouse)
{
grub_term_unregister_input (&grub_mouse_term_input);
#ifdef MOUSE_DEBUG
grub_unregister_command (cmd);
#endif
}
@@ -0,0 +1,795 @@
#define ventoy_keyboard_set_layout(name) if (grub_strcmp(layout, #name) == 0) return ventoy_keyboard_layout_##name()
static void ventoy_keyboard_layout_QWERTY_USA(void) {
grub_keymap_reset();
grub_keymap_disable();
}
static void ventoy_keyboard_layout_AZERTY(void) {
grub_keymap_reset();
grub_keymap_add_by_string("a", "q");
grub_keymap_add_by_string("A", "Q");
grub_keymap_add_by_string("z", "w");
grub_keymap_add_by_string("Z", "W");
grub_keymap_add_by_string("q", "a");
grub_keymap_add_by_string("Q", "A");
grub_keymap_add_by_string("m", "semicolon");
grub_keymap_add_by_string("M", "colon");
grub_keymap_add_by_string("w", "z");
grub_keymap_add_by_string("W", "Z");
grub_keymap_add_by_string("comma", "m");
grub_keymap_add_by_string("question", "M");
grub_keymap_add_by_string("semicolon", "comma");
grub_keymap_add_by_string("period", "less");
grub_keymap_add_by_string("colon", "period");
grub_keymap_add_by_string("slash", "greater");
grub_keymap_add_by_string("exclam", "slash");
grub_keymap_add_by_string("dollar", "bracketright");
grub_keymap_add_by_string("asterisk", "backslash");
grub_keymap_add_by_string("percent", "doublequote");
grub_keymap_add_by_string("ampersand", "1");
grub_keymap_add_by_string("1", "exclam");
grub_keymap_add_by_string("tilde", "2");
grub_keymap_add_by_string("2", "at");
grub_keymap_add_by_string("doublequote", "3");
grub_keymap_add_by_string("3", "numbersign");
grub_keymap_add_by_string("quote", "4");
grub_keymap_add_by_string("4", "dollar");
grub_keymap_add_by_string("parenleft", "5");
grub_keymap_add_by_string("5", "percent");
grub_keymap_add_by_string("minus", "6");
grub_keymap_add_by_string("6", "caret");
grub_keymap_add_by_string("backquote", "7");
grub_keymap_add_by_string("7", "ampersand");
grub_keymap_add_by_string("underscore", "8");
grub_keymap_add_by_string("8", "asterisk");
grub_keymap_add_by_string("caret", "9");
grub_keymap_add_by_string("9", "parenleft");
grub_keymap_add_by_string("at", "0");
grub_keymap_add_by_string("0", "parenright");
grub_keymap_add_by_string("parenright", "minus");
grub_keymap_add_by_string("less", "backquote");
grub_keymap_add_by_string("greater", "tilde");
grub_keymap_add_by_string("numbersign", "braceright");
grub_keymap_add_by_string("backslash", "question");
grub_keymap_add_by_string("bracketright", "braceleft");
grub_keymap_add_by_string("braceleft", "quote");
grub_keymap_add_by_string("braceright", "underscore");
grub_keymap_enable();
}
static void ventoy_keyboard_layout_CZECH_QWERTY(void) {
grub_keymap_reset();
grub_keymap_add_by_string("semicolon", "backquote");
grub_keymap_add_by_string("plus", "1");
grub_keymap_add_by_string("equal", "minus");
grub_keymap_add_by_string("quote", "equal");
grub_keymap_add_by_string("parenright", "bracketright");
grub_keymap_add_by_string("doublequote", "backslash");
grub_keymap_add_by_string("minus", "slash");
grub_keymap_add_by_string("1", "exclam");
grub_keymap_add_by_string("2", "at");
grub_keymap_add_by_string("3", "numbersign");
grub_keymap_add_by_string("4", "dollar");
grub_keymap_add_by_string("5", "percent");
grub_keymap_add_by_string("6", "caret");
grub_keymap_add_by_string("7", "ampersand");
grub_keymap_add_by_string("8", "asterisk");
grub_keymap_add_by_string("9", "parenleft");
grub_keymap_add_by_string("0", "parenright");
grub_keymap_add_by_string("percent", "underscore");
grub_keymap_add_by_string("slash", "braceleft");
grub_keymap_add_by_string("parenleft", "braceright");
grub_keymap_add_by_string("doublequote", "colon");
grub_keymap_add_by_string("exclam", "doublequote");
grub_keymap_add_by_string("quote", "bar");
grub_keymap_add_by_string("question", "less");
grub_keymap_add_by_string("colon", "greater");
grub_keymap_add_by_string("underscore", "question");
grub_keymap_add_by_string("backquote", "Abackquote");
grub_keymap_add_by_string("exclam", "A1");
grub_keymap_add_by_string("at", "A2");
grub_keymap_add_by_string("numbersign", "A3");
grub_keymap_add_by_string("dollar", "A4");
grub_keymap_add_by_string("percent", "A5");
grub_keymap_add_by_string("caret", "A6");
grub_keymap_add_by_string("ampersand", "A7");
grub_keymap_add_by_string("asterisk", "A8");
grub_keymap_add_by_string("parenleft", "A9");
grub_keymap_add_by_string("parenright", "A0");
grub_keymap_add_by_string("minus", "Aminus");
grub_keymap_add_by_string("equal", "Aequal");
grub_keymap_add_by_string("bracketleft", "Abracketleft");
grub_keymap_add_by_string("bracketright", "Abracketright");
grub_keymap_add_by_string("semicolon", "Asemicolon");
grub_keymap_add_by_string("backslash", "Abackslash");
grub_keymap_add_by_string("less", "Acomma");
grub_keymap_add_by_string("greater", "Aperiod");
grub_keymap_add_by_string("slash", "Aslash");
grub_keymap_add_by_string("tilde", "Atilde");
grub_keymap_add_by_string("underscore", "Aunderscore");
grub_keymap_add_by_string("plus", "Aplus");
grub_keymap_add_by_string("braceleft", "Abraceleft");
grub_keymap_add_by_string("braceright", "Abraceright");
grub_keymap_add_by_string("caret", "Adoublequote");
grub_keymap_add_by_string("colon", "Acolon");
grub_keymap_add_by_string("question", "Aquestion");
grub_keymap_add_by_string("bar", "Abar");
grub_keymap_enable();
}
static void ventoy_keyboard_layout_CZECH_QWERTZ(void) {
grub_keymap_reset();
grub_keymap_add_by_string("y", "z");
grub_keymap_add_by_string("z", "y");
grub_keymap_add_by_string("Y", "Z");
grub_keymap_add_by_string("Z", "Y");
grub_keymap_add_by_string("semicolon", "backquote");
grub_keymap_add_by_string("plus", "1");
grub_keymap_add_by_string("equal", "minus");
grub_keymap_add_by_string("quote", "equal");
grub_keymap_add_by_string("parenright", "bracketright");
grub_keymap_add_by_string("doublequote", "backslash");
grub_keymap_add_by_string("minus", "slash");
grub_keymap_add_by_string("1", "exclam");
grub_keymap_add_by_string("2", "at");
grub_keymap_add_by_string("3", "numbersign");
grub_keymap_add_by_string("4", "dollar");
grub_keymap_add_by_string("5", "percent");
grub_keymap_add_by_string("6", "caret");
grub_keymap_add_by_string("7", "ampersand");
grub_keymap_add_by_string("8", "asterisk");
grub_keymap_add_by_string("9", "parenleft");
grub_keymap_add_by_string("0", "parenright");
grub_keymap_add_by_string("percent", "underscore");
grub_keymap_add_by_string("slash", "braceleft");
grub_keymap_add_by_string("parenleft", "braceright");
grub_keymap_add_by_string("doublequote", "colon");
grub_keymap_add_by_string("exclam", "doublequote");
grub_keymap_add_by_string("quote", "bar");
grub_keymap_add_by_string("question", "less");
grub_keymap_add_by_string("colon", "greater");
grub_keymap_add_by_string("underscore", "question");
grub_keymap_add_by_string("tilde", "A1");
grub_keymap_add_by_string("caret", "A3");
grub_keymap_add_by_string("backslash", "Aq");
grub_keymap_add_by_string("bar", "Aw");
grub_keymap_add_by_string("bracketleft", "Af");
grub_keymap_add_by_string("bracketright", "Ag");
grub_keymap_add_by_string("dollar", "Asemicolon");
grub_keymap_add_by_string("numbersign", "Ax");
grub_keymap_add_by_string("ampersand", "Ac");
grub_keymap_add_by_string("at", "Av");
grub_keymap_add_by_string("braceleft", "Ab");
grub_keymap_add_by_string("braceright", "An");
grub_keymap_add_by_string("less", "Acomma");
grub_keymap_add_by_string("greater", "Aperiod");
grub_keymap_add_by_string("asterisk", "Aslash");
grub_keymap_enable();
}
static void ventoy_keyboard_layout_DANISH(void) {
grub_keymap_reset();
grub_keymap_add_by_string("plus", "minus");
grub_keymap_add_by_string("quote", "equal");
grub_keymap_add_by_string("doublequote", "bracketright");
grub_keymap_add_by_string("quote", "backslash");
grub_keymap_add_by_string("minus", "slash");
grub_keymap_add_by_string("doublequote", "at");
grub_keymap_add_by_string("ampersand", "caret");
grub_keymap_add_by_string("slash", "ampersand");
grub_keymap_add_by_string("parenleft", "asterisk");
grub_keymap_add_by_string("parenright", "parenleft");
grub_keymap_add_by_string("equal", "parenright");
grub_keymap_add_by_string("question", "underscore");
grub_keymap_add_by_string("caret", "braceright");
grub_keymap_add_by_string("asterisk", "bar");
grub_keymap_add_by_string("backquote", "plus");
grub_keymap_add_by_string("semicolon", "less");
grub_keymap_add_by_string("colon", "greater");
grub_keymap_add_by_string("underscore", "question");
grub_keymap_add_by_string("bar", "Atilde");
grub_keymap_add_by_string("backslash", "Abackquote");
grub_keymap_add_by_string("greater", "tilde");
grub_keymap_add_by_string("at", "A2");
grub_keymap_add_by_string("dollar", "A4");
grub_keymap_add_by_string("braceleft", "A7");
grub_keymap_add_by_string("bracketleft", "A8");
grub_keymap_add_by_string("bracketright", "A9");
grub_keymap_add_by_string("braceright", "A0");
grub_keymap_add_by_string("backslash", "Aminus");
grub_keymap_add_by_string("less", "quote");
grub_keymap_add_by_string("greater", "doublequote");
grub_keymap_add_by_string("tilde", "Abracketright");
grub_keymap_enable();
}
static void ventoy_keyboard_layout_DVORAK_USA(void) {
grub_keymap_reset();
grub_keymap_add_by_string("[", "minus");
grub_keymap_add_by_string("braceleft", "underscore");
grub_keymap_add_by_string("quote", "q");
grub_keymap_add_by_string("doublequote", "Q");
grub_keymap_add_by_string("comma", "w");
grub_keymap_add_by_string("less", "W");
grub_keymap_add_by_string("s", "semicolon");
grub_keymap_add_by_string("S", "colon");
grub_keymap_add_by_string("semicolon", "z");
grub_keymap_add_by_string("colon", "Z");
grub_keymap_add_by_string("w", "comma");
grub_keymap_add_by_string("W", "less");
grub_keymap_add_by_string("v", "period");
grub_keymap_add_by_string("z", "greater");
grub_keymap_add_by_string("z", "slash");
grub_keymap_add_by_string("equal", "bracketright");
grub_keymap_add_by_string("backslash", "backslash");
grub_keymap_add_by_string("underscore", "doublequote");
grub_keymap_add_by_string("quote", "q");
grub_keymap_add_by_string("doublequote", "Q");
grub_keymap_add_by_string("comma", "w");
grub_keymap_add_by_string("less", "W");
grub_keymap_add_by_string("period", "e");
grub_keymap_add_by_string("greater", "E");
grub_keymap_add_by_string("p", "r");
grub_keymap_add_by_string("P", "R");
grub_keymap_add_by_string("y", "t");
grub_keymap_add_by_string("Y", "T");
grub_keymap_add_by_string("f", "y");
grub_keymap_add_by_string("F", "Y");
grub_keymap_add_by_string("g", "u");
grub_keymap_add_by_string("G", "U");
grub_keymap_add_by_string("c", "c");
grub_keymap_add_by_string("C", "I");
grub_keymap_add_by_string("r", "o");
grub_keymap_add_by_string("R", "O");
grub_keymap_add_by_string("l", "p");
grub_keymap_add_by_string("L", "P");
grub_keymap_add_by_string("bracketright", "equal");
grub_keymap_add_by_string("braceright", "plus");
grub_keymap_add_by_string("a", "a");
grub_keymap_add_by_string("A", "A");
grub_keymap_add_by_string("o", "s");
grub_keymap_add_by_string("O", "S");
grub_keymap_add_by_string("e", "d");
grub_keymap_add_by_string("E", "D");
grub_keymap_add_by_string("u", "f");
grub_keymap_add_by_string("U", "F");
grub_keymap_add_by_string("i", "g");
grub_keymap_add_by_string("I", "G");
grub_keymap_add_by_string("d", "h");
grub_keymap_add_by_string("D", "H");
grub_keymap_add_by_string("h", "j");
grub_keymap_add_by_string("H", "J");
grub_keymap_add_by_string("t", "k");
grub_keymap_add_by_string("T", "K");
grub_keymap_add_by_string("n", "l");
grub_keymap_add_by_string("N", "L");
grub_keymap_add_by_string("s", "semicolon");
grub_keymap_add_by_string("S", "colon");
grub_keymap_add_by_string("minus", "quote");
grub_keymap_add_by_string("underscore", "doublequote");
grub_keymap_add_by_string("semicolon", "z");
grub_keymap_add_by_string("colon", "Z");
grub_keymap_add_by_string("q", "x");
grub_keymap_add_by_string("Q", "X");
grub_keymap_add_by_string("j", "c");
grub_keymap_add_by_string("J", "C");
grub_keymap_add_by_string("k", "v");
grub_keymap_add_by_string("K", "V");
grub_keymap_add_by_string("x", "b");
grub_keymap_add_by_string("X", "B");
grub_keymap_add_by_string("b", "n");
grub_keymap_add_by_string("B", "N");
grub_keymap_add_by_string("w", "comma");
grub_keymap_add_by_string("W", "less");
grub_keymap_add_by_string("v", "period");
grub_keymap_add_by_string("V", "greater");
grub_keymap_add_by_string("z", "slash");
grub_keymap_add_by_string("Z", "question");
grub_keymap_add_by_string("slash", "bracketleft");
grub_keymap_add_by_string("question", "braceleft");
grub_keymap_add_by_string("equal", "bracketright");
grub_keymap_add_by_string("plus", "braceright");
grub_keymap_enable();
}
static void ventoy_keyboard_layout_FRENCH(void) {
grub_keymap_reset();
grub_keymap_add_by_string("less", "backquote");
grub_keymap_add_by_string("greater", "tilde");
grub_keymap_add_by_string("ampersand", "1");
grub_keymap_add_by_string("1", "exclam");
grub_keymap_add_by_string("tilde", "2");
grub_keymap_add_by_string("2", "at");
grub_keymap_add_by_string("doublequote", "3");
grub_keymap_add_by_string("3", "numbersign");
grub_keymap_add_by_string("quote", "4");
grub_keymap_add_by_string("4", "dollar");
grub_keymap_add_by_string("parenleft", "5");
grub_keymap_add_by_string("5", "percent");
grub_keymap_add_by_string("minus", "6");
grub_keymap_add_by_string("6", "caret");
grub_keymap_add_by_string("backquote", "7");
grub_keymap_add_by_string("7", "ampersand");
grub_keymap_add_by_string("underscore", "8");
grub_keymap_add_by_string("8", "asterisk");
grub_keymap_add_by_string("backslash", "9");
grub_keymap_add_by_string("9", "parenleft");
grub_keymap_add_by_string("at", "0");
grub_keymap_add_by_string("0", "parenright");
grub_keymap_add_by_string("parenright", "minus");
grub_keymap_add_by_string("numbersign", "underscore");
grub_keymap_add_by_string("a", "q");
grub_keymap_add_by_string("A", "Q");
grub_keymap_add_by_string("z", "w");
grub_keymap_add_by_string("Z", "W");
grub_keymap_add_by_string("caret", "bracketleft");
grub_keymap_add_by_string("dollar", "bracketright");
grub_keymap_add_by_string("q", "a");
grub_keymap_add_by_string("Q", "A");
grub_keymap_add_by_string("m", "semicolon");
grub_keymap_add_by_string("M", "colon");
grub_keymap_add_by_string("bracketleft", "quote");
grub_keymap_add_by_string("percent", "doublequote");
grub_keymap_add_by_string("asterisk", "backslash");
grub_keymap_add_by_string("bracketright", "bar");
grub_keymap_add_by_string("w", "z");
grub_keymap_add_by_string("W", "Z");
grub_keymap_add_by_string("comma", "m");
grub_keymap_add_by_string("question", "M");
grub_keymap_add_by_string("semicolon", "comma");
grub_keymap_add_by_string("period", "less");
grub_keymap_add_by_string("colon", "period");
grub_keymap_add_by_string("slash", "greater");
grub_keymap_add_by_string("exclam", "slash");
grub_keymap_add_by_string("bar", "question");
grub_keymap_add_by_string("tilde", "A2");
grub_keymap_add_by_string("numbersign", "A3");
grub_keymap_add_by_string("braceleft", "A4");
grub_keymap_add_by_string("bracketleft", "A5");
grub_keymap_add_by_string("bar", "A6");
grub_keymap_add_by_string("quote", "A7");
grub_keymap_add_by_string("backslash", "A8");
grub_keymap_add_by_string("caret", "A9");
grub_keymap_add_by_string("at", "A0");
grub_keymap_add_by_string("bracketright", "Aminus");
grub_keymap_add_by_string("braceright", "Aequal");
grub_keymap_enable();
}
static void ventoy_keyboard_layout_GERMAN(void) {
grub_keymap_reset();
grub_keymap_add_by_string("doublequote", "at");
grub_keymap_add_by_string("ampersand", "caret");
grub_keymap_add_by_string("slash", "ampersand");
grub_keymap_add_by_string("parenleft", "asterisk");
grub_keymap_add_by_string("parenright", "parenleft");
grub_keymap_add_by_string("equal", "parenright");
grub_keymap_add_by_string("question", "underscore");
grub_keymap_add_by_string("backslash", "minus");
grub_keymap_add_by_string("z", "y");
grub_keymap_add_by_string("Z", "Y");
grub_keymap_add_by_string("y", "z");
grub_keymap_add_by_string("Y", "Z");
grub_keymap_add_by_string("plus", "bracketright");
grub_keymap_add_by_string("asterisk", "braceright");
grub_keymap_add_by_string("semicolon", "less");
grub_keymap_add_by_string("colon", "greater");
grub_keymap_add_by_string("minus", "slash");
grub_keymap_add_by_string("backslash", "Aminus");
grub_keymap_add_by_string("underscore", "question");
grub_keymap_add_by_string("caret", "backquote");
grub_keymap_add_by_string("quote", "equal");
grub_keymap_add_by_string("backquote", "plus");
grub_keymap_add_by_string("braceright", "doublequote");
grub_keymap_add_by_string("bar", "bracketleft");
grub_keymap_add_by_string("at", "braceleft");
grub_keymap_add_by_string("numbersign", "backslash");
grub_keymap_add_by_string("at", "Aq");
grub_keymap_add_by_string("less", "backquote");
grub_keymap_add_by_string("greater", "tilde");
grub_keymap_add_by_string("braceleft", "A7");
grub_keymap_add_by_string("bracketleft", "A8");
grub_keymap_add_by_string("bracketright", "A9");
grub_keymap_add_by_string("braceright", "A0");
grub_keymap_add_by_string("tilde", "Abracketright");
grub_keymap_add_by_string("backslash", "Aminus");
grub_keymap_add_by_string("quote", "bar");
grub_keymap_add_by_string("greater", "semicolon");
grub_keymap_add_by_string("less", "colon");
grub_keymap_add_by_string("bar", "quote");
grub_keymap_enable();
}
static void ventoy_keyboard_layout_ITALIANO(void) {
grub_keymap_reset();
grub_keymap_add_by_string("backslash", "backquote");
grub_keymap_add_by_string("bar", "tilde");
grub_keymap_add_by_string("doublequote", "at");
grub_keymap_add_by_string("tilde", "numbersign");
grub_keymap_add_by_string("ampersand", "caret");
grub_keymap_add_by_string("slash", "ampersand");
grub_keymap_add_by_string("parenleft", "asterisk");
grub_keymap_add_by_string("parenright", "parenleft");
grub_keymap_add_by_string("equal", "parenright");
grub_keymap_add_by_string("quote", "minus");
grub_keymap_add_by_string("question", "underscore");
grub_keymap_add_by_string("caret", "plus");
grub_keymap_add_by_string("bracketleft", "bracketleft");
grub_keymap_add_by_string("bracketright", "braceleft");
grub_keymap_add_by_string("plus", "bracketright");
grub_keymap_add_by_string("asterisk", "braceright");
grub_keymap_add_by_string("at", "semicolon");
grub_keymap_add_by_string("braceleft", "colon");
grub_keymap_add_by_string("numbersign", "quote");
grub_keymap_add_by_string("braceright", "doublequote");
grub_keymap_add_by_string("less", "backslash");
grub_keymap_add_by_string("greater", "bar");
grub_keymap_add_by_string("minus", "slash");
grub_keymap_add_by_string("underscore", "question");
grub_keymap_add_by_string("semicolon", "less");
grub_keymap_add_by_string("colon", "greater");
grub_keymap_add_by_string("bracketleft", "Abracketleft");
grub_keymap_add_by_string("bracketright", "Abracketright");
grub_keymap_add_by_string("at", "Asemicolon");
grub_keymap_add_by_string("numbersign", "Aquote");
grub_keymap_add_by_string("braceright", "Abraceright");
grub_keymap_add_by_string("braceleft", "Abraceleft");
grub_keymap_enable();
}
static void ventoy_keyboard_layout_JAPAN_106(void) {
grub_keymap_reset();
grub_keymap_add_by_string("at", "bracketleft");
grub_keymap_add_by_string("doublequote", "at");
grub_keymap_add_by_string("ampersand", "caret");
grub_keymap_add_by_string("quote", "ampersand");
grub_keymap_add_by_string("parenleft", "asterisk");
grub_keymap_add_by_string("parenright", "parenleft");
grub_keymap_add_by_string("underscore", "parenright");
grub_keymap_add_by_string("equal", "underscore");
grub_keymap_add_by_string("plus", "colon");
grub_keymap_add_by_string("colon", "quote");
grub_keymap_add_by_string("asterisk", "doublequote");
grub_keymap_add_by_string("bracketleft", "bracketright");
grub_keymap_add_by_string("braceleft", "braceright");
grub_keymap_add_by_string("bracketright", "backslash");
grub_keymap_add_by_string("braceright", "bar");
grub_keymap_add_by_string("backslash", "backquote");
grub_keymap_add_by_string("tilde", "plus");
grub_keymap_add_by_string("caret", "equal");
grub_keymap_add_by_string("backquote", "braceleft");
grub_keymap_add_by_string("bar", "tilde");
grub_keymap_enable();
}
static void ventoy_keyboard_layout_LATIN_USA(void) {
grub_keymap_reset();
grub_keymap_add_by_string("bar", "backquote");
grub_keymap_add_by_string("quote", "minus");
grub_keymap_add_by_string("question", "underscore");
grub_keymap_add_by_string("backquote", "bracketleft");
grub_keymap_add_by_string("plus", "bracketright");
grub_keymap_add_by_string("braceleft", "quote");
grub_keymap_add_by_string("braceright", "backslash");
grub_keymap_add_by_string("minus", "slash");
grub_keymap_add_by_string("ampersand", "caret");
grub_keymap_add_by_string("doublequote", "at");
grub_keymap_add_by_string("slash", "ampersand");
grub_keymap_add_by_string("parenleft", "asterisk");
grub_keymap_add_by_string("parenright", "parenleft");
grub_keymap_add_by_string("equal", "parenright");
grub_keymap_add_by_string("asterisk", "braceright");
grub_keymap_add_by_string("bracketleft", "doublequote");
grub_keymap_add_by_string("bracketright", "bar");
grub_keymap_add_by_string("semicolon", "less");
grub_keymap_add_by_string("colon", "greater");
grub_keymap_add_by_string("underscore", "question");
grub_keymap_add_by_string("caret", "Aquote");
grub_keymap_add_by_string("doublequote", "braceleft");
grub_keymap_add_by_string("at", "Aq");
grub_keymap_add_by_string("backquote", "Abackslash");
grub_keymap_add_by_string("backslash", "Aminus");
grub_keymap_add_by_string("greater", "plus");
grub_keymap_add_by_string("less", "equal");
grub_keymap_add_by_string("backslash", "Aminus");
grub_keymap_add_by_string("backquote", "Abackslash");
grub_keymap_add_by_string("tilde", "Abracketright");
grub_keymap_add_by_string("caret", "Aquote");
grub_keymap_enable();
}
static void ventoy_keyboard_layout_PORTU_BRAZIL(void) {
grub_keymap_reset();
grub_keymap_add_by_string("quote", "backquote");
grub_keymap_add_by_string("quote", "bracketleft");
grub_keymap_add_by_string("bracketleft", "bracketright");
grub_keymap_add_by_string("tilde", "quote");
grub_keymap_add_by_string("bracketright", "backslash");
grub_keymap_add_by_string("semicolon", "slash");
grub_keymap_add_by_string("bar", "colon");
grub_keymap_add_by_string("doublequote", "tilde");
grub_keymap_add_by_string("backquote", "braceleft");
grub_keymap_add_by_string("braceleft", "braceright");
grub_keymap_add_by_string("caret", "doublequote");
grub_keymap_add_by_string("braceright", "bar");
grub_keymap_add_by_string("colon", "question");
grub_keymap_add_by_string("backslash", "semicolon");
grub_keymap_add_by_string("bar", "Atilde");
grub_keymap_add_by_string("backslash", "Abackquote");
grub_keymap_add_by_string("slash", "Aq");
grub_keymap_add_by_string("question", "Aw");
grub_keymap_enable();
}
static void ventoy_keyboard_layout_QWERTY_UK(void) {
grub_keymap_reset();
grub_keymap_add_by_string("at", "doublequote");
grub_keymap_add_by_string("doublequote", "at");
grub_keymap_add_by_string("tilde", "bar");
grub_keymap_add_by_string("numbersign", "backslash");
grub_keymap_add_by_string("backslash", "numbersign");
grub_keymap_add_by_string("bar", "tilde");
grub_keymap_add_by_string("backslash", "Atilde");
grub_keymap_add_by_string("backslash", "Abackquote");
grub_keymap_enable();
}
static void ventoy_keyboard_layout_QWERTZ(void) {
grub_keymap_reset();
grub_keymap_add_by_string("doublequote", "at");
grub_keymap_add_by_string("ampersand", "percent");
grub_keymap_add_by_string("slash", "ampersand");
grub_keymap_add_by_string("equal", "parenright");
grub_keymap_add_by_string("question", "underscore");
grub_keymap_add_by_string("backslash", "minus");
grub_keymap_add_by_string("z", "y");
grub_keymap_add_by_string("Z", "Y");
grub_keymap_add_by_string("y", "z");
grub_keymap_add_by_string("Y", "Z");
grub_keymap_add_by_string("plus", "bracketright");
grub_keymap_add_by_string("asterisk", "braceright");
grub_keymap_add_by_string("semicolon", "less");
grub_keymap_add_by_string("colon", "greater");
grub_keymap_add_by_string("minus", "slash");
grub_keymap_add_by_string("underscore", "question");
grub_keymap_add_by_string("caret", "backquote");
grub_keymap_add_by_string("backquote", "equal");
grub_keymap_add_by_string("numbersign", "backslash");
grub_keymap_add_by_string("parenright", "parenleft");
grub_keymap_add_by_string("parenleft", "asterisk");
grub_keymap_add_by_string("percent", "caret");
grub_keymap_add_by_string("less", "numbersign");
grub_keymap_add_by_string("greater", "bar");
grub_keymap_enable();
}
static void ventoy_keyboard_layout_QWERTZ_HUN(void) {
grub_keymap_reset();
grub_keymap_add_by_string("y", "z");
grub_keymap_add_by_string("z", "y");
grub_keymap_add_by_string("Y", "Z");
grub_keymap_add_by_string("Z", "Y");
grub_keymap_add_by_string("0", "backquote");
grub_keymap_add_by_string("quote", "exclam");
grub_keymap_add_by_string("doublequote", "at");
grub_keymap_add_by_string("plus", "numbersign");
grub_keymap_add_by_string("exclam", "dollar");
grub_keymap_add_by_string("slash", "caret");
grub_keymap_add_by_string("equal", "ampersand");
grub_keymap_add_by_string("parenleft", "asterisk");
grub_keymap_add_by_string("parenright", "parenleft");
grub_keymap_add_by_string("backslash", "Aq");
grub_keymap_add_by_string("bar", "Aw");
grub_keymap_add_by_string("bracketleft", "Af");
grub_keymap_add_by_string("bracketright", "Ag");
grub_keymap_add_by_string("greater", "Az");
grub_keymap_add_by_string("numbersign", "Ax");
grub_keymap_add_by_string("ampersand", "Ac");
grub_keymap_add_by_string("at", "Av");
grub_keymap_add_by_string("braceleft", "Ab");
grub_keymap_add_by_string("braceright", "An");
grub_keymap_add_by_string("less", "Am");
grub_keymap_add_by_string("dollar", "colon");
grub_keymap_add_by_string("question", "less");
grub_keymap_add_by_string("colon", "greater");
grub_keymap_add_by_string("minus", "slash");
grub_keymap_add_by_string("underscore", "question");
grub_keymap_add_by_string("at", "doublequote");
grub_keymap_add_by_string("tilde", "A1");
grub_keymap_add_by_string("caret", "A3");
grub_keymap_add_by_string("backquote", "A7");
grub_keymap_add_by_string("asterisk", "0");
grub_keymap_add_by_string("dollar", "Asemicolon");
grub_keymap_add_by_string("semicolon", "Acomma");
grub_keymap_add_by_string("greater", "Aperiod");
grub_keymap_add_by_string("asterisk", "Aslash");
grub_keymap_add_by_string("backquote", "A9");
grub_keymap_add_by_string("doublequote", "A0");
grub_keymap_enable();
}
static void ventoy_keyboard_layout_QWERTZ_SLOV_CROAT(void) {
grub_keymap_reset();
grub_keymap_add_by_string("quote", "minus");
grub_keymap_add_by_string("plus", "equal");
grub_keymap_add_by_string("y", "z");
grub_keymap_add_by_string("minus", "slash");
grub_keymap_add_by_string("doublequote", "tilde");
grub_keymap_add_by_string("doublequote", "at");
grub_keymap_add_by_string("ampersand", "caret");
grub_keymap_add_by_string("slash", "ampersand");
grub_keymap_add_by_string("parenleft", "asterisk");
grub_keymap_add_by_string("parenright", "parenleft");
grub_keymap_add_by_string("equal", "parenright");
grub_keymap_add_by_string("question", "underscore");
grub_keymap_add_by_string("asterisk", "plus");
grub_keymap_add_by_string("Y", "Z");
grub_keymap_add_by_string("semicolon", "less");
grub_keymap_add_by_string("colon", "greater");
grub_keymap_add_by_string("underscore", "question");
grub_keymap_add_by_string("tilde", "A1");
grub_keymap_add_by_string("caret", "A3");
grub_keymap_add_by_string("backquote", "A7");
grub_keymap_add_by_string("backslash", "Aq");
grub_keymap_add_by_string("bar", "Aw");
grub_keymap_add_by_string("bracketleft", "Af");
grub_keymap_add_by_string("bracketright", "Ag");
grub_keymap_add_by_string("at", "Av");
grub_keymap_add_by_string("braceleft", "Ab");
grub_keymap_add_by_string("braceright", "An");
grub_keymap_add_by_string("less", "Acomma");
grub_keymap_add_by_string("greater", "Aperiod");
grub_keymap_enable();
}
static void ventoy_keyboard_layout_SPANISH(void) {
grub_keymap_reset();
grub_keymap_add_by_string("slash", "ampersand");
grub_keymap_add_by_string("ampersand", "caret");
grub_keymap_add_by_string("caret", "braceleft");
grub_keymap_add_by_string("asterisk", "braceright");
grub_keymap_add_by_string("parenleft", "asterisk");
grub_keymap_add_by_string("parenright", "parenleft");
grub_keymap_add_by_string("minus", "slash");
grub_keymap_add_by_string("equal", "parenright");
grub_keymap_add_by_string("quote", "minus");
grub_keymap_add_by_string("underscore", "question");
grub_keymap_add_by_string("question", "underscore");
grub_keymap_add_by_string("colon", "greater");
grub_keymap_add_by_string("greater", "bar");
grub_keymap_add_by_string("doublequote", "at");
grub_keymap_add_by_string("backslash", "backquote");
grub_keymap_add_by_string("less", "backslash");
grub_keymap_add_by_string("semicolon", "less");
grub_keymap_add_by_string("backquote", "bracketleft");
grub_keymap_add_by_string("plus", "bracketright");
grub_keymap_add_by_string("plus", "colon");
grub_keymap_add_by_string("at", "semicolon");
grub_keymap_add_by_string("bar", "A1");
grub_keymap_add_by_string("at", "A2");
grub_keymap_add_by_string("numbersign", "A3");
grub_keymap_add_by_string("tilde", "A4");
grub_keymap_add_by_string("bracketleft", "Abracketleft");
grub_keymap_add_by_string("bracketright", "Abracketright");
grub_keymap_add_by_string("braceleft", "Aquote");
grub_keymap_add_by_string("braceright", "Abackslash");
grub_keymap_add_by_string("greater", "bar");
grub_keymap_add_by_string("less", "backslash");
grub_keymap_add_by_string("backslash", "Abackquote");
grub_keymap_enable();
}
static void ventoy_keyboard_layout_SWEDISH(void) {
grub_keymap_reset();
grub_keymap_add_by_string("plus", "minus");
grub_keymap_add_by_string("quote", "equal");
grub_keymap_add_by_string("doublequote", "bracketright");
grub_keymap_add_by_string("quote", "backslash");
grub_keymap_add_by_string("minus", "slash");
grub_keymap_add_by_string("doublequote", "at");
grub_keymap_add_by_string("ampersand", "caret");
grub_keymap_add_by_string("slash", "ampersand");
grub_keymap_add_by_string("parenleft", "asterisk");
grub_keymap_add_by_string("parenright", "parenleft");
grub_keymap_add_by_string("equal", "parenright");
grub_keymap_add_by_string("question", "underscore");
grub_keymap_add_by_string("caret", "braceright");
grub_keymap_add_by_string("asterisk", "bar");
grub_keymap_add_by_string("backquote", "plus");
grub_keymap_add_by_string("semicolon", "less");
grub_keymap_add_by_string("colon", "greater");
grub_keymap_add_by_string("underscore", "question");
grub_keymap_add_by_string("bar", "Atilde");
grub_keymap_add_by_string("backslash", "Abackquote");
grub_keymap_add_by_string("greater", "tilde");
grub_keymap_add_by_string("at", "A2");
grub_keymap_add_by_string("dollar", "A4");
grub_keymap_add_by_string("braceleft", "A7");
grub_keymap_add_by_string("bracketleft", "A8");
grub_keymap_add_by_string("bracketright", "A9");
grub_keymap_add_by_string("braceright", "A0");
grub_keymap_add_by_string("backslash", "Aminus");
grub_keymap_add_by_string("less", "quote");
grub_keymap_add_by_string("greater", "doublequote");
grub_keymap_add_by_string("tilde", "Abracketright");
grub_keymap_enable();
}
static void ventoy_keyboard_layout_TURKISH_Q(void) {
grub_keymap_reset();
grub_keymap_add_by_string("doublequote", "backquote");
grub_keymap_add_by_string("asterisk", "minus");
grub_keymap_add_by_string("minus", "equal");
grub_keymap_add_by_string("comma", "backslash");
grub_keymap_add_by_string("period", "slash");
grub_keymap_add_by_string("quote", "at");
grub_keymap_add_by_string("caret", "numbersign");
grub_keymap_add_by_string("plus", "dollar");
grub_keymap_add_by_string("ampersand", "caret");
grub_keymap_add_by_string("slash", "ampersand");
grub_keymap_add_by_string("parenleft", "asterisk");
grub_keymap_add_by_string("parenright", "parenleft");
grub_keymap_add_by_string("equal", "parenright");
grub_keymap_add_by_string("question", "underscore");
grub_keymap_add_by_string("underscore", "plus");
grub_keymap_add_by_string("semicolon", "bar");
grub_keymap_add_by_string("colon", "question");
grub_keymap_add_by_string("less", "Abackquote");
grub_keymap_add_by_string("greater", "A1");
grub_keymap_add_by_string("numbersign", "A3");
grub_keymap_add_by_string("dollar", "A4");
grub_keymap_add_by_string("braceleft", "A7");
grub_keymap_add_by_string("bracketleft", "A8");
grub_keymap_add_by_string("bracketright", "A9");
grub_keymap_add_by_string("braceright", "A0");
grub_keymap_add_by_string("backslash", "Aminus");
grub_keymap_add_by_string("bar", "Aequal");
grub_keymap_add_by_string("at", "Aq");
grub_keymap_add_by_string("doublequote", "Abracketleft");
grub_keymap_add_by_string("tilde", "Abracketright");
grub_keymap_enable();
}
static void ventoy_keyboard_layout_VIETNAMESE(void) {
grub_keymap_reset();
grub_keymap_add_by_string("exclam", "A1");
grub_keymap_add_by_string("at", "A2");
grub_keymap_add_by_string("numbersign", "A3");
grub_keymap_add_by_string("dollar", "A4");
grub_keymap_add_by_string("percent", "A5");
grub_keymap_add_by_string("caret", "A6");
grub_keymap_add_by_string("ampersand", "A7");
grub_keymap_add_by_string("asterisk", "A8");
grub_keymap_add_by_string("parenleft", "A9");
grub_keymap_add_by_string("parenright", "A0");
grub_keymap_add_by_string("plus", "Aplus");
grub_keymap_add_by_string("equal", "Aequal");
grub_keymap_add_by_string("braceleft", "Abraceleft");
grub_keymap_add_by_string("braceright", "Abraceright");
grub_keymap_add_by_string("colon", "Acolon");
grub_keymap_add_by_string("semicolon", "Asemicolon");
grub_keymap_add_by_string("quote", "Aquote");
grub_keymap_add_by_string("backslash", "Abackslash");
grub_keymap_add_by_string("less", "Aless");
grub_keymap_add_by_string("greater", "Agreater");
grub_keymap_add_by_string("comma", "Acomma");
grub_keymap_add_by_string("period", "Aperiod");
grub_keymap_add_by_string("question", "Aquestion");
grub_keymap_add_by_string("slash", "Aslash");
grub_keymap_add_by_string("tilde", "Atilde");
grub_keymap_add_by_string("backquote", "Abackquote");
grub_keymap_add_by_string("bracketright", "Abracketright");
grub_keymap_add_by_string("bracketleft", "Abracketleft");
grub_keymap_add_by_string("bar", "Abar");
grub_keymap_add_by_string("doublequote", "Adoublequote");
grub_keymap_add_by_string("colon", "Acolon");
grub_keymap_add_by_string("minus", "Aminus");
grub_keymap_add_by_string("underscore", "Aunderscore");
grub_keymap_enable();
}
void ventoy_set_keyboard_layout(const char *layout);
void ventoy_set_keyboard_layout(const char *layout) {
ventoy_keyboard_set_layout(QWERTY_USA);
ventoy_keyboard_set_layout(AZERTY);
ventoy_keyboard_set_layout(CZECH_QWERTY);
ventoy_keyboard_set_layout(CZECH_QWERTZ);
ventoy_keyboard_set_layout(DANISH);
ventoy_keyboard_set_layout(DVORAK_USA);
ventoy_keyboard_set_layout(FRENCH);
ventoy_keyboard_set_layout(GERMAN);
ventoy_keyboard_set_layout(ITALIANO);
ventoy_keyboard_set_layout(JAPAN_106);
ventoy_keyboard_set_layout(LATIN_USA);
ventoy_keyboard_set_layout(PORTU_BRAZIL);
ventoy_keyboard_set_layout(QWERTY_UK);
ventoy_keyboard_set_layout(QWERTZ);
ventoy_keyboard_set_layout(QWERTZ_HUN);
ventoy_keyboard_set_layout(QWERTZ_SLOV_CROAT);
ventoy_keyboard_set_layout(SPANISH);
ventoy_keyboard_set_layout(SWEDISH);
ventoy_keyboard_set_layout(TURKISH_Q);
ventoy_keyboard_set_layout(VIETNAMESE);
}
@@ -0,0 +1,463 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2000,2001,2002,2003,2004,2005,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/serial.h>
#include <grub/term.h>
#include <grub/types.h>
#include <grub/dl.h>
#include <grub/misc.h>
#include <grub/terminfo.h>
#if !defined (GRUB_MACHINE_EMU) && ((defined(__mips__) && _MIPS_SIM != _ABI64) || defined (__i386__) || defined (__x86_64__))
#include <grub/cpu/io.h>
#endif
#include <grub/extcmd.h>
#include <grub/i18n.h>
#include <grub/list.h>
#ifdef GRUB_MACHINE_MIPS_LOONGSON
#include <grub/machine/kernel.h>
#endif
#ifdef GRUB_MACHINE_IEEE1275
#include <grub/ieee1275/console.h>
#endif
GRUB_MOD_LICENSE ("GPLv3+");
#define FOR_SERIAL_PORTS(var) FOR_LIST_ELEMENTS((var), (grub_serial_ports))
enum
{
OPTION_UNIT,
OPTION_PORT,
OPTION_SPEED,
OPTION_WORD,
OPTION_PARITY,
OPTION_STOP,
OPTION_BASE_CLOCK,
OPTION_RTSCTS
};
/* Argument options. */
static const struct grub_arg_option options[] =
{
{"unit", 'u', 0, N_("Set the serial unit."), 0, ARG_TYPE_INT},
{"port", 'p', 0, N_("Set the serial port address."), 0, ARG_TYPE_STRING},
{"speed", 's', 0, N_("Set the serial port speed."), 0, ARG_TYPE_INT},
{"word", 'w', 0, N_("Set the serial port word length."), 0, ARG_TYPE_INT},
{"parity", 'r', 0, N_("Set the serial port parity."), 0, ARG_TYPE_STRING},
{"stop", 't', 0, N_("Set the serial port stop bits."), 0, ARG_TYPE_INT},
{"base-clock", 'b', 0, N_("Set the base frequency."), 0, ARG_TYPE_STRING},
{"rtscts", 'f', 0, N_("Enable/disable RTS/CTS."), "on|off", ARG_TYPE_STRING},
{0, 0, 0, 0, 0, 0}
};
static struct grub_serial_port *grub_serial_ports;
struct grub_serial_output_state
{
struct grub_terminfo_output_state tinfo;
struct grub_serial_port *port;
};
struct grub_serial_input_state
{
struct grub_terminfo_input_state tinfo;
struct grub_serial_port *port;
};
static void
serial_put (grub_term_output_t term, const int c)
{
struct grub_serial_output_state *data = term->data;
data->port->driver->put (data->port, c);
}
static int
serial_fetch (grub_term_input_t term)
{
struct grub_serial_input_state *data = term->data;
return data->port->driver->fetch (data->port);
}
static const struct grub_serial_input_state grub_serial_terminfo_input_template =
{
.tinfo =
{
.readkey = serial_fetch
}
};
static const struct grub_serial_output_state grub_serial_terminfo_output_template =
{
.tinfo =
{
.put = serial_put,
.size = { 80, 24 }
}
};
static struct grub_serial_input_state grub_serial_terminfo_input;
static struct grub_serial_output_state grub_serial_terminfo_output;
static int registered = 0;
static struct grub_term_input grub_serial_term_input =
{
.name = "serial",
.init = grub_terminfo_input_init,
.getkey = grub_terminfo_getkey,
.data = &grub_serial_terminfo_input
};
static struct grub_term_output grub_serial_term_output =
{
.name = "serial",
.init = grub_terminfo_output_init,
.putchar = grub_terminfo_putchar,
.getwh = grub_terminfo_getwh,
.getxy = grub_terminfo_getxy,
.gotoxy = grub_terminfo_gotoxy,
.cls = grub_terminfo_cls,
.setcolorstate = grub_terminfo_setcolorstate,
.setcursor = grub_terminfo_setcursor,
.flags = GRUB_TERM_CODE_TYPE_ASCII,
.data = &grub_serial_terminfo_output,
.progress_update_divisor = GRUB_PROGRESS_SLOW
};
struct grub_serial_port *
grub_serial_find (const char *name)
{
struct grub_serial_port *port;
FOR_SERIAL_PORTS (port)
if (grub_strcmp (port->name, name) == 0)
break;
#if ((defined(__mips__) && _MIPS_SIM != _ABI64) || defined (__i386__) || defined (__x86_64__)) && !defined(GRUB_MACHINE_EMU) && !defined(GRUB_MACHINE_ARC)
if (!port && grub_memcmp (name, "port", sizeof ("port") - 1) == 0
&& grub_isxdigit (name [sizeof ("port") - 1]))
{
name = grub_serial_ns8250_add_port (grub_strtoul (&name[sizeof ("port") - 1],
0, 16));
if (!name)
return NULL;
FOR_SERIAL_PORTS (port)
if (grub_strcmp (port->name, name) == 0)
break;
}
#endif
#ifdef GRUB_MACHINE_IEEE1275
if (!port && grub_memcmp (name, "ieee1275/", sizeof ("ieee1275/") - 1) == 0)
{
name = grub_ofserial_add_port (&name[sizeof ("ieee1275/") - 1]);
if (!name)
return NULL;
FOR_SERIAL_PORTS (port)
if (grub_strcmp (port->name, name) == 0)
break;
}
#endif
return port;
}
static grub_err_t
grub_cmd_serial (grub_extcmd_context_t ctxt, int argc, char **args)
{
struct grub_arg_list *state = ctxt->state;
char pname[40];
const char *name = NULL;
struct grub_serial_port *port;
struct grub_serial_config config;
grub_err_t err;
if (state[OPTION_UNIT].set)
{
grub_snprintf (pname, sizeof (pname), "com%ld",
grub_strtoul (state[0].arg, 0, 0));
name = pname;
}
if (state[OPTION_PORT].set)
{
grub_snprintf (pname, sizeof (pname), "port%lx",
grub_strtoul (state[1].arg, 0, 0));
name = pname;
}
if (argc >= 1)
name = args[0];
if (!name)
name = "com0";
port = grub_serial_find (name);
if (!port)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("serial port `%s' isn't found"),
name);
config = port->config;
if (state[OPTION_SPEED].set) {
config.speed = grub_strtoul (state[OPTION_SPEED].arg, 0, 0);
if (config.speed == 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("unsupported serial port parity"));
}
if (state[OPTION_WORD].set)
config.word_len = grub_strtoul (state[OPTION_WORD].arg, 0, 0);
if (state[OPTION_PARITY].set)
{
if (! grub_strcmp (state[OPTION_PARITY].arg, "no"))
config.parity = GRUB_SERIAL_PARITY_NONE;
else if (! grub_strcmp (state[OPTION_PARITY].arg, "odd"))
config.parity = GRUB_SERIAL_PARITY_ODD;
else if (! grub_strcmp (state[OPTION_PARITY].arg, "even"))
config.parity = GRUB_SERIAL_PARITY_EVEN;
else
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("unsupported serial port parity"));
}
if (state[OPTION_RTSCTS].set)
{
if (grub_strcmp (state[OPTION_RTSCTS].arg, "on") == 0)
config.rtscts = 1;
else if (grub_strcmp (state[OPTION_RTSCTS].arg, "off") == 0)
config.rtscts = 0;
else
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("unsupported serial port flow control"));
}
if (state[OPTION_STOP].set)
{
if (! grub_strcmp (state[OPTION_STOP].arg, "1"))
config.stop_bits = GRUB_SERIAL_STOP_BITS_1;
else if (! grub_strcmp (state[OPTION_STOP].arg, "2"))
config.stop_bits = GRUB_SERIAL_STOP_BITS_2;
else if (! grub_strcmp (state[OPTION_STOP].arg, "1.5"))
config.stop_bits = GRUB_SERIAL_STOP_BITS_1_5;
else
return grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("unsupported serial port stop bits number"));
}
if (state[OPTION_BASE_CLOCK].set)
{
char *ptr;
config.base_clock = grub_strtoull (state[OPTION_BASE_CLOCK].arg, &ptr, 0);
if (grub_errno)
return grub_errno;
if (ptr && *ptr == 'M')
config.base_clock *= 1000000;
if (ptr && (*ptr == 'k' || *ptr == 'K'))
config.base_clock *= 1000;
}
if (config.speed == 0)
config.speed = 9600;
/* Initialize with new settings. */
err = port->driver->configure (port, &config);
if (err)
return err;
#if !defined (GRUB_MACHINE_EMU) && !defined(GRUB_MACHINE_ARC) && ((defined(__mips__) && _MIPS_SIM != _ABI64) || defined (__i386__) || defined (__x86_64__))
/* Compatibility kludge. */
if (port->driver == &grub_ns8250_driver)
{
if (!registered)
{
grub_terminfo_output_register (&grub_serial_term_output, "vt100");
grub_term_register_input ("serial", &grub_serial_term_input);
grub_term_register_output ("serial", &grub_serial_term_output);
}
grub_serial_terminfo_output.port = port;
grub_serial_terminfo_input.port = port;
registered = 1;
}
#endif
return GRUB_ERR_NONE;
}
#ifdef GRUB_MACHINE_MIPS_LOONGSON
const char loongson_defserial[][6] =
{
[GRUB_ARCH_MACHINE_YEELOONG] = "com0",
[GRUB_ARCH_MACHINE_FULOONG2F] = "com2",
[GRUB_ARCH_MACHINE_FULOONG2E] = "com1"
};
#endif
grub_err_t
grub_serial_register (struct grub_serial_port *port)
{
struct grub_term_input *in;
struct grub_term_output *out;
struct grub_serial_input_state *indata;
struct grub_serial_output_state *outdata;
in = grub_malloc (sizeof (*in));
if (!in)
return grub_errno;
indata = grub_malloc (sizeof (*indata));
if (!indata)
{
grub_free (in);
return grub_errno;
}
grub_memcpy (in, &grub_serial_term_input, sizeof (*in));
in->data = indata;
in->name = grub_xasprintf ("serial_%s", port->name);
grub_memcpy (indata, &grub_serial_terminfo_input, sizeof (*indata));
if (!in->name)
{
grub_free (in);
grub_free (indata);
return grub_errno;
}
out = grub_zalloc (sizeof (*out));
if (!out)
{
grub_free (indata);
grub_free ((char *) in->name);
grub_free (in);
return grub_errno;
}
outdata = grub_malloc (sizeof (*outdata));
if (!outdata)
{
grub_free (indata);
grub_free ((char *) in->name);
grub_free (out);
grub_free (in);
return grub_errno;
}
grub_memcpy (out, &grub_serial_term_output, sizeof (*out));
out->data = outdata;
out->name = in->name;
grub_memcpy (outdata, &grub_serial_terminfo_output, sizeof (*outdata));
grub_list_push (GRUB_AS_LIST_P (&grub_serial_ports), GRUB_AS_LIST (port));
((struct grub_serial_input_state *) in->data)->port = port;
((struct grub_serial_output_state *) out->data)->port = port;
port->term_in = in;
port->term_out = out;
grub_terminfo_output_register (out, "vt100");
#ifdef GRUB_MACHINE_MIPS_LOONGSON
if (grub_strcmp (port->name, loongson_defserial[grub_arch_machine]) == 0)
{
grub_term_register_input_active ("serial_*", in);
grub_term_register_output_active ("serial_*", out);
}
else
{
grub_term_register_input_inactive ("serial_*", in);
grub_term_register_output_inactive ("serial_*", out);
}
#else
grub_term_register_input ("serial_*", in);
grub_term_register_output ("serial_*", out);
#endif
return GRUB_ERR_NONE;
}
void
grub_serial_unregister (struct grub_serial_port *port)
{
if (port->driver->fini)
port->driver->fini (port);
if (port->term_in)
grub_term_unregister_input (port->term_in);
if (port->term_out)
grub_term_unregister_output (port->term_out);
grub_list_remove (GRUB_AS_LIST (port));
}
void
grub_serial_unregister_driver (struct grub_serial_driver *driver)
{
struct grub_serial_port *port, *next;
for (port = grub_serial_ports; port; port = next)
{
next = port->next;
if (port->driver == driver)
grub_serial_unregister (port);
}
}
static grub_extcmd_t cmd;
GRUB_MOD_INIT(serial)
{
cmd = grub_register_extcmd ("serial", grub_cmd_serial, 0,
N_("[OPTIONS...]"),
N_("Configure serial port."), options);
grub_memcpy (&grub_serial_terminfo_output,
&grub_serial_terminfo_output_template,
sizeof (grub_serial_terminfo_output));
grub_memcpy (&grub_serial_terminfo_input,
&grub_serial_terminfo_input_template,
sizeof (grub_serial_terminfo_input));
#if !defined (GRUB_MACHINE_EMU) && !defined(GRUB_MACHINE_ARC) && ((defined(__mips__) && _MIPS_SIM != _ABI64) || defined (__i386__) || defined (__x86_64__))
grub_ns8250_init ();
#endif
#ifdef GRUB_MACHINE_IEEE1275
grub_ofserial_init ();
#endif
#ifdef GRUB_MACHINE_EFI
grub_efiserial_init ();
#endif
#ifdef GRUB_MACHINE_ARC
grub_arcserial_init ();
#endif
}
GRUB_MOD_FINI(serial)
{
while (grub_serial_ports)
grub_serial_unregister (grub_serial_ports);
if (registered)
{
grub_term_unregister_input (&grub_serial_term_input);
grub_term_unregister_output (&grub_serial_term_output);
}
grub_unregister_extcmd (cmd);
}
@@ -0,0 +1,388 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2020 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/env.h>
#include <grub/err.h>
#include <grub/dl.h>
#include <grub/extcmd.h>
#include <grub/i18n.h>
#include <grub/term.h>
GRUB_MOD_LICENSE ("GPLv3+");
#define MAX_KEYMAP 255
struct keymap
{
int cnt;
int in[MAX_KEYMAP];
int out[MAX_KEYMAP];
};
static struct keymap setkey_keymap;
struct keysym
{
const char *name; /* the name in unshifted state */
int code; /* scan code */
};
/* The table for key symbols. (from GRUB4DOS) */
static struct keysym keysym_table[] =
{
{"escape", GRUB_TERM_ESC}, // ESC
{"exclam", 0x21}, // '!'
{"at", 0x40}, // '@'
{"numbersign", 0x23}, // '#'
{"dollar", 0x24}, // '$'
{"percent", 0x25}, // '%'
{"caret", 0x5E}, // '^'
{"ampersand", 0x26}, // '&'
{"asterisk", 0x2A}, // '*'
{"parenleft", 0x28}, // '('
{"parenright", 0x29}, // ')'
{"minus", 0x2D}, // '-'
{"underscore", 0x5F}, // '_'
{"equal", 0x3D}, // '='
{"plus", 0x2B}, // '+'
{"backspace", GRUB_TERM_BACKSPACE}, // BS
{"ctrlbackspace", GRUB_TERM_CTRL | GRUB_TERM_BACKSPACE}, // (DEL)
{"tab", GRUB_TERM_TAB}, // Tab
{"bracketleft", 0x5B}, // '['
{"braceleft", 0x7B}, // '{'
{"bracketright", 0x5D}, // ']'
{"braceright", 0x7D}, // '}'
{"enter", 0x0D}, // Enter
{"semicolon", 0x3B}, // ';'
{"colon", 0x3A}, // ':'
{"quote", 0x27}, // '\''
{"doublequote", 0x22}, // '"'
{"backquote", 0x60}, // '`'
{"tilde", 0x7E}, // '~'
{"backslash", 0x5C}, // '\\'
{"bar", 0x7C}, // '|'
{"comma", 0x2C}, // ','
{"less", 0x3C}, // '<'
{"period", 0x2E}, // '.'
{"greater", 0x3E}, // '>'
{"slash", 0x2F}, // '/'
{"question", 0x3F}, // '?'
{"space", 0x20}, // Space
{"F1", GRUB_TERM_KEY_F1},
{"F2", GRUB_TERM_KEY_F2},
{"F3", GRUB_TERM_KEY_F3},
{"F4", GRUB_TERM_KEY_F4},
{"F5", GRUB_TERM_KEY_F5},
{"F6", GRUB_TERM_KEY_F6},
{"F7", GRUB_TERM_KEY_F7},
{"F8", GRUB_TERM_KEY_F8},
{"F9", GRUB_TERM_KEY_F9},
{"F10", GRUB_TERM_KEY_F10},
{"F11", GRUB_TERM_KEY_F11},
{"F12", GRUB_TERM_KEY_F12},
{"home", GRUB_TERM_KEY_HOME},
{"uparrow", GRUB_TERM_KEY_UP},
{"pageup", GRUB_TERM_KEY_NPAGE}, // PgUp
{"leftarrow", GRUB_TERM_KEY_LEFT},
{"center", GRUB_TERM_KEY_CENTER}, // keypad center key
{"rightarrow", GRUB_TERM_KEY_RIGHT},
{"end", GRUB_TERM_KEY_END},
{"downarrow", GRUB_TERM_KEY_DOWN},
{"pagedown", GRUB_TERM_KEY_PPAGE}, // PgDn
{"insert", GRUB_TERM_KEY_INSERT}, // Insert
{"delete", GRUB_TERM_KEY_DC}, // Delete
{"shiftF1", GRUB_TERM_SHIFT | GRUB_TERM_KEY_F1},
{"shiftF2", GRUB_TERM_SHIFT | GRUB_TERM_KEY_F2},
{"shiftF3", GRUB_TERM_SHIFT | GRUB_TERM_KEY_F3},
{"shiftF4", GRUB_TERM_SHIFT | GRUB_TERM_KEY_F4},
{"shiftF5", GRUB_TERM_SHIFT | GRUB_TERM_KEY_F5},
{"shiftF6", GRUB_TERM_SHIFT | GRUB_TERM_KEY_F6},
{"shiftF7", GRUB_TERM_SHIFT | GRUB_TERM_KEY_F7},
{"shiftF8", GRUB_TERM_SHIFT | GRUB_TERM_KEY_F8},
{"shiftF9", GRUB_TERM_SHIFT | GRUB_TERM_KEY_F9},
{"shiftF10", GRUB_TERM_SHIFT | GRUB_TERM_KEY_F10},
{"shiftF11", GRUB_TERM_SHIFT | GRUB_TERM_KEY_F11},
{"shiftF12", GRUB_TERM_SHIFT | GRUB_TERM_KEY_F12},
{"ctrlF1", GRUB_TERM_CTRL | GRUB_TERM_KEY_F1},
{"ctrlF2", GRUB_TERM_CTRL | GRUB_TERM_KEY_F2},
{"ctrlF3", GRUB_TERM_CTRL | GRUB_TERM_KEY_F3},
{"ctrlF4", GRUB_TERM_CTRL | GRUB_TERM_KEY_F4},
{"ctrlF5", GRUB_TERM_CTRL | GRUB_TERM_KEY_F5},
{"ctrlF6", GRUB_TERM_CTRL | GRUB_TERM_KEY_F6},
{"ctrlF7", GRUB_TERM_CTRL | GRUB_TERM_KEY_F7},
{"ctrlF8", GRUB_TERM_CTRL | GRUB_TERM_KEY_F8},
{"ctrlF9", GRUB_TERM_CTRL | GRUB_TERM_KEY_F9},
{"ctrlF10", GRUB_TERM_CTRL | GRUB_TERM_KEY_F10},
{"ctrlF11", GRUB_TERM_CTRL | GRUB_TERM_KEY_F11},
{"ctrlF12", GRUB_TERM_CTRL | GRUB_TERM_KEY_F12},
// A=Alt or AltGr. Provided by steve.
{"Aq", GRUB_TERM_ALT | 0x71},
{"Aw", GRUB_TERM_ALT | 0x77},
{"Ae", GRUB_TERM_ALT | 0x65},
{"Ar", GRUB_TERM_ALT | 0x72},
{"At", GRUB_TERM_ALT | 0x74},
{"Ay", GRUB_TERM_ALT | 0x79},
{"Au", GRUB_TERM_ALT | 0x75},
{"Ai", GRUB_TERM_ALT | 0x69},
{"Ao", GRUB_TERM_ALT | 0x6F},
{"Ap", GRUB_TERM_ALT | 0x70},
{"Aa", GRUB_TERM_ALT | 0x61},
{"As", GRUB_TERM_ALT | 0x73},
{"Ad", GRUB_TERM_ALT | 0x64},
{"Af", GRUB_TERM_ALT | 0x66},
{"Ag", GRUB_TERM_ALT | 0x67},
{"Ah", GRUB_TERM_ALT | 0x68},
{"Aj", GRUB_TERM_ALT | 0x6A},
{"Ak", GRUB_TERM_ALT | 0x6B},
{"Al", GRUB_TERM_ALT | 0x6C},
{"Az", GRUB_TERM_ALT | 0x7A},
{"Ax", GRUB_TERM_ALT | 0x78},
{"Ac", GRUB_TERM_ALT | 0x63},
{"Av", GRUB_TERM_ALT | 0x76},
{"Ab", GRUB_TERM_ALT | 0x62},
{"An", GRUB_TERM_ALT | 0x6E},
{"Am", GRUB_TERM_ALT | 0x6D},
{"A1", GRUB_TERM_ALT | 0x31},
{"A2", GRUB_TERM_ALT | 0x32},
{"A3", GRUB_TERM_ALT | 0x33},
{"A4", GRUB_TERM_ALT | 0x34},
{"A5", GRUB_TERM_ALT | 0x35},
{"A6", GRUB_TERM_ALT | 0x36},
{"A7", GRUB_TERM_ALT | 0x37},
{"A8", GRUB_TERM_ALT | 0x38},
{"A9", GRUB_TERM_ALT | 0x39},
{"A0", GRUB_TERM_ALT | 0x30},
//{"oem102", 0x5c},
//{"shiftoem102", 0x7c},
{"Aminus", GRUB_TERM_ALT | 0x2D},
{"Aequal", GRUB_TERM_ALT | 0x3D},
{"Abracketleft", GRUB_TERM_ALT | 0x5B},
{"Abracketright", GRUB_TERM_ALT | 0x5D},
{"Asemicolon", GRUB_TERM_ALT | 0x3B},
{"Aquote", GRUB_TERM_ALT | 0x27},
{"Abackquote", GRUB_TERM_ALT | 0x60},
{"Abackslash", GRUB_TERM_ALT | 0x5C},
{"Acomma", GRUB_TERM_ALT | 0x2C},
{"Aperiod", GRUB_TERM_ALT | 0x2E},
{"Aslash", GRUB_TERM_ALT | 0x2F},
{"Acolon", GRUB_TERM_ALT | 0x3A},
{"Aplus", GRUB_TERM_ALT | 0x2B},
{"Aless", GRUB_TERM_ALT | 0x3C},
{"Aunderscore", GRUB_TERM_ALT | 0x5F},
{"Agreater", GRUB_TERM_ALT | 0x3E},
{"Aquestion", GRUB_TERM_ALT | 0x3F},
{"Atilde", GRUB_TERM_ALT | 0x7E},
{"Abraceleft", GRUB_TERM_ALT | 0x7B},
{"Abar", GRUB_TERM_ALT | 0x7C},
{"Abraceright", GRUB_TERM_ALT | 0x7D},
{"Adoublequote", GRUB_TERM_ALT | 0x22},
};
static int grub_keymap_getkey (int key)
{
int i;
if (key == GRUB_TERM_NO_KEY)
return key;
if (setkey_keymap.cnt > MAX_KEYMAP)
setkey_keymap.cnt = MAX_KEYMAP;
for (i = 0; i < setkey_keymap.cnt; i++)
{
if (key == setkey_keymap.in[i])
{
key = setkey_keymap.out[i];
break;
}
}
return key;
}
static void
grub_keymap_reset (void)
{
grub_memset (&setkey_keymap, 0, sizeof (struct keymap));
}
static grub_err_t
grub_keymap_add (int in, int out)
{
if (in == GRUB_TERM_NO_KEY || out == GRUB_TERM_NO_KEY)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid key: %d -> %d", in, out);
if (setkey_keymap.cnt >= MAX_KEYMAP)
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
"keymap FULL %d", setkey_keymap.cnt);
setkey_keymap.in[setkey_keymap.cnt] = in;
setkey_keymap.out[setkey_keymap.cnt] = out;
setkey_keymap.cnt++;
return GRUB_ERR_NONE;
}
static void
grub_keymap_enable (void)
{
grub_key_remap = grub_keymap_getkey;
}
static void
grub_keymap_disable (void)
{
grub_key_remap = NULL;
}
static void
grub_keymap_status (void)
{
int i;
if (setkey_keymap.cnt > MAX_KEYMAP)
setkey_keymap.cnt = MAX_KEYMAP;
for (i = 0; i < setkey_keymap.cnt; i++)
{
grub_printf ("0x%x -> 0x%x\n", setkey_keymap.in[i], setkey_keymap.out[i]);
}
}
static const struct grub_arg_option options[] =
{
{"reset", 'r', 0, N_("Reset keymap."), 0, 0},
{"enable", 'e', 0, N_("Enable keymap."), 0, 0},
{"disable", 'd', 0, N_("Disable keymap."), 0, 0},
{"status", 's', 0, N_("Display keymap."), 0, 0},
{0, 0, 0, 0, 0, 0}
};
enum options
{
SETKEY_RESET,
SETKEY_ENABLE,
SETKEY_DISABLE,
SETKEY_STATUS,
};
static int
ishex (const char *str)
{
if (grub_strlen (str) < 3 || str[0] != '0')
return 0;
if (str[1] != 'x' && str[1] != 'X')
return 0;
return 1;
}
static int
parse_key (const char *str)
{
int i;
if (ishex (str))
return grub_strtol (str, NULL, 16);
if (grub_strlen (str) == 1)
return (int) str[0];
for (i = 0; i < (int) (sizeof (keysym_table) / sizeof (keysym_table[0])); i++)
{
if (grub_strcmp (str, keysym_table[i].name) == 0)
return keysym_table[i].code;
}
grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid key %s", str);
return 0;
}
static grub_err_t
grub_cmd_setkey (grub_extcmd_context_t ctxt, int argc, char **args)
{
struct grub_arg_list *state = ctxt->state;
int in, out;
if (state[SETKEY_ENABLE].set)
{
grub_keymap_enable ();
goto out;
}
if (state[SETKEY_DISABLE].set)
{
grub_keymap_disable ();
goto out;
}
if (state[SETKEY_RESET].set)
{
grub_keymap_reset ();
goto out;
}
if (state[SETKEY_STATUS].set)
{
grub_keymap_status ();
goto out;
}
if (argc != 2)
{
grub_printf
("Key names: 0-9, A-Z, a-z or escape, exclam, at, numbersign, dollar,"
"percent, caret, ampersand, asterisk, parenleft, parenright, minus,"
"underscore, equal, plus, backspace, tab, bracketleft, braceleft,"
"bracketright, braceright, enter, semicolon, colon, quote, doublequote,"
"backquote, tilde, backslash, bar, comma, less, period, greater,"
"slash, question, alt, space, delete, [ctrl|shift]F1-12."
"For Alt+ prefix with A, e.g. \'setkey at Aequal\'.");
goto out;
}
in = parse_key (args[1]);
out = parse_key (args[0]);
if (!in || !out)
goto out;
grub_keymap_add (in, out);
out:
return grub_errno;
}
static void grub_keymap_add_by_string(const char *src, const char *dst)
{
int in = 0;
int out = 0;
in = parse_key(dst);
out = parse_key(src);
if (in && out)
{
grub_keymap_add (in, out);
}
}
#include "keyboard_layout.c"
static grub_err_t grub_cmd_set_keylayout (grub_extcmd_context_t ctxt, int argc, char **args)
{
(void)ctxt;
(void)argc;
ventoy_set_keyboard_layout(args[0]);
return 0;
}
static grub_extcmd_t cmd, setcmd;
GRUB_MOD_INIT(setkey)
{
cmd = grub_register_extcmd ("setkey", grub_cmd_setkey, 0, N_("NEW_KEY USA_KEY"),
N_("Map default USA_KEY to NEW_KEY."), options);
setcmd = grub_register_extcmd ("set_keyboard_layout", grub_cmd_set_keylayout, 0, N_("layout"),
N_("Set keyboard layout."), NULL);
}
GRUB_MOD_FINI(setkey)
{
grub_unregister_extcmd (cmd);
}
@@ -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;
}
@@ -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 */
@@ -0,0 +1,668 @@
/*
* 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++;
lzx->output.offset += len;
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;
}
@@ -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
@@ -0,0 +1,2 @@
#define MINIZ_HEADER_FILE_ONLY
#include "miniz.c"
@@ -0,0 +1,650 @@
/******************************************************************************
* ventoy.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/net.h>
#include <grub/misc.h>
#include <grub/kernel.h>
#include <grub/time.h>
#include <grub/memory.h>
#ifdef GRUB_MACHINE_EFI
#include <grub/efi/efi.h>
#include <grub/efi/memory.h>
#endif
#include <grub/ventoy.h>
#include "ventoy_def.h"
GRUB_MOD_LICENSE ("GPLv3+");
int g_ventoy_debug = 0;
static int g_efi_os = 0xFF;
grub_uint32_t g_ventoy_plat_data;
#ifdef GRUB_MACHINE_EFI
static VTOY_SHIM *g_vtoy_shim = NULL;
#endif
void ventoy_debug(const char *fmt, ...)
{
va_list args;
va_start (args, fmt);
grub_vprintf (fmt, args);
va_end (args);
}
void ventoy_str_tolower(char *str)
{
while (*str)
{
*str = grub_tolower(*str);
str++;
}
}
void ventoy_str_toupper(char *str)
{
while (*str)
{
*str = grub_toupper(*str);
str++;
}
}
char *ventoy_str_last(char *str, char ch)
{
char *pos = NULL;
char *last = NULL;
if (!str)
{
return NULL;
}
for (pos = str; *pos; pos++)
{
if (*pos == ch)
{
last = pos;
}
}
return last;
}
int ventoy_str_all_digit(const char *str)
{
if (NULL == str || 0 == *str)
{
return 0;
}
while (*str)
{
if (*str < '0' || *str > '9')
{
return 0;
}
}
return 1;
}
int ventoy_str_all_alnum(const char *str)
{
if (NULL == str || 0 == *str)
{
return 0;
}
while (*str)
{
if (!grub_isalnum(*str))
{
return 0;
}
}
return 1;
}
int ventoy_str_len_alnum(const char *str, int len)
{
int i;
int slen;
if (NULL == str || 0 == *str)
{
return 0;
}
slen = grub_strlen(str);
if (slen <= len)
{
return 0;
}
for (i = 0; i < len; i++)
{
if (!grub_isalnum(str[i]))
{
return 0;
}
}
if (str[len] == 0 || grub_isspace(str[len]))
{
return 1;
}
return 0;
}
char * ventoy_str_basename(char *path)
{
char *pos = NULL;
pos = grub_strrchr(path, '/');
if (pos)
{
pos++;
}
else
{
pos = path;
}
return pos;
}
int ventoy_str_chrcnt(const char *str, char c)
{
int n = 0;
if (str)
{
while (*str)
{
if (*str == c)
{
n++;
}
str++;
}
}
return n;
}
int ventoy_strcmp(const char *pattern, const char *str)
{
while (*pattern && *str)
{
if ((*pattern != *str) && (*pattern != '*'))
break;
pattern++;
str++;
}
return (int)(grub_uint8_t)*pattern - (int)(grub_uint8_t)*str;
}
int ventoy_strncmp (const char *pattern, const char *str, grub_size_t n)
{
if (n == 0)
return 0;
while (*pattern && *str && --n)
{
if ((*pattern != *str) && (*pattern != '*'))
break;
pattern++;
str++;
}
return (int)(grub_uint8_t)*pattern - (int)(grub_uint8_t)*str;
}
grub_err_t ventoy_env_int_set(const char *name, int value)
{
char buf[16];
grub_snprintf(buf, sizeof(buf), "%d", value);
return grub_env_set(name, buf);
}
void ventoy_debug_dump_guid(const char *prefix, grub_uint8_t *guid)
{
int i;
if (!g_ventoy_debug)
{
return;
}
debug("%s", prefix);
for (i = 0; i < 16; i++)
{
grub_printf("%02x ", guid[i]);
}
grub_printf("\n");
}
int ventoy_is_efi_os(void)
{
if (g_efi_os > 1)
{
g_efi_os = (grub_strstr(GRUB_PLATFORM, "efi")) ? 1 : 0;
}
return g_efi_os;
}
void * ventoy_alloc_chain(grub_size_t size)
{
void *p = NULL;
p = grub_malloc(size);
#ifdef GRUB_MACHINE_EFI
if (!p)
{
p = grub_efi_allocate_any_pages(GRUB_EFI_BYTES_TO_PAGES(size));
}
#endif
return p;
}
void ventoy_memfile_env_set(const char *prefix, const void *buf, unsigned long long len)
{
char name[128];
char val[64];
grub_snprintf(name, sizeof(name), "%s_addr", prefix);
grub_snprintf(val, sizeof(val), "0x%llx", (ulonglong)(ulong)buf);
grub_env_set(name, val);
grub_snprintf(name, sizeof(name), "%s_size", prefix);
grub_snprintf(val, sizeof(val), "%llu", len);
grub_env_set(name, val);
return;
}
#ifdef GRUB_MACHINE_EFI
static void ventoy_get_uefi_version(char *str, grub_size_t len)
{
grub_efi_uint8_t uefi_minor_1, uefi_minor_2;
uefi_minor_1 = (grub_efi_system_table->hdr.revision & 0xffff) / 10;
uefi_minor_2 = (grub_efi_system_table->hdr.revision & 0xffff) % 10;
grub_snprintf(str, len, "%d.%d", (grub_efi_system_table->hdr.revision >> 16), uefi_minor_1);
if (uefi_minor_2)
grub_snprintf(str, len, "%s.%d", str, uefi_minor_2);
}
static void ventoy_get_pi_version(char *str, grub_size_t len)
{
grub_uint32_t data = 0;
grub_efi_uintn_t i = 0;
grub_efi_guid_t dxest =
{ 0x05ad34ba, 0x6f02, 0x4214, {0x95, 0x2e, 0x4d, 0xa0, 0x39, 0x8e, 0x2b, 0xb9 } };
for (i = 0; i < grub_efi_system_table->num_table_entries; i++)
{
if (grub_memcmp(&dxest, &grub_efi_system_table->configuration_table[i].vendor_guid, 16) == 0)
{
grub_memcpy(&data, (char *)grub_efi_system_table->configuration_table[i].vendor_table + 8, 4);
break;
}
}
grub_snprintf(str, len, "%d.%d", (data >> 16) & 0xFFFF, (data & 0xFFFF) / 10);
}
int ventoy_set_sb_policy(void)
{
const char *env = NULL;
static int set_once = 0;
/* no need when SecureBoot is disabled */
if (g_sys_sb == 0)
{
return 0;
}
/* can only set once */
if (set_once > 0)
{
return 0;
}
set_once = 1;
/* VTOY_SECURE_BOOT_POLICY only take affect once during init */
env = grub_env_get("VTOY_SECURE_BOOT_POLICY");
if (env)
{
g_sb_policy = (grub_uint8_t)(env[0] - '0');
}
if (g_sb_policy == VTOY_SB_POLICY_BYPASS)
{
if (g_vtoy_shim && g_vtoy_shim->ByPassSB)
{
g_vtoy_shim->ByPassSB();
}
}
else if (g_sb_policy == VTOY_SB_POLICY_CHECK)
{
if (g_vtoy_shim && g_vtoy_shim->CheckSB)
{
g_vtoy_shim->CheckSB();
}
}
return 0;
}
static void ventoy_get_uefi_sb(void)
{
grub_uint8_t secure_boot = 0;
grub_uint8_t setup_mode = 0;
grub_uint8_t *var = NULL;
grub_size_t size = 0;
grub_efi_guid_t global = GRUB_EFI_GLOBAL_VARIABLE_GUID;
var = grub_efi_get_variable("SecureBoot", &global, &size);
if (var && size == 1 && *var == 1)
{
secure_boot = 1;
}
grub_check_free(var);
size = 0;
var = grub_efi_get_variable("SetupMode", &global, &size);
if (var && size == 1 && *var == 1)
{
setup_mode = 1;
}
if (secure_boot == 1 && setup_mode == 0)
{
g_sys_sb = 1;
}
grub_check_free(var);
}
static int ventoy_secure_boot_init(void)
{
grub_efi_guid_t ProtGuid = VTOY_SHIM_POLICY_GUID;
ventoy_get_uefi_sb();
if (g_sys_sb == 0)
{
return 0;
}
if (g_ventoy_plat_data == VTOY_PLAT_X86_64_UEFI)
{
g_vtoy_shim = grub_efi_locate_protocol(&ProtGuid, NULL);
if (g_vtoy_shim == NULL || g_vtoy_shim->ByPassSB == NULL ||
g_vtoy_shim->CheckSB == NULL || g_vtoy_shim->Launched == NULL)
{
/*
* Generally when SecureBoot enabled, Ventoy grub must be launched by Ventoy Shim.
* But there are some exceptions:
* 1. Ventoy key was enrolled directly to the UEFI DB
* 2. Some UEFI firmware (MSI) has Image Execution Policy as Always Execute which
* means Secure Boot is effectively disabled.
*/
}
else
{
g_vtoy_shim->Launched();
}
}
return 0;
}
#endif
static int ventoy_arch_mode_init(void)
{
#ifdef GRUB_MACHINE_EFI
if (grub_strcmp(GRUB_TARGET_CPU, "i386") == 0)
{
g_ventoy_plat_data = VTOY_PLAT_I386_UEFI;
grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "ia32");
}
else if (grub_strcmp(GRUB_TARGET_CPU, "arm64") == 0)
{
g_ventoy_plat_data = VTOY_PLAT_ARM64_UEFI;
grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "aa64");
}
else if (grub_strcmp(GRUB_TARGET_CPU, "mips64el") == 0)
{
g_ventoy_plat_data = VTOY_PLAT_MIPS_UEFI;
grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "mips");
}
else
{
g_ventoy_plat_data = VTOY_PLAT_X86_64_UEFI;
grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "uefi");
}
#else
g_ventoy_plat_data = VTOY_PLAT_X86_LEGACY;
grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "legacy");
#endif
return 0;
}
static int ventoy_calc_totalmem(grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type, void *data)
{
grub_uint64_t *total_mem = (grub_uint64_t *)data;
(void)addr;
(void)type;
*total_mem += size;
return 0;
}
static int ventoy_hwinfo_init(void)
{
char str[256];
grub_uint64_t total_mem = 0;
grub_machine_mmap_iterate(ventoy_calc_totalmem, &total_mem);
grub_snprintf(str, sizeof(str), "%ld", (long)(total_mem / VTOY_SIZE_1MB));
ventoy_env_export("grub_total_ram", str);
#ifdef GRUB_MACHINE_EFI
ventoy_get_uefi_version(str, sizeof(str));
ventoy_env_export("grub_uefi_version", str);
ventoy_get_pi_version(str, sizeof(str));
ventoy_env_export("grub_pi_version", str);
#else
ventoy_env_export("grub_uefi_version", "NA");
ventoy_env_export("grub_pi_version", "NA");
#endif
return 0;
}
static global_var_cfg g_global_vars[] =
{
{ "gfxmode", "1024x768", NULL },
{ ventoy_left_key, "5%", NULL },
{ ventoy_top_key, "95%", NULL },
{ ventoy_color_key, "#0000ff", NULL },
{ NULL, NULL, NULL }
};
static const char * ventoy_global_var_read_hook(struct grub_env_var *var, const char *val)
{
int i;
for (i = 0; g_global_vars[i].name; i++)
{
if (grub_strcmp(g_global_vars[i].name, var->name) == 0)
{
return g_global_vars[i].value;
}
}
return val;
}
static char * ventoy_global_var_write_hook(struct grub_env_var *var, const char *val)
{
int i;
for (i = 0; g_global_vars[i].name; i++)
{
if (grub_strcmp(g_global_vars[i].name, var->name) == 0)
{
grub_check_free(g_global_vars[i].value);
g_global_vars[i].value = grub_strdup(val);
break;
}
}
return grub_strdup(val);
}
int ventoy_global_var_init(void)
{
int i;
for (i = 0; g_global_vars[i].name; i++)
{
g_global_vars[i].value = grub_strdup(g_global_vars[i].defval);
ventoy_env_export(g_global_vars[i].name, g_global_vars[i].defval);
grub_register_variable_hook(g_global_vars[i].name, ventoy_global_var_read_hook, ventoy_global_var_write_hook);
}
return 0;
}
static ctrl_var_cfg g_ctrl_vars[] =
{
{ "VTOY_WIN11_BYPASS_CHECK", "1" },
{ "VTOY_WIN11_BYPASS_NRO", "1" },
{ "VTOY_LINUX_REMOUNT", "0" },
{ "VTOY_SECONDARY_BOOT_MENU", "1" },
{ "VTOY_WIN_UEFI_RES_LOCK", "3" },
{ NULL, "" }
};
static const char * ventoy_ctrl_var_read_hook(struct grub_env_var *var, const char *val)
{
int i;
for (i = 0; g_ctrl_vars[i].name; i++)
{
if (grub_strcmp(g_ctrl_vars[i].name, var->name) == 0)
{
return g_ctrl_vars[i].szval;
}
}
return val;
}
static char * ventoy_ctrl_var_write_hook(struct grub_env_var *var, const char *val)
{
int i;
for (i = 0; g_ctrl_vars[i].name; i++)
{
if (grub_strcmp(g_ctrl_vars[i].name, var->name) == 0)
{
if (val && grub_isdigit(val[0]) && val[1] == 0)
{
g_ctrl_vars[i].szval[0] = val[0];
return grub_strdup(val);
}
else
{
g_ctrl_vars[i].szval[0] = '0';
return grub_strdup("0");
}
}
}
return grub_strdup(val);
}
int ventoy_ctrl_var_init(void)
{
ctrl_var_cfg *cfg = g_ctrl_vars;
while (cfg->name)
{
ventoy_env_export(cfg->name, cfg->szval);
grub_register_variable_hook(cfg->name, ventoy_ctrl_var_read_hook, ventoy_ctrl_var_write_hook);
cfg++;
}
return 0;
}
GRUB_MOD_INIT(ventoy)
{
ventoy_hwinfo_init();
ventoy_env_init();
ventoy_arch_mode_init();
#ifdef GRUB_MACHINE_EFI
ventoy_secure_boot_init();
#endif
ventoy_register_all_cmd();
}
GRUB_MOD_FINI(ventoy)
{
ventoy_unregister_all_cmd();
}
@@ -0,0 +1,652 @@
/******************************************************************************
* ventoy_browser.c
*
* Copyright (c) 2022, 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+");
#define BROWSER_MENU_BUF 65536
static const char *g_vtoy_dev = NULL;
static grub_fs_t g_menu_fs = NULL;
static char *g_menu_device = NULL;
static grub_device_t g_menu_dev = NULL;
static char g_menu_path_buf[1024];
static int g_menu_path_len = 0;
static browser_node *g_browser_list = NULL;
static int ventoy_browser_strcmp(char *str1, char *str2)
{
char *s1, *s2;
int c1 = 0;
int c2 = 0;
for (s1 = str1, s2 = str2; *s1 && *s2; s1++, s2++)
{
c1 = *s1;
c2 = *s2;
if (0 == g_sort_case_sensitive)
{
if (grub_islower(c1))
{
c1 = c1 - 'a' + 'A';
}
if (grub_islower(c2))
{
c2 = c2 - 'a' + 'A';
}
}
if (c1 != c2)
{
break;
}
}
return (c1 - c2);
}
static int ventoy_browser_mbuf_alloc(browser_mbuf *mbuf)
{
grub_memset(mbuf, 0, sizeof(browser_mbuf));
mbuf->buf = grub_malloc(BROWSER_MENU_BUF);
if (!mbuf->buf)
{
return 0;
}
mbuf->pos = 0;
mbuf->max = BROWSER_MENU_BUF;
return 1;
}
static inline void ventoy_browser_mbuf_free(browser_mbuf *mbuf)
{
if (mbuf)
grub_check_free(mbuf->buf)
}
static inline int ventoy_browser_mbuf_extend(browser_mbuf *mbuf)
{
if (mbuf->max - mbuf->pos <= VTOY_SIZE_1KB)
{
mbuf->max += BROWSER_MENU_BUF;
mbuf->buf = grub_realloc(mbuf->buf, mbuf->max);
}
return 0;
}
static browser_node * ventoy_browser_find_top_node(int dir)
{
browser_node *node = NULL;
browser_node *sel = NULL;
for (node = g_browser_list; node; node = node->next)
{
if (node->dir == dir)
{
if (sel)
{
if (ventoy_browser_strcmp(sel->filename, node->filename) > 0)
{
sel = node;
}
}
else
{
sel = node;
}
}
}
return sel;
}
static int ventoy_browser_iterate_partition(struct grub_disk *disk, const grub_partition_t partition, void *data)
{
char partname[64];
char title[256];
grub_device_t dev;
grub_fs_t fs;
char *Label = NULL;
browser_mbuf *mbuf = (browser_mbuf *)data;
(void)data;
if (partition->number == 1 && g_vtoy_dev && grub_strcmp(disk->name, g_vtoy_dev) == 0)
{
return 0;
}
grub_snprintf(partname, sizeof(partname) - 1, "%s,%d", disk->name, partition->number + 1);
dev = grub_device_open(partname);
if (!dev)
{
return 0;
}
fs = grub_fs_probe(dev);
if (!fs)
{
grub_device_close(dev);
return 0;
}
fs->fs_label(dev, &Label);
if (ventoy_check_file_exist("(%s)/.ventoyignore", partname))
{
return 0;
}
if (g_tree_view_menu_style == 0)
{
grub_snprintf(title, sizeof(title), "%-10s (%s,%s%d) [%s] %s %s",
"DISK", disk->name, partition->msdostype == 0xee ? "gpt" : "msdos",
partition->number + 1, (Label ? Label : ""), fs->name,
grub_get_human_size(partition->len << disk->log_sector_size, GRUB_HUMAN_SIZE_SHORT));
}
else
{
grub_snprintf(title, sizeof(title), "(%s,%s%d) [%s] %s %s",
disk->name, partition->msdostype == 0xee ? "gpt" : "msdos",
partition->number + 1, (Label ? Label : ""), fs->name,
grub_get_human_size(partition->len << disk->log_sector_size, GRUB_HUMAN_SIZE_SHORT));
}
if (ventoy_get_fs_type(fs->name) >= ventoy_fs_max)
{
browser_ssprintf(mbuf, "menuentry \"%s\" --class=vtoydisk {\n"
" echo \"unsupported file system type!\" \n"
" ventoy_pause\n"
"}\n",
title);
}
else
{
browser_ssprintf(mbuf, "menuentry \"%s\" --class=vtoydisk {\n"
" vt_browser_dir %s,%d 0x%lx /\n"
"}\n",
title, disk->name, partition->number + 1, (ulong)fs);
}
ventoy_browser_mbuf_extend(mbuf);
return 0;
}
static int ventoy_browser_iterate_disk(const char *name, void *data)
{
grub_disk_t disk;
if (name[0] != 'h')
{
return 0;
}
disk = grub_disk_open(name);
if (disk)
{
grub_partition_iterate(disk, ventoy_browser_iterate_partition, data);
grub_disk_close(disk);
}
return 0;
}
static int ventoy_browser_valid_dirname(const char *name, int len)
{
if ((len == 1 && name[0] == '.') ||
(len == 2 && name[0] == '.' && name[1] == '.'))
{
return 0;
}
if (!ventoy_img_name_valid(name, len))
{
return 0;
}
if (g_filt_trash_dir)
{
if (0 == grub_strncmp(name, ".trash-", 7) ||
0 == grub_strcmp(name, ".Trashes"))
{
return 0;
}
}
if (name[0] == '$')
{
if (0 == grub_strncmp(name, "$RECYCLE.BIN", 12) ||
0 == grub_strncasecmp(name, "$Extend", 7))
{
return 0;
}
}
if (len == 25 && grub_strncmp(name, "System Volume Information", 25) == 0)
{
return 0;
}
return 1;
}
static int ventoy_browser_valid_filename(const char *filename, int len, int *type)
{
if (len < 4)
{
return 0;
}
if (FILE_FLT(ISO) && 0 == grub_strcasecmp(filename + len - 4, ".iso"))
{
*type = img_type_iso;
}
else if (FILE_FLT(WIM) && g_wimboot_enable && (0 == grub_strcasecmp(filename + len - 4, ".wim")))
{
*type = img_type_wim;
}
else if (FILE_FLT(VHD) && g_vhdboot_enable && (0 == grub_strcasecmp(filename + len - 4, ".vhd") ||
(len >= 5 && 0 == grub_strcasecmp(filename + len - 5, ".vhdx"))))
{
*type = img_type_vhd;
}
#ifdef GRUB_MACHINE_EFI
else if (FILE_FLT(EFI) && 0 == grub_strcasecmp(filename + len - 4, ".efi"))
{
*type = img_type_efi;
}
#endif
else if (FILE_FLT(IMG) && 0 == grub_strcasecmp(filename + len - 4, ".img"))
{
if (len == 18 && grub_strncmp(filename, "ventoy_", 7) == 0)
{
if (grub_strncmp(filename + 7, "wimboot", 7) == 0 ||
grub_strncmp(filename + 7, "vhdboot", 7) == 0)
{
return 0;
}
}
*type = img_type_img;
}
else if (FILE_FLT(VTOY) && len >= 5 && 0 == grub_strcasecmp(filename + len - 5, ".vtoy"))
{
*type = img_type_vtoy;
}
else
{
return 0;
}
if (g_filt_dot_underscore_file && filename[0] == '.' && filename[1] == '_')
{
return 0;
}
return 1;
}
static int ventoy_browser_check_ignore(const char *device, const char *root, const char *dir)
{
grub_file_t file;
char fullpath[1024] = {0};
grub_snprintf(fullpath, 1023, "(%s)%s/%s/.ventoyignore", device, root, dir);
file = grub_file_open(fullpath, GRUB_FILE_TYPE_NONE);
if (!file)
{
grub_errno = 0;
return 0;
}
else
{
grub_file_close(file);
return 1;
}
}
static int ventoy_browser_iterate_dir(const char *filename, const struct grub_dirhook_info *info, void *data)
{
int type;
int len;
browser_node *node;
(void)data;
len = grub_strlen(filename);
if (info->dir)
{
if (!ventoy_browser_valid_dirname(filename, len))
{
return 0;
}
if (ventoy_browser_check_ignore(g_menu_device, g_menu_path_buf, filename))
{
return 0;
}
node = grub_zalloc(sizeof(browser_node));
if (!node)
{
return 0;
}
node->dir = 1;
grub_strncpy(node->filename, filename, sizeof(node->filename));
if (g_tree_view_menu_style == 0)
{
grub_snprintf(node->menuentry, sizeof(node->menuentry),
"menuentry \"%-10s [%s]\" --class=vtoydir {\n"
" vt_browser_dir %s 0x%lx \"%s/%s\"\n"
"}\n",
"DIR", filename, g_menu_device, (ulong)g_menu_fs, g_menu_path_buf, filename);
}
else
{
grub_snprintf(node->menuentry, sizeof(node->menuentry),
"menuentry \"[%s]\" --class=vtoydir {\n"
" vt_browser_dir %s 0x%lx \"%s/%s\"\n"
"}\n",
filename, g_menu_device, (ulong)g_menu_fs, g_menu_path_buf, filename);
}
}
else
{
grub_uint64_t fsize = info->size;
if (!ventoy_browser_valid_filename(filename, len, &type))
{
return 0;
}
if (grub_file_is_vlnk_suffix(filename, len))
{
return 0;
}
node = grub_zalloc(sizeof(browser_node));
if (!node)
{
return 0;
}
if (fsize == 0)
{
struct grub_file file;
grub_memset(&file, 0, sizeof(file));
file.device = g_menu_dev;
grub_snprintf(node->menuentry, sizeof(node->menuentry), "%s/%s", g_menu_path_buf, filename);
if (g_menu_fs->fs_open(&file, node->menuentry) == GRUB_ERR_NONE)
{
fsize = file.size;
g_menu_fs->fs_close(&file);
}
}
node->dir = 0;
grub_strncpy(node->filename, filename, sizeof(node->filename));
if (g_tree_view_menu_style == 0)
{
grub_snprintf(node->menuentry, sizeof(node->menuentry),
"menuentry \"%-10s %s\" --class=%s {\n"
" vt_set_fake_vlnk \"(%s)%s/%s\" %s %llu\n"
" %s_common_menuentry\n"
" vt_reset_fake_vlnk\n"
"}\n",
grub_get_human_size(fsize, GRUB_HUMAN_SIZE_SHORT), filename, g_menu_class[type],
g_menu_device, g_menu_path_buf, filename, g_menu_prefix[type], (ulonglong)fsize,
g_menu_prefix[type]);
}
else
{
grub_snprintf(node->menuentry, sizeof(node->menuentry),
"menuentry \"%s\" --class=%s {\n"
" vt_set_fake_vlnk \"(%s)%s/%s\" %s %llu\n"
" %s_common_menuentry\n"
" vt_reset_fake_vlnk\n"
"}\n",
filename, g_menu_class[type],
g_menu_device, g_menu_path_buf, filename, g_menu_prefix[type], (ulonglong)fsize,
g_menu_prefix[type]);
}
}
node->prev = NULL;
node->next = g_browser_list;
if (g_browser_list)
{
g_browser_list->prev = node;
}
g_browser_list = node;
return 0;
}
static grub_err_t ventoy_browser_iso_part(void)
{
char cfgfile[64];
char *buffer = NULL;
int pos = 0;
int buflen = 0;
int cfglen = 0;
cfglen = g_tree_script_pos - g_tree_script_pre;
buflen = cfglen + 512;
buffer = grub_malloc(buflen);
if (!buffer)
{
return 1;
}
if (g_tree_view_menu_style == 0)
{
pos = grub_snprintf(buffer, buflen, "menuentry \"%-10s [../]\" --class=\"vtoyret\" VTOY_RET {\n "
" echo 'return ...' \n}\n", "<--");
}
else
{
pos = grub_snprintf(buffer, buflen, "menuentry \"[../]\" --class=\"vtoyret\" VTOY_RET {\n "
" echo 'return ...' \n}\n");
}
grub_memcpy(buffer + pos, g_tree_script_buf + g_tree_script_pre, cfglen);
pos += cfglen;
grub_snprintf(cfgfile, sizeof(cfgfile), "configfile mem:0x%lx:size:%d", (ulong)buffer, pos);
grub_script_execute_sourcecode(cfgfile);
grub_free(buffer);
VENTOY_CMD_RETURN(GRUB_ERR_NONE);
}
grub_err_t ventoy_cmd_browser_dir(grub_extcmd_context_t ctxt, int argc, char **args)
{
int i;
grub_fs_t fs;
grub_device_t dev;
char cfgfile[64];
browser_node *node;
browser_mbuf mbuf;
(void)ctxt;
(void)argc;
if (args[2][0] == '/' && args[2][1] == 0)
{
grub_snprintf(cfgfile, sizeof(cfgfile), "(%s)", args[0]);
if (grub_strcmp(cfgfile, g_iso_path) == 0)
{
return ventoy_browser_iso_part();
}
}
if (!ventoy_browser_mbuf_alloc(&mbuf))
{
return 1;
}
fs = (grub_fs_t)grub_strtoul(args[1], NULL, 16);
if (!fs)
{
debug("Invalid fs %s\n", args[1]);
return 1;
}
dev = grub_device_open(args[0]);
if (!dev)
{
debug("Failed to open device %s\n", args[0]);
return 1;
}
g_menu_fs = fs;
g_menu_device = args[0];
g_menu_dev = dev;
g_browser_list = NULL;
if (args[2][0] == '/' && args[2][1] == 0)
{
g_menu_path_len = 0;
g_menu_path_buf[0] = 0;
fs->fs_dir(dev, "/", ventoy_browser_iterate_dir, NULL);
}
else
{
g_menu_path_len = grub_snprintf(g_menu_path_buf, sizeof(g_menu_path_buf), "%s", args[2]);
fs->fs_dir(dev, g_menu_path_buf, ventoy_browser_iterate_dir, NULL);
}
grub_device_close(dev);
if (g_tree_view_menu_style == 0)
{
browser_ssprintf(&mbuf, "menuentry \"%-10s [(%s)%s/..]\" --class=\"vtoyret\" VTOY_RET {\n "
" echo 'return ...' \n}\n", "<--", args[0], g_menu_path_buf);
}
else
{
browser_ssprintf(&mbuf, "menuentry \"[(%s)%s/..]\" --class=\"vtoyret\" VTOY_RET {\n "
" echo 'return ...' \n}\n", args[0], g_menu_path_buf);
}
for (i = 1; i >= 0; i--)
{
while (1)
{
node = ventoy_browser_find_top_node(i);
if (node)
{
browser_ssprintf(&mbuf, "%s", node->menuentry);
ventoy_browser_mbuf_extend(&mbuf);
if (node->prev)
{
node->prev->next = node->next;
}
if (node->next)
{
node->next->prev = node->prev;
}
if (node == g_browser_list)
{
g_browser_list = node->next;
}
grub_free(node);
}
else
{
break;
}
}
}
g_browser_list = NULL;
grub_snprintf(cfgfile, sizeof(cfgfile), "configfile mem:0x%lx:size:%d", (ulong)mbuf.buf, mbuf.pos);
grub_script_execute_sourcecode(cfgfile);
ventoy_browser_mbuf_free(&mbuf);
VENTOY_CMD_RETURN(GRUB_ERR_NONE);
}
grub_err_t ventoy_cmd_browser_disk(grub_extcmd_context_t ctxt, int argc, char **args)
{
char cfgfile[64];
browser_mbuf mbuf;
(void)ctxt;
(void)argc;
(void)args;
if (!ventoy_browser_mbuf_alloc(&mbuf))
{
return 1;
}
g_vtoy_dev = grub_env_get("vtoydev");
if (g_tree_view_menu_style == 0)
{
browser_ssprintf(&mbuf, "menuentry \"%-10s [%s]\" --class=\"vtoyret\" VTOY_RET {\n "
" echo 'return ...' \n}\n", "<--",
ventoy_get_vmenu_title("VTLANG_BROWER_RETURN"));
}
else
{
browser_ssprintf(&mbuf, "menuentry \"[%s]\" --class=\"vtoyret\" VTOY_RET {\n "
" echo 'return ...' \n}\n",
ventoy_get_vmenu_title("VTLANG_BROWER_RETURN"));
}
grub_disk_dev_iterate(ventoy_browser_iterate_disk, &mbuf);
grub_snprintf(cfgfile, sizeof(cfgfile), "configfile mem:0x%lx:size:%d", (ulong)mbuf.buf, mbuf.pos);
grub_script_execute_sourcecode(cfgfile);
ventoy_browser_mbuf_free(&mbuf);
VENTOY_CMD_RETURN(GRUB_ERR_NONE);
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,758 @@
/******************************************************************************
* 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;
if (g_ventoy_debug == 0)
{
return;
}
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;
}
if (*(pcPos - 1) == '\\')
{
for (pcPos++; *pcPos; pcPos++)
{
if (*pcPos == '"' && *(pcPos - 1) != '\\')
{
break;
}
}
if (*pcPos == 0 || pcPos < pcTmp)
{
json_debug("Invalid quotes 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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,754 @@
/******************************************************************************
* ventoy_vhd.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/charset.h>
#ifdef GRUB_MACHINE_EFI
#include <grub/efi/efi.h>
#endif
#include <grub/ventoy.h>
#include "ventoy_def.h"
GRUB_MOD_LICENSE ("GPLv3+");
static int g_vhdboot_isolen = 0;
static char *g_vhdboot_totbuf = NULL;
static char *g_vhdboot_isobuf = NULL;
static grub_uint64_t g_img_trim_head_secnum = 0;
static int ventoy_vhd_find_bcd(int *bcdoffset, int *bcdlen, const char *path)
{
grub_uint32_t offset;
grub_file_t file;
char cmdbuf[128];
grub_snprintf(cmdbuf, sizeof(cmdbuf), "loopback vhdiso mem:0x%lx:size:%d", (ulong)g_vhdboot_isobuf, g_vhdboot_isolen);
grub_script_execute_sourcecode(cmdbuf);
file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "(vhdiso)%s", path);
if (!file)
{
return 1;
}
grub_file_read(file, &offset, 4);
offset = (grub_uint32_t)grub_iso9660_get_last_read_pos(file);
*bcdoffset = (int)offset;
*bcdlen = (int)file->size;
debug("vhdiso bcd file offset:%d len:%d\n", *bcdoffset, *bcdlen);
grub_file_close(file);
grub_script_execute_sourcecode("loopback -d vhdiso");
return 0;
}
static int ventoy_vhd_patch_path(char *vhdpath, ventoy_patch_vhd *patch1, ventoy_patch_vhd *patch2,
int bcdoffset, int bcdlen)
{
int i;
int cnt = 0;
char *pos;
grub_size_t pathlen;
const char *plat;
char *newpath = NULL;
grub_uint16_t *unicode_path;
const grub_uint8_t winloadexe[] =
{
0x77, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x61, 0x00, 0x64, 0x00, 0x2E, 0x00,
0x65, 0x00, 0x78, 0x00, 0x65, 0x00
};
while ((*vhdpath) != '/')
{
vhdpath++;
}
pathlen = sizeof(grub_uint16_t) * (grub_strlen(vhdpath) + 1);
debug("unicode path for <%s> len:%d\n", vhdpath, (int)pathlen);
unicode_path = grub_zalloc(pathlen);
if (!unicode_path)
{
return 0;
}
plat = grub_env_get("grub_platform");
if (plat && (plat[0] == 'e')) /* UEFI */
{
pos = g_vhdboot_isobuf + bcdoffset;
/* winload.exe ==> winload.efi */
for (i = 0; i + (int)sizeof(winloadexe) < bcdlen; i++)
{
if (*((grub_uint32_t *)(pos + i)) == 0x00690077 &&
grub_memcmp(pos + i, winloadexe, sizeof(winloadexe)) == 0)
{
pos[i + sizeof(winloadexe) - 4] = 0x66;
pos[i + sizeof(winloadexe) - 2] = 0x69;
cnt++;
}
}
debug("winload patch %d times\n", cnt);
}
newpath = grub_strdup(vhdpath);
for (pos = newpath; *pos; pos++)
{
if (*pos == '/')
{
*pos = '\\';
}
}
grub_utf8_to_utf16(unicode_path, pathlen, (grub_uint8_t *)newpath, -1, NULL);
grub_memcpy(patch1->vhd_file_path, unicode_path, pathlen);
grub_memcpy(patch2->vhd_file_path, unicode_path, pathlen);
grub_free(newpath);
return 0;
}
static int ventoy_vhd_read_parttbl(const char *filename, ventoy_gpt_info *gpt, int *index, grub_uint64_t *poffset)
{
int i;
int find = 0;
int ret = 1;
grub_uint64_t start;
grub_file_t file = NULL;
grub_disk_t disk = NULL;
grub_uint8_t zeroguid[16] = {0};
file = grub_file_open(filename, VENTOY_FILE_TYPE);
if (!file)
{
goto end;
}
disk = grub_disk_open(file->device->disk->name);
if (!disk)
{
goto end;
}
grub_disk_read(disk, 0, 0, sizeof(ventoy_gpt_info), gpt);
start = file->device->disk->partition->start;
if (grub_memcmp(gpt->Head.Signature, "EFI PART", 8) == 0)
{
debug("GPT part start: %llu\n", (ulonglong)start);
for (i = 0; i < 128; i++)
{
if (grub_memcmp(gpt->PartTbl[i].PartGuid, zeroguid, 16))
{
if (start == gpt->PartTbl[i].StartLBA)
{
*index = i;
find = 1;
break;
}
}
}
}
else
{
debug("MBR part start: %llu\n", (ulonglong)start);
for (i = 0; i < 4; i++)
{
if ((grub_uint32_t)start == gpt->MBR.PartTbl[i].StartSectorId)
{
*index = i;
find = 1;
break;
}
}
}
if (find == 0) // MBR Logical partition
{
if (file->device->disk->partition->number > 0)
{
*index = file->device->disk->partition->number;
debug("Fall back part number: %d\n", *index);
}
}
*poffset = start;
ret = 0;
end:
check_free(file, grub_file_close);
check_free(disk, grub_disk_close);
return ret;
}
static int ventoy_vhd_patch_disk(const char *vhdpath, ventoy_patch_vhd *patch1, ventoy_patch_vhd *patch2)
{
int partIndex = 0;
grub_uint64_t offset = 0;
char efipart[16] = {0};
ventoy_gpt_info *gpt = NULL;
if (vhdpath[0] == '/')
{
gpt = g_ventoy_part_info;
partIndex = 0;
debug("This is Ventoy ISO partIndex %d %s\n", partIndex, vhdpath);
}
else
{
gpt = grub_zalloc(sizeof(ventoy_gpt_info));
ventoy_vhd_read_parttbl(vhdpath, gpt, &partIndex, &offset);
debug("This is HDD partIndex %d %s\n", partIndex, vhdpath);
}
grub_memcpy(efipart, gpt->Head.Signature, sizeof(gpt->Head.Signature));
grub_memset(patch1, 0, OFFSET_OF(ventoy_patch_vhd, vhd_file_path));
grub_memset(patch2, 0, OFFSET_OF(ventoy_patch_vhd, vhd_file_path));
if (grub_strncmp(efipart, "EFI PART", 8) == 0)
{
ventoy_debug_dump_guid("GPT disk GUID: ", gpt->Head.DiskGuid);
ventoy_debug_dump_guid("GPT partIndex GUID: ", gpt->PartTbl[partIndex].PartGuid);
grub_memcpy(patch1->disk_signature_or_guid, gpt->Head.DiskGuid, 16);
grub_memcpy(patch1->part_offset_or_guid, gpt->PartTbl[partIndex].PartGuid, 16);
grub_memcpy(patch2->disk_signature_or_guid, gpt->Head.DiskGuid, 16);
grub_memcpy(patch2->part_offset_or_guid, gpt->PartTbl[partIndex].PartGuid, 16);
patch1->part_type = patch2->part_type = 0;
}
else
{
if (offset == 0)
{
offset = gpt->MBR.PartTbl[partIndex].StartSectorId;
}
offset *= 512;
debug("MBR disk signature: %02x%02x%02x%02x Part(%d) offset:%llu\n",
gpt->MBR.BootCode[0x1b8 + 0], gpt->MBR.BootCode[0x1b8 + 1],
gpt->MBR.BootCode[0x1b8 + 2], gpt->MBR.BootCode[0x1b8 + 3],
partIndex + 1, offset);
grub_memcpy(patch1->part_offset_or_guid, &offset, 8);
grub_memcpy(patch2->part_offset_or_guid, &offset, 8);
grub_memcpy(patch1->disk_signature_or_guid, gpt->MBR.BootCode + 0x1b8, 4);
grub_memcpy(patch2->disk_signature_or_guid, gpt->MBR.BootCode + 0x1b8, 4);
patch1->part_type = patch2->part_type = 1;
}
if (gpt != g_ventoy_part_info)
{
grub_free(gpt);
}
return 0;
}
static int ventoy_find_vhdpatch_offset(int bcdoffset, int bcdlen, int *offset)
{
int i;
int cnt = 0;
grub_uint8_t *buf = (grub_uint8_t *)(g_vhdboot_isobuf + bcdoffset);
grub_uint8_t magic[16] = {
0x5C, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00
};
for (i = 0; i < bcdlen - 16 && cnt < 2; i++)
{
if (*(grub_uint32_t *)(buf + i) == 0x0058005C)
{
if (grub_memcmp(magic, buf + i, 16) == 0)
{
*offset++ = i - (int)OFFSET_OF(ventoy_patch_vhd, vhd_file_path);
cnt++;
}
}
}
return 0;
}
grub_err_t ventoy_cmd_patch_vhdboot(grub_extcmd_context_t ctxt, int argc, char **args)
{
int rc;
int bcdoffset, bcdlen;
int patchoffset[2];
ventoy_patch_vhd *patch1;
ventoy_patch_vhd *patch2;
(void)ctxt;
(void)argc;
grub_env_unset("vtoy_vhd_buf_addr");
debug("patch vhd <%s>\n", args[0]);
if ((!g_vhdboot_enable) || (!g_vhdboot_totbuf))
{
debug("vhd boot not ready %d %p\n", g_vhdboot_enable, g_vhdboot_totbuf);
return 0;
}
rc = ventoy_vhd_find_bcd(&bcdoffset, &bcdlen, "/boot/bcd");
if (rc)
{
debug("failed to get bcd location %d\n", rc);
}
else
{
ventoy_find_vhdpatch_offset(bcdoffset, bcdlen, patchoffset);
patch1 = (ventoy_patch_vhd *)(g_vhdboot_isobuf + bcdoffset + patchoffset[0]);
patch2 = (ventoy_patch_vhd *)(g_vhdboot_isobuf + bcdoffset + patchoffset[1]);
debug("Find /boot/bcd (%d %d) now patch it (offset: 0x%x 0x%x) ...\n",
bcdoffset, bcdlen, patchoffset[0], patchoffset[1]);
ventoy_vhd_patch_disk(args[0], patch1, patch2);
ventoy_vhd_patch_path(args[0], patch1, patch2, bcdoffset, bcdlen);
}
rc = ventoy_vhd_find_bcd(&bcdoffset, &bcdlen, "/boot/BCD");
if (rc)
{
debug("No file /boot/BCD \n");
}
else
{
ventoy_find_vhdpatch_offset(bcdoffset, bcdlen, patchoffset);
patch1 = (ventoy_patch_vhd *)(g_vhdboot_isobuf + bcdoffset + patchoffset[0]);
patch2 = (ventoy_patch_vhd *)(g_vhdboot_isobuf + bcdoffset + patchoffset[1]);
debug("Find /boot/BCD (%d %d) now patch it (offset: 0x%x 0x%x) ...\n",
bcdoffset, bcdlen, patchoffset[0], patchoffset[1]);
ventoy_vhd_patch_disk(args[0], patch1, patch2);
ventoy_vhd_patch_path(args[0], patch1, patch2, bcdoffset, bcdlen);
}
/* set buffer and size */
#ifdef GRUB_MACHINE_EFI
ventoy_memfile_env_set("vtoy_vhd_buf", g_vhdboot_totbuf, (ulonglong)(g_vhdboot_isolen + sizeof(ventoy_chain_head)));
#else
ventoy_memfile_env_set("vtoy_vhd_buf", g_vhdboot_isobuf, (ulonglong)g_vhdboot_isolen);
#endif
VENTOY_CMD_RETURN(GRUB_ERR_NONE);
}
grub_err_t ventoy_cmd_load_vhdboot(grub_extcmd_context_t ctxt, int argc, char **args)
{
int buflen;
grub_file_t file;
(void)ctxt;
(void)argc;
g_vhdboot_enable = 0;
grub_check_free(g_vhdboot_totbuf);
file = grub_file_open(args[0], VENTOY_FILE_TYPE);
if (!file)
{
return 0;
}
debug("load vhd boot: <%s> <%lu>\n", args[0], (ulong)file->size);
if (file->size < VTOY_SIZE_1KB * 32)
{
grub_file_close(file);
return 0;
}
g_vhdboot_isolen = (int)file->size;
buflen = (int)(file->size + sizeof(ventoy_chain_head));
#ifdef GRUB_MACHINE_EFI
g_vhdboot_totbuf = (char *)grub_efi_allocate_iso_buf(buflen);
#else
g_vhdboot_totbuf = (char *)grub_malloc(buflen);
#endif
if (!g_vhdboot_totbuf)
{
grub_file_close(file);
return 0;
}
g_vhdboot_isobuf = g_vhdboot_totbuf + sizeof(ventoy_chain_head);
grub_file_read(file, g_vhdboot_isobuf, file->size);
grub_file_close(file);
g_vhdboot_enable = 1;
return 0;
}
static int ventoy_raw_trim_head(grub_uint64_t offset)
{
grub_uint32_t i;
grub_uint32_t memsize;
grub_uint32_t imgstart = 0;
grub_uint32_t imgsecs = 0;
grub_uint64_t sectors = 0;
grub_uint64_t cursecs = 0;
grub_uint64_t delta = 0;
if ((!g_img_chunk_list.chunk) || (!offset))
{
debug("image chunk not ready %p %lu\n", g_img_chunk_list.chunk, (ulong)offset);
return 0;
}
debug("image trim head %lu\n", (ulong)offset);
for (i = 0; i < g_img_chunk_list.cur_chunk; i++)
{
cursecs = g_img_chunk_list.chunk[i].disk_end_sector + 1 - g_img_chunk_list.chunk[i].disk_start_sector;
sectors += cursecs;
if (sectors >= offset)
{
delta = cursecs - (sectors - offset);
break;
}
}
if (sectors < offset || i >= g_img_chunk_list.cur_chunk)
{
debug("Invalid size %lu %lu\n", (ulong)sectors, (ulong)offset);
return 0;
}
if (sectors == offset)
{
memsize = (g_img_chunk_list.cur_chunk - (i + 1)) * sizeof(ventoy_img_chunk);
grub_memmove(g_img_chunk_list.chunk, g_img_chunk_list.chunk + i + 1, memsize);
g_img_chunk_list.cur_chunk -= (i + 1);
}
else
{
g_img_chunk_list.chunk[i].disk_start_sector += delta;
g_img_chunk_list.chunk[i].img_start_sector += (grub_uint32_t)(delta / 4);
if (i > 0)
{
memsize = (g_img_chunk_list.cur_chunk - i) * sizeof(ventoy_img_chunk);
grub_memmove(g_img_chunk_list.chunk, g_img_chunk_list.chunk + i, memsize);
g_img_chunk_list.cur_chunk -= i;
}
}
for (i = 0; i < g_img_chunk_list.cur_chunk; i++)
{
imgsecs = g_img_chunk_list.chunk[i].img_end_sector + 1 - g_img_chunk_list.chunk[i].img_start_sector;
g_img_chunk_list.chunk[i].img_start_sector = imgstart;
g_img_chunk_list.chunk[i].img_end_sector = imgstart + (imgsecs - 1);
imgstart += imgsecs;
}
return 0;
}
grub_err_t ventoy_cmd_get_vtoy_type(grub_extcmd_context_t ctxt, int argc, char **args)
{
int i;
int altboot = 0;
int offset = -1;
grub_file_t file;
grub_uint8_t data = 0;
vhd_footer_t vhdfoot;
VDIPREHEADER vdihdr;
char type[16] = {0};
ventoy_gpt_info *gpt = NULL;
(void)ctxt;
g_img_trim_head_secnum = 0;
if (argc != 4)
{
return 0;
}
file = grub_file_open(args[0], VENTOY_FILE_TYPE);
if (!file)
{
debug("Failed to open file %s\n", args[0]);
return 0;
}
grub_snprintf(type, sizeof(type), "unknown");
grub_file_seek(file, file->size - 512);
grub_file_read(file, &vhdfoot, sizeof(vhdfoot));
if (grub_strncmp(vhdfoot.cookie, "conectix", 8) == 0)
{
offset = 0;
grub_snprintf(type, sizeof(type), "vhd%u", grub_swap_bytes32(vhdfoot.disktype));
}
else
{
grub_file_seek(file, 0);
grub_file_read(file, &vdihdr, sizeof(vdihdr));
if (vdihdr.u32Signature == VDI_IMAGE_SIGNATURE)
{
grub_snprintf(type, sizeof(type), "vdi");
if (grub_strncmp(vdihdr.szFileInfo, VDI_IMAGE_FILE_INFO, grub_strlen(VDI_IMAGE_FILE_INFO)) == 0)
{
offset = 2 * 1048576;
g_img_trim_head_secnum = offset / 512;
debug("VDI V1\n");
}
else if (grub_strncmp(vdihdr.szFileInfo, VDI_IMAGE_FILE_INFO2, grub_strlen(VDI_IMAGE_FILE_INFO2)) == 0)
{
offset = 2 * 1048576;
g_img_trim_head_secnum = offset / 512;
debug("VDI V2\n");
}
else
{
debug("invalid file info <%s>\n", vdihdr.szFileInfo);
}
}
else
{
offset = 0;
grub_snprintf(type, sizeof(type), "raw");
}
}
grub_env_set(args[1], type);
debug("<%s> vtoy type: <%s> offset:%d\n", args[0], type, offset);
if (offset >= 0)
{
gpt = grub_zalloc(sizeof(ventoy_gpt_info));
if (!gpt)
{
grub_env_set(args[1], "unknown");
goto end;
}
grub_file_seek(file, offset);
grub_file_read(file, gpt, sizeof(ventoy_gpt_info));
if (gpt->MBR.Byte55 != 0x55 || gpt->MBR.ByteAA != 0xAA)
{
grub_env_set(args[1], "unknown");
debug("invalid mbr signature: 0x%x 0x%x offset=%d\n", gpt->MBR.Byte55, gpt->MBR.ByteAA, offset);
goto end;
}
if (grub_memcmp(gpt->Head.Signature, "EFI PART", 8) == 0)
{
grub_env_set(args[2], "gpt");
debug("part type: %s\n", "GPT");
if (gpt->MBR.PartTbl[0].FsFlag == 0xEE)
{
for (i = 0; i < 128; i++)
{
if (grub_memcmp(gpt->PartTbl[i].PartType, "Hah!IdontNeedEFI", 16) == 0)
{
debug("part %d is grub_bios part\n", i);
altboot = 1;
grub_env_set(args[3], "1");
break;
}
else if (gpt->PartTbl[i].LastLBA == 0)
{
break;
}
}
}
if (!altboot)
{
if (gpt->MBR.BootCode[92] == 0x22)
{
grub_file_seek(file, offset + 17908);
grub_file_read(file, &data, 1);
if (data == 0x23)
{
altboot = 1;
grub_env_set(args[3], "1");
}
else
{
debug("offset data=0x%x\n", data);
}
}
else
{
debug("BootCode: 0x%x\n", gpt->MBR.BootCode[92]);
}
}
}
else
{
grub_env_set(args[2], "mbr");
debug("part type: %s\n", "MBR");
for (i = 0; i < 4; i++)
{
if (gpt->MBR.PartTbl[i].FsFlag == 0xEF)
{
debug("part %d is esp part in MBR mode\n", i);
altboot = 1;
grub_env_set(args[3], "1");
break;
}
}
}
}
else
{
debug("part type: %s\n", "xxx");
}
end:
grub_check_free(gpt);
grub_file_close(file);
VENTOY_CMD_RETURN(GRUB_ERR_NONE);
}
grub_err_t ventoy_cmd_raw_chain_data(grub_extcmd_context_t ctxt, int argc, char **args)
{
grub_uint32_t size = 0;
grub_uint32_t img_chunk_size = 0;
grub_file_t file;
grub_disk_t disk;
const char *pLastChain = NULL;
ventoy_chain_head *chain;
(void)ctxt;
(void)argc;
if (NULL == g_img_chunk_list.chunk)
{
grub_printf("ventoy not ready\n");
return 1;
}
if (g_img_trim_head_secnum > 0)
{
ventoy_raw_trim_head(g_img_trim_head_secnum);
}
file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
if (!file)
{
return 1;
}
if (grub_strncmp(args[0], g_iso_path, grub_strlen(g_iso_path)))
{
file->vlnk = 1;
}
img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
size = sizeof(ventoy_chain_head) + img_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 = ventoy_alloc_chain(size);
if (!chain)
{
grub_printf("Failed to alloc chain raw memory size %u\n", size);
grub_file_close(file);
return 1;
}
ventoy_memfile_env_set("vtoy_chain_mem", chain, (ulonglong)size);
grub_env_export("vtoy_chain_mem_addr");
grub_env_export("vtoy_chain_mem_size");
grub_memset(chain, 0, sizeof(ventoy_chain_head));
/* part 1: os parameter */
g_ventoy_chain_type = ventoy_chain_linux;
ventoy_fill_os_param(file, &(chain->os_param));
/* 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;
if (g_img_trim_head_secnum > 0)
{
chain->real_img_size_in_bytes -= g_img_trim_head_secnum * 512;
}
chain->virt_img_size_in_bytes = chain->real_img_size_in_bytes;
chain->boot_catalog = 0;
/* 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);
grub_file_seek(file, g_img_trim_head_secnum * 512);
grub_file_read(file, chain->boot_catalog_sector, 512);
grub_file_close(file);
VENTOY_CMD_RETURN(GRUB_ERR_NONE);
}
File diff suppressed because it is too large Load Diff
@@ -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
@@ -0,0 +1,157 @@
/*
* Copyright (C) 2012 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
*
* Xpress Compression Algorithm (MS-XCA) decompression
*
*/
#include "wimboot.h"
#include "huffman.h"
#include "xpress.h"
#pragma GCC diagnostic ignored "-Wcast-align"
/**
* Decompress XCA-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 xca_decompress ( const void *data, size_t len, void *buf ) {
const void *src = data;
const void *end = ( uint8_t * ) src + len;
uint8_t *out = buf;
size_t out_len = 0;
size_t out_len_threshold = 0;
const struct xca_huf_len *lengths;
struct xca xca;
uint32_t accum = 0;
int extra_bits = 0;
unsigned int huf;
struct huffman_symbols *sym;
unsigned int raw;
unsigned int match_len;
unsigned int match_offset_bits;
unsigned int match_offset;
const uint8_t *copy;
int rc;
/* Process data stream */
while ( src < end ) {
/* (Re)initialise decompressor if applicable */
if ( out_len >= out_len_threshold ) {
/* Construct symbol lengths */
lengths = src;
src = ( uint8_t * ) src + sizeof ( *lengths );
if ( src > end ) {
DBG ( "XCA too short to hold Huffman lengths table.\n");
return -1;
}
for ( raw = 0 ; raw < XCA_CODES ; raw++ )
xca.lengths[raw] = xca_huf_len ( lengths, raw );
/* Construct Huffman alphabet */
if ( ( rc = huffman_alphabet ( &xca.alphabet,
xca.lengths,
XCA_CODES ) ) != 0 )
return rc;
/* Initialise state */
accum = XCA_GET16 ( src );
accum <<= 16;
accum |= XCA_GET16 ( src );
extra_bits = 16;
/* Determine next threshold */
out_len_threshold = ( out_len + XCA_BLOCK_SIZE );
}
/* Determine symbol */
huf = ( accum >> ( 32 - HUFFMAN_BITS ) );
sym = huffman_sym ( &xca.alphabet, huf );
raw = huffman_raw ( sym, huf );
accum <<= huffman_len ( sym );
extra_bits -= huffman_len ( sym );
if ( extra_bits < 0 ) {
accum |= ( XCA_GET16 ( src ) << ( -extra_bits ) );
extra_bits += 16;
}
/* Process symbol */
if ( raw < XCA_END_MARKER ) {
/* Literal symbol - add to output stream */
if ( buf )
*(out++) = raw;
out_len++;
} else if ( ( raw == XCA_END_MARKER ) &&
( (uint8_t *) src >= ( ( uint8_t * ) end - 1 ) ) ) {
/* End marker symbol */
return out_len;
} else {
/* LZ77 match symbol */
raw -= XCA_END_MARKER;
match_offset_bits = ( raw >> 4 );
match_len = ( raw & 0x0f );
if ( match_len == 0x0f ) {
match_len = XCA_GET8 ( src );
if ( match_len == 0xff ) {
match_len = XCA_GET16 ( src );
} else {
match_len += 0x0f;
}
}
match_len += 3;
if ( match_offset_bits ) {
match_offset =
( ( accum >> ( 32 - match_offset_bits ))
+ ( 1 << match_offset_bits ) );
} else {
match_offset = 1;
}
accum <<= match_offset_bits;
extra_bits -= match_offset_bits;
if ( extra_bits < 0 ) {
accum |= ( XCA_GET16 ( src ) << (-extra_bits) );
extra_bits += 16;
}
/* Copy data */
out_len += match_len;
if ( buf ) {
copy = ( out - match_offset );
while ( match_len-- )
*(out++) = *(copy++);
}
}
}
return out_len;
}
@@ -0,0 +1,87 @@
#ifndef _XCA_H
#define _XCA_H
/*
* Copyright (C) 2012 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
*
* Xpress Compression Algorithm (MS-XCA) decompression
*
*/
#include "huffman.h"
/** Number of XCA codes */
#define XCA_CODES 512
/** XCA decompressor */
struct xca {
/** Huffman alphabet */
struct huffman_alphabet alphabet;
/** Raw symbols
*
* Must immediately follow the Huffman alphabet.
*/
huffman_raw_symbol_t raw[XCA_CODES];
/** Code lengths */
uint8_t lengths[XCA_CODES];
};
/** XCA symbol Huffman lengths table */
struct xca_huf_len {
/** Lengths of each symbol */
uint8_t nibbles[ XCA_CODES / 2 ];
} __attribute__ (( packed ));
/**
* Extract Huffman-coded length of a raw symbol
*
* @v lengths Huffman lengths table
* @v symbol Raw symbol
* @ret len Huffman-coded length
*/
static inline unsigned int xca_huf_len ( const struct xca_huf_len *lengths,
unsigned int symbol ) {
return ( ( ( lengths->nibbles[ symbol / 2 ] ) >>
( 4 * ( symbol % 2 ) ) ) & 0x0f );
}
/** Get word from source data stream */
#define XCA_GET16( src ) ( { \
const uint16_t *src16 = src; \
src = ( uint8_t * ) src + sizeof ( *src16 ); \
*src16; } )
/** Get byte from source data stream */
#define XCA_GET8( src ) ( { \
const uint8_t *src8 = src; \
src = ( uint8_t * ) src + sizeof ( *src8 ); \
*src8; } )
/** XCA source data stream end marker */
#define XCA_END_MARKER 256
/** XCA block size */
#define XCA_BLOCK_SIZE ( 64 * 1024 )
extern ssize_t xca_decompress ( const void *data, size_t len, void *buf );
#endif /* _XCA_H */
@@ -0,0 +1,56 @@
/* cache.h - Flush the processor's cache. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2004,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/>.
*/
#ifndef GRUB_CACHE_H
#define GRUB_CACHE_H 1
#include <grub/symbol.h>
#include <grub/types.h>
#if defined (__i386__) || defined (__x86_64__)
static inline void
grub_arch_sync_caches (void *address __attribute__ ((unused)),
grub_size_t len __attribute__ ((unused)))
{
}
#else
void EXPORT_FUNC(grub_arch_sync_caches) (void *address, grub_size_t len);
#endif
#ifndef GRUB_MACHINE_EMU
#if defined (__aarch64__) || defined (__ia64__) || defined (__powerpc__) || \
defined (__sparc__)
#elif defined (__i386__) || defined (__x86_64__)
static inline void
grub_arch_sync_dma_caches (volatile void *address __attribute__ ((unused)),
grub_size_t len __attribute__ ((unused)))
{
}
#elif defined(__mips__) && (_MIPS_SIM != _ABI64)
void EXPORT_FUNC(grub_arch_sync_dma_caches) (volatile void *address, grub_size_t len);
#endif
#endif
#ifdef __arm__
void
grub_arm_cache_enable (void);
#endif
#endif /* ! GRUB_CACHE_HEADER */
@@ -0,0 +1,329 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001,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_CHARSET_HEADER
#define GRUB_CHARSET_HEADER 1
#include <grub/types.h>
#define GRUB_UINT8_1_LEADINGBIT 0x80
#define GRUB_UINT8_2_LEADINGBITS 0xc0
#define GRUB_UINT8_3_LEADINGBITS 0xe0
#define GRUB_UINT8_4_LEADINGBITS 0xf0
#define GRUB_UINT8_5_LEADINGBITS 0xf8
#define GRUB_UINT8_6_LEADINGBITS 0xfc
#define GRUB_UINT8_7_LEADINGBITS 0xfe
#define GRUB_UINT8_1_TRAILINGBIT 0x01
#define GRUB_UINT8_2_TRAILINGBITS 0x03
#define GRUB_UINT8_3_TRAILINGBITS 0x07
#define GRUB_UINT8_4_TRAILINGBITS 0x0f
#define GRUB_UINT8_5_TRAILINGBITS 0x1f
#define GRUB_UINT8_6_TRAILINGBITS 0x3f
#define GRUB_MAX_UTF8_PER_UTF16 4
/* You need at least one UTF-8 byte to have one UTF-16 word.
You need at least three UTF-8 bytes to have 2 UTF-16 words (surrogate pairs).
*/
#define GRUB_MAX_UTF16_PER_UTF8 1
#define GRUB_MAX_UTF8_PER_CODEPOINT 4
#define GRUB_UCS2_LIMIT 0x10000
#define GRUB_UTF16_UPPER_SURROGATE(code) \
(0xD800 | ((((code) - GRUB_UCS2_LIMIT) >> 10) & 0x3ff))
#define GRUB_UTF16_LOWER_SURROGATE(code) \
(0xDC00 | (((code) - GRUB_UCS2_LIMIT) & 0x3ff))
/* Process one character from UTF8 sequence.
At beginning set *code = 0, *count = 0. Returns 0 on failure and
1 on success. *count holds the number of trailing bytes. */
static inline int
grub_utf8_process (grub_uint8_t c, grub_uint32_t *code, int *count)
{
if (*count)
{
if ((c & GRUB_UINT8_2_LEADINGBITS) != GRUB_UINT8_1_LEADINGBIT)
{
*count = 0;
/* invalid */
return 0;
}
else
{
*code <<= 6;
*code |= (c & GRUB_UINT8_6_TRAILINGBITS);
(*count)--;
/* Overlong. */
if ((*count == 1 && *code <= 0x1f)
|| (*count == 2 && *code <= 0xf))
{
*code = 0;
*count = 0;
return 0;
}
return 1;
}
}
if ((c & GRUB_UINT8_1_LEADINGBIT) == 0)
{
*code = c;
return 1;
}
if ((c & GRUB_UINT8_3_LEADINGBITS) == GRUB_UINT8_2_LEADINGBITS)
{
*count = 1;
*code = c & GRUB_UINT8_5_TRAILINGBITS;
/* Overlong */
if (*code <= 1)
{
*count = 0;
*code = 0;
return 0;
}
return 1;
}
if ((c & GRUB_UINT8_4_LEADINGBITS) == GRUB_UINT8_3_LEADINGBITS)
{
*count = 2;
*code = c & GRUB_UINT8_4_TRAILINGBITS;
return 1;
}
if ((c & GRUB_UINT8_5_LEADINGBITS) == GRUB_UINT8_4_LEADINGBITS)
{
*count = 3;
*code = c & GRUB_UINT8_3_TRAILINGBITS;
return 1;
}
return 0;
}
/* Convert a (possibly null-terminated) UTF-8 string of at most SRCSIZE
bytes (if SRCSIZE is -1, it is ignored) in length to a UTF-16 string.
Return the number of characters converted. DEST must be able to hold
at least DESTSIZE characters. If an invalid sequence is found, return -1.
If SRCEND is not NULL, then *SRCEND is set to the next byte after the
last byte used in SRC. */
static inline grub_size_t
grub_utf8_to_utf16 (grub_uint16_t *dest, grub_size_t destsize,
const grub_uint8_t *src, grub_size_t srcsize,
const grub_uint8_t **srcend)
{
grub_uint16_t *p = dest;
int count = 0;
grub_uint32_t code = 0;
if (srcend)
*srcend = src;
while (srcsize && destsize)
{
int was_count = count;
if (srcsize != (grub_size_t)-1)
srcsize--;
if (!grub_utf8_process (*src++, &code, &count))
{
code = '?';
count = 0;
/* Character c may be valid, don't eat it. */
if (was_count)
src--;
}
if (count != 0)
continue;
if (code == 0)
break;
if (destsize < 2 && code >= GRUB_UCS2_LIMIT)
break;
if (code >= GRUB_UCS2_LIMIT)
{
*p++ = GRUB_UTF16_UPPER_SURROGATE (code);
*p++ = GRUB_UTF16_LOWER_SURROGATE (code);
destsize -= 2;
}
else
{
*p++ = code;
destsize--;
}
}
if (srcend)
*srcend = src;
return p - dest;
}
/* Determine the last position where the UTF-8 string [beg, end) can
be safely cut. */
static inline grub_size_t
grub_getend (const char *beg, const char *end)
{
const char *ptr;
for (ptr = end - 1; ptr >= beg; ptr--)
if ((*ptr & GRUB_UINT8_2_LEADINGBITS) != GRUB_UINT8_1_LEADINGBIT)
break;
if (ptr < beg)
return 0;
if ((*ptr & GRUB_UINT8_1_LEADINGBIT) == 0)
return ptr + 1 - beg;
if ((*ptr & GRUB_UINT8_3_LEADINGBITS) == GRUB_UINT8_2_LEADINGBITS
&& ptr + 2 <= end)
return ptr + 2 - beg;
if ((*ptr & GRUB_UINT8_4_LEADINGBITS) == GRUB_UINT8_3_LEADINGBITS
&& ptr + 3 <= end)
return ptr + 3 - beg;
if ((*ptr & GRUB_UINT8_5_LEADINGBITS) == GRUB_UINT8_4_LEADINGBITS
&& ptr + 4 <= end)
return ptr + 4 - beg;
/* Invalid character or incomplete. Cut before it. */
return ptr - beg;
}
/* Convert UTF-16 to UTF-8. */
static inline grub_uint8_t *
grub_utf16_to_utf8 (grub_uint8_t *dest, const grub_uint16_t *src,
grub_size_t size)
{
grub_uint32_t code_high = 0;
while (size--)
{
grub_uint32_t code = *src++;
if (code_high)
{
if (code >= 0xDC00 && code <= 0xDFFF)
{
/* Surrogate pair. */
code = ((code_high - 0xD800) << 10) + (code - 0xDC00) + 0x10000;
*dest++ = (code >> 18) | 0xF0;
*dest++ = ((code >> 12) & 0x3F) | 0x80;
*dest++ = ((code >> 6) & 0x3F) | 0x80;
*dest++ = (code & 0x3F) | 0x80;
}
else
{
/* Error... */
*dest++ = '?';
/* *src may be valid. Don't eat it. */
src--;
}
code_high = 0;
}
else
{
if (code <= 0x007F)
*dest++ = code;
else if (code <= 0x07FF)
{
*dest++ = (code >> 6) | 0xC0;
*dest++ = (code & 0x3F) | 0x80;
}
else if (code >= 0xD800 && code <= 0xDBFF)
{
code_high = code;
continue;
}
else if (code >= 0xDC00 && code <= 0xDFFF)
{
/* Error... */
*dest++ = '?';
}
else if (code < 0x10000)
{
*dest++ = (code >> 12) | 0xE0;
*dest++ = ((code >> 6) & 0x3F) | 0x80;
*dest++ = (code & 0x3F) | 0x80;
}
else
{
*dest++ = (code >> 18) | 0xF0;
*dest++ = ((code >> 12) & 0x3F) | 0x80;
*dest++ = ((code >> 6) & 0x3F) | 0x80;
*dest++ = (code & 0x3F) | 0x80;
}
}
}
return dest;
}
#define GRUB_MAX_UTF8_PER_LATIN1 2
/* Convert Latin1 to UTF-8. */
static inline grub_uint8_t *
grub_latin1_to_utf8 (grub_uint8_t *dest, const grub_uint8_t *src,
grub_size_t size)
{
while (size--)
{
if (!(*src & 0x80))
*dest++ = *src;
else
{
*dest++ = (*src >> 6) | 0xC0;
*dest++ = (*src & 0x3F) | 0x80;
}
src++;
}
return dest;
}
/* Convert UCS-4 to UTF-8. */
char *grub_ucs4_to_utf8_alloc (const grub_uint32_t *src, grub_size_t size);
int
grub_is_valid_utf8 (const grub_uint8_t *src, grub_size_t srcsize);
grub_ssize_t grub_utf8_to_ucs4_alloc (const char *msg,
grub_uint32_t **unicode_msg,
grub_uint32_t **last_position);
/* Returns the number of bytes the string src would occupy is converted
to UTF-8, excluding \0. */
grub_size_t
grub_get_num_of_utf8_bytes (const grub_uint32_t *src, grub_size_t size);
/* Converts UCS-4 to UTF-8. Returns the number of bytes effectively written
excluding the trailing \0. */
grub_size_t
grub_ucs4_to_utf8 (const grub_uint32_t *src, grub_size_t size,
grub_uint8_t *dest, grub_size_t destsize);
grub_size_t grub_utf8_to_ucs4 (grub_uint32_t *dest, grub_size_t destsize,
const grub_uint8_t *src, grub_size_t srcsize,
const grub_uint8_t **srcend);
/* Returns -2 if not enough space, -1 on invalid character. */
grub_ssize_t
grub_encode_utf8_character (grub_uint8_t *dest, grub_uint8_t *destend,
grub_uint32_t code);
const grub_uint32_t *
grub_unicode_get_comb_start (const grub_uint32_t *str,
const grub_uint32_t *cur);
int
grub_utf8_get_num_code (const char *src, grub_size_t srcsize);
const char *
grub_utf8_offset_code (const char *src, grub_size_t srcsize, int num);
#endif
@@ -0,0 +1,53 @@
/* compiler.h - macros for various compiler features */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2002,2003,2005,2006,2007,2008,2009,2010,2014 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_COMPILER_HEADER
#define GRUB_COMPILER_HEADER 1
/* GCC version checking borrowed from glibc. */
#if defined(__GNUC__) && defined(__GNUC_MINOR__)
# define GNUC_PREREQ(maj,min) \
((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
#else
# define GNUC_PREREQ(maj,min) 0
#endif
/* Does this compiler support compile-time error attributes? */
#if GNUC_PREREQ(4,3)
# define ATTRIBUTE_ERROR(msg) \
__attribute__ ((__error__ (msg)))
#else
# define ATTRIBUTE_ERROR(msg) __attribute__ ((noreturn))
#endif
#if GNUC_PREREQ(4,4)
# define GNU_PRINTF gnu_printf
#else
# define GNU_PRINTF printf
#endif
#if GNUC_PREREQ(3,4)
# define WARN_UNUSED_RESULT __attribute__ ((warn_unused_result))
#else
# define WARN_UNUSED_RESULT
#endif
#define UNUSED __attribute__((__unused__))
#endif /* ! GRUB_COMPILER_HEADER */

Some files were not shown because too many files have changed in this diff Show More