无忧启动论坛

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

kolibri 汇编代码学习研究

[复制链接]
跳转到指定楼层
1#
发表于 2014-11-14 11:54:47 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 不点 于 2014-11-30 15:02 编辑



最近看了 kolibri 的代码,觉得是个很不错的操作系统学习资料。我准备贴出自己的学习心得,和开发者朋友们分享。如果我理解有错误,希望开发者们能帮助纠正。

请不要发表与技术无关的言论,冲淡这个话题。请版主们给以支持,删除那些与技术无关的帖子。




修改记录:

2014-11-30 添加了虚拟地址映射,将物理地址 0 至 4M 的空间映射为虚拟地址 0xC0000000 处的 4M 空间,方便读写实模式内存。
2014-11-29 添加了实模式处理代码,可以正常重启,重启时不再死机了。
2014-11-28 屏蔽掉 intel 深度休眠代码,避免进入实模式引起启动失败。
2014-11-27 成功进入桌面,只是在点击重启时会死机。原因是重启时要使用实模式,而我们的实模式代码位于扩展内存,没有拷贝到常规内存,所以就死机了。其实关机也有可能死机,这种情况发生在那些必须使用 BIOS 来关机的电脑上。这是小问题,目前我们没必要处理这些问题,因为将来会统一处理实模式与保护模式切换的问题。



kolibri-r5187-patch.rar

2.33 KB, 下载次数: 8, 下载积分: 无忧币 -2

这是 2014-11-23 制作的补丁,仍有问题

kolibri-r5190-patch.diff.7z

2.79 KB, 下载次数: 2, 下载积分: 无忧币 -2

这是 2014-11-27 制作的补丁,成功进入桌面,只是在重启时死掉

kolibri-r5194-patch.diff.7z

2.96 KB, 下载次数: 1, 下载积分: 无忧币 -2

这是 2014-11-28 制作的补丁,暂时屏蔽了 intel 深度休眠代码,避免进入实模式造成启动失败。

kolibri-r5194-patch.diff.7z

3.19 KB, 下载次数: 0, 下载积分: 无忧币 -2

这是 2014-11-29 制作的补丁,添加了实模式处理代码,可以正常重启,重启时不再死机了。

kolibri-r5194-patch.diff.7z

3.37 KB, 下载次数: 7, 下载积分: 无忧币 -2

2014-11-30 更新:添加了虚拟地址映射,将物理地址 0 至 4M 的空间映射为虚拟地址 0xC0000000 处的 4M 空间 ...

2#
 楼主| 发表于 2014-11-14 12:11:05 | 只看该作者
本帖最后由 不点 于 2014-11-15 08:15 编辑

kolibri 的 32 位启动代码是这样的:

  1. ; CR0 Flags - Protected mode and Paging

  2.         mov     ecx, CR0_PE

  3. ; Enabling 32 bit protected mode

  4.         sidt    [cs:old_ints_h]

  5.         cli                             ; disable all irqs
  6.         cld
  7.         mov     al, 255                 ; mask all irqs
  8.         out     0xa1, al
  9.         out     0x21, al
复制代码


首先是用 sidt 指令保存实模式的中断描述符表寄存器到内存地址 cs:old_ints_h。将来要进入实模式时,还要用到这个寄存器值。从它的 bootloader 代码可以分析得到,kolibri 的内核被加载在 1000:0000 处。所以,刚才提到的内存地址大致也在这个位置以上的常规内存空间中。

紧接着是屏蔽硬件中断。

接下来是 enable A20,就不贴了。

之后,是下面这段,装入 GDT,进入 32 位保护模式:

  1.         lgdt    [cs:tmp_gdt]            ; Load GDT
  2.         mov     eax, cr0                ; protected mode
  3.         or      eax, ecx
  4.         and     eax, 10011111b *65536*256 + 0xffffff ; caching enabled
  5.         mov     cr0, eax
  6.         jmp     pword os_code:B32       ; jmp to enable 32 bit mode

  7. align 8
  8. tmp_gdt:

  9.         dw     23
  10.         dd     tmp_gdt+0x10000
  11.         dw     0

  12.         dw     0xffff
  13.         dw     0x0000
  14.         db     0x00
  15.         dw     11011111b *256 +10011010b
  16.         db     0x00

  17.         dw     0xffff
  18.         dw     0x0000
  19.         db     0x00
  20.         dw     11011111b *256 +10010010b
  21.         db     0x00
复制代码


它的 GDT 表没有注释,所以,需要弄清楚它的含义。

两个 GDT 表项的段限都是 limit=0xFFFFF 即,1M,粒度都是 4K,说明实际上是 4G 的段限。基地址都是 base=0。

第一个是代码段,第二个是数据段。

补充: GDT 表 align 8 是不行的,linux 内核早有注释,说应该使用 align 16.

回复

使用道具 举报

3#
 楼主| 发表于 2014-11-14 17:10:14 | 只看该作者
这一段,设置保护模式堆栈。

  1. B32:
  2.         mov     ax, os_stack       ; Selector for os
  3.         mov     ds, ax
  4.         mov     es, ax
  5.         mov     fs, ax
  6.         mov     gs, ax
  7.         mov     ss, ax
  8.         mov     esp, 0x006CC00       ; Set stack
复制代码


堆栈位于常规内存空间范围内。
回复

使用道具 举报

4#
 楼主| 发表于 2014-11-14 17:19:20 | 只看该作者
这一段,清除内存软盘结尾处 0x280000 至堆内存基地址(位于物理地址 8M 处)之间的内存。
  1. ; CLEAR 0x280000 - HEAP_BASE

  2.         xor     eax, eax
  3.         mov     edi, CLEAN_ZONE
  4.         mov     ecx, (HEAP_BASE-OS_BASE-CLEAN_ZONE) / 4
  5.         cld
  6.         rep stosd
复制代码
回复

使用道具 举报

5#
 楼主| 发表于 2014-11-14 17:29:38 | 只看该作者
本帖最后由 不点 于 2014-11-14 17:48 编辑

这一段,清除内核未初始化的全局变量。

  1. ; CLEAR KERNEL UNDEFINED GLOBALS
  2.         mov     edi, endofcode-OS_BASE
  3.         mov     ecx, 0x90000
  4.         sub     ecx, edi
  5.         shr     ecx, 2
  6.         rep stosd
