2006年二月月 发布的文章

Inside the 'i386'

The term ‘i386′ in the title does not refer to the real Intel 80386 processor but the representative of Intel 32-bit architecture(IA32). I prefer ‘i386′ rather than ‘IA32′ just like what the linux kernel does, since you can find ‘i386′ folder in $(linux-2.6.x_dir)/arch directory. This artical describes some basic knowledge of ‘i386′, which may be kinda useful to those guys who wanna do research on or develop operating system.

We know that the ‘i386′ processors are the most widely used and supported today, And even the linux was born on it. As a researcher or a developer, we wonder what the ‘i386′ processor offers to us. In brief ‘i386′ offers us an execution environment which consists of a set of registers and several mechanisms of accessing memory. Today the Intel mainstream processors, such as Pentium series, are almost based on 80386 processor which first introduced 32-bit registers and paging into ‘i386′. let us have a look at the resources supplied by the ‘i386′ processor. A part of the contents below are quoted from the book "Intel Architecture Software Developer’s Manual, Volume 1, Basic Architecture".

1. Memory accessing
Any operating system or executive designed to work with an ‘i386′ processor will use the processor’s memory management facilities to access memory. So far ‘i386′ processors support three memory-accessing model. Once using the processor’s memory management facilities, programs do not directly address physical memory. Instead, they access memory using any of these three memory models: flat, segmented, or real-address mode. With the flat memory model, memory appears to a program as a single, continuous address space, which is byte addressable and is called ‘linear address space’. it covers contiguously from 0 to (4G -1). When using this model, code (a program’s instructions), data, and the procedure stacks are all contained in this address space. With the segmented memory model, memory appears to a program as a group of independent address spaces called segments. When using this model, code, data, and stacks are typically contained in separate segments. To address a byte in a segment, a program must issue a logical address, which consists of a segment selector and an offset. Internally, the processor translates each logical address into a linear address to access a memory location and this translation is transparent to the application program. With either the flat or segmented model, the ‘i386′ processor provides facilities for dividing the linear address space into pages and mapping the pages into virtual memory. If an operating system/executive uses the ‘i386′ processor’s paging mechanism, the existence of the pages is transparent to an application program. we can also do a summary with an image as follows:
Logical Address(segmented mode) –> [Segmentation Unit] –> Liner Address(flat mode) –> [Paging Unit] –> Physical Address

The left real-address mode model uses the memory model for the Intel 8086 processor, the first ‘i386′ processor. The real-address mode uses a specific implementation of segmented memory in which the linear address space for the program and the operating system/executive consists of an array of segments, each of which is up to 64K bytes in size. The maximum size of the linear address space in real-address mode is 1M bytes.

Here, we have to say something about the ‘operatiing mode’. the ‘i386′ processor supports three operating mode which determines which instructions and architectural features are accessible.

(1) Protected mode
It is the native state of the processor. In this mode all instructions and architectural features are available, providing the highest performance and capability. This is the recommended mode for all new applications and operating systems. When in this mode, the processor can use any of the memory models described above. (The real-addressing mode memory model is ordinarily used only when the processor is in the virtual-8086 mode.)

(2) Real-address mode
This mode provides the programming environment of the Intel 8086 processor with a few extensions (such as the ability to switch to protected or system management mode). The processor is placed in real-address mode following power-up or a reset. When in this mode, the processor only supports the real-address mode memory model. As we know the process of booting from disk is in this mode.

(3) System management mode
It is unfamiliar to most of us. we have nothing to say.

2. Registers
The registers in ‘i386′ processors can be grouped into three type: ‘general-purpose data registers’, ‘segment registers’ and ‘status and control registers’. Details as follows:

(1) General-Purpose data registers
There are eight 32-bit registers available for general purpose, such as storing operands and pointers. In theory you can select any of them to do what you wanna do, but many instructions assign specific registers to hold operands. The following is a summary of these special uses:
EAX – Accumulator for operands and results data.
EBX – Pointer to data in the DS segment.
ECX – Counter for string and loop operations.
EDX – I/O pointer.
ESI – Pointer to data in the segment pointed to by the DS register; source pointer for string operations.
EDI – Pointer to data (or destination) in the segment pointed to by the ES register; destination pointer for string operations.
ESP – Stack pointer (in the SS segment).
EBP – Pointer to data on the stack (in the SS segment).

