↓ Skip to main content

AMI Aptio 4 SEC Development: Assembly Hook and POST Code

AMI Aptio 4 SEC Development: Assembly Hook and POST Code

The Security (SEC) phase is the earliest phase in the UEFI Platform Initialization (PI) architecture. It establishes the initial execution environment, prepares temporary memory, and transfers control to the PEI Foundation.

For firmware developers working with AMI Aptio 4, adding diagnostic code at this stage requires careful integration with the existing assembly, build system, and SEC Core execution flow. The routine must be compiled and linked into the correct firmware component, and its invocation must respect the processor state and execution environment available at the selected hook.

This tutorial uses the AMI EFITraining\SEC sample as a starting point for a simple experiment:

  1. Output POST code 77h to I/O port 80h.
  2. Execute a software-controlled delay loop.
  3. Output 00h to port 80h.
  4. Return control to the caller so normal firmware execution can continue.

The integration uses four files:

File Responsibility
EFITrainingSEC.asm Implements the early assembly routine
EFITrainingSEC.mak Defines the assembly compilation and object-file output rules
EFITrainingSEC.sdl Declares module configuration and eLink integration
EFITrainingSEC.cif Registers the component within the Aptio project structure

The exact SDL, MAK, CIF, and eLink syntax depends on the specific Aptio 4 source tree and training package. Use the corresponding definitions from the installed sample rather than assuming that declarations from another Aptio release are interchangeable.

🔍 1. Understanding the SEC Execution Environment
#

The SEC phase establishes the initial firmware execution environment before the platform has completed normal memory and device initialization.

According to the UEFI Platform Initialization Specification, SEC handles platform restart events, creates a temporary memory store, serves as the initial root of trust, and passes handoff information to PEI.

1.1 What makes SEC development different?
#

On x86 platforms, execution begins at the processor reset vector. The initial execution state and the environment available to subsequent SEC code depend on the processor architecture and the platform’s firmware implementation.

At this stage, permanent DRAM may not yet be initialized. SEC implementations can use temporary resources, including Cache-as-RAM (CAR), to support early execution before normal system memory becomes available.

This imposes several constraints on low-level customization:

  • Execution mode: Assembly code must match the execution mode and calling conventions expected at the integration point.
  • Memory availability: Ordinary memory allocation and assumptions about initialized DRAM may not be valid.
  • Stack availability: A callable routine that uses CALL, RET, or stack operations requires a valid stack at that point in the boot flow.
  • Processor state: Registers and other execution state may contain information required by the surrounding initialization code.
  • Hardware availability: I/O ports and chipset facilities cannot be assumed to behave identically across all platforms.

Assembly is used in this example to keep the routine small and avoid unnecessary runtime dependencies. This does not mean that all SEC code must be written in assembly; the implementation depends on the platform, available services, and selected execution point.

For a broader explanation of the firmware boot sequence, see UEFI BIOS Architecture and Boot Flow.

1.2 How AMI Aptio integrates custom SEC code
#

Aptio projects use AMI-specific build and component-management mechanisms to integrate platform customization.

The relevant mechanisms in this example are:

  • TOKEN: Enables or configures the feature through SDL definitions.
  • MODULE: Includes the module’s build configuration in the project.
  • ELINK: Connects the OEM routine to a supported integration point and adds its object file to the relevant link list.
  • MAK: Defines how the assembly source is compiled into an object file.
  • CIF: Registers the component so that the project can discover and integrate it.

The SECCoreAtPowerOn hook and the ADDON_SEC_CORE_OBJ_FILES object list serve different purposes.

The hook controls where the routine is integrated into the SEC execution flow. The object-list entry ensures that the compiled implementation is included in the SEC Core link.

Both must be configured correctly. Compiling an object file does not automatically make its function execute, and declaring a call to a function does not help if the implementation is absent from the linked image.

The precise execution timing and hook semantics must be confirmed in the target Aptio 4 source tree.

📁 2. Preparing the EFITraining SEC Component
#

Place the four component files in the sample directory:

