Skip to content

Commit f5a6829

Browse files
committed
Introduce APM support
Signed-off-by: Daniil Tatianin <[email protected]>
1 parent 849d49b commit f5a6829

File tree

10 files changed

+210
-1
lines changed

10 files changed

+210
-1
lines changed

loader/arch/x86/bios/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ target_sources(
2929
target_sources(
3030
${LOADER_EXECUTABLE}
3131
PRIVATE
32+
apm.c
3233
bios_disk_services.c
3334
bios_entry.c
3435
bios_find.c

loader/arch/x86/bios/apm.c

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#define MSG_FMT(msg) "BIOS-APM: " msg
2+
3+
#include "common/format.h"
4+
#include "common/log.h"
5+
#include "bios_call.h"
6+
#include "services_impl.h"
7+
#include "apm.h"
8+
9+
#define APM_SIGNATURE 0x504D
10+
#define APM_POWER_DEVICE_ID_APM_BIOS 0x0000
11+
12+
#define APM_FLAG_32BIT_INTERFACE_SUPPORTED (1 << 1)
13+
14+
#define APM_INT 0x15
15+
#define APM_CMD 0x53
16+
#define MAKE_APM_CMD(cmd) ((APM_CMD << 8) | (cmd))
17+
18+
#define APM_INSTALLATION_CHECK MAKE_APM_CMD(0x00)
19+
#define APM_PM32_INTERFACE_CONNECT MAKE_APM_CMD(0x03)
20+
#define APM_INTERFACE_DISCONNECT MAKE_APM_CMD(0x04)
21+
22+
static bool check_apm_call(
23+
const struct real_mode_regs *in_regs,
24+
const struct real_mode_regs *out_regs
25+
)
26+
{
27+
if (is_carry_set(out_regs)) {
28+
print_warn(
29+
"APM call 0x%04X failed: %d\n",
30+
in_regs->eax, (out_regs->eax & 0xFFFF) >> 8
31+
);
32+
return false;
33+
}
34+
35+
if (in_regs->eax == APM_INSTALLATION_CHECK) {
36+
u16 signature = out_regs->ebx & 0xFFFF;
37+
38+
if (unlikely(signature != APM_SIGNATURE)) {
39+
print_warn("bad APM signature 0x%04X\n", signature);
40+
return false;
41+
}
42+
}
43+
44+
return true;
45+
}
46+
47+
bool services_setup_apm(struct apm_info *out_info)
48+
{
49+
struct real_mode_regs out_regs, in_regs = { 0 };
50+
51+
// All queries will be for the APM BIOS "device"
52+
in_regs.ebx = APM_POWER_DEVICE_ID_APM_BIOS;
53+
54+
// 1. Check if APM exists at all
55+
in_regs.eax = APM_INSTALLATION_CHECK;
56+
bios_call(APM_INT, &in_regs, &out_regs);
57+
if (!check_apm_call(&in_regs, &out_regs))
58+
return false;
59+
60+
if (!(out_regs.ecx & APM_FLAG_32BIT_INTERFACE_SUPPORTED)) {
61+
print_warn("APM doesn't support 32-bit interface\n");
62+
return false;
63+
}
64+
65+
// 2. Disconnect if anything was connected previously, ignore return value
66+
in_regs.eax = APM_INTERFACE_DISCONNECT;
67+
bios_call(APM_INT, &in_regs, &out_regs);
68+
69+
// 3. Connect the 32-bit interface
70+
in_regs.eax = APM_PM32_INTERFACE_CONNECT;
71+
bios_call(APM_INT, &in_regs, &out_regs);
72+
if (!check_apm_call(&in_regs, &out_regs))
73+
return false;
74+
75+
print_info("32-bit PM interface connected\n");
76+
out_info->pm_code_segment = out_regs.eax & 0xFFFF;
77+
out_info->pm_code_segment_length = out_regs.esi & 0xFFFF;
78+
out_info->pm_offset = out_regs.ebx;
79+
80+
out_info->rm_code_segment = out_regs.ecx & 0xFFFF;
81+
out_info->rm_code_segment_length = out_regs.esi >> 16;
82+
83+
out_info->data_segment = out_regs.edx & 0xFFFF;
84+
out_info->data_segment_length = out_regs.edi & 0xFFFF;
85+
86+
// 4. Recheck flags, as they might change after 32-bit interface install
87+
in_regs.eax = APM_INSTALLATION_CHECK;
88+
bios_call(APM_INT, &in_regs, &out_regs);
89+
if (unlikely(!check_apm_call(&in_regs, &out_regs))) {
90+
in_regs.eax = APM_INTERFACE_DISCONNECT;
91+
bios_call(APM_INT, &in_regs, &out_regs);
92+
return false;
93+
}
94+
95+
out_info->version = out_regs.eax & 0xFFFF;
96+
out_info->flags = out_regs.ecx & 0xFFFF;
97+
return true;
98+
}

loader/boot_protocol/ultra.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,10 +458,28 @@ static bool set_video_mode(struct config *cfg, struct loadable_entry *entry,
458458
return true;
459459
}
460460

461+
static bool apm_setup(struct config *cfg, struct loadable_entry *le,
462+
struct apm_info *out_info)
463+
{
464+
bool wants_apm = false;
465+
466+
cfg_get_bool(cfg, le, SV("setup-apm"), &wants_apm);
467+
if (!wants_apm)
468+
return false;
469+
470+
if (services_get_provider() != SERVICE_PROVIDER_BIOS) {
471+
print_info("ignoring request to set up APM on UEFI\n");
472+
return false;
473+
}
474+
475+
return services_setup_apm(out_info);
476+
}
477+
461478
struct attribute_array_spec {
462479
bool higher_half_pointers;
463480
bool fb_present;
464481
bool cmdline_present;
482+
bool apm_info_present;
465483
uint8_t page_table_depth;
466484

467485
struct ultra_framebuffer fb;
@@ -471,6 +489,8 @@ struct attribute_array_spec {
471489

472490
struct dynamic_buffer module_buf;
473491

492+
struct apm_info apm_info;
493+
474494
ptr_t acpi_rsdp_address;
475495
ptr_t dtb_address;
476496
ptr_t smbios_address;
@@ -595,6 +615,18 @@ static void *write_framebuffer(struct ultra_framebuffer_attribute *fb_attr,
595615
return ++fb_attr;
596616
}
597617

618+
static void *write_apm_info(struct ultra_apm_attribute *apm_attr,
619+
const struct attribute_array_spec *spec)
620+
{
621+
apm_attr->header.type = ULTRA_ATTRIBUTE_APM_INFO;
622+
apm_attr->header.size = sizeof(struct ultra_apm_attribute);
623+
624+
BUILD_BUG_ON(sizeof(apm_attr->info) != sizeof(struct apm_info));
625+
memcpy(&apm_attr->info, &spec->apm_info, sizeof(struct apm_info));
626+
627+
return ++apm_attr;
628+
}
629+
598630
static void *write_memory_map(void *attr_ptr, size_t entry_count)
599631
{
600632
struct ultra_memory_map_attribute *mm = attr_ptr;
@@ -651,6 +683,8 @@ static ptr_t build_attribute_array(const struct attribute_array_spec *spec,
651683
bytes_needed += cmdline_aligned_length;
652684
bytes_needed += spec->fb_present *
653685
sizeof(struct ultra_framebuffer_attribute);
686+
bytes_needed += spec->apm_info_present *
687+
sizeof(struct ultra_apm_attribute);
654688
bytes_needed += sizeof(struct ultra_memory_map_attribute);
655689

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

762+
if (spec->apm_info_present) {
763+
attr_ptr = write_apm_info(attr_ptr, spec);
764+
*attr_count += 1;
765+
}
766+
728767
attr_ptr = write_memory_map(attr_ptr, mm_entry_count);
729768
*attr_count += 1;
730769
return ret;
@@ -1140,6 +1179,8 @@ static void ultra_protocol_boot(struct config *cfg, struct loadable_entry *le)
11401179
spec.dtb_address = services_find_dtb();
11411180
spec.smbios_address = services_find_smbios();
11421181

1182+
spec.apm_info_present = apm_setup(cfg, le, &spec.apm_info);
1183+
11431184
/*
11441185
* Attempt to set video mode last, as we're not going to be able to use
11451186
* legacy tty logging after that.

loader/include/apm.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#pragma once
2+
3+
#include "common/types.h"
4+
5+
struct apm_info {
6+
u16 version;
7+
u16 flags;
8+
9+
u16 pm_code_segment;
10+
u16 pm_code_segment_length;
11+
u32 pm_offset;
12+
13+
u16 rm_code_segment;
14+
u16 rm_code_segment_length;
15+
16+
u16 data_segment;
17+
u16 data_segment_length;
18+
};

loader/include/services.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "common/types.h"
44
#include "common/attributes.h"
5+
#include "apm.h"
56

67
enum service_provider {
78
SERVICE_PROVIDER_INVALID,
@@ -28,6 +29,12 @@ ptr_t services_find_dtb(void);
2829
*/
2930
ptr_t services_find_smbios(void);
3031

32+
/*
33+
* Attempts to setup the 32-bit protected-mode interface for APM if it exists.
34+
* Returns true if the interface was successfully installed, false otherwise.
35+
*/
36+
bool services_setup_apm(struct apm_info *out_info);
37+
3138
/*
3239
* Aborts the loader execution in a platform-specific manner.
3340
* Must be used for unrecoverable errors.

loader/uefi/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ target_sources(
3434
uefi_memory_services.c
3535
uefi_video_services.c
3636
relocator.c
37+
stubs.c
3738
)

loader/uefi/stubs.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#define MSG_FMT(msg) "UEFI-STUBS: " msg
2+
3+
#include "common/log.h"
4+
#include "common/types.h"
5+
6+
bool services_setup_apm(struct apm_info *out_info)
7+
{
8+
UNUSED(out_info);
9+
10+
print_warn("APM setup is unsupported!\n");
11+
return false;
12+
}

tests/disk_image.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
1212
[i686_lower_half]
1313
protocol=ultra
14+
setup-apm = true
1415
1516
cmdline = {cmdline}
1617
binary = "/boot/kernel_i686_lower_half"
@@ -37,6 +38,7 @@
3738
3839
[i686_higher_half]
3940
protocol=ultra
41+
setup-apm = true
4042
4143
cmdline = {cmdline}
4244
binary:
@@ -68,6 +70,7 @@
6870
6971
[amd64_lower_half]
7072
protocol=ultra
73+
setup-apm = true
7174
7275
cmdline = {cmdline}
7376
binary = "/boot/kernel_amd64_lower_half"
@@ -94,6 +97,7 @@
9497
9598
[amd64_higher_half]
9699
protocol=ultra
100+
setup-apm = true
97101
98102
cmdline = {cmdline}
99103
binary:
@@ -109,6 +113,7 @@
109113
110114
[amd64_higher_half_5lvl]
111115
protocol=ultra
116+
setup-apm = true
112117
113118
cmdline = {cmdline}
114119
binary:

tests/kernel/kernel.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,24 @@ static void validate_modules(struct ultra_module_info_attribute *mi,
306306
print("modules OK\n");
307307
}
308308

309+
static void validate_apm_info(struct ultra_apm_attribute *apm)
310+
{
311+
struct ultra_apm_info *info = &apm->info;
312+
313+
if (info->version < 0x0100)
314+
test_fail("bogus APM version 0x%04X\n", info->version);
315+
if (!(info->flags & 2))
316+
test_fail("bogus APM flags: %d\n", info->flags);
317+
if (info->pm_code_segment_length == 0)
318+
test_fail("bogus pm code segment length %d\n", info->pm_code_segment_length);
319+
if (info->rm_code_segment_length == 0)
320+
test_fail("bogus rm code segment length %d\n", info->rm_code_segment_length);
321+
if (info->data_segment_length == 0)
322+
test_fail("bogus data segment length %d\n", info->data_segment_length);
323+
324+
print("APM info OK\n");
325+
}
326+
309327
static void validate_platform_info(struct ultra_platform_info_attribute *pi,
310328
struct ultra_kernel_info_attribute *ki)
311329
{
@@ -363,6 +381,7 @@ static void attribute_array_verify(struct ultra_boot_context *bctx)
363381
struct ultra_command_line_attribute *cl = NULL;
364382
struct ultra_framebuffer_attribute *fb = NULL;
365383
struct ultra_memory_map_attribute *mm = NULL;
384+
struct ultra_apm_attribute *apm_info = NULL;
366385
struct ultra_module_info_attribute *modules_begin = NULL;
367386
size_t i, module_count = 0;
368387
bool modules_eof = false;
@@ -435,6 +454,10 @@ static void attribute_array_verify(struct ultra_boot_context *bctx)
435454

436455
break;
437456

457+
case ULTRA_ATTRIBUTE_APM_INFO:
458+
apm_info = cursor;
459+
break;
460+
438461
default:
439462
test_fail("invalid attribute type %u\n", hdr->type);
440463
}
@@ -454,6 +477,9 @@ static void attribute_array_verify(struct ultra_boot_context *bctx)
454477
validate_platform_info(pi, ki);
455478
validate_modules(modules_begin, module_count, mm, pi);
456479

480+
if (apm_info)
481+
validate_apm_info(apm_info);
482+
457483
print("\nLoader info: %s (version %d.%d) on %s\n",
458484
pi->loader_name, pi->loader_major, pi->loader_minor,
459485
platform_to_string(pi->platform_type));

0 commit comments

Comments
 (0)