Jump to content
  • Sky
  • Blueberry
  • Slate
  • Blackcurrant
  • Watermelon
  • Strawberry
  • Orange
  • Banana
  • Apple
  • Emerald
  • Chocolate
  • Charcoal

OETF #16 - Open Extensible Firmware Interface

Recommended Posts

Open Extensible Firmware Interface v2.2


Lua BIOSes that implements OEFI must allow using conventional booting.
Note that OEFI has been made to be more architecture-independent and will support ANY architecture able to read files and CPIO (basically any architecture with basic access to component and bitwise operations).


  • Application: Executable code booting using OEFI in the EFI1 or EFI2 format.
  • Conventional Booting: OpenComputers default booting method. Often called "BIOS"
  • Operating System (OS) : Complex Application with user interaction and/or providing an environment for programs. Concept only used in this document for Conventional Booting
  • OC: Shortcut for "Open Computers"


Due to the limitation of current OC booting method which basically searches for an init.lua file, the most big problem is that this only work for Lua architecture, and what should other architectures use, init.asm? init.test? init.o? What happens if we want one drive to contains multiple inits for multiple architectures? Well that's where Open Extensible Firmware Interface comes handy. Using . The limitations OEFI fix in Conventional Booting in OC are:

  • In Conventional Booting, there is only one init script per filesystem, while in OEFI there can be multiple applications per filesystem.
  • Conventional Booting doesn't have any native support for multiple applications. An OS that want to boot another one need to manually check for init.lua files in all filesystems, OEFI has oefi.getApplications() method for it, this is even worse for Applications booting on unmanaged drives
  • Conventional Booting isn't made for any other architecture than Lua. And with it, like addressed below, it's impossible to have multiple applications for multiple architectures, while OEFI allows in theory to have one same drive to boot on all architectures it support, without tweaks needed from the user!

OEFI is basically a much more extensible version of OETF #1 « Standardized booting of nonstandard architectures »



  • Removed OEFI configuration data, EEPROM data is now entirely up to the implementation.
  • The specification now goes deeper into some problems (like C struct ordering, non-Lua architecture integration, or even how API version works in an architecture without FPU)


  • Changed EFI2 format into a properties file for easier parsing
  • Using CPIO instdead of URF for same reason: easier parsing
  • Added OEFI Extensions, oefi.loadInternalFile(path) and oefi.setApplications(appTable)
  • Fixed having put AwesomeCatgirl instdead of AdorableCatgirl as username for CAP conntribution

Application Array

The application array must contains tables (on Lua) as entries, if not on Lua those must be arrays (described below).
If using tables, it must contains the drive address with as « drive » entry and the path (from drive) as « path » entry


    drive = "af58dbe2-ff8c-433a-a871-c08723e01f25",
    path = ".efi/sampleos.efi"
    drive = "af58dbe2-ff8c-433a-a871-c08723e01f25",
    path = ".efi/osOnSameDrive.efi2"
    drive = "015f9d4c-cdfb-4686-91cb-bc8cf997f4ec",
    path = ".efi/another_os.efi"

On non-Lua architectures, "drive" is index 0 and "path" is index 1


	["af58dbe2-ff8c-433a-a871-c08723e01f25", ".efi/sampleos.efi"],
	["af58dbe2-ff8c-433a-a871-c08723e01f25", ".efi/osOnSameDrive.efi2"],
	["015f9d4c-cdfb-4686-91cb-bc8cf997f4ec", ".efi/another_os.efi"]

As a 2D array, the entries should be appended in such a way it look like that: drive, path, drive, path, drive, path, ... . This is the default standard used by C for multi-dimensional arrays. If the language supports multi-dimensional arrays then the arrays of the language must be used.

Finding Applications

OEFI Applications can be found on any sort of drives, from disk drives to floppy, with raids, only if they contains a ".efi" directory.
The OEFI should search files in ".efi" directory:

If the file ends with .efi, then check if it contains CPIO signature, if no, boot in compatibility mode (OEFI1, which is not described anymore on this document).
However if the same file contains that signature, then boot as normal.
.efi2 will ALWAYS be in standard EFI2 format, unlike .efi which can be EFI or EFI2.
.efi can be EFI1 or EFI2 to keep compatibility with old OEFI Applications, and to allow 8.3 filesystems to still support OEFI (would be problematic otherwise)

API Methods