(2) Segment registers
There are six registers for holding segment selector which are a special pointer that identifies a segment in memory and all
of these segment registers are 16-bit. To access a particular segment in memory, the segment selector for that segment must be present in the appropriate one of the segment registers. So, although a system can define thousands of segments, only 6 can be available for immediate use. Other segments can be made available by loading their segment selectors into these registers during program execution. Every segment register has a ‘visible’ part(16 bits in 32-bit platform) and a ‘hidden’ part. (The hidden part is sometimes referred to as a ‘descriptor cache’ or a ‘shadow register’.) When a segment selector is loaded into the visible part of a segment register, the processor also loads the hidden part of the segment register with the base address, segment limit, and access control information from the segment descriptor pointed to by the segment selector. Some load instructions such as ‘mov’, ‘pop’, etc explicitly reference the segment registers and other instructions such as ‘call’, ‘jmp’, or ‘ret’ change the contents of the CS register (and sometimes other segment registers) as an incidental part of their operation. How these segment registers are used depends on the type of memory accessing model that the operating system or executive is using.

We just mentioned ‘segment descripters’. A segment descriptor is a data structure in a GDT or LDT that provides the processor with the size and location of a segment, as well as access control and status information. Segment descriptors are typically created by compilers
, linkers, loaders, or the operating system or executive, but not application programs.

(3) Status and control registers
These registers report and allow modification of the state of the processor and of the program being executed. E.g. the 32-
bit EFLAGS register contains a group of status flags, a control flag, and a group of system flags. Details as follows:
CF – Carry Flag
PF – Parity Flag
AF – Auxiliary Carry Flag
ZF – Zero Flag
SF – Sign Flag
TF – Trap Flag
IF – Interrupt Enable Flag
DF – Direction Flag
OF – Overflow Flag
IOPL – I/O Privilege Level
NT – Nested Task
RF – Resume Flag
VM – Virtual-8086 Mode
AC – alignment Check(AC)
VIF – Virtual Interrupt Flag
VIP – Virtual Interrupt Pending
ID – ID Flag
Some of the flags in the EFLAGS register can be modified directly using special-purpose instructions. 

The ‘i386′ processor is so complex that we can not list all of its features here. If you are interested in it, you may read the thick enough ‘i386′ manuals to make all clear.

Retired 'bootsect.S'

We know that the latest linux kernel version is 2.6.x, which is different from the ‘old kernels’ in booting. The ‘bootsect.S’, which used to make the kernel image in the floppy disk bootable in the early days, becomes useless in linux kernel 2.6.x today, although it is still a part of the kernel image.

We know that ‘bootsect.S’ is usu placed in the first 512 bytes of the kernel image and installed in the first sector of some medium on which the kernel image is installed. the mediums usu include hard disk (or the active partition of the hard disk) and floppy disk. As a minimal ‘bootloader’ included in kernel images of earlier linux versions up to the 2.4, the ‘bootsect.S’ is in duty bound to copy the left kernel image from medium to main memory when we boot linux from the floppy disk and then execute the loaded code in order to complete its mission. when we boot linux from hard disk, the ‘bootsect.S’ does nothing actively but to be checked by other booting routine stored in BIOS(Basic Input/Output System) or MBR(Master Boot Record). Today if you wanna boot linux 2.6.x from a floppy disk, you have to select a suitable bootloader yourself, just like that you boot linux from hard disk, since the ‘bootsect.S’ has retired.

Here list the source code of ‘bootsect.S’ and some comments of mine. let us go and see what the retired ‘bootsect.S’ really does! (my comments usu occur following the symbol ‘!’)

/*
 * bootsect.S  Copyright (C) 1991, 1992 Linus Torvalds
 *
 * modified by Drew Eckhardt
 * modified by Bruce Evans (bde)
 * modified by Chris Noe (May 1999) (as86 -> gas)
 * gutted by H. Peter Anvin (Jan 2003)
 *
 * BIG FAT NOTE: We’re in real mode using 64k segments.  Therefore segment
 * addresses must be multiplied by 16 to obtain their respective linear
 * addresses. To avoid confusion, linear addresses are written using leading
 * hex while segment addresses are written as segment:offset.
 *
 * ! $(linux-2.6.15.3_dir)/arch/i386/bootsect.S
 */

/* ! I found this header file in $(linux-2.6.15.3_dir)/include/asm-i386 */
#include

/*
 * ! DEF_INITSEG   0×9000
 * ! DEF_SYSSEG    0×1000
 * ! DEF_SETUPSEG  0×9020
 * ! DEF_SYSSIZE   0x7F00
 * ! These macros above are defined in ‘boot.h’ and
 * ! the values of the first three of them
 * ! used to be stored into ‘cs’ register
 */
