1/*
2 * Copyright (c) 2004-2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <kern/machine.h>
30#include <kern/misc_protos.h>
31#include <kern/thread.h>
32#include <kern/processor.h>
33#include <kern/kalloc.h>
34#include <mach/machine.h>
35#include <mach/processor_info.h>
36#include <mach/mach_types.h>
37#include <i386/pmap.h>
38#include <kern/cpu_data.h>
39#include <IOKit/IOPlatformExpert.h>
40
41#include <pexpert/i386/efi.h>
42
43#include <IOKit/IOHibernatePrivate.h>
44#include <vm/vm_page.h>
45#include <i386/i386_lowmem.h>
46
47extern ppnum_t max_ppnum;
48
49#define MAX_BANKS	32
50
51/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
52
53hibernate_page_list_t *
54hibernate_page_list_allocate(boolean_t log)
55{
56    ppnum_t		    base, num;
57    vm_size_t               size;
58    uint32_t                bank, num_banks;
59    uint32_t		    pages, page_count;
60    hibernate_page_list_t * list;
61    hibernate_bitmap_t *    bitmap;
62
63    EfiMemoryRange *	    mptr;
64    uint32_t		    mcount, msize, i;
65    hibernate_bitmap_t	    dram_ranges[MAX_BANKS];
66    boot_args *		    args = (boot_args *) PE_state.bootArgs;
67    uint32_t		    non_os_pagecount;
68
69    mptr = (EfiMemoryRange *)ml_static_ptovirt(args->MemoryMap);
70    if (args->MemoryMapDescriptorSize == 0)
71	panic("Invalid memory map descriptor size");
72    msize = args->MemoryMapDescriptorSize;
73    mcount = args->MemoryMapSize / msize;
74
75    num_banks = 0;
76    non_os_pagecount = 0;
77    for (i = 0; i < mcount; i++, mptr = (EfiMemoryRange *)(((vm_offset_t)mptr) + msize))
78    {
79	base = (ppnum_t) (mptr->PhysicalStart >> I386_PGSHIFT);
80	num = (ppnum_t) mptr->NumberOfPages;
81
82	if (base > max_ppnum)
83		continue;
84	if ((base + num - 1) > max_ppnum)
85		num = max_ppnum - base + 1;
86	if (!num)
87		continue;
88
89	switch (mptr->Type)
90	{
91	    // any kind of dram
92	    case kEfiACPIMemoryNVS:
93	    case kEfiPalCode:
94		non_os_pagecount += num;
95
96	    // OS used dram
97	    case kEfiLoaderCode:
98	    case kEfiLoaderData:
99	    case kEfiBootServicesCode:
100	    case kEfiBootServicesData:
101	    case kEfiConventionalMemory:
102
103		for (bank = 0; bank < num_banks; bank++)
104		{
105		    if (dram_ranges[bank].first_page <= base)
106			continue;
107		    if ((base + num) == dram_ranges[bank].first_page)
108		    {
109			dram_ranges[bank].first_page = base;
110			num = 0;
111		    }
112		    break;
113		}
114		if (!num) break;
115
116		if (bank && (base == (1 + dram_ranges[bank - 1].last_page)))
117		    bank--;
118		else
119		{
120		    num_banks++;
121		    if (num_banks >= MAX_BANKS) break;
122		    bcopy(&dram_ranges[bank],
123			  &dram_ranges[bank + 1],
124			  (num_banks - bank - 1) * sizeof(hibernate_bitmap_t));
125		    dram_ranges[bank].first_page = base;
126		}
127		dram_ranges[bank].last_page = base + num - 1;
128		break;
129
130	    // runtime services will be restarted, so no save
131	    case kEfiRuntimeServicesCode:
132	    case kEfiRuntimeServicesData:
133	    // contents are volatile once the platform expert starts
134	    case kEfiACPIReclaimMemory:
135	    // non dram
136	    case kEfiReservedMemoryType:
137	    case kEfiUnusableMemory:
138	    case kEfiMemoryMappedIO:
139	    case kEfiMemoryMappedIOPortSpace:
140	    default:
141		break;
142	}
143    }
144
145    if (num_banks >= MAX_BANKS)
146	return (NULL);
147
148    // size the hibernation bitmap
149
150    size = sizeof(hibernate_page_list_t);
151    page_count = 0;
152    for (bank = 0; bank < num_banks; bank++) {
153	pages = dram_ranges[bank].last_page + 1 - dram_ranges[bank].first_page;
154	page_count += pages;
155        size += sizeof(hibernate_bitmap_t) + ((pages + 31) >> 5) * sizeof(uint32_t);
156    }
157
158    list = (hibernate_page_list_t *)kalloc(size);
159    if (!list)
160	return (list);
161
162    list->list_size  = (uint32_t)size;
163    list->page_count = page_count;
164    list->bank_count = num_banks;
165
166    // convert to hibernation bitmap.
167
168    bitmap = &list->bank_bitmap[0];
169    for (bank = 0; bank < num_banks; bank++)
170    {
171        bitmap->first_page = dram_ranges[bank].first_page;
172        bitmap->last_page  = dram_ranges[bank].last_page;
173        bitmap->bitmapwords = (bitmap->last_page + 1
174                               - bitmap->first_page + 31) >> 5;
175        if (log) kprintf("hib bank[%d]: 0x%x000 end 0x%xfff\n",
176        		  bank, bitmap->first_page, bitmap->last_page);
177	bitmap = (hibernate_bitmap_t *) &bitmap->bitmap[bitmap->bitmapwords];
178    }
179    if (log) printf("efi pagecount %d\n", non_os_pagecount);
180
181    return (list);
182}
183
184// mark pages not to be saved, but available for scratch usage during restore
185
186void
187hibernate_page_list_setall_machine( __unused hibernate_page_list_t * page_list,
188                                    __unused hibernate_page_list_t * page_list_wired,
189                                    __unused boolean_t preflight,
190                                    __unused uint32_t * pagesOut)
191{
192}
193
194// mark pages not to be saved and not for scratch usage during restore
195void
196hibernate_page_list_set_volatile( hibernate_page_list_t * page_list,
197				  hibernate_page_list_t * page_list_wired,
198				  uint32_t * pagesOut)
199{
200    boot_args * args = (boot_args *) PE_state.bootArgs;
201
202    if (args->efiRuntimeServicesPageStart)
203    {
204	hibernate_set_page_state(page_list, page_list_wired,
205		    args->efiRuntimeServicesPageStart, args->efiRuntimeServicesPageCount,
206		    kIOHibernatePageStateFree);
207	*pagesOut -= args->efiRuntimeServicesPageCount;
208    }
209}
210
211kern_return_t
212hibernate_processor_setup(IOHibernateImageHeader * header)
213{
214    boot_args * args = (boot_args *) PE_state.bootArgs;
215
216    cpu_datap(0)->cpu_hibernate = 1;
217    header->processorFlags = 0;
218
219    header->runtimePages     = args->efiRuntimeServicesPageStart;
220    header->runtimePageCount = args->efiRuntimeServicesPageCount;
221    header->runtimeVirtualPages = args->efiRuntimeServicesVirtualPageStart;
222    header->performanceDataStart = args->performanceDataStart;
223    header->performanceDataSize = args->performanceDataSize;
224
225    return (KERN_SUCCESS);
226}
227
228void
229hibernate_vm_lock(void)
230{
231    if (current_cpu_datap()->cpu_hibernate) hibernate_vm_lock_queues();
232}
233
234void
235hibernate_vm_unlock(void)
236{
237    if (current_cpu_datap()->cpu_hibernate)  hibernate_vm_unlock_queues();
238}
239