无忧启动论坛

 找回密码
 注册
搜索
系统gho:最纯净好用系统下载站投放广告、加入VIP会员,请联系 微信:wuyouceo
查看: 7982|回复: 15
打印 上一主题 下一主题

Linux 关机重启相关代码研究(备忘之用)

[复制链接]
跳转到指定楼层
1#
发表于 2015-7-7 17:33:07 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
重启的执行步骤:
  1. void kernel_restart(char *cmd)
  2. {
  3.         kernel_restart_prepare(cmd);
  4.         migrate_to_reboot_cpu();
  5.         syscore_shutdown();
  6.         machine_restart(cmd);
  7. }
复制代码


停机的执行步骤:
  1. void kernel_halt(void)
  2. {
  3.         kernel_shutdown_prepare(SYSTEM_HALT);
  4.         migrate_to_reboot_cpu();
  5.         syscore_shutdown();
  6.         machine_halt();
  7. }
复制代码


断电的执行步骤:
  1. void kernel_power_off(void)
  2. {
  3.         kernel_shutdown_prepare(SYSTEM_POWER_OFF);
  4.         if (pm_power_off_prepare)
  5.                 pm_power_off_prepare();
  6.         migrate_to_reboot_cpu();
  7.         syscore_shutdown();
  8.         machine_power_off();
  9. }
复制代码


KEXEC 的执行步骤:
  1.         {
  2.                 kexec_in_progress = true;
  3.                 kernel_restart_prepare(NULL);
  4.                 migrate_to_reboot_cpu();

  5.                 /*
  6.                  * migrate_to_reboot_cpu() disables CPU hotplug assuming that
  7.                  * no further code needs to use CPU hotplug (which is true in
  8.                  * the reboot case). However, the kexec path depends on using
  9.                  * CPU hotplug again; so re-enable it here.
  10.                  */
  11.                 cpu_hotplug_enable();
  12.                 pr_emerg("Starting new kernel\n");
  13.                 machine_shutdown();
  14.         }

  15.         machine_kexec(kexec_image);
复制代码


2#
 楼主| 发表于 2015-7-17 11:31:52 | 只看该作者
Operating Systems Development - Keyboard http://www.brokenthorn.com/Resources/OSDev19.html

The PS/2 Keyboard Interface http://www.computer-engineering.org/ps2keyboard/

"8042" PS/2 Controller  http://wiki.osdev.org/%228042%22_PS/2_Controller
回复

使用道具 举报

3#
 楼主| 发表于 2015-7-21 09:23:30 | 只看该作者
本帖最后由 不点 于 2015-7-21 09:31 编辑

开这个帖子的目的,其实是研究 Linux 下用 kexec 加载 grub.exe 死机的原因。

我编译了一个极简的 Linux 内核,并在 grub.exe 端增加了键盘初始化的代码,已经可以避免死机,即在 kexec 加载 grub.exe 之后能够正常使用 grub4dos 的各项功能。有兴趣者可以在 http://reboot.pro/ 的 grub4dos 专区下载编译好的 bzImage 和 initrd 文件进行测试。

需要说明的是,这是个极简的 Linux 内核,去掉了几乎所有的硬件驱动,只剩下那些与内存盘有关的驱动。这样的 Linux 内核是没有实际使用价值的。但是有一点很重要:它是一个概念证明——它证明了,一个 Linux 内核是可以用 kexec 来加载 grub.exe 的。这个结论或许对某些研究者有用。



我尝试在 Linux 内核中增添硬件驱动,随便增添点什么驱动,都会导致 kexec 加载 grub.exe 之后出现死机。



要想定位导致死机的具体原因,则需要对 Linux 内核有很深入的了解。我相信那些熟悉 Linux 内核的人,尤其是对硬件驱动很熟悉的人,可以深入研究,最终应该能把原因找到。



