arch/x86: Include acpi_s3.c support in postcar stage
[coreboot.git] / src / arch / x86 / acpi_s3.c
blob90f2c4ee7bf3f2166685bcc6d9d0bd78d0562f5b
1 /*
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>
17 #include <string.h>
18 #include <arch/acpi.h>
19 #include <cbmem.h>
20 #include <cpu/cpu.h>
21 #include <fallback.h>
22 #include <timestamp.h>
23 #include <program_loading.h>
24 #include <romstage_handoff.h>
25 #include <rules.h>
26 #include <symbols.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;
39 } else {
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 {
73 uint64_t cbmem;
74 uint64_t lowmem;
75 uint64_t size;
76 uint8_t valid;
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)
84 uintptr_t top;
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);
94 size = top - base;
96 /* Cannot extend existing region, should not happen. */
97 if (backup_mem && (backup_mem->size < size))
98 return -1;
100 /* Allocate backup with room for header. */
101 if (!backup_mem) {
102 size_t header_sz = ALIGN_UP(sizeof(*backup_mem),
103 BACKUP_PAGE_SZ);
104 backup_mem = cbmem_add(CBMEM_ID_RESUME, header_sz + size);
105 if (!backup_mem)
106 return -1;
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;
115 return 0;
118 void *acpi_backup_container(uintptr_t base, size_t size)
120 struct resume_backup *backup_mem = cbmem_find(CBMEM_ID_RESUME);
121 if (!backup_mem)
122 return NULL;
124 if (!IS_ALIGNED(base, BACKUP_PAGE_SZ) || !IS_ALIGNED(size,
125 BACKUP_PAGE_SZ))
126 return NULL;
128 if (backup_create_or_update(backup_mem, base, size) < 0)
129 return NULL;
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().
142 if (!backup_mem)
143 return;
145 /* Check that the backup is not done twice. */
146 if (backup_mem->valid)
147 return;
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)
153 return;
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.
165 * DEPRECATED
167 void acpi_prepare_for_resume(void)
169 struct resume_backup *backup_mem = cbmem_find(CBMEM_ID_RESUME);
170 if (!backup_mem)
171 return;
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
184 * for allocation.
186 void acpi_prepare_resume_backup(void)
188 if (!acpi_s3_resume_allowed())
189 return;
191 if (IS_ENABLED(CONFIG_RELOCATABLE_RAMSTAGE))
192 return;
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;
208 size_t size = 0;
210 if (!acpi_s3_resume_allowed()) {
211 printk(BIOS_WARNING, "ACPI: S3 resume not allowed.\n");
212 return;
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;
222 } else {
223 printk(BIOS_WARNING, "ACPI: Backup memory missing. "
224 "No S3 resume.\n");
225 return;
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",
251 *gnvs_address);
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);