;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                              ;;
;; Copyright (C) KolibriOS team 2004-2016. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License    ;;
;;                                                              ;;
;;  Shutdown for Menuet                                         ;;
;;                                                              ;;
;;  Distributed under General Public License                    ;;
;;  See file COPYING for details.                               ;;
;;  Copyright 2003 Ville Turjanmaa                              ;;
;;                                                              ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

$Revision$

align 4
system_shutdown:          ; shut down the system

        cmp     byte [BOOT_VARS+0x9030], 1
        jne     @F
        ret
@@:
        call    stop_all_services

yes_shutdown_param:
; Shutdown other CPUs, if initialized
        cmp     [ap_initialized], 0
        jz      .no_shutdown_cpus
        mov     edi, [LAPIC_BASE]
        add     edi, 300h
        mov     esi, smpt+4
        mov     ebx, [cpu_count]
        dec     ebx
.shutdown_cpus_loop:
        lodsd
        push    esi
        xor     esi, esi
        inc     esi
        shl     eax, 24
        mov     [edi+10h], eax
; assert INIT IPI
        mov     dword [edi], 0C500h
        call    delay_ms
@@:
        test    dword [edi], 1000h
        jnz     @b
; deassert INIT IPI
        mov     dword [edi], 8500h
        call    delay_ms
@@:
        test    dword [edi], 1000h
        jnz     @b
; don't send STARTUP IPI: let other CPUs be in wait-for-startup state
        pop     esi
        dec     ebx
        jnz     .shutdown_cpus_loop
.no_shutdown_cpus:

        cli
        call    IRQ_mask_all

        mov     eax, [OS_BASE + 0x9030]
        cmp     al, SYSTEM_RESTART
        jne     @F

; load kernel.mnt to _CLEAN_ZONE
        mov     ebx, kernel_file_load
        pushad
        call    file_system_lfn
        popad
@@:
        mov     esi, OS_BASE+restart_code_start ; move kernel re-starter to 0x5000:0
        mov     edi, OS_BASE+0x50000
        mov     ecx, (restart_code_end - restart_code_start)/4
        rep movsd

        call    create_trampoline_pgmap
        mov     cr3, eax
        jmp     @F
org $-OS_BASE
@@:

;disable paging

        mov     eax, cr0
        and     eax, 0x7FFFFFFF
        mov     cr0, eax
        mov     eax, cr3
        mov     cr3, eax

        cmp     byte [0x9030], SYSTEM_SHUTDOWN
        jne     no_acpi_power_off

; system_power_off

        mov     ebx, [acpi_fadt_base-OS_BASE]
        cmp     dword [ebx], 'FACP'
        jne     no_acpi_power_off
        mov     esi, [acpi_dsdt_base-OS_BASE]
        cmp     dword [esi], 'DSDT'
        jne     no_acpi_power_off
        mov     eax, [esi+4] ; DSDT length
        sub     eax, 36+4
        jbe     no_acpi_power_off
        add     esi, 36
.scan_dsdt:
        cmp     dword [esi], '_S5_'
        jnz     .scan_dsdt_cont
        cmp     byte [esi+4], 12h ; DefPackage opcode
        jnz     .scan_dsdt_cont
        mov     dl, [esi+6]
        cmp     dl, 4 ; _S5_ package must contain 4 bytes
                      ; ...in theory; in practice, VirtualBox has 2 bytes
        ja      .scan_dsdt_cont
        cmp     dl, 1
        jb      .scan_dsdt_cont
        lea     esi, [esi+7]
        xor     ecx, ecx
        cmp     byte [esi], 0 ; 0 means zero byte, 0Ah xx means byte xx
        jz      @f
        cmp     byte [esi], 0xA
        jnz     no_acpi_power_off
        inc     esi
        mov     cl, [esi]
@@:
        inc     esi
        cmp     dl, 2
        jb      @f
        cmp     byte [esi], 0
        jz      @f
        cmp     byte [esi], 0xA
        jnz     no_acpi_power_off
        inc     esi
        mov     ch, [esi]
@@:
        jmp     do_acpi_power_off
.scan_dsdt_cont:
        inc     esi
        dec     eax
        jnz     .scan_dsdt
        jmp     no_acpi_power_off
do_acpi_power_off:
        mov     edx, [ebx+48]
        test    edx, edx
        jz      .nosmi
        mov     al, [ebx+52]
        out     dx, al
        mov     edx, [ebx+64]
@@:
        in      ax, dx
        test    al, 1
        jz      @b
