AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
x86_64.c
Go to the documentation of this file.
1
16
17#include <core/arch.h>
18#include <core/panic.h>
19#include <core/printk.h>
20#include <core/string.h>
21#include <core/system.h>
22#include <boot/boot.h>
23#include <cpu/cpu.h>
24#include <sys/cpu.h>
25
26#include <mm/pmap.h>
27#include <mm/buddy.h>
28#include <mm/mm.h>
29#include <mm/vm.h>
30
31MALLOC_DEFINE(M_PMAP, "pmap", "physical memory map structure");
32
33extern struct boot *__kboot;
34
35#define PG_PRESENT 0x001
36#define PG_WRITE 0x002
37#define PG_USER 0x004
38#define PG_PWT 0x008 /* write-through */
39#define PG_PCD 0x010 /* cache disabled */
40#define PG_LARGE 0x080 /* a large page: 2 MiB in a PD, 1 GiB in a PDPT */
41#define PG_GLOBAL 0x100
42
43#define PG_ADDR(e) ((e) & 0x000FFFFFFFFFF000ULL)
44
45/* the index of `va` in its table at `level` (0: PT, 1: PD, 2: PDPT, 3: PML4) */
46#define IDX(va, level) (((va) >> (12 + 9 * (level))) & 0x1FF)
47
48#define KERNEL_PML4E 256
49#define LARGE_SIZE 0x200000ULL
50
51static struct pmap k_pmap;
52
53/* the end of the direct map: the end of RAM, rounded up to 2 MiB
54 * (pmap_init) */
56
57/* the running CPU's page tables, which its CR3 has */
58#define cur_pmap (curcpu()->arch.pmap)
59
61static inline uint64_t *table(paddr_t paddr)
62{
63 return PHYS2VIRT(paddr);
64}
65
68static paddr_t table_alloc(void)
69{
70 struct vm_page *vm_page = mm_page_alloc();
71
72 if (!vm_page)
73 return 0;
74
76 return vm_page->paddr;
77}
78
82{
84
85 if (!paddr)
86 panic("no memory for the kernel's page tables");
87
88 return paddr;
89}
90
93{
95}
96
101static uint64_t *pte_get(struct pmap *pmap, vaddr_t va, int make)
102{
103 uint64_t *t = table(pmap->map);
104 uint64_t flags = PG_PRESENT | PG_WRITE | (IDX(va, 3) < KERNEL_PML4E ? PG_USER : 0);
105
106 for (int level = 3; level > 0; --level) {
107 uint64_t *e = &t[IDX(va, level)];
108
109 if (!(*e & PG_PRESENT)) {
110 paddr_t t_new;
111
112 if (!make || !(t_new = table_alloc()))
113 return NULL;
114
115 *e = t_new | flags;
116 }
117
118 /* the direct map's large pages aren't changed page by page */
119 if (*e & PG_LARGE)
120 return NULL;
121
122 t = table(PG_ADDR(*e));
123 }
124
125 return &t[IDX(va, 0)];
126}
127
128/*
129 * Switches the running CPU to an address space's page tables.
130 */
131struct pmap *pmap_switch(struct pmap *pmap)
132{
133 if (!pmap)
134 panic("pmap?");
135
136 struct pmap *ret = cur_pmap;
137
138 if (pmap != cur_pmap) {
140 write_cr3(pmap->map);
141 cur_pmap = pmap;
142 }
143
144 return ret;
145}
146
148static paddr_t ram_end(void)
149{
150 paddr_t end = 0;
151
152 for (int i = 0; i < __kboot->mmap_count; ++i)
153 if (__kboot->mmap[i].type == MMAP_USABLE && __kboot->mmap[i].end > end)
154 end = __kboot->mmap[i].end;
155
156 return end;
157}
158
159/*
160 * Takes over the boot page tables as the kernel's, at boot: maps all of
161 * RAM in the direct map, with 2 MiB pages, and makes every PDPT of the
162 * kernel's half, so that those PML4 entries never change and every
163 * address space can share them.
164 */
165void pmap_init(void)
166{
167 /* The boot tables (boot/x86_64_bootstrap.S) become the kernel's. They
168 * map the kernel at -2 GiB and the first GiB of the direct map (and
169 * the same memory at 0, which is removed below). */
171 k_pmap.ref = 1;
174 cur_pmap = &k_pmap;
175
176 uint64_t *pml4 = table(k_pmap.map);
177
178 /* map the rest of RAM in the direct map, with 2 MiB pages */
179 paddr_t end = (ram_end() + LARGE_SIZE - 1) & ~(LARGE_SIZE - 1);
180 dmap_end = end;
181
182 for (paddr_t pa = 0; pa < end; pa += LARGE_SIZE) {
183 vaddr_t va = ARCH_DMAP_BASE + pa;
184 uint64_t *t = pml4;
185
186 for (int level = 3; level > 1; --level) {
187 uint64_t *e = &t[IDX(va, level)];
188
189 if (!(*e & PG_PRESENT))
191
192 t = table(PG_ADDR(*e));
193 }
194
195 t[IDX(va, 1)] = pa | PG_PRESENT | PG_WRITE | PG_LARGE | PG_GLOBAL;
196 }
197
198 /* make every PDPT of the kernel's half now, before there is any
199 * other PML4 to share them */
200 for (int i = KERNEL_PML4E; i < 512; ++i)
201 if (!(pml4[i] & PG_PRESENT))
202 pml4[i] = table_alloc_boot() | PG_PRESENT | PG_WRITE;
203
204 /* remove the mapping at 0: the lower half is user space */
205 pml4[0] = 0;
206 tlb_flush();
207
208 printk("x86_64: %ld MiB in the direct map\n", (uint64_t) (end >> 20));
209}
210
212static struct pmap *pmap_alloc(void)
213{
214 return kmalloc(sizeof(struct pmap), &M_PMAP, 0);
215}
216
218static void pmap_release(struct pmap *pmap)
219{
220 /* make sure that no CPU has it loaded anymore */
221 tlb_shootdown(pmap, 0, 0, 1);
222
223 if (pmap->map)
225
226 kfree(pmap);
227}
228
229/*
230 * Makes the page tables of a new address space: an empty user half, and
231 * the kernel's half, which every address space shares.
232 */
233struct pmap *pmap_create(void)
234{
235 struct pmap *pmap = pmap_alloc();
236
237 if (pmap == NULL)
238 return NULL;
239
240 memset(pmap, 0, sizeof(struct pmap));
241
242 if (!(pmap->map = table_alloc())) {
243 kfree(pmap);
244 return NULL;
245 }
246
247 pmap->ref = 1;
248
249 /* the kernel's half, the same in every PML4 */
250 uint64_t *pml4 = table(pmap->map), *kpml4 = table(k_pmap.map);
251
252 for (int i = KERNEL_PML4E; i < 512; ++i)
253 pml4[i] = kpml4[i];
254
255 return pmap;
256}
257
258/*
259 * Adds a reference to an address space's page tables.
260 */
261void pmap_incref(struct pmap *pmap)
262{
263 pmap->ref++;
264}
265
266/*
267 * Drops a reference to an address space's page tables, freeing them with
268 * the last one.
269 */
270void pmap_decref(struct pmap *pmap)
271{
272 if (--pmap->ref == 0)
274}
275
278{
279 uint64_t pte = PG_PRESENT;
280
281 if (flags & (VM_KW | VM_UW))
282 pte |= PG_WRITE;
283
284 if (flags & VM_URWX)
285 pte |= PG_USER;
286
287 /* device memory: uncached */
288 if (flags & VM_NOCACHE)
289 pte |= PG_PCD | PG_PWT;
290
291 return pte;
292}
293
294/*
295 * Maps a page in an address space, replacing what was mapped there.
296 */
297int pmap_add(struct pmap *pmap, vaddr_t va, paddr_t pa, uint32_t flags)
298{
299 if ((va & PAGE_MASK) || (pa & PAGE_MASK))
300 return -EINVAL;
301
302 /* the direct map's large pages aren't changed page by page */
303 if (va >= ARCH_DMAP_BASE && va < ARCH_DMAP_BASE + dmap_end)
304 return -EINVAL;
305
306 uint64_t *pte = pte_get(pmap, va, 1);
307
308 /* there was no memory for a page table on the way */
309 if (!pte)
310 return -ENOMEM;
311
312 uint64_t old = *pte;
313 *pte = pa | pte_flags(flags);
314
315 /* another page was mapped there: flush it from the TLBs */
316 if (old & PG_PRESENT)
317 tlb_shootdown(pmap, va, va + PAGE_SIZE, 0);
318
319 return 0;
320}
321
322/*
323 * Unmaps the pages of a range of an address space, and flushes them from
324 * the TLBs that may have them.
325 */
326void pmap_remove(struct pmap *pmap, vaddr_t sva, vaddr_t eva)
327{
328 if ((sva & PAGE_MASK) || (eva & PAGE_MASK))
329 return;
330
331 vaddr_t first = eva, last = sva;
332
333 for (vaddr_t va = sva; va < eva; va += PAGE_SIZE) {
334 uint64_t *pte = pte_get(pmap, va, 0);
335
336 if (pte && (*pte & PG_PRESENT)) {
337 *pte = 0;
338 first = MIN(first, va);
339 last = va + PAGE_SIZE;
340 }
341 }
342
343 if (first < last)
344 tlb_shootdown(pmap, first, last, 0);
345}
346
350static void tables_free(paddr_t paddr, int level)
351{
352 uint64_t *t = table(paddr);
353
354 for (int i = 0; i < 512; ++i) {
355 if (!(t[i] & PG_PRESENT))
356 continue;
357
358 if (level > 1)
359 tables_free(PG_ADDR(t[i]), level - 1);
360 else
361 mm_page_decref(PG_ADDR(t[i]));
362 }
363
364 table_free(paddr);
365}
366
367/*
368 * Frees the page tables of an address space's user half. The pages they
369 * map lose the reference that the page fault handler gave them
370 * (`mm_page_incref()`).
371 */
373{
374 uint64_t *pml4 = table(pmap->map);
375
376 for (int i = 0; i < KERNEL_PML4E; ++i) {
377 if (pml4[i] & PG_PRESENT) {
378 tables_free(PG_ADDR(pml4[i]), 3);
379 pml4[i] = 0;
380 }
381 }
382
384}
385
386/*
387 * Changes the permissions of the mapped pages of a range of an address
388 * space.
389 */
390void pmap_protect(struct pmap *pmap, vaddr_t sva, vaddr_t eva, uint32_t prot)
391{
392 if (sva & PAGE_MASK)
393 return;
394
395 vaddr_t first = eva, last = sva;
396
397 for (vaddr_t va = sva; va < eva; va += PAGE_SIZE) {
398 uint64_t *pte = pte_get(pmap, va, 0);
399
400 if (pte && (*pte & PG_PRESENT)) {
401 *pte = PG_ADDR(*pte) | pte_flags(prot);
402 first = MIN(first, va);
403 last = va + PAGE_SIZE;
404 }
405 }
406
407 if (first < last)
408 tlb_shootdown(pmap, first, last, 0);
409}
410
411void pmap_copy(struct pmap *dst_map, struct pmap *src_map, vaddr_t dst_addr, size_t len,
412 vaddr_t src_addr)
413{
414 return;
415}
416
417/* Does nothing: changes take effect at once on x86. */
418void pmap_update(struct pmap *pmap)
419{
420 return;
421}
422
423/*
424 * Copies a page of physical memory to another, through the direct map.
425 */
427{
428 memcpy(PHYS2VIRT(dst), PHYS2VIRT(src), PAGE_SIZE);
429}
430
431/* Does nothing: not implemented on x86, nothing uses it. */
432void pmap_page_protect(struct vm_page *pg, uint32_t flags)
433{
434 return;
435}
436
437/* Not implemented on x86, nothing uses it: returns -1. */
439{
440 return -1;
441}
442
443/* Not implemented on x86, nothing uses it: returns -1. */
445{
446 return -1;
447}
448
449/* Not implemented on x86, nothing uses it: returns -1. */
451{
452 return -1;
453}
454
455/* Not implemented on x86, nothing uses it: returns -1. */
457{
458 return -1;
459}
460
461/*
462 * Finds the page mapped at a virtual address of an address space.
463 */
465{
466 uint64_t *pte = pte_get(pmap, vaddr, 0);
467
468 return pte && (*pte & PG_PRESENT) ? PG_ADDR(*pte) : 0;
469}
470
471/*
472 * Unmaps a page of the running CPU's address space.
473 */
475{
476 if (vaddr & PAGE_MASK)
477 return -EINVAL;
478
479 pmap_remove(cur_pmap, vaddr, vaddr + PAGE_SIZE);
480 return 0;
481}
482
483/*
484 * Handles a page fault: turns the CPU's error code into the kernel's
485 * `PF_*` flags, and calls `mm_page_fault()`.
486 */
487int arch_mm_page_fault(vaddr_t vaddr, int err)
488{
489 int flags = 0;
490
491 flags |= err & 0x01? PF_PRESENT : 0;
492 flags |= err & 0x02? PF_WRITE : PF_READ;
493 flags |= err & 0x04? PF_USER : 0;
494 flags |= err & 0x10? PF_EXEC : 0;
495
496 return mm_page_fault(vaddr, flags);
497}
498
499/*
500 * Reads from a page of physical memory, through the direct map.
501 */
502int pmap_page_read(paddr_t paddr, off_t off, size_t size, void *buf)
503{
504 if (off < 0 || off >= PAGE_SIZE)
505 return -EINVAL;
506
507 memcpy(buf, (char *) PHYS2VIRT(paddr) + off, MIN(size, PAGE_SIZE - (size_t) off));
508 return 0;
509}
510
511/*
512 * Writes to a page of physical memory, through the direct map.
513 */
514int pmap_page_write(paddr_t paddr, off_t off, size_t size, void *buf)
515{
516 if (off < 0 || off >= PAGE_SIZE)
517 return -EINVAL;
518
519 memcpy((char *) PHYS2VIRT(paddr) + off, buf, MIN(size, PAGE_SIZE - (size_t) off));
520 return 0;
521}
522
523/* where the window for devices' registers has room next (all of RAM,
524 * and what the boot loader left in it, is in the direct map) */
526
530static void *window_map(uintptr_t paddr, size_t size, uint32_t flags)
531{
532 paddr_t start = PAGE_ALIGN(paddr);
533 size_t len = PAGE_ROUND(paddr + size) - start;
534
536 return NULL; /* no room left in the window */
537
538 vaddr_t vaddr = mmio_next;
539
540 /* if there's no memory for a page table, unmap what was mapped */
541 for (size_t off = 0; off < len; off += PAGE_SIZE) {
542 if (pmap_add(&k_pmap, vaddr + off, start + off, flags)) {
543 pmap_remove(&k_pmap, vaddr, vaddr + off);
544 return NULL;
545 }
546 }
547
548 mmio_next += len;
549
550 return (void *) (vaddr + (paddr - start));
551}
552
553/*
554 * Returns a kernel address for a range of physical memory: its address
555 * in the direct map if it's there, else a mapping made in the window.
556 */
557void *arch_phys_map(uintptr_t paddr, size_t size)
558{
559 if (paddr + size <= dmap_end)
560 return PHYS2VIRT(paddr);
561
562 return window_map(paddr, size, VM_KRW);
563}
564
565/*
566 * Maps a device's registers in the kernel's address space, uncached.
567 */
568void *arch_mmio_map(uintptr_t paddr, size_t size)
569{
570 return window_map(paddr, size, VM_KRW | VM_NOCACHE);
571}
struct boot * __kboot
Definition init.c:19
#define USER_STACK
Definition arch.h:60
#define PAGE_SIZE
Definition system.h:7
#define PAGE_MASK
Definition system.h:8
uint64_t paddr_t
Definition mm.h:39
#define PHYS2VIRT(paddr)
Definition mm.h:26
#define ARCH_DMAP_BASE
Definition mm.h:14
#define ARCH_MMIO_BASE
Definition mm.h:19
static void tlb_flush(void)
Flushes every translation from this CPU's TLB.
Definition mm.h:43
uint64_t vaddr_t
Definition mm.h:40
#define ARCH_MMIO_SIZE
Definition mm.h:20
void pmap_loaded(struct pmap *from, struct pmap *to)
Definition pmap.c:157
void tlb_shootdown(struct pmap *pmap, vaddr_t sva, vaddr_t eva, int leave)
Definition pmap.c:130
#define panic(s)
Definition panic.h:15
static uintptr_t read_cr3(void)
Definition cpu.h:201
int arch_mm_page_fault(vaddr_t vaddr, int err)
Handles a page fault: turns the CPU's error code into the kernel's PF_* flags, and calls mm_page_faul...
Definition i386.c:449
#define PG_LARGE
Definition smp.c:65
#define PG_PRESENT
Definition smp.c:63
#define PG_WRITE
Definition smp.c:64
#define PG_USER
Definition i386.c:35
#define PG_PCD
Definition i386.c:37
int pmap_is_referenced(struct vm_page *pg)
Not implemented on x86, and nothing uses it.
Definition i386.c:415
#define PG_PWT
Definition i386.c:36
void pmap_page_protect(struct vm_page *pg, uint32_t flags)
Not implemented on x86 (it does nothing), and nothing uses it.
Definition i386.c:371
static struct pmap * pmap_alloc(void)
Allocates a struct pmap.
Definition i386.c:157
void pmap_update(struct pmap *pmap)
Makes the changes to an address space's page tables take effect.
Definition i386.c:352
int pmap_clear_modify(struct vm_page *pg)
Not implemented on x86, and nothing uses it.
Definition i386.c:382
static paddr_t table_alloc_boot(void)
Allocates a page table that the kernel can't boot without: it panics if there is no memory.
Definition i386.c:73
void pmap_copy(struct pmap *dst_map, struct pmap *src_map, vaddr_t dst_addr, size_t len, vaddr_t src_addr)
Does nothing: the generic pmap_copy() isn't used on x86.
Definition i386.c:340
int pmap_is_modified(struct vm_page *pg)
Not implemented on x86, and nothing uses it.
Definition i386.c:404
int arch_page_unmap(vaddr_t vaddr)
Unmaps a page of the running CPU's address space.
Definition i386.c:436
#define PG_ADDR(e)
Definition i386.c:39
int pmap_clear_reference(struct vm_page *pg)
Not implemented on x86, and nothing uses it.
Definition i386.c:393
@ MMAP_USABLE
Definition boot.h:23
#define MIN(a, b)
Definition system.h:11
#define PF_READ
Definition mm.h:17
void mm_page_decref(paddr_t paddr)
Drops a reference to a page of RAM.
Definition mm.c:50
#define PAGE_ALIGN(ptr)
Definition mm.h:9
#define PAGE_ROUND(ptr)
Definition mm.h:10
#define PF_EXEC
Definition mm.h:19
#define PF_WRITE
Definition mm.h:18
struct vm_page * mm_page_alloc(void)
Allocates a free page of physical memory.
Definition mm.c:78
int mm_page_fault(vaddr_t vaddr, int flags)
Handles a page fault in the current process's address space.
Definition fault.c:395
#define PF_USER
Definition mm.h:20
void mm_page_dealloc(paddr_t paddr)
Frees a page of physical memory, back to the zone it came from.
Definition mm.c:103
#define PF_PRESENT
Definition mm.h:16
#define MALLOC_DEFINE(type, _name, _desc)
Definition kvmem.h:30
void * kmalloc(size_t size, struct malloc_type *type, int flags)
Allocates memory for one of the kernel's structures.
Definition kvmem.c:123
void kfree(void *)
Frees an object that kmalloc() gave.
Definition kvmem.c:146
void * memset(void *dst, int c, size_t n)
Sets n bytes to c.
Definition libc.c:23
void * memcpy(void *dst, const void *src, size_t n)
Copies n bytes; dst and src must not overlap.
Definition libc.c:11
int pmap_add(struct pmap *pmap, vaddr_t va, paddr_t pa, uint32_t flags)
Maps a page in an address space, replacing what was mapped there.
Definition pmap.c:307
#define cur_pmap
Definition pmap.c:69
static void table_free(paddr_t paddr)
Frees a page table.
Definition pmap.c:91
paddr_t arch_page_get_mapping(struct pmap *pmap, vaddr_t vaddr)
Finds the page mapped at a virtual address of an address space.
Definition pmap.c:431
void pmap_page_copy(paddr_t src, paddr_t dst)
Copies a page of physical memory to another.
Definition pmap.c:423
void pmap_protect(struct pmap *pmap, vaddr_t sva, vaddr_t eva, uint32_t prot)
Changes the permissions of the mapped pages of a range of an address space.
Definition pmap.c:399
int pmap_page_read(paddr_t paddr, off_t off, size_t size, void *buf)
Reads from a page of physical memory, through the kernel's mapping.
Definition pmap.c:440
void pmap_init(void)
Takes over the boot page tables as the kernel's, at boot, and makes the page tables of the kernel's h...
Definition pmap.c:195
static uint64_t * table(paddr_t paddr)
Returns a page table, through the direct map.
Definition pmap.c:72
static void pmap_release(struct pmap *pmap)
Frees a pmap whose last reference went.
Definition pmap.c:228
void * arch_phys_map(uintptr_t paddr, size_t size)
Returns a kernel address for a range of physical memory: its address in the direct map if it's there,...
Definition pmap.c:497
static paddr_t dmap_end
Definition pmap.c:66
void pmap_decref(struct pmap *pmap)
Drops a reference to an address space's page tables, freeing them with the last one.
Definition pmap.c:271
static paddr_t ram_end(void)
Returns the end of the RAM that the boot loader found.
Definition pmap.c:182
static struct pmap k_pmap
Definition pmap.c:59
struct pmap * pmap_switch(struct pmap *pmap)
Switches this CPU to an address space's page tables.
Definition pmap.c:164
static uint64_t pte_flags(uint32_t flags)
Returns the descriptor bits for a page with the VM_* flags flags.
Definition pmap.c:279
int pmap_page_write(paddr_t paddr, off_t off, size_t size, void *buf)
Writes to a page of physical memory, through the kernel's mapping.
Definition pmap.c:452
void pmap_incref(struct pmap *pmap)
Adds a reference to an address space's page tables.
Definition pmap.c:263
void pmap_remove(struct pmap *pmap, vaddr_t sva, vaddr_t eva)
Unmaps the pages of a range of an address space, and flushes them from the TLBs that may have them.
Definition pmap.c:337
static void tables_free(paddr_t paddr, int level)
Frees a table of the user half at level and the tables under it.
Definition pmap.c:361
static void * window_map(uintptr_t paddr, size_t size, uint32_t flags)
Maps physical memory in the window, with flags.
Definition pmap.c:467
static vaddr_t mmio_next
Definition pmap.c:463
struct pmap * pmap_create(void)
Makes the page tables of a new address space: an empty user half, and the kernel's half,...
Definition pmap.c:242
static uint64_t * pte_get(struct pmap *pmap, vaddr_t va, int make)
Returns the page table entry that maps va, in pmap or in the kernel's half.
Definition pmap.c:100
void pmap_remove_all(struct pmap *pmap)
Unmaps the whole user half of an address space, and frees its page tables.
Definition pmap.c:382
static paddr_t table_alloc(void)
Allocates an empty page table.
Definition pmap.c:79
#define IDX(va, level)
Definition pmap.c:55
void * arch_mmio_map(uintptr_t paddr, size_t size)
Maps a device's registers into the kernel's address space, uncached (memory-mapped I/O).
Definition pmap.c:508
int printk(const char *fmt,...)
Writes a kernel message, to the message buffer and the consoles.
Definition printk.c:249
#define NULL
Definition stddef.h:27
__SIZE_TYPE__ uintptr_t
Definition stddef.h:12
unsigned int uint32_t
Definition stddef.h:22
unsigned long long int uint64_t
Definition stddef.h:23
Definition boot.h:35
int mmap_count
Definition boot.h:40
mmap_t * mmap
Definition boot.h:42
uintptr_t end
Definition boot.h:31
enum mmap_type type
Definition boot.h:29
An address space's page tables, on x86.
Definition pmap.h:9
paddr_t map
Definition pmap.h:10
size_t ref
Definition pmap.h:11
A page of physical memory (pages[] has one for every page of RAM).
Definition vm.h:150
paddr_t paddr
its physical address
Definition vm.h:151
struct pmap * pmap
its page tables (the architecture's)
Definition vm.h:27
long int off_t
Definition types.h:10
#define VM_URWX
user read/write/execute
Definition vm.h:56
#define VM_KW
kernel write
Definition vm.h:39
#define VM_NOCACHE
not cached (device memory)
Definition vm.h:45
struct vm_space kvm_space
Definition vmm.c:17
#define VM_KRW
kernel read/write
Definition vm.h:49
#define VM_UW
user write
Definition vm.h:42
#define KERNEL_PML4E
Definition x86_64.c:48
#define LARGE_SIZE
Definition x86_64.c:49
#define PG_GLOBAL
Definition x86_64.c:41