λ

furix

cute os

settings

include/arch/x86_64/io.h

2251 bytes raw

#ifndef FURIX_ARCH_X86_64_IO_H
#define FURIX_ARCH_X86_64_IO_H

#include <types.h>

struct desc_ptr {
  uint16_t limit;
  uint64_t base;
} __attribute__((packed));

/*
 * CR4.PAE is required for the 4-level page tables long mode uses. CR4.PSE is
 * NOT needed there: in long mode 2 MiB pages come from PAE on its own.
 *
 * CR4.FSGSBASE is never set on this CPU model, see percpu.c.
 */
#define CR4_PAE (1u << 5)
#define CR4_FSGSBASE (1u << 16)

/* implemented in arch/x86_64/asm.S, so no inline asm elsewhere */

uint8_t inb(uint16_t port);
void outb(uint16_t port, uint8_t value);

void cpu_cli(void);
void cpu_sti(void);
void cpu_hlt(void);

/* mask interrupts, returning the prior EFLAGS word to pass back to restore */
uint64_t cpu_irq_save(void);
void cpu_irq_restore(uint64_t flags);
uint32_t cpu_irqs_enabled(void);

uint64_t rdmsr(uint32_t msr);
void wrmsr(uint32_t msr, uint64_t value);
void swapgs(void);

uint32_t read_cr4(void);
void write_cr4(uint32_t value);
void wrgsbase(uint64_t value);
uint64_t rdfsbase(void);

uint64_t read_cr2(void);
uint64_t read_cr3(void);

/* result of the last cpuid() call: {eax, ebx, ecx, edx} */
extern uint64_t cpuid_out[4];
void cpuid(uint32_t leaf);

void lgdt(const struct desc_ptr *ptr);
void lidt(const struct desc_ptr *ptr);
void ltr(uint16_t selector);
void reload_cs(uint16_t selector);
void load_data_segments(uint16_t selector);

void gs_store(uint64_t value);
uint64_t gs_load(void);

/*
 * atomics, implemented in asm.S because they need `lock` prefixes that are
 * awkward and error-prone as inline asm (and cannot be written as a plain C
 * `+=` without the compiler knowing the access is shared).
 *
 * fetch_add returns the value BEFORE the addition, so it doubles as a
 * completion index. the u32 form is deliberate: a 64-bit add is not atomic on
 * a 32-bit target and 32 bits is what the counters here need.
 */
uint32_t atomic_fetch_add_u32(volatile uint32_t *ptr, uint32_t value);

/* returns the value read, having set *ptr to 1. nonzero means "lost the race" */
uint8_t atomic_test_and_set_u8(volatile uint8_t *ptr);

/* a volatile load; x86 makes an aligned 32-bit load atomic anyway */
uint32_t atomic_load_u32(const volatile uint32_t *ptr);

#endif /* FURIX_ARCH_X86_64_IO_H */