[kutil] Make enum bitfields usable in other scopes

Changing the SFINAE/enable_if strategy from a type to a constexpr
function means that it can be defined in other scopes than the functions
themselves, because of function overloading. This lets us put everything
into the kutil::bitfields namespace, and make bitfields out of enums in
other namespaces. Also took the chance to clean up the implementation a
bit.
This commit is contained in:
Justin C. Miller
2021-02-19 20:42:49 -08:00
parent cf22ed57a2
commit f9a967caf7
10 changed files with 101 additions and 147 deletions

View File

@@ -84,7 +84,7 @@ enum class acpi_fadt_flags : uint32_t
hw_reduced_acpi = 0x00100000, hw_reduced_acpi = 0x00100000,
low_pwr_s0_idle = 0x00200000 low_pwr_s0_idle = 0x00200000
}; };
IS_BITFIELD(acpi_fadt_flags); is_bitfield(acpi_fadt_flags);
struct acpi_fadt struct acpi_fadt
{ {

View File

@@ -19,6 +19,18 @@ protected:
uint32_t *m_base; uint32_t *m_base;
}; };
/// Controller for processor-local APICs
class lapic :
public apic
{
public:
/// Constructor
/// \arg base Physicl base address of the APIC's MMIO registers
lapic(uintptr_t base);
/// Get the local APIC's ID
uint8_t get_id();
enum class ipi : uint32_t enum class ipi : uint32_t
{ {
// Delivery modes // Delivery modes
@@ -34,19 +46,6 @@ enum class ipi : uint32_t
edge = 0x0000, ///< edge-triggered edge = 0x0000, ///< edge-triggered
level = 0x8000, ///< level-triggered level = 0x8000, ///< level-triggered
}; };
IS_BITFIELD(ipi);
/// Controller for processor-local APICs
class lapic :
public apic
{
public:
/// Constructor
/// \arg base Physicl base address of the APIC's MMIO registers
lapic(uintptr_t base);
/// Get the local APIC's ID
uint8_t get_id();
/// Send an inter-processor interrupt. /// Send an inter-processor interrupt.
/// \arg mode The sending mode /// \arg mode The sending mode
@@ -144,3 +143,6 @@ private:
uint8_t m_id; uint8_t m_id;
uint8_t m_version; uint8_t m_version;
}; };
is_bitfield(lapic::ipi);

View File