Methods are same as in Version 1, of course API version is now 2 (logic), and some new methods are being added, so an implementation of OEFI must implement those methods (ignore oefi prefix),
functions that can always be relied on (that are not optional) are in italic:

  • oefi.loadInternalFile(path) - Loads file from current archive - new in OEFI 2.1
  • oefi.getAPIVersion() - Returns API version number. Unless an FPU is present in the architecture, version is represented as an 8.8 fixed point number, otherwise it is a 32-bit floating point number. This is 2.1 for this version.
  • oefi.getImplementationName() - Returns implementation name
  • oefi.getImplementationVersion() - Returns implementation version. Same number rules as for oefi.getAPIVersion().
  • oefi.getBootAddress() - replaces computer.getBootAddress()
  • oefi.getApplications() - Optional, return an application table as described above
  • oefi.setApplications(appTable) - Optional, set the application table (as described above) to appTable. - new in OEFI 2.1
  • oefi.loadfile(path) - Deprecated and optional, loads file from boot drive. (New Applications are expected to create their own loadfile, which is very simple to do)
  • oefi.returnToOEFI() - Optional, recalls the OEFI program, the expected behavior is that the implementation will try to recover boot environment the most as possible, and then re-execute itself.
  • oefi.execOEFIApp(drive, path) - Optional, boot another OEFI app by using the implementation's routine.
  • oefi.getExtensions() - Optional, OEFI EXTensions is for non-standard features from implementation. This should always be used for implementation methods. The way this work is described below - new in OEFI 2.1

In Lua, API must be available in Global as the "oefi" table
Else, API must be available as a pointer to a struct/class/anything like that available as argument to the code.

Note: Optional functions are nil in Lua when not present, and are NULL in C (or any other language) when not present.

For structs, their pointer must be passed as argument to boot function, not their content! Official C struct (not really easy to read for C beginners, mostly due to function pointers, but they are just basically pointers to a function: function pointers):

struct application_entry {
  char* drive;
  char* path;

struct extension_entry {
  char* name;
  void* function; // pointer to any function

#ifdef NO_FPU // if the architecture doesn't have an FPU
  typedef float uint16_t; // define float as an 8.8 fixed point number

struct oefi_struct {
  char* (*loadfile)(char*);
  char* (*loadInternalFile)(char*);
  char* (*getBootAddress);
  application_entry* (*getApplications)(); // returns an array of application entries
  void (*setApplications)(application_entry*);
  float (*getAPIVersion)();
  char* (*getImplementationName)();
  float (*getImplementationVersion)();
  void (*returnToOEFI)();
  void (*execOEFIApp)(char*, char*);
  extension_entry* (*getExtensions)(); // returns an array of extension entries

Implements must have the same order as that C struct to preserve compatibility.

OEFI Extensions work in the following way: If the architecture supports entries with keys, then a entry should be created containing the function (or its pointer), and the key should be equals to ImplementationName_MethodName_MethodVersion, ImplementationName should be same as in oefi.getImplementationName(), MethodVersion should be the revision number of a method, starts at 1. And MethodName is the name of the method. Example: SuperOEFI_DrawHandSpinner_2, which could be called (in Lua) via oefi.getExtensions().SuperOEFI_DrawHandSpinner_2()

If tables are not supported, then the array (returned by oefi.getExtensions()) should contain all the methods as other arrays. So oefi_ext will be a 2D array. Each array (or struct) should contain the name (see above) at the first index (0 or 1, depends on architecture),  and a pointer to the function (or the function itself, if possible) at second index.

Please also note that computer.setBootAddress(addr) should be REMOVED or throw an error, as due to how OEFI works, it is impossible for that function to work correctly! This is due to that Applications supporting OEFI doesn't need any compatibility methods as thoses conventional booting methods are only kept when booting a OS that only support conventional booting.
Implementations can create their own methods, however it should ALWAYS BE INSIDE oefi.getExtensions(). (see above)


EFI2 is the format for Applications, it replaces EFI(1) in , it is a CPIO archive. All files in that archive must be placed at root and are listed here:

  • app.cfg
  • app.exe

app.exe is only the Application, it contains the code designed for target architecture, and will be launched by the OEFI .

app.cfg is a configuration file using "key=value" scheme. The file should look like this:

name=Application Name

Space after key or before name is parsed as it and aren't ignored, be careful!

"name" is equals to the name of the Application. Will always be a text
"version" is equals to the version of the Application, if you don't want to fill it, just make it stay to 1.0. Will always be a decimal
"arch" is a string with the name of supported architecture, "archMinVer" and "archMaxVer" are self-explanatory and will always be a decimal.
Note that for archMinVer and archMaxVer, -1 can be used if a version doesn't make sense to the architecture to have a version. (example: a 6502 architecture)
"oefiVersion" is equals to the OEFI version the Application has been designed to run with, if the "oefiVersion" field have a version incompatible with the current API version or if it's higher than the current one (ex. it's equals to 3 but we're on API v2.1, or it's equals to 1 but we're on API v2). This will also always be a decimal

And finally, new lines are Unix-style: \n (line feed)

With all thoses fields in mind, the EFI2 was designed to be durable, with only changes being to app.lon

Configuration Data

What's in EEPROM's data is up to the Implementation.



Share this post

Link to post
Share on other sites

I was thinking about component IDs. Shouldn't they be minified? Like, "68ca0f59-ac2c-49f8-ba6a-3c4c7e5f069b" turns into a string of 16 bytes:

{0x68, 0xCA, 0x0F, 0x59, 0xAC, 0x2C, 0x49, 0xF8, 0xBA, 0x6A, 0x3C, 0x4C, 0x7E, 0x5F, 0x06, 0x9B}

Also, OEFI implementations need to have room for custom configuration, as something like Zorya needs some EEPROM space for knowing the device that the "zorya-module" and "zorya-cfg" folders are stored on. Maybe 64 bytes or so can be dedicated to custom config? 64 bytes should be plenty of space for basic configuration, yeah? 32 bytes would be too little, probably, as a component ID can only be shrunk down to 16 bytes.

As for how to convert binary component ID to text component ID and back again:

function binid_to_hexid(id)
  local f, r = string.format, string.rep
  return f(f("%s-%s%s", r("%.2x", 4), r("%.2x%.2x-", 3), r("%.2x", 6)), id:byte(1, 16))

function hexid_to_binid(id)
  local lasthex = 0
  local match = ""
  local bstr = ""
  for i=1, 16 do
    match, lasthex = id:match("%x%x", lasthex)
    bstr = bstr .. string.char(tostring(match, 16))
  return bstr

Otherwise, this looks pretty good.

Share this post

Link to post
Share on other sites

Thanks for the review. I will make a version 2 (on same post) to add the changes you suggested and some others changes. I will also soon make a more presentable form of the specification.

Share this post

Link to post
Share on other sites

So, some recommendations for OEFI v2:

  • Storage of addresses in a binary format
  • Maybe a standard set of commands for an OEFI shell?
  • "oefi.loadfile(path)", which loads a file from the boot device
  • Vendor prefixes on extensions to OEFI (ie "oefi.vendor.extensionmethod()")
  • BIOS configuration space of 64 bytes, at the end of the EEPROM
  • Maybe a network boot protocol?
  • The OEFI library should be global unless booting in compatibility mode (for example, from an init.lua file)
  • OEFI implementation must allow the OS to return
  • Standard for passing kernel arguments (For things like Fuchas NT or Tsuki)
  • Maybe a sample OEFI implementation?
  • Must support compatibility mode, though maybe there can be an option to disable compatibility mode.
  • Maybe a URF-based bootable package?

Share this post

Link to post
Share on other sites

Nice proposals, i'm still making the draft for version 2

The URF-based bootable package is a very good idea, however this would cause problem since the only implementation for writing/reading URF is uncertain of actually being standard and is only available from Lua from OC.
WIth my knowledge i could also make an application to extract/package URFs, but who would trust it anyways? So the best option is: Making an dedicated C/C++/Java app/library, finding a known archive extractor that support plugins. So based on my existing work and on the specifications, gotta try to make those

Share this post

Link to post
Share on other sites

Fair. Also, we could use binary CPIO for bootable packages. That would probably be the best bet. It's standard and been around for a while.


Also, OEFI v2 applications should end in `.efi2` to differentiate them from the OEFI v1 applications. OEFI v2 applications should also probably support a few basic arguments.

Share this post

Link to post
Share on other sites

.efi2 could probably be a CPIO archive. Entry point can be defined by a .cfg file in the root of the archive. Or, we could also check if it's a CPIO file or a Lua file. Dunno, it's up to you.

Share this post

Link to post
Share on other sites

I decided to instdead use URF, sure it's less supported, but it is more adapted to OpenComputers, as it is made for OpenComputers

Share this post

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Create New...

Important Information

By using this site, you agree to our Terms of Use and Privacy Policy.