How to call AMP functions in 32bit Protected Mode?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
donggas90
Member
Member
Posts: 38
Joined: Fri Oct 17, 2014 12:07 pm

How to call AMP functions in 32bit Protected Mode?

Post by donggas90 »

I'm working on APM for power management.
My machine support APM in 32bit PMode, checked by flag output of check installation function.

Code: Select all

ApmCheck16:
   call Power_CheckApmInstallation
   jc ApmNotSupport16
   mov word[BOOT_INFO_OFFSET + SBootInfo.ApmFlags], cx

   call Power_ConnectApm32BitInterface
   jc ApmNotSupport16
   mov word[BOOT_INFO_OFFSET + SBootInfo.Apm32BitCodeSegment], ax
   mov word[BOOT_INFO_OFFSET + SBootInfo.Apm32BitDataSegment], dx
   mov dword[BOOT_INFO_OFFSET + SBootInfo.ApmEntryPointAddress], ebx
   mov word[BOOT_INFO_OFFSET + SBootInfo.Apm32BitCodeLength], si
   mov word[BOOT_INFO_OFFSET + SBootInfo.Apm32BitDataLength], di

   mov al, 1
   mov byte[BOOT_INFO_OFFSET + SBootInfo.ApmConnected], al

   jmp ApmDone16

ApmNotSupport16:
   xor ax, ax
   mov byte[BOOT_INFO_OFFSET + SBootInfo.ApmConnected], al   

ApmDone16:
First, check installation and connect to 32bit PMode interfaces.
Then, how can I use them in 32bit PMode?

In real mode, can use interrupt but protected mode.
I think, maybe AMP segments are clues.
but cannot figure out exactly how.
APM Wiki has not any content about that.

Can I see any example? or pseudo code?

Thanks!
User avatar
max
Member
Member
Posts: 616
Joined: Mon Mar 05, 2012 11:23 am
Libera.chat IRC: maxdev
Location: Germany
Contact:

Re: How to call AMP functions in 32bit Protected Mode?

Post by max »

Hey,

I don't have experience with APM, but maybe the APM specification can help you. IIRC there is a 32bit protected mode interface for this.
Just curious, why are you using the (obsolete) APM and not ACPI?

Greets,
Max
donggas90
Member
Member
Posts: 38
Joined: Fri Oct 17, 2014 12:07 pm

Re: How to call AMP functions in 32bit Protected Mode?

Post by donggas90 »

Thanks, max.
max wrote: Just curious, why are you using the (obsolete) APM and not ACPI?
I also know about that.
but my testing machine, VMware, maby do not support ACPI correctly.
so I'm trying to use it.

and I already seen that doc but couldn't found exact way to call them.
User avatar
SpyderTL
Member
Member
Posts: 1074
Joined: Sun Sep 19, 2010 10:05 pm

Re: How to call AMP functions in 32bit Protected Mode?

Post by SpyderTL »

ACPI is a lot harder to use than APM, but I wouldn't worry about ACPI not being supported or being broken in VMware.

I'd be more worried about APM not being supported on real hardware.

However, I'm also a bit curious about how the 32-bit APM interface works, so I'll scan through the APM 1.2 spec sheet (there is a link at the bottom of the APM wiki page), and I'll see what I can find.

Edit: Link broken. Here is the APM 1.2 spec link: http://download.microsoft.com/download/ ... APMV12.rtf
Project: OZone
Source: GitHub
Current Task: LIB/OBJ file support
"The more they overthink the plumbing, the easier it is to stop up the drain." - Montgomery Scott
donggas90
Member
Member
Posts: 38
Joined: Fri Oct 17, 2014 12:07 pm

Re: How to call AMP functions in 32bit Protected Mode?

Post by donggas90 »

SpyderTL wrote: However, I'm also a bit curious about how the 32-bit APM interface works, so I'll scan through the APM 1.2 spec sheet (there is a link at the bottom of the APM wiki page), and I'll see what I can find.
I have both MS Word and PDF doc about AMP 1.2 that written by Intel and Microsoft.
You can see about 32bit Protected Mode at Section 4.6.4 about Interface connect function section.
The doc just describe about segment selector, stack etc.
but the keypoint missing, how to call that in PMode without interrupt and how to use returned values.
Last edited by donggas90 on Mon Nov 17, 2014 10:00 am, edited 1 time in total.
User avatar
SpyderTL
Member
Member
Posts: 1074
Joined: Sun Sep 19, 2010 10:05 pm

Re: How to call AMP functions in 32bit Protected Mode?

Post by SpyderTL »

I added some details to the APM page. Hopefully, this will help you guys get started.

Feel free to fix any mistakes I've made, or to clarify anything that I've left out...
Project: OZone
Source: GitHub
Current Task: LIB/OBJ file support
"The more they overthink the plumbing, the easier it is to stop up the drain." - Montgomery Scott
donggas90
Member
Member
Posts: 38
Joined: Fri Oct 17, 2014 12:07 pm

Re: How to call AMP functions in 32bit Protected Mode?

Post by donggas90 »

In the Wiki,
Note: The 16-bit and 32-bit code segments returned from the function above are the real-mode segments containing the protected mode code. This means that the value returned must first be converted from the real-mode segment number to a linear address. This can be done by shifting the value to the left by 8 bits.
it means call like below in VC++?
Example that to call Engaging.

Code: Select all

uint32_t lCall = (gBootInfo->mApm32BitCodeSegment << 8) + gBootInfo->mApmEntryPointAddress;
bool lResult;
_asm
{
	mov ah, 0x53;
	mov al, 0x0F;
	mov bx, 0x01;
	mov cx, 0x01;
	call dword ptr[lCall];
	jc _Fail;
	mov byte ptr[lResult], 1;

_Fail:
	mov byte ptr[lResult], 0;
}
but it occur page fault or general protection fault.
I already identity mapped whole of physical memory.
User avatar
SpyderTL
Member
Member
Posts: 1074
Joined: Sun Sep 19, 2010 10:05 pm

Re: How to call AMP functions in 32bit Protected Mode?

Post by SpyderTL »

If I'm reading the spec correctly, you can't use a flat memory model to call the APM entry point. I think you have to set up a GDT entry just for APM to use. Actually 3 entries--one for 32-bit code, one for 16-bit code, and one for data.

Someone correct me if I'm wrong.

Edit: Just to clarify, I think you have to set up (for 32-bit protected mode) 3 GDT (or LDT) entries, and you must use the values that were returned by the "Connect 32-Bit Protected Mode Interface" BIOS call (when you were in 16-Bit Real Mode) to set up the correct GDT entries that the APM code demands. This means that you have to store the values returned in AX, BX, CX, SI and DI somewhere safe, so that you can use them when setting up your GDT tables.

So if you are using entries 0x08 and 0x10 now, for your flat memory model, you will need to add an additional 3 entries: 0x18 for the 32-bit protected mode code segment specified by the "connect" call, which will be used when calling the APM entry point (pointed to by BX), 0x20 for the 16-bit code segment that APM will use to switch between 32-bit and 16-bit mode, internally, and 0x28 which will be used by both 32-bit and 16-bit APM code to store data, temporarily.

I imagine that all three of these segments, which are "demanded" by the APM connect call will be within the "reserved" System BIOS memory area, which should already be marked as reserved by the BIOS memory map, so you shouldn't lose any actual "usable" memory when setting these GDT entries up. But that's just a guess...
Project: OZone
Source: GitHub
Current Task: LIB/OBJ file support
"The more they overthink the plumbing, the easier it is to stop up the drain." - Montgomery Scott
donggas90
Member
Member
Posts: 38
Joined: Fri Oct 17, 2014 12:07 pm

Re: How to call AMP functions in 32bit Protected Mode?

Post by donggas90 »

SpyderTL wrote: Edit: Just to clarify, I think you have to set up (for 32-bit protected mode) 3 GDT (or LDT) entries,
Oh, I got it.

Code: Select all


	static uint32_t lsApm[6];
	lsApm[0] = gBootInfo->mApm32BitCodeSegment;
	lsApm[1] = gBootInfo->mApm32BitCodeLength;
	lsApm[2] = gBootInfo->mApm16BitCodeSegment;
	lsApm[3] = gBootInfo->mApm16BitCodeLength;
	lsApm[4] = gBootInfo->mApm32BitDataSegment;
	lsApm[5] = gBootInfo->mApm32BitDataLength;

Code: Select all

	msGdts[3].Clear().SetBase(pApm[0]).SetLimit(pApm[1] - 1)
		.SetReadableAndWritable(true).SetExecutable(true).SetDescriptorType(EGdtDescriptorType::CodeOrData)
		.SetSegmentPresent(true).SetPrivilegeLevel(EDescriptorPrivilegeLevel::Ring0).SetGranularity(EGdtGranularity::Kilobytes)
		.SetOperandSize(EGdtOperandSize::_32Bit);
	msGdts[4].Clear().SetBase(pApm[2]).SetLimit(pApm[3] - 1)
		.SetReadableAndWritable(true).SetExecutable(true).SetDescriptorType(EGdtDescriptorType::CodeOrData)
		.SetSegmentPresent(true).SetPrivilegeLevel(EDescriptorPrivilegeLevel::Ring0).SetGranularity(EGdtGranularity::Kilobytes)
		.SetOperandSize(EGdtOperandSize::_16Bit);
	msGdts[5].Clear().SetBase(pApm[4]).SetLimit(pApm[5] - 1)
		.SetReadableAndWritable(true).SetExecutable(true).SetDescriptorType(EGdtDescriptorType::CodeOrData)
		.SetSegmentPresent(true).SetPrivilegeLevel(EDescriptorPrivilegeLevel::Ring0).SetGranularity(EGdtGranularity::Kilobytes)
		.SetOperandSize(EGdtOperandSize::_32Bit);

Code: Select all

uint32_t lCall = gBootInfo->mApmEntryPointAddress;
bool lResult;
_asm
{
	cli
	mov ax, 0x18;
	mov ds, ax;
	sti
	mov ah, 0x53;
	mov al, 0x0F;
	mov bx, 0x01;
	mov cx, 0x01;
	call dword ptr[lCall];
	cli
	mov ax, 0x10;
	mov ds, ax;
	sti
	jc _Fail;
	mov byte ptr[lResult], 1;

_Fail:
	mov byte ptr[lResult], 0;
}
I added new 3 GDT for APM those values are from APM returned values.
but triple fault occur when call the entry.
Icee
Member
Member
Posts: 100
Joined: Wed Jan 08, 2014 8:41 am
Location: Moscow, Russia

Re: How to call AMP functions in 32bit Protected Mode?

Post by Icee »

@donggas90

I see two problems with the code.

1. The call must be a far call with the 32-bit code selector you configured for the APM BIOS, here is a quote:
The APM caller invokes the APM BIOS routines using the 32-bit interface by making a far call to the 32-bit code segment selector it initializes, with the offset returned in EBX from this call.
2. I am not sure you are setting up your descriptors correctly. How do you distinguish code from data?

This must be the code segment:

Code: Select all

msGdts[3].Clear().SetBase(pApm[0]).SetLimit(pApm[1] - 1)
      .SetReadableAndWritable(true).SetExecutable(true).SetDescriptorType(EGdtDescriptorType::CodeOrData)
      .SetSegmentPresent(true).SetPrivilegeLevel(EDescriptorPrivilegeLevel::Ring0).SetGranularity(EGdtGranularity::Kilobytes)
      .SetOperandSize(EGdtOperandSize::_32Bit);
and this the data segment:

Code: Select all

msGdts[5].Clear().SetBase(pApm[4]).SetLimit(pApm[5] - 1)
      .SetReadableAndWritable(true).SetExecutable(true).SetDescriptorType(EGdtDescriptorType::CodeOrData)
      .SetSegmentPresent(true).SetPrivilegeLevel(EDescriptorPrivilegeLevel::Ring0).SetGranularity(EGdtGranularity::Kilobytes)
      .SetOperandSize(EGdtOperandSize::_32Bit);
But the method call chains appear the same to me.
donggas90
Member
Member
Posts: 38
Joined: Fri Oct 17, 2014 12:07 pm

Re: How to call AMP functions in 32bit Protected Mode?

Post by donggas90 »

