/* 
 * Copyright 2010 Stefan Lankes, Chair for Operating Systems,
 *                               RWTH Aachen University
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * This file is part of MetalSVM.
 */

/**
 * @file arch/x86/include/asm/gdt.h
 * @brief Definitions and functions related to segmentation
 * @author Stefan Lankes
 *
 * This file defines the interface for segmentation as like structures to describe segments.\n
 * There are also some other gdt-private functions in the gdt.c file defined.
 */

#ifndef __ARCH_GDT_H__
#define __ARCH_GDT_H__

#include <metalsvm/stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

/// This segment is a data segment
#define GDT_FLAG_DATASEG        0x02
/// This segment is a code segment
#define GDT_FLAG_CODESEG        0x0a
#define GDT_FLAG_TSS            0x09
#define GDT_FLAG_TSS_BUSY	0x02

#define GDT_FLAG_SEGMENT        0x10
/// Privilege level: Ring 0 
#define GDT_FLAG_RING0          0x00
/// Privilege level: Ring 1
#define GDT_FLAG_RING1          0x20
/// Privilege level: Ring 2 
#define GDT_FLAG_RING2          0x40
/// Privilege level: Ring 3 
#define GDT_FLAG_RING3          0x60
/// Segment is present
#define GDT_FLAG_PRESENT        0x80
/** 
 * @brief Granularity of segment limit 
 * - set: segment limit unit is 4 KB (page size)
 * - not set: unit is bytes
 */
#define GDT_FLAG_4K_GRAN        0x80
/**
 * @brief Default operand size 
 * - set: 32 bit
 * - not set: 16 bit
 */
#define GDT_FLAG_32_BIT         0x40

/** @brief Defines a GDT entry 
 *
 * A global descriptor table entry consists of:
 * - 32 bit base address (chunkwise embedded into this structure)
 * - 20 bit limit
 */
typedef struct {
	/// Lower 16 bits of limit range
	unsigned short limit_low;
	/// Lower 16 bits of base address
	unsigned short base_low;
	/// middle 8 bits of base address
	unsigned char base_middle;
	/// Access bits
	unsigned char access;
	/// Granularity bits
	unsigned char granularity;
	/// Higher 8 bits of base address
	unsigned char base_high;
} __attribute__ ((packed)) gdt_entry_t;

/** @brief defines the GDT pointer structure
 *
 * This structure tells the address and size of the table.
 */
typedef struct {
	/// Size of the table in bytes (not the number of entries!)
	unsigned short limit;
	/// Address of the table
	unsigned int base;
} __attribute__ ((packed)) gdt_ptr_t;

/// Defines the maximum number of GDT entries
#define GDT_ENTRIES     (5+MAX_TASKS)
#if GDT_ENTRIES > 8192
#error Too many GDT entries!
#endif

/** @brief Installs the global descriptor table
 *
 * The installation involves the following steps:
 * - set up the special GDT pointer 
 * - set up the entries in our GDT
 * - finally call gdt_flush() in our assembler file 
 *   in order to tell the processor where the new GDT is
 * - update the new segment registers 
 */
void gdt_install(void);

/** @brief Configures and returns a GDT descriptor with chosen attributes
 *
 * Just feed this function with address, limit and the flags 
 * you have seen in idt.h
 *
 * @return a preconfigured gdt descriptor
 */
gdt_entry_t configure_gdt_entry(unsigned long base, unsigned long limit,
		unsigned char access, unsigned char gran);

#ifdef __cplusplus
}
#endif

#endif