SETUPSECTS = 4   /* default nr of setup-sectors */
BOOTSEG  = 0x07C0  /* original address of boot-sector */
INITSEG  = DEF_INITSEG  /* we move boot here – out of the way */
SETUPSEG = DEF_SETUPSEG  /* setup starts here */
SYSSEG  = DEF_SYSSEG  /* system loaded at 0×10000 (65536) */
SYSSIZE  = DEF_SYSSIZE  /* system size: # of 16-byte clicks */
                                            /* to be loaded */
/*
 * ! Here no matter what the ‘ROOT_DEV’ is is insignificant.
 * ! When kernel image builds, this ‘ROOT_DEV’ will be reset.
 * ! And so does ‘SWAP_DEV’.
 * ! ‘ROOT_DEV’ is variable which represents the type of the device
 * ! in which the root file system stores.
 * ! ‘ROOT_DEV = 0′ means the same type of floopy as boot. 
 */
ROOT_DEV = 0    /* ROOT_DEV is now written by "build" */
SWAP_DEV = 0   /* SWAP_DEV is now written by "build" */

#ifndef SVGA_MODE
#define SVGA_MODE ASK_VGA
#endif

#ifndef RAMDISK
#define RAMDISK 0
#endif

#ifndef ROOT_RDONLY
#define ROOT_RDONLY 1
#endif

/*
 * !Now we are running in 16-bit real mode, neither in
 * ! 32-bit real mode nor in 32-bit protected mode
 */
.code16
.text

.global _start
_start:

 /*
  * ! jmpl is an ‘jump’ instruction which
  * ! jumps between segments.
  * ! the instruction below first stores the
  * ! immediate number ‘$BOOTSEG’ into ‘CS’
  * ! register and stores the address of label
  * ! ‘start2′ into ‘EIP’ register, and then jumps
  * ! to label ‘start2′ to execute.
  * ! Now, R[%cs] = $BOOTSEG = 0x07C0
  */
 # Normalize the start address
 jmpl $BOOTSEG, $start2

start2:
 /*
  * ! initialize some general registers
  * ! R[%ds] = R[%es] = R[%ss] = 0x07C0
  * ! R[%sp] = 0x7c00
  */
 movw %cs, %ax
 movw %ax, %ds
 movw %ax, %es
 movw %ax, %ss
 movw $0x7c00, %sp

 /*
  * ! sti – set the interrupt flag
  * ! cld – clear ‘df’(direction flag). after it executed,
  * !       string operations will increment the index
  * !       registers (si and/or di) that they use
  */
 sti
 cld

 /*
  * ! store the address of ‘bugger_off_msg’
  * ! into register ‘si’(source-index register)
  */
 movw $bugger_off_msg, %si

 /*
  * ! this loop prints the ‘bugger_off_msg’ on screen
  * ! and jumps to ‘die’ label.
  */
msg_loop:
 /*
  * ! lodsb loads ‘al’ register with single memory
  * ! byte at the position pointed to by ‘si’ register
  * ! after the executing, the ‘si’ is automatically
  * ! increased or decreased according to the ‘df’.
  */
 lodsb
 andb %al, %al
 jz die
 movb $0xe, %ah
 movw $7, %bx
 int $0×10
 jmp msg_loop

 /*
  * ! the computer dies and you have to reboot.
  */
die:
 # Allow the user to press a key, then reboot
 xorw %ax, %ax

 /*
  * ! int 16h – bios interrupt to give user
  * ! a chance to enter something from the keyboard
  */
 int $0×16
 int $0×19

 # int 0×19 should never return.  In case it does anyway,
 # invoke the BIOS reset code…
 ljmp $0xf000,$0xfff0

bugger_off_msg:
 .ascii "Direct booting from floppy is no longer supported.\r\n"
 .ascii "Please use a boot loader program instead.\r\n"
 .ascii "\n"
 .ascii "Remove disk and press any key to reboot . . .\r\n"
 .byte 0

 # Kernel attributes; used by setup

 /*
  * ! variables below are important since
  * ! they would be refered by ‘setup.S’
  * ! the total size of these variables is
  * ! 15 bytes, 497 + 15 = 512 :)
  * ! the last word is ’0xAA55′, which indicates
  * ! this is a boot sector
  */
 .org 497
setup_sects: .byte SETUPSECTS
root_flags: .word ROOT_RDONLY
syssize: .word SYSSIZE
swap_dev: .word SWAP_DEV
ram_size: .word RAMDISK
vid_mode: .word SVGA_MODE
root_dev: .word ROOT_DEV
boot_flag: .word 0xAA55

/* ! end of bootsect.S */

Thus, we know that the retired ‘bootsect.S’ only tells us it has retired.

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats