Skip to content

Commit 71cb4fc

Browse files
Cyanoxygenchenx97
authored andcommitted
commands: add new command memsize
- This command traverses the entire GRUB memory map, adds the size of each region together. The result is the total amount of system memory available to GRUB in bytes. - It stores the vaule to the environment as the name `memsize' by default, but it is customizable. The unit of the value is also customizable too. - It checks if the vaule is too large for the `test' command to process, whose limit is INT_MAX. - This command is useful with `test' command to implement mechanisms such as booting the live media with live filesystem loaded into RAN or not, instead of waiting initrd to fail: insmod memsize memsize -M --set=mem_avail if [ $mem_avail -gt 4000 ] ; then menuentry 'Live OS (Load into RAM)' { linux /boot/vmlinuz rd.live.ram=1 ... initrd /boot/initrd.img } fi It is also possible to implement mechanisms to boot into different mode (e.g. graphical or multi-user), depending on RAM available. * docs/grub.texi: add documentation for memsize Signed-off-by: Xinhui Yang <[email protected]>
1 parent c23c121 commit 71cb4fc

File tree

3 files changed

+301
-0
lines changed

3 files changed

+301
-0
lines changed

docs/grub.texi

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4379,6 +4379,7 @@ you forget a command, you can run the command @command{help}
43794379
* ls:: List devices or files
43804380
* lsfonts:: List loaded fonts
43814381
* lsmod:: Show loaded modules
4382+
* memsize:: Get the amount of available RAM
43824383
* md5sum:: Compute or check MD5 hash
43834384
* module:: Load module for multiboot kernel
43844385
* multiboot:: Load multiboot compliant kernel
@@ -5149,6 +5150,68 @@ List loaded fonts.
51495150
Show list of loaded modules.
51505151
@end deffn
51515152

5153+
5154+
@node memsize
5155+
@subsection memsize
5156+
5157+
@deffn Command memsize [@option{-b}|@option{-k}|@option{-m}|@option{-g}|@option{-K}|@option{-M}|@option{-G}] [@option{-q}] [@option{--set} VARNAME]
5158+
Iterate through GRUB memory map to get the amount of system RAM available to
5159+
GRUB. The final integer value of the unit specified will be exported to the
5160+
environment as @code{VARNAME} to make good use of the @pxref{test} command,
5161+
and optionally be printed out to the console.
5162+
5163+
The final value is checked to make sure it does not exceed the limit of the
5164+
@pxref{test} command, which is INT_MAX. Otherwise, it raises an error.
5165+
5166+
The default unit used for the value is MiB (@option{-M}).
5167+
5168+
The default variable name is @code{memsize}.
5169+
5170+
@option{-q} disables the output.
5171+
5172+
@code{VARNAME} should be a valid identifier and should not exceed 63
5173+
characters.
5174+
5175+
The units can be:
5176+
5177+
@table @code
5178+
@item @option{-b}
5179+
Display and export the value in number of bytes.
5180+
5181+
@item @option{-k}
5182+
Display and export the vaule in kilobytes (@code{kB}).
5183+
5184+
@item @option{-m}
5185+
Display and export the vaule in megabytes (@code{MB}).
5186+
5187+
@item @option{-g}
5188+
Display and export the vaule in gigabytes (@code{GB}).
5189+
5190+
@item @option{-K}
5191+
Display and export the vaule in kibibytes (@code{KiB}).
5192+
5193+
@item @option{-M}
5194+
Display and export the vaule in mebibytes (@code{MiB}).
5195+
5196+
@item @option{-G}
5197+
Display and export the vaule in gibibytes (@code{GiB}).
5198+
@end table
5199+
5200+
One can make use of this command to implement mechanisms like e.g. booting
5201+
into different modes depending on available RAM:
5202+
5203+
@example
5204+
memsize -M --set=mem_avail
5205+
if [ "$mem_avail" -lt 512 ] ; then
5206+
set boot_target="multi-user.target"
5207+
else
5208+
set boot_target="graphical.target"
5209+
fi
5210+
@end example
5211+
5212+
@end deffn
5213+
5214+
51525215
@node md5sum
51535216
@subsection md5sum
51545217

grub-core/Makefile.core.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,11 @@ module = {
10331033
common = commands/memrw.c;
10341034
};
10351035

