├── .gitignore ├── Doxyfile ├── Doxygen_groups.dox ├── INSTALL.manual.md ├── INSTALL.vagrant.md ├── LICENSE.md ├── Makefile.edk ├── README.md ├── UEFIStarter.dec ├── UEFIStarter.dsc ├── apps ├── ac97.c ├── ac97.inf ├── cpuid.c ├── cpuid.inf ├── gop.c ├── gop.inf ├── helloworld.c ├── helloworld.inf ├── input.c ├── input.inf ├── lspci.c ├── lspci.inf ├── quit.c ├── quit.inf ├── rotation.c ├── rotation.inf ├── snow.c ├── snow.inf ├── timer.c └── timer.inf ├── include └── UEFIStarter │ ├── ac97.h │ ├── core.h │ ├── core │ ├── cmdline.h │ ├── console.h │ ├── files.h │ ├── logger.h │ ├── memory.h │ ├── string.h │ └── timestamp.h │ ├── graphics.h │ ├── pci.h │ └── tests │ ├── asserts.h │ ├── graphics.h │ ├── output.h │ ├── tests.h │ └── types.h ├── library ├── ac97.c ├── ac97.inf ├── core.inf ├── core │ ├── cmdline.c │ ├── cmdline.inf │ ├── console.c │ ├── console.inf │ ├── files.c │ ├── files.inf │ ├── logger.c │ ├── logger.inf │ ├── memory.c │ ├── memory.inf │ ├── string.c │ ├── string.inf │ ├── timestamp.c │ └── timestamp.inf ├── graphics.c ├── graphics.inf ├── pci.c ├── pci.inf └── tests │ ├── asserts.c │ ├── graphics.c │ ├── output.c │ ├── tests.c │ └── tests.inf ├── static ├── demoimg.ppm ├── font815.pgm ├── pci.ids └── startup.nsh ├── tests └── suites │ ├── lib │ ├── ac97.c │ ├── cmdline.c │ ├── files.c │ ├── graphics.c │ ├── logger.c │ ├── memory.c │ ├── pci.c │ ├── string.c │ └── testlib.inf │ └── selftest │ ├── asserts.c │ ├── graphics.c │ ├── runner.c │ └── testself.inf ├── tools ├── create_package.sh ├── generate_runall_tests_script.sh └── generate_test_runner.sh └── vagrant ├── Vagrantfile └── config ├── authorized_keys ├── bashrc.additional ├── gitconfig ├── pablo_rnfix.vim ├── screenrc.additional ├── vagrant-config.yml └── vimrc.additional /.gitignore: -------------------------------------------------------------------------------- 1 | /tmp 2 | /target 3 | .*.swp 4 | /tests/suites/*/generated 5 | -------------------------------------------------------------------------------- /Doxygen_groups.dox: -------------------------------------------------------------------------------- 1 | /** 2 | * \defgroup group_apps Applications 3 | * \brief Executable .efi applications 4 | */ 5 | 6 | /** 7 | * \defgroup group_lib Library Functions 8 | * \brief Commonly used functions 9 | * \{ 10 | */ 11 | 12 | /** \defgroup group_lib_cmdline Command-line Functions */ 13 | /** \defgroup group_lib_console Console Functions */ 14 | /** \defgroup group_lib_logger Logging Functions */ 15 | /** \defgroup group_lib_memory Memory Tracking Functions */ 16 | /** \defgroup group_lib_string String Functions */ 17 | /** \defgroup group_lib_files File I/O Functions */ 18 | /** \defgroup group_lib_timestamp Timer Functions */ 19 | /** \defgroup group_lib_pci PCI Functions */ 20 | /** \defgroup group_lib_graphics Graphics Functions */ 21 | /** \defgroup group_lib_ac97 AC'97 Audio Functions */ 22 | 23 | /** \} */ 24 | 25 | /** 26 | * \defgroup group_tests Testing Framework 27 | * \brief Generates executable .efi test suites 28 | * \{ 29 | */ 30 | 31 | /** 32 | * \defgroup group_tests_runner Test Runner 33 | * \brief The basis for all tests 34 | */ 35 | 36 | /** 37 | * \defgroup group_tests_asserts Assertions 38 | * \brief Assertions to use in tests 39 | */ 40 | 41 | /** 42 | * \defgroup group_tests_selftest Self Tests 43 | * \brief A test suite to make sure the testing framework works as expected 44 | */ 45 | 46 | /** \} */ 47 | -------------------------------------------------------------------------------- /INSTALL.manual.md: -------------------------------------------------------------------------------- 1 | # Manual Installation 2 | 3 | ### Basic Structure 4 | 5 | Before you start you should have a working EDK2 setup, e.g. in /usr/src/edk2. All following relative paths are relative 6 | to this. 7 | 8 | In your sources root you'll need a UEFIStarter/ directory - either clone the repository directly into this directory or 9 | create a symlink pointing to the UEFIStarter directory. 10 | 11 | The framework comes with a Makefile that needs to be linked into the sources root: 12 | 13 | ln -s UEFIStarter/Makefile.edk Makefile 14 | 15 | After this you should have 2 new entries in your sources root (I have the sources on another volume so "UEFIStarter" is 16 | a symlink in my setup): 17 | 18 | /usr/src/edk2$ ls -lnd Makefile UEFIStarter 19 | lrwxrwxrwx 1 1000 1000 24 Dec 28 20:39 Makefile -> UEFIStarter/Makefile.edk 20 | lrwxrwxrwx 1 1000 1000 26 Dec 28 20:39 UEFIStarter -> /mnt/ueficode/UEFIStarter 21 | 22 | 23 | ### Configuration 24 | 25 | You'll need to edit Conf/target.txt and set these values: 26 | 27 | ACTIVE_PLATFORM = UEFIStarter/UEFIStarter.dsc 28 | TARGET_ARCH = X64 29 | TOOL_CHAIN_TAG = GCC5 30 | MAX_CONCURRENT_THREAD_NUMBER = 7 31 | 32 | Set the maximum thread number to the number of available CPU cores +1. I'm working in a VM with 6 cores, so I'm using 7 33 | threads. If you can spare the cores the build process will make good use of these. 34 | 35 | The Makefile needs a few changes: 36 | 37 | * set OVMF\_IMAGE to your copy of the OVMF image 38 | * set LOOP\_DEVICE to an available loop device 39 | 40 | The system user needs sudo privileges for these commands: 41 | 42 | * losetup 43 | * mkdosfs 44 | * mount 45 | * umount 46 | 47 | `UEFIStarter/Makefile.edk` contains a list of all commands executed as root: you can grant sudo access to individual 48 | command lines, allow general sudo access or do anything in between. 49 | 50 | ### Building 51 | 52 | Once you set everything up you should be able to just invoke `make` (output shortened): 53 | 54 | $ make 55 | [...] 56 | make[1]: Leaving directory '/usr/src/edk2/Build/UEFIStarter/DEBUG_GCC5/X64/UEFIStarter/tests/suites/lib/testlib' 57 | 58 | - Done - 59 | Build end time: 10:05:16, Dec.30 2017 60 | Build total time: 00:00:14 61 | 62 | test -f UEFIStarter/target/uefi.img || dd if=/dev/zero of=UEFIStarter/target/uefi.img bs=512 count=93750 63 | [...] 64 | mkisofs -quiet -input-charset utf8 -o UEFIStarter/target/uefi.iso /mnt/uefi/ 65 | sudo umount /mnt/uefi/ 66 | sudo losetup -d /dev/loop0 67 | 68 | When this finishes successfully the UEFIStarter/target directory contains an .img file with a filesystem for QEMU, and 69 | an .iso image with the same contents for e.g. a VirtualBox virtual CD/DVD drive. 70 | 71 | If you encounter build issues while the loopback device is mounted the mount point or the loopback device may be left 72 | active and following `make` attempts will fail with e.g. "Device is busy". To fix this there's a Makefile target: 73 | 74 | $ make free 75 | 76 | This will unmount and free the loopback device. After that `make` should work again. 77 | -------------------------------------------------------------------------------- /INSTALL.vagrant.md: -------------------------------------------------------------------------------- 1 | # Installation with Vagrant 2 | 3 | ### General 4 | 5 | The included Vagrantfile (in the `vagrant/` directory) automates the setup process by creating a new virtual machine for 6 | you. This VM contains all the tools required to build UEFI applications and run the console-based ones. 7 | 8 | The virtual machine will be based on Ubuntu 17.10 (Artful Aardvark). 9 | 10 | By using the supplied Vagrantfile you agree to the licenses of all automatically installed pieces of software, 11 | including, but not limited to: 12 | 13 | * Ubuntu and its components, see [https://www.ubuntu.com/about/about-ubuntu/licensing](https://www.ubuntu.com/about/about-ubuntu/licensing) 14 | * TianoCore and its components, see [https://github.com/tianocore/tianocore.github.io/wiki/Legalese](https://github.com/tianocore/tianocore.github.io/wiki/Legalese) 15 | * UEFIStarter of course, see "License" near the end of this document. 16 | 17 | ### Configuration 18 | 19 | In the `vagrant/config/` directory there are 3 files you'll probably want to edit: 20 | 21 | * `authorized_keys`: this file's contents will be added to `~vagrant/.ssh/authorized_keys`, allowing you to include as 22 | many ssh keys as you want to access the VM. You can leave this file empty if you want, in which case you can still 23 | access the VM with `vagrant ssh`. 24 | * `gitconfig`: this will be user "vagrant"'s global git configuration. If you're planning on pushing commits somewhere 25 | you can add your user information here. 26 | * `vagrant-config.yml`: this contains configuration settings for the virtual machine. You can e.g. set your time zone 27 | and configure a shared directory with your host system. 28 | 29 | ### Creating the Virtual Machine 30 | 31 | Once you have working VirtualBox and Vagrant installations and have edited the VM's configuration files to suit your 32 | needs you can tell Vagrant to build the VM: 33 | 34 | $ vagrant up 35 | 36 | If all goes well this will create the virtual machine, install a basic system, download and build parts of TianoCore 37 | edk2, download UEFIStarter if not mounted already and then build it. 38 | 39 | This will take a few minutes. Once it's done you should get output similar to this: 40 | 41 | ==> dev: 'UEFIStarter' development VM. Use 'vagrant ssh' or your installed ssh key 42 | (localhost:2222) to connect, then go to /usr/src/edk2 and execute 'make run' 43 | 44 | ### Using the Virtual Machine 45 | 46 | You can always use `vagrant ssh` to access the VM. If you added keys to `config/authorized_keys` you can use those to 47 | connect as well. By default Vagrant will make the VM listen for ssh connections on localhost, port 2222. 48 | 49 | The sources root for TianoCore edk2 is `/usr/src/edk2`. The UEFIStarter image should already be built and ready to be 50 | started: 51 | 52 | $ cd /usr/src/edk2 53 | make run 54 | -------------------------------------------------------------------------------- /Makefile.edk: -------------------------------------------------------------------------------- 1 | ###################### 2 | # Basic Configuration 3 | #################### 4 | 5 | # Running UEFI applications with qemu requires an OVMF image. You can either download it or build it with edk2 6 | # (see vagrant/Vagrantfile on how to do that). 7 | # Either way, OVMF_IMAGE needs to point to the image for the "run" target to work. 8 | OVMF_IMAGE = /usr/share/qemu/OVMF.fd 9 | 10 | # The build process generates a FAT32 file system that's accessed via loopback device. 11 | # Set LOOP_DEVICE to an available device. 12 | LOOP_DEVICE = /dev/loop0 13 | 14 | 15 | ######################### 16 | # Detailed Configuration 17 | ####################### 18 | 19 | PROJECT_NAME = UEFIStarter 20 | PROJECT_DIR = UEFIStarter 21 | VERSIONFILE = .version 22 | IMAGE_FILENAME = UEFIStarter.img 23 | ISO_FILENAME = UEFIStarter.iso 24 | MOUNT_POINT := $(shell readlink -f $(PROJECT_DIR))/tmp 25 | BUILD_DIR = $(PROJECT_DIR)/target 26 | SHELL_BINARY = ShellBinPkg/UefiShell/X64/Shell.efi 27 | 28 | CURRENT_USER := $(shell whoami) 29 | SRCFILES := $(shell find $(PROJECT_DIR)/ -name '*.c' -or -name '*.h') 30 | STATICS := $(shell find $(PROJECT_DIR)/static -type f ! -name '.*.swp') 31 | 32 | 33 | ########## 34 | # Targets 35 | ######## 36 | 37 | all: build $(BUILD_DIR)/$(IMAGE_FILENAME) 38 | 39 | build: 40 | mkdir -p $(BUILD_DIR) 41 | $(PROJECT_DIR)/tools/generate_test_runner.sh 42 | build 43 | 44 | free: 45 | mount | grep "$(MOUNT_POINT) " && sudo umount $(MOUNT_POINT) || true 46 | losetup | grep $(LOOP_DEVICE) && sudo losetup -d $(LOOP_DEVICE) || true 47 | 48 | clean: 49 | find $(PROJECT_DIR)/ -name '*.o' -or -name '*.so' -or -name '*.efi' | xargs -r rm 50 | rm -f $(BUILD_DIR)/$(IMAGE_FILENAME) $(BUILD_DIR)/$(ISO_FILENAME) 51 | 52 | $(BUILD_DIR)/$(IMAGE_FILENAME): free $(SRCFILES) $(STATICS) FORCE 53 | mkdir -p $(BUILD_DIR) $(MOUNT_POINT) 54 | test -f $(BUILD_DIR)/$(IMAGE_FILENAME) || dd if=/dev/zero of=$(BUILD_DIR)/$(IMAGE_FILENAME) bs=512 count=93750 55 | sgdisk -o -n 1:2048:93716 -t 1:ef00 $(BUILD_DIR)/$(IMAGE_FILENAME) >/dev/null 56 | sudo losetup --offset 1048576 --sizelimit 46934528 $(LOOP_DEVICE) $(BUILD_DIR)/$(IMAGE_FILENAME) 57 | sudo mkdosfs -F 32 $(LOOP_DEVICE) >/dev/null 58 | sudo mount -o uid=$(CURRENT_USER) $(LOOP_DEVICE) $(MOUNT_POINT) 59 | 60 | echo "$(PROJECT_NAME)" > $(MOUNT_POINT)/$(VERSIONFILE) 61 | date +"%Y-%m-%d %H:%M:%S UTC%:::z (%Z)" >> $(MOUNT_POINT)/$(VERSIONFILE) 62 | 63 | cp Build/$(PROJECT_NAME)/DEBUG_GCC5/X64/*.efi $(MOUNT_POINT)/ 64 | cp -R $(PROJECT_DIR)/static/* $(MOUNT_POINT)/ 65 | 66 | mkdir -p $(MOUNT_POINT)/tests 67 | mv $(MOUNT_POINT)/test*.efi $(MOUNT_POINT)/tests/ 68 | 69 | $(PROJECT_DIR)/tools/generate_runall_tests_script.sh $(PROJECT_DIR)/tests/suites >> $(MOUNT_POINT)/tests/run.nsh 70 | 71 | mkdir -p $(MOUNT_POINT)/EFI/Boot 72 | cp $(SHELL_BINARY) $(MOUNT_POINT)/EFI/Boot/bootx64.efi 73 | 74 | mkisofs -quiet -J -input-charset utf8 -o $(BUILD_DIR)/$(ISO_FILENAME) $(MOUNT_POINT)/ 75 | sudo umount $(MOUNT_POINT) 76 | sudo losetup -d $(LOOP_DEVICE) 77 | 78 | img: $(BUILD_DIR)/$(IMAGE_FILENAME) 79 | 80 | FORCE: 81 | 82 | run: $(BUILD_DIR)/$(IMAGE_FILENAME) 83 | qemu-system-x86_64 -cpu qemu64 -bios $(OVMF_IMAGE) -nographic -drive file=$(BUILD_DIR)/$(IMAGE_FILENAME),format=raw,if=ide -net none -soundhw ac97 -no-reboot 84 | 85 | check: 86 | @echo checking for TAB characters... 87 | @! echo $(SRCFILES) | xargs grep -P '\x09' 88 | @echo checking for duplicate GUIDs... 89 | @! grep -hE '_GUID.*=' `find $(PROJECT_DIR)/ -name '*.inf' -or -name '*.d[es]c'` | sed 's/.*= *//' | sort | uniq -c | grep -vE '^ *1 ' 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Synopsis 2 | 3 | This is a small C framework for UEFI development built on top of TianoCore EDK2. 4 | 5 | This repository includes a Vagrantfile for setting up a virtual machine to develop and run UEFI applications. If you're 6 | familiar with Vagrant and have it set up already you can run your first UEFI applications in just a few minutes! 7 | 8 | The sources are hosted on [GitHub](https://github.com/rinusser/UEFIStarter). 9 | 10 | 11 | # General 12 | 13 | ### What this IS NOT 14 | 15 | This project is not a comprehensive course in UEFI development. If you're just starting to write UEFI code you'll need 16 | to use additional material like the [official TianoCore documentation](https://github.com/tianocore/tianocore.github.io/wiki/EDK-II-User-Documentation), 17 | and the [UEFI Specification](http://www.uefi.org/specifications). 18 | 19 | ### What this IS 20 | 21 | The library and UEFI applications included in this code are meant to simplify a few repetitive tasks when developing 22 | UEFI code. For example there is a configurable command line argument parser that will validate input strings and convert 23 | them into the target datatype, e.g. integers. 24 | 25 | This project started out with another UEFI development kit (gnu-efi) but eventually outgrew the original SDK, so I 26 | migrated it to TianoCore EDK2017. As a result of this there are still a few library functions included that are already 27 | built-in into TianoCore. 28 | 29 | It is my hope that this code helps anyone looking into, or starting with, UEFI development: I did that myself a few 30 | months ago and found parts of the various documentations frustratingly lacking. If I can spare you some of the headache 31 | I had I'm happy. 32 | 33 | 34 | # Requirements 35 | 36 | You can either set up your own development environment ("Manual Installation"), or use the supplied Vagrantfile to 37 | create a development VM ("Vagrant"). 38 | 39 | ## Vagrant 40 | 41 | To use the included Vagrantfile you'll need: 42 | 43 | * a 64-bit x86 processor (physical or virtual, see Recommendations below) 44 | * a UEFI-capable mainboard (physical or virtual, see Recommendations below) 45 | * Vagrant (tested with 2.0) 46 | * VirtualBox (tested with 5.1) 47 | 48 | ## Manual Installation 49 | 50 | ### Bare Minimum 51 | 52 | To set up your own development environment you'll need at least: 53 | 54 | * a 64-bit x86 processor (physical or virtual, see Recommendations below) 55 | * a UEFI-capable mainboard (physical or virtual, see Recommendations below) 56 | * Linux (tested with Ubuntu 17.04) with sgdisk, mkisofs and mkdosfs installed (you'll need root access) 57 | * a working TianoCore EDK2017 development setup (earlier versions might be OK too) with GCC 58 | 59 | ### Optional 60 | 61 | * Doxygen (if you want to generate the documentation), plus whatever you may need for your desired output format 62 | 63 | ### Recommendations 64 | 65 | You can reboot into the UEFI applications if you want (and have the spare machine or don't mind rebooting all the time), 66 | but it's easier to use virtualization for development. There are multiple options: 67 | 68 | * QEMU with OVMF - I use mostly this, except for running graphical applications. It's very fast to work with. The OVMF 69 | image adds EFI support to QEMU, you'll need to download and install it separately. 70 | * VirtualBox - has built-in EFI support. I use this to run the graphical UEFI applications: it easily outperforms QEMU, 71 | at least on my machines. 72 | * Other: you're probably fine with any hypervisor that supports EFI. 73 | 74 | If you *do* want to actually boot a machine into the custom UEFI environment a spare USB stick you don't mind formatting 75 | comes in handy. 76 | 77 | 78 | # Installation 79 | 80 | The instructions on how to install the Vagrant virtual machine are in 81 | [INSTALL.vagrant.md](https://github.com/rinusser/UEFIStarter/blob/master/INSTALL.vagrant%2Emd). 82 | 83 | If you prefer to set up your own development environment see 84 | [INSTALL.manual.md](https://github.com/rinusser/UEFIStarter/blob/master/INSTALL.manual%2Emd). 85 | 86 | 87 | # Usage 88 | 89 | ### Running 90 | 91 | If you're using QEMU you can start it with: 92 | 93 | $ make run 94 | 95 | Alternatively you can mount the .iso image UEFIStarter/target/UEFIStarter.iso in e.g. VirtualBox and boot from it. 96 | 97 | Either way, the default startup.nsh script will run all test suites and return to the UEFI shell. 98 | 99 | The root directory contains all built applications. 100 | 101 | :warning: Contains AC'97 audio output that may be too loud, see apps/ac97.c for details. 102 | 103 | ### Booting from USB 104 | 105 | You'll need a USB storage device formatted with FAT32 for this. A USB stick will do, an external hard drive works just 106 | as well. You can boot from any UEFI-capable 64-bit x86 system, as long as "Secure Boot" is disabled. 107 | 108 | After successfully building UEFIStarter all you need to do is copy the ISO file's contents onto the USB device. If, for 109 | example, your USB device is mounted on /mnt/usb/, after copying there should be files/directories /mnt/usb/EFI, 110 | /mnt/usb/tests, /mnt/usb/.version and so on. On Windows it should be e.g. D:\\EFI, D:\\.version etc. 111 | 112 | After copying the .iso's contents onto the USB device you can boot from it. This usually involves pressing a 113 | vendor-specific key or key combination at boot time to get to a boot device selection, like F2, Shift-F8, F12 etc. 114 | 115 | ### Working on UEFIStarter 116 | 117 | There is a semi-automatically generated documentation hosted on [GitHub](https://rinusser.github.io/UEFIStarter/). 118 | The functions, data types etc. are described on the [Modules](https://rinusser.github.io/UEFIStarter/modules.html) page. 119 | 120 | The documentation is still a work in progress. 121 | 122 | Each shell you want to run the edk2 build process in needs to import `edksetup.sh` in the edk2 sources root: 123 | 124 | $ . edksetup.sh 125 | 126 | You only need to do this once per shell. The Makefile contains everything else you need to build the UEFIStarter 127 | package: 128 | 129 | $ make 130 | 131 | When this finishes successfully the UEFIStarter/target directory contains an .img file with a filesystem for QEMU, and 132 | an .iso image with the same contents for e.g. a VirtualBox virtual CD/DVD drive. 133 | 134 | ### Including UEFIStarter 135 | 136 | To make the UEFIStarter's package contents (e.g. library files) available to your own EDK2 project you'll need to 137 | register the libraries in your package's .dsc file and add the "UEFIStarter/UEFIStarter.dec" package dependency in e.g. 138 | your applications' .inf files. 139 | 140 | There's an installer script you can use to create a new EDK2 project based on UEFIStarter. Execute it in your edk2 141 | sources root: 142 | 143 | $ UEFIStarter/tools/create_package.sh 144 | 145 | The directory must not exist. If it's outside the edk2 sources root (e.g. /mnt/ueficode/MyProject) a symlink will be 146 | created in the sources root. The directory name (e.g. MyProject) will be used as the edk2 package name. 147 | 148 | The new package comes with a simple application you can run. There are no custom includes or tests, if you want to use 149 | the test framework you'll currently need to copy the tools/generate\_\*.sh scripts and change the paths in them. 150 | 151 | Instead of creating a new package you can also just experiment with your local UEFIStarter copy, e.g. in a new branch. 152 | 153 | There's already a separate project (a game, actually) using this framework but it hasn't been published yet. Once that's 154 | done its code will offer additional examples of how to use UEFIStarter in other edk2 packages. 155 | 156 | # Tests 157 | 158 | This project contains a test framework for writing tests. There are test suites for validating the framework functions 159 | and the test framework itself. The tests are executed in the UEFI shell - there is a script to run all test suites: 160 | 161 | FS0:\> cd tests 162 | FS0:\tests\> run 163 | 164 | Test suites are individual .efi executables that can be run individually, e.g.: 165 | 166 | FS0:\tests\> testlib -verbosity 1 167 | ........................... 168 | Result: SUCCESS 169 | 170 | Successful tests: 27 171 | Failed tests: 0 172 | Incomplete tests: 0 173 | Skipped groups: 0 174 | 175 | The `testself` suite contains a "runner" test group that fails on purpose, the `run.nsh` script skips this. 176 | 177 | The test suites support command line parameters, for example you can skip test groups or change the output verbosity. 178 | You can get a full list of parameters with "-help". 179 | 180 | 181 | # Legal 182 | 183 | ### Copyright 184 | 185 | Copyright (C) 2017-2018 Richard Nusser 186 | 187 | ### License 188 | 189 | This program is free software: you can redistribute it and/or modify 190 | it under the terms of the GNU General Public License as published by 191 | the Free Software Foundation, either version 3 of the License, or 192 | (at your option) any later version. 193 | 194 | This program is distributed in the hope that it will be useful, 195 | but WITHOUT ANY WARRANTY; without even the implied warranty of 196 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 197 | GNU General Public License for more details. 198 | 199 | You should have received a copy of the GNU General Public License 200 | along with this program. If not, see . 201 | 202 | ### Credits 203 | 204 | Contains an excerpt from [The PCI ID Repository](http://pci-ids.ucw.cz/), licensed under GPLv3. 205 | 206 | Contains an optional automated installer, a "Vagrantfile". By using this installer you additionally agree to the 207 | licenses of all installed components, see the respective websites for details: 208 | * Ubuntu: [https://www.ubuntu.com/about/about-ubuntu/licensing](https://www.ubuntu.com/about/about-ubuntu/licensing) 209 | * TianoCore: [https://github.com/tianocore/tianocore.github.io/wiki/Legalese](https://github.com/tianocore/tianocore.github.io/wiki/Legalese) 210 | -------------------------------------------------------------------------------- /UEFIStarter.dec: -------------------------------------------------------------------------------- 1 | [Defines] 2 | DEC_SPECIFICATION = 0x00010005 3 | PACKAGE_NAME = UEFIStarter 4 | PACKAGE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f00000 5 | PACKAGE_VERSION = 0.01 6 | 7 | 8 | [Includes] 9 | include 10 | -------------------------------------------------------------------------------- /UEFIStarter.dsc: -------------------------------------------------------------------------------- 1 | [Defines] 2 | PLATFORM_NAME = UEFIStarter 3 | PLATFORM_GUID = 871898a8-41d5-4fa5-a813-f6bea9f00001 4 | PLATFORM_VERSION = 0.01 5 | DSC_SPECIFICATION = 0x00010006 6 | OUTPUT_DIRECTORY = Build/UEFIStarter 7 | SUPPORTED_ARCHITECTURES = X64 8 | BUILD_TARGETS = DEBUG|RELEASE|NOOPT 9 | SKUID_IDENTIFIER = DEFAULT 10 | 11 | [PcdsFeatureFlag] 12 | 13 | [PcdsFixedAtBuild] 14 | 15 | [PcdsFixedAtBuild.IPF] 16 | 17 | [LibraryClasses] 18 | UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf 19 | ShellCEntryLib|ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.inf 20 | 21 | UefiLib|MdePkg/Library/UefiLib/UefiLib.inf 22 | BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf 23 | BaseLib|MdePkg/Library/BaseLib/BaseLib.inf 24 | PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf 25 | PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf 26 | UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf 27 | DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf 28 | MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf 29 | DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf 30 | UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf 31 | IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf 32 | PciLib|MdePkg/Library/BasePciLibCf8/BasePciLibCf8.inf 33 | PciCf8Lib|MdePkg/Library/BasePciLibCf8/BasePciLibCf8.inf 34 | 35 | DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf 36 | 37 | #StdLib requirements 38 | UefiRuntimeLib|MdePkg/Library/UefiRuntimeLib/UefiRuntimeLib.inf 39 | UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf 40 | HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf 41 | UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf 42 | StdLib|StdLib/LibC/StdLib/StdLib.inf 43 | 44 | UEFIStarterCore|UEFIStarter/library/core.inf 45 | logger|UEFIStarter/library/core/logger.inf 46 | cmdline|UEFIStarter/library/core/cmdline.inf 47 | string|UEFIStarter/library/core/string.inf 48 | console|UEFIStarter/library/core/console.inf 49 | memory|UEFIStarter/library/core/memory.inf 50 | timestamp|UEFIStarter/library/core/timestamp.inf 51 | files|UEFIStarter/library/core/files.inf 52 | 53 | UEFIStarterPCI|UEFIStarter/library/pci.inf 54 | UEFIStarterGraphics|UEFIStarter/library/graphics.inf 55 | UEFIStarterAC97|UEFIStarter/library/ac97.inf 56 | 57 | UEFIStarterTests|UEFIStarter/library/tests/tests.inf 58 | 59 | 60 | [Components] 61 | UEFIStarter/apps/quit.inf 62 | UEFIStarter/apps/cpuid.inf 63 | UEFIStarter/apps/helloworld.inf 64 | UEFIStarter/apps/input.inf 65 | UEFIStarter/apps/snow.inf 66 | UEFIStarter/apps/timer.inf 67 | UEFIStarter/apps/lspci.inf 68 | UEFIStarter/apps/gop.inf 69 | UEFIStarter/apps/rotation.inf 70 | UEFIStarter/apps/ac97.inf 71 | 72 | UEFIStarter/library/core/logger.inf 73 | UEFIStarter/library/core/cmdline.inf 74 | UEFIStarter/library/core/string.inf 75 | UEFIStarter/library/core/memory.inf 76 | UEFIStarter/library/core/console.inf 77 | UEFIStarter/library/core/timestamp.inf 78 | UEFIStarter/library/core/files.inf 79 | 80 | UEFIStarter/library/pci.inf 81 | UEFIStarter/library/graphics.inf 82 | UEFIStarter/library/ac97.inf 83 | 84 | UEFIStarter/library/tests/tests.inf 85 | 86 | UEFIStarter/tests/suites/selftest/testself.inf 87 | UEFIStarter/tests/suites/lib/testlib.inf 88 | 89 | !include StdLib/StdLib.inc 90 | -------------------------------------------------------------------------------- /apps/ac97.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = ac97app 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f00017 5 | MODULE_TYPE = UEFI_APPLICATION 6 | VERSION_STRING = 1.0 7 | ENTRY_POINT = ShellCEntryLib 8 | 9 | [Sources] 10 | ac97.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | ShellPkg/ShellPkg.dec 15 | UEFIStarter/UEFIStarter.dec 16 | 17 | [LibraryClasses] 18 | UefiLib 19 | ShellCEntryLib 20 | PciLib 21 | UEFIStarterCore 22 | UEFIStarterPCI 23 | UEFIStarterAC97 24 | 25 | [Guids] 26 | 27 | [Ppis] 28 | 29 | [Protocols] 30 | 31 | [FeaturePcd] 32 | 33 | [Pcd] 34 | 35 | -------------------------------------------------------------------------------- /apps/cpuid.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * This application will show some information about the CPU. 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_apps 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | /** 17 | * helper macro to quickly output a CPUID flag 18 | * 19 | * \param NAME the flag's name 20 | * \param REGISTER the variable to read from 21 | * \param BIT the flag's bit offset 22 | */ 23 | #define CI(NAME,REGISTER,BIT) Print(L ## #NAME ": %d\n",(REGISTER&1<0) 24 | 25 | /** 26 | * Prints the CPU's vendor ID and a few of the capability flags. 27 | */ 28 | void cpuid() 29 | { 30 | UINT16 buf[13]; 31 | UINT64 rax,rbx,rcx,rdx; 32 | UINT64 id1,id2,id3; 33 | 34 | asm("xor %%rax,%%rax\n" 35 | "cpuid\n" 36 | :"=b" (id1), "=d" (id2), "=c" (id3) 37 | : 38 | :"rax"); 39 | LOG.trace(L"id1=%l08X id2=%l08X id3=%l08X\n",id1,id2,id3); 40 | 41 | buf[0]=(id1&0xff); 42 | buf[1]=(id1&0xff00)>>8; 43 | buf[2]=(id1&0xff0000)>>16; 44 | buf[3]=(id1&0xff000000)>>24; 45 | buf[4]=(id2&0xff); 46 | buf[5]=(id2&0xff00)>>8; 47 | buf[6]=(id2&0xff0000)>>16; 48 | buf[7]=(id2&0xff000000)>>24; 49 | buf[8]=(id3&0xff); 50 | buf[9]=(id3&0xff00)>>8; 51 | buf[10]=(id3&0xff0000)>>16; 52 | buf[11]=(id3&0xff000000)>>24; 53 | buf[12]=0; 54 | Print(L"vendor id: %s\n",buf); 55 | 56 | asm("mov $1,%%rax\n" 57 | "cpuid\n" 58 | :"=a" (rax), "=b" (rbx), "=c" (rcx), "=d" (rdx)); 59 | LOG.trace(L"rax=%l08X rbx=%l08X rcx=%l08X rdx=%l08X\n",rax,rbx,rcx,rdx); 60 | 61 | Print(L"stepping: %d\n",rax&0xf); 62 | Print(L"model: %d\n",(rax&0xf0)>>4); 63 | Print(L"family: %d\n",(rax&0xf00)>>8); 64 | Print(L"processor type: %d\n",(rax&0x3000)>>12); 65 | Print(L"extended model: %d\n",(rax&0xf0000)>>16); 66 | Print(L"extended family: %d\n",(rax&0xff00000)>>20); 67 | 68 | CI(fpu,rdx,0); 69 | CI(msr,rdx,5); 70 | CI(apic,rdx,9); 71 | CI(mmx,rdx,23); 72 | CI(sse,rdx,25); 73 | CI(sse2,rdx,26); 74 | CI(htt,rdx,28); 75 | 76 | CI(sse3,rcx,0); 77 | CI(ssse3,rcx,9); 78 | CI(sse4.1,rcx,19); 79 | CI(sse4.2,rcx,20); 80 | CI(aes,rcx,25); 81 | CI(avx,rcx,28); 82 | CI(hypervisor,rcx,31); 83 | } 84 | 85 | /** 86 | * Reads a CPU's model-specific register. 87 | * 88 | * \param rcx the register selector for the RDMSR instruction 89 | * \return the register's value 90 | */ 91 | UINT64 rdmsr(UINT64 rcx) 92 | { 93 | UINT64 rdx, rax; 94 | 95 | asm("rdmsr" 96 | :"=d" (rdx), "=a" (rax) 97 | :"c" (rcx)); 98 | return ((rdx&0xffffffff)<<32)|(rax&0xffffffff); 99 | } 100 | 101 | /** 102 | * Prints a selection of model-specific CPU registers. 103 | * Currently the selection is very narrow: the function just prints the APIC base address. 104 | */ 105 | void read_msrs() 106 | { 107 | UINT32 func=0x1b; 108 | Print(L"MSRs:\n"); 109 | Print(L" %02X: %016lX (%s)\n",func,rdmsr(func),L"APIC base address"); 110 | } 111 | 112 | /** data type for interrupt descriptor table metadata */ 113 | typedef struct 114 | { 115 | unsigned short limit; /**< the IDT's size, in bytes-1 */ 116 | void *address; /**< the IDT's starting address */ 117 | } __attribute__((packed)) idt_reg_t; 118 | 119 | /** 120 | * Interrupt handler, this will be registered by test_idt() and should get called when signalling the interrupt there. 121 | * This will set rax (the return register) to the current instruction pointer. 122 | */ 123 | void handler() 124 | { 125 | asm volatile ("lea (%rip),%rax\n\ 126 | iretq"); 127 | } 128 | 129 | /** 130 | * Writes an interrupt handler address into the IDT. 131 | * 132 | * \param entry_base the start of the IDT entry to write to 133 | * \param func the handler's address 134 | */ 135 | void write_idt_entry_address(void *entry_base, void *func) 136 | { 137 | UINT64 addr=(UINT64)func; 138 | *((UINT16 *)(entry_base+0))=addr&0xffff; 139 | *((UINT16 *)(entry_base+6))=(addr>>16)&0xffff; 140 | *((UINT32 *)(entry_base+8))=(addr>>32)&0xffffffff; 141 | } 142 | 143 | /** 144 | * Tests whether interrupt handlers can be registered. 145 | * 146 | * This will register handler() as the handling function for INT 3, then signal INT 3. The handler should get called 147 | * and should return its instruction pointer (which should be very close to handler()'s address). This function checks 148 | * the returned instruction pointer. 149 | */ 150 | void test_idt() 151 | { 152 | UINT64 delta; 153 | UINT64 handler_address; 154 | UINT64 rax; 155 | idt_reg_t idt_reg; 156 | idt_reg.address=NULL; 157 | idt_reg.limit=0; 158 | asm volatile ("sidt %0" :"=m" (idt_reg)); 159 | Print(L"IDT address=%016lX, limit=%d\n",idt_reg.address,idt_reg.limit); 160 | 161 | Print(L"writing INT 3 handler...\n"); 162 | write_idt_entry_address(idt_reg.address+3*16,handler); 163 | 164 | Print(L"calling INT 3...\n"); 165 | rax=0; 166 | asm volatile ("int $3" :"=a" (rax)); 167 | 168 | handler_address=(UINT64)&handler; 169 | Print(L"int 3 handler address: %016lX\n",handler_address); 170 | Print(L"int 3 handler returned %016lX ",rax); 171 | if(rax>handler_address) 172 | delta=rax-handler_address; 173 | else 174 | rax=handler_address-rax; 175 | if(delta<100) 176 | Print(L"(OK: close enough)\n"); 177 | else 178 | Print(L"(ERROR: difference too high)\n"); 179 | } 180 | 181 | /** 182 | * Main function, gets invoked by UEFI shell. 183 | * 184 | * \param argc the number of command-line arguments passed 185 | * \param argv the command-line arguments passed 186 | * \return an EFI status code for the shell 187 | */ 188 | INTN EFIAPI ShellAppMain(UINTN argc, CHAR16 **argv) 189 | { 190 | EFI_STATUS rv; 191 | if((rv=init(argc,argv,0))!=EFI_SUCCESS) 192 | return rv; 193 | 194 | cpuid(); 195 | Print(L"\n"); 196 | read_msrs(); 197 | test_idt(); 198 | 199 | shutdown(); 200 | return EFI_SUCCESS; 201 | } 202 | -------------------------------------------------------------------------------- /apps/cpuid.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = cpuid 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f0000a 5 | MODULE_TYPE = UEFI_APPLICATION 6 | VERSION_STRING = 1.0 7 | ENTRY_POINT = ShellCEntryLib 8 | 9 | [Sources] 10 | cpuid.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | ShellPkg/ShellPkg.dec 15 | UEFIStarter/UEFIStarter.dec 16 | 17 | [LibraryClasses] 18 | UefiLib 19 | ShellCEntryLib 20 | UEFIStarterCore 21 | 22 | [Guids] 23 | 24 | [Ppis] 25 | 26 | [Protocols] 27 | 28 | [FeaturePcd] 29 | 30 | [Pcd] 31 | 32 | -------------------------------------------------------------------------------- /apps/gop.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = gop 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f00014 5 | MODULE_TYPE = UEFI_APPLICATION 6 | VERSION_STRING = 1.0 7 | ENTRY_POINT = ShellCEntryLib 8 | 9 | [Sources] 10 | gop.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | ShellPkg/ShellPkg.dec 15 | UEFIStarter/UEFIStarter.dec 16 | 17 | [LibraryClasses] 18 | UefiLib 19 | ShellCEntryLib 20 | PciLib 21 | UEFIStarterCore 22 | UEFIStarterGraphics 23 | 24 | [Guids] 25 | 26 | [Ppis] 27 | 28 | [Protocols] 29 | 30 | [FeaturePcd] 31 | 32 | [Pcd] 33 | 34 | -------------------------------------------------------------------------------- /apps/helloworld.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * This application is a simple example of how to read command line arguments and output to console. 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_apps 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | /** 18 | * Validator function for "-int" parameter. 19 | * Values must be greater than or equal to 2 to be valid. 20 | * 21 | * \param value the value to validate 22 | * \return whether the value is >=2 23 | */ 24 | BOOLEAN validate_int(cmdline_value_t value) 25 | { 26 | if(value.uint64>=2) 27 | return TRUE; 28 | LOG.error(L"int must be >=2"); 29 | return FALSE; 30 | } 31 | 32 | /** command-line arguments in group 1 */ 33 | cmdline_argument_t args1[] = { 34 | {{uint64:0},ARG_BOOL, NULL,L"-bool",L"boolean parameter"}, 35 | {{dbl:0.66},ARG_DOUBLE,NULL,L"-dbl", L"double parameter"}, 36 | }; 37 | 38 | /** command-line argument group 1 */ 39 | ARG_GROUP(_arg_group1,args1,L"Group 1"); 40 | 41 | /** command-line arguments in group 2 */ 42 | cmdline_argument_t args2[] = { 43 | {{uint64:2},ARG_INT,validate_int,L"-int",L"integer parameter"}, 44 | }; 45 | 46 | /** command-line argument group 2 */ 47 | ARG_GROUP(_arg_group2,args2,L"Group 2"); 48 | 49 | 50 | /** 51 | * Main function, gets invoked by UEFI shell. 52 | * 53 | * \param argc the number of command-line arguments passed 54 | * \param argv the command-line arguments passed 55 | * \return an EFI status code for the shell 56 | */ 57 | INTN EFIAPI ShellAppMain(UINTN argc, CHAR16 **argv) 58 | { 59 | EFI_STATUS result; 60 | Print(L"Greetings, non-spherical habitation rock!\n"); 61 | 62 | if((result=init(argc,argv,2,&_arg_group1,&_arg_group2))!=EFI_SUCCESS) 63 | return result; 64 | 65 | Print(L"\nThere's a -help parameter that'll show command line options!\n\n"); 66 | 67 | Print(L"effective argument values after defaults:\n"); 68 | Print(L" -bool: %d\n",args1[0].value.uint64); 69 | Print(L" -dbl: %s\n",ftowcs(args1[1].value.dbl)); 70 | Print(L" -int: %d\n\n",args2[0].value.uint64); 71 | 72 | shutdown(); 73 | return EFI_SUCCESS; 74 | } 75 | -------------------------------------------------------------------------------- /apps/helloworld.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = helloworld 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f0000b 5 | MODULE_TYPE = UEFI_APPLICATION 6 | VERSION_STRING = 1.0 7 | ENTRY_POINT = ShellCEntryLib 8 | 9 | [Sources] 10 | helloworld.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | ShellPkg/ShellPkg.dec 15 | UEFIStarter/UEFIStarter.dec 16 | 17 | [LibraryClasses] 18 | UefiLib 19 | ShellCEntryLib 20 | UEFIStarterCore 21 | 22 | [Guids] 23 | 24 | [Ppis] 25 | 26 | [Protocols] 27 | 28 | [FeaturePcd] 29 | 30 | [Pcd] 31 | 32 | -------------------------------------------------------------------------------- /apps/input.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * This application showcases ways to read input from the keyboard. 3 | * 4 | * Keep in mind that terminals (and terminal multiplexers) in virtualized environments often don't send the pressed keys' 5 | * raw scancodes but escape sequences instead. For example you may have to hit the Escape key 2-3 times in succession 6 | * until it's registered once. 7 | * 8 | * \author Richard Nusser 9 | * \copyright 2017-2018 Richard Nusser 10 | * \license GPLv3 (see http://www.gnu.org/licenses/) 11 | * \sa https://github.com/rinusser/UEFIStarter 12 | * \ingroup group_apps 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | /** helper macro to access the "handle offset" command-line argument's value */ 21 | #define ARG_HANDLE _args[0].value.uint64 22 | 23 | /** helper macro to access the alternative wait command-line argument's value */ 24 | #define ARG_OTHER_WAIT_EVENT _args[1].value.uint64 25 | 26 | /** command-line arguments */ 27 | static cmdline_argument_t _args[] = { 28 | {{uint64:0},ARG_INT, NULL,L"-handle", L"Use (zero-based) nth handle"}, 29 | {{uint64:0},ARG_BOOL,NULL,L"-other-wait-event",L"Use alternate wait event"}, 30 | }; 31 | 32 | /** command-line argument group */ 33 | static ARG_GROUP(_arguments,_args,L"Application-specific options"); 34 | 35 | 36 | /** 37 | * Reads keyboard input the easiest way: SIMPLE_TEXT_INPUT_PROTOCOL. 38 | */ 39 | void test_simple_input() 40 | { 41 | EFI_STATUS result; 42 | EFI_INPUT_KEY key; 43 | UINTN tc; 44 | 45 | for(tc=0;tc<1000;tc++) 46 | { 47 | Print(L"waiting for key (q to exit)... "); 48 | gST->BootServices->WaitForEvent(1,&gST->ConIn->WaitForKey,&tc); 49 | ON_ERROR_RETURN(L"WaitForEvent",); 50 | result=gST->ConIn->ReadKeyStroke(gST->ConIn,&key); 51 | ON_ERROR_RETURN(L"ReadKeyStroke",); 52 | Print(L"done: scancode=%04X, char=%1c (%04X)\n",key.ScanCode,key.UnicodeChar>0x32?key.UnicodeChar:L' ',key.UnicodeChar); 53 | if(key.UnicodeChar==L'q') 54 | break; 55 | } 56 | } 57 | 58 | /** 59 | * Locates an UEFI protocol handler, opens and returns it. 60 | * 61 | * UEFI protocols can have multiple handlers attached, use the (0-based) "offset" parameter to determine which handler 62 | * you want. Usually the handler at offset 0 works fine. 63 | * 64 | * \param guid the protocol GUID to locate 65 | * \param offset makes this function return the (0-based) nth handler for the requested protocol 66 | * \return the protoco handler's address, or NULL on error 67 | * 68 | * \TODO extract to lib and rename to open_protocol() 69 | */ 70 | void *find_device(EFI_GUID *guid, unsigned int offset) 71 | { 72 | EFI_STATUS result; 73 | EFI_HANDLE handles[100]; 74 | UINTN handles_size=100*sizeof(EFI_HANDLE); 75 | UINTN handle_count; 76 | void *device; 77 | 78 | result=gST->BootServices->LocateHandle(ByProtocol,guid,NULL,&handles_size,(void **)&handles); 79 | ON_ERROR_RETURN(L"LocateHandle",NULL); 80 | handle_count=handles_size/sizeof(EFI_HANDLE); 81 | LOG.debug(L"handles size: %d bytes (%d entries)",handles_size,handle_count); 82 | 83 | if(offset>=handle_count) 84 | { 85 | LOG.error(L"cannot get protocol handle, requested offset %d beyond handle count %d",offset,handle_count); 86 | return NULL; 87 | } 88 | LOG.trace(L"handle: %016lX",handles[offset]); 89 | result=gST->BootServices->OpenProtocol(handles[offset],guid,&device,gImageHandle,NULL,EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL); 90 | ON_ERROR_RETURN(L"OpenProtocol",NULL); 91 | 92 | return device; 93 | } 94 | 95 | /** 96 | * Finds the protocol handler for SIMPLE_TEXT_INPUT_EX_PROTOCOL 97 | * 98 | * \return the handler's address, or NULL on error 99 | */ 100 | EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *find_input_ex() 101 | { 102 | EFI_GUID guid=EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; 103 | return (EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *)find_device(&guid,ARG_HANDLE); 104 | } 105 | 106 | /** 107 | * Reads keyboard input with the extended protocol: SIMPLE_TEXT_INPUT_EX_PROTOCOL. 108 | */ 109 | void test_ex_input() 110 | { 111 | EFI_STATUS result; 112 | EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *prot; 113 | UINTN tc; 114 | EFI_KEY_DATA data; 115 | EFI_EVENT event; 116 | 117 | if(!(prot=find_input_ex())) 118 | return; 119 | 120 | event=ARG_OTHER_WAIT_EVENT?&gST->ConIn->WaitForKey:prot->WaitForKeyEx; 121 | 122 | for(tc=0;tc<1000;tc++) 123 | { 124 | Print(L"any key, or q to quit... "); 125 | gST->BootServices->WaitForEvent(1,&event,&tc); 126 | ON_ERROR_RETURN(L"WaitForEvent",); 127 | result=prot->ReadKeyStrokeEx(prot,&data); 128 | ON_ERROR_RETURN(L"ReadKeyStrokeEx",); 129 | if(data.Key.UnicodeChar==L'q') 130 | { 131 | Print(L"\n"); 132 | return; 133 | } 134 | Print(L"scancode=%04X, key=%04X, shiftstate=%08X, toggles=%02X\n",data.Key.ScanCode,data.Key.UnicodeChar,data.KeyState.KeyShiftState,data.KeyState.KeyToggleState); 135 | } 136 | } 137 | 138 | /** 139 | * Callback function for keyboard handler, prints the pressed key(s). 140 | * This function gets called on individually registered keystrrokes only. 141 | * 142 | * \param data the pressed keys' data 143 | * \return the EFI status code, currently always indicating success 144 | */ 145 | EFI_STATUS EFIAPI ex_notify(EFI_KEY_DATA *data) 146 | { 147 | Print(L"scancode=%04X, key=%04X, shiftstate=%08X, toggles=%02X\n",data->Key.ScanCode,data->Key.UnicodeChar,data->KeyState.KeyShiftState,data->KeyState.KeyToggleState); 148 | return EFI_SUCCESS; 149 | } 150 | 151 | /** 152 | * Registers a listener for a specific key combination and waits for that combination to be pressed. 153 | * 154 | * There are no wildcards allowing to register multiple keys at once and left/right modifier states (e.g. "left shift" 155 | * vs. "right shift") have to be registered separately. An application needs to register, remember and then unregister 156 | * handlers for each combination of keys, so this way of reading the keyboard is probably too cumbersome for large 157 | * amounts of key combinations. 158 | */ 159 | void test_ex_notify() 160 | { 161 | EFI_STATUS result; 162 | EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *prot; 163 | EFI_KEY_DATA data; 164 | void *handle; 165 | unsigned int tc; 166 | 167 | if(!(prot=find_input_ex())) 168 | return; 169 | 170 | data.Key.ScanCode=0; 171 | data.Key.UnicodeChar=L'q'; 172 | // data.Key.UnicodeChar=0; 173 | data.KeyState.KeyShiftState=0; 174 | data.KeyState.KeyToggleState=0; 175 | result=prot->RegisterKeyNotify(prot,&data,(EFI_KEY_NOTIFY_FUNCTION)ex_notify,&handle); 176 | ON_ERROR_RETURN(L"RegisterKeyNotify",); 177 | 178 | Print(L"waiting for 10s, press the 'q' key as often as you want..."); 179 | for(tc=0;tc<100;tc++) 180 | { 181 | result=gBS->Stall(100000); 182 | ON_ERROR_RETURN(L"Stall",); 183 | } 184 | 185 | result=prot->UnregisterKeyNotify(prot,handle); 186 | ON_ERROR_RETURN(L"UnregisterKeyNotify",); 187 | 188 | //at this point keys pressed while we waited are still in the keyboard buffer, remove them silently 189 | drain_key_buffer(); 190 | } 191 | 192 | 193 | /** 194 | * Main function, gets invoked by UEFI shell. 195 | * 196 | * \param argc the number of command-line arguments passed 197 | * \param argv the command-line arguments passed 198 | * \return an EFI status code for the shell 199 | */ 200 | INTN EFIAPI ShellAppMain(UINTN argc, CHAR16 **argv) 201 | { 202 | EFI_STATUS rv; 203 | if((rv=init(argc,argv,1,&_arguments))!=EFI_SUCCESS) 204 | return rv; 205 | 206 | test_simple_input(); 207 | test_ex_input(); 208 | test_ex_notify(); 209 | 210 | shutdown(); 211 | return EFI_SUCCESS; 212 | } 213 | -------------------------------------------------------------------------------- /apps/input.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = input 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f0000c 5 | MODULE_TYPE = UEFI_APPLICATION 6 | VERSION_STRING = 1.0 7 | ENTRY_POINT = ShellCEntryLib 8 | 9 | [Sources] 10 | input.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | ShellPkg/ShellPkg.dec 15 | UEFIStarter/UEFIStarter.dec 16 | 17 | [LibraryClasses] 18 | UefiLib 19 | ShellCEntryLib 20 | UefiBootServicesTableLib 21 | UEFIStarterCore 22 | 23 | [Guids] 24 | 25 | [Ppis] 26 | 27 | [Protocols] 28 | 29 | [FeaturePcd] 30 | 31 | [Pcd] 32 | 33 | -------------------------------------------------------------------------------- /apps/lspci.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * This application lists PCI devices. 3 | * The built-in static/pci.ids contains just a tiny subset of possible device names. The file can be replaced with the 4 | * full version of pci.ids. 5 | * 6 | * \author Richard Nusser 7 | * \copyright 2017-2018 Richard Nusser 8 | * \license GPLv3 (see http://www.gnu.org/licenses/) 9 | * \sa https://github.com/rinusser/UEFIStarter 10 | * \ingroup group_apps 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | /** helper macro to access the `-print-classes` argument value */ 21 | #define ARG_PRINT_CLASSES _args[0].value.uint64 22 | 23 | /** list of command-line arguments */ 24 | static cmdline_argument_t _args[] = { 25 | {{uint64:0},ARG_BOOL,NULL,L"-print-classes",L"Prints known PCI device classes"}, 26 | }; 27 | 28 | /** command-line argument group */ 29 | static ARG_GROUP(_argsgroup,_args,L"Application-specific options"); 30 | 31 | /** 32 | * Main function, gets invoked by UEFI shell. 33 | * 34 | * \param argc the number of command-line arguments passed 35 | * \param argv the command-line arguments passed 36 | * \return an EFI status code for the shell 37 | */ 38 | INTN EFIAPI ShellAppMain(UINTN argc, CHAR16 **argv) 39 | { 40 | EFI_STATUS rv; 41 | if((rv=init(argc,argv,1,&_argsgroup))!=EFI_SUCCESS) 42 | return rv; 43 | init_pci_lib(); 44 | 45 | if(ARG_PRINT_CLASSES) 46 | print_known_pci_classes(); 47 | 48 | print_pci_devices(); 49 | 50 | shutdown_pci_lib(); 51 | shutdown(); 52 | return EFI_SUCCESS; 53 | } 54 | -------------------------------------------------------------------------------- /apps/lspci.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = lspci 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f00011 5 | MODULE_TYPE = UEFI_APPLICATION 6 | VERSION_STRING = 1.0 7 | ENTRY_POINT = ShellCEntryLib 8 | 9 | [Sources] 10 | lspci.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | ShellPkg/ShellPkg.dec 15 | UEFIStarter/UEFIStarter.dec 16 | 17 | [LibraryClasses] 18 | UefiLib 19 | ShellCEntryLib 20 | PciLib 21 | UEFIStarterCore 22 | UEFIStarterPCI 23 | 24 | [Guids] 25 | 26 | [Ppis] 27 | 28 | [Protocols] 29 | 30 | [FeaturePcd] 31 | 32 | [Pcd] 33 | 34 | -------------------------------------------------------------------------------- /apps/quit.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * This application will halt the UEFI environment. 3 | * QEMU will exit and return to the system shell, VirtualBox will halt the VM. 4 | * 5 | * \author Richard Nusser 6 | * \copyright 2017-2018 Richard Nusser 7 | * \license GPLv3 (see http://www.gnu.org/licenses/) 8 | * \sa https://github.com/rinusser/UEFIStarter 9 | * \ingroup group_apps 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | /** 19 | * Main function, gets invoked by UEFI shell. 20 | * 21 | * \param argc the number of command-line arguments passed 22 | * \param argv the command-line arguments passed 23 | * \return an EFI status code for the shell 24 | */ 25 | INTN EFIAPI ShellAppMain(UINTN argc, CHAR16 **argv) 26 | { 27 | EFI_STATUS rv; 28 | if((rv=init(argc,argv,0))!=EFI_SUCCESS) 29 | return rv; 30 | 31 | Print(L"shutting down...\n"); 32 | gST->RuntimeServices->ResetSystem(EfiResetShutdown,EFI_SUCCESS,0,NULL); 33 | 34 | shutdown(); 35 | return EFI_UNSUPPORTED; 36 | } 37 | -------------------------------------------------------------------------------- /apps/quit.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = quit 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f00007 5 | MODULE_TYPE = UEFI_APPLICATION 6 | VERSION_STRING = 1.0 7 | ENTRY_POINT = ShellCEntryLib 8 | 9 | [Sources] 10 | quit.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | ShellPkg/ShellPkg.dec 15 | UEFIStarter/UEFIStarter.dec 16 | 17 | [LibraryClasses] 18 | UefiLib 19 | ShellCEntryLib 20 | UefiBootServicesTableLib 21 | UEFIStarterCore 22 | 23 | [Guids] 24 | 25 | [Ppis] 26 | 27 | [Protocols] 28 | 29 | [FeaturePcd] 30 | 31 | [Pcd] 32 | 33 | -------------------------------------------------------------------------------- /apps/rotation.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * This application demonstrates image rotation and bilinear interpolation. 3 | * You'll need to run this in a graphics-capable environment, see gop.c 4 | * 5 | * \author Richard Nusser 6 | * \copyright 2017-2018 Richard Nusser 7 | * \license GPLv3 (see http://www.gnu.org/licenses/) 8 | * \sa https://github.com/rinusser/UEFIStarter 9 | * \ingroup group_apps 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | 22 | /** additional buffer required to rotate image */ 23 | EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buffer2; 24 | 25 | 26 | /** shortcut macro to access "radius" command-line parameter */ 27 | #define ARG_RADIUS args[0].value.uint64 28 | 29 | /** list of command-line arguments */ 30 | cmdline_argument_t args[]={ 31 | {{uint64:50},ARG_INT,NULL,L"-radius",L"circle radius [px]"} 32 | }; 33 | 34 | /** application-specific command-line argument group */ 35 | ARG_GROUP(arggroup,args,L"Application-specific options"); 36 | 37 | 38 | /** 39 | * This draws an animated gradient. 40 | * It's actually a bilinear interpolation between the 4 corners of the screen: the corner colors change between frames. 41 | */ 42 | void draw_gradient() 43 | { 44 | EFI_GRAPHICS_OUTPUT_BLT_PIXEL corners[4]; 45 | EFI_STATUS result; 46 | UINTN x, y; 47 | UINTN tc; 48 | UINT64 prev_ts, cur_ts; 49 | UINTN td; 50 | 51 | UINTN rel_pages; 52 | float *rel_xs, *rel_ys; 53 | 54 | ZeroMem(corners,sizeof(corners)); 55 | corners[0].Red=255; 56 | corners[1].Blue=255; 57 | corners[2].Green=255; 58 | 59 | rel_pages=(sizeof(float)*(graphics_fs_width+graphics_fs_height)-1)/4096+1; 60 | rel_xs=allocate_pages(rel_pages); 61 | if(!rel_xs) 62 | return; 63 | rel_ys=rel_xs+graphics_fs_width; 64 | 65 | for(tc=0;tc=graphics_fs_width) 82 | { 83 | x=0; 84 | y++; 85 | } 86 | graphics_fs_buffer[td]=interpolate_4px(corners,2,rel_xs[x],rel_ys[y]); 87 | x++; 88 | } 89 | 90 | result=graphics_protocol->Blt(graphics_protocol,graphics_fs_buffer,EfiBltBufferToVideo,0,0,0,0,graphics_fs_width,graphics_fs_height,0); 91 | ON_ERROR_RETURN(L"graphics_protocol->Blt",); 92 | cur_ts=get_timestamp(); 93 | gST->ConOut->SetCursorPosition(gST->ConOut,0,0); 94 | Print(L"%dms",(int)(timestamp_diff_seconds(prev_ts,cur_ts)*1000)); 95 | prev_ts=cur_ts; 96 | } 97 | 98 | free_pages(rel_xs,rel_pages); 99 | 100 | wait_for_key(); 101 | } 102 | 103 | /** 104 | * shortcut macro: defines an EFI_GRAPHICS_OUTPUT_BLT_PIXEL color value 105 | * 106 | * \param R red 107 | * \param G green 108 | * \param B blue 109 | * \param A alpha/reserved 110 | */ 111 | #define RGBA(R,G,B,A) {B,G,R,A} 112 | 113 | /** 114 | * internal: draws the image to be rotated to the full-screen graphics buffer 115 | */ 116 | void draw_circle() 117 | { 118 | EFI_STATUS result; 119 | INTN x, y; 120 | UINTN center_x, center_y; 121 | UINTN y_sq; 122 | UINTN r_sq; 123 | 124 | COLOR ring=RGBA(92,92,92,0); 125 | COLOR gray=RGBA(25,25,25,0); 126 | COLOR orange=RGBA(255,128,0,0); 127 | 128 | INTN outer_circle_max=ARG_RADIUS; 129 | INTN outer_circle_min=ARG_RADIUS*0.975; 130 | UINTN outer_circle_max_sq=outer_circle_max*outer_circle_max; 131 | UINTN outer_circle_min_sq=outer_circle_min*outer_circle_min; 132 | INTN diameter=outer_circle_max*2+1; 133 | 134 | center_x=ARG_RADIUS; 135 | center_y=ARG_RADIUS; 136 | 137 | SetMem(graphics_fs_buffer,graphics_fs_pages*4096,0); 138 | for(y=-outer_circle_max;youter_circle_max_sq) 145 | continue; 146 | 147 | if(r_sq>=outer_circle_min_sq) 148 | graphics_fs_buffer[(center_y+y)*diameter+center_x+x]=ring; 149 | else if((x<=0&&y<=0) || (x>0&&y>0)) 150 | graphics_fs_buffer[(center_y+y)*diameter+center_x+x]=orange; 151 | else 152 | graphics_fs_buffer[(center_y+y)*diameter+center_x+x]=gray; 153 | } 154 | } 155 | 156 | result=graphics_protocol->Blt(graphics_protocol,graphics_fs_buffer,EfiBltBufferToVideo,0,0,0,0,diameter,diameter,0); 157 | ON_ERROR_RETURN(L"graphics_protocol->Blt",); 158 | } 159 | 160 | /** 161 | * Animates a rotating image. 162 | */ 163 | void rotate_buffer() 164 | { 165 | EFI_STATUS result; 166 | float theta; 167 | INTN radius=ARG_RADIUS; 168 | UINT64 prev_ts, minimum_frame_ticks; 169 | 170 | set_graphics_sin_func(sin); 171 | set_graphics_cos_func(cos); 172 | buffer2=allocate_pages(graphics_fs_pages); 173 | if(!buffer2) 174 | return; 175 | SetMem(buffer2,graphics_fs_pages*4096,0); 176 | 177 | minimum_frame_ticks=get_timestamp_ticks_per_second()/ARG_FPS; 178 | prev_ts=get_timestamp(); 179 | for(theta=0;theta<=10*M_PI;theta+=M_PI/128) 180 | { 181 | rotate_image(graphics_fs_buffer,buffer2,radius,theta); 182 | result=graphics_protocol->Blt(graphics_protocol,buffer2,EfiBltBufferToVideo,0,0,0,0,2*radius+1,2*radius+1,0); 183 | ON_ERROR_RETURN(L"graphics_protocol->Blt",); 184 | gST->ConOut->SetCursorPosition(gST->ConOut,0,0); 185 | limit_framerate(&prev_ts,minimum_frame_ticks); 186 | } 187 | free_graphics_fs_buffer(buffer2); 188 | wait_for_key(); 189 | } 190 | 191 | 192 | /** 193 | * Main function, gets invoked by UEFI shell. 194 | * 195 | * \param argc the number of command-line arguments passed 196 | * \param argv_ascii the command-line arguments passed, as ASCII 197 | * \return an EFI status code for the shell 198 | */ 199 | int main(int argc, char **argv_ascii) 200 | { 201 | EFI_STATUS rv; 202 | CHAR16 **argv; 203 | 204 | argv=argv_from_ascii(argc,argv_ascii); 205 | rv=init(argc,argv,2,&graphics_arguments,&arggroup); 206 | free_argv(); 207 | 208 | if(rv==EFI_SUCCESS) 209 | rv=init_graphics(); 210 | 211 | if(rv!=EFI_SUCCESS) 212 | { 213 | shutdown(); 214 | return rv; 215 | } 216 | 217 | init_timestamps(); 218 | draw_circle(); 219 | rotate_buffer(); 220 | draw_gradient(); 221 | 222 | shutdown_graphics(); 223 | shutdown(); 224 | return EFI_SUCCESS; 225 | } 226 | -------------------------------------------------------------------------------- /apps/rotation.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = rotation 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f00015 5 | MODULE_TYPE = UEFI_APPLICATION 6 | VERSION_STRING = 1.0 7 | ENTRY_POINT = ShellCEntryLib 8 | 9 | [Sources] 10 | rotation.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | ShellPkg/ShellPkg.dec 15 | StdLib/StdLib.dec 16 | UEFIStarter/UEFIStarter.dec 17 | 18 | [LibraryClasses] 19 | UefiLib 20 | ShellCEntryLib 21 | UefiBootServicesTableLib 22 | LibMath 23 | UEFIStarterCore 24 | UEFIStarterGraphics 25 | 26 | [Guids] 27 | 28 | [Ppis] 29 | 30 | [Protocols] 31 | 32 | [FeaturePcd] 33 | 34 | [Pcd] 35 | 36 | -------------------------------------------------------------------------------- /apps/snow.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = snow 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f0000d 5 | MODULE_TYPE = UEFI_APPLICATION 6 | VERSION_STRING = 1.0 7 | ENTRY_POINT = ShellCEntryLib 8 | 9 | [Sources] 10 | snow.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | ShellPkg/ShellPkg.dec 15 | UEFIStarter/UEFIStarter.dec 16 | 17 | [LibraryClasses] 18 | UefiLib 19 | ShellCEntryLib 20 | UefiBootServicesTableLib 21 | UEFIStarterCore 22 | 23 | [Guids] 24 | 25 | [Ppis] 26 | 27 | [Protocols] 28 | 29 | [FeaturePcd] 30 | 31 | [Pcd] 32 | 33 | -------------------------------------------------------------------------------- /apps/timer.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * This application shows how to wait for events and register a timer. 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_apps 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | /** 18 | * Callback function for the timer. 19 | * Contains a Print() call that may crash a Virtualbox VM. In current builds it does not, but it used to for some 20 | * reason. Calling Print() in a timer callback is just for demonstration anyway. 21 | * 22 | * \param event (unused) the triggered timer event 23 | * \param context (unused) the triggered timer event's context data 24 | */ 25 | void callback(EFI_EVENT event, void *context) 26 | { 27 | Print(L" 2c\n"); 28 | } 29 | 30 | /** 31 | * Prints the current date/time info and timing capabilities of the UEFI runtime service's GetTime(). 32 | * This shows resolutions of 1Hz on all tested systems, the nanosecond field seems to be always empty. This makes 33 | * GetTime() useless for timing e.g. animation frames. 34 | */ 35 | void do_gettime_tests() 36 | { 37 | EFI_STATUS result; 38 | EFI_TIME time; 39 | EFI_TIME_CAPABILITIES caps; 40 | 41 | result=gST->RuntimeServices->GetTime(&time,&caps); 42 | ON_ERROR_RETURN(L"GetTime",); 43 | 44 | Print(L"time: %04d-%02d-%02d %02d:%02d:%02d.%09d (tz=%d, dst=%d)\n",time.Year,time.Month,time.Day,time.Hour,time.Minute,time.Second,time.Nanosecond,time.TimeZone,time.Daylight); 45 | Print(L"date: resolution=%dHz, accuracy=%dppm, tozero=%d\n",caps.Resolution,caps.Accuracy/1000000,caps.SetsToZero); 46 | } 47 | 48 | 49 | /** 50 | * Compares the internal get_timestamp() against the UEFI service's timer functions. 51 | * The timer interval is 1s, so the internal timestamp should increase by 1 for each Hz of CPU frequency. 52 | */ 53 | void do_timestamp_tests() 54 | { 55 | UINT64 start, end, diff; 56 | EFI_STATUS result; 57 | EFI_EVENT event; 58 | UINTN index; 59 | unsigned int tc; 60 | unsigned int loop_count=10; 61 | UINT64 min, max, middle; 62 | float accuracy; 63 | 64 | result=gST->BootServices->CreateEvent(EVT_TIMER,TPL_CALLBACK,NULL,NULL,&event); 65 | ON_ERROR_RETURN(L"CreateEvent",); 66 | 67 | result=gST->BootServices->SetTimer(event,TimerPeriodic,1000*1000*10); 68 | ON_ERROR_RETURN(L"SetTimer",); 69 | 70 | LOG.info(L"prepared event, waiting for 1+%d intervals...",loop_count); 71 | 72 | result=gST->BootServices->WaitForEvent(1,&event,&index); 73 | ON_ERROR_RETURN(L"WaitForEvent",); 74 | 75 | start=get_timestamp(); 76 | Print(L"start timestamp: %016lX (%ld)\n",start,start); 77 | 78 | for(tc=0;tcBootServices->WaitForEvent(1,&event,&index); 81 | ON_ERROR_RETURN(L"WaitForEvent",); 82 | end=get_timestamp(); 83 | 84 | diff=end-start; 85 | Print(L"interval: %016lX (%ld)\n",diff,diff); 86 | start=end; 87 | if(tc>0) 88 | { 89 | if(diffmax) 92 | max=diff; 93 | } 94 | else 95 | { 96 | min=diff; 97 | max=diff; 98 | } 99 | } 100 | middle=(max+min)/2; //not an actual average! 101 | accuracy=100.0*(max-min)/middle; 102 | Print(L"ticks per second: min=%lu, max=%lu, diff=%lu => timer accuracy: +-%s%%\n",min,max,max-min,ftowcs(accuracy)); 103 | } 104 | 105 | /** 106 | * Registers events to wait for. 107 | * 108 | * One of the events is a callback function, it will get called asynchronously whenever the event is fired. The called 109 | * handler currently prints to the console - this used to hang when executed inside a VirtualBox VM. 110 | */ 111 | void do_event_tests() 112 | { 113 | EFI_STATUS result; 114 | EFI_EVENT events[2]; 115 | UINTN tc; 116 | UINTN index; 117 | UINT64 start_ts, end_ts; 118 | 119 | init_timestamps(); 120 | 121 | result=gST->BootServices->CreateEvent(EVT_TIMER,TPL_CALLBACK,NULL,NULL,&events[0]); 122 | ON_ERROR_RETURN(L"CreateEvent",); 123 | 124 | result=gST->BootServices->CreateEvent(EVT_TIMER|EVT_NOTIFY_SIGNAL,TPL_CALLBACK,(EFI_EVENT_NOTIFY)callback,NULL,&events[1]); 125 | ON_ERROR_RETURN(L"CreateEvent",); 126 | // uefi_call_or_return(,ST->BootServices->CreateEvent,5,EVT_TIMER|EVT_NOTIFY_WAIT,TPL_CALLBACK,callback,NULL,&events[1]); 127 | 128 | result=gST->BootServices->SetTimer(events[0],TimerPeriodic,100*1000*10); 129 | ON_ERROR_RETURN(L"SetTimer",); 130 | result=gST->BootServices->SetTimer(events[1],TimerPeriodic,500*1000*10); 131 | ON_ERROR_RETURN(L"SetTimer",); 132 | 133 | start_ts=get_timestamp(); 134 | Print(L"waiting for events (c..callback, w..wait)...\n"); 135 | for(tc=0;tc<20;tc++) 136 | { 137 | LOG.trace(L"waiting..."); 138 | result=gST->BootServices->WaitForEvent(1,&events[0],&index); 139 | ON_ERROR_RETURN(L"WaitForEvent",); 140 | Print(L" %dw%s",index+1,index>0?L"\n":L""); 141 | } 142 | Print(L"\n"); 143 | end_ts=get_timestamp(); 144 | Print(L"waited for %ss\n",ftowcs(timestamp_diff_seconds(start_ts,end_ts))); 145 | 146 | result=gST->BootServices->CloseEvent(events[0]); 147 | ON_ERROR_RETURN(L"CloseEvent",); 148 | result=gST->BootServices->CloseEvent(events[1]); 149 | ON_ERROR_RETURN(L"CloseEvent",); 150 | } 151 | 152 | 153 | /** 154 | * Main function, gets invoked by UEFI shell. 155 | * 156 | * \param argc the number of command-line arguments passed 157 | * \param argv the command-line arguments passed 158 | * \return an EFI status code for the shell 159 | */ 160 | INTN EFIAPI ShellAppMain(UINTN argc, CHAR16 **argv) 161 | { 162 | EFI_STATUS rv; 163 | if((rv=init(argc,argv,0))!=EFI_SUCCESS) 164 | return rv; 165 | 166 | do_gettime_tests(); 167 | do_timestamp_tests(); 168 | do_event_tests(); 169 | 170 | shutdown(); 171 | return EFI_UNSUPPORTED; 172 | } 173 | -------------------------------------------------------------------------------- /apps/timer.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = timer 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f0000e 5 | MODULE_TYPE = UEFI_APPLICATION 6 | VERSION_STRING = 1.0 7 | ENTRY_POINT = ShellCEntryLib 8 | 9 | [Sources] 10 | timer.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | ShellPkg/ShellPkg.dec 15 | UEFIStarter/UEFIStarter.dec 16 | 17 | [LibraryClasses] 18 | UefiLib 19 | ShellCEntryLib 20 | UEFIStarterCore 21 | 22 | [Guids] 23 | 24 | [Ppis] 25 | 26 | [Protocols] 27 | 28 | [FeaturePcd] 29 | 30 | [Pcd] 31 | 32 | -------------------------------------------------------------------------------- /include/UEFIStarter/ac97.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * AC'97 audio functions 3 | * 4 | * Check these documents for details on how AC'97 works (they're both available from Intel): 5 | * * Audio Codec '97 specification 6 | * * Intel I/O Controller Hub 6 (ICH6) High Definition Audio / AC '97 Programmer's Reference Manual 7 | * 8 | * \author Richard Nusser 9 | * \copyright 2017-2018 Richard Nusser 10 | * \license GPLv3 (see http://www.gnu.org/licenses/) 11 | * \sa https://github.com/rinusser/UEFIStarter 12 | * \ingroup group_lib_ac97 13 | * 14 | * \TODO maybe combine find and init functions 15 | */ 16 | 17 | #ifndef __AC97_H 18 | #define __AC97_H 19 | 20 | #include 21 | #include 22 | 23 | 24 | #define AC97_BUFFER_COUNT 32 /**< number of audio data buffers, up to 32 supported by AC'97 specs */ 25 | 26 | 27 | #define ARG_MUTE ac97_argument_list[0].value.uint64 /**< shortcut macro to access "mute" argument */ 28 | #define ARG_VOLUME ac97_argument_list[1].value.dbl /**< shortcut macro to access "volume" argument */ 29 | #define ARG_SAMPLE_RATE ac97_argument_list[2].value.uint64 /**< shortcut macro to access "sample rate" argument */ 30 | extern cmdline_argument_t ac97_argument_list[]; 31 | extern cmdline_argument_group_t ac97_arguments; 32 | 33 | 34 | #define AC97_MIXER_RESET 0x00 /**< "reset" mixer register */ 35 | #define AC97_MIXER_MASTER 0x02 /**< "master volume" mixer register */ 36 | #define AC97_MIXER_PCM_OUT 0x18 /**< "PCM OUT volume" mixer register */ 37 | #define AC97_PCM_RATE_FRONT 0x2C /**< "PCM front channel DAC sample rate" mixer register */ 38 | #define AC97_PCM_RATE_SURROUND 0x2E /**< "PCM surround channel DAC sample rate" mixer register */ 39 | #define AC97_PCM_RATE_LFE 0x30 /**< "PCM LFE channel DAC sample rate" mixer register */ 40 | 41 | 42 | //XXX when adding bus master registers, remember to add non-byte widths to getter function 43 | #define AC97_DESCRIPTOR_PCM_OUT 0x10 /**< "PCM OUT descriptor base address" bus master register */ 44 | #define AC97_CIV_PCM_OUT 0x14 /**< "PCM OUT current index value" bus master register */ 45 | #define AC97_LVI_PCM_OUT 0x15 /**< "PCM OUT last valid index" bus master register */ 46 | #define AC97_STATUS_PCM_OUT 0x16 /**< "PCM OUT status" bus master register */ 47 | #define AC97_CONTROL_PCM_OUT 0x1B /**< "PCM OUT control" bus master register */ 48 | #define AC97_GLOBAL_CONTROL 0x2C /**< "global control" bus master register */ 49 | 50 | 51 | /** 52 | * Data type for an AC'97 "baseline audio register set". 53 | * The AC'97 specification contains a description of these registers. 54 | */ 55 | typedef struct 56 | { 57 | UINT16 reset; /**< offset 0x00 */ 58 | UINT16 master_vol; /**< offset 0x02 */ 59 | UINT16 aux_out_vol; /**< offset 0x04 */ 60 | UINT16 mono_vol; /**< offset 0x06 */ 61 | UINT16 master_tone; /**< offset 0x08 */ 62 | UINT16 pc_beep_vol; /**< offset 0x0A */ 63 | UINT16 phone_vol; /**< offset 0x0C */ 64 | UINT16 mic_vol; /**< offset 0x0E */ 65 | UINT16 line_in_vol; /**< offset 0x10 */ 66 | UINT16 cd_vol; /**< offset 0x12 */ 67 | UINT16 video_vol; /**< offset 0x14 */ 68 | UINT16 aux_in_vol; /**< offset 0x16 */ 69 | UINT16 pcm_out_vol; /**< offset 0x18 */ 70 | UINT16 record_select; /**< offset 0x1A */ 71 | UINT16 record_gain; /**< offset 0x1C */ 72 | UINT16 record_gain_mic; /**< offset 0x1E */ 73 | UINT16 general_purpose; /**< offset 0x20 */ 74 | UINT16 three_d_control; /**< offset 0x22 */ 75 | UINT16 _reserved24; /**< offset 0x24 */ 76 | UINT16 powerdown_ctrlstat; /**< offset 0x26 */ 77 | union 78 | { 79 | UINT16 raw; /**< raw access */ 80 | struct 81 | { 82 | UINT16 vra:1; /**< variable rate PCM audio support */ 83 | UINT16 dra:1; /**< double-rate PCM audio support */ 84 | UINT16 spdif:1; /**< S/PDIF support */ 85 | UINT16 vrm:1; /**< variable rate MIC input support */ 86 | UINT16 dsa:2; /**< DAC slot assignments */ 87 | UINT16 cdac:1; /**< PCM center DAC support */ 88 | UINT16 sdac:1; /**< PCM surround DAC support */ 89 | UINT16 ldac:1; /**< PCM LFE DAC support */ 90 | UINT16 amap:1; /**< slot/DAC mappings support */ 91 | UINT16 rev:2; /**< codec revision */ 92 | UINT16 _reserved:2; /**< (reserved) */ 93 | UINT16 id:2; /**< codec configuration */ 94 | }; /**< structured access */ 95 | } extended_audio_id; /**< offset 0x28 */ 96 | union 97 | { 98 | UINT16 raw; /**< raw access */ 99 | struct 100 | { 101 | UINT16 vra:1; /**< VRA enabled */ 102 | UINT16 dra:1; /**< DRA enabled */ 103 | UINT16 spdif:1; /**< S/PDIF enabled */ 104 | UINT16 vrm:1; /**< VRM enabled */ 105 | UINT16 spsa:2; /**< S/PDIF source */ 106 | UINT16 cdac:1; /**< PCM center DAC ready */ 107 | UINT16 sdac:1; /**< PCM surround DACs ready */ 108 | UINT16 ldac:1; /**< PCM LFE DAC ready */ 109 | UINT16 madc:1; /**< MIC ADC ready */ 110 | UINT16 spcv:1; /**< S/PDIF configuration valid */ 111 | UINT16 pri:1; /**< PCM center DAC suppressed */ 112 | UINT16 prj:1; /**< PCM surround DACs suppressed */ 113 | UINT16 prk:1; /**< PCM LFE DACs suppressed */ 114 | UINT16 prl:1; /**< MIC ADC suppressed */ 115 | UINT16 vcfg:1; /**< S/PDIF idle configuration */ 116 | }; /**< structured access */ 117 | } extended_audio_statctrl; /**< offset 0x2A */ 118 | UINT16 pcm_front_dac_rate; /**< offset 0x2C */ 119 | UINT16 pcm_surr_dac_rate; /**< offset 0x2E */ 120 | UINT16 pcm_lfe_dac_rate; /**< offset 0x30 */ 121 | UINT16 pcm_lr_adc_rate; /**< offset 0x32 */ 122 | UINT16 pcm_mic_adc_rate; /**< offset 0x34 */ 123 | UINT16 _unhandled3[5]; /**< offset 0x36 */ 124 | UINT16 _unhandled4[8]; /**< offset 0x40 */ 125 | UINT16 _unhandled5[8]; /**< offset 0x50 */ 126 | UINT16 _unhandled6[8]; /**< offset 0x60 */ 127 | UINT16 _unhandled7[6]; /**< offset 0x70 */ 128 | UINT16 vendor_id1; /**< offset 0x7C */ 129 | UINT16 vendor_id2; /**< offset 0x7E */ 130 | } ac97_bar_t; 131 | 132 | /** data type for AC'97 buffer descriptor */ 133 | typedef struct 134 | { 135 | UINT32 address; /**< start of buffer */ 136 | UINT16 length; /**< length of buffer, in bytes */ 137 | union 138 | { 139 | UINT16 raw; /**< raw access to buffer configuration */ 140 | struct 141 | { 142 | UINT16 _reserved:14; /**< (reserved) */ 143 | UINT16 bup:1; /**< buffer underrun policy */ 144 | UINT16 ioc:1; /**< interrupt on completion */ 145 | }; /**< structured access to buffer configuration */ 146 | } control; /**< buffer configuration */ 147 | } ac97_buffer_descriptor_t; 148 | 149 | /** 150 | * data type for signed 16-bit integer audio buffers descriptor 151 | * 152 | * \TODO ac97_buffers_s16_t.buffers doesn't need to be DMA transferred, change this to e.g. an output of init_buffers() 153 | */ 154 | typedef struct 155 | { 156 | ac97_buffer_descriptor_t descriptors[32]; /**< the list of buffer descriptors */ 157 | INT16 *buffers[32]; /**< pointers to buffer contents */ 158 | } ac97_buffers_s16_t; 159 | 160 | /** AC'97 handle, this is the high-level handle for library use */ 161 | typedef struct 162 | { 163 | ac97_buffers_s16_t *buffers; /**< ring buffer for audio output */ 164 | UINTN buffer_pages; /**< number of memory pages allocated for audio buffers */ 165 | EFI_PHYSICAL_ADDRESS device_address; /**< physical memory address to access the AC'97 codec */ 166 | void *mapping; /**< DMA memory mapping for transferring audio data to AC'97 */ 167 | EFI_PCI_IO_PROTOCOL *pci; /**< UEFI PCI handle to use */ 168 | UINT8 max_master_vol; /**< maximum master volume, either 31 or 63, depending on hardware */ 169 | } ac97_handle_t; 170 | 171 | /** data type for bus master status register */ 172 | typedef union 173 | { 174 | UINT16 raw; /**< raw access */ 175 | struct 176 | { 177 | UINT16 dma_controller_halted:1; /**< DCH */ 178 | UINT16 current_equals_last_valid:1; /**< CELV */ 179 | UINT16 last_valid_buffer_completion_interrupt:1; /**< LVBCI */ 180 | UINT16 buffer_completion_interrupt:1; /**< BCIS */ 181 | UINT16 fifo_error:1; /**< FIFOE */ 182 | UINT16 _reserved:11; /**< (reserved) */ 183 | }; /**< structured access */ 184 | } ac97_busmaster_status_t; 185 | 186 | 187 | EFI_PCI_IO_PROTOCOL *find_ac97_device(); 188 | void *init_ac97_handle(ac97_handle_t *handle, EFI_PCI_IO_PROTOCOL *pip); 189 | void close_ac97_handle(ac97_handle_t *handle); 190 | 191 | 192 | /** 193 | * Generates an AC'97 mixer value. 194 | * 195 | * \param LEFT the left channel's value, within [0..63] 196 | * \param RIGHT the right channel's value, within [0..63] 197 | * \param MUTE whether to mute (0 for no, anything else for yes) 198 | * \return the mixer value, ready to write into an AC'97 mixer register 199 | */ 200 | #define ac97_mixer_value(LEFT,RIGHT,MUTE) ((((LEFT)&0x3F)<<8)|((RIGHT)&0x3F)|((MUTE)?0x8000:0)) 201 | 202 | EFI_STATUS write_mixer_reg(ac97_handle_t *handle, UINT32 reg, UINT16 value); 203 | EFI_STATUS read_mixer_reg(ac97_handle_t *handle, UINT32 reg, UINT16 *value); 204 | void print_volume_register(UINT16 *text, UINT16 value); 205 | void print_volume_register_mono(UINT16 *text, UINT16 value); 206 | EFI_STATUS set_ac97_cmdline_volume(ac97_handle_t *handle); 207 | EFI_STATUS set_ac97_cmdline_sample_rate(ac97_handle_t *handle); 208 | 209 | EFI_STATUS write_busmaster_reg(ac97_handle_t *handle, UINT32 reg, UINTN value); 210 | EFI_STATUS read_busmaster_reg(ac97_handle_t *handle, UINT32 reg, UINTN *value); 211 | 212 | EFI_STATUS flush_ac97_output(ac97_handle_t *handle); 213 | EFI_STATUS ac97_play(ac97_handle_t *handle); 214 | void ac97_wait_until_last_buffer_sent(ac97_handle_t *handle, UINTN timeout_in_milliseconds); 215 | 216 | 217 | #define AC97_DUMP_VOLUME 0x00000001 /**< flag for dump_audio_registers(): dump volume registers */ 218 | #define AC97_DUMP_OTHER 0x80000000 /**< flag for dump_audio_registers(): dump other registers */ 219 | #define AC97_DUMP_ALL -1 /**< flag for dump_audio_registers(): dump all registers */ 220 | void dump_audio_registers(ac97_handle_t *handle, UINTN flags); 221 | 222 | 223 | #endif 224 | -------------------------------------------------------------------------------- /include/UEFIStarter/core.h: -------------------------------------------------------------------------------- 1 | #ifndef __CORE_H 2 | #define __CORE_H 3 | 4 | #include "core/cmdline.h" 5 | #include "core/console.h" 6 | #include "core/files.h" 7 | #include "core/logger.h" 8 | #include "core/memory.h" 9 | #include "core/string.h" 10 | #include "core/timestamp.h" 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /include/UEFIStarter/core/cmdline.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Command line parameter parser 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_lib_cmdline 9 | */ 10 | 11 | #ifndef __CMDLINE_H 12 | #define __CMDLINE_H 13 | 14 | #include 15 | 16 | /** EFI_STATUS compatible return value, indicating user wanted the help screen */ 17 | #define RV_HELP 0x10000000 18 | 19 | /** the supported command line argument types */ 20 | typedef enum 21 | { 22 | ARG_BOOL=1, /**< boolean */ 23 | ARG_INT, /**< integer */ 24 | ARG_DOUBLE, /**< double */ 25 | ARG_STRING, /**< UTF-16 string */ 26 | } argument_type; 27 | 28 | /** 29 | * one parsed command line value 30 | */ 31 | typedef union { 32 | double dbl; /**< double variant */ 33 | UINT64 uint64; /**< UINT64 variant */ 34 | CHAR16 *wcstr; /**< UTF-16 string variant */ 35 | } cmdline_value_t; 36 | 37 | /** function pointer type for command line argument validators */ 38 | typedef BOOLEAN validate_argument_f(cmdline_value_t); 39 | 40 | /** model for one command line argument */ 41 | typedef struct { 42 | cmdline_value_t value; /**< the parsed value */ 43 | argument_type type; /**< the argument's type */ 44 | validate_argument_f *validator_func; /**< the validator function, may be NULL */ 45 | CHAR16 *name; /**< the argument's name */ 46 | CHAR16 *helptext; /**< a user-friendly help text */ 47 | } cmdline_argument_t; 48 | 49 | /** model for group of command line arguments */ 50 | typedef struct { 51 | CHAR16 *name; /**< the group's name or short description, may be NULL */ 52 | UINTN count; /**< the number of entries in the group */ 53 | cmdline_argument_t *list; /**< the list of arguments in this group */ 54 | } cmdline_argument_group_t; 55 | 56 | /** 57 | * shortcut macro for quickly defining an argument group 58 | * 59 | * \param VARIABLE the group variable to define 60 | * \param LIST the list of arguments, as cmdline_argument_t[] 61 | * \param DESCRIPTION the group's name or description, may be NULL 62 | */ 63 | #define ARG_GROUP(VARIABLE,LIST,DESCRIPTION) cmdline_argument_group_t VARIABLE={DESCRIPTION,sizeof(LIST)/sizeof(cmdline_argument_t),LIST}; 64 | 65 | 66 | BOOLEAN wctype_int(CHAR16 *string); 67 | BOOLEAN wctype_float(CHAR16 *string); 68 | double _wcstof(CHAR16 *str); 69 | 70 | /** 71 | * shortcut macro for quickly defining a validator for "double" type arguments 72 | * 73 | * \param FUNC the function name to define 74 | * \param FIELD the field name to use in error messages 75 | * \param MIN the minimum value (inclusive) 76 | * \param MAX the maximum value (inclusive) 77 | */ 78 | #define DOUBLE_RANGE_VALIDATOR(FUNC,FIELD,MIN,MAX) static BOOLEAN FUNC(cmdline_value_t v) { return validate_double_range(v,FIELD,MIN,MAX); } 79 | 80 | BOOLEAN validate_double_range(cmdline_value_t v, CHAR16 *field, double min, double max); 81 | 82 | /** 83 | * shortcut macro for quickly defining a validator for "integer" type arguments 84 | * 85 | * \param FUNC the function name to define 86 | * \param FIELD the field name to use in error messages 87 | * \param MIN the minimum value (inclusive) 88 | * \param MAX the maximum value (inclusive) 89 | */ 90 | #define INT_RANGE_VALIDATOR(FUNC,FIELD,MIN,MAX) static BOOLEAN FUNC(cmdline_value_t v) { return validate_uint64_range(v,FIELD,MIN,MAX); } 91 | 92 | BOOLEAN validate_uint64_range(cmdline_value_t v, CHAR16 *field, UINT64 min, UINT64 max); 93 | 94 | EFI_STATUS parse_parameters(INTN argc, CHAR16 **argv, UINTN group_count, VA_LIST groups); 95 | void print_help_text(UINTN group_count, VA_LIST groups); 96 | 97 | CHAR16 **argv_from_ascii(int argc, char **argv_ascii); 98 | void free_argv(); 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /include/UEFIStarter/core/console.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * CLI initialization and I/O helpers 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_lib_console 9 | */ 10 | 11 | #ifndef __CONSOLE_H 12 | #define __CONSOLE_H 13 | 14 | #include 15 | #include "cmdline.h" 16 | 17 | EFI_STATUS EFIAPI init(INTN argc, CHAR16 **argv, UINTN arg_group_count, ...); 18 | void shutdown(); 19 | 20 | void print_console_modes(); 21 | EFI_STATUS set_console_mode(unsigned int requested_mode); 22 | void EFIAPI color_print(UINTN color, CHAR16 *fmt, ...); 23 | 24 | 25 | void drain_key_buffer(); 26 | void wait_for_key(); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /include/UEFIStarter/core/files.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * File handling functions 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_lib_files 9 | */ 10 | 11 | #ifndef __FILES_H 12 | #define __FILES_H 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | /** 19 | * Data structure for file contents. 20 | * This gets allocated dynamically, make sure you free the memory pages when you're done with it. 21 | */ 22 | typedef struct { 23 | UINTN memory_pages; /**< the number of memory pages required to hold the current instance */ 24 | UINT64 data_length; /**< the file's content length */ 25 | char data[]; /**< the file's content data */ 26 | } file_contents_t; 27 | 28 | EFI_FILE_HANDLE find_root_volume(); 29 | EFI_FILE_HANDLE find_file(CHAR16 *pathname); 30 | file_contents_t *get_file_contents(CHAR16 *filename); 31 | 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /include/UEFIStarter/core/logger.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Logging facility, supports verbosity levels 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_lib_logger 9 | */ 10 | 11 | #ifndef __LOGGER_H 12 | #define __LOGGER_H 13 | 14 | #include 15 | 16 | /** helper macro to log a TRACE message containing the current file and line */ 17 | #define TRACE_HERE LOG.trace(L"%a#%d",__FILE__,__LINE__); 18 | 19 | /** 20 | * helper macro to quickly log a WARN message if an EFI result is anything but EFI_SUCCESS 21 | * 22 | * \param TEXT the text to log 23 | */ 24 | #define ON_ERROR_WARN(TEXT) if(result!=EFI_SUCCESS) LOG.warn(TEXT); 25 | 26 | /** 27 | * helper macro to quickly log an ERROR message and return if an EFI result is anything but EFI_SUCCESS 28 | * 29 | * \param TEXT the text to log 30 | * \param RV the value to return 31 | */ 32 | #define ON_ERROR_RETURN(TEXT,RV) if(result!=EFI_SUCCESS) \ 33 | { \ 34 | LOG.error(L"%s() returned status %d (%r)",TEXT,result,result); \ 35 | return RV; \ 36 | } 37 | 38 | 39 | /** 40 | * the list of log levels 41 | */ 42 | typedef enum 43 | { 44 | OFF=0, /**< disable logging: la-la-la, I can't hear you */ 45 | ERROR, /**< error messages: something definitely broke */ 46 | WARN, /**< warnings: something may have broken */ 47 | INFO, /**< informative messages: just in case something breaks */ 48 | DEBUG, /**< debug messages: something breaks and I'm trying to fix it */ 49 | TRACE /**< trace messages: something breaks and I don't know what or where */ 50 | } LOGLEVEL; 51 | 52 | extern const CHAR16 *logger_level_names[]; 53 | 54 | 55 | /** 56 | * function pointer for log output functions 57 | * 58 | * \param level the log level to output at 59 | * \param msg the log message to print, as UTF-16 60 | */ 61 | typedef void logger_print_function_t(LOGLEVEL level, CHAR16 *msg); 62 | 63 | /** 64 | * function pointer for logging functions to be invoked directly 65 | * 66 | * \param fmt the message's format string, as UTF-16 - takes same format as Print() functions 67 | * \param ... any additional arguments matching the format string placeholders 68 | */ 69 | typedef void EFIAPI logger_function_t(UINT16 *fmt, ...); 70 | 71 | /** logging facility data type, contains logging function pointer for each log level */ 72 | typedef struct 73 | { 74 | logger_function_t *trace; /**< logger for TRACE level */ 75 | logger_function_t *debug; /**< logger for DEBUG level */ 76 | logger_function_t *info; /**< logger for INFO level */ 77 | logger_function_t *warn; /**< logger for WARN level */ 78 | logger_function_t *error; /**< logger for ERROR level */ 79 | } loggers_t; 80 | 81 | extern const loggers_t LOG; 82 | 83 | 84 | LOGLEVEL get_log_level(); 85 | LOGLEVEL set_log_level(LOGLEVEL level); 86 | 87 | void reset_logger_entry_counts(); 88 | UINTN get_logger_entry_count(LOGLEVEL level); 89 | 90 | void kill(); 91 | 92 | logger_print_function_t *set_logger_function(logger_print_function_t *func); 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /include/UEFIStarter/core/memory.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Memory allocation tracker: use this to prevent memory leaks 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_lib_memory 9 | */ 10 | 11 | #ifndef __MEMORY_H 12 | #define __MEMORY_H 13 | 14 | /** 15 | * maximum number of entries per memory page list node 16 | * 17 | * Keep this as high as possible without breaking page size: sizeof(memory_page_list_t) should be as close to n*4096 18 | * as possible without going over it. 19 | * 20 | * As of right now 510 will result in 2 pages for 64 bit systems and 1 page for 32 bit systems. 21 | */ 22 | #define MEMORY_PAGE_LIST_MAX_ENTRY_COUNT 510 23 | 24 | /** like MEMORY_PAGE_LIST_MAX_ENTRY_COUNT, but for pool_memory_list_t */ 25 | #define POOL_MEMORY_LIST_MAX_ENTRY_COUNT 1022 26 | 27 | 28 | /** type for a single tracked allocation of memory pages */ 29 | typedef struct 30 | { 31 | UINTN pages; /**< the number of pages allocated */ 32 | void *address; /**< the memory location of the first page */ 33 | } memory_page_list_entry_t; 34 | 35 | /** type for a list node of tracked memory page allocations */ 36 | typedef struct 37 | { 38 | UINTN entry_count; /**< the number of entries in this node */ 39 | memory_page_list_entry_t entries[MEMORY_PAGE_LIST_MAX_ENTRY_COUNT]; /**< the allocation entries in this node */ 40 | struct memory_page_list_t *next; /**< the next list node, may be null */ 41 | } memory_page_list_t; 42 | 43 | /** type for a list node of tracked pool memory */ 44 | typedef struct 45 | { 46 | UINTN entry_count; /**< the number of entries in this node */ 47 | void *entries[POOL_MEMORY_LIST_MAX_ENTRY_COUNT]; /**< the allocation entries in this node */ 48 | struct pool_memory_list_t *next; /**< the next list node, may be null */ 49 | } pool_memory_list_t; 50 | 51 | 52 | void reset_memory_tracking(); 53 | 54 | 55 | void *allocate_pages(UINTN pages); 56 | BOOLEAN free_pages(void *address, UINTN pages); 57 | 58 | void *allocate_pages_ex(UINTN pages, BOOLEAN track, EFI_ALLOCATE_TYPE type, void *target_address); 59 | BOOLEAN free_pages_ex(void *address, UINTN pages, BOOLEAN track); 60 | 61 | 62 | void track_pool_memory(void *address); 63 | UINTN free_pool_memory_entries(); 64 | 65 | 66 | void print_memory_page_list(); 67 | void print_pool_memory_list(); 68 | 69 | void init_tracking_memory(); 70 | UINTN stop_tracking_memory(); 71 | 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /include/UEFIStarter/core/string.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * String functions missing from EDK 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_lib_string 9 | */ 10 | 11 | #ifndef __STRING_H 12 | #define __STRING_H 13 | 14 | #include 15 | 16 | CHAR16 *ftowcs(double value); 17 | CHAR16 *sprint_status(CHAR16 *funcname, EFI_STATUS status); 18 | 19 | BOOLEAN ctype_whitespace(char ch); 20 | UINT64 atoui64(char *str); 21 | 22 | CHAR16* EFIAPI memsprintf(const CHAR16 *fmt, ...); 23 | 24 | UINTN split_string(CHAR16 ***list, CHAR16 *input, CHAR16 separator); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /include/UEFIStarter/core/timestamp.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Timing functions 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_lib_timestamp 9 | */ 10 | 11 | #ifndef __TIMESTAMP_H 12 | #define __TIMESTAMP_H 13 | 14 | #include 15 | 16 | 17 | double timestamp_diff_seconds(UINT64 start, UINT64 end); 18 | int init_timestamps(); 19 | UINT64 get_timestamp(); 20 | UINT64 get_timestamp_ticks_per_second(); 21 | 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /include/UEFIStarter/graphics.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Functions for creating and showing graphics 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_lib_graphics 9 | */ 10 | 11 | #ifndef __GRAPHICS_H 12 | #define __GRAPHICS_H 13 | 14 | #include 15 | #include 16 | #include "core/cmdline.h" 17 | #include "core/files.h" 18 | #include "core/logger.h" 19 | 20 | #define COLOR EFI_GRAPHICS_OUTPUT_BLT_PIXEL /**< shortcut macro: indicates pixel data is intended to be used as paint color */ 21 | #define SPRITE EFI_GRAPHICS_OUTPUT_BLT_PIXEL * /**< shortcut macro: indicates pixel data is intended to be as a drawable image */ 22 | #define GFX_BUFFER EFI_GRAPHICS_OUTPUT_BLT_PIXEL * /**< shortcut macro: indicates pixel data is intended to be used as a screen buffer */ 23 | 24 | 25 | extern EFI_GRAPHICS_OUTPUT_PROTOCOL *graphics_protocol; 26 | extern EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *graphics_info; 27 | extern UINTN graphics_fs_width, graphics_fs_height; 28 | extern GFX_BUFFER graphics_fs_buffer; 29 | extern UINTN graphics_fs_pages; 30 | extern UINTN graphics_fs_pixel_count; 31 | 32 | #define ARG_MODE graphics_argument_list[0].value.uint64 /**< helper macro to access the "graphics mode" command-line argument's value */ 33 | #define ARG_VSYNC graphics_argument_list[1].value.uint64 /**< helper macro to access the "vsync mode" command-line argument's value */ 34 | #define ARG_FPS graphics_argument_list[2].value.uint64 /**< helper macro to access the "framerate limit" command-line argument's value */ 35 | #define ARG_DISPLAY graphics_argument_list[3].value.uint64 /**< helper macro to access the "display handle" command-line argument's value */ 36 | extern cmdline_argument_t graphics_argument_list[]; 37 | extern cmdline_argument_group_t graphics_arguments; 38 | 39 | 40 | /********** 41 | * General 42 | */ 43 | 44 | EFI_STATUS init_graphics(); 45 | void shutdown_graphics(); 46 | 47 | GFX_BUFFER create_graphics_fs_buffer(); 48 | void free_graphics_fs_buffer(void *addr); 49 | EFI_STATUS graphics_fs_blt(GFX_BUFFER buffer); 50 | 51 | EFI_GRAPHICS_OUTPUT_PROTOCOL *get_graphics_protocol(); //move to init and make field static? 52 | EFI_STATUS draw_filled_rect(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, UINTN x, UINTN y, UINTN width, UINTN height, COLOR *color); 53 | void print_graphics_modes(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop); 54 | EFI_STATUS set_graphics_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, int mode); 55 | EFI_STATUS query_current_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **info); 56 | void wait_vsync(); 57 | BOOLEAN limit_framerate(UINT64 *previous, UINT64 minimum_frame_ticks); 58 | 59 | typedef double trig_func(double); /**< data type for trigonometry function pointers */ 60 | void set_graphics_sin_func(trig_func *f); 61 | void set_graphics_cos_func(trig_func *f); 62 | 63 | 64 | /********* 65 | * Images 66 | */ 67 | 68 | /** data type for image data, size is dynamic */ 69 | typedef struct 70 | { 71 | UINTN memory_pages; /**< the number of memory pages allocated */ 72 | UINT32 width; /**< the image's width, in pixels */ 73 | UINT32 height; /**< the image's height, in pixels */ 74 | EFI_GRAPHICS_OUTPUT_BLT_PIXEL data[]; /**< the image's pixel data */ 75 | } image_t; 76 | 77 | /** data type for image assets */ 78 | typedef struct 79 | { 80 | image_t **image; /**< the asset's content */ 81 | CHAR16 *filename; /**< the asset's filename */ 82 | } image_asset_t; 83 | 84 | 85 | image_t *parse_ppm_image_data(file_contents_t *contents); 86 | image_t *parse_pgm_image_data(file_contents_t *contents); 87 | image_t *parse_pbm_image_data(file_contents_t *contents); 88 | 89 | image_t *load_ppm_file(CHAR16 *filename); 90 | image_t *load_pgm_file(CHAR16 *filename); 91 | image_t *load_pbm_file(CHAR16 *filename); 92 | image_t *load_netpbm_file(CHAR16 *filename); 93 | image_t *create_image(INTN width, INTN height); 94 | void free_image(image_t *image); 95 | void load_image_assets(UINTN count, image_asset_t *assets); 96 | void free_image_assets(UINTN count, image_asset_t *assets); 97 | 98 | COLOR interpolate_2px(COLOR *colors, float ratio); 99 | COLOR interpolate_4px(COLOR *corners, UINTN row_width, float x, float y); 100 | void rotate_image(SPRITE original, SPRITE rotated, INTN radius, float theta); 101 | 102 | 103 | /******** 104 | * Fonts 105 | */ 106 | 107 | /** data type for individual 8x15 font glyphs */ 108 | typedef struct 109 | { 110 | CHAR16 chr; /**< the character the glyph is for */ 111 | UINT8 data[8*15]; /**< the glyph's pixel data */ 112 | } glyph_t; 113 | 114 | /** data type for list of glyphs, size is dynamic */ 115 | typedef struct 116 | { 117 | UINT32 memory_pages; /**< the number of allocated memory pages */ 118 | UINT16 glyph_count; /**< the number of glyphs in this list */ 119 | glyph_t glyphs[]; /**< the list of glyphs */ 120 | } glyph_list_t; 121 | 122 | glyph_list_t *parse_glyphs(image_t *image, CHAR16 *text); 123 | glyph_list_t *load_font(); 124 | void draw_text(SPRITE buffer, UINTN buffer_width, glyph_list_t *glyphs, UINT32 x, UINT32 y, COLOR color, CHAR16 *text); 125 | void draw_glyph(SPRITE start, UINTN buffer_width, glyph_t *glyph, COLOR color); 126 | void free_glyphs(glyph_list_t *glyphs); 127 | 128 | 129 | #endif 130 | -------------------------------------------------------------------------------- /include/UEFIStarter/pci.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Functions for accessing PCI devices 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_lib_pci 9 | */ 10 | 11 | #ifndef __PCI_H 12 | #define __PCI_H 13 | 14 | #include 15 | #include 16 | #include 17 | #include "core/logger.h" 18 | 19 | 20 | /** data type for PCI device subclass names */ 21 | typedef struct 22 | { 23 | UINT8 subclass_code; /**< the subclass's code */ 24 | UINT16 *subclass_name; /**< the subclass's name, as UTF-16 */ 25 | } pci_subclass_name_t; 26 | 27 | /** data type for PCI device class names */ 28 | typedef struct 29 | { 30 | UINT8 class_code; /**< the class's code */ 31 | UINT16 *class_name; /**< the class's name, as UTF-16 */ 32 | pci_subclass_name_t *subclasses; /**< the class's subclasses */ 33 | } pci_class_names_t; 34 | 35 | 36 | CHAR16 *find_pci_device_name(UINT16 vendor_id, UINT16 device_id, UINT16 subvendor_id, UINT16 subdevice_id); 37 | CHAR16 *find_pci_class_name(UINT8 class_code[3]); 38 | void print_pci_devices(); 39 | void print_known_pci_classes(); 40 | EFI_PCI_IO_PROTOCOL *find_pci_device(UINT16 vendor_id, UINT16 device_id); 41 | 42 | //call before using PCI library 43 | void init_pci_lib(); 44 | 45 | //call whenever PCI library is used (currently frees device ID file buffer) 46 | void shutdown_pci_lib(); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /include/UEFIStarter/tests/asserts.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * General assertions for tests 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_tests_asserts 9 | */ 10 | 11 | #ifndef __TEST_ASSERTS_H 12 | #define __TEST_ASSERTS_H 13 | 14 | #include 15 | #include 16 | 17 | 18 | //invert next assertion check - there probably won't be a use case for this other than testing assertions themselves 19 | extern BOOLEAN invert_next_assert; 20 | 21 | //boolean 22 | 23 | BOOLEAN assert_true(BOOLEAN actual, CHAR16 *message); 24 | BOOLEAN assert_false(BOOLEAN actual, CHAR16 *message); 25 | 26 | 27 | //numbers 28 | 29 | BOOLEAN assert_intn_equals(INTN expected, INTN actual, CHAR16 *message); 30 | BOOLEAN assert_intn_greater_than_or_equal_to(INTN expected, INTN actual, CHAR16 *message); 31 | BOOLEAN assert_intn_less_than_or_equal_to(INTN expected, INTN actual, CHAR16 *message); 32 | BOOLEAN assert_intn_in_closed_interval(INTN min, INTN max, INTN actual, CHAR16 *message); 33 | 34 | BOOLEAN assert_uint64_equals(UINT64 expected, UINT64 actual, CHAR16 *message); 35 | 36 | BOOLEAN assert_double_near(double expected, double epsilon, double actual, CHAR16 *message); 37 | BOOLEAN assert_double_greater_than(double threshold, double actual, CHAR16 *message); 38 | BOOLEAN assert_double_greater_than_or_equal_to(double threshold, double actual, CHAR16 *message); 39 | BOOLEAN assert_double_less_than(double threshold, double actual, CHAR16 *message); 40 | BOOLEAN assert_double_less_than_or_equal_to(double threshold, double actual, CHAR16 *message); 41 | 42 | 43 | //pointers and compounds 44 | 45 | BOOLEAN assert_null(void *actual, CHAR16 *message); 46 | BOOLEAN assert_not_null(void *actual, CHAR16 *message); 47 | 48 | BOOLEAN assert_uint8_array(UINTN size, UINT8 *expected, UINT8 *actual, CHAR16 *message); 49 | BOOLEAN assert_wcstr_equals(CHAR16 *expected, CHAR16 *actual, CHAR16 *message); 50 | 51 | BOOLEAN assert_pixel(EFI_GRAPHICS_OUTPUT_BLT_PIXEL expected, EFI_GRAPHICS_OUTPUT_BLT_PIXEL actual, CHAR16 *message); 52 | BOOLEAN assert_pixel_values(UINT8 red, UINT8 green, UINT8 blue, UINT8 reserved, EFI_GRAPHICS_OUTPUT_BLT_PIXEL actual, CHAR16 *message); 53 | BOOLEAN assert_pixel_near(EFI_GRAPHICS_OUTPUT_BLT_PIXEL expected, INTN epsilon, EFI_GRAPHICS_OUTPUT_BLT_PIXEL actual, CHAR16 *message); 54 | BOOLEAN assert_pixel_values_near(UINT8 red, UINT8 green, UINT8 blue, UINT8 reserved, INTN epsilon, EFI_GRAPHICS_OUTPUT_BLT_PIXEL actual, CHAR16 *message); 55 | 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /include/UEFIStarter/tests/graphics.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Assertions and utilities for graphics tests 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_tests_asserts 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | 15 | /** shortcut macro for pixel's size in bytes */ 16 | #define BYTES_PER_PIXEL sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL) 17 | 18 | /** default background color for color tests */ 19 | #define DIFFTEST_DEFAULT_BACKGROUND_UINT32 0x11223344 20 | 21 | 22 | /** data type for bounding boxes */ 23 | typedef struct 24 | { 25 | INTN left; /**< the bounding box's left coordinate */ 26 | INTN top; /**< the bounding box's top coordinate */ 27 | INTN right; /**< the bounding box's right coordinate */ 28 | INTN bottom; /**< the bounding box's bottom coordinate */ 29 | } bounding_box_t; 30 | 31 | /** data type for image change tests */ 32 | typedef struct 33 | { 34 | bounding_box_t box; /**< the expected bounding box for changes */ 35 | INTN image_width; /**< the images' width */ 36 | INTN image_height; /**< the images' height */ 37 | image_t *before; /**< the "before" image */ 38 | image_t *after; /**< the "after" image */ 39 | } graphics_difftest_t; 40 | 41 | 42 | 43 | void init_graphics_difftest_ex(graphics_difftest_t *difftest, INTN width, INTN height, UINT32 bgcol); 44 | void init_graphics_difftest(graphics_difftest_t *difftest, INTN width, INTN height); 45 | 46 | void find_bounding_box_for_changes(graphics_difftest_t *difftest); 47 | void assert_box_equals(bounding_box_t *box, INTN left, INTN top, INTN right, INTN bottom, CHAR16 *message); 48 | void assert_differences_within_box(graphics_difftest_t *difftest, INTN min_width, INTN max_width, INTN min_height, INTN max_height, CHAR16 *message); 49 | 50 | void reset_bounding_box(bounding_box_t *box); 51 | void reset_graphics_difftest(graphics_difftest_t *difftest); 52 | 53 | void destroy_graphics_difftest(graphics_difftest_t *difftest); 54 | 55 | -------------------------------------------------------------------------------- /include/UEFIStarter/tests/output.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Presentation logic for tests 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_tests_runner 9 | */ 10 | 11 | #ifndef __TEST_OUTPUT_H 12 | #define __TEST_OUTPUT_H 13 | 14 | #include 15 | #include "types.h" 16 | 17 | 18 | void print_test_group_start(CHAR16 *name); 19 | void print_group_result(test_results_t *results); 20 | void print_test_group_end(); 21 | 22 | void print_individual_test_start(CHAR16 *description); 23 | void print_individual_result(test_results_t *results); 24 | void print_assert_counts(INT64 fails, INT64 asserts); 25 | void print_test_result_summary(test_results_t *results); 26 | void print_assertion(BOOLEAN success, CHAR16 *description, CHAR16 *message); 27 | 28 | void debug_print_verbosity(); 29 | void debug_print_results(test_results_t *results); 30 | 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /include/UEFIStarter/tests/tests.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Basis for test suites. 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_tests_runner 9 | */ 10 | 11 | #ifndef __TESTS_H 12 | #define __TESTS_H 13 | 14 | #include 15 | #include "types.h" 16 | #include "asserts.h" 17 | #include "output.h" 18 | #include 19 | 20 | 21 | extern test_results_t individual_test_results; 22 | extern test_results_t global_test_results; 23 | extern test_verbosity_t test_verbosity; 24 | 25 | 26 | /** 27 | * Runs a test group. 28 | * This gets created automatically (in generated/runner.c in the test suite's directory). 29 | */ 30 | void run_tests(); 31 | 32 | void run_test(void (*func)(), CHAR16 *description); 33 | void run_group(BOOLEAN (*func)()); 34 | BOOLEAN is_skipped_test(CHAR16 *name); 35 | 36 | /** 37 | * Helper macro to start a testgroup. 38 | * 39 | * \param NAME the test group's name 40 | */ 41 | #define INIT_TESTGROUP(NAME) \ 42 | if(is_skipped_test(NAME)) \ 43 | { \ 44 | global_test_results.skipped_count++; \ 45 | return FALSE; \ 46 | } \ 47 | print_test_group_start(NAME); 48 | 49 | /** 50 | * Helper macro to end a testgroup. 51 | */ 52 | #define FINISH_TESTGROUP() return TRUE; 53 | 54 | /** 55 | * Helper macro to run a test. 56 | * 57 | * \param FUNC the test's function to execute 58 | * \param DESC the test's description 59 | */ 60 | #define RUN_TEST(FUNC,DESC) run_test(FUNC,DESC); 61 | 62 | /** Helper macro to mark a test as incomplete */ 63 | #define mark_test_incomplete() individual_test_results.incomplete_count++; 64 | 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /include/UEFIStarter/tests/types.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Common types for tests 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_tests_runner 9 | */ 10 | 11 | #ifndef __TEST_TYPES_H 12 | #define __TEST_TYPES_H 13 | 14 | #include 15 | 16 | 17 | /** data type for test verbosity configuration */ 18 | typedef struct 19 | { 20 | UINTN individual_groups:1; /**< whether individual test groups should be shown */ 21 | UINTN individual_tests:1; /**< whether individual tests should be shown */ 22 | UINTN one_char_per_test:1; /**< whether there should be a one character summary per test */ 23 | UINTN multiple_lines_per_test:1; /**< whether test output should span multiple lines */ 24 | UINTN assertion_counts:1; /**< whether the number of assertions should be shown */ 25 | UINTN individual_assertions:1; /**< whether individual assertions should be shown */ 26 | UINTN summary_statistics:1; /**< whether test result statistics should be shown */ 27 | } test_verbosity_t; 28 | 29 | /** data type for test outcome status */ 30 | typedef enum 31 | { 32 | FAILURE=0, /**< the test failed */ 33 | INCOMPLETE, /**< the test was incomplete */ 34 | SUCCESS /**< the test succeeded */ 35 | } test_outcome; 36 | 37 | /** data type for test results */ 38 | typedef struct 39 | { 40 | INT64 assert_count; /**< the total number of assertions */ 41 | INT64 assert_fails; /**< the number of failed assertions */ 42 | INT64 successful_test_count; /**< the number of succeeded tests */ 43 | INT64 failed_test_count; /**< the number of failed tests */ 44 | INT64 incomplete_count; /**< the number of incomplete tests */ 45 | INT64 skipped_count; /**< the number of skipped tests */ 46 | test_outcome outcome; /**< the test outcome */ 47 | } test_results_t; 48 | 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /library/ac97.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = ac97 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f00016 5 | MODULE_TYPE = UEFI_DRIVER 6 | VERSION_STRING = 1.0 7 | LIBRARY_CLASS = UEFIStarterAC97|UEFI_APPLICATION UEFI_DRIVER DXE_RUNTIME_DRIVER DXE_DRIVER 8 | 9 | [Sources] 10 | ac97.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | UEFIStarter/UEFIStarter.dec 15 | 16 | [LibraryClasses] 17 | UefiLib 18 | UefiBootServicesTableLib 19 | IoLib 20 | PciLib 21 | UEFIStarterCore 22 | 23 | [Guids] 24 | 25 | [Ppis] 26 | 27 | [Protocols] 28 | 29 | [FeaturePcd] 30 | 31 | [Pcd] 32 | 33 | -------------------------------------------------------------------------------- /library/core.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = core 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f00019 5 | MODULE_TYPE = UEFI_DRIVER 6 | VERSION_STRING = 1.0 7 | LIBRARY_CLASS = UEFIStarterCore|UEFI_APPLICATION UEFI_DRIVER DXE_RUNTIME_DRIVER DXE_DRIVER 8 | 9 | [Sources] 10 | core/cmdline.c 11 | core/console.c 12 | core/files.c 13 | core/timestamp.c 14 | core/string.c 15 | core/memory.c 16 | core/logger.c 17 | 18 | [Packages] 19 | MdePkg/MdePkg.dec 20 | UEFIStarter/UEFIStarter.dec 21 | 22 | [LibraryClasses] 23 | UefiLib 24 | UefiBootServicesTableLib 25 | IoLib 26 | -------------------------------------------------------------------------------- /library/core/cmdline.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = cmdline 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f00002 5 | MODULE_TYPE = UEFI_DRIVER 6 | VERSION_STRING = 1.0 7 | LIBRARY_CLASS = cmdline|UEFI_APPLICATION UEFI_DRIVER DXE_RUNTIME_DRIVER DXE_DRIVER 8 | 9 | [Sources] 10 | cmdline.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | UEFIStarter/UEFIStarter.dec 15 | 16 | [LibraryClasses] 17 | UefiLib 18 | string 19 | logger 20 | 21 | [Guids] 22 | 23 | [Ppis] 24 | 25 | [Protocols] 26 | 27 | [FeaturePcd] 28 | 29 | [Pcd] 30 | 31 | -------------------------------------------------------------------------------- /library/core/console.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Console initialization and I/O helpers 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_lib_console 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | /** 21 | * Prints the list of available console modes. 22 | * Keep in mind that UEFI requires all systems to list modes 0 and 1, but only 0 and any listed 2+ have to work. 23 | */ 24 | void print_console_modes() 25 | { 26 | int tc; 27 | EFI_STATUS result; 28 | UINTN cols, rows; 29 | 30 | Print(L"number of console modes: %d\n",gST->ConOut->Mode->MaxMode); 31 | for(tc=0;tcConOut->Mode->MaxMode;tc++) 32 | { 33 | result=gST->ConOut->QueryMode(gST->ConOut,tc,&cols,&rows); 34 | if(result!=EFI_SUCCESS) 35 | Print(L" %02d: (error)",tc); 36 | else 37 | Print(L" %02d: %3dx%3d",tc,cols,rows); 38 | if(tc%5==4) 39 | Print(L"\n"); 40 | } 41 | if(tc%5!=4) 42 | Print(L"\n"); 43 | } 44 | 45 | /** 46 | * Sets a new text mode. 47 | * Keep in mind that the graphics mode takes precedence: if, for example, you set graphics mode 640x480 and then change 48 | * to a text mode that would require 960x700 pixels you'll get the requested console size but the window will remain 49 | * at 640x480! 50 | * 51 | * \param requested_mode the graphics mode to set 52 | * \return an EFI status code 53 | */ 54 | EFI_STATUS set_console_mode(unsigned int requested_mode) 55 | { 56 | EFI_STATUS result; 57 | 58 | if(requested_mode > gST->ConOut->Mode->MaxMode-1) 59 | { 60 | LOG.error(L"requested mode %d, but %d is max",requested_mode,gST->ConOut->Mode->MaxMode-1); 61 | return EFI_UNSUPPORTED; 62 | } 63 | if(gST->ConOut->Mode->Mode==requested_mode) 64 | { 65 | LOG.debug(L"already at console mode %d",requested_mode); 66 | return EFI_SUCCESS; 67 | } 68 | 69 | result=gST->ConOut->SetMode(gST->ConOut,requested_mode); 70 | if(result!=EFI_SUCCESS) 71 | return result; 72 | 73 | LOG.debug(L"switched to console mode %d",requested_mode); 74 | return result; 75 | } 76 | 77 | /** 78 | * Prints a text with the given color. 79 | * Takes the same format strings as EDK's print functions. 80 | * 81 | * \param color the color value to print with, as a 4-bit number 82 | * \param fmt the format string, as UTF-16 83 | * \param ... any parameters to the print function 84 | */ 85 | void EFIAPI color_print(UINTN color, CHAR16 *fmt, ...) 86 | { 87 | CHAR16 *str; 88 | UINTN attr=gST->ConOut->Mode->Attribute; 89 | VA_LIST args; 90 | 91 | VA_START(args,fmt); 92 | str=CatVSPrint(NULL,fmt,args); 93 | VA_END(args); 94 | 95 | gST->ConOut->SetAttribute(gST->ConOut,(attr&0xFFFFFFF0)+(color&0x0F)); 96 | Print(str); 97 | gST->ConOut->SetAttribute(gST->ConOut,attr); 98 | 99 | FreePool(str); 100 | } 101 | 102 | /** 103 | * Empties the keyboard input buffer. 104 | * Use this e.g. before and after waiting for the much sought after "any" key to prevent previous keystrokes from 105 | * canceling the wait and the new keystrokes from triggering later input events. 106 | */ 107 | void drain_key_buffer() 108 | { 109 | EFI_INPUT_KEY key; 110 | EFI_STATUS result; 111 | UINTN tc; 112 | 113 | for(tc=0;tc<50;tc++) 114 | { 115 | result=gST->ConIn->ReadKeyStroke(gST->ConIn,&key); 116 | if(result!=EFI_SUCCESS) 117 | return; 118 | } 119 | } 120 | 121 | /** 122 | * Waits for any keystroke. 123 | * Note that if you're in a virtualized environment some keystrokes will not trigger this, depending on your input 124 | * method, terminal emulation, console multiplexers and so on. This is most apparent with the Escape key: under some 125 | * circumstances it just won't register as an individual keystroke. 126 | */ 127 | void wait_for_key() 128 | { 129 | UINTN dummy; 130 | 131 | gST->BootServices->WaitForEvent(1,&gST->ConIn->WaitForKey,&dummy); 132 | drain_key_buffer(); 133 | } 134 | 135 | 136 | /** 137 | * Initializes an UEFIStarter application. 138 | * Call this as early as possible to gain access to the memory tracking, logging and other features. 139 | * 140 | * If this function returns error/warning codes the exact code should tell what went wrong; usually there are log 141 | * entries as well. 142 | * 143 | * Keep in mind that the RV_HELP return code isn't an error per se, it's just to indicate that the user 144 | * wanted (and received) the help screen. 145 | * 146 | * \param argc the number of command-line arguments 147 | * \param argv the list of command-line arguments, as UTF-16 148 | * \param arg_group_count the number of command-line argument groups 149 | * \param ... the list of command-line argument groups (cmdline_argument_group_t *) 150 | * \return an EFI status code: EFI_SUCCESS if everything went well and application can continue executing 151 | */ 152 | EFI_STATUS EFIAPI init(INTN argc, CHAR16 **argv, UINTN arg_group_count, ...) 153 | { 154 | EFI_STATUS result; 155 | VA_LIST args; 156 | 157 | // InitializeLib(image, est); 158 | // _uefi_image=image; 159 | reset_memory_tracking(); 160 | init_tracking_memory(); 161 | reset_logger_entry_counts(); 162 | VA_START(args,arg_group_count); 163 | result=parse_parameters(argc,argv,arg_group_count,args); 164 | VA_END(args); 165 | if(result!=EFI_SUCCESS) 166 | return result; 167 | 168 | if(set_console_mode(gST->ConOut->Mode->MaxMode-1)!=EFI_SUCCESS) /** \TODO could add cmdline arg */ 169 | print_console_modes(); 170 | 171 | return EFI_SUCCESS; 172 | } 173 | 174 | /** 175 | * Shuts the UEFIStarter internals down. 176 | * This will stop the memory tracker, upon which any unfreed memory gets reported. 177 | * 178 | * Unfreed memory will continue to use up memory in the UEFI environment even after the application stopped. If you 179 | * didn't keep the pages allocated on purpose you'll probably want to free them before calling this. 180 | * 181 | * If you're keeping the pages allocated on purpose (e.g. because you need to mark memory as reserved) you can disable 182 | * tracking (and thus reporting) for them by using allocate_pages_ex() with track=FALSE (if you make it accessible with 183 | * "extern"). Alternatively you can just use UEFI's AllocatePages() function, it doesn't track allocated pages at all. 184 | */ 185 | void shutdown() 186 | { 187 | stop_tracking_memory(); 188 | } 189 | -------------------------------------------------------------------------------- /library/core/console.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = console 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f00003 5 | MODULE_TYPE = UEFI_DRIVER 6 | VERSION_STRING = 1.0 7 | LIBRARY_CLASS = console|UEFI_APPLICATION UEFI_DRIVER DXE_RUNTIME_DRIVER DXE_DRIVER 8 | 9 | [Sources] 10 | console.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | UEFIStarter/UEFIStarter.dec 15 | 16 | [LibraryClasses] 17 | UefiLib 18 | UefiBootServicesTableLib 19 | memory 20 | 21 | [Guids] 22 | 23 | [Ppis] 24 | 25 | [Protocols] 26 | 27 | [FeaturePcd] 28 | 29 | [Pcd] 30 | 31 | -------------------------------------------------------------------------------- /library/core/files.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * File handling functions 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_lib_files 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | /** 20 | * Opens a handle for the first filesystem root. 21 | * In the UEFI shell, this will most likely be FS0:. 22 | * 23 | * \return the root volume's handle on success, NULL otherwise 24 | */ 25 | EFI_FILE_HANDLE find_root_volume() 26 | { 27 | EFI_FILE_IO_INTERFACE *protocol; 28 | EFI_FILE_HANDLE root; 29 | EFI_GUID guid=SIMPLE_FILE_SYSTEM_PROTOCOL; 30 | EFI_HANDLE handles[100]; 31 | UINTN handles_size=100*sizeof(EFI_HANDLE); 32 | UINTN handle_count; 33 | 34 | if(gST->BootServices->LocateHandle(ByProtocol,&guid,NULL,&handles_size,(void**)&handles)!=EFI_SUCCESS) 35 | return NULL; 36 | handle_count=handles_size/sizeof(EFI_HANDLE); 37 | LOG.debug(L"handles size: %d bytes (%d entries)",handles_size,handle_count); 38 | 39 | if(gST->BootServices->OpenProtocol(handles[0],&guid,(void **)&protocol,gImageHandle,NULL,EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL)!=EFI_SUCCESS) 40 | return NULL; 41 | if(protocol->OpenVolume(protocol,&root)!=EFI_SUCCESS) 42 | return NULL; 43 | 44 | return root; 45 | } 46 | 47 | /** 48 | * Opens a file handle, if the given file exists. 49 | * This assumes the file is on the first root volume (usually FS0:). 50 | * 51 | * \param pathname the file's full path within the volume, e.g. "\\startup.nsh" to get the startup script. 52 | * \return a handle to the file on success, NULL otherwise 53 | */ 54 | EFI_FILE_HANDLE find_file(CHAR16 *pathname) 55 | { 56 | EFI_FILE_HANDLE root, file; 57 | 58 | root=find_root_volume(); 59 | if(!root) 60 | return NULL; 61 | LOG.trace(L"found root volume, looking for %s...",pathname); 62 | if(root->Open(root,&file,pathname,EFI_FILE_MODE_READ,0)!=EFI_SUCCESS) 63 | return NULL; 64 | LOG.trace(L"found requested file, closing..."); 65 | root->Close(root); 66 | return file; 67 | } 68 | 69 | /** 70 | * Reads a file's contents. 71 | * If you just want to read files you'll probably want to use this function: it performs all the UEFI overhead for you 72 | * already. All you have to do is free the returned pointer's memory pages when you're done. 73 | * 74 | * \param filename the file's full path within the volume, e.g. "\\startup.nsh" to get the startup script. 75 | * \return a pointer to the file content descriptor, or NULL on error 76 | */ 77 | file_contents_t *get_file_contents(CHAR16 *filename) 78 | { 79 | EFI_FILE_HANDLE file; 80 | UINTN bufsize=SIZE_OF_EFI_FILE_INFO+200; 81 | char buffer[bufsize]; 82 | EFI_FILE_INFO *info=(EFI_FILE_INFO *)buffer; 83 | EFI_GUID info_guid=EFI_FILE_INFO_ID; 84 | UINTN pages; 85 | file_contents_t *file_contents=NULL; 86 | 87 | file=find_file(filename); 88 | if(!file) 89 | return NULL; 90 | 91 | if(file->GetInfo(file,&info_guid,&bufsize,info)!=EFI_SUCCESS) 92 | return NULL; 93 | LOG.trace(L"filename: %s (%ld bytes)",info->FileName,info->FileSize); 94 | 95 | pages=(info->FileSize+sizeof(file_contents_t))/4096+1; 96 | if((file_contents=allocate_pages(pages))==NULL) 97 | return NULL; 98 | 99 | file_contents->memory_pages=pages; 100 | file_contents->data_length=info->FileSize; 101 | bufsize=pages*4096; 102 | 103 | /** \TODO start a list of pitfalls, e.g. this: bufsize was too small (unsigned int instead of UINTN), so this call 104 | changed the last declared uninitialized variable in this function */ 105 | if(file->Read(file,&bufsize,file_contents->data)!=EFI_SUCCESS) 106 | return NULL; 107 | 108 | file->Close(file); 109 | return file_contents; 110 | } 111 | -------------------------------------------------------------------------------- /library/core/files.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = files 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f00008 5 | MODULE_TYPE = UEFI_DRIVER 6 | VERSION_STRING = 1.0 7 | LIBRARY_CLASS = files|UEFI_APPLICATION UEFI_DRIVER DXE_RUNTIME_DRIVER DXE_DRIVER 8 | 9 | [Sources] 10 | files.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | UEFIStarter/UEFIStarter.dec 15 | 16 | [LibraryClasses] 17 | UefiLib 18 | UefiBootServicesTableLib 19 | IoLib 20 | logger 21 | memory 22 | 23 | [Guids] 24 | 25 | [Ppis] 26 | 27 | [Protocols] 28 | 29 | [FeaturePcd] 30 | 31 | [Pcd] 32 | 33 | -------------------------------------------------------------------------------- /library/core/logger.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Logging facility, supports verbosity levels 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_lib_logger 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | /** list of log level's printable names */ 17 | const CHAR16 *logger_level_names[]={L"",L"ERROR",L"WARN",L"INFO",L"DEBUG",L"TRACE"}; 18 | 19 | /** current log level; anything higher that won't be logged */ 20 | static int logging_threshold=TRACE; 21 | 22 | /** numbers of logged messages for each log level */ 23 | static UINTN logger_entry_counts[TRACE+1]; //technically entry 0 (OFF) will be unused, but it's probably wiser to keep indexing simple to avoid bugs 24 | 25 | 26 | /** 27 | * Fetches the current log level. 28 | * 29 | * \return the current log level 30 | */ 31 | LOGLEVEL get_log_level() 32 | { 33 | return logging_threshold; 34 | } 35 | 36 | /** 37 | * Sets a new log level. 38 | * You can disable logging by calling this with log level "OFF". 39 | * 40 | * \param level the new log level to use 41 | * \return the previous log level 42 | */ 43 | LOGLEVEL set_log_level(LOGLEVEL level) 44 | { 45 | LOGLEVEL prev=logging_threshold; 46 | logging_threshold=level; 47 | return prev; 48 | } 49 | 50 | /** 51 | * Default log writer: prints to text console. 52 | * 53 | * \param level the log level to log at 54 | * \param msg the log message, as UTF-16 55 | */ 56 | void log_print(LOGLEVEL level, CHAR16 *msg) 57 | { 58 | Print(L"%s: %s\n",logger_level_names[level],msg); 59 | } 60 | 61 | /** pointer to the current log writer */ 62 | logger_print_function_t *logger_print_func=log_print; 63 | 64 | /** 65 | * internal: formats log message and calls log writer with final string to log. 66 | * This function uses the same format strings as the Print() functions. 67 | * 68 | * \param level the log level to log at 69 | * \param message the message's format string 70 | * \param args the vararg list of parameters matching matching the format string's placeholder 71 | */ 72 | static void _logger_function_va(LOGLEVEL level, UINT16 *message, VA_LIST args) 73 | { 74 | CHAR16 *msg; 75 | 76 | logger_entry_counts[level]++; 77 | if(level>logging_threshold) 78 | return; 79 | 80 | msg=CatVSPrint(NULL,message,args); 81 | logger_print_func(level,msg); 82 | FreePool(msg); 83 | } 84 | 85 | /** 86 | * helper macro for quickly defining a callable logging function 87 | * 88 | * \param NAME the logging function's name 89 | * \param LEVEL the log level to log at 90 | */ 91 | #define LOGGER_FORWARD_FUNC(NAME,LEVEL) static void EFIAPI NAME(CHAR16 *fmt, ...) \ 92 | { \ 93 | VA_LIST args; \ 94 | VA_START(args,fmt); \ 95 | _logger_function_va(LEVEL,fmt,args); \ 96 | VA_END(args); \ 97 | } 98 | 99 | /** 100 | * internal: forwards TRACE messages to internal handler 101 | * \param fmt the log entry's format string 102 | * \param ... any additional parameters to be formatted 103 | */ 104 | LOGGER_FORWARD_FUNC(_trace,TRACE); 105 | 106 | /** 107 | * internal: forwards DEBUG messages to internal handler 108 | * \param fmt the log entry's format string 109 | * \param ... any additional parameters to be formatted 110 | */ 111 | LOGGER_FORWARD_FUNC(_debug,DEBUG); 112 | 113 | /** 114 | * internal: forwards INFO messages to internal handler 115 | * \param fmt the log entry's format string 116 | * \param ... any additional parameters to be formatted 117 | */ 118 | LOGGER_FORWARD_FUNC(_info,INFO); 119 | 120 | /** 121 | * internal: forwards WARN messages to internal handler 122 | * \param fmt the log entry's format string 123 | * \param ... any additional parameters to be formatted 124 | */ 125 | LOGGER_FORWARD_FUNC(_warn,WARN); 126 | 127 | /** 128 | * internal: forwards ERROR messages to internal handler 129 | * \param fmt the log entry's format string 130 | * \param ... any additional parameters to be formatted 131 | */ 132 | LOGGER_FORWARD_FUNC(_error,ERROR); 133 | 134 | /** 135 | * The global logging facility. 136 | * 137 | * This contains function pointers for each log level, so users can call e.g. LOG.debug() to log a debug message. 138 | */ 139 | const loggers_t LOG={ 140 | .trace=_trace, 141 | .debug=_debug, 142 | .info=_info, 143 | .warn=_warn, 144 | .error=_error 145 | }; 146 | 147 | /** 148 | * Resets all log levels' message counts. 149 | * Used e.g. by the test framework to detect which tests generated errors. 150 | */ 151 | void reset_logger_entry_counts() 152 | { 153 | unsigned int tc=0; 154 | for(tc=OFF;tc<=TRACE;tc++) 155 | logger_entry_counts[tc]=0; 156 | } 157 | 158 | /** 159 | * Gets a log level's message count. 160 | * 161 | * \param level the log level to get the count for 162 | * \return the number of logged messages at the given level since the last reset 163 | */ 164 | UINTN get_logger_entry_count(LOGLEVEL level) 165 | { 166 | return logger_entry_counts[level]; 167 | } 168 | 169 | /** 170 | * Immediately halts the UEFI environment. 171 | * 172 | * Use this to e.g. debug issues in graphical animations where it's vital to stop everything before the target gets 173 | * painted over. 174 | * 175 | * Technically the emitted interrupt 3 is usually used for debuggers but the UEFI specification requires INT 3 to be 176 | * unassigned by default, thus halting everything. If you use this function with a debugger attached this will probably 177 | * trigger the debugger instead. 178 | */ 179 | void kill() 180 | { 181 | asm volatile ("int $3"); 182 | } 183 | 184 | /** 185 | * Sets a new log writer. 186 | * Use this to replace the built-in text mode writer, e.g. to output log messages in a graphical environment 187 | * 188 | * \param func the new log writer function 189 | * \return the previous log writer function 190 | */ 191 | logger_print_function_t *set_logger_function(logger_print_function_t *func) 192 | { 193 | logger_print_function_t *previous=logger_print_func; 194 | logger_print_func=func; 195 | return previous; 196 | } 197 | -------------------------------------------------------------------------------- /library/core/logger.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = logger 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f00004 5 | MODULE_TYPE = UEFI_DRIVER 6 | VERSION_STRING = 1.0 7 | LIBRARY_CLASS = logger|UEFI_APPLICATION UEFI_DRIVER DXE_RUNTIME_DRIVER DXE_DRIVER 8 | 9 | [Sources] 10 | logger.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | UEFIStarter/UEFIStarter.dec 15 | 16 | [LibraryClasses] 17 | UefiLib 18 | 19 | [Guids] 20 | 21 | [Ppis] 22 | 23 | [Protocols] 24 | 25 | [FeaturePcd] 26 | 27 | [Pcd] 28 | 29 | -------------------------------------------------------------------------------- /library/core/memory.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = memory 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f00005 5 | MODULE_TYPE = UEFI_DRIVER 6 | VERSION_STRING = 1.0 7 | LIBRARY_CLASS = memory|UEFI_APPLICATION UEFI_DRIVER DXE_RUNTIME_DRIVER DXE_DRIVER 8 | 9 | [Sources] 10 | memory.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | UEFIStarter/UEFIStarter.dec 15 | 16 | [LibraryClasses] 17 | UefiLib 18 | UefiBootServicesTableLib 19 | logger 20 | 21 | [Guids] 22 | 23 | [Ppis] 24 | 25 | [Protocols] 26 | 27 | [FeaturePcd] 28 | 29 | [Pcd] 30 | 31 | -------------------------------------------------------------------------------- /library/core/string.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * String functions missing from EDK 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_lib_string 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | //this will generate a lot of TRACE output 18 | //#define DEBUG_STRSTR 19 | 20 | /** the lowest value ftowcs() can parse */ 21 | #define FTOWCS_MIN_VALUE -1000000000.0 22 | 23 | /** the highest value ftowcs() can parse */ 24 | #define FTOWCS_MAX_VALUE 1000000000.0 25 | 26 | 27 | /** 28 | * Converts a double value into a UTF-16 string with 3 decimals. 29 | * Will only convert numbers between FTOWCS_MIN_VALUE and FTOWCS_MAX_VALUE. 30 | * 31 | * \param value the number to convert 32 | * \return the number as a UTF-16 string, or NULL on error 33 | * 34 | * \TODO make number of decimals configurable 35 | */ 36 | CHAR16 *ftowcs(double value) 37 | { 38 | BOOLEAN negative=FALSE; 39 | INT64 left; 40 | UINT64 right; 41 | 42 | if(valueFTOWCS_MAX_VALUE) 48 | { 49 | LOG.error(L"double value too high to convert"); 50 | return NULL; 51 | } 52 | 53 | LOG.trace(L" value<0: %s",value<0?L"yes":L"no"); 54 | if(value<0) 55 | { 56 | negative=TRUE; 57 | value=-value; 58 | } 59 | left=(INT64)value; 60 | right=(UINT64)((value-left)*1000); 61 | LOG.trace(L" left=%ld, right=%ld",left,right); 62 | if(right%10>=5) 63 | { 64 | right+=5; 65 | LOG.trace(L" left=%ld, right=%ld (right value needs rounding up, increased it by 5)",left,right); 66 | if(right>=1000) 67 | { 68 | right-=1000; 69 | left+=1; 70 | LOG.trace(L" left=%ld, right=%ld (right value wrapped around, decreased it and increased left value by 1)",left,right); 71 | } 72 | } 73 | right/=10; 74 | return memsprintf(L"%s%ld.%02ld",negative?L"-":L"",left,right); 75 | } 76 | 77 | /** 78 | * Converts an ASCII integer string to UINT64 79 | * 80 | * \param str the integer string to convert, as ASCII 81 | * \return the UINT64 integer, or -1 on error 82 | */ 83 | UINT64 atoui64(char *str) 84 | { 85 | UINT64 rv=0; 86 | UINTN tc; 87 | 88 | if(str==NULL || str[0]==0) 89 | { 90 | LOG.error(L"atoui64: cannot parse NULL or empty string"); 91 | return -1; 92 | } 93 | LOG.debug(L"atoui64: parsing \"%a\"",str); 94 | for(tc=0;tc<20;tc++) 95 | { 96 | LOG.trace(L"atoui64: tc=%d, current=%ld",tc,rv); 97 | if(str[tc]==0) 98 | break; 99 | if(str[tc]<'0' || str[tc]>'9') 100 | { 101 | LOG.error(L"invalid string to convert: %a, failing char:0x%02X",str,str[tc]); 102 | return -1; 103 | } 104 | rv=rv*10+str[tc]-'0'; 105 | } 106 | return rv; 107 | } 108 | 109 | /** 110 | * Checks if an ASCII character is a whitespace. 111 | * 112 | * \param ch the ASCII character to check 113 | * \return whether it's a whitespace character 114 | */ 115 | BOOLEAN ctype_whitespace(char ch) 116 | { 117 | return ch=='\t' || ch=='\n' || ch=='\r' || ch==' '; 118 | } 119 | 120 | /** 121 | * Helper function to format an EFI function call result into a human-readable string. 122 | * 123 | * \param function the function name, as UTF-16 124 | * \param code the resulting EFI status code 125 | * \return the pretty-printed UTF-16 string 126 | */ 127 | CHAR16 *sprint_status(CHAR16 *function, EFI_STATUS code) 128 | { 129 | return memsprintf(L"%s() returned status %d (%r)",function,code,code); 130 | } 131 | 132 | /** 133 | * Prints a nicely formatted EFI function call result. 134 | * 135 | * \param function the function name, as UTF-16 136 | * \param code the resulting EFI status code 137 | * 138 | * \TODO check if this function is still required 139 | */ 140 | void print_status(CHAR16 *function, EFI_STATUS code) 141 | { 142 | Print(L"%s\n",sprint_status(function,code)); 143 | } 144 | 145 | 146 | /** 147 | * sprintf() replacement that tracks allocated pool memory for automatic cleanup. 148 | * This function uses the same format codes as Print(). 149 | * 150 | * \param fmt the format string, as UTF-16 151 | * \param ... any additional parameters matching the format string placeholders 152 | * 153 | * \return a pointer to the formatted UTF-16 string 154 | */ 155 | CHAR16* EFIAPI memsprintf(const CHAR16 *fmt, ...) 156 | { 157 | CHAR16 *str; 158 | VA_LIST args; 159 | 160 | if(fmt==NULL) 161 | LOG.warn(L"memsprintf(): format string should not be NULL"); 162 | 163 | VA_START(args,fmt); 164 | str=CatVSPrint(NULL,fmt,args); 165 | VA_END(args); 166 | 167 | track_pool_memory(str); 168 | 169 | return str; 170 | } 171 | 172 | 173 | /** 174 | * Splits a string by a separator character into an array of strings. 175 | * 176 | * \param list the address where to write the resulting array of strings to 177 | * \param input the text to split 178 | * \param separator the character to split the text by 179 | * \return the number of array entries 180 | */ 181 | UINTN split_string(CHAR16 ***list, CHAR16 *input, CHAR16 separator) 182 | { 183 | UINTN count=1; 184 | UINTN length, tc; 185 | CHAR16 **ptrs; 186 | 187 | if(list==NULL) 188 | { 189 | LOG.error(L"list pointer can't be NULL"); 190 | return 0; 191 | } 192 | 193 | if(input==NULL) 194 | { 195 | *list=NULL; 196 | return 0; 197 | } 198 | 199 | length=StrLen(input); 200 | for(tc=0;tc 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | //when defined the RDTSC assembly call details will be checked and logged. 18 | //x86 specs shouldn't require this, so to increase timestamp accuracy this isn't compiled in by default. 19 | //#define DEBUG_TIMESTAMPS 20 | 21 | /** internal storage for the number of timestamp ticks per second */ 22 | UINT64 _rdtsc_ticks_per_second=0; 23 | 24 | 25 | /** 26 | * Initializes the timestamp features. 27 | * This will take ~2 seconds at current settings. This function takes the naive approach and simply measures how many 28 | * ticks pass in 2 seconds. This tick frequency will be used later when converting ticks to seconds. 29 | * 30 | * Make sure to call this function before you attempt to determine elapsed wallclock time. 31 | * 32 | * \return 0 on success, an error code otherwise. 33 | */ 34 | int init_timestamps() 35 | { 36 | EFI_STATUS result; 37 | EFI_EVENT event; 38 | UINT64 start, end, diff; 39 | UINTN index; 40 | 41 | result=gST->BootServices->CreateEvent(EVT_TIMER,TPL_CALLBACK,NULL,NULL,&event); 42 | ON_ERROR_RETURN(L"CreateEvent",-1); 43 | result=gST->BootServices->SetTimer(event,TimerPeriodic,1000*1000*10); 44 | ON_ERROR_RETURN(L"CreateEvent",-2); 45 | result=gST->BootServices->WaitForEvent(1,&event,&index); 46 | ON_ERROR_RETURN(L"CreateEvent",-3); 47 | 48 | start=get_timestamp(); 49 | 50 | result=gST->BootServices->WaitForEvent(1,&event,&index); 51 | ON_ERROR_RETURN(L"CreateEvent",-4); 52 | 53 | end=get_timestamp(); 54 | diff=end-start; 55 | _rdtsc_ticks_per_second=diff; 56 | 57 | LOG.trace(L"start timestamp: %lX (%ld)",start,start); 58 | LOG.trace(L"end timestamp: %lX (%ld)",end,end); 59 | LOG.trace(L"timestamp ticks per second: %lX (%s GHz)",diff,ftowcs(((double)diff)/1000000000)); 60 | 61 | return 0; 62 | } 63 | 64 | /** 65 | * Fetches and returns the current timestamp. 66 | * 67 | * \return the current timestamp 68 | * 69 | * \XXX this could be made inline for improved timing accuracy 70 | */ 71 | UINT64 get_timestamp() 72 | { 73 | UINT64 rdx,rax; 74 | asm volatile ("rdtsc" 75 | :"=d" (rdx), "=a" (rax)); 76 | #ifdef DEBUG_TIMESTAMPS 77 | LOG.trace(L"rdx=%lX, rax=%lX",rdx,rax); 78 | if((rax>0xFFFFFFFF) || (rdx>0xFFFFFFFF)) 79 | { 80 | LOG.error(L"expected 32bit values in rax and rdx, got a value exceeding that"); 81 | return 0; 82 | } 83 | #endif 84 | return (rdx<<32)+rax; 85 | } 86 | 87 | /** 88 | * Calculates the number of seconds between two timestamps. 89 | * This will only work if init_timestamps() has been called first. 90 | * 91 | * \param start the start of the timestamp interval 92 | * \param end the end of the timestamp interval 93 | * \return the difference in seconds 94 | * 95 | * \TODO this doesn't check for division by 0 errors on purpose, maybe check performance hit and add it if insignificant 96 | */ 97 | double timestamp_diff_seconds(UINT64 start, UINT64 end) 98 | { 99 | #ifdef DEBUG_TIMESTAMPS 100 | if(!_rdtsc_ticks_per_second) 101 | { 102 | LOG.error(L"timestamp ticks per second unknown, most likely init_timestamps() wasn't called"); 103 | return -1.0; 104 | } 105 | #endif 106 | return ((double)end-start)/_rdtsc_ticks_per_second; 107 | } 108 | 109 | /** 110 | * Returns the number of timestamp ticks per second 111 | * 112 | * \return timestamp ticks per second 113 | */ 114 | UINT64 get_timestamp_ticks_per_second() 115 | { 116 | return _rdtsc_ticks_per_second; 117 | } 118 | -------------------------------------------------------------------------------- /library/core/timestamp.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = timestamp 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f00009 5 | MODULE_TYPE = UEFI_DRIVER 6 | VERSION_STRING = 1.0 7 | LIBRARY_CLASS = timestamp|UEFI_APPLICATION UEFI_DRIVER DXE_RUNTIME_DRIVER DXE_DRIVER 8 | 9 | [Sources] 10 | timestamp.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | UEFIStarter/UEFIStarter.dec 15 | 16 | [LibraryClasses] 17 | UefiLib 18 | UefiBootServicesTableLib 19 | IoLib 20 | logger 21 | files 22 | 23 | [Guids] 24 | 25 | [Ppis] 26 | 27 | [Protocols] 28 | 29 | [FeaturePcd] 30 | 31 | [Pcd] 32 | 33 | -------------------------------------------------------------------------------- /library/graphics.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = graphics 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f00013 5 | MODULE_TYPE = UEFI_DRIVER 6 | VERSION_STRING = 1.0 7 | LIBRARY_CLASS = UEFIStarterGraphics|UEFI_APPLICATION UEFI_DRIVER DXE_RUNTIME_DRIVER DXE_DRIVER 8 | 9 | [Sources] 10 | graphics.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | UEFIStarter/UEFIStarter.dec 15 | 16 | [LibraryClasses] 17 | UefiLib 18 | UefiBootServicesTableLib 19 | IoLib 20 | PciLib 21 | UEFIStarterCore 22 | 23 | [Guids] 24 | 25 | [Ppis] 26 | 27 | [Protocols] 28 | 29 | [FeaturePcd] 30 | 31 | [Pcd] 32 | 33 | -------------------------------------------------------------------------------- /library/pci.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = pci 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f00012 5 | MODULE_TYPE = UEFI_DRIVER 6 | VERSION_STRING = 1.0 7 | LIBRARY_CLASS = UEFIStarterPCI|UEFI_APPLICATION UEFI_DRIVER DXE_RUNTIME_DRIVER DXE_DRIVER 8 | 9 | [Sources] 10 | pci.c 11 | 12 | [Packages] 13 | MdePkg/MdePkg.dec 14 | UEFIStarter/UEFIStarter.dec 15 | 16 | [LibraryClasses] 17 | UefiLib 18 | UefiBootServicesTableLib 19 | IoLib 20 | PciLib 21 | UEFIStarterCore 22 | 23 | [Guids] 24 | 25 | [Ppis] 26 | 27 | [Protocols] 28 | 29 | [FeaturePcd] 30 | 31 | [Pcd] 32 | 33 | -------------------------------------------------------------------------------- /library/tests/graphics.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Assertions and utilities for graphics tests 3 | * 4 | * Some of the graphics tests work by looking for changes between two images, then asserting whether those changes are 5 | * within a given rectangular region, the bounding box. 6 | * 7 | * \author Richard Nusser 8 | * \copyright 2017-2018 Richard Nusser 9 | * \license GPLv3 (see http://www.gnu.org/licenses/) 10 | * \sa https://github.com/rinusser/UEFIStarter 11 | * \ingroup group_tests_asserts 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | /** 21 | * Initializes a bounding box. 22 | * 23 | * \param box the bounding box to reset 24 | */ 25 | void reset_bounding_box(bounding_box_t *box) 26 | { 27 | box->left=-1; 28 | box->right=-1; 29 | box->top=-1; 30 | box->bottom=-1; 31 | } 32 | 33 | /** 34 | * Resets a graphics difference test. 35 | * Initializes the expected bounding box and resets the "after" image. 36 | * 37 | * \param difftest the difference test to reset 38 | */ 39 | void reset_graphics_difftest(graphics_difftest_t *difftest) 40 | { 41 | reset_bounding_box(&difftest->box); 42 | CopyMem(difftest->after->data,difftest->before->data,difftest->image_width*difftest->image_height*BYTES_PER_PIXEL); 43 | } 44 | 45 | /** 46 | * Initializes a graphics difference test. 47 | * 48 | * \param difftest the difference test to reset 49 | * \param width the compared images' width 50 | * \param height the compared images' height 51 | * \param bgcol the background color to paint "before" and "after" images with 52 | */ 53 | void init_graphics_difftest_ex(graphics_difftest_t *difftest, INTN width, INTN height, UINT32 bgcol) 54 | { 55 | difftest->image_width=width; 56 | difftest->image_height=height; 57 | difftest->before=create_image(width,height); 58 | difftest->after=create_image(width,height); 59 | SetMem32(difftest->before->data,width*height*BYTES_PER_PIXEL,bgcol); 60 | reset_graphics_difftest(difftest); 61 | } 62 | 63 | /** 64 | * Initializes a default graphics difference test. 65 | * 66 | * \param difftest the difference test to reset 67 | * \param width the compared images' width 68 | * \param height the compared images' height 69 | */ 70 | void init_graphics_difftest(graphics_difftest_t *difftest, INTN width, INTN height) 71 | { 72 | init_graphics_difftest_ex(difftest,width,height,DIFFTEST_DEFAULT_BACKGROUND_UINT32); 73 | } 74 | 75 | /** 76 | * Destroys a graphics difference test structure. 77 | * 78 | * \param difftest the difference test to destroy 79 | */ 80 | void destroy_graphics_difftest(graphics_difftest_t *difftest) 81 | { 82 | free_image(difftest->before); 83 | free_image(difftest->after); 84 | } 85 | 86 | /** 87 | * Compares "before" and "after" images of a difference test and determines the bounding box changes happened in. 88 | * 89 | * \param difftest the difference test to update 90 | */ 91 | void find_bounding_box_for_changes(graphics_difftest_t *difftest) 92 | { 93 | INTN x, y; 94 | INTN y_offset; 95 | UINT32 *s1=(UINT32 *)difftest->before->data; 96 | UINT32 *s2=(UINT32 *)difftest->after->data; 97 | reset_bounding_box(&difftest->box); 98 | 99 | for(y=0;yimage_height;y++) 100 | { 101 | y_offset=y*difftest->image_width; 102 | for(x=0;ximage_width;x++) 103 | { 104 | if(s1[y_offset+x]!=s2[y_offset+x]) 105 | { 106 | if(difftest->box.left==-1 || difftest->box.left>x) 107 | difftest->box.left=x; 108 | if(difftest->box.right==-1 || difftest->box.rightbox.right=x; 110 | if(difftest->box.top==-1) 111 | difftest->box.top=y; 112 | if(difftest->box.bottom==-1 || difftest->box.bottombox.bottom=y; 114 | } 115 | } 116 | } 117 | } 118 | 119 | /** 120 | * Asserts a bounding box matches expected values. 121 | * 122 | * \param box the actual bounding box 123 | * \param left the expected left coordinate 124 | * \param top the expected top coordinate 125 | * \param right the expected right coordinate 126 | * \param bottom the expected bottom coordinate 127 | * \param message a descriptive message of what's being tested 128 | */ 129 | void assert_box_equals(bounding_box_t *box, INTN left, INTN top, INTN right, INTN bottom, CHAR16 *message) 130 | { 131 | assert_intn_equals(left, box->left, memsprintf(L"%s, left", message)); 132 | assert_intn_equals(top, box->top, memsprintf(L"%s, top", message)); 133 | assert_intn_equals(right, box->right, memsprintf(L"%s, right", message)); 134 | assert_intn_equals(bottom,box->bottom,memsprintf(L"%s, bottom",message)); 135 | } 136 | 137 | /** 138 | * Asserts the bounding box of changes is within given ranges of width and height. 139 | * 140 | * \param difftest the difference test to check 141 | * \param min_width the smallest allowed bounding box width 142 | * \param max_width the largest allowed bounding box width 143 | * \param min_height the smallest allowed bounding box height 144 | * \param max_height the largest allowed bounding box height 145 | * \param message a descriptive message of what's being tested 146 | */ 147 | void assert_differences_within_box(graphics_difftest_t *difftest, INTN min_width, INTN max_width, INTN min_height, INTN max_height, CHAR16 *message) 148 | { 149 | find_bounding_box_for_changes(difftest); 150 | assert_intn_in_closed_interval(min_width, max_width, difftest->box.right-difftest->box.left+1,memsprintf(L"%s width",message)); 151 | assert_intn_in_closed_interval(min_height,max_height,difftest->box.bottom-difftest->box.top+1,memsprintf(L"%s height",message)); 152 | } 153 | -------------------------------------------------------------------------------- /library/tests/output.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Presentation logic for tests: generates output with verbosity level in mind 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_tests_runner 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | 15 | #if 0 16 | /** helper macro for debugging output logic - this will mark the start of an output function */ 17 | #define TRACE_STARTTYPE(NAME) ErrorPrint(L"[%s]",NAME); 18 | 19 | /** helper macro for debugging output logic - this will mark the end of an output function */ 20 | #define TRACE_ENDTYPE(NAME) ErrorPrint(L"[/%s]",NAME); 21 | #else 22 | /** helper macro for debugging output logic - the silent version is active, this won't do anything */ 23 | #define TRACE_STARTTYPE(NAME) 24 | 25 | /** helper macro for debugging output logic - the silent version is active, this won't do anything */ 26 | #define TRACE_ENDTYPE(NAME) 27 | #endif 28 | 29 | extern test_verbosity_t test_verbosity; 30 | 31 | 32 | /** 33 | * Internal: prints either a suitable whitespace prefix if multiline output is enabled, or the given string if not. 34 | * 35 | * \param or the alternative string to print if multiline output is disabled, may be NULL 36 | */ 37 | static void _print_optional_multiline_test_prefix_or(CHAR16 *or) 38 | { 39 | if(test_verbosity.multiple_lines_per_test) 40 | Print(L"\n "); 41 | else if(or) 42 | Print(or); 43 | } 44 | 45 | 46 | /** 47 | * Prints the start of a test group. 48 | * 49 | * \param name the test group's name 50 | */ 51 | void print_test_group_start(CHAR16 *name) 52 | { 53 | TRACE_STARTTYPE(L"grp start"); 54 | if(test_verbosity.individual_groups) 55 | { 56 | Print(L"running %s tests: ",name); 57 | } 58 | TRACE_ENDTYPE(L"grp start"); 59 | } 60 | 61 | /** 62 | * Prints the end of a test group. 63 | */ 64 | void print_test_group_end() 65 | { 66 | TRACE_STARTTYPE(L"grp end"); 67 | if(test_verbosity.individual_groups) 68 | Print(L"\n"); 69 | TRACE_ENDTYPE(L"grp end"); 70 | } 71 | 72 | /** 73 | * Prints assertion counts, if enabled. 74 | * 75 | * \param fails the number of failed assertions 76 | * \param asserts the total number of assertions 77 | */ 78 | void print_assert_counts(INT64 fails, INT64 asserts) 79 | { 80 | TRACE_STARTTYPE(L"assert cnt"); 81 | if(test_verbosity.assertion_counts) 82 | { 83 | if(fails==0 && asserts!=0) 84 | Print(L": %ld assertion%s passed",asserts,asserts==1?L"":L"s"); 85 | else if(asserts!=0) 86 | Print(L": %ld out of %ld assertion%s failed",fails,asserts,asserts==1?L"":L"s"); 87 | else 88 | Print(L": no assertions"); 89 | } 90 | TRACE_ENDTYPE(L"assert cnt"); 91 | } 92 | 93 | /** 94 | * Internal: prints a test result status. 95 | * 96 | * \param outcome the result to print 97 | */ 98 | static void _print_outcome(test_outcome outcome) 99 | { 100 | if(outcome==SUCCESS) 101 | color_print(EFI_LIGHTGREEN,L"SUCCESS"); 102 | else if(outcome==INCOMPLETE) 103 | color_print(EFI_YELLOW,L"INCOMPLETE"); 104 | else 105 | color_print(EFI_LIGHTRED,L"ERROR"); 106 | } 107 | 108 | /** 109 | * Internal: prints a test result status, as 1 character. 110 | * 111 | * \param outcome the result to print 112 | */ 113 | static void _print_1char_outcome(test_outcome outcome) 114 | { 115 | if(outcome==SUCCESS) 116 | Print(L"."); 117 | else if(outcome==INCOMPLETE) 118 | Print(L"I"); 119 | else 120 | Print(L"E"); 121 | } 122 | 123 | /** 124 | * Prints a test group's results. 125 | * 126 | * \param results the results to print 127 | */ 128 | void print_group_result(test_results_t *results) 129 | { 130 | TRACE_STARTTYPE(L"grp result"); 131 | if(!test_verbosity.individual_tests && !test_verbosity.one_char_per_test) 132 | { 133 | _print_outcome(results->outcome); 134 | print_assert_counts(results->assert_fails,results->assert_count); 135 | } 136 | TRACE_ENDTYPE(L"grp result"); 137 | } 138 | 139 | /** 140 | * Prints an individual test's results. 141 | * 142 | * \param results the results to print 143 | */ 144 | void print_individual_result(test_results_t *results) 145 | { 146 | TRACE_STARTTYPE(L"indiv result"); 147 | if(test_verbosity.one_char_per_test) 148 | _print_1char_outcome(results->outcome); 149 | else if(test_verbosity.individual_tests) 150 | { 151 | _print_optional_multiline_test_prefix_or(NULL); 152 | _print_outcome(results->outcome); 153 | print_assert_counts(results->assert_fails,results->assert_count); 154 | } 155 | TRACE_ENDTYPE(L"indiv result"); 156 | } 157 | 158 | /** 159 | * Prints test result summary, if enabled. 160 | * 161 | * \param results the results to summarize 162 | */ 163 | void print_test_result_summary(test_results_t *results) 164 | { 165 | TRACE_STARTTYPE(L"res summary"); 166 | Print(L"\nResult: "); 167 | _print_outcome(results->outcome); 168 | Print(L"\n"); 169 | 170 | if(test_verbosity.summary_statistics) 171 | { 172 | Print(L"\n"); 173 | Print(L"Successful tests: %u\n", results->successful_test_count); 174 | Print(L"Failed tests: %u\n", results->failed_test_count); 175 | Print(L"Incomplete tests: %u\n", results->incomplete_count); 176 | Print(L"Skipped groups: %u\n\n",results->skipped_count); 177 | } 178 | TRACE_ENDTYPE(L"res summary"); 179 | } 180 | 181 | /** 182 | * Prints the start of an indivual test, if enabled. 183 | * 184 | * \param description the test's description to print 185 | */ 186 | void print_individual_test_start(CHAR16 *description) 187 | { 188 | TRACE_STARTTYPE(L"indiv start"); 189 | if(test_verbosity.individual_tests && !test_verbosity.one_char_per_test) 190 | { 191 | Print(L"\n testing %s: ",description); 192 | } 193 | TRACE_ENDTYPE(L"indiv start"); 194 | } 195 | 196 | /** 197 | * Prints an assertion's result. 198 | * 199 | * \param success whether the assertion passed 200 | * \param description the assertion's description to print, if enabled 201 | * \param message the assertion's error message to print, if enabled 202 | */ 203 | void print_assertion(BOOLEAN success, CHAR16 *description, CHAR16 *message) 204 | { 205 | TRACE_STARTTYPE(L"assert"); 206 | if(!test_verbosity.one_char_per_test) 207 | { 208 | if(success) 209 | { 210 | if(test_verbosity.individual_assertions) 211 | { 212 | _print_optional_multiline_test_prefix_or(NULL); 213 | color_print(EFI_GREEN,L"asserted %s (%s)",description,message); 214 | if(!test_verbosity.multiple_lines_per_test || !test_verbosity.individual_tests) 215 | Print(L" "); 216 | } 217 | } 218 | else 219 | { 220 | _print_optional_multiline_test_prefix_or(NULL); 221 | color_print(EFI_RED,L"failed asserting %s (%s)",description,message); 222 | if(!test_verbosity.multiple_lines_per_test || !test_verbosity.individual_tests) 223 | Print(L" "); 224 | } 225 | } 226 | TRACE_ENDTYPE(L"assert"); 227 | } 228 | 229 | 230 | /** 231 | * Internal: prints verbosity settings. 232 | */ 233 | void debug_print_verbosity() 234 | { 235 | ErrorPrint(L"individual groups=%d tests=%d asserts=%d, 1c/t=%d, assertion_cnt=%d, multiline/t=%d, stats=%d\n", 236 | test_verbosity.individual_groups, 237 | test_verbosity.individual_tests, 238 | test_verbosity.individual_assertions, 239 | test_verbosity.one_char_per_test, 240 | test_verbosity.assertion_counts, 241 | test_verbosity.multiple_lines_per_test, 242 | test_verbosity.summary_statistics); 243 | } 244 | 245 | /** 246 | * Internal: prints raw test results. 247 | * 248 | * \param results the test results to output 249 | */ 250 | void debug_print_results(test_results_t *results) 251 | { 252 | ErrorPrint(L"assert_count=%d, assert_fails=%d, successful_test_count=%d, failed_test_count=%d, incomplete_count=%d, skipped_count=%d, outcome=", 253 | results->assert_count, 254 | results->assert_fails, 255 | results->successful_test_count, 256 | results->failed_test_count, 257 | results->incomplete_count, 258 | results->skipped_count); 259 | if(results->outcome==SUCCESS) 260 | ErrorPrint(L"SUCCESS"); 261 | else if(results->outcome==INCOMPLETE) 262 | ErrorPrint(L"INCOMPLETE"); 263 | else if(results->outcome==FAILURE) 264 | ErrorPrint(L"FAILURE"); 265 | else 266 | ErrorPrint(L"unknown (%d)",results->outcome); 267 | 268 | ErrorPrint(L"\n"); 269 | } 270 | -------------------------------------------------------------------------------- /library/tests/tests.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * This is the basis for all test suites. 3 | * This handles the test execution, the test suite is just responsible for producing results. 4 | * 5 | * \author Richard Nusser 6 | * \copyright 2017-2018 Richard Nusser 7 | * \license GPLv3 (see http://www.gnu.org/licenses/) 8 | * \sa https://github.com/rinusser/UEFIStarter 9 | * \ingroup group_tests_runner 10 | * 11 | * \TODO add pause-on-error/incomplete feature 12 | * \XXX could add a way to capture error messages and output them later 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | /** internal storage for the initial log level to set before each test */ 27 | static LOGLEVEL _initial_log_level; 28 | 29 | static CHAR16 **_skipped_tests_list; /**< internal storage for the list of skipped tests */ 30 | static UINTN _skipped_tests_count; /**< internal storage for the number of skipped tests */ 31 | 32 | /** the current test verbosity */ 33 | test_verbosity_t test_verbosity; 34 | 35 | test_results_t individual_test_results; /**< results for the current test */ 36 | test_results_t group_test_results; /**< results for the current test group */ 37 | test_results_t global_test_results; /**< results for all tests in this suite */ 38 | 39 | 40 | #define ARG_SKIP_TESTS tests_args[0].value.wcstr /**< helper macro to access the -skip argument's values */ 41 | #define ARG_VERBOSITY tests_args[1].value.uint64 /**< helper macro to access the -verbosity argument's value */ 42 | #define ARG_MULTILINE tests_args[2].value.uint64 /**< helper macro to access the -multiline argument's value */ 43 | #define ARG_NO_COUNTS tests_args[3].value.uint64 /**< helper macro to access the -no-counts argument's value */ 44 | #define ARG_ASSERTIONS tests_args[4].value.uint64 /**< helper macro to access the -assertions argument's value */ 45 | #define ARG_NO_STATISTICS tests_args[5].value.uint64 /**< helper macro to access the -no-statistics argument's value */ 46 | 47 | 48 | /** 49 | * Validator for the `-verbosity ` argument. 50 | * 51 | * \param v the input to validate 52 | * \return whether the given value is a valid verbosity 53 | */ 54 | INT_RANGE_VALIDATOR(validate_verbosity,L"verbosity",1,4); 55 | 56 | 57 | /** list of command-line arguments */ 58 | cmdline_argument_t tests_args[]={ 59 | {{wcstr:L""},ARG_STRING,NULL, L"-skip", L"Comma-separated test groups to skip"}, 60 | {{uint64:4}, ARG_INT, validate_verbosity,L"-verbosity", L"Set verbosity (the higher the more detailed) [1..4]"}, 61 | {{uint64:0}, ARG_BOOL, NULL, L"-multiline", L"Use multiple lines per test (verbosity 4 only)"}, 62 | {{uint64:0}, ARG_BOOL, NULL, L"-no-counts", L"Disable assertion counts (shown at verbosities 2 and 4 only)"}, 63 | {{uint64:0}, ARG_BOOL, NULL, L"-assertions", L"Show successful assertions (verbosity 4 only)"}, 64 | {{uint64:0}, ARG_BOOL, NULL, L"-no-statistics",L"Disable summary statistics"}, 65 | }; 66 | 67 | /** command-line argument group */ 68 | ARG_GROUP(tests_arggroup,tests_args,L"Test options") 69 | 70 | 71 | /** 72 | * Log printer function writing to STDERR. 73 | * 74 | * \param level the log entry's log level 75 | * \param msg the log entry's message 76 | */ 77 | void log_errorprint(LOGLEVEL level, CHAR16 *msg) 78 | { 79 | ErrorPrint(L"%s: %s\n",logger_level_names[level],msg); 80 | } 81 | 82 | 83 | /** 84 | * Initializes a test_results_t structure. 85 | * 86 | * \param results the structure to reset 87 | */ 88 | void reset_test_results(test_results_t *results) 89 | { 90 | results->assert_count=0; 91 | results->assert_fails=0; 92 | results->successful_test_count=0; 93 | results->failed_test_count=0; 94 | results->incomplete_count=0; 95 | results->skipped_count=0; 96 | results->outcome=SUCCESS; 97 | } 98 | 99 | /** 100 | * Internal: combines two test outcomes. 101 | * 102 | * \param v1 the first value to compare 103 | * \param v2 the second value to compare 104 | * \return the combined outcome 105 | */ 106 | static test_outcome _combine_outcomes(test_outcome v1, test_outcome v2) 107 | { 108 | if(v1==FAILURE||v2==FAILURE) 109 | return FAILURE; 110 | if(v1==INCOMPLETE||v2==INCOMPLETE) 111 | return INCOMPLETE; 112 | return SUCCESS; 113 | } 114 | 115 | /** 116 | * Adds test result data to a larger collection of results. 117 | * This is e.g. used to sum up individual test results into test group results. 118 | * 119 | * \param target the larger collection of results to add to 120 | * \param source the new data to add 121 | */ 122 | void add_test_results(test_results_t *target, test_results_t *source) 123 | { 124 | target->assert_count+=source->assert_count; 125 | target->assert_fails+=source->assert_fails; 126 | target->successful_test_count+=source->successful_test_count; 127 | target->failed_test_count+=source->failed_test_count; 128 | target->incomplete_count+=source->incomplete_count; 129 | target->skipped_count+=source->skipped_count; 130 | target->outcome=_combine_outcomes(target->outcome,source->outcome); 131 | } 132 | 133 | /** 134 | * Updates internal test_results_t data after a test. 135 | * 136 | * \param result the data to update 137 | */ 138 | void handle_result(test_results_t *result) 139 | { 140 | if(result->incomplete_count>0 || (result->assert_fails==0&&result->assert_count==0)) 141 | { 142 | result->incomplete_count=1; 143 | result->outcome=INCOMPLETE; 144 | } 145 | else if(result->assert_fails==0) 146 | { 147 | result->successful_test_count++; 148 | result->outcome=SUCCESS; 149 | } 150 | else 151 | { 152 | result->failed_test_count++; 153 | result->outcome=FAILURE; 154 | } 155 | } 156 | 157 | /** 158 | * Processes an individual test's results. 159 | */ 160 | void handle_individual_result() 161 | { 162 | handle_result(&individual_test_results); 163 | add_test_results(&group_test_results,&individual_test_results); 164 | print_individual_result(&individual_test_results); 165 | } 166 | 167 | /** 168 | * Processes a test group's results. 169 | */ 170 | void handle_group_result() 171 | { 172 | add_test_results(&global_test_results,&group_test_results); 173 | print_group_result(&group_test_results); 174 | } 175 | 176 | /** 177 | * Runs an individual test. 178 | * This function takes care of required setup/teardown around tests. 179 | * 180 | * \param func the test function to execute 181 | * \param description the test's description 182 | */ 183 | void run_test(void (*func)(), CHAR16 *description) 184 | { 185 | set_logger_function(log_errorprint); 186 | set_log_level(_initial_log_level); 187 | 188 | reset_test_results(&individual_test_results); 189 | print_individual_test_start(description); 190 | 191 | func(); 192 | 193 | set_logger_function(log_errorprint); 194 | set_log_level(_initial_log_level); 195 | 196 | handle_individual_result(); 197 | 198 | stop_tracking_memory(); 199 | } 200 | 201 | 202 | /** 203 | * Runs a test group. 204 | * 205 | * \param func the test group to run 206 | */ 207 | void run_group(BOOLEAN (*func)()) 208 | { 209 | BOOLEAN ran; 210 | reset_test_results(&group_test_results); 211 | ran=func(); 212 | handle_group_result(); 213 | if(ran) 214 | print_test_group_end(); 215 | } 216 | 217 | /** 218 | * Internal: parses a list of skipped tests into internal storage. 219 | * 220 | * \param str the list of tests to skip 221 | */ 222 | static void _parse_skipped_tests(CHAR16 *str) 223 | { 224 | if(!str || StrLen(str)<1) 225 | { 226 | _skipped_tests_list=NULL; 227 | return; 228 | } 229 | 230 | _skipped_tests_count=split_string(&_skipped_tests_list,str,L','); 231 | } 232 | 233 | /** 234 | * Tests whether a given test should be skipped. 235 | * 236 | * \param name the test's name 237 | * \return whether the test should be skipped 238 | */ 239 | BOOLEAN is_skipped_test(CHAR16 *name) 240 | { 241 | UINTN tc; 242 | for(tc=0;tc<_skipped_tests_count;tc++) 243 | if(StrCmp(name,_skipped_tests_list[tc])==0) 244 | return TRUE; 245 | return FALSE; 246 | } 247 | 248 | /** 249 | * Transforms the -verbosity command-line argument into its internal representation. 250 | */ 251 | void assemble_and_set_verbosity() 252 | { 253 | UINTN vmask=0; 254 | UINTN vmask_by_verbosity[]={4,1,5,3}; 255 | 256 | if(ARG_VERBOSITY>=1 && ARG_VERBOSITY<=sizeof(vmask_by_verbosity)/sizeof(UINTN)) 257 | vmask|=vmask_by_verbosity[ARG_VERBOSITY-1]; 258 | else 259 | LOG.error(L"unhandled verbosity: %d",ARG_VERBOSITY); 260 | *((UINTN *)&test_verbosity)=vmask; 261 | 262 | test_verbosity.multiple_lines_per_test=ARG_VERBOSITY>=4&&ARG_MULTILINE; 263 | test_verbosity.assertion_counts=(ARG_VERBOSITY==2||ARG_VERBOSITY>=4)&&!ARG_NO_COUNTS; 264 | test_verbosity.individual_assertions=ARG_VERBOSITY>=4&&ARG_ASSERTIONS; 265 | test_verbosity.summary_statistics=!ARG_NO_STATISTICS; 266 | } 267 | 268 | /** 269 | * Main function for test suite, gets invoked by UEFI shell. 270 | * 271 | * \param argc the number of command-line arguments passed 272 | * \param argv_ascii the command-line arguments passed, as ASCII 273 | * \return an EFI status code for the shell 274 | */ 275 | int main(int argc, char **argv_ascii) 276 | { 277 | EFI_STATUS rv; 278 | CHAR16 **argv; 279 | 280 | argv=argv_from_ascii(argc,argv_ascii); 281 | rv=init(argc,argv,1,&tests_arggroup); 282 | free_argv(); 283 | 284 | if(rv==EFI_SUCCESS) 285 | { 286 | assemble_and_set_verbosity(); 287 | reset_test_results(&global_test_results); 288 | _initial_log_level=get_log_level(); 289 | _parse_skipped_tests(ARG_SKIP_TESTS); 290 | 291 | //init_timestamps(); 292 | run_tests(); 293 | 294 | print_test_result_summary(&global_test_results); 295 | if(_skipped_tests_list) 296 | FreePool(_skipped_tests_list); 297 | } 298 | 299 | shutdown(); 300 | return rv; 301 | } 302 | -------------------------------------------------------------------------------- /library/tests/tests.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = tests 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f00018 5 | MODULE_TYPE = UEFI_DRIVER 6 | VERSION_STRING = 1.0 7 | LIBRARY_CLASS = UEFIStarterTests|UEFI_APPLICATION UEFI_DRIVER DXE_RUNTIME_DRIVER DXE_DRIVER 8 | 9 | [Sources] 10 | asserts.c 11 | graphics.c 12 | output.c 13 | tests.c 14 | 15 | [Packages] 16 | MdePkg/MdePkg.dec 17 | UEFIStarter/UEFIStarter.dec 18 | 19 | [LibraryClasses] 20 | UefiLib 21 | 22 | [Guids] 23 | 24 | [Ppis] 25 | 26 | [Protocols] 27 | 28 | [FeaturePcd] 29 | 30 | [Pcd] 31 | 32 | -------------------------------------------------------------------------------- /static/demoimg.ppm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rinusser/UEFIStarter/8d5b385e0de6db2c0917d754623a8c351edfc5c5/static/demoimg.ppm -------------------------------------------------------------------------------- /static/font815.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rinusser/UEFIStarter/8d5b385e0de6db2c0917d754623a8c351edfc5c5/static/font815.pgm -------------------------------------------------------------------------------- /static/pci.ids: -------------------------------------------------------------------------------- 1 | # This is a subset of the PCI ID Project's pci.ids, as maintained by Albert Pool, Martin Mares, and 2 | # other volunteers from the PCI ID Project at http://pci-ids.ucw.cz/. 3 | # 4 | # The full version of pci.ids can be used instead to identify other devices as well. 5 | # 6 | # Syntax: 7 | # vendor vendor_name 8 | # device device_name <-- single tab 9 | # subvendor subdevice subsystem_name <-- two tabs 10 | 11 | 106b Apple Inc. 12 | 003f KeyLargo/Intrepid USB 13 | 1af4 1100 QEMU Virtual Machine 14 | 1234 QEMU 15 | 1111 Standard VGA Driver 16 | 8086 Intel Corporation 17 | 100e 82540EM Gigabit Ethernet Controller 18 | 1af4 1100 QEMU Virtual Machine 19 | 1237 440FX - 82441FX PMC [Natoma] 20 | 1af4 1100 Qemu virtual machine 21 | 2415 82801AA AC'97 Audio Controller 22 | 1af4 1100 QEMU Virtual Machine 23 | 2829 82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] 24 | 1025 0121 Aspire 5920G 25 | 103c 30c0 Compaq 6710b 26 | 103c 30c1 Compaq 6910p 27 | 103c 30cc Pavilion dv6700 28 | 103c 30d9 Presario C700 29 | 104d 9005 Vaio VGN-FZ260E 30 | 104d 902d VAIO VGN-NR120E 31 | 17aa 20a7 ThinkPad T61/R61 32 | 17c0 4083 Medion WIM 2210 Notebook PC [MD96850] 33 | e4bf cc47 CCG-RUMBA 34 | 7000 82371SB PIIX3 ISA [Natoma/Triton II] 35 | 1af4 1100 Qemu virtual machine 36 | 7010 82371SB PIIX3 IDE [Natoma/Triton II] 37 | 1af4 1100 Qemu virtual machine 38 | 7113 82371AB/EB/MB PIIX4 ACPI 39 | 15ad 1976 Virtual Machine Chipset 40 | 1af4 1100 Qemu virtual machine 41 | 80ee InnoTek Systemberatung GmbH 42 | beef VirtualBox Graphics Adapter 43 | cafe VirtualBox Guest Service 44 | 45 | 46 | # List of known device classes, subclasses and programming interfaces 47 | 48 | # Syntax: 49 | # C class class_name 50 | # subclass subclass_name <-- single tab 51 | # prog-if prog-if_name <-- two tabs 52 | 53 | C 00 Unclassified device 54 | 00 Non-VGA unclassified device 55 | 01 VGA compatible unclassified device 56 | C 01 Mass storage controller 57 | 00 SCSI storage controller 58 | 01 IDE interface 59 | 02 Floppy disk controller 60 | 03 IPI bus controller 61 | 04 RAID bus controller 62 | 05 ATA controller 63 | 20 ADMA single stepping 64 | 30 ADMA continuous operation 65 | 06 SATA controller 66 | 00 Vendor specific 67 | 01 AHCI 1.0 68 | 02 Serial Storage Bus 69 | 07 Serial Attached SCSI controller 70 | 01 Serial Storage Bus 71 | 08 Non-Volatile memory controller 72 | 01 NVMHCI 73 | 02 NVM Express 74 | 80 Mass storage controller 75 | C 02 Network controller 76 | 00 Ethernet controller 77 | 01 Token ring network controller 78 | 02 FDDI network controller 79 | 03 ATM network controller 80 | 04 ISDN controller 81 | 05 WorldFip controller 82 | 06 PICMG controller 83 | 07 Infiniband controller 84 | 08 Fabric controller 85 | 80 Network controller 86 | C 03 Display controller 87 | 00 VGA compatible controller 88 | 00 VGA controller 89 | 01 8514 controller 90 | 01 XGA compatible controller 91 | 02 3D controller 92 | 80 Display controller 93 | C 04 Multimedia controller 94 | 00 Multimedia video controller 95 | 01 Multimedia audio controller 96 | 02 Computer telephony device 97 | 03 Audio device 98 | 80 Multimedia controller 99 | C 05 Memory controller 100 | 00 RAM memory 101 | 01 FLASH memory 102 | 80 Memory controller 103 | C 06 Bridge 104 | 00 Host bridge 105 | 01 ISA bridge 106 | 02 EISA bridge 107 | 03 MicroChannel bridge 108 | 04 PCI bridge 109 | 00 Normal decode 110 | 01 Subtractive decode 111 | 05 PCMCIA bridge 112 | 06 NuBus bridge 113 | 07 CardBus bridge 114 | 08 RACEway bridge 115 | 00 Transparent mode 116 | 01 Endpoint mode 117 | 09 Semi-transparent PCI-to-PCI bridge 118 | 40 Primary bus towards host CPU 119 | 80 Secondary bus towards host CPU 120 | 0a InfiniBand to PCI host bridge 121 | 80 Bridge 122 | C 07 Communication controller 123 | 00 Serial controller 124 | 00 8250 125 | 01 16450 126 | 02 16550 127 | 03 16650 128 | 04 16750 129 | 05 16850 130 | 06 16950 131 | 01 Parallel controller 132 | 00 SPP 133 | 01 BiDir 134 | 02 ECP 135 | 03 IEEE1284 136 | fe IEEE1284 Target 137 | 02 Multiport serial controller 138 | 03 Modem 139 | 00 Generic 140 | 01 Hayes/16450 141 | 02 Hayes/16550 142 | 03 Hayes/16650 143 | 04 Hayes/16750 144 | 04 GPIB controller 145 | 05 Smard Card controller 146 | 80 Communication controller 147 | C 08 Generic system peripheral 148 | 00 PIC 149 | 00 8259 150 | 01 ISA PIC 151 | 02 EISA PIC 152 | 10 IO-APIC 153 | 20 IO(X)-APIC 154 | 01 DMA controller 155 | 00 8237 156 | 01 ISA DMA 157 | 02 EISA DMA 158 | 02 Timer 159 | 00 8254 160 | 01 ISA Timer 161 | 02 EISA Timers 162 | 03 HPET 163 | 03 RTC 164 | 00 Generic 165 | 01 ISA RTC 166 | 04 PCI Hot-plug controller 167 | 05 SD Host controller 168 | 06 IOMMU 169 | 80 System peripheral 170 | C 09 Input device controller 171 | 00 Keyboard controller 172 | 01 Digitizer Pen 173 | 02 Mouse controller 174 | 03 Scanner controller 175 | 04 Gameport controller 176 | 00 Generic 177 | 10 Extended 178 | 80 Input device controller 179 | C 0a Docking station 180 | 00 Generic Docking Station 181 | 80 Docking Station 182 | C 0b Processor 183 | 00 386 184 | 01 486 185 | 02 Pentium 186 | 10 Alpha 187 | 20 Power PC 188 | 30 MIPS 189 | 40 Co-processor 190 | C 0c Serial bus controller 191 | 00 FireWire (IEEE 1394) 192 | 00 Generic 193 | 10 OHCI 194 | 01 ACCESS Bus 195 | 02 SSA 196 | 03 USB controller 197 | 00 UHCI 198 | 10 OHCI 199 | 20 EHCI 200 | 30 XHCI 201 | 80 Unspecified 202 | fe USB Device 203 | 04 Fibre Channel 204 | 05 SMBus 205 | 06 InfiniBand 206 | 07 IPMI SMIC interface 207 | 08 SERCOS interface 208 | 09 CANBUS 209 | C 0d Wireless controller 210 | 00 IRDA controller 211 | 01 Consumer IR controller 212 | 10 RF controller 213 | 11 Bluetooth 214 | 12 Broadband 215 | 20 802.1a controller 216 | 21 802.1b controller 217 | 80 Wireless controller 218 | C 0e Intelligent controller 219 | 00 I2O 220 | C 0f Satellite communications controller 221 | 01 Satellite TV controller 222 | 02 Satellite audio communication controller 223 | 03 Satellite voice communication controller 224 | 04 Satellite data communication controller 225 | C 10 Encryption controller 226 | 00 Network and computing encryption device 227 | 10 Entertainment encryption device 228 | 80 Encryption controller 229 | C 11 Signal processing controller 230 | 00 DPIO module 231 | 01 Performance counters 232 | 10 Communication synchronizer 233 | 20 Signal processing management 234 | 80 Signal processing controller 235 | C 12 Processing accelerators 236 | 00 Processing accelerators 237 | C 13 Non-Essential Instrumentation 238 | C 40 Coprocessor 239 | C ff Unassigned class 240 | -------------------------------------------------------------------------------- /static/startup.nsh: -------------------------------------------------------------------------------- 1 | @echo -off 2 | fs0: 3 | 4 | alias ll "ls" 5 | alias clear "cls" 6 | alias q "quit" 7 | 8 | cd tests 9 | run.nsh 10 | -------------------------------------------------------------------------------- /tests/suites/lib/ac97.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Tests for AC'97 functions. 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_lib_ac97 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | /** 18 | * Shortcut macro: checks whether symbol has given size. 19 | * 20 | * \param NAME the symbol's name 21 | * \param SIZE the expected size, in bytes 22 | */ 23 | #define STRUCT_SIZE_TEST(NAME,SIZE) assert_intn_equals(SIZE,sizeof(NAME),L"sizeof " #NAME); 24 | 25 | /** 26 | * Makes sure AC'97 built-in structures are sized correctly. 27 | * 28 | * \test ac97_bar_t's size is exactly 128 bytes, as required by AC'97 specs 29 | * \test ac97_buffer_descriptor_t's size is exactly 8 bytes, as required by AC'97 specs 30 | * \test ac97_busmaster_status_t's size is exactly 2 bytes, as required by AC'97 specs 31 | */ 32 | void test_struct_sizes() 33 | { 34 | STRUCT_SIZE_TEST(ac97_bar_t,128); 35 | STRUCT_SIZE_TEST(ac97_buffer_descriptor_t,8); 36 | STRUCT_SIZE_TEST(ac97_busmaster_status_t,2); 37 | } 38 | 39 | 40 | /** data type for test_volume_macro() test cases */ 41 | typedef struct 42 | { 43 | UINT16 expected; /**< the expected mixer value */ 44 | UINT8 left; /**< the left channel's volume to set */ 45 | UINT8 right; /**< the right channel's volume to set */ 46 | UINT8 mute; /**< the mute value to set */ 47 | } volume_macro_testcase_t; 48 | 49 | /** test cases for test_volume_macro() */ 50 | volume_macro_testcase_t volume_macro_testcases[]={ 51 | {0x8000,0,0,1}, 52 | {0x0000,0,0,0}, 53 | {0x0808,8,8,0}, 54 | {0x3F3F,0x3F,0x3F,0}, 55 | {0x9F12,0x1F,0x12,7} 56 | }; 57 | 58 | /** 59 | * Makes sure ac97_mixer_value() works. 60 | * 61 | * \test ac97_mixer_value() handles left and right channel separately 62 | * \test ac97_mixer_value() converts mute value to 1 bit 63 | */ 64 | void test_volume_macro() 65 | { 66 | INTN tc, count; 67 | volume_macro_testcase_t *cases=volume_macro_testcases; 68 | count=sizeof(volume_macro_testcases)/sizeof(volume_macro_testcase_t); 69 | 70 | for(tc=0;tc 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | /** 19 | * A validator function for integers, used in tests. 20 | * 21 | * \param val the value to validate 22 | * \return whether the value is valid 23 | */ 24 | BOOLEAN validate_int(cmdline_value_t val) 25 | { 26 | return !(val.uint64%2); 27 | } 28 | 29 | /** 30 | * A list of arguments, used in tests. 31 | */ 32 | cmdline_argument_t cmdline_args_list[]= 33 | { 34 | {{uint64:0}, ARG_BOOL, NULL, L"-bool", L"some boolean"}, 35 | {{uint64:12}, ARG_INT, validate_int,L"-int", L"some integer"}, 36 | {{dbl:2.5}, ARG_DOUBLE,NULL, L"-double",L"some double"}, 37 | {{wcstr:L"foo"},ARG_STRING,NULL, L"-string",L"some string"}, 38 | }; 39 | 40 | /** 41 | * An argument group, used in tests. 42 | */ 43 | cmdline_argument_group_t cmdline_args_group= 44 | { 45 | NULL, 46 | sizeof(cmdline_args_list)/sizeof(cmdline_argument_t), 47 | cmdline_args_list 48 | }; 49 | 50 | /** data type for command-line argument parser testcase */ 51 | typedef struct 52 | { 53 | CHAR16 *message; /**< a descriptive message for the testcase */ 54 | BOOLEAN expected_success; /**< whether the parser is expected to indicate parsed values were valid */ 55 | BOOLEAN expected_bool; /**< the boolean argument's expected value */ 56 | INTN expected_int; /**< the integer argument's expected value */ 57 | double expected_double; /**< the double argument's expected value */ 58 | CHAR16 *expected_string; /**< the string argument's expected value */ 59 | CHAR16 *input; /**< the command-line argument string to pass to the parser */ 60 | } cmdline_args_testcase_t; 61 | 62 | /** testcases for test_parse_parameters() */ 63 | cmdline_args_testcase_t cmdline_args_testcases[]= 64 | { 65 | {L"empty list", TRUE, 0,12, 2.5, L"foo",NULL}, 66 | {L"disable logging", TRUE, 0,12, 2.5, L"foo",L"-no-log "}, //keep whitespace: compiler breaks test otherwise 67 | {L"negative uint64 1",FALSE,0,12, 2.5, L"foo",L"-int -1 -no-log"}, 68 | {L"negative uint64 2",FALSE,0,12, 2.5, L"foo",L"-int -2 -no-log"}, //keep whitespace: compiler breaks test otherwise 69 | {L"failing validator",FALSE,0,23, 2.5, L"foo",L"-int 23"}, 70 | {L"passing all", TRUE, 1,22,-12.98766,L"bar",L"-double -12.98766 -int 22 -string bar -bool"}, 71 | }; 72 | 73 | /** 74 | * Runs an individual testcase. 75 | * This currently doesn't really support multiple argument groups, the vararg setup is just there to convert the 76 | * argument group to VA_LIST for parse_parameters(). 77 | * 78 | * \param testcase the testcase to run 79 | * \param list the parsed argument's data 80 | * \param count the number of argument groups passed 81 | * \param ... the list of argument groups (as cmdline_argument_group_t *) 82 | */ 83 | void EFIAPI do_parse_parameters_testcase(cmdline_args_testcase_t *testcase, cmdline_argument_t *list, UINTN count, ...) 84 | { 85 | BOOLEAN success; 86 | VA_LIST args; 87 | UINTN argc; 88 | CHAR16 **argv; 89 | LOGLEVEL previous_log_level; 90 | 91 | argc=split_string(&argv,testcase->input,L' '); 92 | previous_log_level=get_log_level(); 93 | 94 | VA_START(args,count); 95 | success=parse_parameters(argc,argv,count,args)==EFI_SUCCESS; 96 | VA_END(args); 97 | 98 | set_log_level(previous_log_level); 99 | 100 | assert_intn_equals(testcase->expected_success,success,L"success"); 101 | assert_intn_equals(testcase->expected_bool,list[0].value.uint64,L"bool"); 102 | assert_intn_equals(testcase->expected_int, list[1].value.uint64,L"int"); 103 | assert_double_near(testcase->expected_double,0.0000001,list[2].value.dbl,L"double"); 104 | assert_wcstr_equals(testcase->expected_string,list[3].value.wcstr,L"string"); 105 | 106 | FreePool(argv); 107 | } 108 | 109 | /** 110 | * Makes sure the command-line parser works. 111 | * 112 | * \test not passing any parameters is OK 113 | * \test passing a parameter value a validator deems invalid results in parse failure 114 | * \test passing all valid values results in parse success 115 | * \test passing negative numbers to unsigned integer parameters results in parse failure 116 | * \test can supply logging parameters on command-line 117 | */ 118 | void test_parse_parameters() 119 | { 120 | UINTN count=sizeof(cmdline_args_testcases)/sizeof(cmdline_args_testcase_t); 121 | UINTN tc; 122 | cmdline_args_testcase_t *cases=cmdline_args_testcases; 123 | 124 | for(tc=0;tc 12 | #include 13 | #include 14 | #include 15 | 16 | /** 17 | * Makes sure get_file_content() works. 18 | * 19 | * \test get_file_contents() returns a file's contents 20 | * 21 | * \TODO read a file that's guaranteed to exist, e.g. the currently running .efi, instead of startup.nsh. 22 | */ 23 | void test_get_file_contents() 24 | { 25 | file_contents_t *contents=get_file_contents(L"\\startup.nsh"); 26 | 27 | if(!assert_not_null(contents,L"error reading file")) 28 | return; 29 | if(!assert_intn_greater_than_or_equal_to(9,contents->data_length,L"minimum file length")) 30 | { 31 | free_pages(contents,contents->memory_pages); 32 | return; 33 | } 34 | assert_intn_equals('@',contents->data[0],L"first character"); 35 | assert_intn_equals('e',contents->data[1],L"second character"); 36 | free_pages(contents,contents->memory_pages); 37 | } 38 | 39 | /** 40 | * Test runner for this group. 41 | * Gets called via the generated test runner. 42 | * 43 | * \return whether the test group was executed 44 | */ 45 | BOOLEAN run_files_tests() 46 | { 47 | INIT_TESTGROUP(L"files"); 48 | RUN_TEST(test_get_file_contents,L"get_file_contents"); 49 | FINISH_TESTGROUP(); 50 | } 51 | -------------------------------------------------------------------------------- /tests/suites/lib/logger.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Tests for the logging facility. 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_lib_logger 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | /** 17 | * The number of entries in the log counter array. 18 | * Index is of type LOGLEVEL: "OFF" is 0, "TRACE" starts at 1, so we need to reserve the index 0 entry. 19 | */ 20 | #define LOG_COUNT_ENTRIES 6 21 | 22 | /** 23 | * Internal storage for the _counting_logger() function. 24 | * Contains the number of entries issued per log level. 25 | */ 26 | static UINTN _log_counts[LOG_COUNT_ENTRIES]; 27 | 28 | /** 29 | * Reset's the log message counters to 0. 30 | */ 31 | static void _reset_log_counts() 32 | { 33 | UINTN tc; 34 | for(tc=0;tc 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | /** 19 | * Makes sure memory page tracking works. 20 | * 21 | * \test stopping the memory tracker with unfreed tracked pages results in a logged error 22 | * \test freeing tracked pages twice shouldn't work and result in a logged error 23 | * \test stopping the memory tracker with unfreed untracked pages should not log an error 24 | */ 25 | void test_page_tracking() 26 | { 27 | void *ptr; 28 | UINTN prev_error_count; 29 | LOGLEVEL previous_log_level; 30 | BOOLEAN result; 31 | 32 | reset_memory_tracking(); 33 | 34 | //allocating pages then stopping should result in error 35 | ptr=allocate_pages(1); 36 | prev_error_count=get_logger_entry_count(ERROR); 37 | previous_log_level=get_log_level(); 38 | set_log_level(OFF); 39 | stop_tracking_memory(); 40 | set_log_level(previous_log_level); 41 | assert_intn_equals(1,get_logger_entry_count(ERROR)-prev_error_count,L"1 unfreed page entry should result in 1 error on stopping"); 42 | free_pages_ex(ptr,1,FALSE); 43 | 44 | //freeing unallocated tracked should result in error and rv FALSE 45 | prev_error_count=get_logger_entry_count(ERROR); 46 | previous_log_level=get_log_level(); 47 | set_log_level(OFF); 48 | result=free_pages(ptr,1); 49 | set_log_level(previous_log_level); 50 | assert_intn_equals(FALSE,result,L"freeing pages twice shouldn't work"); 51 | assert_intn_equals(1,get_logger_entry_count(ERROR)-prev_error_count,L"freeing pages twice should throw an error"); 52 | 53 | //allocating untracked pages then stopping shouldn't throw an error 54 | ptr=allocate_pages_ex(1,FALSE,AllocateAnyPages,NULL); 55 | prev_error_count=get_logger_entry_count(ERROR); 56 | stop_tracking_memory(); 57 | assert_intn_equals(0,get_logger_entry_count(ERROR)-prev_error_count,L"unfreed untracked page entries should be ignored on stopping"); 58 | free_pages_ex(ptr,1,FALSE); 59 | } 60 | 61 | /** 62 | * Makes sure pool memory tracking works. 63 | * 64 | * \test free_pool_memory_entries() should return the number of freed pool memory entries 65 | */ 66 | void test_pool_tracking() 67 | { 68 | UINTN result; 69 | 70 | free_pool_memory_entries(); 71 | 72 | //tracking n pool entries then freeing pool entries should result in rv n 73 | track_pool_memory(AllocatePool(1)); 74 | track_pool_memory(AllocatePool(20)); 75 | track_pool_memory(AllocatePool(12)); 76 | result=free_pool_memory_entries(); 77 | assert_intn_equals(3,result,L"function should return number of freed entries"); 78 | } 79 | 80 | 81 | /** 82 | * Test runner for this group. 83 | * Gets called via the generated test runner. 84 | * 85 | * \return whether the test group was executed 86 | */ 87 | BOOLEAN run_memory_tests() 88 | { 89 | INIT_TESTGROUP(L"memory"); 90 | RUN_TEST(test_page_tracking,L"page tracking"); 91 | RUN_TEST(test_pool_tracking,L"pool tracking"); 92 | FINISH_TESTGROUP(); 93 | } 94 | -------------------------------------------------------------------------------- /tests/suites/lib/pci.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Tests for the PCI library functions. 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_lib_pci 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | /** data type for test_find_pci_device_name() testcases */ 18 | typedef struct 19 | { 20 | UINT16 vendor_id; /**< the input vendor ID */ 21 | UINT16 device_id; /**< the input device ID */ 22 | CHAR16 *expected_name; /**< the expected PCI device name */ 23 | } pci_device_name_testcase_t; 24 | 25 | /** testcases for test_find_pci_device_name() */ 26 | pci_device_name_testcase_t pci_device_name_testcases[]= 27 | { 28 | {0x106b,0x003f,L"Apple Inc., KeyLargo/Intrepid USB"}, //first entry in shortened pci.ids, there was a strstr() bug affecting this 29 | {0x8086,0x2415,L"Intel Corporation, 82801AA AC'97 Audio Controller"}, 30 | {0x0000,0x0000,L"(unknown)"}, 31 | {0x8086,0x0000,L"Intel Corporation, unknown device"} 32 | }; 33 | 34 | /** 35 | * Makes sure determining PCI device names works. 36 | * 37 | * \test find_pci_device_name() finds entries with known vendor and device IDs 38 | * \test find_pci_device_name() finds known vendor entries and marks unknown device IDs 39 | * \test find_pci_device_name() marks unknown vendor IDs 40 | * \test find_pci_device_name() isn't affected by old strstr() bug 41 | */ 42 | void test_find_pci_device_name() 43 | { 44 | UINTN count=sizeof(pci_device_name_testcases)/sizeof(pci_device_name_testcase_t); 45 | UINTN tc; 46 | pci_device_name_testcase_t *cases=pci_device_name_testcases; 47 | 48 | init_pci_lib(); 49 | 50 | for(tc=0;tc 11 | #include 12 | #include 13 | 14 | 15 | /** 16 | * Makes sure the boolean assertions work. 17 | * 18 | * \test assert_true() and assert_false() check C-style truthy values and can be inverted 19 | */ 20 | void test_boolean() 21 | { 22 | assert_true(TRUE,L"should work"); 23 | assert_true(123,L"should work"); 24 | 25 | invert_next_assert=TRUE; 26 | assert_true(FALSE,L"should fail"); 27 | invert_next_assert=TRUE; 28 | assert_true(0,L"should fail"); 29 | 30 | assert_false(FALSE,L"should work"); 31 | assert_false(0,L"should work"); 32 | invert_next_assert=TRUE; 33 | assert_false(TRUE,L"should fail"); 34 | invert_next_assert=TRUE; 35 | assert_false(12,L"should fail"); 36 | } 37 | 38 | /** 39 | * Makes sure the integer comparison assertions work. 40 | * 41 | * \test assert_intn_equals() asserts `actual == expected` and can be inverted 42 | * \test assert_intn_greater_than_or_equal_to() asserts `actual >= expected` and can be inverted 43 | * \test assert_intn_less_than_or_equal_to() asserts `actual <= expected` and can be inverted 44 | * \test assert_intn_in_closed_interval() asserts `expected_lower <= actual <= expected_upper` and can be inverted 45 | */ 46 | void test_integer() 47 | { 48 | assert_intn_equals(1,1,L"should work"); 49 | invert_next_assert=TRUE; 50 | assert_intn_equals(5,2,L"should fail"); 51 | 52 | assert_intn_greater_than_or_equal_to(3,3,L"should work"); 53 | assert_intn_greater_than_or_equal_to(3,4,L"should work"); 54 | invert_next_assert=TRUE; 55 | assert_intn_greater_than_or_equal_to(4,3,L"should fail"); 56 | 57 | assert_intn_less_than_or_equal_to(3,3,L"should work"); 58 | assert_intn_less_than_or_equal_to(4,3,L"should work"); 59 | invert_next_assert=TRUE; 60 | assert_intn_less_than_or_equal_to(3,4,L"should fail"); 61 | 62 | assert_intn_in_closed_interval(-1,3,-1,L"should work"); 63 | assert_intn_in_closed_interval(-1,3, 0,L"should work"); 64 | assert_intn_in_closed_interval(-1,3, 3,L"should work"); 65 | 66 | invert_next_assert=TRUE; 67 | assert_intn_in_closed_interval(-1,3,-2,L"should fail"); 68 | invert_next_assert=TRUE; 69 | assert_intn_in_closed_interval(-1,3, 4,L"should fail"); 70 | } 71 | 72 | /** 73 | * Makes sure the double comparison assertions work. 74 | * 75 | * \test assert_double_near() asserts `expected-epsilon <= actual <= expected+epsilon` and can be inverted 76 | * \test assert_double_greater_than() asserts `actual > expected` and can be inverted 77 | * \test assert_double_greater_than_or_equal_to() asserts `actual >= expected` and can be inverted 78 | */ 79 | void test_double() 80 | { 81 | assert_double_near(10,0.1,10.0999,L"should pass"); 82 | invert_next_assert=TRUE; 83 | assert_double_near(10,0.1,10.1001,L"should fail"); 84 | assert_double_near(10,0.1,9.9001,L"should pass"); 85 | invert_next_assert=TRUE; 86 | assert_double_near(10,0.1,9.8999,L"should fail"); 87 | 88 | assert_double_greater_than(-20,-19.9999,L"should pass"); 89 | invert_next_assert=TRUE; 90 | assert_double_greater_than(-20,-20,L"should fail"); 91 | invert_next_assert=TRUE; 92 | assert_double_greater_than(-20,-20.0001,L"should fail"); 93 | 94 | assert_double_greater_than_or_equal_to(-20,-19.9999,L"should pass"); 95 | assert_double_greater_than_or_equal_to(-20,-20,L"should work"); 96 | invert_next_assert=TRUE; 97 | assert_double_greater_than_or_equal_to(-20,-20.0001,L"should fail"); 98 | 99 | assert_double_less_than(0.5,0.4999,L"should pass"); 100 | invert_next_assert=TRUE; 101 | assert_double_less_than(0.5,0.5,L"should fail"); 102 | invert_next_assert=TRUE; 103 | assert_double_less_than(0.5,0.5001,L"should fail"); 104 | 105 | 106 | assert_double_less_than_or_equal_to(0.5,0.4999,L"should pass"); 107 | assert_double_less_than_or_equal_to(0.5,0.5,L"should work"); 108 | invert_next_assert=TRUE; 109 | assert_double_less_than_or_equal_to(0.5,0.5001,L"should fail"); 110 | } 111 | 112 | /** 113 | * Makes sure the compound assertions work. 114 | * 115 | * \test assert_null() and assert_not_null() check for NULL values and can be inverted. 116 | */ 117 | void test_compounds() 118 | { 119 | assert_null(NULL,L"should work"); 120 | invert_next_assert=TRUE; 121 | assert_null(test_compounds,L"should fail"); 122 | 123 | assert_not_null(test_compounds,L"should work"); 124 | invert_next_assert=TRUE; 125 | assert_not_null(NULL,L"should fail"); 126 | } 127 | 128 | /** 129 | * Makes sure the pixel assertions work. 130 | * 131 | * \test assert_pixel() asserts pixel values match exactly and can be inverted 132 | * \test assert_pixel_near() asserts the sum of absolute differences between actual and expected pixels' channels is <=epsilon and can be inverted 133 | * 134 | * \TODO move this to the graphics tests, once they're in this repository 135 | */ 136 | void test_graphics() 137 | { 138 | EFI_GRAPHICS_OUTPUT_BLT_PIXEL data[]={{127,127,127,0},{150,127,127,0},{127,65,127,0},{127,127,0,0},{127,127,127,127},{127,127,127,0}}; 139 | 140 | assert_pixel(data[0],data[0],L"should work"); 141 | assert_pixel(data[0],data[5],L"should work"); 142 | invert_next_assert=TRUE; 143 | assert_pixel(data[0],data[1],L"should fail"); 144 | invert_next_assert=TRUE; 145 | assert_pixel(data[0],data[2],L"should fail"); 146 | invert_next_assert=TRUE; 147 | assert_pixel(data[0],data[3],L"should fail"); 148 | invert_next_assert=TRUE; 149 | assert_pixel(data[0],data[4],L"should fail"); 150 | 151 | assert_pixel_near(data[0],0,data[0],L"should work"); 152 | assert_pixel_near(data[0],23,data[1],L"should work"); 153 | invert_next_assert=TRUE; 154 | assert_pixel_near(data[0],22,data[1],L"should fail"); 155 | } 156 | 157 | 158 | /** 159 | * Test runner for this group. 160 | * Gets called via the generated test runner. 161 | * 162 | * \return whether the test group was executed 163 | */ 164 | BOOLEAN run_asserts_tests() 165 | { 166 | INIT_TESTGROUP(L"asserts"); 167 | RUN_TEST(test_boolean,L"boolean assertions"); 168 | RUN_TEST(test_integer,L"integer assertions"); 169 | RUN_TEST(test_double,L"floating point assertions"); 170 | RUN_TEST(test_compounds,L"compound/pointer assertions"); 171 | RUN_TEST(test_graphics,L"graphics assertions"); 172 | FINISH_TESTGROUP(); 173 | } 174 | -------------------------------------------------------------------------------- /tests/suites/selftest/graphics.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Self-tests for graphics test assertions/utilities 3 | * 4 | * \author Richard Nusser 5 | * \copyright 2017-2018 Richard Nusser 6 | * \license GPLv3 (see http://www.gnu.org/licenses/) 7 | * \sa https://github.com/rinusser/UEFIStarter 8 | * \ingroup group_tests_selftest 9 | */ 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | /** 17 | * Makes sure the difference tests and bounding box functions work. 18 | * 19 | * \test find_bounding_box_for_changes() finds initial bounding box if there were no changes 20 | * \test find_bounding_box_for_changes() finds 1x1 bounding box if just one pixel changed 21 | * \test find_bounding_box_for_changes() finds narrow bounding box if a line of pixel changed 22 | * \test find_bounding_box_for_changes() finds arbitrary bounding box for given changes 23 | * \test find_bounding_box_for_changes() finds image-sized bounding box if changes span entire image 24 | * \test assert_box_equals() compares all struct members 25 | */ 26 | void test_bounding_box() 27 | { 28 | graphics_difftest_t difftest; 29 | init_graphics_difftest(&difftest,20,20); 30 | 31 | find_bounding_box_for_changes(&difftest); 32 | assert_box_equals(&difftest.box,-1,-1,-1,-1,L"no differences"); 33 | 34 | difftest.after->data[20*10+7].Red=123; 35 | find_bounding_box_for_changes(&difftest); 36 | assert_box_equals(&difftest.box,7,10,7,10,L"1px difference"); 37 | 38 | difftest.after->data[20*10+8].Red=123; 39 | find_bounding_box_for_changes(&difftest); 40 | assert_box_equals(&difftest.box,7,10,8,10,L"2x1px difference"); 41 | 42 | difftest.after->data[0].Red=123; 43 | find_bounding_box_for_changes(&difftest); 44 | assert_box_equals(&difftest.box,0,0,8,10,L"9x11px difference"); 45 | 46 | difftest.after->data[19*20+19].Red=123; 47 | find_bounding_box_for_changes(&difftest); 48 | assert_box_equals(&difftest.box,0,0,19,19,L"full image size difference"); 49 | 50 | destroy_graphics_difftest(&difftest); 51 | } 52 | 53 | 54 | /** 55 | * Test runner for this group. 56 | * Gets called via the generated test runner. 57 | * 58 | * \return whether the test group was executed 59 | */ 60 | BOOLEAN run_graphics_tests() 61 | { 62 | INIT_TESTGROUP(L"graphics"); 63 | RUN_TEST(test_bounding_box,L"bounding box"); 64 | FINISH_TESTGROUP(); 65 | } 66 | -------------------------------------------------------------------------------- /tests/suites/selftest/runner.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * Self-tests for test runner. 3 | * This generates "incomplete" and "failure" results on purpose; the default test runner script will skip this test. 4 | * You'll have to check the test's result yourself, the console should show results matching the descriptions. 5 | * 6 | * \author Richard Nusser 7 | * \copyright 2017-2018 Richard Nusser 8 | * \license GPLv3 (see http://www.gnu.org/licenses/) 9 | * \sa https://github.com/rinusser/UEFIStarter 10 | * \ingroup group_tests_selftest 11 | */ 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | /** 18 | * Should produce a SUCCESS result in the test runner. 19 | * 20 | * \test making a successful assertion results in SUCCESS for the test 21 | */ 22 | void test_runner_success() 23 | { 24 | assert_null(NULL,L"should pass"); 25 | } 26 | 27 | /** 28 | * Should produce an ERROR result in the test runner. 29 | * 30 | * \test making a failing assertion results in FAILURE for the test 31 | */ 32 | void test_runner_failure() 33 | { 34 | assert_not_null(NULL,L"should fail"); 35 | } 36 | 37 | /** 38 | * Should produce an INCOMPLETE but successful result in the test runner. 39 | * 40 | * \test marking a test incomplete when there are successful assertions results in INCOMPLETE but success for the test 41 | */ 42 | void test_runner_incomplete_success() 43 | { 44 | assert_null(NULL,L"should pass"); 45 | mark_test_incomplete(); 46 | } 47 | 48 | /** 49 | * Should produce an INCOMPLETE but unsucessful result in the test runner. 50 | * 51 | * \test marking a test incomplete when there are failed assertions results in INCOMPLETE but failure for the test 52 | */ 53 | void test_runner_incomplete_failure() 54 | { 55 | assert_not_null(NULL,L"should fail"); 56 | mark_test_incomplete(); 57 | } 58 | 59 | /** 60 | * Should produce an INCOMPLETE (no assertions) result in the test runner. 61 | * 62 | * \test tests without any assertions are marked as incomplete 63 | */ 64 | void test_runner_empty() 65 | { 66 | } 67 | 68 | 69 | /** 70 | * Test runner for this group. 71 | * Gets called via the generated test runner. 72 | * 73 | * \return whether the test group was executed 74 | */ 75 | BOOLEAN run_runner_tests() 76 | { 77 | INIT_TESTGROUP(L"runner"); 78 | RUN_TEST(test_runner_success,L"runner success"); 79 | RUN_TEST(test_runner_failure,L"runner failure"); 80 | RUN_TEST(test_runner_incomplete_success,L"runner incomplete success"); 81 | RUN_TEST(test_runner_incomplete_failure,L"runner incomplete failure"); 82 | RUN_TEST(test_runner_empty,L"runner without tests"); 83 | FINISH_TESTGROUP(); 84 | } 85 | -------------------------------------------------------------------------------- /tests/suites/selftest/testself.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.25 3 | BASE_NAME = testself 4 | FILE_GUID = 871898a8-41d5-4fa5-a813-f6bea9f00010 5 | MODULE_TYPE = UEFI_APPLICATION 6 | VERSION_STRING = 1.0 7 | ENTRY_POINT = ShellCEntryLib 8 | 9 | [Sources] 10 | generated/runner.c 11 | asserts.c 12 | runner.c 13 | graphics.c 14 | 15 | [Packages] 16 | MdePkg/MdePkg.dec 17 | ShellPkg/ShellPkg.dec 18 | StdLib/StdLib.dec 19 | UEFIStarter/UEFIStarter.dec 20 | 21 | [LibraryClasses] 22 | UefiLib 23 | ShellCEntryLib 24 | LibMath 25 | StdLib 26 | UEFIStarterCore 27 | UEFIStarterGraphics 28 | UEFIStarterTests 29 | 30 | [Guids] 31 | 32 | [Ppis] 33 | 34 | [Protocols] 35 | 36 | [FeaturePcd] 37 | 38 | [Pcd] 39 | 40 | -------------------------------------------------------------------------------- /tools/create_package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | PACKAGE_DIR=$1 3 | SOURCE_NAME=UEFIStarter 4 | 5 | _error() { 6 | echo "Syntax: $(basename $0) " 7 | echo -e "\nERROR: $2" 8 | exit $1 9 | } 10 | 11 | _info() { 12 | echo $* 13 | } 14 | 15 | 16 | if [ ! -f "Conf/target.txt" ] || [ ! -e "$SOURCE_NAME" ]; then 17 | _error -1 "You need to execute this script in the edk2 sources root with UEFIStarter installed." 18 | fi 19 | 20 | # check directory parameter 21 | [ -z "$PACKAGE_DIR" ] && _error -2 "You need to specify a package directory to create." 22 | [ -e "$PACKAGE_DIR" ] && _error -3 "The package directory must not exist, it will be created automatically." 23 | 24 | # extract project name 25 | PROJECT_NAME=`basename "$PACKAGE_DIR"` 26 | 27 | [ -e Makefile ] && [ ! -L Makefile ] && _error -4 "'Makefile' must either be a symlink or not exist." 28 | 29 | 30 | _info "creating project directory and link if necessary..." 31 | mkdir -p "$PACKAGE_DIR" 32 | [ -e "$PROJECT_NAME" ] || ln -s "$PACKAGE_DIR" "$PROJECT_NAME" 33 | 34 | 35 | _info "adding package basics to project..." 36 | cp $SOURCE_NAME/*.dec $PROJECT_NAME/$PROJECT_NAME.dec 37 | cp $SOURCE_NAME/*.dsc $PROJECT_NAME/$PROJECT_NAME.dsc 38 | cp $SOURCE_NAME/Makefile.edk $PROJECT_NAME/ 39 | 40 | mkdir "$PROJECT_NAME/static" 41 | echo -e "FS0:\napp" > "$PROJECT_NAME/static/startup.nsh" 42 | 43 | 44 | _info "editing package basics..." 45 | 46 | # change package name/guid in .dec file 47 | sed -i 's/^\(\s*PACKAGE_NAME\s*=\s*\).*/\1'"$PROJECT_NAME/" $PROJECT_NAME/*.dec 48 | sed -i 's/^\(\s*PACKAGE_GUID\s*=\s*\).*/\1'"$(uuid)/" $PROJECT_NAME/*.dec 49 | 50 | # change platform name/guid in .dsc file 51 | sed -i 's/^\(\s*PLATFORM_NAME\s*=\s*\).*/\1'"$PROJECT_NAME/" $PROJECT_NAME/*.dsc 52 | sed -i 's/^\(\s*PLATFORM_GUID\s*=\s*\).*/\1'"$(uuid)/" $PROJECT_NAME/*.dsc 53 | sed -i 's/^\(\s*OUTPUT_DIRECTORY\s*=\s*\).*/\1Build\/'"$PROJECT_NAME/" $PROJECT_NAME/*.dsc 54 | 55 | # remove useless libraries 56 | sed -i 's/^.*library\/core\/.*//' $PROJECT_NAME/*.dsc 57 | 58 | # replace components and remove extra newlines 59 | cat $PROJECT_NAME/*.dsc | tr '\n' '\001' | sed 's/\(\[Components\]\).*\(!include\)/\1\n '"$PROJECT_NAME"'\/apps\/app.inf\n\n\2/' | sed 's/\x01\x01\x01*/\x01\x01/g' | tr '\001' '\n' > $PROJECT_NAME/*.dsc 60 | 61 | 62 | _info "writing demo application..." 63 | 64 | mkdir $PROJECT_NAME/apps 65 | 66 | echo "#include 67 | #include 68 | #include 69 | 70 | 71 | INTN EFIAPI ShellAppMain(UINTN argc, CHAR16 **argv) 72 | { 73 | EFI_STATUS rv; 74 | if((rv=init(argc,argv,0))!=EFI_SUCCESS) 75 | return rv; 76 | 77 | Print(L\"Hello, world!\\n\"); 78 | //insert or call your code here 79 | 80 | shutdown(); 81 | return EFI_SUCCESS; 82 | }" >> $PROJECT_NAME/apps/app.c 83 | 84 | echo "[Defines] 85 | INF_VERSION = 1.25 86 | BASE_NAME = app 87 | FILE_GUID = "`uuid`" 88 | MODULE_TYPE = UEFI_APPLICATION 89 | VERSION_STRING = 1.0 90 | ENTRY_POINT = ShellCEntryLib 91 | 92 | [Sources] 93 | app.c 94 | 95 | [Packages] 96 | MdePkg/MdePkg.dec 97 | ShellPkg/ShellPkg.dec 98 | UEFIStarter/UEFIStarter.dec 99 | 100 | [LibraryClasses] 101 | UefiLib 102 | ShellCEntryLib 103 | UEFIStarterCore" >> $PROJECT_NAME/apps/app.inf 104 | 105 | 106 | _info "updating edk2 build target..." 107 | sed -i 's/^\(\s*ACTIVE_PLATFORM\s*=\s*\).*/\1'"$PROJECT_NAME\/$PROJECT_NAME.dsc/" Conf/target.txt 108 | 109 | 110 | _info "editing package's Makefile..." 111 | sed -i "s/$SOURCE_NAME/$PROJECT_NAME/g" $PROJECT_NAME/Makefile.edk 112 | sed -i 's/\(.*\/\(tools\|tests\)\/.*\)/#\1/' $PROJECT_NAME/Makefile.edk 113 | 114 | 115 | _info "updating Makefile symlink..." 116 | [ -e Makefile ] && rm Makefile 117 | ln -s "$PROJECT_NAME/Makefile.edk" Makefile 118 | 119 | 120 | _info "all done." 121 | echo "" 122 | echo "You can build and run your new package with:" 123 | echo " make && make run" 124 | -------------------------------------------------------------------------------- /tools/generate_runall_tests_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Generates a script to run all testsuites 3 | # Outputs script to stdout - gets used by Makefile.edk to create tests/run.nsh 4 | # 5 | # \author Richard Nusser 6 | # \copyright 2017-2018 Richard Nusser 7 | # \license GPLv3 (see http://www.gnu.org/licenses/) 8 | # \link https://github.com/rinusser/UEFIStarter 9 | # 10 | 11 | dir=$1 12 | [ "$dir" == "" ] && dir=tests/suites 13 | suites=`\grep -hE "^[[:space:]]*BASE_NAME[[:space:]=]*" $dir/*/*.inf | sed 's/^[^=]*=[[:space:]*]//'` 14 | scriptfile=/dev/stdout 15 | 16 | echo "@echo -off" > $scriptfile 17 | 18 | #run self test suite first to make sure test framework actually works as expected and so (usually useless) results are the first thing to go off-screen 19 | for suite in $suites; do 20 | [ "$suite" == "testself" ] && echo $suite -skip runner >> $scriptfile -verbosity 3 21 | done 22 | 23 | #run other test suites 24 | for suite in $suites; do 25 | [ "$suite" == "testself" ] || echo $suite >> $scriptfile -verbosity 3 26 | done 27 | -------------------------------------------------------------------------------- /tools/generate_test_runner.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Generates test suite runners, one for each test suite 3 | # 4 | # \TODO don't replace existing file if nothing changed, otherwise "make" will rebuild test suite every time 5 | # 6 | # \author Richard Nusser 7 | # \copyright 2017-2018 Richard Nusser 8 | # \license GPLv3 (see http://www.gnu.org/licenses/) 9 | # \link https://github.com/rinusser/UEFIStarter 10 | # 11 | 12 | suitesdir=UEFIStarter/tests/suites/ 13 | 14 | generate() { 15 | testsdir=$1 16 | if [ ! -d "$testsdir" ]; then 17 | echo "internal error: $testsdir doesn't exist" 18 | exit 19 | fi 20 | 21 | mkdir -p $testsdir/generated 22 | runner=$testsdir/generated/runner.c 23 | echo generating $runner 24 | funcs=`\grep -hoE "BOOLEAN run_.*_tests\(\)" $testsdir/*.c | colrm 1 7 | tr '()' ' '` 25 | now=`date +"%Y-%m-%d %H:%M:%S %Z (UTC%:::z)"` 26 | 27 | echo "//DO NOT EDIT" > $runner 28 | echo "//This file was generated automatically by $0 at $now" >> $runner 29 | echo "" >> $runner 30 | 31 | echo "#include " >> $runner 32 | echo "" >> $runner 33 | 34 | for func in $funcs; do 35 | echo "BOOLEAN $func();" >> $runner 36 | done 37 | 38 | echo "" >> $runner 39 | 40 | echo -e "void run_tests()\n{" >> $runner 41 | for func in $funcs; do 42 | echo " run_group($func);" >> $runner 43 | done 44 | echo "}" >> $runner 45 | } 46 | 47 | for dir in `find $suitesdir -maxdepth 1 -mindepth 1 -type d `; do 48 | generate $dir 49 | done 50 | -------------------------------------------------------------------------------- /vagrant/Vagrantfile: -------------------------------------------------------------------------------- 1 | # Vagrantfile for UEFIStarter development VM 2 | require 'yaml' 3 | 4 | settings = YAML.load_file './config/vagrant-config.yml' 5 | 6 | Vagrant.configure("2") do |config| 7 | config.vm.box = "ubuntu/artful64" 8 | config.vm.define "dev" 9 | 10 | config.vm.post_up_message = "'UEFIStarter' development VM. Use 'vagrant ssh' or your installed ssh key (localhost:2222) to connect, then go to /usr/src/edk2 and execute 'make run'" 11 | config.ssh.forward_agent = true 12 | 13 | # disable default global share: in some setups this attempts to copy all your harddisks' and mounted volumes' data into the VM. 14 | config.vm.synced_folder "/", "/vagrant", disabled: true 15 | 16 | # mount the "code" share if it's configured in the settings file 17 | if settings.key?('shares') and settings['shares'] and settings['shares'].key?('code') and settings['shares']['code'] 18 | config.vm.synced_folder settings['shares']['code'], "/mnt/ueficode" 19 | end 20 | 21 | # set basic VM settings and add a larger disk 22 | config.vm.provider "virtualbox" do |vb| 23 | vb.gui = true 24 | vb.memory = "2048" 25 | 26 | docker_disk_file="./data.vdi" 27 | if not File.exists?(docker_disk_file) 28 | vb.customize ["createmedium","--filename",docker_disk_file,"--variant","Standard","--size",8192] 29 | vb.customize ["storageattach",:id,"--storagectl","SCSI","--port",3,"--device",0,"--type","hdd","--medium",docker_disk_file] 30 | end 31 | end 32 | 33 | # copy configuration and preference files to VM 34 | config.vm.provision "file", source: "config", destination: "/dev/shm/config" 35 | 36 | # set VM's hostname and timezone 37 | config.vm.provision "shell" do |sh| 38 | sh.inline = " 39 | sed -i \"s/stretch/$1/g\" /etc/hosts 40 | hostnamectl set-hostname $1 41 | timedatectl set-timezone $2" 42 | sh.args = [settings["hostname"],settings["timezone"]] 43 | end 44 | 45 | # customize user account's git and ssh 46 | config.vm.provision "shell", privileged: false, inline: " 47 | cd /home/vagrant/ 48 | cp /dev/shm/config/gitconfig ./.gitconfig 49 | cat /dev/shm/config/authorized_keys >> .ssh/authorized_keys 50 | ln -s /usr/src/edk2" 51 | 52 | # prepare the build environment 53 | config.vm.provision "shell", inline: " 54 | # create and format partition on bigger storage disk, then mount it on /mnt/data 55 | echo -e \"n\np\n1\n\n\nw\nq\n\" | fdisk /dev/sdc 56 | mkfs.ext4 /dev/sdc1 57 | mkdir /mnt/data 58 | mount /dev/sdc1 /mnt/data 59 | chown -R vagrant /mnt/data/ 60 | 61 | # install additional software 62 | apt-get update 63 | apt-get install -y git vim ctags vim-scripts screen dos2unix build-essential uuid-dev iasl nasm python mkisofs \\ 64 | qemu-system-x86 doxygen alsa-utils alsa-base linux-image-extra-`uname -r` uuid 65 | 66 | # load sound kernel module and grant access to the sound system 67 | modprobe snd-intel8x0 68 | usermod -G audio vagrant 69 | 70 | # customize a few applications and services 71 | cat /dev/shm/config/screenrc.additional >> /etc/screenrc 72 | cat /dev/shm/config/vimrc.additional >> /etc/vim/vimrc 73 | cat /dev/shm/config/bashrc.additional >> /etc/bash.bashrc 74 | cp /dev/shm/config/pablo_rnfix.vim /usr/share/vim/vim80/colors/ 75 | chmod a-x /etc/update-motd.d/[1-5]* 76 | 77 | # prepare edk2 sources root on larger filesystem 78 | ln -s /mnt/data/edk2 /usr/src/edk2 79 | 80 | # forward base directory for custom code to larger filesystem if it isn't mounted as a shared volume already 81 | [ -d /mnt/ueficode ] || ln -s /mnt/data /mnt/ueficode 82 | 83 | # create mount point for building UEFI images 84 | mkdir /mnt/uefi" 85 | 86 | # fetch code, finish setup and build 87 | config.vm.provision "shell", privileged: false, inline: " 88 | # download edk2 89 | mkdir /mnt/data/edk2 90 | cd /usr/src/edk2 91 | git init 92 | git remote add origin https://github.com/tianocore/edk2.git 93 | git pull origin UDK2017 94 | 95 | # add UEFIStarter to edk2, download if necessary 96 | ln -s /mnt/ueficode/UEFIStarter 97 | ln -s UEFIStarter/Makefile.edk Makefile 98 | if [ ! -d /mnt/ueficode/UEFIStarter ]; then 99 | mkdir /mnt/ueficode/UEFIStarter 100 | cd /usr/src/edk2/UEFIStarter 101 | git init 102 | git remote add github https://github.com/rinusser/UEFIStarter.git 103 | git pull github master 104 | fi 105 | 106 | # prepare build environment 107 | cd /usr/src/edk2 108 | make -C BaseTools 109 | . edksetup.sh 110 | sed -i 's/^TARGET_ARCH.*=.*/TARGET_ARCH = X64/' Conf/target.txt 111 | sed -i 's/^TOOL_CHAIN_TAG.*=.*/TOOL_CHAIN_TAG = GCC5/' Conf/target.txt 112 | sed -i 's/^MAX_CONCURRENT_THREAD_NUMBER.*=.*/MAX_CONCURRENT_THREAD_NUMBER = 3/' Conf/target.txt 113 | sed -i 's/^DEFINE GCC5_X64_CC_FLAGS.*=.*/DEFINE GCC5_X64_CC_FLAGS = DEF(GCC49_X64_CC_FLAGS) -Wno-error=unused-const-variable -Wno-error=misleading-indentation/' Conf/tools_def.txt 114 | 115 | # build OVMF image 116 | sed -i 's/^ACTIVE_PLATFORM.*=.*/ACTIVE_PLATFORM = OvmfPkg\\/OvmfPkgX64.dsc/' Conf/target.txt 117 | build 118 | sudo cp Build/OvmfX64/DEBUG_GCC5/FV/OVMF.fd /usr/share/qemu/ 119 | 120 | # build UEFIStarter 121 | sed -i 's/^ACTIVE_PLATFORM.*=.*/ACTIVE_PLATFORM = UEFIStarter\\/UEFIStarter.dsc/' Conf/target.txt 122 | make 123 | " 124 | 125 | end 126 | # -*- mode: ruby -*- 127 | # vi: set ft=ruby : 128 | -------------------------------------------------------------------------------- /vagrant/config/authorized_keys: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rinusser/UEFIStarter/8d5b385e0de6db2c0917d754623a8c351edfc5c5/vagrant/config/authorized_keys -------------------------------------------------------------------------------- /vagrant/config/bashrc.additional: -------------------------------------------------------------------------------- 1 | # this will get appended to /etc/bash.bashrc 2 | 3 | alias ls='ls --color=auto' 4 | alias ll='ls -la' 5 | alias NOW='date +%Y%m%d%H%M%S' 6 | alias TODAY='date +%Y%m%d' 7 | alias grep='grep -H -n --color=auto' 8 | 9 | export TERM=xterm-256color 10 | export EDITOR=vim 11 | 12 | alias hi='history | \grep' 13 | -------------------------------------------------------------------------------- /vagrant/config/gitconfig: -------------------------------------------------------------------------------- 1 | [user] 2 | name = "Change Me" 3 | email = "changeme@example.com" 4 | -------------------------------------------------------------------------------- /vagrant/config/pablo_rnfix.vim: -------------------------------------------------------------------------------- 1 | " Modded version of "pablo" color scheme: newer vim versions ignore custom color overrides outside scheme files (vim bug #542). 2 | " The workaround is moving all overrides into the scheme file, so here it is. 3 | " 4 | " - Richard Nusser, 2017-04-26 5 | " 6 | 7 | hi clear 8 | set background=dark 9 | if exists("syntax_on") 10 | syntax reset 11 | endif 12 | let g:colors_name="pablo_rnfix" 13 | 14 | highlight Comment ctermfg=8 guifg=#808080 15 | highlight Constant ctermfg=14 cterm=none guifg=#00ffff gui=none 16 | highlight Identifier ctermfg=6 guifg=#00c0c0 17 | highlight Statement ctermfg=3 cterm=bold guifg=#c0c000 gui=bold 18 | highlight PreProc ctermfg=10 guifg=#00ff00 19 | highlight Type ctermfg=2 guifg=#00c000 20 | highlight Special ctermfg=12 guifg=#0000ff 21 | highlight Error ctermbg=9 guibg=#ff0000 22 | highlight Todo ctermfg=4 ctermbg=3 guifg=#000080 guibg=#c0c000 23 | highlight Directory ctermfg=2 guifg=#00c000 24 | highlight StatusLine ctermfg=11 ctermbg=12 cterm=none guifg=#ffff00 guibg=#0000ff gui=none 25 | highlight Normal guifg=#ffffff guibg=#000000 26 | highlight Search ctermbg=3 guibg=#c0c000 27 | 28 | highlight StatusLine ctermfg=253 ctermbg=236 guifg=#d0d0d0 guibg=#303030 29 | highlight TAG_DEBUG ctermfg=255 ctermbg=242 30 | highlight Search ctermbg=192 31 | highlight Todo ctermbg=202 32 | -------------------------------------------------------------------------------- /vagrant/config/screenrc.additional: -------------------------------------------------------------------------------- 1 | # this will be appended to /etc/screenrc 2 | startup_message off 3 | backtick 1 0 1 date '+%Y-%m-%d %H:%M:%S UTC%:::z' 4 | hardstatus alwayslastline "%{kg}%?%-Lw%?%{WK}%f%n %t%{kg}%?%+Lw%?%-46=%{Kg}[%{kg}%{kg}uefistarter-dev %{Kg}|%{kg} %1`%{Kg}]" 5 | vbell on 6 | defscrollback 3000 7 | bind j select 8 | bind x 9 | -------------------------------------------------------------------------------- /vagrant/config/vagrant-config.yml: -------------------------------------------------------------------------------- 1 | # Configuration file for UEFIStarter VM 2 | 3 | shares: 4 | # Set this to your UEFI projects' root directory, e.g. "/my/code" if your copy of UEFIStarter is in /my/code/UEFIStarter. 5 | # Make sure you escape backslashes in Windows paths, e.g. "c:\\my\\code". 6 | # 7 | # You can comment this out if you want, in which case UEFIStarter will be fetched from GitHub. 8 | code: "/path/to/uefiprojects" 9 | 10 | # Set this to your preferred timezone as listed in the tz database, for example "Antarctica/South_Pole" 11 | # See e.g. https://en.wikipedia.org/wiki/List_of_tz_database_time_zones for reference. 12 | timezone: "UTC" 13 | 14 | # You can change the VM's hostname here if you want 15 | hostname: "uefistarter-dev" 16 | -------------------------------------------------------------------------------- /vagrant/config/vimrc.additional: -------------------------------------------------------------------------------- 1 | " this will be appended to /etc/vim/vimrc 2 | syntax on 3 | set background=dark 4 | if has("autocmd") 5 | au BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif 6 | endif 7 | set ignorecase 8 | set ruler 9 | set showmatch 10 | set showmode 11 | set esckeys 12 | set backspace=indent,eol,start 13 | color pablo_rnfix 14 | set shiftwidth=2 15 | set tabstop=2 16 | set hlsearch 17 | set smartcase 18 | set smartindent 19 | set number 20 | set expandtab 21 | highlight StatusLine ctermbg=236 ctermfg=253 guifg=#d0d0d0 guibg=#303030 22 | set laststatus=2 23 | set pastetoggle= 24 | 25 | set foldmethod=indent 26 | set foldlevelstart=20 27 | 28 | highlight TAG_DEBUG ctermbg=242 ctermfg=255 29 | match TAG_DEBUG /DEBUG/ 30 | 31 | highlight Search ctermbg=192 32 | highlight Todo ctermbg=202 33 | 34 | "F9/[shift-]F10: comments color 35 | map [33~ :hi comment ctermfg=247 36 | map [34~ :hi comment ctermfg=244 37 | map [21~ :hi comment ctermfg=8 38 | 39 | 40 | "F2: remove search highlights 41 | map [[B :noh 42 | 43 | "F5/F6: open/close indents 44 | map [[E zc 45 | map [17~ zo 46 | 47 | "shift-F5/shift-F6: open/close all 48 | "map [28~ zC 49 | "map [29~ zO 50 | 51 | command W w 52 | command Q q 53 | --------------------------------------------------------------------------------