中国DOS联盟论坛

中国DOS联盟

-- 联合DOS 推动DOS 发展DOS --

联盟域名:www.cn-dos.net  论坛域名:www.cn-dos.net/forum
DOS,代表着自由开放与发展,我们努力起来,学习FreeDOS和Linux的自由开放与GNU精神,共同创造和发展美好的自由与GNU GPL世界吧!

游客:  注册 | 登录 | 命令行 | 搜索 | 上传 | 帮助 »
中国DOS联盟论坛 » DOS开发编程 & 发展交流 (开发室) » 有没有研究 FDAPM 关机的朋友。
作者:
标题: 有没有研究 FDAPM 关机的朋友。 上一主题 | 下一主题
zhgwbzhd
高级用户





积分 506
发帖 187
注册 2005-12-4
状态 离线
『楼 主』:  有没有研究 FDAPM 关机的朋友。 [已使用 LLM 解释]

由于这次遇到了 在dos下无法关机的现象。
最终找到了 FDAPM。是可以用ACPI关机的(我我想应该是这样的)
想把他的那些搞明白。
可惜我的汇编能力较弱。有很多地方看的迷迷糊糊。
有愿意搞这个的,我们一起研究他的代码。
源代码我这里有。

愿意的跟帖啊。
LLM 解释

关于FDAPM相关内容的技术分析与探讨

1. FDAPM简介

FDAPM(可能是指与高级配置与电源管理接口(ACPI)相关的特定实现或工具)与在DOS下关机相关。ACPI是一种用于现代计算机系统电源管理的标准规范,它定义了操作系统与硬件之间关于电源状态转换等方面的接口和机制。在DOS环境下正常关机涉及到与硬件电源管理相关的底层操作,而FDAPM可能是一个用于实现基于ACPI进行关机操作的软件相关代码或工具集。

2. 汇编代码相关分析

如果有FDAPM的源代码包含汇编代码部分,对于其中涉及的x86汇编代码部分:

  • 寄存器使用:x86汇编中常用的寄存器如EAXEBXECXEDX等有特定的用途。例如,EAX常被用作函数调用的返回值寄存器或者用于传递功能调用的操作码等。如果代码中有对这些寄存器的操作,需要分析其操作目的是进行内存访问、计算还是与硬件设备进行交互(比如与ACPI相关的硬件寄存器通信)。
  • 内存访问与操作:汇编代码中可能会涉及到对内存地址的读取、写入操作。比如,可能会通过特定的内存地址来获取ACPI相关的数据结构信息(如ACPI的描述表等)。这时候需要明确所操作的内存地址对应的内存区域的用途,是用于存储系统配置信息还是硬件状态等。
  • 中断与调用:x86架构下存在各种中断调用,可能会涉及到通过中断指令(如INT指令)来调用BIOS或ACPI相关的服务例程。例如,可能会通过特定的中断调用号来触发与电源管理相关的操作,这时候需要了解对应的中断调用规范,明确调用该中断后会执行什么样的电源管理相关操作,是进行关机前的资源释放、状态保存等操作。

3. 多种解决方案思路

  • 反汇编与调试
    • 使用反汇编工具:可以使用像IDA Pro这样的反汇编工具对FDAPM的二进制代码进行反汇编,将二进制代码转换为汇编代码形式,这样可以更清晰地看到代码的逻辑结构。通过反汇编能够逐步分析代码中各个函数、指令的功能,尤其是与电源管理相关的部分。
    • 调试器配合:在支持x86平台的调试器(如WinDbg、GDB等)中对FDAPM相关代码进行调试。通过设置断点、单步执行等操作,观察代码在执行过程中寄存器的值变化、内存内容变化以及函数调用流程等,从而理解代码是如何实现基于ACPI进行关机操作的。例如,在GDB中可以使用break命令设置断点,step命令单步执行汇编代码,观察每一步操作对系统状态的影响。
  • 查阅相关文档
    • ACPI规范文档:深入查阅ACPI的官方规范文档,了解ACPI的各种数据结构、操作方法以及相关的函数调用接口等。因为FDAPM是基于ACPI进行关机操作的,所以ACPI规范中的内容是理解FDAPM代码的关键依据。例如,ACPI描述表(如RSDT、XSDT等)的结构以及如何通过这些表来获取系统的电源管理相关信息等内容都需要从ACPI规范中获取。
    • DOS下电源管理相关文档:查找关于在DOS环境下进行电源管理相关操作的文档资料,了解DOS环境下与硬件电源管理交互的传统方式以及ACPI在DOS环境下的适配情况等。这有助于将FDAPM的代码与DOS环境下的电源管理场景结合起来理解。

如果有具体的FDAPM源代码片段,可以进一步针对其中的具体代码进行更细致的分析和探讨。例如,如果有一段汇编代码如下:

