/* Copyright (C) 2004 David Decotigny Copyright (C) 2003 Thomas Petazzoni 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 */ sos_ui16_t limit_15_0; /* Segment limit, bits 15..0 */ sos_ui16_t base_paged_addr_15_0; /* Base address, bits 15..0 */ /* Highest dword */ sos_ui8_t base_paged_addr_23_16; /* Base address bits 23..16 */ sos_ui8_t segment_type:4; /* Section 3.4.3.1 (code/data) and 3.5 (system) of Intel x86 vol 3 */ sos_ui8_t descriptor_type:1; /* 0=system, 1=Code/Data */ sos_ui8_t dpl:2; /* Descriptor privilege level */ sos_ui8_t present:1; sos_ui8_t limit_19_16:4; /* Segment limit, bits 19..16 */ sos_ui8_t custom:1; sos_ui8_t zero:1; sos_ui8_t op_size:1; /* 0=16bits instructions, 1=32bits */ sos_ui8_t granularity:1; /* 0=limit in bytes, 1=limit in pages */ sos_ui8_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 { /** Intel doc says that the real GDT register (ie the "limit" field) should be odd-word aligned. That's why we add a padding here. Credits to Romain for having signalled this to us. */ sos_ui16_t padding; /** The maximum GDT offset allowed to access an entry in the GDT */ sos_ui16_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, in SOS we configure the * 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 */ sos_ui32_t base_addr; } __attribute__((packed, aligned(4))); /** * 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, \ .zero= 0, \ .op_size= 1, /* 32 bits instr/data */ \ .granularity= 1, /* limit is in 4kB Pages */ \ .base_paged_addr_31_24= 0 \ }) /** The actual GDT */ static struct x86_segment_descriptor gdt[] = { [SOS_SEG_NULL] = (struct x86_segment_descriptor){ 0, }, [SOS_SEG_KCODE] = BUILD_GDTE(0, 1), [SOS_SEG_KDATA] = BUILD_GDTE(0, 0), [SOS_SEG_UCODE] = BUILD_GDTE(3, 1), [SOS_SEG_UDATA] = BUILD_GDTE(3, 0), [SOS_SEG_KERNEL_TSS] = { 0, } /* Initialized by register_kernel_tss */ }; sos_ret_t sos_gdt_subsystem_setup(void) { struct x86_gdt_register gdtr; /* Put some garbage in the padding field of the GDTR */ gdtr.padding = ~0; /* Address of the GDT */ gdtr.base_addr = (sos_ui32_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.limit) /* The real beginning of the GDT register is /after/ the "padding" field, ie at the "limit" field. */, "i"(SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KCODE)), "i"(SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KDATA)) :"memory","eax"); return SOS_OK; } sos_ret_t sos_gdt_register_kernel_tss(sos_vaddr_t tss_vaddr) { sos_ui16_t regval_tss; /* Initialize the GDT entry */ gdt[SOS_SEG_KERNEL_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 */ .zero= 0, .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 = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KERNEL_TSS); asm ("ltr %0" : :"r"(regval_tss)); return SOS_OK; }