xref: /kvm-unit-tests/x86/s3.c (revision 798b0d956df84b860bb134eb15518c70c89b4ad4)
1c1bc863aSGleb Natapov #include "libcflat.h"
2c1bc863aSGleb Natapov 
3c1bc863aSGleb Natapov struct rsdp_descriptor {        /* Root System Descriptor Pointer */
4c1bc863aSGleb Natapov     u64 signature;              /* ACPI signature, contains "RSD PTR " */
5c1bc863aSGleb Natapov     u8  checksum;               /* To make sum of struct == 0 */
6c1bc863aSGleb Natapov     u8  oem_id [6];             /* OEM identification */
7c1bc863aSGleb Natapov     u8  revision;               /* Must be 0 for 1.0, 2 for 2.0 */
8c1bc863aSGleb Natapov     u32 rsdt_physical_address;  /* 32-bit physical address of RSDT */
9c1bc863aSGleb Natapov     u32 length;                 /* XSDT Length in bytes including hdr */
10c1bc863aSGleb Natapov     u64 xsdt_physical_address;  /* 64-bit physical address of XSDT */
11c1bc863aSGleb Natapov     u8  extended_checksum;      /* Checksum of entire table */
12c1bc863aSGleb Natapov     u8  reserved [3];           /* Reserved field must be 0 */
13c1bc863aSGleb Natapov };
14c1bc863aSGleb Natapov 
15c1bc863aSGleb Natapov #define ACPI_TABLE_HEADER_DEF   /* ACPI common table header */ \
16c1bc863aSGleb Natapov     u32 signature;          /* ACPI signature (4 ASCII characters) */ \
17c1bc863aSGleb Natapov     u32 length;                 /* Length of table, in bytes, including header */ \
18c1bc863aSGleb Natapov     u8  revision;               /* ACPI Specification minor version # */ \
19c1bc863aSGleb Natapov     u8  checksum;               /* To make sum of entire table == 0 */ \
20c1bc863aSGleb Natapov     u8  oem_id [6];             /* OEM identification */ \
21c1bc863aSGleb Natapov     u8  oem_table_id [8];       /* OEM table identification */ \
22c1bc863aSGleb Natapov     u32 oem_revision;           /* OEM revision number */ \
23c1bc863aSGleb Natapov     u8  asl_compiler_id [4];    /* ASL compiler vendor ID */ \
24c1bc863aSGleb Natapov     u32 asl_compiler_revision;  /* ASL compiler revision number */
25c1bc863aSGleb Natapov 
26c1bc863aSGleb Natapov #define RSDT_SIGNATURE 0x54445352
27c1bc863aSGleb Natapov struct rsdt_descriptor_rev1 {
28c1bc863aSGleb Natapov     ACPI_TABLE_HEADER_DEF
29c1bc863aSGleb Natapov     u32 table_offset_entry[0];
30c1bc863aSGleb Natapov };
31c1bc863aSGleb Natapov 
32c1bc863aSGleb Natapov #define FACP_SIGNATURE 0x50434146 // FACP
33c1bc863aSGleb Natapov struct fadt_descriptor_rev1
34c1bc863aSGleb Natapov {
35c1bc863aSGleb Natapov     ACPI_TABLE_HEADER_DEF     /* ACPI common table header */
36c1bc863aSGleb Natapov     u32 firmware_ctrl;          /* Physical address of FACS */
37c1bc863aSGleb Natapov     u32 dsdt;                   /* Physical address of DSDT */
38c1bc863aSGleb Natapov     u8  model;                  /* System Interrupt Model */
39c1bc863aSGleb Natapov     u8  reserved1;              /* Reserved */
40c1bc863aSGleb Natapov     u16 sci_int;                /* System vector of SCI interrupt */
41c1bc863aSGleb Natapov     u32 smi_cmd;                /* Port address of SMI command port */
42c1bc863aSGleb Natapov     u8  acpi_enable;            /* Value to write to smi_cmd to enable ACPI */
43c1bc863aSGleb Natapov     u8  acpi_disable;           /* Value to write to smi_cmd to disable ACPI */
44c1bc863aSGleb Natapov     u8  S4bios_req;             /* Value to write to SMI CMD to enter S4BIOS state */
45c1bc863aSGleb Natapov     u8  reserved2;              /* Reserved - must be zero */
46c1bc863aSGleb Natapov     u32 pm1a_evt_blk;           /* Port address of Power Mgt 1a acpi_event Reg Blk */
47c1bc863aSGleb Natapov     u32 pm1b_evt_blk;           /* Port address of Power Mgt 1b acpi_event Reg Blk */
48c1bc863aSGleb Natapov     u32 pm1a_cnt_blk;           /* Port address of Power Mgt 1a Control Reg Blk */
49c1bc863aSGleb Natapov     u32 pm1b_cnt_blk;           /* Port address of Power Mgt 1b Control Reg Blk */
50c1bc863aSGleb Natapov     u32 pm2_cnt_blk;            /* Port address of Power Mgt 2 Control Reg Blk */
51c1bc863aSGleb Natapov     u32 pm_tmr_blk;             /* Port address of Power Mgt Timer Ctrl Reg Blk */
52c1bc863aSGleb Natapov     u32 gpe0_blk;               /* Port addr of General Purpose acpi_event 0 Reg Blk */
53c1bc863aSGleb Natapov     u32 gpe1_blk;               /* Port addr of General Purpose acpi_event 1 Reg Blk */
54c1bc863aSGleb Natapov     u8  pm1_evt_len;            /* Byte length of ports at pm1_x_evt_blk */
55c1bc863aSGleb Natapov     u8  pm1_cnt_len;            /* Byte length of ports at pm1_x_cnt_blk */
56c1bc863aSGleb Natapov     u8  pm2_cnt_len;            /* Byte Length of ports at pm2_cnt_blk */
57c1bc863aSGleb Natapov     u8  pm_tmr_len;             /* Byte Length of ports at pm_tm_blk */
58c1bc863aSGleb Natapov     u8  gpe0_blk_len;           /* Byte Length of ports at gpe0_blk */
59c1bc863aSGleb Natapov     u8  gpe1_blk_len;           /* Byte Length of ports at gpe1_blk */
60c1bc863aSGleb Natapov     u8  gpe1_base;              /* Offset in gpe model where gpe1 events start */
61c1bc863aSGleb Natapov     u8  reserved3;              /* Reserved */
62c1bc863aSGleb Natapov     u16 plvl2_lat;              /* Worst case HW latency to enter/exit C2 state */
63c1bc863aSGleb Natapov     u16 plvl3_lat;              /* Worst case HW latency to enter/exit C3 state */
64c1bc863aSGleb Natapov     u16 flush_size;             /* Size of area read to flush caches */
65c1bc863aSGleb Natapov     u16 flush_stride;           /* Stride used in flushing caches */
66c1bc863aSGleb Natapov     u8  duty_offset;            /* Bit location of duty cycle field in p_cnt reg */
67c1bc863aSGleb Natapov     u8  duty_width;             /* Bit width of duty cycle field in p_cnt reg */
68c1bc863aSGleb Natapov     u8  day_alrm;               /* Index to day-of-month alarm in RTC CMOS RAM */
69c1bc863aSGleb Natapov     u8  mon_alrm;               /* Index to month-of-year alarm in RTC CMOS RAM */
70c1bc863aSGleb Natapov     u8  century;                /* Index to century in RTC CMOS RAM */
71c1bc863aSGleb Natapov     u8  reserved4;              /* Reserved */
72c1bc863aSGleb Natapov     u8  reserved4a;             /* Reserved */
73c1bc863aSGleb Natapov     u8  reserved4b;             /* Reserved */
74c1bc863aSGleb Natapov };
75c1bc863aSGleb Natapov 
76c1bc863aSGleb Natapov #define FACS_SIGNATURE 0x53434146 // FACS
77c1bc863aSGleb Natapov struct facs_descriptor_rev1
78c1bc863aSGleb Natapov {
79c1bc863aSGleb Natapov     u32 signature;           /* ACPI Signature */
80c1bc863aSGleb Natapov     u32 length;                 /* Length of structure, in bytes */
81c1bc863aSGleb Natapov     u32 hardware_signature;     /* Hardware configuration signature */
82c1bc863aSGleb Natapov     u32 firmware_waking_vector; /* ACPI OS waking vector */
83c1bc863aSGleb Natapov     u32 global_lock;            /* Global Lock */
84c1bc863aSGleb Natapov     u32 S4bios_f        : 1;    /* Indicates if S4BIOS support is present */
85c1bc863aSGleb Natapov     u32 reserved1       : 31;   /* Must be 0 */
86c1bc863aSGleb Natapov     u8  resverved3 [40];        /* Reserved - must be zero */
87c1bc863aSGleb Natapov };
88c1bc863aSGleb Natapov 
89c1bc863aSGleb Natapov u32* find_resume_vector_addr(void)
90c1bc863aSGleb Natapov {
91c1bc863aSGleb Natapov     unsigned long addr;
92c1bc863aSGleb Natapov     struct rsdp_descriptor *rsdp;
93c1bc863aSGleb Natapov     struct rsdt_descriptor_rev1 *rsdt;
94c1bc863aSGleb Natapov     void *end;
95c1bc863aSGleb Natapov     int i;
96c1bc863aSGleb Natapov 
97c1bc863aSGleb Natapov     for(addr = 0xf0000; addr < 0x100000; addr += 16) {
98c1bc863aSGleb Natapov 	rsdp = (void*)addr;
99c1bc863aSGleb Natapov 	if (rsdp->signature == 0x2052545020445352LL)
100c1bc863aSGleb Natapov           break;
101c1bc863aSGleb Natapov     }
102c1bc863aSGleb Natapov     if (addr == 0x100000) {
103c1bc863aSGleb Natapov         printf("Can't find RSDP\n");
104c1bc863aSGleb Natapov         return 0;
105c1bc863aSGleb Natapov     }
106c1bc863aSGleb Natapov 
107c1bc863aSGleb Natapov     printf("RSDP is at %x\n", rsdp);
108c1bc863aSGleb Natapov     rsdt = (void*)(ulong)rsdp->rsdt_physical_address;
109c1bc863aSGleb Natapov     if (!rsdt || rsdt->signature != RSDT_SIGNATURE)
110c1bc863aSGleb Natapov         return 0;
111c1bc863aSGleb Natapov 
112c1bc863aSGleb Natapov     printf("RSDT is at %x\n", rsdt);
113c1bc863aSGleb Natapov 
114c1bc863aSGleb Natapov     end = (void*)rsdt + rsdt->length;
115c1bc863aSGleb Natapov     for (i=0; (void*)&rsdt->table_offset_entry[i] < end; i++) {
116c1bc863aSGleb Natapov         struct fadt_descriptor_rev1 *fadt = (void*)(ulong)rsdt->table_offset_entry[i];
117c1bc863aSGleb Natapov         struct facs_descriptor_rev1 *facs;
118c1bc863aSGleb Natapov         if (!fadt || fadt->signature != FACP_SIGNATURE)
119c1bc863aSGleb Natapov             continue;
120c1bc863aSGleb Natapov         printf("FADT is at %x\n", fadt);
121c1bc863aSGleb Natapov         facs = (void*)(ulong)fadt->firmware_ctrl;
122c1bc863aSGleb Natapov         if (!facs || facs->signature != FACS_SIGNATURE)
123c1bc863aSGleb Natapov             return 0;
124c1bc863aSGleb Natapov         printf("FACS is at %x\n", facs);
125c1bc863aSGleb Natapov         return &facs->firmware_waking_vector;
126c1bc863aSGleb Natapov     }
127c1bc863aSGleb Natapov    return 0;
128c1bc863aSGleb Natapov }
129c1bc863aSGleb Natapov 
130*798b0d95SPaolo Bonzini #define RTC_SECONDS_ALARM       1
131*798b0d95SPaolo Bonzini #define RTC_MINUTES_ALARM       3
132*798b0d95SPaolo Bonzini #define RTC_HOURS_ALARM         5
133*798b0d95SPaolo Bonzini #define RTC_ALARM_DONT_CARE     0xC0
134*798b0d95SPaolo Bonzini 
135*798b0d95SPaolo Bonzini #define RTC_REG_A               10
136*798b0d95SPaolo Bonzini #define RTC_REG_B               11
137*798b0d95SPaolo Bonzini #define RTC_REG_C               12
138*798b0d95SPaolo Bonzini 
139*798b0d95SPaolo Bonzini #define REG_A_UIP               0x80
140*798b0d95SPaolo Bonzini #define REG_B_AIE               0x20
141*798b0d95SPaolo Bonzini 
142*798b0d95SPaolo Bonzini static inline int rtc_in(u8 reg)
143*798b0d95SPaolo Bonzini {
144*798b0d95SPaolo Bonzini     u8 x = reg;
145*798b0d95SPaolo Bonzini     asm volatile("outb %b1, $0x70; inb $0x71, %b0"
146*798b0d95SPaolo Bonzini 		 : "+a"(x) : "0"(x));
147*798b0d95SPaolo Bonzini     return x;
148*798b0d95SPaolo Bonzini }
149*798b0d95SPaolo Bonzini 
150*798b0d95SPaolo Bonzini static inline void rtc_out(u8 reg, u8 val)
151*798b0d95SPaolo Bonzini {
152*798b0d95SPaolo Bonzini     asm volatile("outb %b1, $0x70; mov %b2, %b1; outb %b1, $0x71"
153*798b0d95SPaolo Bonzini 		 : "+a"(reg) : "0"(reg), "ri"(val));
154*798b0d95SPaolo Bonzini }
155*798b0d95SPaolo Bonzini 
156c1bc863aSGleb Natapov extern char resume_start, resume_end;
157c1bc863aSGleb Natapov 
158c1bc863aSGleb Natapov int main(int argc, char **argv)
159c1bc863aSGleb Natapov {
160c1bc863aSGleb Natapov 	volatile u32 *resume_vector_ptr = find_resume_vector_addr();
161c1bc863aSGleb Natapov 	char *addr, *resume_vec = (void*)0x1000;
162c1bc863aSGleb Natapov 
163c1bc863aSGleb Natapov 	*resume_vector_ptr = (u32)(ulong)resume_vec;
164c1bc863aSGleb Natapov 
165c1bc863aSGleb Natapov 	printf("resume vector addr is %x\n", resume_vector_ptr);
166c1bc863aSGleb Natapov 	for (addr = &resume_start; addr < &resume_end; addr++)
167c1bc863aSGleb Natapov 		*resume_vec++ = *addr;
168c1bc863aSGleb Natapov 	printf("copy resume code from %x\n", &resume_start);
169*798b0d95SPaolo Bonzini 
170*798b0d95SPaolo Bonzini 	/* Setup RTC alarm to wake up on the next second.  */
171*798b0d95SPaolo Bonzini 	while ((rtc_in(RTC_REG_A) & REG_A_UIP) == 0);
172*798b0d95SPaolo Bonzini 	while ((rtc_in(RTC_REG_A) & REG_A_UIP) != 0);
173*798b0d95SPaolo Bonzini 	rtc_in(RTC_REG_C);
174*798b0d95SPaolo Bonzini 	rtc_out(RTC_SECONDS_ALARM, RTC_ALARM_DONT_CARE);
175*798b0d95SPaolo Bonzini 	rtc_out(RTC_MINUTES_ALARM, RTC_ALARM_DONT_CARE);
176*798b0d95SPaolo Bonzini 	rtc_out(RTC_HOURS_ALARM, RTC_ALARM_DONT_CARE);
177*798b0d95SPaolo Bonzini 	rtc_out(RTC_REG_B, rtc_in(RTC_REG_B) | REG_B_AIE);
178*798b0d95SPaolo Bonzini 
179c1bc863aSGleb Natapov 	*(volatile int*)0 = 0;
180c1bc863aSGleb Natapov 	asm volatile("out %0, %1" :: "a"(0x2400), "d"((short)0xb004):"memory");
181c1bc863aSGleb Natapov 	while(1)
182c1bc863aSGleb Natapov 		*(volatile int*)0 = 1;
183c1bc863aSGleb Natapov 
184c1bc863aSGleb Natapov 	return 0;
185c1bc863aSGleb Natapov }
186c1bc863aSGleb Natapov 
187c1bc863aSGleb Natapov asm (
188c1bc863aSGleb Natapov         ".global resume_start\n"
189c1bc863aSGleb Natapov 	".global resume_end\n"
190c1bc863aSGleb Natapov 	".code16\n"
191c1bc863aSGleb Natapov 	"resume_start:\n"
192c1bc863aSGleb Natapov 	"mov 0x0, %eax\n"
193c1bc863aSGleb Natapov 	"mov $0xf4, %dx\n"
194c1bc863aSGleb Natapov 	"out %eax, %dx\n"
195c1bc863aSGleb Natapov 	"1: hlt\n"
196c1bc863aSGleb Natapov 	"jmp 1b\n"
197c1bc863aSGleb Natapov 	"resume_end:\n"
198c1bc863aSGleb Natapov #ifdef __i386__
199c1bc863aSGleb Natapov 	".code32\n"
200c1bc863aSGleb Natapov #else
201c1bc863aSGleb Natapov 	".code64\n"
202c1bc863aSGleb Natapov #endif
203c1bc863aSGleb Natapov     );
204