作者:Maxwell Li
日期:2017/12/20
未经作者允许,禁止转载本文任何内容。如需转载请留言。
[TOC]
16.1 Shell 的编译与执行
ShellPkg 目录下包含了 Shell 的源代码,可以通过以下命令编译获得 shell.efi。
build -a IA32 -p ShellPkg\ShellPkg.dsc
build -a X64 -p ShellPkg\ShellPkg.dsc
将32位或64位的 shell.efi 复制到 ESP 分区的 efi\boot 目录下并重命名为 BootIA32.efi 或 BootX64.efi,启动 UEFI 系统时就会执行 BootIA32.efi 或 BootX64.efi 从而进入 Shell。
该方式启动 Shell 时通常不带参数(使用系统参数)。
- UEFI 进入 Shell 时,会将 Shell Protocol 安装到 Shell 的 ImageHandle 上。然后 UEFI 系统中的应用才可以使用 Shell Protocol 服务。
- 进入 UEFI 后, Shell 首先检查 efi\Boot\ 目录下是否有 startup.nsh 脚本。
- 如果有,则执行该脚本,然后进入 Shell 命令行等待用户输入;
- 如果没有,则直接进入命令行等待用户输入。
- 执行外部命令时:
- Shell 先用 Load Image Protocol 将可执行文件载入内存生成 Image;
- 在该 Image 上安装 Shell Parameter Protocol;
- 调用这个 Image 的入口函数从而执行该外部命令。
Shell 启动参数:
shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
Shell 启动参数表
启动参数 | 作用 |
---|---|
-nostartup | 进入 Shell 时不执行脚本 startup.nsh |
-noconsoleout | Shell 标准输出不显示 |
-noconsolein | Shell 无标准输入 |
-delay[:n] | 指定等待 startup.nsh 启动的时间。默认是5秒,-delay:0 表示立即执行 |
-nointerrupt | 不支持 <Ctrl+C> 的终止程序功能 |
-nomap | 启动后不显示块设备的 map 信息 |
-noversion | 启动后不显示 version |
-startup | 进入 Shell 是执行脚本 startup.nsh |
当 ShellOpt-optitions 和 options 中均不包含 -startup 时,可以使用 file-name [file-name-options] 指定 Shell 启动后要执行的文件。如果启动参数中既有 -startup 又有 file-name [file-name-options],则 file-name [file-name-options] 被忽略。
16.2 Shell 服务
Shell 为程序开发者提供了两种使用 Shell 服务的方式:使用 Shell Protocol 和 Shell Parameters Protocol;使用 UEFIShellLib 提供的函数。
Shell Protocol 提供服务
EFI_SHELL_PROTOCOL 结构体如下:
typedef struct _EFI_SHELL_PROTOCOL {
EFI_SHELL_EXECUTE Execute; // 执行 Shell 命令行命令
EFI_SHELL_GET_ENV GetEnv; // 获得环境变量的值
EFI_SHELL_SET_ENV SetEnv; // 设置环境变量的值
EFI_SHELL_GET_ALIAS GetAlias; // 获得 Shell 命令的别名
EFI_SHELL_SET_ALIAS SetAlias; // 设置 Shell 命令的别名
EFI_SHELL_GET_HELP_TEXT GetHelpText; // 获得 Shell 命令的帮助信息
EFI_SHELL_GET_DEVICE_PATH_FROM_MAP GetDevicePathFromMap; // 从 Map 名获得 DevicePath
EFI_SHELL_GET_MAP_FROM_DEVICE_PATH GetMapFromDevicePath; // 从 DevicePath 获得 Map 名
EFI_SHELL_GET_DEVICE_PATH_FROM_FILE_PATH GetDevicePathFromFilePath;
EFI_SHELL_GET_FILE_PATH_FROM_DEVICE_PATH GetFilePathFromDevicePath;
EFI_SHELL_SET_MAP SetMap; // 设置设备的 Map 名
EFI_SHELL_GET_CUR_DIR GetCurDir;
EFI_SHELL_SET_CUR_DIR SetCurDir;
EFI_SHELL_OPEN_FILE_LIST OpenFileList;
EFI_SHELL_FREE_FILE_LIST FreeFileList;
EFI_SHELL_REMOVE_DUP_IN_FILE_LIST RemoveDupInFileList;
EFI_SHELL_BATCH_IS_ACTIVE BatchIsActive; // 是否正在执行脚本
EFI_SHELL_IS_ROOT_SHELL IsRootShell; // 是否为 Root
EFI_SHELL_ENABLE_PAGE_BREAK EnablePageBreak; // 启动分屏显示模式
EFI_SHELL_DISABLE_PAGE_BREAK DisablePageBreak; // 禁止分屏显示模式
EFI_SHELL_GET_PAGE_BREAK GetPageBreak; // 获取当前分屏显示模式状态
EFI_SHELL_GET_DEVICE_NAME GetDeviceName; // 获取设备的名字
EFI_SHELL_GET_FILE_INFO GetFileInfo;
EFI_SHELL_SET_FILE_INFO SetFileInfo;
EFI_SHELL_OPEN_FILE_BY_NAME OpenFileByName;
EFI_SHELL_CLOSE_FILE CloseFile;
EFI_SHELL_CREATE_FILE CreateFile;
EFI_SHELL_READ_FILE ReadFile;
EFI_SHELL_WRITE_FILE WriteFile;
EFI_SHELL_DELETE_FILE DeleteFile;
EFI_SHELL_DELETE_FILE_BY_NAME DeleteFileByName;
EFI_SHELL_GET_FILE_POSITION GetFilePosition;
EFI_SHELL_SET_FILE_POSITION SetFilePosition;
EFI_SHELL_FLUSH_FILE FlushFile;
EFI_SHELL_FIND_FILES FindFiles;
EFI_SHELL_FIND_FILES_IN_DIR FindFilesInDir;
EFI_SHELL_GET_FILE_SIZE GetFileSize;
EFI_SHELL_OPEN_ROOT OpenRoot; // 打开设备的根目录
EFI_SHELL_OPEN_ROOT_BY_HANDLE OpenRootByHandle; // 打开 Handle 指定的设备根目录
EFI_EVENT ExecutionBreak; // 用户按下“Ctrl+C”时触发该事件
UINT32 MajorVersion;
UINT32 MinorVersion;
// Added for Shell 2.1
EFI_SHELL_REGISTER_GUID_NAME RegisterGuidName;
EFI_SHELL_GET_GUID_NAME GetGuidName;
EFI_SHELL_GET_GUID_FROM_NAME GetGuidFromName;
EFI_SHELL_GET_ENV_EX GetEnvEx;
} EFI_SHELL_PROTOCOL;
EFI_SHELL_PROTOCOL 的 Execute 服务函数原型如下:
typedef EFI_STATUS (EFIAPI *EFI_SHELL_EXECUTE) (
IN EFI_HANDLE *ParentImageHandle, // 执行 Execute 函数的 ImageHandle
IN CHAR16 *CommandLine OPTIONAL, // Shell 命令行命令
IN CHAR16 **Environment OPTIONAL, // 环境变量
OUT EFI_STATUS *StatusCode OPTIONAL // Shell 命令返回值
);
该函数会启动一个子 Shell,并在子 Shell 中执行指定命令,命令的退出码将作为 Execute 函数的返回值。
- 参数 ParentImageHandle 是执行 Execute 命令的 Image 的 ImageHandle,通常是 gImageHandle;
- 参数 CommandLine 是启动 Shell 时的命令行参数;
- 参数 Environment 是字符串数组,数组以 NULL 为最后一项,其余每一项表示一个环境变量,格式为 Var=Value;
- 如果 Environment 为 NULL,当前的环境变量会传递到子 Shell 中,从子 Shell 返回时,对环境变量的更改被保存。
- 如果 Environment 不为 NULL,子 Shell 使用指定的环境变量,从子 Shell 返回时,对环境变量的更改被丢弃。
- 参数 StatusCode 是 Shell 命令的返回值,该返回值通过 exit 命令设置。
基本上对于所有 Shell Protocol 中的函数,在 UefiShellLib 中都有与之对应的函数。例如 Shell Protocol 中的 Execute 函数, UefiShellLib 提供了对应的 ShellExecute 函数,函数原型如下:
EFI_STATUS EFIAPI ShellExecute (
IN EFI_HANDLE *ParentHandle,
IN CHAR16 *CommandLine OPTIONAL,
IN BOOLEAN Output OPTIONAL,
IN CHAR16 **EnvironmentVariables OPTIONAL,
OUT EFI_STATUS *Status OPTIONAL
)
Output 为 TURE,表示现实调试信息。
UefiShellLib 的初始化
gEfiShellProtocol 是 EFI_SHELL_PROTOCOL* 类型的变量,由 UefiShellLib 提供。Library 可以提供构造函数,构造函数在 ENTRY_POINT 之前执行。在 UefiSHellLib 的工程文件中定义了 CONSTRUCTOR 和 DESTRUCTOR。引用 UeifShellLib 时,“Status=ShellLibConstructor(ImageHandle, SystemTable);”会被添加到 ProcessLibraryConstructorList 函数中,从而在调用 ShellAppMain 之前执行。
16.3 Shell 脚本
在 UEFI Shell 中可以执行 Shell 脚本,Shell 脚本是以 .nsh 为扩展名的文件,在脚本中可以执行 Shell 命令和外部命令,也可以使用内置的流程控制命令 for、endfor、goto、if、else、endif、exit。
if 语句比较运算符
运算符 | 含义 |
---|---|
gt | 大于 |
lt | 小于 |
ne | 等于 |
ugt | 无符号值大于 |
ult | 无符号值小于 |
eq | 相等 |
ge | 大于或等于 |
le | 小于或等于 |
= = | 相等 |
uge | 无符号值大于或等于 |
ule | 无符号值小于或等于 |
if语句布尔函数说明
布尔函数 | 当返回值为 TRUE 时 | 当返回值为 FALSE 时 |
---|---|---|
isInt(para) | para 是数字 | para 不是数字 |
Exists file | file 存在 | file 不存在 |
Avaliable file | file 存在于 path 指定的目录或当前目录 | 均不存在 |
Profile para | para 匹配 profile 中某项 | para 不匹配 profile 中任一项 |
16.4 Shell 内置命令
16.4.1 调试设备的相关命令
deme 命令用于查看内存或设备内存。address、size 都是十六进制数,-MMIO 指定地址为设备内存。
dmem [-b] [address] [size] [-MMIO]
mm 命令用于查看或修改 MEM(系统内存)、MMIO(设备内存)、IO(寄存器)、PCI(PCI 配置空间)、PCIE(PCIE 配置空间)。
mm address [value] [-w 1|2|4|8] [-MEM| -MMIO | -IO | -PCI | -PCIE] [-n]
address 为地址;value 为要写入的值;-w 后跟访问宽度;-n 表示非交互模式,若不指定 -n,则该命令会进入交互模式。
pci 命令显示 PCI 设备列表或显示 PCI 配置空间。
pci [Bus Dev [Func] [-s Seg] [-i]]
不带参数的 pci 命令用于列出所有 PCI 设备。带参数时,总线号(Bus)\设备号(Dev)\功能号(Func)用于指定 PCI 设备。-s Seg 用于指定 Segment。Func 和 Seg 默认值为0。
16.4.2 驱动相关命令
dh 命令用于列出系统中的所有设备信息,或某个设备的相关信息。
dh [-l<lang>] [handle | -p <prot_id>] [-d] [-v]
- -l<lang> 表使用指定的语言显示。
- handle 是指 UEFI Handle 在系统中的编号。
- -p <prot_id> 列出所有安装了 protocol prot_id 的设备信息。
- -d 用于列出驱动相关信息。
- -v 用于输出 verbose 信息。
device 命令用于显示所有被驱动管理的设备。
drivers 命令用于列出系统中的 driver。
Connect 命令用于加载驱动到设备上并启动加载的驱动。
Connect [[DeviceHandle] [DriverHandle] | [-c] | [-r]]
- -c 用于连接控制台设备。
- -r 用于递归扫描所有 handle,发现匹配的设备和驱动就加进来,没有 -r,新产生的设备将不会被连接。
load 命令用于加载驱动。
load [-nc] file [file2 ...]
-nc 表示只加载驱动到内存,不进行 connect。不带 -nc 选项时,load 加载驱动后会调用 connect 将该驱动加载到匹配的设备上。
unload 命令用于将驱动从内存中清楚。
unload [-n] [-v] Handle
-n 表示在执行 unload 过程中跳过所有提示信息,不需要用户确认。
16.4.3 网络相关命令
ifconfig 命令用于配置网络设备。
ifconfig [-?] [-c [Name]] [-l [Name]] [-s <Name> dhcp | <static <IP><MASK><Geteway>> [permanent]]
- Name是网络适配器的名字。
- -c 用于清除网络适配器的配置。
- -l 用于列出网络适配器的配置。
- -s 用于设备网络适配器的IP地址。
ping命令用于 ping 目标机器。
ping [ -n number] [-l size] TargetIP
- -l size 用于发送 size 字节的数据。
- -n number 表示发送数据的次数。