.nosmi:
        and     cx, 0x0707
        shl     cx, 2
        or      cx, 0x2020
        mov     edx, [ebx+64]
        in      ax, dx
        and     ax, 203h
        or      ah, cl
        out     dx, ax
        mov     edx, [ebx+68]
        test    edx, edx
        jz      @f
        in      ax, dx
        and     ax, 203h
        or      ah, ch
        out     dx, ax
@@:
        jmp     $

no_acpi_power_off:
        jmp     0x50000

align 4
restart_code_start:
org 0x50000

        cmp     byte [0x9030], SYSTEM_RESTART
        jne     @F

        mov     esi, _CLEAN_ZONE-OS_BASE
        mov     edi, 0x10000
        mov     ecx, 0x31000/4
        cld
        rep movsd
@@:

        xor     ebx, ebx
        xor     edx, edx
        xor     ecx, ecx
        xor     esi, esi
        xor     edi, edi
        xor     ebp, ebp
        lidt    [.idt]
        lgdt    [.gdt]
        jmp     8:@f
align 8
.gdt:
; selector 0 - not used
        dw      23
        dd      .gdt
        dw      0
; selector 8 - code from 5000:0000 to 1000:FFFF
        dw      0FFFFh
        dw      0
        db      5
        db      10011011b
        db      00000000b
        db      0
; selector 10h - data from 1000:0000 to 1000:FFFF
        dw      0FFFFh
        dw      0
        db      1
        db      10010011b
        db      00000000b
        db      0
.idt:
        dw 256*4
        dd 0
org $ - 0x50000
use16
@@:
        mov     ax, 10h
        mov     ds, ax
        mov     es, ax
        mov     fs, ax
        mov     gs, ax
        mov     ss, ax

        mov     eax, cr0
        and     eax, not 80000001h
        mov     cr0, eax
        jmp     0x5000:.real_mode

align 4
.real_mode:

; setup stack

        mov     ax, (TMP_STACK_TOP and 0xF0000) shr 4
        mov     ss, ax
        mov     esp, TMP_STACK_TOP and 0xFFFF

;remap IRQs
        mov     al, 0x11
        out     0x20, al
        out     0xA0, al

        mov     al, 0x08
        out     0x21, al
        mov     al, 0x70
        out     0xA1, al

        mov     al, 0x04
        out     0x21, al
        mov     al, 0x02
        out     0xA1, al

        mov     al, 0x01
        out     0x21, al
        out     0xA1, al

        mov     al, 0xB8
        out     0x21, al
        mov     al, 0xBD
        out     0xA1, al

        mov     al, 00110100b
        out     43h, al
        mov     al, 0xFF
        out     40h, al
        out     40h, al

        xor     ax, ax
        mov     ds, ax
        mov     al, [0x9030]
        cmp     al, SYSTEM_RESTART
        je      .restart

        cmp     al, SYSTEM_SHUTDOWN
        je      .APM_PowerOff

        mov     word[0x0472], 0x1234
        jmp     0xF000:0xFFF0

.APM_PowerOff:
        mov     ax, 5304h
        xor     bx, bx
        int     15h
;!!!!!!!!!!!!!!!!!!!!!!!!
        mov     ax, 0x5300
        xor     bx, bx
        int     0x15
        push    ax

        mov     ax, 0x5301
        xor     bx, bx
        int     0x15

        mov     ax, 0x5308
        mov     bx, 1
        mov     cx, bx
        int     0x15

        mov     ax, 0x530E
        xor     bx, bx
        pop     cx
        int     0x15

        mov     ax, 0x530D
        mov     bx, 1
        mov     cx, bx
        int     0x15

        mov     ax, 0x530F
        mov     bx, 1
        mov     cx, bx
        int     0x15

        mov     ax, 0x5307
        mov     bx, 1
        mov     cx, 3
        int     0x15
;!!!!!!!!!!!!!!!!!!!!!!!!
        jmp     $

.restart:

; (hint by Black_mirror)
; We must read data from keyboard port,
; because there may be situation when previous keyboard interrupt is lost
; (due to return to real mode and IRQ reprogramming)
; and next interrupt will not be generated (as keyboard waits for handling)

        mov     cx, 16
@@:
        in      al, 0x64
        test    al, 1
        jz      @F
        in      al, 0x60
        loop    @B
@@:

; bootloader interface
        push    0x1000
        pop     ds
        mov     si, kernel_restart_bootblock
        mov     ax, 'KL'
        jmp     0x1000:0000

align 4
org restart_code_start + $
restart_code_end:

org $+OS_BASE
use32