MOV EAX, 0x1234
MOV EBX, [0x5678]
INT 0x15

那么可以分析:MOV EAX, 0x1234是将立即数0x1234移动到EAX寄存器中,可能是设置某个功能调用的操作码;MOV EBX, [0x5678]是从内存地址0x5678处读取数据到EBX寄存器中,这里的内存地址可能存储着与电源管理相关的参数;INT 0x15是触发中断号为0x15的中断调用,需要去查找该中断调用在ACPI或BIOS相关规范中对应的功能,看是否是用于电源管理相关的操作,比如可能是调用了一个与ACPI关机操作相关的BIOS服务例程。



2010-8-17 16:28
查看资料  发送邮件  发短消息  网志   编辑帖子  回复  引用回复
zhgwbzhd
高级用户





积分 506
发帖 187
注册 2005-12-4
状态 离线
『第 2 楼』:   [已使用 LLM 解释]

我一边看代码,一般尝试用C写代码。

目前遇到的一个难题是。

我需要读取线性地址为 3FF40000 处的值。
但是用int 0x15 ,ah = 0x87,用int86x()来读取,确读到的值是0.

有什么更好的方法没啊??

谢谢!
LLM 解释

关于读取线性地址值相关问题的分析与可能解决方案

1. 首先回顾涉及的相关技术背景

  • x86中断机制int 0x15是x86架构下的一个BIOS中断调用。其中ah=0x87这个功能号可能与获取物理内存相关信息等操作有关。而int86x()是C语言中用于调用中断的一种方式,通常需要正确设置寄存器等参数来进行有效的中断调用。
  • 线性地址与物理地址的关系:在x86系统中,线性地址到物理地址的转换涉及到分页机制等。如果要读取线性地址处的值,需要考虑系统的内存管理模式(比如是否启用了分页)等因素。

2. 针对当前问题的可能排查与解决方案

  • 检查中断调用参数设置是否正确
    • 首先要确认使用int 0x15, ah=0x87时,其他相关寄存器的设置是否正确。例如,对于读取线性地址处的值,可能需要正确设置eax等寄存器来指定线性地址等参数。假设要读取线性地址3FF40000处的值,正确的参数设置应该是:
#include <dos.h>
union REGS regs;
struct SREGS sregs;

regs.h.ah = 0x87;
regs.x.eax = 0x3FF40000; // 这里设置要读取的线性地址到eax寄存器
int86x(0x15, &regs, &regs, &sregs);
if(regs.h.ah == 0) {
    // 读取成功,获取相应值等操作
    unsigned int value = regs.x.ebx; // 假设返回值在ebx等寄存器中,具体根据中断功能定义
} else {
    // 中断调用出错,处理错误情况
}
- 要仔细核对中断功能号`ah=0x87`的具体参数要求和返回值的寄存器约定。可以查阅相关的BIOS中断文档,确认正确的参数传递和结果获取方式。
  • 考虑内存管理模式的影响
    • 如果系统启用了分页机制,那么线性地址到物理地址的转换需要通过页表来进行。这时候可能需要先获取页目录和页表的信息,然后通过页表项来将线性地址转换为物理地址,再去读取物理地址处的值。例如,在保护模式下,获取页目录基址可以通过读取控制寄存器CR3(mov eax, cr3),然后根据线性地址计算出对应的页目录项和页表项的位置,进而得到物理地址。
    • 代码示例(简单示意获取页目录和页表相关信息):
#include <stdio.h>
#include <stdint.h>

// 获取CR3寄存器的值,即页目录基址
uint32_t get_cr3() {
    uint32_t cr3;
    __asm__ volatile ("mov %%cr3, %0" : "=r" (cr3));
    return cr3;
}

// 根据线性地址和页目录基址获取物理地址
uint32_t linear_to_physical(uint32_t linear_addr, uint32_t cr3) {
    uint32_t page_dir_index = (linear_addr >> 22) & 0x3FF;
    uint32_t page_table_base = *((uint32_t*)(cr3 + page_dir_index * 4));
    uint32_t page_table_index = (linear_addr >> 12) & 0x3FF;
    uint32_t physical_addr = ((page_table_base & 0xFFFFF000) + (linear_addr & 0xFFF));
    return physical_addr;
}

int main() {
    uint32_t linear_addr = 0x3FF40000;
    uint32_t cr3 = get_cr3();
    uint32_t physical_addr = linear_to_physical(linear_addr, cr3);
    // 然后可以通过读取物理地址处的值的方式来获取,比如使用端口操作等方式(如果是内存映射I/O的情况)
    // 或者如果是常规内存,可以尝试用指针等方式,但要注意内存权限等问题
    return 0;
}
- 这里通过获取CR3寄存器值,再根据页目录和页表的结构来将线性地址转换为物理地址,之后就可以针对物理地址来读取值了。不过这种方式相对复杂,需要对x86的内存管理机制有较深入的理解。
  • 检查硬件环境等其他因素
    • 确保目标系统的硬件环境是正常的,比如内存是否存在问题等。可以尝试在不同的硬件环境下测试代码,看是否是特定硬件环境导致的读取失败问题。

