I am looking at using Bootservices function LoadImage to load a UEFI application image from memory. Function parameters are:
typedef
EFI_STATUS
LoadImage (
IN BOOLEAN BootPolicy,
IN EFI_HANDLE ParentImageHandle,
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
IN VOID *SourceBuffer OPTIONAL,
IN UINTN SourceSize,
OUT EFI_HANDLE *ImageHandle
);
Where I am allowed to provide a sourcebuffer that is already populated with the PE/COFF image to load. I am currently using sourcebuffer, and have prepopulated the buffer with a valid PE/COFF files contents. I pass that in under SourceBuffer and set DevicePath to Null. I receive the error "EFI_LOAD_ERROR Image was not loaded because the image format was corrupt or not understood."
What am I doing wrong?
You need to provide the information about the memory buffer as a device path - in the DevicePath argument.
Have a look through chapter 9 Device Path Protocol in the UEFI 2.5 specification (from the UEFI forum). Especially 9.3.2.3 Memory Mapped Device Path.
You should load the entire file just as is is - no picking apart of sections required.
For an example, you can have a look at the GRUB arm64 Linux loader, which does just this.
Related
I am writing a model for a serial flash device. I wonder how do I best model the internal memory? I would like the memory size to be configurable by the user since I intend to reuse this model for different serial flashes. It also needs to retain its content upon reset while the rest of the logic is reset.
I tried using a statically allocated character buffer but that is not configurable and seems very inappropriate for such large sizes as my flash model. My flash model is 512 MB.
saved char flash_image[512 * 1024 * 1024];
Assuming this will go into Simics, it is best modeled with two subobjects, both of which are available in Simics Base.
The first is an image which stores the actual data, the second is a ram which is used to read/write into the image.
Both the image and the ram are connects using the init_as_subobj template which makes Simics automatically create them as sub-objects of the device.
They are placed in a group where we can add an init method, which sets default values for the required attributes of the subobjects; in this case the "size" of the image and the "image" of the ram.
The ram object also uses the map_target template, which gives us useful methods for reading and writing into the ram.
dml 1.4;
device sample_dev;
import "utility.dml";
import "simics/simulator-api.dml";
group flash is init {
param size = 0x1000;
method init() {
SIM_set_attribute_default(
ram.obj, "image", SIM_make_attr_object(image.obj));
SIM_set_attribute_default(
image.obj, "size", SIM_make_attr_uint64(size));
}
connect image is init_as_subobj {
param classname = "image";
}
connect ram is (init_as_subobj, map_target) {
param classname = "ram";
}
}
method write_test() {
try
flash.ram.write(0, 1, 42);
catch
log error: "write failed, it shouldn't!";
}
The size of the image is given a default value by a parameter in this example, but it can also be set or overridden by the user when creating the object, by assigning to the attribute flash.image.size, e.g. like this:
SIM_create_object("sample_dev", "dev", **{"flash.image.size": 0x2000})
I'm using an .nsh to modify my pci from efi, and then I launch Windows from bootmgfw.efi.
But doing so the shell is visible for some seconds while commands are exacuting, and I don't think there is a way to execute this nsh silently.
But I don't know how to achieve this in an efi application:
"mm 000100003E 1" "fs0:/EFI/Microsoft/Boot/bootmgfw.efi"
So I'm here to ask you if there is a way to create an .efi to change my pci the exact same way, and launching the Windows bootloader after those changes, but silently.
The best would be to read pci addresses and the path of the bootloader from a .conf file. It would be helpful for lot of people in my case.
Short questions:
Commands to modify pci in an efi app?
Command to chainload to another .efi file?
EXTRA: Command to read a .conf file from filesystem to get text lines and use them inside the application as variables?
Thank you for your help
mm 000100003E 1 command is to write 1 at memory address 0x000100003E, in edk2 ShellPkg, it is implemented with a simple CopyMem function call, you can implement your own like this:
UINT8 value = 1;
CopyMem((VOID *)0x000100003E, &value, 1);
Calling another efi application in one efi application is possible, but its very complex, you need to do the following works:
Get device handle of fs0
Create device path for /EFI/Microsoft/Boot/bootmgfw.efi
Load and start the efi application
As the efi application you are going to write is also located in fs0, probably fs0:\EFI\BOOT\BOOTX64.EFI, you can get fs0's device handle from it.
// Get device handle of fs0 from current application
EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
gBS->OpenProtocol(ImageHandle, &LoadedImageProtocol, &loaded_image, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
// Create device path
EFI_DEVICE_PATH_PROTOCOL *path = FileDevicePath(loaded_image->DeviceHandle, L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
// Load and start the image
EFI_HANDLE image;
gBS->LoadImage(FALSE, ImageHandle, path, NULL, 0, &image);
gBS->StartImage(image, NULL, NULL);
To read a configuration file, you need to use EFI File Protocol to open your config file and parse it on your own.
EFI_FILE_PROTOCOL *rootdir, *conffile;
UINT8 buf[1024];
UINTN bufsize = sizeof(buf);
// Open config file
rootdir = LibOpenRoot(loaded_image->DeviceHandle);
rootdir->Open(rootdir, &conffile, L"\\path\\to\\config", EFI_FILE_MODE_READ, 0);
// Read config file
conffile->Read(conffile, &bufsize, buf);
// Parse the file content in buf
// ...
// Close conffile and rootdir
conffile->Close(conffile);
rootdir->Close(rootdir);
edk2 or gnu-efi is required for the above code to work.
References:
edk2 ShellPkg mm command source code
I'm currently integrating libFuzzer in a project which parses files on the hard drive. I have some prior experience with AFL, where a command line like this one was used:
afl-fuzz -m500 -i input/ -o output/ -t100 -- program_to_fuzz ##
...where ## was a path to the generated input.
Looking at libFuzzer however, I see that the fuzz targets look like this:
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
DoSomethingInterestingWithMyAPI(Data, Size);
return 0; // Non-zero return values are reserved for future use.
}
I understand that the input isn't provided in the form of a file, but as a buffer in-memory instead. The problem is that the program I'm trying to fuzz works with files and obtains its data through fread() calls. At no point in time is the whole input supposed to be loaded in memory (where, in the general case, it might not even fit); so there's not much I can do with a const uint8_t*.
Writing the buffer back to the hard drive to get back a file seems extremely inefficient. Is there a way around this?
You can do as in this example from google security team.
The buf_to_file defined here takes your buffer and returns a char* pathname you can then pass to you target:
(from https://github.com/google/security-research-pocs/blob/master/autofuzz/fuzz_utils.h#L27 )
// Write the data provided in buf to a new temporary file. This function is
// meant to be called by LLVMFuzzerTestOneInput() for fuzz targets that only
// take file names (and not data) as input.
//
// Return the path of the newly created file or NULL on error. The caller should
// eventually free the returned buffer (see delete_file).
extern "C" char *buf_to_file(const uint8_t *buf, size_t size);
Be sure to free the ressource with the delete_file function.
You could use LD_PRELOAD and override fread.
In mmap() manpage:
Its prototype is:
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
and description:
The mmap() function asks to map 'length' bytes starting at offset 'offset'
from the file (or other object) specified by the file descriptor fd into
memory, preferably at address 'start'.
Sepcifically, for the last argument:
'offset' should be a multiple of the page size as returned by getpagesize(2).
From what I practised, offset MUST be a multiple of the page size, for example, 4096 on my Linux, otherwise, mmap() would return Invalid argument, offset is for file offset, why it must be multiple of virtual memory system's page size?
Thanks,
The simple answer: to make it fast. The more complex answer: whenever you access the memory at a location within the mapped memory, the OS must make sure that this location is filled with the contents of the file. But the OS can only detect whether you access a memory page - not a single location. What it does is, it creates a simple relation between offsets in the file and memory pages - and whenever you access a memory page, that part of the file is loaded. To make these calculations fast, it restricts you to start at certain offsets.
I am using embedded Linux for the NIOS II processor and device tree. The GPIO functionality provides the ability to read and or write a single bit at a time. I have some firmware and PIOS that I want to read or write atomically by setting or reading all 32 bits at one time. It seems like there would be a generic device driver that if the device tree was given the proper compatibility a driver would exist that would allow opening the device and then reading and writing the device. I have searched for this functionality and do not find a driver. One existing in a branch but was removed by Linus.
My question is what is the Linux device tree way to read and write a device that is a general purpose 32 bit register/pio?
Your answer is SCULL
Character Device Drivers
You will have to write a character device driver with file operations to open and close a device. Read, write, ioctl, and copy the contents of device.
static struct file_operations query_fops =
{
.owner = THIS_MODULE,
.open = my_open,
.release = my_close,
.ioctl = my_ioctl
};
Map the address using iomem and directly read and write to that address using rawread and rawwrite. Create and register a device as follows and then it can be accessed from userspace:
register_chrdev (0, DEVICE_NAME, & query_fops);
device_create (dev_class, NULL, MKDEV (dev_major, 0), NULL, DEVICE_NAME);
and then access it from userspace as follows:
fd = open("/dev/mydevice", O_RDWR);
and then you can play with GPIO from userspace using ioctl's:
ioctl(fd, SET_STATE);