matos/arch/x86/gdt.c

177 lines
8.2 KiB
C
Raw Permalink Normal View History

2021-10-17 15:37:20 +02:00
/* Copyright (C) 2021 Mathieu Maret
Copyright (C) 2004 David Decotigny
2018-07-20 15:41:58 +02:00
Copyright (C) 1999 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
*/
#include "segment.h"
#include "gdt.h"
/**
* The sructure of a segment descriptor.
*
* @see Intel x86 doc, Vol 3, section 3.4.3, figure 3-8. For segment
* types, see section 3.5
*/
struct x86_segment_descriptor {
/* Lowest dword */
uint16_t limit_15_0; /* Segment limit, bits 15..0 */
uint16_t base_paged_addr_15_0; /* Base address, bits 15..0 */
/* Highest dword */
uint8_t base_paged_addr_23_16; /* Base address bits 23..16 */
uint8_t segment_type : 4; /* Section 3.4.3.1 (code/data)
and 3.5 (system) of Intel x86 vol 3 */
uint8_t descriptor_type : 1; /* 0=system, 1=Code/Data */
uint8_t dpl : 2;
uint8_t present : 1;
uint8_t limit_19_16 : 4; /* Segment limit, bits 19..16 */
uint8_t custom : 1;
uint8_t zero : 1;
uint8_t op_size : 1; /* 0=16bits instructions, 1=32bits */
uint8_t granularity : 1; /* 0=limit in bytes, 1=limit in pages */
uint8_t base_paged_addr_31_24; /* Base address bits 31..24 */
} __attribute__((packed, aligned(8)));
/**
* The GDT register, which stores the address and size of the
* GDT.
*
* @see Intel x86 doc vol 3, section 2.4, figure 2-4; and section
* 3.5.1
*/
struct x86_gdt_register {
/* The maximum GDT offset allowed to access an entry in the GDT */
uint16_t limit;
/* This is not exactly a "virtual" address, ie an adddress such as
those of instructions and data; this is a "linear" address, ie an
address in the paged memory. However, we configure the
2018-07-20 15:41:58 +02:00
segmented memory as a "flat" space: the 0-4GB segment-based (ie
"virtual") addresses directly map to the 0-4GB paged memory (ie
"linear"), so that the "linear" addresses are numerically equal
to the "virtual" addresses: this base_addr will thus be the same
as the address of the gdt array */
uint32_t base_addr;
} __attribute__((packed, aligned(8)));
/**
* Helper macro that builds a Segment descriptor for the virtual
* 0..4GB addresses to be mapped to the linear 0..4GB linear
* addresses.
*/
#define BUILD_GDTE(descr_privilege_level, is_code) \
((struct x86_segment_descriptor){ \
.limit_15_0 = 0xffff, \
.base_paged_addr_15_0 = 0, \
.base_paged_addr_23_16 = 0, \
.segment_type = ((is_code) ? 0xb : 0x3), /* With descriptor_type (below) = 1 \
* (code/data), see Figure 3-1 of \
* section 3.4.3.1 in Intel x86 vol 3: \
* - Code (bit 3 = 1): \
* bit 0: 1=Accessed \
* bit 1: 1=Readable \
* bit 2: 0=Non-Conforming \
* - Data (bit 3 = 0): \
* bit 0: 1=Accessed \
* bit 1: 1=Writable \
* bit 2: 0=Expand up (stack-related) \
* For Conforming/non conforming segments, \
* see Intel x86 Vol 3 section 4.8.1.1 \
*/ \
.descriptor_type = 1, /* 1=Code/Data */ \
.dpl = ((descr_privilege_level)&0x3), \
.present = 1, \
.limit_19_16 = 0xf, \
.custom = 0, \
.op_size = 1, /* 32 bits instr/data */ \
.granularity = 1 /* limit is in 4kB Pages */ \
2018-07-20 15:41:58 +02:00
})
/** The actual GDT */
static struct x86_segment_descriptor gdt[] = {
[SEG_NULL] =
2018-07-20 15:41:58 +02:00
(struct x86_segment_descriptor){
0,
},
[SEG_KCODE] = BUILD_GDTE(0, 1),
[SEG_KDATA] = BUILD_GDTE(0, 0),
2021-10-17 15:37:20 +02:00
[SEG_UCODE] = BUILD_GDTE(3, 1),
[SEG_UDATA] = BUILD_GDTE(3, 0),
[SEG_K_TSS] = {0,}, // Used by syscall, IRQ while in user space
// initialized by gdtRegisterTSS
2018-07-20 15:41:58 +02:00
};
int gdtSetup(void)
{
struct x86_gdt_register gdtr;
/* Address of the GDT */
gdtr.base_addr = (uint32_t)gdt;
/* The limit is the maximum offset in bytes from the base address of
the GDT */
gdtr.limit = sizeof(gdt) - 1;
/* Commit the GDT into the CPU, and update the segment
registers. The CS register may only be updated with a long jump
to an absolute address in the given segment (see Intel x86 doc
vol 3, section 4.8.1). */
asm volatile("lgdt %0 \n\
ljmp %1,$1f \n\
1: \n\
movw %2, %%ax \n\
movw %%ax, %%ss \n\
movw %%ax, %%ds \n\
movw %%ax, %%es \n\
movw %%ax, %%fs \n\
movw %%ax, %%gs"
:
: "m"(gdtr), "i"(BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KCODE)),
"i"(BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KDATA))
2018-07-20 15:41:58 +02:00
: "memory", "eax");
return 0;
}
2021-10-17 15:37:20 +02:00
int gdtRegisterTSS(vaddr_t tss_vaddr)
{
uint16_t regval_tss;
/* Initialize the GDT entry */
gdt[SEG_K_TSS] = (struct x86_segment_descriptor){
.limit_15_0 = 0x67, /* See Intel x86 vol 3 section 6.2.2 */
.base_paged_addr_15_0 = (tss_vaddr)&0xffff,
.base_paged_addr_23_16 = (tss_vaddr >> 16) & 0xff,
.segment_type = 0x9, /* See Intel x86 vol 3 figure 6-3 */
.descriptor_type = 0, /* (idem) */
.dpl = 3, /* Allowed for CPL3 tasks */
.present = 1,
.limit_19_16 = 0, /* Size of a TSS is < 2^16 ! */
.custom = 0, /* Unused */
.op_size = 0, /* See Intel x86 vol 3 figure 6-3 */
.granularity = 1, /* limit is in Bytes */
.base_paged_addr_31_24 = (tss_vaddr >> 24) & 0xff};
/* Load the TSS register into the processor */
regval_tss = BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_K_TSS);
asm("ltr %0" : : "r"(regval_tss));
return 0;
}