@@ -34,13 +34,13 @@ GDT::GDT(TSS *tss) :
m_ptr.base = &m_entries[0]; m_ptr.base = &m_entries[0];
// Kernel CS/SS - always 64bit // Kernel CS/SS - always 64bit
set(kern_cs_index, 0, 0xfffff, true, gdt_type::read_write | gdt_type::execute); set(kern_cs_index, 0, 0xfffff, true, type::read_write | type::execute);
set(kern_ss_index, 0, 0xfffff, true, gdt_type::read_write); set(kern_ss_index, 0, 0xfffff, true, type::read_write);
// User CS32/SS/CS64 - layout expected by SYSRET // User CS32/SS/CS64 - layout expected by SYSRET
set(user_cs32_index, 0, 0xfffff, false, gdt_type::ring3 | gdt_type::read_write | gdt_type::execute); set(user_cs32_index, 0, 0xfffff, false, type::ring3 | type::read_write | type::execute);
set(user_ss_index, 0, 0xfffff, true, gdt_type::ring3 | gdt_type::read_write); set(user_ss_index, 0, 0xfffff, true, type::ring3 | type::read_write);
set(user_cs64_index, 0, 0xfffff, true, gdt_type::ring3 | gdt_type::read_write | gdt_type::execute); set(user_cs64_index, 0, 0xfffff, true, type::ring3 | type::read_write | type::execute);
set_tss(tss); set_tss(tss);
} }
@@ -63,7 +63,7 @@ GDT::install() const
} }
void void
GDT::set(uint8_t i, uint32_t base, uint64_t limit, bool is64, gdt_type type) GDT::set(uint8_t i, uint32_t base, uint64_t limit, bool is64, type t)
{ {
m_entries[i].limit_low = limit & 0xffff; m_entries[i].limit_low = limit & 0xffff;
m_entries[i].size = (limit >> 16) & 0xf; m_entries[i].size = (limit >> 16) & 0xf;
@@ -73,7 +73,7 @@ GDT::set(uint8_t i, uint32_t base, uint64_t limit, bool is64, gdt_type type)
m_entries[i].base_mid = (base >> 16) & 0xff; m_entries[i].base_mid = (base >> 16) & 0xff;
m_entries[i].base_high = (base >> 24) & 0xff; m_entries[i].base_high = (base >> 24) & 0xff;
m_entries[i].type = type | gdt_type::system | gdt_type::present; m_entries[i].type = t | type::system | type::present;
} }
struct tss_descriptor struct tss_descriptor
@@ -81,7 +81,7 @@ struct tss_descriptor
uint16_t limit_low; uint16_t limit_low;
uint16_t base_00; uint16_t base_00;
uint8_t base_16; uint8_t base_16;
gdt_type type; GDT::type type;
uint8_t size; uint8_t size;
uint8_t base_24; uint8_t base_24;
uint32_t base_32; uint32_t base_32;
@@ -105,10 +105,10 @@ GDT::set_tss(TSS *tss)
tssd.reserved = 0; tssd.reserved = 0;
tssd.type = tssd.type =
gdt_type::accessed | type::accessed |
gdt_type::execute | type::execute |
gdt_type::ring3 | type::ring3 |
gdt_type::present; type::present;
kutil::memcpy(&m_entries[tss_index], &tssd, sizeof(tss_descriptor)); kutil::memcpy(&m_entries[tss_index], &tssd, sizeof(tss_descriptor));
} }
@@ -141,26 +141,26 @@ GDT::dump(unsigned index) const
gdt[i].limit_low; gdt[i].limit_low;
cons->printf(" %02d:", i); cons->printf(" %02d:", i);
if (! bitfield_has(gdt[i].type, gdt_type::present)) { if (! (gdt[i].type && type::present)) {
cons->puts(" Not Present\n"); cons->puts(" Not Present\n");
continue; continue;
} }
cons->printf(" Base %08x limit %05x ", base, limit); cons->printf(" Base %08x limit %05x ", base, limit);
switch (gdt[i].type & gdt_type::ring3) { switch (gdt[i].type & type::ring3) {
case gdt_type::ring3: cons->puts("ring3"); break; case type::ring3: cons->puts("ring3"); break;
case gdt_type::ring2: cons->puts("ring2"); break; case type::ring2: cons->puts("ring2"); break;
case gdt_type::ring1: cons->puts("ring1"); break; case type::ring1: cons->puts("ring1"); break;
default: cons->puts("ring0"); break; default: cons->puts("ring0"); break;
} }
cons->printf(" %s %s %s %s %s %s %s\n", cons->printf(" %s %s %s %s %s %s %s\n",
bitfield_has(gdt[i].type, gdt_type::accessed) ? "A" : " ", (gdt[i].type && type::accessed) ? "A" : " ",
bitfield_has(gdt[i].type, gdt_type::read_write) ? "RW" : " ", (gdt[i].type && type::read_write) ? "RW" : " ",
bitfield_has(gdt[i].type, gdt_type::conforming) ? "C" : " ", (gdt[i].type && type::conforming) ? "C" : " ",
bitfield_has(gdt[i].type, gdt_type::execute) ? "EX" : " ", (gdt[i].type && type::execute) ? "EX" : " ",
bitfield_has(gdt[i].type, gdt_type::system) ? "S" : " ", (gdt[i].type && type::system) ? "S" : " ",
(gdt[i].size & 0x80) ? "KB" : " B", (gdt[i].size & 0x80) ? "KB" : " B",
(gdt[i].size & 0x60) == 0x20 ? "64" : (gdt[i].size & 0x60) == 0x20 ? "64" :
(gdt[i].size & 0x60) == 0x40 ? "32" : "16"); (gdt[i].size & 0x60) == 0x40 ? "32" : "16");

View File

@@ -7,20 +7,6 @@
class TSS; class TSS;
enum class gdt_type : uint8_t
{
accessed = 0x01,
read_write = 0x02,
conforming = 0x04,
execute = 0x08,
system = 0x10,
ring1 = 0x20,
ring2 = 0x40,
ring3 = 0x60,
present = 0x80
};
IS_BITFIELD(gdt_type);
class GDT class GDT
{ {
public: public:
@@ -39,8 +25,21 @@ public:
/// \arg index Which entry to print, or -1 for all entries /// \arg index Which entry to print, or -1 for all entries
void dump(unsigned index = -1) const; void dump(unsigned index = -1) const;
enum class type : uint8_t
{
accessed = 0x01,
read_write = 0x02,
conforming = 0x04,
execute = 0x08,
system = 0x10,
ring1 = 0x20,
ring2 = 0x40,
ring3 = 0x60,
present = 0x80
};
private: private:
void set(uint8_t i, uint32_t base, uint64_t limit, bool is64, gdt_type type); void set(uint8_t i, uint32_t base, uint64_t limit, bool is64, type t);
void set_tss(TSS *tss); void set_tss(TSS *tss);
struct descriptor struct descriptor
@@ -48,7 +47,7 @@ private:
uint16_t limit_low; uint16_t limit_low;
uint16_t base_low; uint16_t base_low;
uint8_t base_mid; uint8_t base_mid;
gdt_type type; type type;
uint8_t size; uint8_t size;
uint8_t base_high; uint8_t base_high;
} __attribute__ ((packed, align(8))); } __attribute__ ((packed, align(8)));
@@ -64,3 +63,5 @@ private:
ptr m_ptr; ptr m_ptr;
}; };
is_bitfield(GDT::type);

