mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-26 04:22:07 +00:00 
			
		
		
		
	 6d2c298b66
			
		
	
	
		6d2c298b66
		
	
	
	
	
		
			
			As there is no need for a Prekernel on aarch64, the Prekernel code was moved into Kernel itself. The functionality remains the same. SERENITY_KERNEL_AND_INITRD in run.sh specifies a kernel and an inital ramdisk to be used by the emulator. This is needed because aarch64 does not need a Prekernel and the other ones do.
		
			
				
	
	
		
			675 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			675 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
| #include <AK/Platform.h>
 | |
| #include <Kernel/Prekernel/Prekernel.h>
 | |
| 
 | |
| .code32
 | |
| 
 | |
| .section .stack, "aw", @nobits
 | |
| stack_bottom:
 | |
| .skip 32768
 | |
| stack_top:
 | |
| 
 | |
| .global kernel_cmdline
 | |
| kernel_cmdline:
 | |
| .skip 4096
 | |
| 
 | |
| .section .page_tables, "aw", @nobits
 | |
| .align 4096
 | |
| #if ARCH(X86_64)
 | |
| .global boot_pml4t
 | |
| boot_pml4t:
 | |
| .skip 4096
 | |
| #endif
 | |
| .global boot_pdpt
 | |
| boot_pdpt:
 | |
| .skip 4096
 | |
| .global boot_pd0
 | |
| boot_pd0:
 | |
| .skip 4096
 | |
| .global boot_pd0_pts
 | |
| boot_pd0_pts:
 | |
| .skip 4096 * (MAX_KERNEL_SIZE >> 21)
 | |
| .global boot_pd_kernel
 | |
| boot_pd_kernel:
 | |
| .skip 4096
 | |
| .global boot_pd_kernel_pt0
 | |
| boot_pd_kernel_pt0:
 | |
| .skip 4096
 | |
| .global boot_pd_kernel_image_pts
 | |
| boot_pd_kernel_image_pts:
 | |
| .skip 4096 * (MAX_KERNEL_SIZE >> 21)
 | |
| .global boot_pd_kernel_pt1023
 | |
| boot_pd_kernel_pt1023:
 | |
| .skip 4096
 | |
| 
 | |
| .section .text
 | |
| 
 | |
| .global start
 | |
| .type start, @function
 | |
| 
 | |
| .extern init
 | |
| .type init, @function
 | |
| 
 | |
| .global reload_cr3
 | |
| .type reload_cr3, @function
 | |
| 
 | |
| .extern multiboot_info_ptr
 | |
| .type multiboot_info_ptr, @object
 | |
| 
 | |
| /*
 | |
|     construct the following (64-bit PML4T) page table layout:
 | |
|     (the PML4T part is not used for 32-bit x86)
 | |
| 
 | |
| pml4t:
 | |
| 
 | |
|     0: pdpt (0-512GB)
 | |
| 
 | |
| pdpt
 | |
| 
 | |
|     0: boot_pd0 (0-1GB)
 | |
|     1: n/a      (1-2GB)
 | |
|     2: n/a      (2-3GB)
 | |
|     3: n/a      (3-4GB)
 | |
| 
 | |
| boot_pd0 : 512 PDEs
 | |
| 
 | |
|     boot_pd0_pts (0MB - MAX_KERNEL_SIZE) (id 512 4KB pages)
 | |
| 
 | |
| the page tables each contain 512 PTEs that map individual 4KB pages
 | |
| 
 | |
| */
 | |
| 
 | |
| #if ARCH(X86_64)
 | |
| gdt64:
 | |
|     .quad 0
 | |
| gdt64code:
 | |
|     .quad (1<<43) | (1<<44) | (1<<47) | (1<<53) /* executable, code segment, present, 64-bit */
 | |
| .global gdt64ptr
 | |
| gdt64ptr:
 | |
|     .short . - gdt64 - 1
 | |
|     .quad gdt64
 | |
| 
 | |
| .set code64_sel_value, gdt64code - gdt64
 | |
| 
 | |
| .global code64_sel
 | |
| code64_sel:
 | |
| .short code64_sel_value
 | |
| #endif
 | |
| 
 | |
| start:
 | |
|     jmp real_start
 | |
| 
 | |
| /*
 | |
|     param 1: pointer to C string
 | |
|     returns: Length of string (including null byte)
 | |
| */
 | |
| print_no_halt:
 | |
|     pushl %ebp
 | |
|     movl %esp, %ebp
 | |
| 
 | |
|     pushl %esi
 | |
|     pushl %ecx
 | |
| 
 | |
|     movl 8(%ebp), %esi
 | |
| 
 | |
|     mov $0xb8000, %ecx    /* VRAM address. */
 | |
|     mov $0x07, %ah        /* grey-on-black text. */
 | |
| 
 | |
| .Lprint_str_loop:
 | |
|     lodsb                 /* Loads a byte from address at %esi into %al and increments %esi. */
 | |
| 
 | |
|     test %al, %al
 | |
|     jz .Lprint_str_end
 | |
| 
 | |
|     movw %ax, (%ecx)
 | |
|     add $2, %ecx
 | |
| 
 | |
|     jmp .Lprint_str_loop
 | |
| .Lprint_str_end:
 | |
| 
 | |
|     mov %esi, %eax
 | |
|     sub 8(%ebp), %eax
 | |
| 
 | |
|     popl %ecx
 | |
|     popl %esi
 | |
| 
 | |
|     movl %ebp, %esp
 | |
|     popl %ebp
 | |
|     ret
 | |
| 
 | |
| 
 | |
| 
 | |
| /* 
 | |
|     this function assumes that paging is disabled (or everything is mapped 1:1)
 | |
|     param 1: pointer to string ended with null terminator (C string)
 | |
| */
 | |
| print_and_halt:
 | |
| 
 | |
| /*  from now on, we don't really care about booting because we are missing required CPU features such as PAE or long mode.
 | |
|     the flow from now is like so:
 | |
|     1. Copy all necessary parts to low memory section in RAM
 | |
|     2. Jump to that section
 | |
|     3. In that section we do:
 | |
|         a. exit protected mode to pure 16 bit real mode
 | |
|         b. load the "<missing feature> is not supported" String, call the BIOS print to screen service
 | |
|         c. halt
 | |
| */
 | |
| 
 | |
| .equ COPIED_STRING_LOCATION, 0x400
 | |
| .equ GDT_REAL_MODE_LOCATION, 0x45000
 | |
| .equ EXITING_PROTECTED_MODE_CODE_LOCATION, 0x10000
 | |
| .equ REAL_MODE_CODE, 0x500
 | |
| .equ PROTECTED_MODE_16_BIT_CODE, 0x600
 | |
|     movl %esp, %ebp
 | |
|     movl 4(%ebp), %esi
 | |
| 
 | |
|     /* Print string using non-destructive methods */
 | |
|     pushl %esi
 | |
|     call print_no_halt
 | |
|     addl $4, %esp
 | |
| 
 | |
|     /* print_no_halt returns the string length (including null byte) in eax. */
 | |
|     mov %eax, %ecx
 | |
|     movw %cx, (COPIED_STRING_LOCATION)    /* Store string length for later use. */
 | |
| 
 | |
|     /* Copy string into lower memory */
 | |
|     mov 4(%ebp), %esi
 | |
|     mov $COPIED_STRING_LOCATION + 2, %edi
 | |
|     rep movsb
 | |
| 
 | |
|     /* Copy gdt_table_real_mode to low memory section  */
 | |
|     movl $gdt_table_real_mode, %eax
 | |
|     movl $gdt_table_real_mode_end, %ebx
 | |
| 
 | |
|     movl %ebx, %ecx
 | |
|     sub %eax, %ecx
 | |
|     mov %eax, %esi          /* source address of the code */
 | |
|     mov $GDT_REAL_MODE_LOCATION, %edi     /* destination address of the code */
 | |
|     rep movsb
 | |
| 
 | |
|     /* Copy protected_mode_16_bit to real_mode to low memory section  */
 | |
|     movl $protected_mode_16_bit, %eax
 | |
|     movl $real_mode, %ebx
 | |
| 
 | |
|     movl %ebx, %ecx
 | |
|     sub %eax, %ecx
 | |
|     mov %eax, %esi          /* source address of the code */
 | |
|     mov $PROTECTED_MODE_16_BIT_CODE, %edi     /* destination address of the code */
 | |
|     rep movsb
 | |
| 
 | |
|     /* Copy real_mode to end_of_print_and_halt_function to low memory section  */
 | |
|     movl $real_mode, %eax
 | |
|     movl $end_of_print_and_halt_function, %ebx
 | |
| 
 | |
|     movl %ebx, %ecx
 | |
|     sub %eax, %ecx
 | |
|     mov %eax, %esi          /* source address of the code */
 | |
|     mov $REAL_MODE_CODE, %edi     /* destination address of the code */
 | |
|     rep movsb
 | |
| 
 | |
| 
 | |
|     /* Copy all opcodes from exiting_real_mode label to protected_mode_16_bit label to low memory RAM  */
 | |
|     movl $exiting_real_mode, %eax
 | |
|     movl $protected_mode_16_bit, %ebx
 | |
| 
 | |
|     movl %ebx, %ecx
 | |
|     sub %eax, %ecx
 | |
|     mov %eax, %esi          /* source address of the code */
 | |
|     mov $EXITING_PROTECTED_MODE_CODE_LOCATION, %edi     /* destination address of the code */
 | |
|     pushl %edi
 | |
|     rep movsb
 | |
|     popl %edi
 | |
|     pushl %edi
 | |
|     ret
 | |
| 
 | |
| gdt_table_real_mode:
 | |
|     .quad 0             /* Empty entry */
 | |
| 
 | |
| 	.short 0xffff
 | |
| 	.short 0
 | |
|     .byte 0
 | |
|     .byte 0b10011010
 | |
|     .byte 0b00001111
 | |
|     .byte 0x0
 | |
| 
 | |
|     .short 0xffff
 | |
| 	.short 0
 | |
|     .byte 0
 | |
|     .byte 0b10010010
 | |
|     .byte 0b00001111
 | |
|     .byte 0x0
 | |
| gdt_table_real_mode_end:
 | |
| 
 | |
| no_long_mode_string:
 | |
|     .asciz "Your computer does not support long mode (64-bit mode). Halting!"
 | |
| 
 | |
| no_pae_string:
 | |
|     .asciz "Your computer does not support PAE. Halting!"
 | |
| 
 | |
| pentium_m_forcepae_string:
 | |
|     .asciz "Intel Pentium M detected. Assuming present but unadvertised PAE support."
 | |
| 
 | |
| kernel_image_too_big_string:
 | |
|     .asciz "Error: Kernel Image too big for memory slot. Halting!"
 | |
| 
 | |
| /*
 | |
|     This part is completely standalone - it doesn't involve any location from this
 | |
|     near code. It uses arbitrary locations in the low memory section of the RAM.
 | |
|     We don't really worry about where are these locations, because we only want to quickly
 | |
|     print a string and halt.
 | |
| */
 | |
| .code32
 | |
| exiting_real_mode:
 | |
| 
 | |
|     /* Build IDT pointer and load it */
 | |
|     mov $0x50000, %eax
 | |
|     pushl %eax
 | |
|     movl $0x3ff, 0(%eax)
 | |
|     add $2, %eax
 | |
|     movl $0, 0(%eax)
 | |
|     popl %eax
 | |
|     lidt (%eax)
 | |
| 
 | |
|     /* Build GDT pointer and load it */
 | |
|     mov $0x40000, %eax
 | |
|     pushl %eax
 | |
|     movl $32, 0(%eax)
 | |
|     add $2, %eax
 | |
|     movl $GDT_REAL_MODE_LOCATION, 0(%eax)
 | |
|     popl %eax
 | |
|     lgdt (%eax)
 | |
| 
 | |
|     /* far jump to protected_mode_16_bit in 0x5000 */
 | |
|     pushw $8
 | |
|     push $PROTECTED_MODE_16_BIT_CODE
 | |
|     lret
 | |
|     hlt
 | |
| 
 | |
| .code16
 | |
| protected_mode_16_bit:
 | |
|     xor %eax, %eax
 | |
|     movl $0x10, %eax
 | |
|     movw %ax, %ds
 | |
|     and $0xFE, %al       /* switch to pure real mode */
 | |
|     mov %eax, %cr0
 | |
|     mov $0x10, %eax
 | |
|     movl %eax, %cr0
 | |
| 
 | |
|     pushw $0
 | |
|     push $REAL_MODE_CODE
 | |
|     lret
 | |
|     hlt
 | |
| 
 | |
| real_mode:
 | |
|     movw $0x7000, %ax
 | |
|     movl $0x0000, %esp
 | |
|     movw %ax, %ss
 | |
| 
 | |
|     xor %ax, %ax
 | |
|     movw %ax, %ds
 | |
|     movw %ax, %es
 | |
|     movw %ax, %fs
 | |
|     movw %ax, %gs
 | |
| 
 | |
|     mov $0x3, %ax
 | |
|     int $0x10
 | |
| 
 | |
|     movb $0x13, %ah
 | |
|     movb $0x0, %bh
 | |
|     movb $0xf, %bl
 | |
|     movw (COPIED_STRING_LOCATION), %cx
 | |
|     movw $0, %dx
 | |
|     movw $COPIED_STRING_LOCATION + 2, %bp
 | |
|     int $0x10
 | |
| 
 | |
|     movl $0xdeadcafe, %ebx
 | |
|     cli
 | |
|     hlt
 | |
| end_of_print_and_halt_function:
 | |
| 
 | |
| .code32
 | |
| real_start:
 | |
|     cli
 | |
|     cld
 | |
|     mov $end_of_prekernel_image, %esi
 | |
|     cmp $MAX_KERNEL_SIZE, %esi
 | |
|     jbe kernel_not_too_large
 | |
|     
 | |
|     movl $kernel_image_too_big_string, %esi
 | |
|     pushl %esi
 | |
|     call print_and_halt
 | |
|     /* We should not return, but just in case, halt */
 | |
|     hlt
 | |
| 
 | |
| kernel_not_too_large:
 | |
|     /* test for PAE presence, save the most important registers from corruption */
 | |
|     pushl %eax
 | |
|     pushl %edx
 | |
|     pushl %ebx
 | |
| 
 | |
|     movl $0x1, %eax       /* PAE presence is in CPUID input 0x1 */
 | |
|     cpuid
 | |
|     testl $(1 << 6), %edx /* Test if the PAE-bit, which is bit 6, is set in the edx register. */
 | |
|     jnz pae_supported     /* If the bit is not set, there is no PAE capability. */
 | |
| 
 | |
|     /* We might have a Pentium M, which supports PAE but doesn't advertise it. */
 | |
|     call is_pentium_m_with_hidden_pae
 | |
|     test %eax, %eax
 | |
|     jz .Lskip_forcepae
 | |
| 
 | |
|     /* Print a warning message, but continue. */
 | |
|     pushl $pentium_m_forcepae_string
 | |
|     call print_no_halt
 | |
|     subl $4, %esp
 | |
|     jmp pae_supported
 | |
| .Lskip_forcepae:
 | |
| 
 | |
|     /* Since there is no PAE capability, halt with an error message */
 | |
|     movl $no_pae_string, %esi
 | |
|     pushl %esi
 | |
|     call print_and_halt
 | |
|     /* We should not return, but just in case, halt */
 | |
|     hlt
 | |
| 
 | |
| 
 | |
| #if ARCH(X86_64)
 | |
| pae_supported:
 | |
|     movl $0x80000001, %eax
 | |
|     cpuid
 | |
|     testl $(1 << 29), %edx   /* Test if the LM-bit, which is bit 29, is set in the edx register. */
 | |
|     jnz long_mode_supported             /* If LM-bit is not enabled, there is no long mode. */
 | |
| 
 | |
|     /* Since there is no long mode, halt with an error message */
 | |
|     movl $no_long_mode_string, %esi
 | |
|     pushl %esi
 | |
|     call print_and_halt
 | |
|     /* We should not return, but just in case, halt */
 | |
|     hlt
 | |
| 
 | |
| 
 | |
| /* If both PAE and long mode is supported, continue with booting the system */
 | |
| 
 | |
| long_mode_supported:
 | |
|     /* restore the pushed registers and continue with booting */
 | |
|     popl %ebx
 | |
|     popl %edx
 | |
|     popl %eax
 | |
| #else
 | |
| /* If PAE is supported, continue with booting the system */
 | |
| 
 | |
| pae_supported:
 | |
|     /* restore the pushed registers and continue with booting */
 | |
|     popl %ebx
 | |
|     popl %edx
 | |
|     popl %eax
 | |
| #endif
 | |
| 
 | |
|     /* We don't know where the bootloader might have put the command line.
 | |
|      * It might be at an inconvenient location that we're not about to map,
 | |
|      * so let's just copy it to a convenient location while we have the whole
 | |
|      * memory space identity-mapped anyway. :^)
 | |
|      */
 | |
| 
 | |
|     movl %ebx, %esi
 | |
|     addl $16, %esi
 | |
|     movl (%esi), %esi
 | |
|     movl $1024, %ecx
 | |
|     movl $kernel_cmdline, %edi
 | |
|     rep movsl
 | |
| 
 | |
| #if ARCH(X86_64)
 | |
|     /* clear pml4t */
 | |
|     movl $boot_pml4t, %edi
 | |
|     movl $1024, %ecx
 | |
|     xorl %eax, %eax
 | |
|     rep stosl
 | |
| 
 | |
|     /* set up pml4t[0] */
 | |
|     movl $boot_pml4t, %edi
 | |
|     movl $boot_pdpt, 0(%edi)
 | |
|     /* R/W + Present */
 | |
|     orl $0x3, 0(%edi)
 | |
| #endif
 | |
| 
 | |
|     /* clear pdpt */
 | |
|     movl $boot_pdpt, %edi
 | |
|     movl $1024, %ecx
 | |
|     xorl %eax, %eax
 | |
|     rep stosl
 | |
| 
 | |
|     /* set up pdpt[0] and pdpt[3] */
 | |
|     movl $boot_pdpt, %edi
 | |
| #if ARCH(X86_64)
 | |
|     movl $(boot_pd0 + 3), 0(%edi)
 | |
| #else
 | |
|     movl $(boot_pd0 + 1), 0(%edi)
 | |
| #endif
 | |
| 
 | |
|     /* clear pd0 */
 | |
|     movl $boot_pd0, %edi
 | |
|     movl $1024, %ecx
 | |
|     xorl %eax, %eax
 | |
|     rep stosl
 | |
| 
 | |
|     /* clear pd0's PTs */
 | |
|     movl $boot_pd0_pts, %edi
 | |
|     movl $(1024 * (MAX_KERNEL_SIZE >> 21)), %ecx
 | |
|     xorl %eax, %eax
 | |
|     rep stosl
 | |
| 
 | |
|     /* add boot_pd0_pts to boot_pd0 */
 | |
|     movl $(MAX_KERNEL_SIZE >> 21), %ecx
 | |
|     movl $boot_pd0, %edi
 | |
|     movl $boot_pd0_pts, %eax
 | |
| 
 | |
| 1:
 | |
|     movl %eax, 0(%edi)
 | |
|     /* R/W + Present */
 | |
|     orl $0x3, 0(%edi)
 | |
|     addl $8, %edi
 | |
|     addl $4096, %eax
 | |
|     loop 1b
 | |
| 
 | |
|     /* identity map the 0MB to MAX_KERNEL_SIZE range */
 | |
|     movl $(512 * (MAX_KERNEL_SIZE >> 21)), %ecx
 | |
|     movl $boot_pd0_pts, %edi
 | |
|     xorl %eax, %eax
 | |
| 
 | |
| 1:
 | |
|     movl %eax, 0(%edi)
 | |
|     /* R/W + Present */
 | |
|     orl $0x3, 0(%edi)
 | |
|     addl $8, %edi
 | |
|     addl $4096, %eax
 | |
|     loop 1b
 | |
| 
 | |
| #if ARCH(X86_64)
 | |
|     /* point CR3 to PML4T */
 | |
|     movl $boot_pml4t, %eax
 | |
| #else
 | |
|     /* point CR3 to PDPT */
 | |
|     movl $boot_pdpt, %eax
 | |
| #endif
 | |
| 
 | |
|     movl %eax, %cr3
 | |
| 
 | |
|     /* enable PAE + PSE */
 | |
|     movl %cr4, %eax
 | |
|     orl $0x60, %eax
 | |
|     movl %eax, %cr4
 | |
| 
 | |
| #if ARCH(X86_64)
 | |
| 1:
 | |
|     /* Enter Long-mode! ref(https://wiki.osdev.org/Setting_Up_Long_Mode)*/
 | |
|     mov $0xC0000080, %ecx           /* Set the C-register to 0xC0000080, which is the EFER MSR.*/
 | |
|     rdmsr                           /* Read from the model-specific register.*/
 | |
|     or $(1 << 8), %eax              /* Set the LM-bit which is the 9th bit (bit 8).*/
 | |
|     wrmsr                           /* Write to the model-specific register.*/
 | |
| #endif
 | |
| 
 | |
|     /* enable PG */
 | |
|     movl %cr0, %eax
 | |
|     orl $0x80000000, %eax
 | |
|     movl %eax, %cr0
 | |
| 
 | |
|     /* set up stack */
 | |
|     mov $stack_top, %esp
 | |
|     and $-16, %esp
 | |
| 
 | |
| #if ARCH(X86_64)
 | |
|     /* Now we are in 32-bit compatibility mode, We still need to load a 64-bit GDT */
 | |
|     mov $gdt64ptr, %eax
 | |
|     lgdt (%eax)
 | |
|     ljmpl $code64_sel_value, $1f
 | |
| 
 | |
| .code64
 | |
| 1:
 | |
|     movl %ebx, %ebx
 | |
|     movq %rbx, multiboot_info_ptr
 | |
| 
 | |
|     mov $0, %ax
 | |
|     mov %ax, %ss
 | |
|     mov %ax, %ds
 | |
|     mov %ax, %es
 | |
|     mov %ax, %fs
 | |
|     mov %ax, %gs
 | |
| #else
 | |
|     movl %ebx, multiboot_info_ptr
 | |
| #endif
 | |
| 
 | |
|     call reload_cr3
 | |
|     call init
 | |
| 
 | |
|     cli
 | |
| loop:
 | |
|     hlt
 | |
|     jmp loop
 | |
| 
 | |
| reload_cr3:
 | |
| #if ARCH(X86_64)
 | |
|     pushq %rax
 | |
|     mov %cr3, %rax
 | |
|     mov %rax, %cr3
 | |
|     popq %rax
 | |
| #else
 | |
|     pushl %eax
 | |
|     movl %cr3, %eax
 | |
|     movl %eax, %cr3
 | |
|     popl %eax
 | |
| #endif
 | |
|     ret
 | |
| 
 | |
| 
 | |
| .code32
 | |
| 
 | |
| /* Determines if the CPU is made by Intel */
 | |
| is_intel_cpu:
 | |
|     pushl %ebp
 | |
|     movl %esp, %ebp
 | |
| 
 | |
|     pushl %ebx
 | |
| 
 | |
|     xorl %eax, %eax
 | |
|     cpuid
 | |
| 
 | |
|     xorl %eax, %eax
 | |
|     cmpl $0x756e6547, %ebx /* "Genu" */
 | |
|     jnz .Lis_intel_cpu_end
 | |
|     cmpl $0x49656e69, %edx /* "ineI" */
 | |
|     jnz .Lis_intel_cpu_end
 | |
|     cmpl $0x6c65746e, %ecx /* "ntel" */
 | |
|     jnz .Lis_intel_cpu_end
 | |
| 
 | |
|     movl $1, %eax
 | |
| 
 | |
| .Lis_intel_cpu_end:
 | |
|     popl %ebx
 | |
| 
 | |
|     movl %ebp, %esp
 | |
|     popl %ebp
 | |
| 
 | |
|     ret
 | |
| 
 | |
| /* Fetches the CPU family (eax) and model (edx) */
 | |
| get_cpu_model_family:
 | |
|     pushl %ebp
 | |
|     movl %esp, %ebp
 | |
| 
 | |
|     pushl %ebx
 | |
| 
 | |
|     movl $0x1, %eax
 | |
|     cpuid
 | |
|     movl %eax, %ebx
 | |
|     movl %eax, %ecx
 | |
|     movl %eax, %edx
 | |
| 
 | |
|     /* Bit 8 - 11: Processor Family */
 | |
|     shrl $8, %eax
 | |
|     andl $0xf, %eax
 | |
| 
 | |
|     /* Bit 4 - 7: Processor Model */
 | |
|     shrl $4, %edx
 | |
|     andl $0xf, %edx
 | |
| 
 | |
|     /* Bit 16 - 19: Extended Model ID */
 | |
|     /* (only applies if Family is 6 or 15) */
 | |
|     cmpl $6, %eax
 | |
|     jz .Ldo_ext_model
 | |
|     cmpl $15, %eax
 | |
|     jz .Ldo_ext_model
 | |
|     jmp .Lskip_ext_model
 | |
| .Ldo_ext_model:
 | |
|     shrl $16, %ebx
 | |
|     andl $0xf, %ebx
 | |
|     shll $4, %ebx
 | |
|     addl %ebx, %edx
 | |
| .Lskip_ext_model:
 | |
| 
 | |
|     /* Bit 20 - 27: Extended Family */
 | |
|     /* (only applies if Family is 15) */
 | |
|     cmpl $15, %eax
 | |
|     jnz .Lskip_ext_family
 | |
|     shrl $20, %ecx
 | |
|     andl $0xff, %ecx
 | |
|     addl %ecx, %eax
 | |
| .Lskip_ext_family:
 | |
| 
 | |
|     popl %ebx
 | |
| 
 | |
|     movl %ebp, %esp
 | |
|     popl %ebp
 | |
| 
 | |
|     ret
 | |
| 
 | |
| /* Determines if the CPU is an Intel Pentium M with hidden PAE flag. */
 | |
| is_pentium_m_with_hidden_pae:
 | |
|     pushl %ebp
 | |
|     movl %esp, %ebp
 | |
| 
 | |
|     /* Check the manufacturer string. */
 | |
|     call is_intel_cpu
 | |
|     testl %eax, %eax
 | |
|     jz .Lis_pentium_m_end
 | |
| 
 | |
|     /* Check the processor model. */
 | |
|     call get_cpu_model_family
 | |
|     movl %eax, %ecx       /* Free up eax for the return value. */
 | |
|     xorl %eax, %eax
 | |
| 
 | |
|     cmpl $6, %ecx         /* Family 6: Big Cores */
 | |
|     jnz .Lis_pentium_m_end
 | |
| 
 | |
|     cmpl $9, %edx         /* Model 9: Pentium M (Banias) */
 | |
|     jz .Lpass_model_check
 | |
|     cmpl $13, %edx        /* Model 13: Pentium M (Dothan) */
 | |
|     jz .Lpass_model_check
 | |
|     jmp .Lis_pentium_m_end
 | |
| .Lpass_model_check:
 | |
| 
 | |
|     /* We are a Pentium M. */
 | |
|     movl $1, %eax
 | |
| 
 | |
| .Lis_pentium_m_end:
 | |
|     movl %ebp, %esp
 | |
|     popl %ebp
 | |
| 
 | |
|     ret
 |