2 * This file is part of the coreboot project.
4 * Copyright (C) 2005-2009 coresystems GmbH
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
16 #include <console/console.h>
18 #include <arch/acpi.h>
22 #include <timestamp.h>
23 #include <program_loading.h>
24 #include <romstage_handoff.h>
28 #if ENV_RAMSTAGE || ENV_POSTCAR
30 /* This is filled with acpi_is_wakeup() call early in ramstage. */
31 static int acpi_slp_type
= -1;
33 static void acpi_handoff_wakeup(void)
35 if (acpi_slp_type
< 0) {
36 if (romstage_handoff_is_resume()) {
37 printk(BIOS_DEBUG
, "S3 Resume.\n");
38 acpi_slp_type
= ACPI_S3
;
40 printk(BIOS_DEBUG
, "Normal boot.\n");
41 acpi_slp_type
= ACPI_S0
;
46 int acpi_is_wakeup(void)
48 acpi_handoff_wakeup();
49 /* Both resume from S2 and resume from S3 restart at CPU reset */
50 return (acpi_slp_type
== ACPI_S3
|| acpi_slp_type
== ACPI_S2
);
53 int acpi_is_wakeup_s3(void)
55 acpi_handoff_wakeup();
56 return (acpi_slp_type
== ACPI_S3
);
59 int acpi_is_wakeup_s4(void)
61 acpi_handoff_wakeup();
62 return (acpi_slp_type
== ACPI_S4
);
65 void acpi_fail_wakeup(void)
67 if (acpi_slp_type
== ACPI_S3
|| acpi_slp_type
== ACPI_S2
)
68 acpi_slp_type
= ACPI_S0
;
70 #endif /* ENV_RAMSTAGE */
72 struct resume_backup
{
79 #define BACKUP_PAGE_SZ 4096
81 static int backup_create_or_update(struct resume_backup
*backup_mem
,
82 uintptr_t base
, size_t size
)
86 if (IS_ENABLED(CONFIG_ACPI_HUGE_LOWMEM_BACKUP
)) {
87 base
= CONFIG_RAMBASE
;
88 size
= HIGH_MEMORY_SAVE
;
91 /* Align backup region to complete pages. */
92 top
= ALIGN_UP(base
+ size
, BACKUP_PAGE_SZ
);
93 base
= ALIGN_DOWN(base
, BACKUP_PAGE_SZ
);
96 /* Cannot extend existing region, should not happen. */
97 if (backup_mem
&& (backup_mem
->size
< size
))
100 /* Allocate backup with room for header. */
102 size_t header_sz
= ALIGN_UP(sizeof(*backup_mem
),
104 backup_mem
= cbmem_add(CBMEM_ID_RESUME
, header_sz
+ size
);
108 /* Container starts from boundary after header. */
109 backup_mem
->cbmem
= (uintptr_t)backup_mem
+ header_sz
;
112 backup_mem
->valid
= 0;
113 backup_mem
->lowmem
= base
;
114 backup_mem
->size
= size
;
118 void *acpi_backup_container(uintptr_t base
, size_t size
)
120 struct resume_backup
*backup_mem
= cbmem_find(CBMEM_ID_RESUME
);
124 if (!IS_ALIGNED(base
, BACKUP_PAGE_SZ
) || !IS_ALIGNED(size
,
128 if (backup_create_or_update(backup_mem
, base
, size
) < 0)
131 backup_mem
->valid
= 1;
132 return (void *)(uintptr_t)backup_mem
->cbmem
;
135 void backup_ramstage_section(uintptr_t base
, size_t size
)
137 struct resume_backup
*backup_mem
= cbmem_find(CBMEM_ID_RESUME
);
139 /* For first boot we exit here as CBMEM_ID_RESUME is only
140 * created late in ramstage with acpi_prepare_resume_backup().
145 /* Check that the backup is not done twice. */
146 if (backup_mem
->valid
)
149 /* When we are called from ramstage loader, update header with
150 * properties of the ramstage we will load.
152 if (backup_create_or_update(backup_mem
, base
, size
) < 0)
155 /* Back up the OS-controlled memory where ramstage will be loaded. */
156 memcpy((void *)(uintptr_t)backup_mem
->cbmem
,
157 (void *)(uintptr_t)backup_mem
->lowmem
,
158 (size_t)backup_mem
->size
);
159 backup_mem
->valid
= 1;
162 /* Make backup of low-memory region, relying on the base and size
163 * of the ramstage that was loaded before entry to ACPI S3.
167 void acpi_prepare_for_resume(void)
169 struct resume_backup
*backup_mem
= cbmem_find(CBMEM_ID_RESUME
);
173 /* Back up the OS-controlled memory where ramstage will be loaded. */
174 memcpy((void *)(uintptr_t)backup_mem
->cbmem
,
175 (void *)(uintptr_t)backup_mem
->lowmem
,
176 (size_t)backup_mem
->size
);
177 backup_mem
->valid
= 1;
180 /* Let's prepare the ACPI S3 Resume area now already, so we can rely on
181 * it being there during reboot time. If this fails, ACPI resume will
182 * be disabled. We assume that ramstage does not change while in suspend,
183 * so base and size of the currently running ramstage are used
186 void acpi_prepare_resume_backup(void)
188 if (!acpi_s3_resume_allowed())
191 if (IS_ENABLED(CONFIG_RELOCATABLE_RAMSTAGE
))
194 backup_create_or_update(NULL
, (uintptr_t)_program
, _program_size
);
197 #define WAKEUP_BASE 0x600
199 asmlinkage
void (*acpi_do_wakeup
)(uintptr_t vector
, u32 backup_source
,
200 u32 backup_target
, u32 backup_size
) = (void *)WAKEUP_BASE
;
202 extern unsigned char __wakeup
;
203 extern unsigned int __wakeup_size
;
205 static void acpi_jump_to_wakeup(void *vector
)
207 uintptr_t source
= 0, target
= 0;
210 if (!acpi_s3_resume_allowed()) {
211 printk(BIOS_WARNING
, "ACPI: S3 resume not allowed.\n");
215 if (!IS_ENABLED(CONFIG_RELOCATABLE_RAMSTAGE
)) {
216 struct resume_backup
*backup_mem
= cbmem_find(CBMEM_ID_RESUME
);
217 if (backup_mem
&& backup_mem
->valid
) {
218 backup_mem
->valid
= 0;
219 target
= backup_mem
->lowmem
;
220 source
= backup_mem
->cbmem
;
221 size
= backup_mem
->size
;
223 printk(BIOS_WARNING
, "ACPI: Backup memory missing. "
229 /* Copy wakeup trampoline in place. */
230 memcpy((void *)WAKEUP_BASE
, &__wakeup
, __wakeup_size
);
232 set_boot_successful();
234 timestamp_add_now(TS_ACPI_WAKE_JUMP
);
236 acpi_do_wakeup((uintptr_t)vector
, source
, target
, size
);
239 void __attribute__((weak
)) mainboard_suspend_resume(void)
243 void acpi_resume(void *wake_vec
)
245 if (IS_ENABLED(CONFIG_HAVE_SMI_HANDLER
)) {
246 u32
*gnvs_address
= cbmem_find(CBMEM_ID_ACPI_GNVS_PTR
);
248 /* Restore GNVS pointer in SMM if found */
249 if (gnvs_address
&& *gnvs_address
) {
250 printk(BIOS_DEBUG
, "Restore GNVS pointer to 0x%08x\n",
252 smm_setup_structures((void *)*gnvs_address
, NULL
, NULL
);
256 /* Call mainboard resume handler first, if defined. */
257 mainboard_suspend_resume();
259 post_code(POST_OS_RESUME
);
260 acpi_jump_to_wakeup(wake_vec
);