总之,首先要严格按照中断调用的规范检查参数设置等情况,同时考虑内存管理模式对线性地址到物理地址转换的影响,必要时通过转换线性地址为物理地址的方式来读取对应位置的值。



2010-8-24 17:44
查看资料  发送邮件  发短消息  网志   编辑帖子  回复  引用回复
zhgwbzhd
高级用户





积分 506
发帖 187
注册 2005-12-4
状态 离线
『第 3 楼』:   [已使用 LLM 解释]

unsigned char TheBuffer[50];
unsigned char Buffer[20];

//参数seg在这里是高16位,offset是低16位,需要取得值在他们表示的后16字节后。
int fetch_high(unsigned int seg,unsigned int offset)
{
unsigned int i;
union REGS in,out;
struct SREGS segregs;

//GDT的前16字节和后16字节。
for(i=0;i<16;i++)
{
TheBuffer[i] = 0;
TheBuffer[0x20 + i] = 0;
}

TheBuffer[0x10]=16;
TheBuffer[0x11]=0;
TheBuffer[0x12]=peekb(seg,offset + 0x10);//低16位中的低8位 值00
TheBuffer[0x13]=peekb(seg,offset + 0x11);//低16位中的高16位 值00
TheBuffer[0x14]=peekb(seg,offset + 0x12);//高16位中的低8位 值FF
TheBuffer[0x15]=0x93;//权限
TheBuffer[0x16]=0;
TheBuffer[0x17]=peekb(seg,offset + 0x13);//高16位中的高8位 值03

TheBuffer[0x18]=16;
TheBuffer[0x19]=0;
i = FP_OFF(Buffer);//想把高端的内存数值复制到本地的数组内
TheBuffer[0x1A]=i & 0xFF;
TheBuffer[0x1B]=i & 0xFF00;

i = FP_SEG(Buffer);
TheBuffer[0x1C]=i & 0xFF;

TheBuffer[0x1D]=0x93;
TheBuffer[0x1E]=0;
TheBuffer[0x1F]=i & 0xFF00;

segregs.es = FP_SEG(TheBuffer);
in.x.si = FP_OFF(TheBuffer);
in.h.ah = 0x87;
in.x.cx = 8;

//以下显示信息为了校验测试。
for(i=0;i<16;i++)
printf("%2x ",TheBuffer[i]);
printf("\n");
for(i=0;i<16;i++)
printf("%2x ",TheBuffer[0x10 + i]);
printf("\n");
for(i=0;i<16;i++)
printf("%2x ",TheBuffer[0x20 + i]);
printf("\n");


int86x(0x15, &in, &out, &segregs);

if(out.x.cflag == 1)
{
printf("fetch_high error \n");
return 0;//error
}

for(i=0;i<16;i++)
printf("%2x ",Buffer[i]);

return 1;

}
LLM 解释

代码功能概述

这段C语言代码主要是用于从内存中获取相关信息并进行一些操作,可能与系统底层内存访问和中断调用有关。

代码详细分析

  1. 变量定义
    • 定义了两个无符号字符数组TheBuffer[50]Buffer[20]
    • 定义了一些局部变量,如unsigned int i,以及用于寄存器操作的union REGS in, outstruct SREGS segregs,这些都是与Intel x86体系结构下的寄存器访问相关的结构体,用于在C语言中模拟对CPU寄存器的操作。
  2. 初始化操作
    • 首先将TheBuffer数组的前16字节和后16字节都初始化为0。
    • 然后对TheBuffer数组的一些特定位置进行赋值操作:
      • TheBuffer[0x10]=16TheBuffer[0x11]=0TheBuffer[0x12]TheBuffer[0x13]seg段和offset + 0x10offset + 0x11偏移处读取字节值,TheBuffer[0x14]seg段和offset + 0x12偏移处读取字节值,TheBuffer[0x15]=0x93设置权限相关值,TheBuffer[0x16]=0TheBuffer[0x17]seg段和offset + 0x13偏移处读取字节值。
      • 接着对TheBuffer数组的其他位置进行赋值,通过FP_OFFFP_SEG宏获取Buffer数组的段和偏移信息,并将其填充到TheBuffer数组中。
  3. 调试输出
    • 有多个循环用于输出TheBuffer数组不同部分的内容,这是为了校验测试,查看数据是否正确填充。
  4. 中断调用
    • 调用int86x函数进行中断调用,中断号为0x15。根据out.x.cflag的值来判断操作是否出错,如果out.x.cflag == 1,则输出错误信息并返回0表示出错;否则继续操作并返回1表示成功。最后输出Buffer数组的内容。