Uefi Training\5OEMporting\Samplecode\EFITraining\SEC\
    EFITrainingSEC.asm
    EFITrainingSEC.mak
    EFITrainingSEC.sdl
    EFITrainingSEC.cif

The recommended development order is to write the assembly routine first, configure its compilation rules, declare the module and linking relationships, and then verify component registration.

2.1 Configure the assembly source
#

The .asm file contains the routine that writes the two POST codes and executes the delay loop.

Its exported symbol must match the name referenced by the eLink configuration. In this example, that symbol is:

EFITraining_SEC_Test

2.2 Configure the MAK file
#

The MAK file must define how the project assembles EFITrainingSEC.asm and produces the expected object file:

$(BUILD_DIR)\EFITrainingSEC.obj

Verify the following items in the actual training sample:

  • The correct assembler and command-line options are used.
  • The source file is included as a build dependency.
  • The output path matches the object-list entry used by the SEC Core.
  • The build directory is created when necessary.
  • The object is generated in the intended configuration.

Assembly syntax and object format are toolchain-specific. Follow neighboring SEC assembly modules in the same project when configuring these rules.

2.3 Configure the SDL file
#

The SDL file declares the module’s configuration tokens, build integration, and eLink relationships.

Confirm that the feature is enabled under the intended build configuration and that the module’s parent references match the sample’s SEC Core integration points.

Do not assume that an SDL definition from another Aptio project can be copied directly: token names, list names, and hook declarations can vary with the core release.

2.4 Register the CIF component
#

The CIF file registers the component in the Aptio project hierarchy.

Ensure that it identifies the expected files and follows the layout used by the existing EFITraining components. After registration, confirm that the development environment recognizes the component and processes its SDL and MAK definitions.

💻 3. Implementing the SEC Assembly Routine
#

The goal is to output POST code 77h, run two delay loops, and then output 00h.

The following listing is an x86, 16-bit assembly example based on the supplied routine. It makes the code segment explicit, preserves the registers it uses, and includes a return instruction for a conventional callable-function integration.

3.1 Assembly code
#

;--------------------------------------------------------------------
; Procedure: EFITraining_SEC_Test
; Description: SEC-stage POST-code demonstration
; Input:       None
; Output:      Writes 77h and then 00h to I/O port 80h
;
; IMPORTANT:
; - The segment declaration must match the target assembler.
; - The caller must provide a valid stack for this callable routine.
; - The delay is approximate and is not calibrated to one second.
;--------------------------------------------------------------------

STARTUP_SEG SEGMENT

EFITraining_SEC_Test PROC PUBLIC

    ; Preserve registers used by this routine.
    push ax
    push cx

    ; Output POST code 77h to port 80h.
    mov al, 77h
    out 80h, al

    ; Delay loop A.
    mov cx, 64000
delay_a:
    out 0EDh, al
    out 0EDh, al
    out 0EDh, al
    out 0EDh, al
    loop delay_a

    ; Delay loop B.
    mov cx, 64000
delay_b:
    out 0EDh, al
    out 0EDh, al
    out 0EDh, al
    out 0EDh, al
    loop delay_b

    ; Output 00h as the completion marker.
    mov al, 00h
    out 80h, al

    ; Restore the saved registers and return to the caller.
    pop cx
    pop ax
    ret

EFITraining_SEC_Test ENDP

STARTUP_SEG ENDS

END

Assembly integration note: The sample above assumes the Aptio assembler supports the shown segment and procedure syntax, and that the function is invoked as a normal callable routine. In a real firmware project, match the segment declaration and calling convention to adjacent SEC modules and the exact eLink hook implementation.

The routine must not be invoked through a normal call if its execution point does not provide a valid stack. If the platform’s early SEC hook executes before temporary stack initialization, use the integration pattern supported by that hook instead.

3.2 Understanding the POST-code operations
#

The instruction:

mov al, 77h
out 80h, al

loads 77h into the low byte of EAX and writes the value to x86 I/O port 80h.

