xref: /kvm-unit-tests/x86/s3.c (revision 2c96b77ec9d3b1fcec7525174e23a6240ee05949)
1 #include "libcflat.h"
2 #include "x86/acpi.h"
3 #include "asm/io.h"
4 
5 static u32* find_resume_vector_addr(void)
6 {
7     struct facs_descriptor_rev1 *facs = find_acpi_table_addr(FACS_SIGNATURE);
8     if (!facs)
9         return 0;
10     printf("FACS is at %p\n", facs);
11     return &facs->firmware_waking_vector;
12 }
13 
14 #define RTC_SECONDS_ALARM       1
15 #define RTC_MINUTES_ALARM       3
16 #define RTC_HOURS_ALARM         5
17 #define RTC_ALARM_DONT_CARE     0xC0
18 
19 #define RTC_REG_A               10
20 #define RTC_REG_B               11
21 #define RTC_REG_C               12
22 
23 #define REG_A_UIP               0x80
24 #define REG_B_AIE               0x20
25 
26 static inline int rtc_in(u8 reg)
27 {
28     outb(reg, 0x70);
29     return inb(0x71);
30 }
31 
32 static inline void rtc_out(u8 reg, u8 val)
33 {
34     outb(reg, 0x70);
35     outb(val, 0x71);
36 }
37 
38 extern char resume_start, resume_end;
39 
40 int main(int argc, char **argv)
41 {
42 	struct fadt_descriptor_rev1 *fadt = find_acpi_table_addr(FACP_SIGNATURE);
43 	volatile u32 *resume_vector_ptr = find_resume_vector_addr();
44 	char *addr, *resume_vec = (void*)0x1000;
45 
46 	*resume_vector_ptr = (u32)(ulong)resume_vec;
47 
48 	printf("resume vector addr is %p\n", resume_vector_ptr);
49 	for (addr = &resume_start; addr < &resume_end; addr++)
50 		*resume_vec++ = *addr;
51 	printf("copy resume code from %p\n", &resume_start);
52 
53 	printf("PM1a event registers at %" PRIx32 "\n", fadt->pm1a_evt_blk);
54 	outw(0x400, fadt->pm1a_evt_blk + 2);
55 
56 	/* Setup RTC alarm to wake up on the next second.  */
57 	while ((rtc_in(RTC_REG_A) & REG_A_UIP) == 0);
58 	while ((rtc_in(RTC_REG_A) & REG_A_UIP) != 0);
59 	rtc_in(RTC_REG_C);
60 	rtc_out(RTC_SECONDS_ALARM, RTC_ALARM_DONT_CARE);
61 	rtc_out(RTC_MINUTES_ALARM, RTC_ALARM_DONT_CARE);
62 	rtc_out(RTC_HOURS_ALARM, RTC_ALARM_DONT_CARE);
63 	rtc_out(RTC_REG_B, rtc_in(RTC_REG_B) | REG_B_AIE);
64 
65 	*(volatile int*)0 = 0;
66 	asm volatile("outw %0, %1" :: "a"((short)0x2400), "d"((short)fadt->pm1a_cnt_blk):"memory");
67 	while(1)
68 		*(volatile int*)0 = 1;
69 
70 	return 0;
71 }
72 
73 asm (
74         ".global resume_start\n"
75 	".global resume_end\n"
76 	".code16\n"
77 	"resume_start:\n"
78 	"mov 0x0, %eax\n"
79 	"mov $0xf4, %dx\n"
80 	"out %eax, %dx\n"
81 	"1: hlt\n"
82 	"jmp 1b\n"
83 	"resume_end:\n"
84 #ifdef __i386__
85 	".code32\n"
86 #else
87 	".code64\n"
88 #endif
89     );
90