root/headers/private/kernel/arch/x86/64/descriptors.h
/*
 * Copyright 2012, Alex Smith, alex@alex-smith.me.uk.
 * Distributed under the terms of the MIT License.
 */
#ifndef _KERNEL_ARCH_X86_64_DESCRIPTORS_H
#define _KERNEL_ARCH_X86_64_DESCRIPTORS_H


// Segment definitions.
// Note that the ordering of these is important to SYSCALL/SYSRET.
#define KERNEL_CODE_SEGMENT             1
#define KERNEL_DATA_SEGMENT             2
#define USER32_CODE_SEGMENT             3
#define USER_DATA_SEGMENT               4
#define USER_CODE_SEGMENT               5

#define BOOT_GDT_SEGMENT_COUNT  (USER_CODE_SEGMENT + 1)

#define KERNEL_CODE_SELECTOR    ((KERNEL_CODE_SEGMENT << 3) | DPL_KERNEL)
#define KERNEL_DATA_SELECTOR    ((KERNEL_DATA_SEGMENT << 3) | DPL_KERNEL)

#define USER32_CODE_SELECTOR    ((USER32_CODE_SEGMENT << 3) | DPL_USER)
#define USER_CODE_SELECTOR              ((USER_CODE_SEGMENT << 3) | DPL_USER)
#define USER_DATA_SELECTOR              ((USER_DATA_SEGMENT << 3) | DPL_USER)


#ifndef _ASSEMBLER


// Structure of a segment descriptor.
struct segment_descriptor {
        uint32 limit0 : 16;
        uint32 base0 : 24;
        uint32 type : 4;
        uint32 desc_type : 1;
        uint32 dpl : 2;
        uint32 present : 1;
        uint32 limit1 : 4;
        uint32 available : 1;
        uint32 long_mode : 1;
        uint32 d_b : 1;
        uint32 granularity : 1;
        uint32 base1 : 8;
} _PACKED;

struct tss {
        uint32 _reserved1;
        uint64 sp0;
        uint64 sp1;
        uint64 sp2;
        uint64 _reserved2;
        uint64 ist1;
        uint64 ist2;
        uint64 ist3;
        uint64 ist4;
        uint64 ist5;
        uint64 ist6;
        uint64 ist7;
        uint64 _reserved3;
        uint16 _reserved4;
        uint16 io_map_base;
} _PACKED;


static inline void
clear_segment_descriptor(segment_descriptor* desc)
{
        *(uint64*)desc = 0;
}


static inline void
set_segment_descriptor(segment_descriptor* desc, uint8 type, uint8 dpl)
{
        clear_segment_descriptor(desc);

        // In 64-bit mode the CPU ignores the base/limit of code/data segments,
        // it always treats base as 0 and does no limit checks.
        desc->base0 = 0;
        desc->base1 = 0;
        desc->limit0 = 0xffff;
        desc->limit1 = 0xf;
        desc->granularity = 1;

        desc->type = type;
        desc->desc_type = DT_CODE_DATA_SEGMENT;
        desc->dpl = dpl;
        desc->present = 1;

        desc->long_mode = (type & DT_CODE_EXECUTE_ONLY) ? 1 : 0;
                // Must be set to 1 for code segments only.
}


unsigned x86_64_set_user_tls_segment_base(int cpu, addr_t base);


#endif  /* _ASSEMBLER */

#endif  /* _KERNEL_ARCH_X86_64_DESCRIPTORS_H */