Thanks for regard, Icee.
Icee wrote: 1. The call must be a far call with the 32-bit code selector you configured for the APM BIOS, here is a quote:
2. I am not sure you are setting up your descriptors correctly. How do you distinguish code from data?
No.2 was solved just remove executability from data descriptor.

Hmm, I didn't noticed about far call.
BTW the real problem is that I didn't use far call in VC inline assembly at all.
so I don't know, how to do it!

The situation is so rare case to far call in Windows application,
so couldn't found useful information about that by searching google.

Can anyone tell me how to far call in VC ASM?
I have two values for this.

Code: Select all

const uint16_t lSelector = 0x18;
const uint32_t lCall = gBootInfo->mApmEntryPointAddress;
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: How to call AMP functions in 32bit Protected Mode?

Post by Brendan »

Hi,
donggas90 wrote:Can anyone tell me how to far call in VC ASM?
I have two values for this.

Code: Select all

const uint16_t lSelector = 0x18;
const uint32_t lCall = gBootInfo->mApmEntryPointAddress;
Are they in that order? If they are, they're in the wrong order (the offset part needs to come first).

For the syntax in Visual C's inline assembly, I don't know, but I'd guess "call far dword ptr [lCall];"


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Icee
Member
Member
Posts: 100
Joined: Wed Jan 08, 2014 8:41 am
Location: Moscow, Russia

Re: How to call AMP functions in 32bit Protected Mode?

Post by Icee »

That should be "JMP FWORD PTR [target]" (if I'm not mistaken), where target is defined as:

Code: Select all

#pragma pack(push, 1)
struct { uint32_t offset; uint16_t selector; } target;
#pragma pack(pop)
donggas90
Member
Member
Posts: 38
Joined: Fri Oct 17, 2014 12:07 pm

Re: How to call AMP functions in 32bit Protected Mode?

Post by donggas90 »

Thanks again, Brendan.
Brendan wrote: For the syntax in Visual C's inline assembly, I don't know, but I'd guess "call far dword ptr [lCall];"
Fword was the point.

Code: Select all

const uint16_t lSelector = 0x18;
const uint32_t lCall = gBootInfo->mApmEntryPointAddress;
const uint64_t lCallFword = (static_cast<uint64_t>(lSelector) << 32) | static_cast<uint64_t>(lCall);
bool lResult(false);
_asm
{
	mov ah, 0x53;
	mov al, 0x0F;
	mov bx, 0x01;
	mov cx, 0x01;
	call fword ptr[lCallFword];
	jc _Fail;
	mov byte ptr[lResult], 1;

_Fail:
	mov byte ptr[lResult], 0;
}
but page fault occur. #-o
I cannot understand why.
because I can access(R/W) the memory with far called virtual address.
(0x18SelectorBase + lCall)

Additionally, GDT is not problem because base and limit were matched with APM returned values in runtime.

ADD.
In case, the stack invaded another data part in APM routine, in particularly, page entries.
so I marked 4K*16 of stack as used part with my memory manager to avoid to use them.
Then page fault was gone, maybe some page entries were corrupted by the stack.
but general protection fault occured instead of page fault!
donggas90
Member
Member
Posts: 38
Joined: Fri Oct 17, 2014 12:07 pm

Re: How to call AMP functions in 32bit Protected Mode?

Post by donggas90 »

I got some problems.

First, APM returned segments are real mode based segment values.
So need to multiply 16 for GDT base value.

Second, Data segment is pointing Available to use space(0x40:00h) by the OS so already wrote them all.
Thus maybe APM data was corrupted, and if it needs to read some data from there maybe cause some problems.
In real mode, particulary bootloader, that space usually use for it.
so it's strange. Why APM selected that space as its data segment obstinately leave reserved space.

Third, APM returned length values are odd because they are not aligned.
In my case, 0xFFFF. but expected aligned 0x10000(0x0000) because it will be -1 for GDT limit value, then being 0xFFFF.

The point is that, at least code segments were not corrupted
because BIOS already marked there as reserved space with memory map.
so I didn't access.
but General Protection Fault occuring.

Maybe APM also broken in VMware.
At last, I returned ZERO again.

Thanks for regards.
Post Reply