复制代码


edi 指向 code 结尾处,从此处到 9000:0000 的内存被清除。前面已经看到,堆栈也在这个范围之内。这应该是没问题的,因为此时堆栈还没有开始使用,比如,没有使用 push,pop,call,ret,int,iret 等指令。

补充:这个清理并不十分安全。清理到 8000:0000 是安全的。有些 PXE bios 使用了 8000:0000 以上的空间,如果这个空间被清除,那以后如果使用 BIOS 就会死机了。最安全的做法是使用 BIOS 数据区 0x413 处的值来决定应该清理的内存量。
回复

使用道具 举报

6#
 楼主| 发表于 2014-11-14 17:42:12 | 只看该作者
这一段,是清除最开头的 64K 物理内存。当然,最开头的 4K 页面是不可以清除的,因为 BIOS 要用它。位于 0000:9000 的 4K 也保留,未被清除,可能内核要用它。

  1. ; SAVE & CLEAR 0-0xffff

  2.         mov     edi, 0x1000
  3.         mov     ecx, 0x8000 / 4
  4.         rep stosd
  5.         mov     edi, 0xa000
  6.         mov     ecx, 0x6000 / 4
  7.         rep stosd
复制代码


内核就位于 1000:0000 处,也就是位于第一个 64K 空间的结尾处。

回复

使用道具 举报

7#
 楼主| 发表于 2014-11-15 07:56:29 | 只看该作者
本帖最后由 不点 于 2014-11-15 08:09 编辑

接下来这几个函数都在 init.inc 文件中。

  1. call    test_cpu
  2.         bts     [cpu_caps-OS_BASE], CAPS_TSC    ;force use rdtsc

  3.         call    check_acpi
  4.         call    init_BIOS32
  5. ; MEMORY MODEL
  6.         call    mem_test
  7.         call    init_mem
  8.         call    init_page_map
复制代码


本楼留着以后讨论其中某些部分。
回复

使用道具 举报

8#
 楼主| 发表于 2014-11-15 08:22:54 | 只看该作者
本帖最后由 不点 于 2014-11-25 10:12 编辑

memmap.inc 是一个说明文件,列出了内存使用情况。其中实模式启动部分所使用的内存如下:
  1. ;   Boot:
  2. ;
  3. ;   0:9000     byte   bits per pixel
  4. ;   0:9001     word   scanline length
  5. ;   0:9008     word   vesa video mode
  6. ;   0:900A     word   X res
  7. ;   0:900C     word   Y res
  8. ;   0:9010     byte   mouse port  - not used
  9. ;   0:9014     dword  Vesa 1.2 pm bank switch
  10. ;   0:9018     dword  Vesa 2.0 LFB address
  11. ;   0:901C     byte   0 or 1 : enable MTRR graphics acceleration
  12. ;   0:901D     byte   not used anymore (0 or 1 : enable system log display)
  13. ;   0:901E     byte   0 or 1 : enable direct lfb write, paging disabled
  14. ;   0:901F     byte   DMA write : 1=yes, 2=no
  15. ;   0:9020     8bytes pci data
  16. ;   0:9030     byte   VRR start enabled 1, 2-no
  17. ;   0:9031     word   IDEContrRegsBaseAddr
  18. ;    0x9040 - dword - entry point of APM BIOS
  19. ;    0x9044 -  word - version (BCD)
  20. ;    0x9046 -  word - flags
  21. ;   0:907F     byte   number of BIOS hard disks
  22. ;   0:9080     Nbytes BIOS hard disks
  23. ;   0:9100     word   available physical memory map: number of blocks
  24. ;   0:9104            available physical memory map: blocks
  25. ;
复制代码


