Library Design
AXL — AximCode Library for UEFI
A GLib-inspired C library that makes UEFI application development
look and feel like writing Linux C code. Applications use snake_case
axl_ functions and never touch EDK2 headers directly.
Name: AXL = AximCode Library. Pronounced “axle.”
Related: UdkLib-Design.md covers the current networking stack
(UdkLib Phases 1-10). AXL is the next evolution — a rename and
expansion of UdkLib into a full platform abstraction layer. The
existing UdkLib modules (Log, Data, Util, Loop, Task, Net) will be
renamed to AXL and gain the POSIX-style API described here.
Vision
#include <axl.h>
int axl_main(int argc, char **argv)
{
axl_printf("Hello from %s\n", argv[0]);
AxlStream *f = axl_fopen("fs0:/data.txt", "r");
char *line = axl_readline(f);
axl_printf("first line: %s\n", line);
axl_free(line);
axl_fclose(f);
return 0;
}
No gBS, no Print(), no AllocatePool(), no CHAR16, no
PascalCase, no wide strings. AXL is UTF-8 everywhere, like GLib.
EDK2 is the backend.
API Style: GLib for UEFI
AXL is a GLib-style library. All new public API uses the same
naming pattern as GLib: types are AxlPascalCase (like
GHashTable), functions are axl_snake_case (like
g_hash_table_new). This is the only API that applications use.
#include <axl.h>
AxlHashTable *h = axl_hash_table_new_str();
axl_hash_table_insert(h, "key", value);
char *v = axl_hash_table_lookup(h, "key");
axl_hash_table_free(h);
All new code — library modules, application code, tests — must use this style. No new PascalCase EDK2-style functions.
EDK2-style layer (legacy, internal only)
Library/AxlLib.h contains the original EDK2-style internals
(PascalCase, UEFI types). This layer is frozen — no new functions
are added to it. It exists only because the internal implementation
hasn’t been migrated yet (Phase S5). Applications never include it
unless they need direct UEFI protocol access (SMBIOS, RAM disk).
What AXL Provides
Already implemented (EDK2 internals, POSIX wrappers pending Phase S5)
Category |
Target API (axl.h) |
GLib equivalent |
|---|---|---|
Hash table |
|
|
Dynamic array |
|
|
Strings |
|
|
JSON |
|
json-glib |
Event loop |
|
|
Arg parsing |
|
|
Logging |
|
|
File I/O |
|
|
Task pool |
|
|
TCP sockets |
|
|
HTTP server |
|
libsoup |
HTTP client |
|
libsoup |
URL parsing |
|
|
These modules are fully working internally using EDK2-style names
(AxlHashNew, AxlArrayAppend, etc.). Phase S5 wraps them with
the axl_ public API shown above.
New (this document)
Category |
POSIX-style (axl.h) |
Purpose |
|---|---|---|
Memory |
|
Allocation with leak tracking |
String builder |
|
Mutable auto-growing strings |
I/O streams |
|
Stream abstraction |
Printf |
|
Console and stream output |
Conversion |
|
Encoding utilities |
Phases
Phase |
Module |
Scope |
Depends On |
|---|---|---|---|
R |
Rename |
UdkLib -> AXL (all symbols, files, packages) |
None |
S1 |
axl_mem |
|
Rename |
S2 |
axl_string |
Mutable strings, format, convert, base64 |
S1 |
S3 |
axl_io |
Streams, printf, readline |
S1, S2 |
S4 |
AXL_APP |
Entry point macro, |
S1-S3 |
M1 |
Migrate AxlLog |
GLib-style logging API |
S1-S3 |
M2 |
Migrate AxlData |
Hash, Array, String, JSON |
M1 |
M3 |
Migrate AxlUtil |
File, Path, Args, HexDump, Time |
M1, M2 |
M4 |
Migrate AxlLoop |
Event loop, timers |
M1 |
M5 |
Migrate AxlTask |
Worker pool, arena |
M1, M4 |
M6 |
Migrate AxlNet |
TCP, HTTP, URL |
M1-M5 |
Phase R: Rename UdkLib to AXL
Global rename across all repositories. Mechanical, no logic changes.
Symbol renames
Old |
New |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Directory renames
Old |
New |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Affected repositories
uefi-devkit— AxlLib source, DevKitPkg consumers, docshttpfs— HttpFsPkg.dsc references, WebDavFsDxe consumersoftbmc— SoftBmcPkg.dsc references, all consumersuefi-ipmitool— IpmiToolPkg.dsc references
Approach
Rename directory
UdkLib/->AxlLib/Sed all files:
UDK_->AXL_,Udk->Axl,UdkLib->AxlLibRename header files
Update all .dsc/.inf/.dec files
Build + test all projects
Update external repos (httpfs, softbmc, ipmitool)
Phase S1: axl_mem
POSIX-style allocation wrappers with dmalloc-inspired debug features.
Internal EDK2 code continues to use AllocatePool/FreePool directly
until Phase S5 migration.
API (axl.h)
void *axl_malloc(size_t size);
void *axl_calloc(size_t count, size_t size);
void *axl_realloc(void *ptr, size_t size);
void axl_free(void *ptr);
char *axl_strdup(const char *s);
void *axl_memdup(const void *src, size_t size);
#define axl_new(Type) ((Type *)axl_calloc(1, sizeof(Type)))
#define axl_new_array(Type, Count) ((Type *)axl_calloc((Count), sizeof(Type)))
Debug features (dmalloc-inspired, DEBUG builds only)
All debug features compile in via AXL_MEM_DEBUG (set automatically
in DEBUG builds). RELEASE builds have only the size header for realloc.
Feature |
Pattern |
Purpose |
|---|---|---|
Fence-post guards |
|
Detect buffer underflow/overflow |
Alloc fill |
|
Expose use-before-init |
Free fill |
|
Expose use-after-free |
File/line tracking |
|
Pinpoint leak and corruption sources |
Leak report |
|
Log all unfreed with file:line |
Fence validation |
|
Validate a pointer’s fence-posts on demand |
Stats |
|
Live count/bytes + lifetime totals |
The public API functions are macros that inject __FILE__/__LINE__
and call _impl functions internally.
The size tracking trick
POSIX realloc(ptr, new_size) doesn’t take old size. EDK2’s
ReallocatePool(old_size, new_size, ptr) requires it. Solution:
[hidden header: UINTN alloc_size][user data...]
^-- returned pointer
axl_malloc(n) allocates n + header_size, stores n in the
header, returns pointer past it. axl_realloc reads the header
to get old size, allocates new, copies, frees old. axl_free
backs up to the header before calling FreePool.
Memory layout (DEBUG)
[UINTN: fence_head 0xC0C0AB1B]
[UINTN: alloc_size ]
[CHAR8*: file pointer ]
[UINTN: line number ]
[UINTN: fence_mid 0xC0C0AB1B]
[user data ... padded to UINTN]
[UINTN: fence_tail 0xFACADE69]
Memory layout (RELEASE)
[UINTN: alloc_size ]
[user data ......................]
No EDK2-style wrappers
There is no AxlAlloc/AxlFree layer. Internal EDK2-style code
uses AllocatePool/AllocateZeroPool/FreePool directly. Phase S5
migrates internals straight to axl_malloc/axl_free. UEFI protocol
buffers that are passed to/from UEFI APIs stay as raw AllocatePool
since UEFI may free them.
Phase S2: axl_string
Mutable auto-growing string buffers. Works with char * (UTF-8),
consistent with the rest of the AXL API.
POSIX-style API (axl.h)
typedef struct AxlString AxlStrBuf;
AxlString *axl_string_new(void);
AxlString *axl_string_new_size(size_t reserve);
void axl_string_append(AxlString *b, const char *s);
void axl_string_append_n(AxlString *b, const char *data, size_t len);
void axl_string_append_printf(AxlString *b, const char *fmt, ...);
void axl_string_putc(AxlString *b, char c);
const char *axl_string_str(AxlString *b);
size_t axl_string_len(AxlString *b);
char *axl_string_steal(AxlString *b); // caller owns string
void axl_string_clear(AxlString *b); // reset, keep alloc
void axl_string_free(AxlString *b);
// Convenience: format into a new string (like asprintf)
char *axl_asprintf(const char *fmt, ...);
Conversion utilities
// UTF-8 <-> UCS-2 conversion (internal use, exposed for UEFI protocol interop)
// Newly allocated, caller frees.
unsigned short *axl_utf8_to_ucs2(const char *s);
char *axl_ucs2_to_utf8(const unsigned short *s);
// Base64
char *axl_base64_encode(const void *data, size_t size);
int axl_base64_decode(const char *b64, void **data, size_t *size);
// Number parsing (handles 0x prefix for hex)
uint64_t axl_strtou64(const char *s);
Phase S3: axl_io
Stream-based I/O. Every target (console, file, buffer) is an
axl_stream with read/write/printf/close.
POSIX-style API (axl.h)
typedef struct AxlStream AxlStream;
// Standard streams (initialized at startup)
extern AxlStream *axl_stdout;
extern AxlStream *axl_stderr;
extern AxlStream *axl_stdin;
// File streams
AxlStream *axl_fopen(const char *path, const char *mode); // "r", "w", "a"
void axl_fclose(AxlStream *s);
// Buffer streams (in-memory, auto-growing)
AxlStream *axl_bufopen(void);
const void *axl_bufdata(AxlStream *s, size_t *size);
void *axl_bufsteal(AxlStream *s, size_t *size);
// Read/Write
size_t axl_fread(void *buf, size_t size, size_t count, AxlStream *s);
size_t axl_fwrite(const void *buf, size_t size, size_t count, AxlStream *s);
char *axl_readline(AxlStream *s); // caller frees, NULL at EOF
// Printf family
int axl_printf(const char *fmt, ...); // -> stdout
int axl_eprintf(const char *fmt, ...); // -> stderr
int axl_fprintf(AxlStream *s, const char *fmt, ...); // -> any stream
Format specifiers
AXL has its own printf engine (axl_vformat) using standard C
va_arg. Format specifiers follow standard C printf conventions:
Specifier |
Type |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
literal |
Width (%10d), zero-padding (%08x), left-align (%-20s),
precision (%.5s), and * width/precision from args are supported.
Use __attribute__((format(printf, N, M))) for compile-time checking.
Stream internals
typedef size_t (*AxlWriteFn)(void *ctx, const void *data, size_t size);
typedef size_t (*AxlReadFn)(void *ctx, void *buf, size_t size);
typedef void (*AxlCloseFn)(void *ctx);
struct AxlStream {
void *ctx;
AxlWriteFn write;
AxlReadFn read;
AxlCloseFn close;
};
Extensible: serial port, network socket, UEFI variable backends can be added by providing the vtable functions.
Phase S4: axl.h and axl_main
The top-level header and entry point that ties everything together.
axl.h
Single include that pulls in the full POSIX-style API:
#ifndef AXL_H_
#define AXL_H_
#include <stddef.h> // size_t
#include <stdint.h> // uint64_t, etc.
#include <stdbool.h> // bool
// Memory
void *axl_malloc(size_t size);
void *axl_calloc(size_t count, size_t size);
void *axl_realloc(void *ptr, size_t size);
void axl_free(void *ptr);
char *axl_strdup(const char *s);
void *axl_memdup(const void *src, size_t size);
#define axl_new(Type) ((Type *)axl_calloc(1, sizeof(Type)))
#define axl_new_array(Type, Count) ((Type *)axl_calloc((Count), sizeof(Type)))
// I/O
typedef struct AxlStream AxlStream;
extern AxlStream *axl_stdout;
extern AxlStream *axl_stderr;
extern AxlStream *axl_stdin;
int axl_printf(const char *fmt, ...);
int axl_fprintf(AxlStream *s, const char *fmt, ...);
char *axl_asprintf(const char *fmt, ...);
// ... etc.
// Data structures — GLib-style hash table (see GHashTable docs)
typedef struct AxlHashTable AxlHashTable;
AxlHashTable *axl_hash_table_new(void); // string keys, copied
AxlHashTable *axl_hash_table_new_full(AxlHashFunc, AxlEqualFunc,
AxlDestroyNotify key_free,
AxlDestroyNotify val_free); // generic keys
int axl_hash_table_insert(AxlHashTable *h, const void *key, void *val);
int axl_hash_table_replace(AxlHashTable *h, const void *key, void *val);
void *axl_hash_table_lookup(AxlHashTable *h, const void *key);
bool axl_hash_table_contains(AxlHashTable *h, const void *key);
bool axl_hash_table_remove(AxlHashTable *h, const void *key);
bool axl_hash_table_steal(AxlHashTable *h, const void *key);
size_t axl_hash_table_foreach_remove(AxlHashTable *h, ...);
// Iterator: AxlHashTableIter with iter_init, iter_next, iter_remove
#endif
axl_main entry point
// In axl.h
#define AXL_APP(main_func) \
EFI_STATUS EFIAPI _AxlEntry( \
EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) { \
_axl_init(ImageHandle, SystemTable); \
int argc; char **argv; \
_axl_get_args(&argc, &argv); \
int rc = main_func(argc, argv); \
_axl_cleanup(); \
return (rc == 0) ? EFI_SUCCESS : EFI_ABORTED; \
}
// App code:
#include <axl.h>
int my_main(int argc, char **argv)
{
axl_printf("Hello %s\n", argv[1]);
return 0;
}
AXL_APP(my_main)
The AXL_APP macro:
Defines the real UEFI entry point (PascalCase, hidden)
Initializes AXL (stdout/stderr/stdin streams, memory subsystem)
Converts UEFI
CHAR16 **Argvto Cchar **argvCalls the user’s
mainfunction with C-style argc/argvCleans up and returns EFI_STATUS
Apps never see EFI_HANDLE, EFI_SYSTEM_TABLE, or EFIAPI.
Phase S5: Migration
Rename all UdkLib internals to AXL and rewrite apps to use axl.h.
Internal rename
All UdkLib source files: UDK_ -> AXL_, Udk -> Axl. The
EDK2-style API (Library/AxlLib.h) is the internal layer. The
POSIX-style API (axl.h) wraps it.
App migration example
Before (current):
#include "SysInfo.h" // pulls in Uefi.h, UefiLib.h, ShellLib.h, ...
BOOLEAN gVerbose = FALSE;
STATIC CONST UDK_OPT_DEF mOpts[] = { ... };
EFI_STATUS EFIAPI SysInfoMain(EFI_HANDLE Img, EFI_SYSTEM_TABLE *ST)
{
EFI_SHELL_PARAMETERS_PROTOCOL *P;
gBS->OpenProtocol(Img, &gEfiShellParametersProtocolGuid, ...);
UDK_ARGS *Args;
UdkArgsParse(P->Argc, P->Argv, mOpts, &Args);
if (UdkArgsFlag(Args, 'v')) gVerbose = TRUE;
Print(L"CPU: %s\n", CpuName);
UdkArgsFree(Args);
return EFI_SUCCESS;
}
After:
#include <axl.h>
static bool verbose = false;
static AxlOptopts[] = {
{ 'v', NULL, AXL_OPT_FLAG, NULL, "Verbose output" },
{ 'h', "--help", AXL_OPT_FLAG, NULL, "Show help" },
{ 0 }
};
int sysinfo_main(int argc, char **argv)
{
AxlArgs *args = axl_args_parse(argc, argv, opts);
if (axl_args_flag(args, 'h')) { axl_args_usage("SysInfo", "[section]", opts); }
verbose = axl_args_flag(args, 'v');
axl_printf("CPU: %s\n", cpu_name);
axl_args_free(args);
return 0;
}
AXL_APP(sysinfo_main)
Migration Phases (M1–M6)
Migrate each internal EDK2-style module to GLib-style. For each module the work is:
Create
axl/<module>.hheader with GLib-style declarationsRename functions:
AxlFoo→axl_fooRename types: keep
AxlPascalCase(already correct)Switch allocations:
AllocatePool→axl_malloc,FreePool→axl_freeSwitch logging:
axl_error/Print→axl_printerrSwitch strings:
CHAR8 *→char *,UINTN→size_tRename source files:
AxlFoo.c→axl-foo.cUpdate tests to use new API names
Remove old declarations from
Library/AxlLib.hUpdate consumer projects (uefi-devkit, httpfs, uefi-ipmitool, softbmc)
Backwards compatibility during migration
Each phase provides a compatibility header
Library/AxlLib.h with #define macros mapping old names to new:
/* Temporary compat — remove after all consumers are updated */
#define AxlHashNew() axl_hash_table_new_str()
#define AxlHashFree(h) axl_hash_table_free(h)
#define AxlHashSet(h, k, v) axl_hash_table_insert((h), (k), (v))
This lets consumer projects migrate incrementally rather than requiring a flag-day update across all repos. Compat macros are removed once all consumers have switched.
Consumer projects to update
Project |
Repo |
Modules used |
Notes |
|---|---|---|---|
uefi-devkit |
aximcode/uefi-devkit |
Log, Data, Util, Loop, Task, Net |
Heaviest consumer — all modules |
httpfs |
aximcode/httpfs |
Log, Data, Loop, Net |
WebDavFsDxe uses Hash + HttpClient; HttpFS uses Loop + HttpServer |
uefi-ipmitool |
aximcode/uefi-ipmitool |
(none yet) |
Does not currently use AxlLib |
softbmc |
aximcode/softbmc |
Log, Data, Util, Loop, Net |
Not yet migrated |
For each migration phase, the “Consumer project changes” subsection
lists exactly what each project needs. All projects need
AxlFormatLib added to their DSC (done in M1).
M1: Migrate AxlLog ✓
Logging is the foundation — every other module depends on it.
Old API (AxlLib.h):
AxlLogFull(level, domain, func, line, fmt, ...);
AxlLogSetLevel(level);
AxlLogAddHandler(handler, data);
New API (axl/axl-log.h):
axl_log_set_level(level);
axl_log_add_handler(handler, data);
/* Convenience macros unchanged: axl_error, axl_info, etc.
— these are macros, not functions, and stay uppercase
because they inject __func__/__LINE__ */
Implementation notes:
AxlFormatLib extracted as zero-dependency library (breaks Log↔Data cycle)
Ring buffer and file handler keep
AllocatePool/FreePool(breaks Log↔Mem cycle)File handler keeps ShellLib (breaks Log↔IO cycle)
Format engine switched from PrintLib (
%a) to axl_vformat (%s)__attribute__((format(printf,...)))onaxl_log_full— catches mismatches at compile timeAxlLogFileAttach(CHAR16*)replaced byaxl_log_file_attach(char*)— no compat macro (signature changed, callers must update)
Consumer project changes for M1:
All projects: add
AxlFormatLibto.dsc[LibraryClasses]:AxlFormatLib|src/format/AxlFormat.infAll projects using
axl_error/axl_infowith%aformat: change to%sAll projects using
%u/%d/%xwithUINTN/INTN: change to%llu/%lld/%llxAll projects calling
AxlLogFileAttach(L"..."): change toaxl_log_file_attach("...")All projects defining
AXL_LOG_HANDLERcallbacks: update signature fromVOID EFIAPI (UINTN, CONST CHAR8*, CONST CHAR8*, VOID*)tovoid (int, const char*, const char*, void*)httpfs: no code changes needed (does not call log macros with format args or define handler callbacks). DSC needs
AxlFormatLibline added.uefi-ipmitool: no changes needed (does not use AxlLib)
M2: Migrate AxlData ✓
Hash table, dynamic array, string utilities, JSON.
Old: AxlHashNew, AxlArrayAppend, AxlStrDup, AxlJsonParse
New: axl_hash_table_new, axl_array_append, axl_strdup (already exists), axl_json_parse
Consumer project changes for M2:
httpfs (WebDavFsDxe): uses
AxlHashNew,AxlHashSet,AxlHashFree,AXL_HASH_TABLE. Compat macros cover function renames. Type renameAXL_HASH_TABLE→AxlHashTableneeds a compat#define.uefi-devkit: uses hash, array, string, JSON extensively. Compat macros cover most usage; manual migration of type names may be needed.
M3: Migrate AxlUtil
File I/O, path utils, args, hex dump, time.
Old: AxlFileReadAll, AxlPathBasename, AxlArgsParse
New: axl_file_read_all (→ merge with axl_file_get_contents), axl_path_get_basename, axl_args_parse
Consumer project changes for M3:
AxlFile paths switch from
CHAR16*tochar*(UTF-8). All consumers usingAxlFileReadAll/AxlFileWriteAllwith wide paths must convert. No compat macro possible (signature change).AxlArgs switches from
CHAR16**argv tochar**argv. Apps usingAXL_APP()already getchar** argv; legacy entry points need conversion.uefi-devkit: heavy user of AxlArgs and AxlFile — most impacted project.
M4: Migrate AxlLoop
Event loop, timers, idle dispatch.
Old: AxlLoopNew, AxlLoopRun, AxlLoopAddTimer
New: axl_loop_new, axl_loop_run, axl_loop_add_timer
Consumer project changes for M4:
httpfs (HttpFS serve command): uses AxlLoop for HTTP server event loop. Compat macros cover function renames. Callback types (
AXL_CALLBACK,AXL_KEY_CALLBACK) will be renamed — compat#defineneeded.uefi-devkit: several tools use AxlLoop. Same compat approach.
Event Loop Architecture
AxlLoop is a GLib-style event loop with multiple source types:
Source |
API |
Behavior |
|---|---|---|
Timer |
|
Repeating at fixed interval |
Timeout |
|
One-shot, auto-removed |
Keypress |
|
Console input |
Idle |
|
Fires every iteration |
Protocol |
|
UEFI protocol install |
Event |
|
Raw EFI_EVENT — async notification |
The raw event source (AXL_SOURCE_EVENT) accepts any EFI_EVENT handle
and fires a callback when signaled. The caller owns the event — the
loop watches but does not close it. This enables async integration:
TCP completion tokens signal when connect/send/recv complete
Custom protocol events from UEFI drivers
Any firmware event (USB, storage, network state)
Dispatch model: The loop builds an event array from all active non-idle sources and calls WaitForEvent (blocking) or CheckEvent (non-blocking). A 10ms poll timer provides Ctrl-C detection. Idle callbacks fire every iteration before the wait.
Future: Async TCP. The current TCP API blocks with poll+Stall
loops. A future async API will use axl_loop_add_event to register
completion token events directly, eliminating polling:
axl_tcp_connect_async(host, port, loop, on_connect, data);
axl_tcp_recv_async(sock, loop, on_data, data);
axl_tcp_accept_async(listener, loop, on_accept, data);
The blocking API will remain for simple use cases. The HTTP server will migrate from idle-based polling to event-driven I/O.
M5: Migrate AxlTask
AP worker pool, arena allocator.
Old: AxlTaskPoolInit, AxlTaskSubmit, AxlArenaCreate
New: axl_task_pool_init, axl_task_submit, axl_arena_create
Consumer project changes for M5:
uefi-devkit: SysInfo uses task pool for parallel CPU/memory probing. Compat macros cover function renames. Callback types (
AXL_TASK_PROC,AXL_TASK_COMPLETE) will be renamed.httpfs, uefi-ipmitool: do not use AxlTask. No changes needed.
M6: Migrate AxlNet
TCP, HTTP server/client, URL parser.
Old: AxlTcpConnect, AxlHttpServerNew, AxlUrlParse
New: axl_tcp_connect, axl_http_server_new, axl_url_parse
Consumer project changes for M6:
httpfs (WebDavFsDxe): uses
AxlHttpClientNew,AxlHttpGet,AxlHttpRequest,AxlHttpClientResponseFree,AxlHttpClientFree. Compat macros cover function renames. Type renames (AXL_HTTP_CLIENT→AxlHttpClient,AXL_HTTP_CLIENT_RESPONSE→AxlHttpResponse) need compat#define.httpfs (HttpFS serve): uses
AxlHttpServer*extensively. Same approach.uefi-devkit (Fetch): uses HTTP client. Compat macros cover it.
uefi-ipmitool: does not use AxlNet. No changes needed.
C1: Style Guide Compliance
Post-migration cleanup. The M-phases focused on API renames and type migration; this phase enforces the coding style consistently across all source files.
Scope:
Spaces before parens: remove
func (arg)→func(arg)everywhereDoc comments: convert
@paramblocks to///<inline style per the updated coding style guideEDK2 qualifiers: remove remaining
IN,OUT,OPTIONAL,STATIC(useconst, pointer semantics,static)EDK2 types in migrated code:
TRUE→true,FALSE→false,CONST→const,BOOLEAN→bool,VOID→voidin function signatures (internal UEFI code may keep them)Allocator consistency: replace remaining
AllocatePool/FreePoolwithaxl_malloc/axl_freewhere safe (not in AP-callable code or UEFI protocol buffers)Include cleanup: remove unnecessary EDK2 includes from files that no longer use UEFI APIs directly
Files: all axl-*.c and axl-*.h under src/ and include/.
Consumer projects: httpfs, uefi-devkit (if affected).
C2: Dogfooding
Switch internal implementations from EDK2 primitives to AXL APIs. The migration phases renamed the public API but left internals using raw UEFI calls. This phase makes AXL eat its own dog food.
Scope:
Memory: replace remaining
AllocatePool/AllocateZeroPool/FreePoolwithaxl_malloc/axl_calloc/axl_freein all migrated modules (Net, Data, Util). Exception: AP-callable code and buffers passed to/from UEFI protocols.String ops: replace
AsciiStrLen/AsciiStrCmp/AsciiStrnCmp/AsciiSPrint/CopyMemwith local helpers or AXL equivalents (axl_strlcpy,axl_vformat, etc.).Console output: replace
Print(L"...")withaxl_print()in all migrated modules.axl-json-print.cis the main target.Logging: ensure every module has
AXL_LOG_DOMAINand usesaxl_error/axl_warning/axl_info/axl_debugfor diagnostics instead of silent failures orPrint. Audit modules that currently have no logging (e.g. axl-string.c, axl-url.c).Include cleanup: remove
<Library/UefiLib.h>,<Library/PrintLib.h>,<Library/MemoryAllocationLib.h>from files that no longer need them after switching to AXL APIs.
C3: Test Modernization
Migrate test applications from EDK2 entry points to AXL_APP and
standardize the test framework.
Scope:
AXL_APP entry points: convert all 9 test files from
EFI_STATUS EFIAPI AxlTestFooMain(EFI_HANDLE, EFI_SYSTEM_TABLE*)toint test_foo_main(int argc, char **argv)+AXL_APP(...). Update .inf files:ENTRY_POINT = _AxlEntry.Test output: replace
Print(L"PASS: %s\n", Name)withaxl_printf("PASS: %s\n", name)— UTF-8 test names instead of wide strings.Test helpers: extract shared Pass/Fail/Check into a common header (
axl-test.h) instead of duplicating in every test file.Test .inf cleanup: standardize
[LibraryClasses]across all test .inf files (add AxlAppLib, remove UefiApplicationEntryPoint where possible).Consumer test coverage: add build verification for httpfs and uefi-devkit as part of the test-axl.sh runner.
Coding Style
See AXL-Coding-Style.md for the full style guide. The short version: GLib naming, 4-space indent, standard C types in public API, no space before parens.
Design Principles
C is the language.
char *notCHAR8.size_tnotUINTN.void *notVOID *.boolnotBOOLEAN. snake_case, not PascalCase.%smeans char*. AXL’s printf follows C convention, not EDK2.%S(uppercase) for wide strings.axl.h is self-contained. One include, no EDK2 headers leak through. Apps that need UEFI protocols include
<Library/AxlLib.h>explicitly for those specific APIs.NULL-safe.
axl_free(NULL),axl_fclose(NULL), etc. are no-ops.Allocation failures return NULL. No abort. Caller checks.
UTF-8 everywhere. All strings in the
axl.hAPI arechar *(UTF-8). NoCHAR16, nochar16_t, no wide strings. AXL converts to UCS-2 internally when calling UEFI APIs (file paths, console). Conversion functions (axl_utf8_to_ucs2,axl_ucs2_to_utf8) are exposed for apps that need direct UEFI protocol interop.EDK2 is invisible. The
AXL_APPmacro hides the UEFI entry point._axl_initsets up streams and memory. The app seesint main(int argc, char **argv).Eat our own dog food. AXL’s own internals use the AXL API. The library allocates with
axl_malloc, reports errors withaxl_printerr, builds strings withaxl_string. If the API isn’t good enough for our own code, it isn’t good enough to ship.Exception: very early initialization (before
axl_io_init) may use EDK2 primitives directly since streams aren’t available yet.No legacy EDK2-style API. There is no separate PascalCase layer. All functions follow GLib naming (
axl_snake_case). UEFI types are confined to_impldeclarations and internal implementation files.
Project
Repository
Name: aximcode/axl-sdk (public repo)
AXL is a standalone library, not embedded in uefi-devkit. Consumer projects (uefi-devkit, httpfs, softbmc, ipmitool) add it to PACKAGES_PATH like they add EDK2.
EDK2 packaging
AxlPkg — a standalone EDK2 package with both .dec (for
consumers) and .dsc (for building AXL standalone + tests).
Consumer projects reference it like:
PACKAGES_PATH = /path/to/axl-sdk:/path/to/edk2
[Packages]
AxlPkg/AxlPkg.dec
[LibraryClasses]
AxlMemLib|src/mem/AxlMem.inf
AxlLogLib|src/log/AxlLog.inf
# ... (see AxlPkg.dsc for full list)
Minimum EDK2 version
edk2-stable202511 (November 2025).
Versioning
Semantic versioning (semver). axl.h is the stable public API —
once 1.0 ships, breaking changes require a major version bump.
Library/AxlLib.h (EDK2-style internal API) can change in minor
releases.
License
BSD-2-Clause-Patent (matching EDK2).
Repository layout
axl-sdk/
README.md Quick start, what AXL is, first app
LICENSE BSD-2-Clause-Patent
CHANGELOG.md Version history
docs/
AXL-Design.md This document (moved from uefi-devkit)
tutorial.md Your first AXL app
include/
axl.h Umbrella header (#include <axl/axl-mem.h> etc.)
axl/
axl-mem.h Memory allocation (Phase S1)
axl-string.h String builder (Phase S2)
axl-io.h I/O streams (Phase S3)
uefi/ UEFI type headers (native backend)
src/
log/ Logging, domains, handlers
data/ Hash, Array, String, JSON, StrBuf
util/ File I/O, Path, Args, HexDump, Time
loop/ Event loop, timers
task/ AP worker pool, arena
net/ TCP, HTTP server/client, URL
mem/ Allocation wrappers (Phase S1)
io/ Streams, printf (Phase S3)
posix/ axl.h implementation, AXL_APP entry point (Phase S4)
backend/ Backend abstraction + compat headers
crt0/ UEFI entry point stub
test/
unit/ Test binaries (.c + .inf)
integration/ QEMU test runner + HTTP integration tests
test-axl.sh QEMU-based test runner
test-http.sh HTTP integration tests
common-test.sh Shared test helpers
AxlPkg/
AxlPkg.dec Package declaration
AxlPkg.dsc Standalone build (library + tests)
scripts/
build.sh Build wrapper
CI (GitHub Actions)
Build X64 + AARCH64 on every push
Run test-axl.sh (QEMU unit tests)
Release: tag -> build -> attach .efi binaries to GitHub release
Dependencies
Phase |
Adds |
Depends On |
|---|---|---|
R |
Rename + repo split |
Nothing |
S1 |
src/mem/axl-mem.c |
MdePkg |
S2 |
src/data/axl-strbuf.c |
S1 |
S3 |
src/io/ (new module) |
S1, S2, ShellPkg |
S4 |
axl.h, AxlAppEntryPoint |
S1-S3 |
S5 |
No new modules |
S1-S4 |
Relation to Networking Phases
Phases 8-10 (middleware, client TLS, SoftBMC migration) continue
using the EDK2-style API (Library/AxlNet.h). The POSIX-style
wrappers for networking (axl_http_get, axl_tcp_connect, etc.)
come as part of Phase S4 or S5 when the full axl.h is assembled.
Recommended order: Phase R (rename + repo split) first — it’s mechanical and unblocks everything. Then S1-S3 in sequence. S4-S5 and Phases 8-10 can interleave.