Port 80h has historically been used for POST diagnostics. If the platform routes these writes to a diagnostic card or another capture mechanism, the output can be used to identify progress through early firmware execution.

However, a write to port 80h does not guarantee that a code will appear on the screen. Visibility depends on the motherboard, chipset, firmware configuration, and diagnostic hardware.

The later write:

mov al, 00h
out 80h, al

emits 00h as the completion marker. It does not necessarily clear the diagnostic history or reset the display on every POST-code monitoring device.

3.3 Understanding the delay loops
#

Each loop initializes CX to 64000. It then performs four writes to port 0EDh per iteration.

Across the two loops, the routine executes:

  • 128,000 loop iterations.
  • 512,000 writes to port 0EDh.
  • One initial POST-code write to port 80h.
  • One completion POST-code write to port 80h.

Port 0EDh is used as an I/O delay mechanism on some x86 platforms. Linux’s x86 I/O-delay implementation documents it as one of several selectable mechanisms, but the elapsed time is not universally calibrated by the port operation itself.

Consequently, the loop should be treated as a platform-dependent demonstration rather than a reliable one-second timer. Actual elapsed time depends on the processor, chipset, I/O implementation, virtualization environment, and other platform details.

For a deterministic delay, use a platform-approved timing mechanism that is available and initialized at the selected SEC hook. In early boot, that may require a hardware-specific timer or a later integration point.

🔗 4. Linking the Routine into SEC Core
#

Compiling EFITrainingSEC.asm produces an object file, but the routine will not execute unless the Aptio project’s module integration and linking configuration include it.

Two separate integration requirements must be satisfied.

4.1 Add the routine to the SEC execution flow
#

The function must be connected to the intended SEC Core hook through the project’s eLink mechanism.

In this example, the relevant integration point is identified as SECCoreAtPowerOn.

Before using that hook, inspect the existing SEC Core implementation and confirm:

  • When the hook is invoked relative to processor and temporary-memory initialization.
  • Whether the integration mechanism expects a callable function or another form of code insertion.
  • Which registers and execution state must be preserved.
  • Whether the hook runs once per expected boot path or can be reached during other restart conditions.

The routine’s implementation must follow the hook’s real calling convention. A function that is syntactically valid can still cause a boot failure if it returns incorrectly or corrupts state expected by the caller.

4.2 Add the object to the SEC Core link list #

The compiled object must also be included in the relevant SEC Core object list:

ADDON_SEC_CORE_OBJ_FILES

The eLink configuration should refer to the actual object path generated by the MAK file, for example:

$(BUILD_DIR)\EFITrainingSEC.obj

Use the exact list and syntax defined by the target Aptio 4 source tree. Do not substitute a similarly named list from another core version without checking its role.

The final integration should satisfy both conditions:

  1. The OEM routine is included in the SEC Core link.
  2. The SEC Core execution flow invokes it at the intended point.

4.3 Verify the generated configuration
#

After the project configuration has been generated, inspect the eKernel table or the equivalent integration report provided by the installed development environment.

Confirm that the eLink relationship exists, the function is assigned to the correct parent, and the object appears in the expected SEC Core configuration.

This step helps distinguish an integration failure from an assembly or runtime failure.

🧪 5. Build and Verify the POST-Code Sequence
#

Verification should begin with the build output and continue through the resulting firmware image.

5.1 Confirm that the object was generated
#

Build the EFITrainingSEC component and inspect the build log.

Verify that:

  • EFITrainingSEC.asm was assembled successfully.
  • The expected EFITrainingSEC.obj file was produced.
  • The object path matches the SEC Core integration configuration.
  • The build completed without missing-symbol or object-file errors.

A successful compilation confirms only that the object was generated; it does not establish that the SEC Core includes or executes it.

5.2 Confirm SEC Core integration
#

Inspect the generated link inputs and relevant build reports.

Verify that EFITrainingSEC.obj is included in the SEC Core image and that the symbol EFITraining_SEC_Test is resolved according to the intended linking model.

