1/*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#pragma once
8
9#include <model/statedata.h>
10#include <arch/machine/cpu_registers.h>
11#include <arch/model/smp.h>
12#include <arch/machine.h>
13
14/* Address space control */
15static inline paddr_t getCurrentPD(void)
16{
17    return MODE_NODE_STATE(ia32KSCurrentPD);
18}
19
20static inline void setCurrentPD(paddr_t addr)
21{
22    MODE_NODE_STATE(ia32KSCurrentPD) = addr;
23    write_cr3(addr);
24}
25
26static inline void setCurrentVSpaceRoot(paddr_t addr, word_t pcid)
27{
28    /* pcid is not supported on ia32 and so we should always be passed zero */
29    assert(pcid == 0);
30    setCurrentPD(addr);
31}
32
33static inline cr3_t getCurrentCR3(void)
34{
35    /* on ia32 the PD is the full value of CR3, so we can just return that */
36    return cr3_new(getCurrentPD());
37}
38
39static inline void invalidateLocalTLBEntry(vptr_t vptr)
40{
41    asm volatile("invlpg (%[vptr])" :: [vptr] "r"(vptr));
42}
43
44/* Invalidates page structures cache */
45static inline void invalidateLocalPageStructureCache(void)
46{
47    /* invalidate an arbitrary line to invalidate the page structure cache */
48    invalidateLocalTLBEntry(0);
49}
50
51static inline void invalidateLocalPageStructureCacheASID(paddr_t root, asid_t asid)
52{
53    /* ignore asid */
54    invalidateLocalPageStructureCache();
55}
56
57static inline void invalidateLocalTLB(void)
58{
59    /* rewrite the current page directory */
60    write_cr3(MODE_NODE_STATE(ia32KSCurrentPD));
61}
62
63static inline void invalidateLocalTranslationSingle(vptr_t vptr)
64{
65    /* Just invalidate a single entry in the TLB */
66    invalidateLocalTLBEntry(vptr);
67}
68
69static inline void invalidateLocalTranslationSingleASID(vptr_t vptr, asid_t asid)
70{
71    /* no asid support in 32-bit, just invalidate TLB */
72    invalidateLocalTLBEntry(vptr);
73}
74
75static inline void invalidateLocalTranslationAll(void)
76{
77    invalidateLocalTLB();
78    invalidateLocalPageStructureCache();
79}
80
81static inline rdmsr_safe_result_t x86_rdmsr_safe(const uint32_t reg)
82{
83    uint32_t low;
84    uint32_t high;
85    word_t returnto;
86    rdmsr_safe_result_t result;
87    asm volatile(
88        "movl $1f, (%[returnto_addr]) \n\
89         rdmsr \n\
90         1: \n\
91         movl (%[returnto_addr]), %[returnto] \n\
92         movl $0, (%[returnto_addr])"
93        : [returnto] "=&r"(returnto),
94        [high] "=&d"(high),
95        [low] "=&a"(low)
96        : [returnto_addr] "r"(&ARCH_NODE_STATE(x86KSGPExceptReturnTo)),
97        [reg] "c"(reg)
98        : "memory"
99    );
100    result.success = returnto != 0;
101    result.value = ((uint64_t)high << 32) | (uint64_t)low;
102    return result;
103}
104
105/* GDT installation */
106void ia32_install_gdt(gdt_idt_ptr_t *gdt_idt_ptr);
107
108/* IDT installation */
109void ia32_install_idt(gdt_idt_ptr_t *gdt_idt_ptr);
110
111/* LDT installation */
112void ia32_install_ldt(uint32_t ldt_sel);
113
114/* TSS installation */
115void ia32_install_tss(uint32_t tss_sel);
116
117void ia32_load_fs(word_t selector);
118void ia32_load_gs(word_t selector);
119
120#if defined(CONFIG_FSGSBASE_GDT) || !defined(CONFIG_FSGSBASE_MSR)
121
122static inline void FORCE_INLINE x86_write_fs_base_impl(word_t base)
123{
124    gdt_entry_gdt_data_ptr_set_base_low(&x86KSGlobalState[CURRENT_CPU_INDEX()].x86KSgdt[GDT_FS], base & 0xFFFF);
125    gdt_entry_gdt_data_ptr_set_base_mid(&x86KSGlobalState[CURRENT_CPU_INDEX()].x86KSgdt[GDT_FS], (base >> 16) & 0xFF);
126    gdt_entry_gdt_data_ptr_set_base_high(&x86KSGlobalState[CURRENT_CPU_INDEX()].x86KSgdt[GDT_FS], (base >> 24) & 0xFF);
127    asm volatile(
128        "movw %0, %%ax\n"
129        "movw %%ax, %%fs\n"
130        :
131        : "i"(SEL_FS)
132        : "ax"
133    );
134}
135
136static inline void FORCE_INLINE x86_write_gs_base_impl(word_t base)
137{
138    gdt_entry_gdt_data_ptr_set_base_low(&x86KSGlobalState[CURRENT_CPU_INDEX()].x86KSgdt[GDT_GS], base & 0xFFFF);
139    gdt_entry_gdt_data_ptr_set_base_mid(&x86KSGlobalState[CURRENT_CPU_INDEX()].x86KSgdt[GDT_GS], (base >> 16) & 0xFF);
140    gdt_entry_gdt_data_ptr_set_base_high(&x86KSGlobalState[CURRENT_CPU_INDEX()].x86KSgdt[GDT_GS], (base >> 24) & 0xFF);
141    asm volatile(
142        "movw %0, %%ax\n"
143        "movw %%ax, %%gs\n"
144        :
145        : "i"(SEL_GS)
146        : "ax"
147    );
148}
149
150static inline word_t FORCE_INLINE x86_read_fs_base_impl(void)
151{
152    word_t base = 0;
153    base &= gdt_entry_gdt_data_ptr_get_base_low(&x86KSGlobalState[CURRENT_CPU_INDEX()].x86KSgdt[GDT_FS]) & 0xFFFF;
154    base &= (gdt_entry_gdt_data_ptr_get_base_mid(&x86KSGlobalState[CURRENT_CPU_INDEX()].x86KSgdt[GDT_FS]) & 0xFF) << 16;
155    base &= (gdt_entry_gdt_data_ptr_get_base_high(&x86KSGlobalState[CURRENT_CPU_INDEX()].x86KSgdt[GDT_FS]) & 0xFF) << 24;
156    return base;
157}
158
159static inline word_t FORCE_INLINE x86_read_gs_base_impl(void)
160{
161    word_t base = 0;
162    base &= gdt_entry_gdt_data_ptr_get_base_low(&x86KSGlobalState[CURRENT_CPU_INDEX()].x86KSgdt[GDT_GS]) & 0xFFFF;
163    base &= (gdt_entry_gdt_data_ptr_get_base_mid(&x86KSGlobalState[CURRENT_CPU_INDEX()].x86KSgdt[GDT_GS]) & 0xFF) << 16;
164    base &= (gdt_entry_gdt_data_ptr_get_base_high(&x86KSGlobalState[CURRENT_CPU_INDEX()].x86KSgdt[GDT_GS]) & 0xFF) << 24;
165    return base;
166}
167
168#elif defined(CONFIG_FSGSBASE_MSR)
169
170static inline void x86_write_gs_base_impl(word_t base)
171{
172    x86_wrmsr(IA32_GS_BASE_MSR, base);
173}
174
175static inline word_t x86_read_gs_base_impl(void)
176{
177    return x86_rdmsr(IA32_GS_BASE_MSR);
178}
179
180#endif
181
182static inline void x86_set_tls_segment_base(word_t tls_base)
183{
184    x86_write_gs_base(tls_base, SMP_TERNARY(getCurrentCPUIndex(), 0));
185}
186
187static inline void init_syscall_msrs(void)
188{
189    fail("syscall not supported on ia32");
190}
191
192