Page 1 of 1

How to call AMP functions in 32bit Protected Mode?

Posted: Mon Nov 17, 2014 8:01 am
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!

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

Posted: Mon Nov 17, 2014 8:30 am
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

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

Posted: Mon Nov 17, 2014 9:08 am
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.

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

Posted: Mon Nov 17, 2014 9:15 am
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

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

Posted: Mon Nov 17, 2014 9:55 am
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.

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

Posted: Mon Nov 17, 2014 9:58 am
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...

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

Posted: Mon Nov 17, 2014 11:38 am
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.

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

Posted: Mon Nov 17, 2014 12:28 pm
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...

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

Posted: Tue Nov 18, 2014 4:24 am
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.

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

Posted: Tue Nov 18, 2014 5:33 am
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.

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

Posted: Tue Nov 18, 2014 9:12 am
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;

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

Posted: Tue Nov 18, 2014 9:39 am
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

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

Posted: Tue Nov 18, 2014 10:01 am
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)

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

Posted: Tue Nov 18, 2014 10:22 am
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!

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

Posted: Tue Nov 18, 2014 12:29 pm
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.