另外一个方面的考虑:那个极简 Linux 内核,仅仅是缺少了硬件驱动。Linux 现有的硬件驱动会破坏 BIOS 运行环境。那么,假如有人懂得自己编写硬件驱动,编写的过程中注意不破坏掉 BIOS 运行环境,则有可能发展出一个新的 Linux 分支,它能够正常加载 grub.exe,而这个新的 Linux 分支,其本身可以表现为一个独立的 bootloader(比如说它本身可以具有 grub4dos 的功能)。

如何检验它是否破坏 BIOS 运行环境?很简单,只要能够用 kexec 顺利加载 grub.exe ,那就是没有破坏 BIOS 运行环境;否则,那就是破坏了。通过这个简单的测试,驱动程序的编写者很容易知道自己的驱动程序是否已经伤害了 BIOS 运行环境。
回复

使用道具 举报

4#
 楼主| 发表于 2015-7-22 16:04:11 | 只看该作者
How to Use the vm86 System Call in Linux


Years ago, back when my standards for what qualifies as good code were apparently much lower, I read through the source code of LRMI in order to learn how to use the otherwise undocumented vm86 system call.

The following is a copy of a newsgroup post I wrote afterwards. My apologies if the pre-formatted text doesn't fit within the margins, but I don't care to reformat it. I'm just tired of searching the internet to find this information every time I need it.

The Only Documentation for vm86 that Seems to Exist

I've looked over the source for LRMI, and here's what I've discovered about the vm86 call, just in case
someone cares.  Feel free to turn this into a webpage or something if you feel the urge.

I should add that LRMI (Linux Real Mode Interface) is the finest piece of code I've ever seen.  I'm used to not
liking other people's code at all, but I like this code better than code that I write myself.  It's easy to
read, there are very few comments but the variable and function names are nearly always descriptive, and
it looks as if the author considered every possible thing that might happen and made the code handle it
appropriately.  So if it provides everything that you need, I'd recommend that you just use the LRMI code
instead of writing your own.  Unless you're some kind of genius, I don't think your code will be any better.
This code is just awesome.

Overview:
=========

When you call vm86 (the number 113 vm86, at least), the real-mode process inherits your processes address
space.  So before calling vm86, you need to map everything you want in the real-mode address space into your
address space, at the offsets where you want it to be at.  You also need to allocate any RAM that you want to
exist in real mode.

Zero the entire vm86_struct, as it seems that fields not mentioned here should be set to zero.

Then you set the int_revectored field of vm86_struct to specify which interrupts you want to receive from
real-mode, and which ones you want Linux to handle automatically for you.  (I assume it just calls the
real-mode interrupt handler.)

Then you load the vm86_regs structure in vm_86_struct with what you want the initial register contents of
the real mode process to be.  Depending on what you are doing, you will probably want to set up a stack at least.

The real-mode process inherits your FS and GS selectors, the fs and gs fields in vm86_regs are (I guess) only
for return values.  You can either leave them as they are, giving the real-mode process access to your
protected mode address space via FS and GS, or you can load them with real-mode selectors before calling vm86.

Then you call vm86.  vm86 runs until either a real-mode interrupt occurs, a not-allowed-in-vm86-mode
opcode is executed, or some other miscellaneous error occurs.  A handy way to allow your real-mode code to
exit the vm86 call is to have it call interrupt 255, which you previously set to be one which you wish to receive.

Upon return from vm86, the vm86_regs structure contains all of the register contents from the real-mode process.

The return value of vm86 contains two fields, a type field and an argument field, the type field being the
lowest 8 bits, and the argument field being the eight bits above that.  If the type field is VM86_INTx, then
the argument field is the number of the interrupt that was called.

Various Details:
================

There are two vm86 system calls.  LRMI makes sure to get the one that is numbered 113.  This is the one declared
as such:
  1.   int vm86(struct vm86_struct *);
复制代码
The other one takes an additional parameter, about which I know nothing.

