Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions loader/arch/x86/bios/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ target_sources(
target_sources(
${LOADER_EXECUTABLE}
PRIVATE
apm.c
bios_disk_services.c
bios_entry.c
bios_find.c
Expand Down
98 changes: 98 additions & 0 deletions loader/arch/x86/bios/apm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#define MSG_FMT(msg) "BIOS-APM: " msg

#include "common/format.h"
#include "common/log.h"
#include "bios_call.h"
#include "services_impl.h"
#include "apm.h"

#define APM_SIGNATURE 0x504D
#define APM_POWER_DEVICE_ID_APM_BIOS 0x0000

#define APM_FLAG_32BIT_INTERFACE_SUPPORTED (1 << 1)

#define APM_INT 0x15
#define APM_CMD 0x53
#define MAKE_APM_CMD(cmd) ((APM_CMD << 8) | (cmd))

#define APM_INSTALLATION_CHECK MAKE_APM_CMD(0x00)
#define APM_PM32_INTERFACE_CONNECT MAKE_APM_CMD(0x03)
#define APM_INTERFACE_DISCONNECT MAKE_APM_CMD(0x04)

static bool check_apm_call(
const struct real_mode_regs *in_regs,
const struct real_mode_regs *out_regs
)
{
if (is_carry_set(out_regs)) {
print_warn(
"APM call 0x%04X failed: %d\n",
in_regs->eax, (out_regs->eax & 0xFFFF) >> 8
);
return false;
}

if (in_regs->eax == APM_INSTALLATION_CHECK) {
u16 signature = out_regs->ebx & 0xFFFF;

if (unlikely(signature != APM_SIGNATURE)) {
print_warn("bad APM signature 0x%04X\n", signature);
return false;
}
}

return true;
}

bool services_setup_apm(struct apm_info *out_info)
{
struct real_mode_regs out_regs, in_regs = { 0 };

// All queries will be for the APM BIOS "device"
in_regs.ebx = APM_POWER_DEVICE_ID_APM_BIOS;

// 1. Check if APM exists at all
in_regs.eax = APM_INSTALLATION_CHECK;
bios_call(APM_INT, &in_regs, &out_regs);
if (!check_apm_call(&in_regs, &out_regs))
return false;

if (!(out_regs.ecx & APM_FLAG_32BIT_INTERFACE_SUPPORTED)) {
print_warn("APM doesn't support 32-bit interface\n");
return false;
}

// 2. Disconnect if anything was connected previously, ignore return value
in_regs.eax = APM_INTERFACE_DISCONNECT;
bios_call(APM_INT, &in_regs, &out_regs);

// 3. Connect the 32-bit interface
in_regs.eax = APM_PM32_INTERFACE_CONNECT;
bios_call(APM_INT, &in_regs, &out_regs);
if (!check_apm_call(&in_regs, &out_regs))
return false;

print_info("32-bit PM interface connected\n");
out_info->pm_code_segment = out_regs.eax & 0xFFFF;
out_info->pm_code_segment_length = out_regs.esi & 0xFFFF;
out_info->pm_offset = out_regs.ebx;

out_info->rm_code_segment = out_regs.ecx & 0xFFFF;
out_info->rm_code_segment_length = out_regs.esi >> 16;

out_info->data_segment = out_regs.edx & 0xFFFF;
out_info->data_segment_length = out_regs.edi & 0xFFFF;

// 4. Recheck flags, as they might change after 32-bit interface install
in_regs.eax = APM_INSTALLATION_CHECK;
bios_call(APM_INT, &in_regs, &out_regs);
if (unlikely(!check_apm_call(&in_regs, &out_regs))) {
in_regs.eax = APM_INTERFACE_DISCONNECT;
bios_call(APM_INT, &in_regs, &out_regs);
return false;
}

out_info->version = out_regs.eax & 0xFFFF;
out_info->flags = out_regs.ecx & 0xFFFF;
return true;
}
41 changes: 41 additions & 0 deletions loader/boot_protocol/ultra.c
Original file line number Diff line number Diff line change
Expand Up @@ -458,10 +458,28 @@ static bool set_video_mode(struct config *cfg, struct loadable_entry *entry,
return true;
}

static bool apm_setup(struct config *cfg, struct loadable_entry *le,
struct apm_info *out_info)
{
bool wants_apm = false;

cfg_get_bool(cfg, le, SV("setup-apm"), &wants_apm);
if (!wants_apm)
return false;

if (services_get_provider() != SERVICE_PROVIDER_BIOS) {
print_info("ignoring request to set up APM on UEFI\n");
return false;
}

return services_setup_apm(out_info);
}

struct attribute_array_spec {
bool higher_half_pointers;
bool fb_present;
bool cmdline_present;
bool apm_info_present;
uint8_t page_table_depth;

struct ultra_framebuffer fb;
Expand All @@ -471,6 +489,8 @@ struct attribute_array_spec {

struct dynamic_buffer module_buf;

struct apm_info apm_info;

ptr_t acpi_rsdp_address;
ptr_t dtb_address;
ptr_t smbios_address;
Expand Down Expand Up @@ -595,6 +615,18 @@ static void *write_framebuffer(struct ultra_framebuffer_attribute *fb_attr,
return ++fb_attr;
}

static void *write_apm_info(struct ultra_apm_attribute *apm_attr,
const struct attribute_array_spec *spec)
{
apm_attr->header.type = ULTRA_ATTRIBUTE_APM_INFO;
apm_attr->header.size = sizeof(struct ultra_apm_attribute);

BUILD_BUG_ON(sizeof(apm_attr->info) != sizeof(struct apm_info));
memcpy(&apm_attr->info, &spec->apm_info, sizeof(struct apm_info));

return ++apm_attr;
}

static void *write_memory_map(void *attr_ptr, size_t entry_count)
{
struct ultra_memory_map_attribute *mm = attr_ptr;
Expand Down Expand Up @@ -651,6 +683,8 @@ static ptr_t build_attribute_array(const struct attribute_array_spec *spec,
bytes_needed += cmdline_aligned_length;
bytes_needed += spec->fb_present *
sizeof(struct ultra_framebuffer_attribute);
bytes_needed += spec->apm_info_present *
sizeof(struct ultra_apm_attribute);
bytes_needed += sizeof(struct ultra_memory_map_attribute);

// Add 2 to give some leeway for memory map growth after the next allocation
Expand Down Expand Up @@ -725,6 +759,11 @@ static ptr_t build_attribute_array(const struct attribute_array_spec *spec,
*attr_count += 1;
}

if (spec->apm_info_present) {
attr_ptr = write_apm_info(attr_ptr, spec);
*attr_count += 1;
}

attr_ptr = write_memory_map(attr_ptr, mm_entry_count);
*attr_count += 1;
return ret;
Expand Down Expand Up @@ -1140,6 +1179,8 @@ static void ultra_protocol_boot(struct config *cfg, struct loadable_entry *le)
spec.dtb_address = services_find_dtb();
spec.smbios_address = services_find_smbios();

spec.apm_info_present = apm_setup(cfg, le, &spec.apm_info);

/*
* Attempt to set video mode last, as we're not going to be able to use
* legacy tty logging after that.
Expand Down
2 changes: 1 addition & 1 deletion loader/boot_protocol/ultra_protocol
Submodule ultra_protocol updated 2 files
+48 −1 README.md
+21 −0 ultra_protocol.h
18 changes: 18 additions & 0 deletions loader/include/apm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include "common/types.h"

struct apm_info {
u16 version;
u16 flags;

u16 pm_code_segment;
u16 pm_code_segment_length;
u32 pm_offset;

u16 rm_code_segment;
u16 rm_code_segment_length;

u16 data_segment;
u16 data_segment_length;
};
7 changes: 7 additions & 0 deletions loader/include/services.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "common/types.h"
#include "common/attributes.h"
#include "apm.h"

enum service_provider {
SERVICE_PROVIDER_INVALID,
Expand All @@ -28,6 +29,12 @@ ptr_t services_find_dtb(void);
*/
ptr_t services_find_smbios(void);

/*
* Attempts to setup the 32-bit protected-mode interface for APM if it exists.
* Returns true if the interface was successfully installed, false otherwise.
*/
bool services_setup_apm(struct apm_info *out_info);

/*
* Aborts the loader execution in a platform-specific manner.
* Must be used for unrecoverable errors.
Expand Down
1 change: 1 addition & 0 deletions loader/uefi/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ target_sources(
uefi_memory_services.c
uefi_video_services.c
relocator.c
stubs.c
)
13 changes: 13 additions & 0 deletions loader/uefi/stubs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#define MSG_FMT(msg) "UEFI-STUBS: " msg

#include "common/log.h"
#include "common/types.h"
#include "apm.h"

bool services_setup_apm(struct apm_info *out_info)
{
UNUSED(out_info);

print_warn("APM setup is unsupported!\n");
return false;
}
5 changes: 5 additions & 0 deletions tests/disk_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

[i686_lower_half]
protocol=ultra
setup-apm = true

cmdline = {cmdline}
binary = "/boot/kernel_i686_lower_half"
Expand All @@ -37,6 +38,7 @@

[i686_higher_half]
protocol=ultra
setup-apm = true

cmdline = {cmdline}
binary:
Expand Down Expand Up @@ -68,6 +70,7 @@

[amd64_lower_half]
protocol=ultra
setup-apm = true

cmdline = {cmdline}
binary = "/boot/kernel_amd64_lower_half"
Expand All @@ -94,6 +97,7 @@

[amd64_higher_half]
protocol=ultra
setup-apm = true

cmdline = {cmdline}
binary:
Expand All @@ -109,6 +113,7 @@

[amd64_higher_half_5lvl]
protocol=ultra
setup-apm = true

cmdline = {cmdline}
binary:
Expand Down
26 changes: 26 additions & 0 deletions tests/kernel/kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,24 @@ static void validate_modules(struct ultra_module_info_attribute *mi,
print("modules OK\n");
}

static void validate_apm_info(struct ultra_apm_attribute *apm)
{
struct ultra_apm_info *info = &apm->info;

if (info->version < 0x0100)
test_fail("bogus APM version 0x%04X\n", info->version);
if (!(info->flags & 2))
test_fail("bogus APM flags: %d\n", info->flags);
if (info->pm_code_segment_length == 0)
test_fail("bogus pm code segment length %d\n", info->pm_code_segment_length);
if (info->rm_code_segment_length == 0)
test_fail("bogus rm code segment length %d\n", info->rm_code_segment_length);
if (info->data_segment_length == 0)
test_fail("bogus data segment length %d\n", info->data_segment_length);

print("APM info OK\n");
}

static void validate_platform_info(struct ultra_platform_info_attribute *pi,
struct ultra_kernel_info_attribute *ki)
{
Expand Down Expand Up @@ -363,6 +381,7 @@ static void attribute_array_verify(struct ultra_boot_context *bctx)
struct ultra_command_line_attribute *cl = NULL;
struct ultra_framebuffer_attribute *fb = NULL;
struct ultra_memory_map_attribute *mm = NULL;
struct ultra_apm_attribute *apm_info = NULL;
struct ultra_module_info_attribute *modules_begin = NULL;
size_t i, module_count = 0;
bool modules_eof = false;
Expand Down Expand Up @@ -435,6 +454,10 @@ static void attribute_array_verify(struct ultra_boot_context *bctx)

break;

case ULTRA_ATTRIBUTE_APM_INFO:
apm_info = cursor;
break;

default:
test_fail("invalid attribute type %u\n", hdr->type);
}
Expand All @@ -454,6 +477,9 @@ static void attribute_array_verify(struct ultra_boot_context *bctx)
validate_platform_info(pi, ki);
validate_modules(modules_begin, module_count, mm, pi);

if (apm_info)
validate_apm_info(apm_info);

print("\nLoader info: %s (version %d.%d) on %s\n",
pi->loader_name, pi->loader_major, pi->loader_minor,
platform_to_string(pi->platform_type));
Expand Down
Loading