保护模式使用的内存如下:

  1. ; 0x00000000 -> 0x7FFFFFFF  application 2Gb

  2. ; 0x80000000 -> 0FFF  physical page zero - do not write
  3. ;                     (used by int 13h in some configurations)
  4. ;
  5. ; 0x80001000 -> 2FFF   window_data   - 256 entries
  6. ;
  7. ;         0000 dword  x start
  8. ;         0004 dword  y start
  9. ;         0008 dword  x size
  10. ;         000C dword  y size
  11. ;         0010 dword  color of work area
  12. ;         0014 dword  color of grab bar
  13. ;         0018 dword  color of frames
  14. ;         001C dword  window flags, +30 = window drawn, +31 redraw flag
  15. ;
  16. ;   3000  ->   4FFF   task list      - 256 entries
  17. ;
  18. ;         00   dword  process count
  19. ;         04   dword  no of processes
  20. ;         10   dword  base of running process at 0x3000+
  21. ;
  22. ;         20   dword  application event mask
  23. ;         24   dword  PID - process identification number
  24. ;         2a   byte   slot state: 0=running, 1,2=suspended
  25. ;                        3=zombie, 4=terminate,
  26. ;                        5=waiting for event, 9 = not used
  27. ;         2e   byte   window number on screen
  28. ;         30   dword  exact position in memory
  29. ;         34   dword  counter sum
  30. ;         38   dword  time stamp counter add
  31. ;         3c   dword  cpu usage in cpu timer tics
  32. ;
  33. ;
  34. ;   5000  ->   68FF   free (6k6)
  35. ;   6900  ->   6EFF   saved picture under mouse pointer (1k5)
  36. ;
  37. ;   6F00  ->   6FFF   free (256)
  38. ;
  39. ;   7000  ->   7FFF   used CD driver
  40. ;
  41. ;   8000  ->   A3FF   used FLOPPY driver
  42. ;
  43. ;   A400  ->   B0FF   free (3k3), unused ACTIVE_PROC_STACK

  44. ;   B100  ->   B307   IDT for int_0x00..int_0x40

  45. ;   B308  ->   BFFF   free (3k3)

  46. ;   C000  ->   C3FF   window stack C000 no of windows - all in words
  47. ;   C402  ->   C7FF   window position in stack
  48. ;   D000  ->   D1FF   FDC controller
  49. ;   D200  ->   D3FF   FDC controller for Fat12
  50. ;   D400  ->   DFFF   free (3k)
  51. ;   E000  byte        multitasking started
  52. ;   E020  dword       putpixel address
  53. ;   E024  dword       getpixel address
  54. ;   E030  dword       Vesa 1.2 pm bank switch address
  55. ;   E034  ->   F1FF   free (4k5)
  56. ;   F200  dword       mousepicture -pointer
  57. ;   F204  dword       mouse appearance counter
  58. ;   F208  ->   F2FF   free (248)
  59. ;   F300  dword       x & y temp for windowmove
  60. ;   F304  ->   F3FF   free (252)
  61. ;   F400  byte        no of keys in buffer
  62. ;   F401  byte        'buffer'
  63. ;   F402  ->   F4FF   reserved for keys
  64. ;   F500  byte        no of buttons in buffer
  65. ;   F501  dword       'buffer'
  66. ;   F502  ->   F5FF   reserved for buttons
  67. ;   F600  dword       tsc / second
  68. ;   F604  byte        (unused?) mouse port: 1 ps2, 2 com1, 3 com2
  69. ;   F605  ->   FAFF   free (1k2)
  70. ;   FB00  ->   FB0F   mouse memory 00 chunk count, that includes:
  71. ;   FB08 word       -- mouse H-scroll
  72. ;   FB0A word       -- mouse x
  73. ;   FB0C word         -- mouse y
  74. ;   FB0E word       -- mouse V-scroll
  75. ;   FB10  ->   FB17   mouse color mem
  76. ;   FB21              x move
  77. ;   FB22              y move
  78. ;   FB28              high bits temp
  79. ;   FB30              color temp
  80. ;   FB40  byte        buttons down
  81. ;   FB44  byte        0 mouse down -> do not draw
  82. ;   FB4A  ->   FB4D   FB4A-B x-under - FB4C-D y-under
  83. ;   FBF1  byte        bits per pixel
  84. ;   FC00  ->   FCFE   com1/ps2 buffer
  85. ;   FCFF              com1/ps2 buffer count starting from FC00
  86. ;   FD00  ->   FDFF   free (256)
  87. ;   FE00  dword       screen x size            
  88. ;   FE04  dword       screen y size            
  89. ;   FE08  dword       screen y multiplier      
  90. ;   FE0C  dword       screen mode      
  91. ;   FE10  ->   FE7F   free (112)
  92. ;   FE80  dword       address of LFB in physical
  93. ;   FE84  dword       address of applications memory start in physical  ?
  94. ;   FE88  dword       address of button list
  95. ;   FE8C  dword       memory to use
  96. ;   FE90  ->   FEFF   free (112)
  97. ;   FF00  byte        1 = system shutdown request
  98. ;   FF01  byte        task activation request?
  99. ;   FFF0  byte        >0 if redraw background request from app
  100. ;   FFF1  byte        free
  101. ;   FFF2              write and read bank in screen
  102. ;   FFF4  byte        0 if first mouse draw & do not return picture under
  103. ;   FFF5  byte        1 do not draw pointer
  104. ;   FFFF  byte        do not change task for 1/100 sec.
  105. ;
  106. ; 0x80010000 ->  6CBFF   kernel, 32-bit run-time code (up to 371 Kb)

  107. ; 0x8006CC00 ->  6DBFF   stack at boot time (4Kb)
  108. ;
  109. ; 0x8006DC00 ->  6E5FF   free (2560)
  110. ; 0x8006E600 ->  6Efff   free (2560)
  111. ; 0x8006F000 ->  6FFFF   main page directory

  112. ; 0x80070000 ->  7FFFF   data of retrieved disks and partitions (Mario79)
  113. ; 0x80080000 ->  8FFFF   additional app info, in 256 byte steps - 256 entries
  114. ;
  115. ;         00  11db  name of app running
  116. ;       0x10 dword  pointer to  fpu save area
  117. ;       0x14 dword  event count
  118. ;       0x18 dword  user fpu exceptoins handler
  119. ;       0x1c dword  user sse exceptions handler
  120. ;         20 dword  PL0 stack base
  121. ;         24 dword  user heap base
  122. ;         28 dword  user heap top
  123. ;         2c dword  window cursor handle
  124. ;         30 dword  first event in list
  125. ;         34 dword  last event in list
  126. ;         38 dword  first kernel object in list
  127. ;         3c dword  last kernel object in list
  128. ;         40 dword  thread esp
  129. ;         44 dword  io permission map page 0
  130. ;         48 dword  io permission map page 1
  131. ;         4c dword  debug state: 1= load debug registers
  132. ;         50 dword  current directory ptr
  133. ;         54 dword  wait timeout
  134. ;         58 dword  thread TSS._esp0 (= pl0 stack base + size except for V86)
  135. ;         5C-7F     unused
  136. ;
  137. ;         80 dword  address of random shaped window area
  138. ;         84 byte   shape area scale
  139. ;         88 dword  free
  140. ;         8C dword  application memory size
  141. ;         90 dword  window X position save
  142. ;         94 dword  window Y position save
  143. ;         98 dword  window X size save
  144. ;         9C dword  window Y size save
  145. ;         A0 dword  IPC memory start
  146. ;         A4 dword  IPC memory size
  147. ;         A8 dword  event bits: mouse, stack,..
  148. ;         AC dword  0 or debugger slot
  149. ;         B0 dword  free
  150. ;         B4  byte  keyboard mode: 0 = keymap, 1 = scancodes
  151. ;         B8 dword  physical address of directory table
  152. ;         BC dword  address of debug event memory
  153. ;         C0  5 dd  thread debug registers: DR0,DR1,DR2,DR3,DR7
  154. ;
  155. ; 0x80090000 ->  9FFFF   tmp (64k) - unused?
  156. ; 0x800A0000 ->  AFFFF   screen access area
  157. ; 0x800B0000 ->  FFFFF   bios rest in peace -area (320k)        ?
  158. ; 0x80100000 -> 27FFFF   diskette image (1m5)
  159. ; 0x80280000 -> 283FFF   free (16k)
  160. ;
  161. ; 0x80284000 -> 28BFFF   HDD DMA AREA   (32k)
  162. ; 0x8028C000 -> 297FFF   free (48k)
  163. ;
  164. ; 0x80298000 -> 29FFFF   auxiliary table for background smoothing code (32k)
  165. ;
  166. ; 0x802A0000 -> 2B00FF   wav device buffer (64k)
  167. ; 0x802A0000 -> 2B00FF   wav device status (256)
  168. ;
  169. ; 0x802B0100 -> 2B3FFD   free (15k7)
  170. ;
  171. ; 0x802B3FEE -> 2B3FEF   button info (64K+ 16 + 2 byte)
  172. ;     2B3FEE   0000 word    number of buttons
  173. ;     2B3FF0    first button entry
  174. ;      
  175. ;        button entry at 0x10
  176. ;       +0000 word   process number
  177. ;       +0002 word   button id number : bits 00-15
  178. ;       +0004 word   x start
  179. ;       +0006 word   x size
  180. ;       +0008 word   y start
  181. ;       +000A word   y size
  182. ;       +000C word   button id number : bits 16-31
  183. ;
  184. ; 0x802C4000 -> 2C9FFF   area for fast getting offset to LFB (24k)
  185. ;                        BPSLine_calc_area
  186. ; 0x802CA000 -> 2CFFFF   area for fast getting offset to _WinMapAddress (24k)
  187. ;                        d_width_calc_area
  188. ;
  189. ; 0x802D0000 -> 2DFFFF   reserved port area (64k)
  190. ;
  191. ;       0000 dword   no of port areas reserved
  192. ;       0010 dword   process id
  193. ;            dword   start port
  194. ;            dword   end port
  195. ;            dword   0
  196. ;
  197. ; 0x802E0000 -> 2EFFFF   irq data area  (64k)  ;BOOT_VAR
  198. ;
  199. ; 0x802F0000 -> 2F3FFF   tcp memory  stack_data_start eth_data_start (16k)
  200. ;
  201. ; 0x802F4000 -> 30ffff   stack_data | stack_data_end (112k)
  202. ;
  203. ; 0x80310000 -> 317fff   resendQ (32k)
  204. ;
  205. ; 0x80318000 -> 31ffff   skin_data (32k)
  206. ;
  207. ; 0x80320000 -> 323FF3   draw data     - 256 entries (4k)
  208. ;         00   dword  draw limit - x start
  209. ;         04   dword  draw limit - y start
  210. ;         08   dword  draw limit - x end
  211. ;         0C   dword  draw limit - y end
  212. ;
  213. ; 0x8032BFF4 -> 32BFFF   background info
  214. ;         0x80323FF4    BgrDrawMode
  215. ;         0x80323FF8    BgrDataWidth
  216. ;         0x80323FFC    BgrDataHeight
  217. ;
  218. ; 0x80324000             page map     (length b = memsize shr 15)
  219. ; 0x80324000 + b         start of static pagetables

  220. ; 0x803FFFFF <- no direct address translation beyond this point
  221. ; =============================================================

  222. ; 0x805FF000 -> 5FFF80   TSS  
  223. ; 0x80600000 -> 601FFF   i/o maps

  224. ; 0x80800000 ->       kernel heap
  225. ; 0x80FFFFFF          heap min limit
  226. ; 0xFDBFFFFF          heap max limit

  227. ; 0xF0000000 -> 0xF1FFFFFF  PCI-express extended config space
  228. ; 0xFDC00000 -> 0xFDFFFFFF  page tables 4Mb
  229. ; 0xFE000000 -> 0xFFFFFFFF  LFB 32Mb
  230. ; 0xFE000000 -> 0xFE7FFFFF  application available LFB 8Mb
  231. ; 0xFE800000 -> 0xFFFFFFFF  kernel LFB part 24 Mb