Here is vm86_struct:
  1.   struct vm86_struct {
  2.           struct vm86_regs regs;
  3.           unsigned long flags;
  4.           unsigned long screen_bitmap;
  5.           unsigned long cpu_type;
  6.           struct revectored_struct int_revectored;
  7.           struct revectored_struct int21_revectored;
  8.   };
复制代码
LRMI begins by simply setting the entire thing to zero.

flags, screen_bitmap, cpu_type, and int21_revectored are never accessed by LRMI, except when it zeros
the entire structure, so I don't know what any of them do.

int_revectored appears to be a bitmask, with one bit for each interrupt.  Setting a bit to 0 tells Linux that
you want it to emulate that interrupt.  Setting a bit to 1 tells Linux that you want to service that
interrupt.  I'm not sure if this is reliable, as there's also code in LRMI that emulates the interrupt if
Linux doesn't, but maybe that's just a failsafe.

vm86_regs looks like this:
  1.   struct vm86_regs {
  2.   /*
  3.    * normal regs, with special meaning for the segment descriptors..
  4.    */
  5.           long ebx;
  6.           long ecx;
  7.           long edx;
  8.           long esi;
  9.           long edi;
  10.           long ebp;
  11.           long eax;
  12.           long __null_ds;
  13.           long __null_es;
  14.           long __null_fs;
  15.           long __null_gs;
  16.           long orig_eax;
  17.           long eip;
  18.           unsigned short cs, __csh;
  19.           long eflags;
  20.           long esp;
  21.           unsigned short ss, __ssh;
  22.   /*
  23.    * these are specific to v86 mode:
  24.    */
  25.           unsigned short es, __esh;
  26.           unsigned short ds, __dsh;
  27.           unsigned short fs, __fsh;
  28.           unsigned short gs, __gsh;
  29.   };
复制代码
All of the register fields are what you would expect.  LRMI never accesses orig_eax, or any of the __null_
segment registers.