1036+
module = {
1037+
name = memsize;
1038+
common = commands/memsize.c;
1039+
};
1040+
10361041
module = {
10371042
name = minicmd;
10381043
common = commands/minicmd.c;

grub-core/commands/memsize.c

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
/*
2+
* memsize.c - Iterate through GRUB memory map to get the total amount of
3+
* available RAM. The memory map does NOT reflect the total physical
4+
* amount, so use with care.
5+
* It can export the value to the environment, to make great use of tests.
6+
*/
7+
/*
8+
* GRUB -- GRand Unified Bootloader
9+
* Copyright (C) 2003,2007 Free Software Foundation, Inc.
10+
* Copyright (C) 2003 NIIBE Yutaka <[email protected]>
11+
*
12+
* GRUB is free software: you can redistribute it and/or modify
13+
* it under the terms of the GNU General Public License as published by
14+
* the Free Software Foundation, either version 3 of the License, or
15+
* (at your option) any later version.
16+
*
17+
* GRUB is distributed in the hope that it will be useful,
18+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
* GNU General Public License for more details.
21+
*
22+
* You should have received a copy of the GNU General Public License
23+
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
24+
*/
25+
26+
#include <grub/types.h>
27+
#include <grub/misc.h>
28+
#include <grub/memory.h>
29+
#include <grub/err.h>
30+
#include <grub/env.h>
31+
#include <grub/dl.h>
32+
#include <grub/extcmd.h>
33+
#include <grub/i18n.h>
34+
35+
GRUB_MOD_LICENSE ("GPLv3+");
36+
37+
const char *default_varname = "memsize";
38+
grub_extcmd_t cmd;
39+
grub_uint64_t grub_avail_mem_bytes = 0;
40+
41+
static const struct grub_arg_option grub_options_memsize[] =
42+
{
43+
{"byte", 'b', GRUB_ARG_OPTION_OPTIONAL,
44+
N_("Print and export the vaule as number of bytes."), 0, 0},
45+
{"kibi", 'K', GRUB_ARG_OPTION_OPTIONAL,
46+
N_("Print and export the value as kibibytes."), 0, 0},
47+
{"mebi", 'M', GRUB_ARG_OPTION_OPTIONAL,
48+
N_("Print and export the value as mebibytes."), 0, 0},
49+
{"gibi", 'G', GRUB_ARG_OPTION_OPTIONAL,
50+
N_("Print and export the value as gibibytes."), 0, 0},
51+
{"quiet", 'q', GRUB_ARG_OPTION_OPTIONAL,
52+
N_("Do not print anyting, just set a variable quietly."), 0, 0},
53+
{"set", 's', GRUB_ARG_OPTION_OPTIONAL,
54+
N_("Set a variable to the vaule as the unit specified."),
55+
N_("VARNAME"), ARG_TYPE_STRING},
56+
{0, 0, 0, 0, 0, 0},
57+
};
58+
59+
/* Corresponding strings of the units */
60+
const char* unit_strs[] = {
61+
"B", "KiB", "MiB", "GiB",
62+
};
63+
64+
/* Corresponding vaules to convert */
65+
const int conversions[] = {
66+
0, 10, 20, 30
67+
};
68+
69+
enum grub_options_memsize
70+
{
71+
UNIT_BYTES,
72+
UNIT_BIN_KIB,
73+
UNIT_BIN_MIB,
74+
UNIT_BIN_GIB,
75+
FLAG_QUIET,
76+
HAS_CUSTOM_VARIABLE,
77+
};
78+
79+
#ifdef GRUB_MACHINE_EMU
80+
__attribute__ ((unused))
81+
#endif
82+
static int
83+
memsize_hook (grub_uint64_t addr __attribute__ ((unused)),
84+
grub_uint64_t size, grub_memory_type_t type, void *data)
85+
{
86+
/*
87+
* Iterate through GRUB memory map.
88+
* FIXME: Which type of memory region should we consider to add up?
89+
*/
90+
grub_uint64_t *total = (grub_uint64_t *) data;
91+
if (type == GRUB_MEMORY_AVAILABLE) {
92+
*total += size;
93+
}
94+
return GRUB_ERR_NONE;
95+
}
96+
97+
static grub_err_t
98+
grub_cmd_memsize (grub_extcmd_context_t ctxt,
99+
int argc __attribute__ ((unused)),
100+
char **args __attribute__ ((unused)))
101+
{
102+
/* Parsed argument list. */
103+
struct grub_arg_list *state = ctxt->state;
104+
/* Length of the variable name. */
105+
grub_size_t len = 0;
106+
/* Buffer of variable name. */
107+
char buf[64] = {0};
108+
/* Requested unit to convert. */
109+
enum grub_options_memsize unit = UNIT_BIN_MIB;
110+
enum grub_options_memsize opt = UNIT_BYTES;
111+
/* Flag to make sure only one unit is specified. */
112+
int unit_switch_is_set = 0;
113+
/* Total amount of available RAM. */
114+
grub_uint64_t avail_mem = 0;
115+
/* The converted value. */
116+
grub_uint64_t converted_avail_mem = 0;
117+
char *converted_avail_mem_str = 0;
118+
/* Record errors during grub_machine_mmap_iterate. */
119+
#ifdef GRUB_MACHINE_EMU
120+
__attribute__ ((unused))
121+
#endif
122+
grub_err_t err;
123+
/* Sanity checks */
124+
/* Only one unit switch is allowed. */
125+
for (; opt < FLAG_QUIET; opt++)
126+
{
127+
if (state[opt].set != 0)
128+
{
129+
if (unit_switch_is_set != 0)
130+
return grub_error (GRUB_ERR_BAD_ARGUMENT,
131+
N_("Only one unit switch is allowed"));
132+
else
133+
{
134+
unit = opt;
135+
unit_switch_is_set = 1;
136+
}
137+
}
138+
}
139+
/* Check if the specified variable name is valid. */
140+
if (state[HAS_CUSTOM_VARIABLE].set != 0)
141+
{
142+
char *str = state[HAS_CUSTOM_VARIABLE].arg;
143+
len = grub_strlen (str);
144+
if (len > 63) {
145+
return grub_error (GRUB_ERR_BAD_ARGUMENT,
146+
N_("Variable names should not exceed 63 characters"));
147+
} else if (len == 0) {
148+
return grub_error (GRUB_ERR_BAD_ARGUMENT,
149+
N_("Variable name expected but not specified"));
150+
}
151+
/* [0-9a-zA-Z_] for variable names */
152+
for (char *c = str; c < str + len; c++)
153+
{
154+
if ( !(*c >= '0' && *c <= '9')
155+
&& !(*c >= 'a' && *c <= 'z')
156+
&& !(*c >= 'A' && *c <= 'Z')
157+
&& !(*c == '_'))
158+
return grub_error(GRUB_ERR_BAD_ARGUMENT,
159+
N_("Invalid variable name"));
160+
}
161+
grub_strncpy (buf, str, len);
162+
}
163+
else
164+
{
165+
len = grub_strlen (default_varname);
166+
grub_strncpy (buf, default_varname, len);
167+
}
168+
/* Iterate through GRUB's memory map. */
169+
#ifndef GRUB_MACHINE_EMU
170+
err = grub_machine_mmap_iterate (memsize_hook, (void *) &avail_mem);
171+
if (err != GRUB_ERR_NONE)
172+
return err;
173+
#endif
174+
/*
175+
* Convert the vaule to the specified unit, and export it to the
176+
* environment.
177+
* Can not use division, which might requires libgcc!
178+
*/
179+
converted_avail_mem = avail_mem >> conversions[unit];
180+
/*
181+
* Unfortunately the test command can only perform comparisons across
182+
* signed ints, or the test command will fail. We need to check this in
183+
* order to make it usable to test command.
184+
*/
185+
if (converted_avail_mem > GRUB_INT_MAX)
186+
{
187+
return grub_error (GRUB_ERR_BAD_NUMBER,
188+
N_("Value is too large (%"
189+
PRIuGRUB_UINT64_T " > %"
190+
PRIdGRUB_INT32_T ")"),
191+
converted_avail_mem,
192+
GRUB_INT_MAX);
193+
}
194+
if (state[FLAG_QUIET].set == 0)
195+
{
196+
grub_printf (N_("The amount of available RAM is %"
197+
PRIuGRUB_UINT64_T " %s.\n"),
198+
converted_avail_mem, unit_strs[unit]);
199+
}
200+
converted_avail_mem_str =
201+
grub_xasprintf ("%" PRIuGRUB_UINT64_T,
202+
converted_avail_mem);
203+
if (!converted_avail_mem_str)
204+
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
205+
N_("Can't allocate space for value string"));
206+
207+
grub_env_set (buf, converted_avail_mem_str);
208+
grub_env_export (buf);
209+
210+
return GRUB_ERR_NONE;
211+
}
212+
213+
GRUB_MOD_INIT(memsize)
214+
{
215+
cmd =
216+
grub_register_extcmd ("memsize",
217+
grub_cmd_memsize,
218+
GRUB_COMMAND_FLAG_EXTCMD,
219+
N_("[-b|-K|-M|-G] [-q] [--set VARNAME]"),
220+
N_("Get the amount of system RAM available to GRUB, "
221+
"and export the integer value to the environment. "
222+
"The vaule will be printed out by default. "
223+
"-q disables the output. "
224+
"The default variable name is `memsize'. "
225+
"The unit can be b for bytes,"
226+
"or K, M, G for binary units. The default unit is MiB."),
227+
grub_options_memsize);
228+
}
229+
230+
GRUB_MOD_FINI(memsize)
231+
{
232+
grub_unregister_extcmd (cmd);
233+
}

0 commit comments

Comments
 (0)