复制代码
回复

使用道具 举报

9#
 楼主| 发表于 2014-11-15 08:43:50 | 只看该作者
本帖最后由 不点 于 2014-11-25 11:30 编辑

init_mem 函数很重要,一字一句详细分析一下。
  1. align 4
  2. proc init_mem
  3. ; calculate maximum allocatable address and number of allocatable pages
  4.         mov     edi, BOOT_VARS-OS_BASE + 0x9104   ; 从 0000:9104 开始,记录的是可用内存块的数组。
  5.         mov     ecx, [edi-4]      ; 而 0000:9100 处的四字节记录了可用内存块的个数。
  6.         xor     esi, esi; esi will hold total amount of memory
  7.         xor     edx, edx; edx will hold maximum allocatable address
  8. .calcmax:
  9. ; round all to pages
  10.         mov     eax, [edi] ; 取内存块起始地址到 eax
  11.         cmp     [edi+16], byte 1 ; 检查内存块类型是否是可用内存
  12.         jne     .unusable ; 不是可用内存,跳过

  13.         test    eax, 0xFFF ; 检查起始地址是否为页面对齐
  14.         jz      @f               ; 是的,跳过
  15.         neg     eax ; 不是按页对齐,取反它
  16.         and     eax, 0xFFF ; 屏蔽掉高位,只留下余数
  17.         add     [edi], eax ; 把它加到内存起始地址上,让其变成页面对齐
  18.         adc     dword [edi+4], 0 ; 进位时,调整高 32 位值
  19.         sub     [edi+8], eax ; 相应地,内存块长度也要适当减少
  20.         sbb     dword [edi+12], 0 ; 借位时,调整高 32 位的值
  21.         jc      .unusable ; 万一不够减,说明它太小,就忽略这块内存
  22. @@:
  23.         and     dword [edi+8], not 0xFFF ; 割掉内存块长度的尾巴, 让其 4K 对齐
  24.         jz      .unusable ; 割掉以后长度为 0, 无用了,忽略这块内存
  25. ; ignore memory after 4 Gb
  26.         cmp     dword [edi+4], 0 ; 起始地址高 32 位是否为 0?
  27.         jnz     .unusable ; 不是 0, 说明位于 4G 以上,忽略这块内存
  28.         mov     eax, [edi] ; 内存块起始于 4G 以内,取其起始地址到 eax
  29.         cmp     dword [edi+12], 0 ; 内存块长度高 32 位是否为 0?
  30.         jnz     .overflow ; 不是 0, 说明长度超过 4G, 需要截短
  31.         add     eax, [edi+8] ; 长度不超过 4G, 加到 eax 上,得到内存块的尾端地址
  32.         jnc     @f ; 没有发生进位,不用截短
  33. .overflow:
  34.         mov     eax, 0xFFFFF000 ; 截短, 尾部使用最高地址减去 4K
  35. @@:
  36.         cmp     edx, eax ; edx 比尾部地址大吗?
  37.         jae     @f ; 已经够大,跳过
  38.         mov     edx, eax ; 不够大,让其取当前最大的尾部地址值
  39. @@:
  40.         sub     eax, [edi] ; 尾部地址减去起始地址,等于内存块长度
  41.         mov     [edi+8], eax ; 修正长度低 32 位,但高 32 位也应该清 0, 却忘记了!
  42.         add     esi, eax ; 累加到 esi, 记录 4G 以内的全部可用内存量。
  43.         jmp     .usable
  44. .unusable:
  45. ;        and     dword [edi+8], 0
  46. .usable:
  47.         add     edi, 20 ; 处理下一个内存块
  48.         loop    .calcmax ; 循环计算最大值
  49. .calculated: ; 最大值计算完成,保存可用内存量到两个地址处
  50.         mov     [MEM_AMOUNT-OS_BASE], esi ; 可用内存量记录在地址 0xFE8C 处
  51.         mov     [pg_data.mem_amount-OS_BASE], esi
  52.         shr     esi, 12 ; 变成页面个数, 加以保存
  53.         mov     [pg_data.pages_count-OS_BASE], esi

  54.         shr     edx, 12 ; 把最高尾端地址也转化为页面值
  55.         add     edx, 31 ; 加上尾巴, 准备按 32 对齐
  56.         and     edx, not 31 ; 割掉尾巴
  57.         shr     edx, 3 ; 右移 3 位,就是除以 8, 理解不了----这可能是个错误。假如不是错误的话,那就是把每个页面对应于一个 bit,8 个页面就对应于一个字节,所以要除以 8,得到页映射位图所需要的字节数。
  58.         mov     [pg_data.pagemap_size-OS_BASE], edx

  59.         add     edx, (sys_pgmap-OS_BASE)+4095 ; 从系统页映射(应该是位图)基地址开始,加上页映射位图所需要的字节数
  60.         and     edx, not 4095 ; 再向上进行页对齐
  61.         mov     [tmp_page_tabs], edx ; 就得到了下一个临时页表的基地址。

  62.         mov     edx, esi ; 取可用页面的总数
  63.         and     edx, -1024 ; 向下页对齐
  64.         cmp     edx, (OS_BASE/4096) ; 比用户程序虚拟内存页数多吗?
  65.         jbe     @F ; 不多,跳过
  66.         mov     edx, (OS_BASE/4096) ; 多了,截短, 只使用所需要的页数
  67.         jmp     .set
  68. @@:
  69.         cmp     edx, (HEAP_BASE-OS_BASE+HEAP_MIN_SIZE)/4096 ; 比内核所需要的页数少吗? HEAP_BASE位于8M处,HEAP_MIN_SIZE=16M
  70.         jae     .set ; 不少,跳过
  71.         mov     edx, (HEAP_BASE-OS_BASE+HEAP_MIN_SIZE)/4096 ; 少了,扩大到内核所需要的页面数
  72. .set:
  73.         mov     [pg_data.kernel_pages-OS_BASE], edx ; 这就得到了内核要处理的页面总数
  74. 页面总容量的范围是 24M 至 2G。
  75.         shr     edx, 10 ; 每个页表可以含有 1024 个页面指针,所以,除以 1024
  76.         mov     [pg_data.kernel_tables-OS_BASE], edx ; 得到内核所需要的页表数。
  77. 每个页表对应于 4M 空间,所以,最多只有 512 个页表。

  78. ; 清除内核进程空间 8K,页目录被清零。
  79.         xor     eax, eax
  80.         mov     edi, sys_proc-OS_BASE ; EDI=0x6F000
  81.         mov     ecx, 8192/4
  82.         cld
  83.         rep stosd

  84.         mov     edx, (sys_proc-OS_BASE+PROC.pdt_0)+ 0x800; (OS_BASE shr 20) 指向 kernel 所对应的页目录项
  85.         bt      [cpu_caps-OS_BASE], CAPS_PSE ; CPU 支持页面大小扩展吗?
  86.         jnc     .no_PSE ; 不支持,跳过

  87.         mov     ebx, cr4
  88.         or      ebx, CR4_PSE
  89.         mov     eax, PG_LARGE+PG_SW ; 页目录项直接使用 4M 的大页面
  90.         mov     cr4, ebx ; 开启页面大小扩展功能
  91.         dec     [pg_data.kernel_tables-OS_BASE] ; 内核页表个数减少 1 个

  92.         mov     [edx], eax ; 写入第一个页目录项,指向物理地址 0 处的 4M 空间。
  93.         add     edx, 4 ; 这是下一个页目录项的指针

  94.         mov     edi, [tmp_page_tabs]
  95.         jmp     .map_kernel_heap        ; new kernel fits to the first 4Mb - nothing to do with ".map_low"

  96. .no_PSE:

  97. 先填充临时页表,它包含了从物理地址0到tmp_page_tabs的全部页

  98. tmp_page_tabs 大约位于 0x344000,那么,总共的页数就是 0x344 个。

  99.         mov     eax, PG_SW
  100.         mov     ecx, [tmp_page_tabs]
  101.         shr     ecx, 12
  102. .map_low:
  103.         mov     edi, [tmp_page_tabs]
  104. @@:                                   ;
  105.         stosd
  106.         add     eax, 0x1000
  107.         dec     ecx
  108.         jnz     @B

  109. 0x344 个指针的长度是 0x344 × 4 = 0xD10 = 3344 字节。

  110. 写完临时页表之后,紧接着是内核页表。

  111. .map_kernel_heap:

  112. 首先清空内核页表,它最大有 2M 的长度。

  113.         mov     ecx, [pg_data.kernel_tables-OS_BASE]
  114.         shl     ecx, 10
  115.         xor     eax, eax
  116.         rep stosd

  117. 开始写内核页目录项,指向临时页表,在 PSE 的情况,它是指向清空了的内核页表;
  118. 在 no_PSE 的情况,它是指向临时页表,最终映射到物理地址 0。

  119. 这里比较混乱。在 PSE 的情况,4M 是已经完全映射的。而在 no_PSE 的情况,4M 没有完全映射。

  120.         mov     ecx, [pg_data.kernel_tables-OS_BASE]
  121.         mov     eax, [tmp_page_tabs]
  122.         or      eax, PG_SW
  123.         mov     edi, edx

  124. .map_kernel_tabs:
  125.         stosd
  126.         add     eax, 0x1000
  127.         dec     ecx
  128.         jnz     .map_kernel_tabs
  129. 内核页目录项写完了,再把页目录所在的地址映射到 page_tabs 处。
  130.         mov     dword [sys_proc-OS_BASE+PROC.pdt_0+(page_tabs shr 20)], sys_proc+PROC.pdt_0+PG_SW-OS_BASE

  131.         mov     edi, (sys_proc+PROC.pdt_0-OS_BASE)
  132.         lea     esi, [edi+(OS_BASE shr 20)]
  133.         movsd
  134.         movsd
  135.         ret
  136. endp
