blob: 305b9a0fb69579d26ca351f773ee71032dc5d74a [file] [log] [blame]
/* Copyright 2020 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "registers.h"
.equ PMU_STATUS_PG, (1 << 4)
.equ PMU_STATUS_PG_AON, (1 << 5)
.equ PMU_PG_EN, (1 << 0)
.equ PMU_PG_EXIT_COMPLETE, (1 << 8)
.equ FWST_AON_STATE_MASK, (0x7 << 24)
.equ FWST_AON_STATE_HALT, (0x2 << 24)
.equ EFLAGS_NT, (1 << 14)
.equ TSS_ESP0_OFFSET, 0x4
.equ TSS_LDT_SEG_SEL_OFFSET, 0x60
.equ AON_CS, 0x4
.equ AON_DS, 0xc
.global ipapg
ipapg:
push %ebp
push %edi
push %esi
push %ebx
mov %cr0, %eax
push %eax
mov %cr4, %eax
push %eax
clts
#write down return address for ROM
movl $(PMU_STATUS_PG|PMU_STATUS_PG_AON), PMU_STATUS_REG_ADDR
movl $out_of_pg, %eax
movl %eax, PMU_SCRATCHPAD0_REG_ADDR
movl (%eax), %eax
movl %eax, PMU_SCRATCHPAD1_REG_ADDR
#enable IPAPG, we will actually enter PG on the next halt
movl $(PMU_PG_EN|PMU_PG_EXIT_COMPLETE), PMU_PG_EN_REG_ADDR
#save esp so we can restore stack after returning from ROM
lea aon_tss, %eax
movl %esp, TSS_ESP0_OFFSET(%eax)
sti
hlt
#unreachable
#got out of IPAPG, jumped here from ROM if there was no abort condition
out_of_pg:
cli
#restore stack
lea aon_tss, %eax
movl TSS_ESP0_OFFSET(%eax), %esp
#set the nested task bit in eflags
pushfl
orl $EFLAGS_NT, (%esp)
popfl
clts
fninit
#restore non-volatile registers and CR0 & CR4
pop %eax
mov %eax, %cr4
pop %eax
mov %eax, %cr0
pop %ebx
pop %esi
pop %edi
pop %ebp
#check if we're indeed after IPAPG exit
testl $PMU_STATUS_PG, PMU_STATUS_REG_ADDR
jz after_pg
#we didn't go through ROM, clear PG_EN bit and return an abort condition to AON
movl $0, PMU_PG_EN_REG_ADDR
movl $0, %eax
jmp return_to_aon
after_pg:
#return to caller that we got ouf of PG
movl $1, %eax
return_to_aon:
movl $0, PMU_STATUS_REG_ADDR
#return to AON task (still with ROM GDT and segments in case of PG exit)
ret
.global pg_exit_save_ctx
pg_exit_save_ctx:
sgdtl mainfw_gdt
str tr
ret
.global pg_exit_restore_ctx
pg_exit_restore_ctx:
#load RTOS GDT and AON task
lgdtl mainfw_gdt
ltr tr
#load AON LDT and segments
lea aon_tss, %eax
lldt TSS_LDT_SEG_SEL_OFFSET(%eax)
mov $AON_DS, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss
ljmpl $AON_CS, $cont
cont:
nop
ret