Linux ignores the fs and gs fields.  (I think that's worth mentioning twice.)  LRMI loads it's own FS and GS
with the real-mode process' FS and GS before calling vm86, and restores it's old FS and GS after the call
returns.  It does not save the contents of FS and GS upon return from vm86, leading me to believe that Linux
saves them in vm86_regs.

LRMI maps address 0x00000 size 0x400 into it's memory space, so that it has the origional interrupt table
from when the computer booted.  It also maps address 0x00400 size 0x00102, as this is the BIOS data area.  It
also maps address 0xA0000 size 0x60000, to map in any ROMs that may be in that area.

LRMI maps an anonymous block of /dev/zero of size 0x40000 to address 0x10000.  It uses this memory in it's own
internal LRMI_alloc_real function which allocates memory in the real-mode address space.

Although the real-mode process inherits your address space, it does not appear to inherit your I/O
permissions.  Either that or the code to emulate I/O instructions in LRMI is never being used.

These are the possible type codes for the vm86 return value:
  1.   #define VM86_SIGNAL     0       /* return due to signal */
  2.   #define VM86_UNKNOWN    1       /* unhandled GP fault - IO-instruction or similar */
  3.   #define VM86_INTx       2       /* int3/int x instruction (ARG = x) */
  4.   #define VM86_STI        3       /* sti/popf/iret instruction enabled virtual interrupts */
复制代码
If the return code is VM86_UNKNOWN, then LRMI checks to see if it's an opcode that it knows how to emulate.  It
emulates every opcode that operates on an I/O port, and thus I assume that those opcodes must be emulated.
Additionally, it emulates segment overrides, data size overrides, and repeat prefixes, taking into
account the direction flag.  It ignores the address size override, as well as the F0 and F2 prefixes.

If the instruction isn't one which it knows how to emulate, it appears to have a nice piece of code that prints
a register dump of the real mode registers.  It also calls this function if the return value type is anything
other than VM86_INTx or VM86_UNKNOWN.

I believe that it's possible that nothing has to be done for VM86_STI.  Judging from the comment in the
structure above, it sounds like it's mearly a signal to let you know that interrupts were enabled.

Additionally, I believe VM86_SIGNAL is just like the EINTR return value of many other functions, that is,
it simply indicates that your process recieved a signal, but that the vm86 process is otherwise just fine.
This might be useful if you wish to only allow the vm86 process to run for a limited time.  You could use the
alarm or itimer system calls to schedual an alarm signal, and when that signal occurs, the vm86 call will exit.

That's it, hopefully I didn't forget anything.


回复

使用道具 举报

5#
 楼主| 发表于 2015-7-24 20:07:20 | 只看该作者
已经知道导致 kexec 加载 grub.exe 后死机的原因之一是 Linux 的中断控制系统造成的。这也可能是唯一的原因。machine_shutdown 函数没能把中断控制系统完全恢复到 BIOS 的原始状态,所以造成死机。这应该算是 Linux 内核的 bug。不过,要排除这个 bug,则需要对中断控制的知识很熟悉才行。x86 体系的中断控制系统非常复杂,有原始的 8259 中断控制器,也有 EISA 系统的 ELCR(边沿电平触发控制寄存器),还有 PCI IRQ 路由和 ACPI 的中断控制方式,最后还有 APIC(包括 Local APIC 和 IO APIC)的中断控制方式。只要其中有一个没能恢复到 BIOS 原始状态,就会造成死机。而且某些新型的机器还去除了原始的 8259 中断控制器,所以,对它进行初始化是没用的。

回复

使用道具 举报

6#
 楼主| 发表于 2015-7-25 19:12:19 | 只看该作者
System BIOS Programming Guidelines Version 1.4

Example A-1. Programming Local APIC for Virtual Wire Mode

  1. ;---------------------------------------------------------------;
  2. ; InitLocalAPIC( ) ;
  3. ;---------------------------------------------------------------;
  4. ;
  5. ; Initialize the local APIC to virtual wire mode.
  6. ;
  7. ;---------------------------------------------------------------;
  8. SVR                equ        0FEE000F0H
  9. LVT1                equ        0FEE00350H
  10. LVT2                equ        0FEE00360H
  11. APIC_ENABLED        equ        000000100H

  12.         public InitLocalAPIC
  13. InitLocalAPIC proc near
  14.         push        ds         ; save regs used for APIC init
  15.         push        es
  16.         push        esi
  17.         mov        al,080h ; ensure NMI disabled
  18.         out        070h,al
  19.         in        al,021h ; read primary imr
  20.         push        ax         ; save settings
  21.         mov        al,0ffh ; mask all off
  22.         out        021h,al
  23.         in        al,0a1h ; read secondary imr
  24.         push        ax         ; save settings
  25.         mov        al,0ffh ; mask all off
  26.         out        0a1h,al
  27.         extrn        pmode_on : near
  28.         call        pmode_on ; switch into real big mode
  29. ;
  30. ; The APIC spurious interrupt must point to a vector whose lower
  31. ; nibble is 0F, that is 0xF, where x is 0 - F. Here we use Int 00FH,
  32. ; which handles spurious interrupts and supplies the necessary IRET.
  33. ; This vector is assumed to have already been initialized in memory.
  34. ;
  35. ; Enable the APIC via SVR and set the spurious interrupt to use Int 00F
  36. ;
  37.         mov        esi,SVR
  38.         mov        eax,[esi] ; read SVR
  39.         and        eax,0FFFFFF0FH ; clear spurious vector (use vector 00FH)
  40.         or        eax,APIC_ENABLED ; bit 8 = 1
  41.         mov        [esi],eax ; write SVR
  42. ;
  43. ; Program LVT1 as ExtInt, which delivers the signal to the INTR signal of all
  44. ; processors' cores listed in the destination as an interrupt that originated
  45. ; in an externally-connected interrupt controller.
  46. ;

  47.         mov        esi,LVT1
  48.         mov        eax,[esi]         ; read LVT1
  49.         and        eax,0FFFE00FFH ; not masked, edge, active high
  50.         or        eax,000005700H ; ExtInt
  51.         mov        [esi],eax         ; write LVT1
  52. ;
  53. ; Program LVT2 as NMI, which delivers the signal on the NMI signal of all
  54. ; processors' cores listed in the destination.
  55. ;
  56.         mov        esi,LVT2
  57.         mov        eax,[esi]         ; read LVT2
  58.         and        eax,0FFFE00FFH ; not masked, edge, active high
  59.         or        eax,000005400H ; NMI
  60.         mov        [esi],eax         ; write LVT2
  61.         extrn        pmode_off : near
  62.         call        pmode_off         ; switch back to real mode
  63.         pop ax                         ; restore imr settings
  64.         out 0a1h,al                 ; restore secondary imr
  65.         pop ax
  66.         out 021h,al                 ; restore primary imr
  67.                                  ; this routine leaves NMI disabled
  68.         pop esi                 ; restore regs used in APIC init
  69.         pop es                         ; (unless also saved for CPUID)
  70.         pop ds
  71.         ret
  72. InitLocalAPIC endp
复制代码




回复

使用道具 举报

7#
 楼主| 发表于 2015-7-27 17:33:36 | 只看该作者
本帖最后由 不点 于 2015-7-27 18:01 编辑

找到一个貌似很强大的工具集 :sg3_utils

http://www.linuxfromscratch.org/blfs/view/svn/general/sg3_utils.html

我是在搜 “scsi reset disk” 时找到这个工具集的,它竟然有如此多的磁盘控制工具:

scsi_logging_levelaccess Linux SCSI logging level information.
scsi_mandatcheck SCSI device support for mandatory commands.
scsi_readcapdo SCSI READ CAPACITY command on disks.
scsi_readydo SCSI TEST UNIT READY on devices.
scsi_satlcheck SCSI to ATA Translation (SAT) device support.
scsi_startstart one or more SCSI disks.
scsi_stopstop one or more SCSI disks.
scsi_temperaturefetch the temperature of a SCSI device.
sg_compare_and_writesends the SCSI COMPARE AND WRITE command to device.
sg_copy_resultssends the SCSI RECEIVE COPY RESULTS command (XCOPY related).
sg_ddcopies data to and from files and devices. Specialised for devices that understand the SCSI command set.
sg_decode_sensetakes SCSI sense data in binary or as a sequence of ASCII hexadecimal bytes and decodes it.
sg_emc_trespasschanges ownership of a LUN from another Service-Processor to this one.
sg_formatformat or resize a SCSI disk (perhaps change its block size).
sg_get_configsends a SCSI GET CONFIGURATION command (MMC-4 +).
sg_get_lba_statussends the SCSI GET LBA STATUS command.
sg_identsends a SCSI REPORT or SET IDENTIFYING INFORMATION command.
sginfoaccess mode page information for a SCSI (or ATAPI) device.
sg_inqsends a SCSI INQUIRY or ATA IDENTIFY (PACKET) DEVICE command and outputs the response.
sg_logsaccess log pages with SCSI LOG SENSE command.
sg_lunssends the SCSI REPORT LUNS command.
sg_mapdisplays mapping between linux sg and other SCSI devices.
sg_map26maps a special file to a SCSI generic (sg) device (or vice versa).
sgm_ddcopies data to and from files and devices. Specialized for devices that understand the SCSI command set and does memory mapped transfers from sg devices.
sg_modesreads mode pages with SCSI MODE SENSE command.
sg_opcodesreports information on supported SCSI commands or task management functions.
sgp_ddcopies data to and from files and devices. Specialized for devices that understand the SCSI command set.
sg_persistsends a SCSI PERSISTENT RESERVE (IN or OUT) command to manipulate registrations and reservations.
sg_preventsends a SCSI PREVENT ALLOW MEDIUM REMOVAL command.
sg_rawsends an arbitrary SCSI command to a device.
sg_rbufreads data using SCSI READ BUFFER command.
sg_rdacDisplay or Modify RDAC Redundant Controller Page.
sg_readread blocks of data continually from same offset.
sg_read_block_limitssends a SCSI READ BLOCK LIMITS command.
sg_read_buffersends a SCSI READ BUFFER command.
sg_readcapsends a SCSI READ CAPACITY command.
sg_read_longsends a SCSI READ LONG command.
sg_reassignsends a SCSI REASSIGN BLOCKS command.
sg_referralssends the SCSI REPORT REFERRALS command.
sg_rep_zonessends SCSI REPORT ZONES command.
sg_requestssends one or more SCSI REQUEST SENSE commands.
sg_resetsends SCSI device, target, bus or host reset; or checks reset state.
sg_reset_wpsends SCSI RESET WRITE POINTER command.
sg_rmsnsends a SCSI READ MEDIA SERIAL NUMBER command.
sg_rtpgsends a SCSI REPORT TARGET PORT GROUPS command.
sg_saftefetch status from a SCSI Accessed Fault-Tolerant Enclosure (SAF-TE) device.
sg_sanitizesends a SCSI SANITIZE command.
sg_sat_identifysends a ATA IDENTIFY (PACKET) DEVICE command via a SCSI to ATA Translation (SAT) layer.
sg_sat_phy_eventsends an ATA READ LOG EXT command via a SAT pass through to fetch log page 11h which contains SATA phy event counters.
sg_sat_read_gploguses ATA READ LOG EXT command via a SCSI to ATA Translation (SAT) layer.
sg_sat_set_featuressends a ATA SET FEATURES command via a SCSI to ATA Translation (SAT) layer.
sg_scandoes a scan of sg devices (or given SCSI/ATAPI/ATA devices) and prints the results.
sg_senddiagperforms a SCSI SEND DIAGNOSTIC command.
sg_sessends controls and fetch status from a SCSI Enclosure Services (SES) device.
sg_ses_microcodesends microcode to a SCSI enclosure.
sg_startsends SCSI START STOP UNIT command to start, stop, load or eject medium.
sg_stpgsends a SCSI SET TARGET PORT GROUPS command.
sg_syncsends the scsi command synchronize cache.
sg_test_rwbuftests the SCSI host adapter by issuing write and read operations on a device's buffer and calculating checksums.
sg_turssends one or more SCSI TEST UNIT READY commands.
sg_unmapsends a SCSI UNMAP command.
sg_verifyinvoke SCSI VERIFY command(s) on a block device.
sg_vpdfetches Vital Product Data (VPD) pages using a SCSI INQUIRY command.
sg_write_buffersends a SCSI WRITE BUFFER command.
sg_write_longsends the SCSI WRITE LONG command.
sg_write_samesends the SCSI WRITE SAME command.
sg_write_verifysends the SCSI WRITE AND VERIFY command.
sg_wr_modewrites mode page.
sg_xcopycopies data to and from files and devices using SCSI EXTENDED COPY (XCOPY).

回复

使用道具 举报

8#
 楼主| 发表于 2015-7-28 17:00:24 | 只看该作者
本帖最后由 不点 于 2015-7-28 18:33 编辑

非常好的 DMA Programming Tutorial,它从最基本的概念入手,让一个外行可以很快入门。这样的资料是很难得的。

http://bos.asmhackers.net/docs/dma/docs/dmaprogramming.pdf




kexec 加载 grub.exe 后,在访问硬盘时,总是出现

Fatal! inconsistent data read from (0x80)0+63

只要访问硬盘,都会出现这个错误。

这是一个很有价值的错误信息。这条信息的含义是,BIOS int13 调用返回的是 CF=0 (成功),但是,得到的扇区数据不正确。

这让我想到,可能是 DMA 出了问题。猜测 Linux 的硬盘驱动程序(以某种方式)修改了 DMA 参数,让扇区数据写往扩展内存中的某个地址。

因此,(在回到实模式以后)DMA 控制器仍然写往扩展内存中的那个位置,写入成功后就返回 CF=0 给调用者。

然而调用者却在常规内存中找不到扇区数据。这就出现了上述错误信息。

解决的思路是设法清除掉 DMA 地址寄存器的高位。然而在所有的文档中都没有发现 DMA 的 32 位地址的控制端口。

甚至在 Linux 内核源程序里面也没有发现有关 DMA 32 位地址寄存器的信息。

回复

使用道具 举报

9#
 楼主| 发表于 2015-7-29 08:39:33 | 只看该作者
本帖最后由 不点 于 2015-7-29 18:11 编辑

做了试验,清除页面寄存器(port 0x80~0x8F),无效。看来得找到控制 32 位地址的寄存器才行。

真不容易——这个文档涉及到了 32 位的 DMA 地址寄存器:
http://pdf.datasheetcatalog.com/datasheet/Intel/mXryzyt.pdf

按照这个文档,清除全部 32 位寄存器,也无效。看来用 “猜测原因” 的办法是失败的。只能通过调试 Linux 内核的源代码来查找原因了。

回复

使用道具 举报

10#
 楼主| 发表于 2015-7-29 09:42:36 | 只看该作者
本帖最后由 不点 于 2015-7-29 09:46 编辑

Common I/O base address device assignments in IBM PC compatible computers
I/O address
Device
00 – 1FFirst DMA controller 8237 A-5
20 – 3FFirst Programmable Interrupt Controller, 8259A, Master
40 – 5FProgrammable Interval Timer (System Timer), 8254
60 – 6FKeyboard, 8042
70 – 7FReal Time Clock, NMI mask
80 – 9FDMA Page Register, 74LS612
87DMA Channel 0
83DMA Channel 1
81DMA Channel 2
82DMA Channel 3
8BDMA Channel 5
89DMA Channel 6
8ADMA Channel 7
8FRefresh
A0 – BFSecond Programmable Interrupt Controller, 8259A, Slave
C0 – DFSecond DMA controller 8237 A-5
F0Clear 80287 Busy
F1Reset 80287
F8 – FFMath coprocessor, 80287
F0 – F5PCjr Disk Controller
F8 – FFReserved for future microprocessor extensions
100 – 10FPOS Programmable Option Select (PS/2)
110 – 1EFSystem I/O channel
140 – 15FSecondary SCSI host adapter
170 – 177Secondary Parallel ATA Disk Controller
1F0 – 1F7Primary Parallel ATA Hard Disk Controller
200 – 20FGame port
210 – 217Expansion Unit
220 – 233Sound Blaster and most other sound cards
278 – 27FParallel port 3
280 – 29FLCD on Wyse 2108 PC SMC Elite default factory setting
2B0 – 2DFAlternate Enhanced Graphics Adapter (EGA) display control
2E8 – 2EFSerial port 4
2E1GPIB/IEEE-488 Adapter 0
2E2 – 2E3Data acquisition
2F8 – 2FFSerial port 2
300 – 31FPrototype Card
300 – 31FNovell NE1000 compatible Ethernet network interfaces
300 – 31FAMD Am7990 Ethernet network interface, IRQ=5.
320 – 323ST-506 and compatible hard disk drive interface
330 – 331MPU-401 MIDI Processing Unit on most sound cards
340 – 35FPrimary SCSI host adapter
370 – 377Secondary floppy disk drive controller
378 – 37FParallel port 2
380 – 38CSecondary Binary Synchronous Data Link Control (SDLC) adapter
388 – 389AdLib Music Synthesizer Card
3A0 – 3A9Primary Binary Synchronous Data Link Control (SDLC) adapter
3B0 – 3BBMonochrome Display Adapter (MDA) display control
3BC – 3BFParallel port 1 on MDA card
3C0 – 3CFEnhanced Graphics Adapter (EGA) display control
3D0 – 3DFColor Graphics Adapter (CGA)
3E8 – 3EFSerial port 3
3F0 – 3F7Primary floppy disk drive controller. Primary IDE controller (slave drive) (3F6–3F7h)
3F8 – 3FFSerial port 1
CF8 – CFCPCI configuration space
Note: For many devices listed above the assignments can be changed via jumpers, DIP switches, or Plug-And-Play software.

https://en.wikipedia.org/wiki/Input/Output_Base_Address

回复

使用道具 举报

11#
 楼主| 发表于 2015-8-2 00:55:24 | 只看该作者
在 GRUB2 下用 insmod ahci 之后,有了 ahci0 这个设备,它就是本地硬盘。

但遗憾的是,此时 BIOS 环境已经丧失(hd0 不存在了)。即使 rmmod ahci (卸载 ahci)也无法访问 (hd0) 了。

看来 PCI 系统下的硬盘驱动都是一样的:破坏掉 BIOS 的硬盘接口信息(导致 int13 失效)。

这也说明了,即使把 grub2 的硬盘驱动引入到 grub4dos 中,也解决不了 bios 硬盘访问失效的问题。

回复

使用道具 举报

12#
 楼主| 发表于 2015-8-2 16:38:28 | 只看该作者
在 Linux 内核中调试,也得到了相同的结论:正是“AHCI SATA Support” 造成了与 BIOS 硬盘访问的冲突。

这容易让我想到,AHCI 也曾给 WinXP 带来了很多麻烦。这是微软不断更改硬件接口规范所带来的必然后果,目的没有别的,仅仅就是为了制造不兼容性而已。

好了,到此为止,这个问题也该收场了,再研究下去似乎也没有意义了。整个 x86 的生存状况都不怎么样,算了,我还是明智点,继续按部就班地逐渐远离 x86 吧。

回复

使用道具 举报

13#
 楼主| 发表于 2015-8-9 17:10:06 | 只看该作者
用“顺藤摸瓜”的方法,把 GRUB2 里面的硬盘驱动弄出来了。但是究竟如何把它弄到 grub4dos 里面,则是另外一个问题。

记录一下我是怎么做的,以便将来能够回忆起来;另外这个做法说不定也可以供别人参考。

我先找到 ahci.c,把它拷贝到我自己的某个工作目录之下。用 gcc ahci.c 进行编译。它报错,说缺少某某 .h 文件,那么就把 grub2 里面相应的 .h 复制过来。它会继续报错,说缺少某个函数。于是我就从 grub2 里面找到相应的 .c 文件,把它复制过来。用 gcc -I. *.c 进行编译,直到不再报错为止。注意,它总要报错说:“缺少 main() 函数”——这个就不算是错误了,因为自己编写一个空的 main()函数即可。

好了,这是初步的结果,总算把它弄出来了。我不打算把它改成 asm 语言格式。目前它很庞大,是的。但是假如隐藏在扩展内存中,则它所占用的空间大小并不重要。

回复

使用道具 举报

14#
发表于 2015-8-26 22:48:00 来自手机 | 只看该作者
虽然看不懂,但还是要支持支持
回复

使用道具 举报

15#
发表于 2020-3-4 14:53:14 | 只看该作者
为什么好像就1个人在说话?
回复

使用道具 举报

16#
发表于 2020-3-4 22:18:31 | 只看该作者
楼主的研究精神值得钦佩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|Archiver|捐助支持|无忧启动 ( 闽ICP备05002490号-1 )

闽公网安备 35020302032614号

GMT+8, 2024-11-11 06:22

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表