关于peekbFP_OFFFP_SEGint86x等函数或宏的说明

  • peekb函数
    • 功能:用于从指定的内存段(seg)和偏移(offset)处读取一个字节的值。
    • 解决方案:如果自己实现类似功能,在x86体系结构下,可以通过汇编代码来实现内存读取操作,例如使用MOV AL, [ES:DI]之类的指令(假设通过段寄存器ES和偏移寄存器DI来访问内存),在C语言中可以通过内联汇编来封装这个功能。
  • FP_OFF
    • 功能:获取一个指针的偏移地址部分。例如,对于一个指向数组的指针,它会返回该指针相对于段起始地址的偏移量。
    • 解决方案:如果自己实现,可以通过位运算等方式来计算偏移量。例如,对于一个32位的地址,低16位就是偏移量部分,可以通过(ptr) & 0xFFFF来获取。
  • FP_SEG
    • 功能:获取一个指针的段地址部分。
    • 解决方案:同样可以通过位运算,对于32位地址,高16位就是段地址部分,可以通过((ptr) >> 16) & 0xFFFF来获取。
  • int86x函数
    • 功能:用于进行扩展的80x86中断调用。它可以更灵活地设置和获取寄存器的值。
    • 解决方案:如果要自己实现类似功能,在Windows平台下可以使用Windows API中的相关函数来模拟中断调用,或者在Linux等平台下通过系统调用和对寄存器的直接操作来实现类似的功能。例如,在Linux下可以通过syscall来调用底层功能,并手动设置和获取寄存器的值来模拟中断调用的过程。
; 以下是可能的汇编实现思路示例(仅供参考,具体实现需根据完整上下文)
; 假设要实现peekb函数的汇编版本
peekb:
    push es
    mov ax, [esp + 8]  ; seg参数
    mov es, ax
    mov bx, [esp + 12] ; offset参数
    mov al, [es:bx]
    pop es
    ret


2010-8-24 23:33
查看资料  发送邮件  发短消息  网志   编辑帖子  回复  引用回复
zhgwbzhd
高级用户





积分 506
发帖 187
注册 2005-12-4
状态 离线
『第 4 楼』:   使用 LLM 解释/回答一下

问题已经解决:
谢谢各位!


2010-8-25 09:33
查看资料  发送邮件  发短消息  网志   编辑帖子  回复  引用回复
zhgwbzhd
高级用户





积分 506
发帖 187
注册 2005-12-4
状态 离线
『第 5 楼』:   使用 LLM 解释/回答一下

今天遇到了惠普 400 wrokstation

居然 fdapm 都不能关。

郁闷啊。。。。。。


2010-9-7 14:42
查看资料  发送邮件  发短消息  网志   编辑帖子  回复  引用回复
pfox
银牌会员




积分 1451
发帖 446
注册 2002-10-20
状态 离线
『第 6 楼』:   使用 LLM 解释/回答一下

现在不少主板fdapm都不能关了,所以现在比较好用的方法是直接按电源开关。希望楼主能够改造出一个更好用的关机软件。





我的下载空间
2010-9-16 12:08
查看资料  发送邮件  发短消息  网志   编辑帖子  回复  引用回复
zhgwbzhd
高级用户





积分 506
发帖 187
注册 2005-12-4
状态 离线
『第 7 楼』:   使用 LLM 解释/回答一下

嗯,我相信会解决的。

我现在正在致力于此。


2010-9-28 16:22
查看资料  发送邮件  发短消息  网志   编辑帖子  回复  引用回复
asdfq
初级用户





积分 34
发帖 15
注册 2018-12-28
状态 离线
『第 8 楼』:  就当水一贴 使用 LLM 解释/回答一下

GRUB4DOS 的 halt 是能关机的。但是它毕竟是启动器而不是电源管理程序。虽然可以在 DOS 下用命令行运行,但是颇有些杀鸡用牛刀的感觉。

在源代码中 sage2/asm.S 找 grub_halt 是关机代码。

然而我不会汇编(逃)

传言正常靠谱操作 ACPI 需要写虚拟机什么的,但 GRUB4DOS 的不像有这类东西。

希望 fdapm 更新。

Last edited by asdfq on 2023-8-8 at 19:07 ]


2023-8-8 19:04
查看资料  发送邮件  发短消息  网志   编辑帖子  回复  引用回复

请注意:您目前尚未注册或登录,请您注册登录以使用论坛的各项功能,例如发表和回复帖子等。


可打印版本 | 推荐给朋友 | 订阅主题 | 收藏主题



论坛跳转: