;//--------------------------------------------------------------- ;// mphello.s ;// ;// This program employs Intel's MP Initialization Protocal ;// to awaken any auxilliary processors that may be present ;// and allows each processor to display its APIC Local-ID. ;// ;// assemble with: $ as86 mphello.s -b mphello.b ;// install using: $ dd if=mphello.b of=/dev/fd0 seek=1 ;// ;// NOTE: This code begins executing with CS:IP = 1000:0002. ;// ;// programmer: ALLAN CRUSE ;// written on: 06 MAY 2004 ;//--------------------------------------------------------------- .SECT .TEXT ;----------------------------------------------------------------- .WORD 0xABCD ; programming signature ;----------------------------------------------------------------- main: mov ax, cs ; address this segment mov ds, ax ; with DS register pop dword return_address ; store return-address mov ss, ax ; adjust SS register lea esp, tos0 ; and set new stacktop call setup_timer_channel2 call allow_4GB_addressing call display_APIC_LocalID call broadcast_AP_startup call delay_until_APs_halt push dword return_address ; recover our exit-address retf ; exit to program launcher ;----------------------------------------------------------------- return_address: .LONG 0 ; to store exit-address ;----------------------------------------------------------------- ; EQUATES realCS EQU 0x1000 ; segment-address of code limGDT EQU 0x000F ; allocates 2 descriptors sel_fs EQU 0x0008 ; flat-segment selector ;----------------------------------------------------------------- .ALIGN 8 theGDT: .WORD 0x0000, 0x0000, 0x0000, 0x0000 ; null descriptor .WORD 0xFFFF, 0x0000, 0x9200, 0x008F ; flat descriptor ;----------------------------------------------------------------- msg: .ASCII "Hello from processor " ; message from processor pid: .ASCII " \n\r" ; buffer for cpu LocalID len: .WORD * - msg ; length of message text att: .BYTE 0x0B ; display attribute byte ;----------------------------------------------------------------- mutex: .WORD 1 ; mutual-exclusion flag n_cpu: .WORD 0 ; count of awakened APs n_fin: .WORD 0 ; count of finished APs new_SS: .WORD 0x2000 ; stack segment-address ;----------------------------------------------------------------- ;----------------------------------------------------------------- allow_4GB_addressing: pushf push ds push #0x0001 ; push base-address hiword push #theGDT ; push base-address loword push #limGDT ; push GDT's segment-limit lgdt [esp] ; load GDTR register-image add sp, #6 ; discard three stackwords cli ; no device interrupts mov eax, cr0 ; get machine status bts eax, #0 ; set PE-bit to 1 mov cr0, eax ; enable protection mov ax, #sel_fs ; address 4GB data-segment mov ds, ax ; with the DS register mov eax, cr0 ; get machine status btr eax, #0 ; reset PE-bit to 0 mov cr0, eax ; disable protection pop ds popf ret ; back to main routine ;----------------------------------------------------------------- delay_EAX_micro_secs: pushad mov ecx, eax ; number of microseconds mov eax, #1000000 ; microseconds-per-second xor edx, edx ; is extended to quadword div ecx ; division by double-word mov ecx, eax ; input-frequency divisor mov eax, #1193182 ; timer's input-frequency xor edx, edx ; is extended to quadword div ecx ; division by double-word out #0x42, al ; transfer to Latch LSB xchg al, ah ; LSB swapped with MSB out #0x42, al ; transfer to Latch MSB .T0: in al, #0x61 ; check PORT_B settings test al, #0x20 ; has counter2 expired? jz .T0 ; no, continue polling popad ret ;----------------------------------------------------------------- delay_until_APs_halt: .W0: mov ax, n_cpu ; number of APs awoken sub ax, n_fin ; less number finished jne .W0 ; spin unless all done ret ;----------------------------------------------------------------- ;----------------------------------------------------------------- broadcast_AP_startup: push ds xor ax, ax ; address flat segment mov ds, ax ; with DS register ; issue an 'INIT' Inter-Processor Interrupt command mov eax, #0x000C4500 ; broadcast INIT_IPI mov [0xFEE00300], eax ; to all-except-self .B0: bt dword [0xFEE00300], #12 ; command in progress? jc .B0 ; yes, spin till done ; do ten-millisecond delay, allow time for APs to awaken mov eax, #10000 ; number of microseconds call delay_EAX_micro_secs ; for a programmed delay ; Finish the Intel 'MP Initialization Protocol' mov ecx, #2 ; issue 'Startup' twice nxIPI: ; issue a 'Startup' Inter-Processor Interrupt command mov eax, #0x000C4611 ; broadcast Startup_IPI mov [0xFEE00300], eax ; to all-except-self .B1: bt dword [0xFEE00300], #12 ; command in progress? jc .B1 ; yes, spin till done ; delay for 200-microseconds mov eax, #200 ; number of microseconds call delay_EAX_micro_secs ; for a programmed delay loop nxIPI ; again to finish protocol pop ds ret ;----------------------------------------------------------------- initAP: cli mov ax, cs ; address this segment mov ds, ax ; with DS register mov es, ax ; also ES register lock ; insure 'atomic' update inc word [n_cpu] ; increment count of APs ; setup an exclusive stack-area for this processor mov ax, #0x1000 ; paragraphs in segment xadd new_SS, ax ; 'atomic' xchg-and-add mov ss, ax ; segment address in SS xor esp, esp ; top-of-stack into ESP call allow_4GB_addressing ; adjust DS's seg-limit call display_APIC_LocalID ; display this CPU's ID ; put this processor to sleep lock ; insure 'atomic' update inc word [n_fin] ; increment count of APs freeze: cli ; do not awaken this CPU hlt ; 'fetch-execute' ceases jmp freeze ; just-in-case of an NMI ;----------------------------------------------------------------- ;----------------------------------------------------------------- display_APIC_LocalID: push ds ; preserve DS contents xor ax, ax ; address flat segment mov ds, ax ; with DS register mov eax, [0xFEE00020] ; APIC Local-ID register pop ds ; restore segmt-address ; acquire the spinlock -- allow only one CPU at a time spin: bt mutex, #0 ; test: is spinlock free? jnc spin ; no, wait till released lock ; insure 'atomic' update btr mutex, #0 ; acquire the spinlock jnc spin ; spin until successful lea di, pid ; address shared buffer mov ecx, #2 ; number of hex digits .L0: rol eax, #4 ; next nybble into AL and al, #0x0F ; isolate the nybble cmp al, #10 ; -- Lopez' algorithm -- sbb al, #0x69 ; -- for converting -- das ; -- to a hex numeral -- mov [di], al ; write digit to buffer inc di ; advance buffer pointer loop .L0 ; again for other nybble mov ah, #0x0F ; display-page into BH int 0x10 ; request BIOS service mov ah, #0x03 ; cursor-locn in DH,DL int 0x10 ; request BIOS service lea bp, msg ; setup message offset mov cx, len ; setup message length mov bl, att ; setup character color mov ax, #0x1301 ; write_string service int 0x10 ; request BIOS service ; release spinlock -- finished with 'non-reentrant' code lock ; insure 'atomic' update bts mutex, #0 ; release the spinlock ret ;----------------------------------------------------------------- setup_timer_channel2: ; enable the 8254 Channel-2 counter in al, #0x61 ; get PORT_B settings and al, #0xFD ; turn PC speaker off or al, #0x01 ; turn on GATE2 input out #0x61, al ; output new settings ; program Channel-2 for one-shot countdown mov al, #0xB0 ; chn2, r/w LSB/MSB out #0x43, al ; issue PIT command ret ;----------------------------------------------------------------- .ORG 4096 ; alignment at next page ;----------------------------------------------------------------- tos0: jmpf #initAP, #realCS ; initialize awakened AP ;----------------------------------------------------------------- END