View File

@@ -263,7 +263,7 @@ start_aps(lapic &apic, const kutil::vector<uint8_t> &ids, void *kpml4)
size_t free_stack_count = 0; size_t free_stack_count = 0;
uintptr_t stack_area_start = 0; uintptr_t stack_area_start = 0;
ipi mode = ipi::init | ipi::level | ipi::assert; lapic::ipi mode = lapic::ipi::init | lapic::ipi::level | lapic::ipi::assert;
apic.send_ipi_broadcast(mode, false, 0); apic.send_ipi_broadcast(mode, false, 0);
for (uint8_t id : ids) { for (uint8_t id : ids) {
@@ -301,7 +301,7 @@ start_aps(lapic &apic, const kutil::vector<uint8_t> &ids, void *kpml4)
size_t current_count = ap_startup_count; size_t current_count = ap_startup_count;
log::debug(logs::boot, "Starting AP %d: stack %llx", cpu->index, stack_end); log::debug(logs::boot, "Starting AP %d: stack %llx", cpu->index, stack_end);
ipi startup = ipi::startup | ipi::assert; lapic::ipi startup = lapic::ipi::startup | lapic::ipi::assert;
apic.send_ipi(startup, vector, id); apic.send_ipi(startup, vector, id);
for (unsigned i = 0; i < 20; ++i) { for (unsigned i = 0; i < 20; ++i) {

View File

@@ -24,6 +24,11 @@ using memory::kernel_max_heap;
using namespace kernel; using namespace kernel;
namespace kernel {
namespace args {
is_bitfield(section_flags);
}}
extern "C" void initialize_main_thread(); extern "C" void initialize_main_thread();
extern "C" uintptr_t initialize_main_user_stack(); extern "C" uintptr_t initialize_main_user_stack();
@@ -203,8 +208,8 @@ load_simple_process(args::program &program)
for (const auto &sect : program.sections) { for (const auto &sect : program.sections) {
vm_flags flags = vm_flags flags =
(bitfield_has(sect.type, section_flags::execute) ? vm_flags::exec : vm_flags::none) | ((sect.type && section_flags::execute) ? vm_flags::exec : vm_flags::none) |
(bitfield_has(sect.type, section_flags::write) ? vm_flags::write : vm_flags::none); ((sect.type && section_flags::write) ? vm_flags::write : vm_flags::none);
vm_area *vma = new vm_area_fixed(sect.phys_addr, sect.size, flags); vm_area *vma = new vm_area_fixed(sect.phys_addr, sect.size, flags);
space.add(sect.virt_addr, vma); space.add(sect.virt_addr, vma);

View File

@@ -174,4 +174,4 @@ private:
}; };
IS_BITFIELD(vm_flags); is_bitfield(vm_flags);

View File

@@ -194,4 +194,4 @@ inline bool operator<(page_table::level a, page_table::level b) {
inline page_table::level& operator++(page_table::level& l) { l = l + 1; return l; } inline page_table::level& operator++(page_table::level& l) { l = l + 1; return l; }
inline page_table::level& operator--(page_table::level& l) { l = l - 1; return l; } inline page_table::level& operator--(page_table::level& l) { l = l - 1; return l; }
IS_BITFIELD(page_table::flag); is_bitfield(page_table::flag);

View File

@@ -132,4 +132,4 @@ private:
kutil::spinlock m_lock; kutil::spinlock m_lock;
}; };
IS_BITFIELD(vm_space::fault_type); is_bitfield(vm_space::fault_type);

View File

@@ -2,16 +2,16 @@
#include <type_traits> #include <type_traits>
template<typename E> namespace kutil {
struct is_enum_bitfield { static constexpr bool value = false; }; namespace bitfields {
#define IS_BITFIELD(name) \ template <typename E>
template<> struct ::is_enum_bitfield<name> {static constexpr bool value=true;} constexpr bool is_enum_bitfield(E) { return false; }
template <typename E> template <typename E>
struct enum_or_int { struct enum_or_int {
static constexpr bool value = static constexpr bool value =
std::disjunction< is_enum_bitfield<E>, std::is_integral<E> >::value; is_enum_bitfield(E{}) || std::is_integral<E>::value;
}; };
template <typename E, typename F> template <typename E, typename F>
@@ -34,116 +34,62 @@ template <> struct integral<unsigned long> { using type = unsigned long; };
template <> struct integral<long long> { using type = long long; }; template <> struct integral<long long> { using type = long long; };
template <> struct integral<unsigned long long> { using type = unsigned long long; }; template <> struct integral<unsigned long long> { using type = unsigned long long; };
template <typename E, typename F>
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type
operator & (E lhs, F rhs)
{
return static_cast<E> (
static_cast<typename integral<E>::type>(lhs) &
static_cast<typename integral<F>::type>(rhs));
}
template <typename E, typename F>
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type
operator | (E lhs, F rhs)
{
return static_cast<E> (
static_cast<typename integral<E>::type>(lhs) |
static_cast<typename integral<F>::type>(rhs));
}
template <typename E, typename F>
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type
operator ^ (E lhs, F rhs)
{
return static_cast<E> (
static_cast<typename integral<E>::type>(lhs) ^
static_cast<typename integral<F>::type>(rhs));
}
template <typename E>
constexpr typename std::enable_if<is_enum_bitfield<E>::value,E>::type
operator ~ (E rhs)
{
return static_cast<E>(~static_cast<typename std::underlying_type<E>::type>(rhs));
}
template <typename E, typename F> template <typename E, typename F>
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type& constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type&
operator |= (E &lhs, F rhs) operator |= (E &lhs, F rhs)
{ {
lhs = static_cast<E>( return lhs = static_cast<E>(
static_cast<typename integral<E>::type>(lhs) | static_cast<typename integral<E>::type>(lhs) |
static_cast<typename integral<F>::type>(rhs)); static_cast<typename integral<F>::type>(rhs));
return lhs;
} }
template <typename E, typename F> template <typename E, typename F>
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type& constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type&
operator &= (E &lhs, F rhs) operator &= (E &lhs, F rhs)
{ {
lhs = static_cast<E>( return lhs = static_cast<E>(
static_cast<typename integral<E>::type>(lhs) & static_cast<typename integral<E>::type>(lhs) &
static_cast<typename integral<F>::type>(rhs)); static_cast<typename integral<F>::type>(rhs));
return lhs;
} }
template <typename E, typename F> template <typename E, typename F>
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type& constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type&
operator ^= (E &lhs, F rhs) operator ^= (E &lhs, F rhs)
{ {
lhs = static_cast<E>( return lhs = static_cast<E>(
static_cast<typename integral<E>::type>(lhs) ^ static_cast<typename integral<E>::type>(lhs) ^
static_cast<typename integral<F>::type>(rhs)); static_cast<typename integral<F>::type>(rhs));
return lhs;
} }
template <typename E, typename F> template <typename E, typename F>
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type& constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type
operator -= (E &lhs, F rhs) operator & (E lhs, F rhs) { return lhs &= rhs; }
{
lhs = static_cast<E>(
static_cast<typename integral<E>::type>(lhs) &
~static_cast<typename integral<F>::type>(rhs));
return lhs;
}
template <typename E, typename F> template <typename E, typename F>
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type& constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type
operator += (E &lhs, F rhs) operator | (E lhs, F rhs) { return lhs |= rhs; }
{
lhs = static_cast<E>(
static_cast<typename integral<E>::type>(lhs) |
static_cast<typename integral<F>::type>(rhs));
return lhs; template <typename E, typename F>
} constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type
operator ^ (E lhs, F rhs) { return lhs ^= rhs; }
template <typename E> template <typename E>
constexpr typename std::enable_if<is_enum_bitfield<E>::value,bool>::type constexpr typename std::enable_if<is_enum_bitfield(E{}),E>::type
operator ! (E rhs) operator ~ (E rhs) { return static_cast<E>(~static_cast<typename std::underlying_type<E>::type>(rhs)); }
{
return static_cast<typename std::underlying_type<E>::type>(rhs) == 0;
}
template <typename E> template <typename E>
constexpr bool constexpr typename std::enable_if<is_enum_bitfield(E{}),bool>::type
bitfield_has(E set, E flag) operator ! (E rhs) { return static_cast<typename std::underlying_type<E>::type>(rhs) == 0; }
{
return
(static_cast<typename std::underlying_type<E>::type>(set) &
static_cast<typename std::underlying_type<E>::type>(flag)) ==
static_cast<typename std::underlying_type<E>::type>(flag);
}
// Overload the logical-and operator to be 'bitwise-and, bool-cast' /// Override logical-and to mean 'rhs contains all bits in lhs'
template <typename E> template <typename E>
constexpr typename std::enable_if<is_enum_bitfield<E>::value,bool>::type constexpr typename std::enable_if<is_enum_bitfield(E{}),bool>::type
operator && (E set, E flag) operator && (E rhs, E lhs) { return (rhs & lhs) == lhs; }
{
return (set & flag) == flag; } // namespace bitfields
} } // namespace kutil
#define is_bitfield(name) \
constexpr bool is_enum_bitfield(name) { return true; } \
using namespace kutil::bitfields;