linux-0.11 analysis: boot file head.s Third Essay


Refer to [the blogger github][ ]

  1. Change stack top position

    	movl $0x10,%eax
    	mov %ax,%ds
    	mov %ax,%es
    	mov %ax,%fs
    	mov %ax,%gs
    	lss _stack_start,%esp

    First, ds, es, fs, gs are all set to 0x10

    And then this. lss _stack_start, esp is equivalent to pointing the top pointer of ss:sep to _ stack_start, which is not found in head.s, is in sched.c

    long user_stack [ PAGE_SIZE>>2 ] ;
    struct {
    	long * a;
    	short b;
    	} stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };

    sched.c in the kernel file of the linux-0.11 file, around 67-72 lines;

  2. Set idt and gdt from the new settings;

    call setup_idt
    call setup_gdt
    movl $0x10,%eax		; Reload all segment registers
    mov %ax,%ds		; change gdt After. CS Already
    mov %ax,%es		; In " setup\u gdt"Reload
    mov %ax,%fs
    mov %ax,%gs
    lss _stack_start,%esp

    The first two lines set values for idt and gdt, respectively

    ds, es, fs, gs have been reset since these values were changed in the settings idt and gdt, and there is no stack here for the call operation, so you need to set them from the new settings and from the top of the new stack

  3. First look at dit settings

    	lea ignore_int,%edx
    	movl $0x00080000,%eax
    	movw %dx,%ax		/* selector = 0x0008 = cs */
    	movw $0x8E00,%dx	/* interrupt gate - dpl=0, present */
    	lea _idt,%edi
    	mov $256,%ecx
    	movl %eax,(%edi)
    	movl %edx,4(%edi)
    	addl $8,%edi
    	dec %ecx
    	jne rp_sidt
    	lidt idt_descr

    Set **256 ** interrupt descriptors so that the interrupt routine in each interrupt descriptor points to a ignore_ Function address of int

    Look at ignore_int

    	pushl %eax
    	pushl %ecx
    	pushl %edx
    	push %ds
    	push %es
    	push %fs
    	movl $0x10,%eax
    	mov %ax,%ds
    	mov %ax,%es
    	mov %ax,%fs
    	pushl $int_msg
    	call _printk
    	popl %eax
    	pop %fs
    	pop %es
    	pop %ds
    	popl %edx
    	popl %ecx
    	popl %eax

    call_ Pritk This address is a print operation.....

  4. Look again at the settings for gdt

    	lgdt gdt_descr
    	.word 256*8-1		# so does gdt (not that that's any
    	.long _gdt		# magic number, but it works for me :^)
    	.align 3
    	.quad 0x0000000000000000	/* NULL descriptor */
    	.quad 0x00c09a0000000fff	/* 16Mb */
    	.quad 0x00c0920000000fff	/* 16Mb */
    	.quad 0x0000000000000000	/* TEMPORARY - don't use */
    	.fill 252,8,0

    Just like the gdt you set up earlier, the location in memory has changed

    And see how the memory changes

  5. Then look at the code that goes into main.c

    	pushl $0		# These are the parameters to main :-)
    	pushl $0
    	pushl $0
    	pushl $L6		# return address for main, if it decides to.
    	pushl $_main
    	jmp setup_paging
    	jmp L6	

    Put _ Main's address is stacked when JMP setup_is complete After paging, the stack goes into the main.c function

  6. Next, take a look at setup_paging this function

    	movl $1024*5,%ecx		/* 5 pages - pg_dir+4 page tables */
    	xorl %eax,%eax
    	xorl %edi,%edi			/* pg_dir is at 0x000 */
    	movl $pg0+7,_pg_dir		/* set present bit/user r/w */
    	movl $pg1+7,_pg_dir+4		/*  --------- " " --------- */
    	movl $pg2+7,_pg_dir+8		/*  --------- " " --------- */
    	movl $pg3+7,_pg_dir+12		/*  --------- " " --------- */
    	movl $pg3+4092,%edi
    	movl $0xfff007,%eax		/*  16Mb - 4096 + 7 (r/w user,p) */
    1:	stosl			/* fill pages backwards - more efficient :-) */
    	subl $0x1000,%eax
    	jge 1b
    	xorl %eax,%eax		/* pg_dir is at 0x0000 */
    	movl %eax,%cr3		/* cr3 - page directory start */
    	movl %cr0,%eax
    	orl $0x80000000,%eax
    	movl %eax,%cr0		/* set paging (PG) bit */

    This is setting the page directory and table;

    Paging is also equivalent to changing the way physical addressing is done, or adding an extra step to physical addressing

    First look at the previous segmentation mechanism:

    Segment selector: The 12-bit high=>stores the index of the segment descriptor; Segment selectors such as ds, cs, es, etc.

    Find the segment descriptor by index of the segment descriptor: at get segment base + offset address = physical address

    Now the pages of this paging mechanism are similar, but just behind the paging mechanism

    Let's start with the official words: logical address, linear address, virtual address, physical address

    Logical Address: The address given by our programmer when writing a program

    Linear Address: The logical address is segmented into 32 bits.

    Virtual Address: This address is actually a virtual address if you turn on paging, then the linear address you just got is a virtual address, just a name change

    Physical address: Without paging, the linear address you just got is the physical address. If the page mechanism is used, the physical address is the one that the linear address (virtual address) just got through the page mechanism.

    Here's a clear look through a picture:

    Now let's see how the paging mechanism addresses:

    First segment the linear address (32 bits): 10 bits high; Middle 10; Last 12 bits

    1. Find Page Catalog Items with High 10-Bit Page-to-Page Table of Contents
    2. Then stitch together the middle 10 digits of the page table items to find the page table items in the page table.
    3. The final physical address is the page table entry plus the last 12-bit offset address

    All these operations are called MMU by one of the computer's hardware, memory management unit in Chinese, sometimes PMMU, paging memory management unit. This part is responsible for converting virtual addresses to physical addresses.

    So how do you turn on paging? You need the PG bit of this cr0 register

    Since Linux 0.11 uses a 20-bit physical address, it is 16MB in size

    A page table contains up to 1024 page table entries (i.e., 1024 page tables), a page table contains up to 1024 page table entries (i.e., 1024 pages), and a page is 4 KB (due to 12-bit offset addresses), so 16M address space can be accomplished with 1 page table + 4 page tables.

    4 (Number of Page Tables) * 1024 (Number of Page Table Items) * 4KB (Size of Page) = 16MB

    .org 0x1000
    .org 0x2000
    .org 0x3000
    .org 0x4000
    .org 0x5000

    The starting location is stored in the cr3 register

    First page directory exists: 0x0000 ~ 0x1000

    Four page table entries exist: 0x1000 ~ 0x2000; 0x2000 ~ 0x3000; 0x3000 ~ 0x4000; 0x4000 ~ 0x5000

    Note: This covers the code for the ystem from 0x0000 to 0x5000 because these codes are already running and have not saved anything useful, everything can be overwritten

    Now, let's look at the memory layout, and we're going to go into the main.c function.

    1. Look again at the last entry into main.c

      	pushl $0		# These are the parameters to main :-)
      	pushl $0
      	pushl $0
      	pushl $L6		# return address for main, if it decides to.
      	pushl $_main
      	jmp setup_paging
      	..... Omit a large section here
      • You can see that setup_is entered in jmp Paging first stacks the addresses of 0, 0, 0, L6, and main.c

      • Last setup_executed The top element of the ret stack after paging is the address of main.c

      • So far it's completely in the main.c's code does not end with ret on it, waiting for other applications to call the operating system, but it's not that simple

      Finally, recall what bootsect.s, setup.s, head.s did under boot!!! (Supplement Ha, this is under linux-0.11 operating system yo); Last memory sample on top

Posted by simplyi on Thu, 11 Aug 2022 21:38:03 +0300