复制代码

回复

使用道具 举报

10#
 楼主| 发表于 2014-11-22 02:05:47 | 只看该作者
本帖最后由 不点 于 2014-11-25 15:51 编辑
  1. proc init_page_map
  2. ; mark all memory as unavailable 先把所有的内存标为 "不可用"。
  3.         mov     edi, sys_pgmap-OS_BASE 准备写系统页位图
  4.         mov     ecx, [pg_data.pagemap_size-OS_BASE] 取页位图的长度
  5.         shr     ecx, 2 转换成 dword 的个数
  6.         xor     eax, eax 写入 0
  7.         cld
  8.         rep stosd

  9. 扫描内存图,将自由区域标为 "可用"
  10. ; scan through memory map and mark free areas as available
  11.         mov     ebx, BOOT_VARS-OS_BASE + 0x9104 取内存块基地址
  12.         mov     edx, [ebx-4] 取内存块个数
  13. .scanmap:
  14.         cmp     [ebx+16], byte 1 是自由内存吗?
  15.         jne     .next 不是的,忽略

  16.         mov     ecx, [ebx+8] 取内存块长度
  17.         shr     ecx, 12; ecx = number of pages 转换成页数
  18.         jz      .next 是0,跳过
  19.         mov     edi, [ebx] 取内存块基地址
  20.         shr     edi, 12; edi = first page 转换成页序号
  21.         mov     eax, edi
  22.         shr     edi, 5 除以 32
  23.         shl     edi, 2 乘4,得到字节偏移
  24.         add     edi, sys_pgmap-OS_BASE 得到要修改的字节指针
  25.         and     eax, 31 得到余数
  26.         jz      .startok 如果余数是0,跳过
  27.         add     ecx, eax
  28.         sub     ecx, 32
  29.         jbe     .onedword 只有 1 个 dword 宽
  30.         push    ecx
  31.         mov     ecx, eax 先处理余数,保存在 ecx 中
  32.         or      eax, -1 此时 EAX=FFFFFFFF
  33.         shl     eax, cl
  34.         or      [edi], eax 把开头的余数部分处理完毕,剩下是 dword 对齐的了
  35.         add     edi, 4 下一个 dword
  36.         pop     ecx
  37. .startok:
  38.         push    ecx
  39.         shr     ecx, 5 页数除以 32
  40.         or      eax, -1 每次标注 32 个页为可用页
  41.         rep stosd
  42.         pop     ecx
  43.         and     ecx, 31 余数
  44.         neg     eax  此时 EAX=1
  45.         shl     eax, cl
  46.         dec     eax
  47.         or      [edi], eax 所有的余数页都处理完毕
  48.         jmp     .next
  49. .onedword:
  50.         add     ecx, 32
  51.         sub     ecx, eax
  52. @@:
  53.         bts     [edi], eax
  54.         inc     eax
  55.         loop    @b
  56. .next:
  57.         add     ebx, 20
  58.         dec     edx
  59.         jnz     .scanmap

  60. ; mark kernel memory as allocated (unavailable) 把内核的内存标为不可用
  61.         mov     ecx, [tmp_page_tabs] 内核代码数据尾端地址
  62.         mov     edx, [pg_data.pages_count-OS_BASE] 总页数
  63.         shr     ecx, 12 内核代码数据占用的页数
  64.         add     ecx, [pg_data.kernel_tables-OS_BASE] 再加上内核要处理的页表数,得到内核保留的页数
  65.         sub     edx, ecx 总页数减去内核保留的页数,得到自由页数
  66.         mov     [pg_data.pages_free-OS_BASE], edx 保存自由页数

  67.         mov     edi, sys_pgmap-OS_BASE
  68.         mov     ebx, ecx 内核保留的页数
  69.         shr     ecx, 5
  70.         xor     eax, eax 设定为不可用
  71.         rep stosd

  72.         not     eax
  73.         mov     ecx, ebx
  74.         and     ecx, 31 余数部分
  75.         shl     eax, cl
  76.         and     [edi], eax 处理完毕
  77.         add     edi, OS_BASE
  78.         mov     [page_start-OS_BASE], edi; 页位图的自由页起始地址保存在 page_start 变量中

  79.         mov     ebx, sys_pgmap 页位图起始地址
  80.         add     ebx, [pg_data.pagemap_size-OS_BASE] 加上页位图的长度
  81.         mov     [page_end-OS_BASE], ebx 页位图的终止地址,保存在 page_end 中。

  82.         ret
  83. endp

复制代码
回复

使用道具 举报

11#
 楼主| 发表于 2014-11-23 22:23:03 | 只看该作者
本帖最后由 不点 于 2014-11-24 10:13 编辑

尝试修改了一下,制作了补丁,在一楼下载。

编译生成 kernel.mnt 文件,这就是 kolibri 的内核。

用它替换掉 kolibri.img 里面的同名文件,即可测试了。

目前 USB 初始化部分有问题。进入桌面后,不能用鼠标。

总之,排错的工作量很大。
回复

使用道具 举报

12#
 楼主| 发表于 2014-11-29 10:08:53 | 只看该作者
本帖最后由 不点 于 2014-11-29 10:13 编辑


整理一下 grub4dos 的分页数据结构。

/* Paging structure : PML4, PDPT, PD  4096-bytes each */
/* Memory area from 0x50000 to the end of low memory is used by gfxmenu. So we
* should not use 0x60000 for page tables. And all other free room in the low
* memory is reserved. So we should use extended memory if possible.
* Currently we use the ending 16K of the first 16M. -- tinybit
*
* Big problem! Some chipset use 1M at 15M. -- tinybit 2012-11-01
*/

//#define PAGING_TABLES_BUF        0x60000
//#define PAGING_TABLES_BUF        0xFFC000
#define PAGING_TABLES_BUF        0xEFC000
#define PAGING_TABLES_BUFLEN        0x4000


以下是 char_io.c 里面关于分页的代码。

#define PAGING_PML4_ADDR (PAGING_TABLES_BUF+0x0000)
#define PAGING_PDPT_ADDR (PAGING_TABLES_BUF+0x1000)
#define PAGING_PD_ADDR   (PAGING_TABLES_BUF+0x2000)

// If this value is changed, memory_paging_map_for_transfer must also be modified.
#define PAGINGTXSTEP 0x800000

#define DST_VIRTUAL_BASE  0x1000000UL
#define SRC_VIRTUAL_BASE  0x2000000UL
#define DST_VIRTUAL_ADDR(addr) (((unsigned long)(addr) & 0x1FFFFFUL)+DST_VIRTUAL_BASE)
#define SRC_VIRTUAL_ADDR(addr) (((unsigned long)(addr) & 0x1FFFFFUL)+SRC_VIRTUAL_BASE)
#define DST_VIRTUAL_PTR(addr) ((void*)DST_VIRTUAL_ADDR(addr))
#define SRC_VIRTUAL_PTR(addr) ((void*)SRC_VIRTUAL_ADDR(addr))

// Set to 0 to test mem64 function
#define DISABLE_AMD64 1

extern void memory_paging_init(void);
extern void memory_paging_enable(void);
extern void memory_paging_disable(void);
extern void memory_paging_map_for_transfer(unsigned long long dst_addr, unsigned long long src_addr);

unsigned char memory_paging_initialized = 0;

void memory_paging_init()
{
    // prepare PDP, PDT
    unsigned long long *paging_PML4 = (unsigned long long *)PAGING_PML4_ADDR;
    unsigned long long *paging_PDPT = (unsigned long long *)PAGING_PDPT_ADDR;
    unsigned long long *paging_PD   = (unsigned long long *)PAGING_PD_ADDR;
    unsigned long long a;
   
    paging_PML4[0] = PAGING_PDPT_ADDR | PML4E_P;
    _memset(paging_PML4+1,0,4096-8*1);
   
    paging_PDPT[0] = PAGING_PD_ADDR | PDPTE_P;
    _memset(paging_PDPT+1,0,4096-8*1);
   
    // virtual address 0-16MB = physical address 0-16MB
    paging_PD[ 0] = a = 0ULL | (PDE_P|PDE_RW|PDE_US|PDE_PS|PDE_G);
    paging_PD[ 1] = (a += 0x200000);
    paging_PD[ 2] = (a += 0x200000);
    paging_PD[ 3] = (a += 0x200000);
    paging_PD[ 4] = (a += 0x200000);
    paging_PD[ 5] = (a += 0x200000);
    paging_PD[ 6] = (a += 0x200000);
    paging_PD[ 7] = (a += 0x200000);
    _memset(paging_PD+8,0,4096-8*8);
   
    memory_paging_initialized = 1;
}
void memory_paging_map_for_transfer(unsigned long long dst_addr, unsigned long long src_addr)
{
    unsigned long long *paging_PD = (unsigned long long *)PAGING_PD_ADDR;
    unsigned long long a;
    if (!memory_paging_initialized)
        memory_paging_init();
    // map 5 2MB-pages for transfer up to 4*2MB.
    // virtual address 16MB 0x01000000
    paging_PD[ 8] = a = (dst_addr&(-2ULL<<20)) | (PDE_P|PDE_RW|PDE_US|PDE_PS);
    paging_PD[ 9] = (a += 0x200000);
    paging_PD[10] = (a += 0x200000);
    paging_PD[11] = (a += 0x200000);
    paging_PD[12] = (a += 0x200000);
    // virtual address 32MB 0x02000000
    paging_PD[16] = a = (src_addr&(-2ULL<<20)) | (PDE_P|PDE_RW|PDE_US|PDE_PS);
    paging_PD[17] = (a += 0x200000);
    paging_PD[18] = (a += 0x200000);
    paging_PD[19] = (a += 0x200000);
    paging_PD[20] = (a += 0x200000);
    // invalidate non-global TLB entries
    { int r0;
      asm volatile ("movl %%cr3,%0; movl %0,%%cr3" : "=&q"(r0) : : "memory");
    }
}
void memory_paging_enable()
{
    if (!memory_paging_initialized)
        memory_paging_init();
    // enable paging
    {
      int r0,r1;
      asm volatile (
        "movl %%cr0, %0; movl %%cr4, %1; \n\t"
        "orl  $0x80000001,%0; \n\t" // CR0.PE(bit0)|PG(bit31)
        "orl  $0x00000030,%1; \n\t" // CR4.PAE(bit4)|PSE(bit5)
        "movl %1, %%cr4; \n\t"  // set PAE|PSE
        "movl %2, %%cr3; \n\t"  // point to PDPT
        "movl %0, %%cr0; \n\t"  // set PE|PG
        "ljmp %3,$(1f) \n1:\t"  // flush instruction cache
        //"movl %4, %0; movl %0, %%ds; movl %0, %%es; movl %0, %%ss;" // reload DS,ES,SS
        "btsl $7, %1;    \n\t"  // CR4.PGE(bit7)
        "movl %1, %%cr4; \n\t"  // set PGE
        :"=&r"(r0),"=&r"(r1)
        :"r"(PAGING_PDPT_ADDR),
         "i"(PROT_MODE_CSEG),
         "i"(PROT_MODE_DSEG)
        :"memory");
    }
}
void memory_paging_disable()
{
    int r0;
    asm volatile (
        "movl %%cr0,%0;  andl %1,%0;  movl %0,%%cr0; \n\t"
        "ljmp %3,$(1f) \n1:\t"  // flush instruction cache
        "movl %%cr4,%0;  andl %2,%0;  movl %0,%%cr4; \n\t"
        "                xorl %0,%0;  movl %0,%%cr3; \n\t"
        :"=&a"(r0)
        :"i"(~(CR0_PG)),
         "i"(~(CR4_PSE|CR4_PAE|CR4_PGE)),
         "i"(PROT_MODE_CSEG)
        :"memory");
}