If the object is missing, check the object-list eLink and the MAK output path.

If the object is present but the routine does not execute, examine the call-site integration, conditional tokens, and invocation order.

5.3 Observe the POST-code sequence
#

On a development board with an appropriate POST-code capture mechanism, monitor port 80h during startup.

The expected sequence is:

Stage Expected I/O operation
Routine begins Write 77h to port 80h
Delay loop A Repeated writes to port 0EDh
Delay loop B Repeated writes to port 0EDh
Routine completes Write 00h to port 80h
Routine returns Continue the caller’s normal execution flow

The delay should not be judged against an exact one-second target unless the platform’s I/O timing has been independently characterized.

5.4 Diagnose common failures
#

Symptom Items to investigate
Assembly source is not compiled Component registration, module enable token, MAK inclusion
Object file is missing Assembler command, output directory, target path
Linker cannot resolve the function Symbol spelling, visibility, object-list configuration
Routine is compiled but never runs eLink hook, invocation order, conditional build settings
Platform hangs after executing the routine Calling convention, return instruction, stack availability, register preservation
No POST code is visible Port 80h routing, debug-card support, platform-specific diagnostic configuration
Delay differs between platforms Port 0EDh behavior, processor and chipset implementation, virtualization
Boot fails after adding the code Incorrect hook timing, unavailable stack, invalid execution mode, segment configuration

A disciplined approach is to verify compilation, linking, invocation, and observable hardware behavior separately rather than treating a successful build as proof that the experiment is working.

⚠️ 6. SEC Debugging and Integration Precautions
#

SEC is a sensitive stage of firmware execution. Even a short assembly routine can affect the boot sequence if it assumes that a resource or execution service is already available.

Before using this example outside a controlled development environment, consider the following requirements.

Respect the hook’s execution context
#

Do not assume that every SEC hook has an initialized stack, configured DRAM, working timers, or the same processor state.

Verify the specific Aptio core implementation and ensure the selected entry point can safely support the operations performed by the routine.

Preserve the expected processor state
#

The example preserves AX and CX, which it modifies. Other registers and state should also follow the contract of the actual integration point.

The routine uses stack operations and RET, so its calling context must provide a valid stack and a conventional return address.

If the hook uses an inline insertion or another nonstandard integration mechanism, use that mechanism’s documented expectations instead.

Avoid treating the I/O loop as a calibrated timer
#

The repeated writes are useful for a simple experiment, but they are not a portable timing facility.

Do not depend on the loop to enforce a precise boot delay or synchronize other hardware. Use a supported timing source when accurate elapsed time is necessary.

Validate on a development platform first
#

The routine changes early firmware behavior and introduces extra I/O operations before normal boot completes.

Test on a recoverable development platform and retain a known-good firmware image. Confirm the complete boot sequence and the expected POST output before considering any broader deployment.

✅ Conclusion: A Minimal SEC Experiment Requires Correct Integration
#

Adding a small assembly routine to AMI Aptio 4 SEC involves more than writing a few instructions. The routine must be assembled, registered with the project, linked into the SEC Core, and connected to an integration point whose execution environment matches its assumptions.

The EFITraining\SEC sample illustrates the main building blocks:

  • EFITrainingSEC.asm implements the POST-code experiment.
  • EFITrainingSEC.mak compiles the source into an object file.
  • EFITrainingSEC.sdl declares the module and eLink configuration.
  • EFITrainingSEC.cif registers the component within the Aptio project.

The two essential linking conditions are to include the object in the relevant SEC Core link list and to connect the function to a valid execution point.

The experiment also illustrates several broader firmware-development principles: SEC code must respect the execution environment, callable routines need a valid calling convention and stack, and low-level I/O operations may behave differently across platforms.

Finally, the repeated writes to port 0EDh provide a demonstration delay rather than a guaranteed one-second interval. Actual timing and POST-code visibility must be verified on the target hardware.

With these constraints understood, the sample provides a useful starting point for learning how early-boot assembly code is integrated into an AMI Aptio 4 firmware project.

Related