/*
Transfer data in memory.
Limitation:
code must be below 16MB as mapped by memory_paging_init function
*/
unsigned long long
grub_memmove64(unsigned long long dst_addr, unsigned long long src_addr, unsigned long long len)
{
    if (!len)      { errnum = 0; return dst_addr; }
    if (!dst_addr) { errnum = ERR_WONT_FIT; return 0; }

    // forward copy should be faster than backward copy
    // If src_addr < dst_addr < src_addr+len, forward copy is not safe, so we do backward copy in that case.
    unsigned char backward = ((src_addr < dst_addr) && (dst_addr < src_addr+len));  
    unsigned long highaddr = (unsigned long)( dst_addr       >>32)
                           | (unsigned long)((dst_addr+len-1)>>32)
                           | (unsigned long)( src_addr       >>32)
                           | (unsigned long)((src_addr+len-1)>>32);
    if ( highaddr==0 )
    { // below 4GB just copy it normally
        void *pdst = (void*)(unsigned long)dst_addr;
        void *psrc = (void*)(unsigned long)src_addr;
        if (backward)
            _memcpy_backward(pdst, psrc, len);
        else
            _memcpy_forward(pdst, psrc, len);
        errnum = 0; return dst_addr;
    }
    else if ( (highaddr>>(52-32))==0 && (is64bit & IS64BIT_AMD64) && !DISABLE_AMD64)
    { // AMD64/IA32-e paging
        mem64 (1, dst_addr, src_addr, len);        /* 1 for MOVE */
        return dst_addr;
    }
    else if ( (highaddr>>(52-32))==0 && (is64bit & IS64BIT_PAE))
    { // PAE paging
        void *pdst = DST_VIRTUAL_PTR(dst_addr);
        void *psrc = SRC_VIRTUAL_PTR(src_addr);
        unsigned long long dsta = dst_addr, srca = src_addr;
        memory_paging_enable();
        memory_paging_map_for_transfer(dsta, srca);
        // transfer
        unsigned long long nr = len; // number of bytes remaining
        while (1)
        {
            unsigned long n1 = (nr>=PAGINGTXSTEP)? PAGINGTXSTEP: (unsigned long)nr;  // number of bytes per round (8MB)
            // Copy
            if (backward)
                _memcpy_backward(pdst, psrc, n1);
            else
                _memcpy_forward(pdst, psrc, n1);
            // update loop variables
            if ((nr -= n1)==0) break;
            memory_paging_map_for_transfer((dsta+=n1), (srca+=n1));
        }
        memory_paging_disable();
        errnum = 0; return dst_addr;
    }
    else
    {
        errnum = ERR_WONT_FIT; return 0;
    }
}
unsigned long long
grub_memset64(unsigned long long dst_addr, unsigned int data, unsigned long long len)
{
    if (!len)      { errnum=0; return dst_addr; }
    if (!dst_addr) { errnum = ERR_WONT_FIT; return 0; }
    unsigned long highaddr = (unsigned long)( dst_addr       >>32)
                           | (unsigned long)((dst_addr+len-1)>>32);
    if ( highaddr==0 )
    { // below 4GB
        _memset((void*)(unsigned long)dst_addr, data, len);
        errnum = 0; return dst_addr;
    }
    else if ( (highaddr>>(52-32))==0 && (is64bit & IS64BIT_AMD64) && !DISABLE_AMD64)
    { // AMD64/IA32-e paging
        mem64 (3, dst_addr, data, len);        /* 3 for SET */
        return dst_addr;
    }
    else if ( (highaddr>>(52-32))==0 && (is64bit & IS64BIT_PAE))
    { // PAE paging
        void *pdst = DST_VIRTUAL_PTR(dst_addr);
        unsigned long long dsta = dst_addr;
        memory_paging_enable();
        memory_paging_map_for_transfer(dsta, dsta);
        // transfer
        unsigned long long nr = len; // number of bytes remaining
        while (1)
        {
            unsigned long n1 = (nr>=PAGINGTXSTEP)? PAGINGTXSTEP: (unsigned long)nr;  // number of bytes per round (8MB)
            // Copy
            _memset(pdst, data, n1);
            // update loop variables
            if ((nr -= n1)==0) break;
            dsta+=n1;
            memory_paging_map_for_transfer(dsta, dsta);
        }
        memory_paging_disable();
        errnum = 0; return dst_addr;
    }
    else
    {
        errnum = ERR_WONT_FIT; return 0;
    }
}
int
grub_memcmp64(unsigned long long str1addr, unsigned long long str2addr, unsigned long long len)
{
    if (!len)      { errnum=0; return 0; }
    unsigned long highaddr = (unsigned long)( str1addr       >>32)
                           | (unsigned long)((str1addr+len-1)>>32)
                           | (unsigned long)( str2addr       >>32)
                           | (unsigned long)((str2addr+len-1)>>32);
    if ( highaddr==0 )
    { // below 4GB
        return _memcmp((const char*)(unsigned long)str1addr,
            (const char*)(unsigned long)str2addr, (unsigned long)len);
    }
    else if ( (highaddr>>(52-32))==0 && (is64bit & IS64BIT_AMD64) && !DISABLE_AMD64)
    { // AMD64/IA32-e paging
        return mem64 (2, str1addr, str2addr, len);        /* 2 for CMP */
    }
    else if ( (highaddr>>(52-32))==0 && (is64bit & IS64BIT_PAE))
    { // PAE paging
        void *p1 = DST_VIRTUAL_PTR(str1addr);
        void *p2 = SRC_VIRTUAL_PTR(str2addr);
        unsigned long long a1=str1addr, a2= str2addr;
        unsigned long long nr = len; // number of bytes remaining
        int r=0;
        memory_paging_enable();
        memory_paging_map_for_transfer(a1, a2);
        {
            while (1)
            {
                unsigned long n1 = (nr>=PAGINGTXSTEP)? PAGINGTXSTEP: (unsigned long)nr;  // number of bytes per round (8MB)
                // Compare
                r = _memcmp(p1, p2, n1);
                if (r) break;
                // update loop variables
                if ((nr -= n1)==0) break;
                memory_paging_map_for_transfer((a1+=n1), (a2+=n1));
            }
        }
        memory_paging_disable();
        return r;
    }
    else
    {
        errnum = ERR_WONT_FIT; return 0;
    }
}

回复

使用道具 举报

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

本版积分规则

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

闽公网安备 35020302032614号

GMT+8, 2024-11-15 02:11

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

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