Rearrange AHCI code, attempt to read WIP

This commit is contained in:
Justin C. Miller
2018-05-11 01:24:59 -07:00
parent 8ae3eea19c
commit d06dd2ef43
15 changed files with 774 additions and 243 deletions

View File

@@ -1,153 +0,0 @@
#include "kutil/enum_bitfields.h"
#include "ahci.h"
#include "log.h"
#include "device_manager.h"
#include "page_manager.h"
enum class ata_status : uint8_t
{
error = 0x01,
index = 0x02,
corrected = 0x04,
data_ready = 0x08,
seek_done = 0x10,
fault = 0x20,
ready = 0x40,
busy = 0x80
};
IS_BITFIELD(ata_status);
enum class ata_error : uint8_t
{
amnf = 0x01, // Address mark not found
tkznf = 0x02, // Track 0 not found
abort = 0x04, // Command abort
mcr = 0x08, // No media
idnf = 0x10, // Id not found
mc = 0x20, // No media
unc = 0x40, // Uncorrectable
bbk = 0x80, // Bad sector
};
IS_BITFIELD(ata_error);
enum class ata_cmd : uint8_t
{
read_pio = 0x20,
read_pio_ext = 0x24,
read_dma = 0xC8,
read_dma_ext = 0x25,
write_pio = 0x30,
write_pio_ext = 0x34,
write_dma = 0xCA,
write_dma_ext = 0x35,
cache_flush = 0xE7,
cache_flush_ext = 0xEA,
packet = 0xA0,
identify_packet = 0xA1,
identify = 0xEC
};
struct ahci_port_data
{
uint64_t command_base;
uint64_t fis_base;
uint32_t interrupt_status;
uint32_t interrupt_enable;
uint32_t command;
uint32_t reserved;
uint32_t task_file;
uint32_t signature;
uint32_t serial_status;
uint32_t serial_control;
uint32_t serial_error;
} __attribute__ ((packed));
ahci_port::ahci_port(ahci_port_data *data, bool impl) :
m_data(data),
m_state(state::unimpl)
{
if (impl) {
m_state = state::inactive;
update();
}
}
void
ahci_port::update()
{
if (m_state == state::unimpl) return;
uint32_t detected = m_data->serial_status & 0x0f;
uint32_t power = (m_data->serial_status >> 8) & 0x0f;
if (detected == 0x3 && power == 0x1)
m_state = state::active;
else
m_state = state::inactive;
}
enum class ahci_hba::cap : uint32_t
{
cccs = 0x00000080, // Command completion coalescing
sam = 0x00040000, // ACHI-only mode
sclo = 0x01000000, // Command list override
ssntf = 0x40000000, // SNotification register
sncq = 0x40000000, // Native command queuing
s64a = 0x80000000 // 64bit addressing
};
IS_BITFIELD(ahci_hba::cap);
enum class ahci_hba::cap2 : uint32_t
{
boh = 0x00000001 // BIOS OS hand-off
};
IS_BITFIELD(ahci_hba::cap2);
ahci_hba::ahci_hba(pci_device *device)
{
page_manager *pm = page_manager::get();
uint32_t bar5 = device->get_bar(5);
m_bara = reinterpret_cast<uint32_t *>(bar5 & ~0xfffull);
pm->map_offset_pointer(reinterpret_cast<void **>(&m_bara), 0x2000);
ahci_port_data *port_data = reinterpret_cast<ahci_port_data *>(
kutil::offset_pointer(m_bara, 0x100));
uint32_t cap_reg = m_bara[0];
m_cap = static_cast<cap>(cap_reg);
m_cap2 = static_cast<cap2>(m_bara[6]);
uint32_t ports = (cap_reg & 0xf) + 1;
uint32_t slots = ((cap_reg >> 8) & 0x1f) + 1;
log::info(logs::driver, " %d ports", ports);
log::info(logs::driver, " %d command slots", slots);
m_ports.ensure_capacity(ports);
uint32_t pi = m_bara[3];
for (int i = 0; i < ports; ++i) {
bool impl = ((pi & (1 << i)) != 0);
m_ports.emplace(&port_data[i], impl);
}
}
ahci_driver::ahci_driver()
{
}
void
ahci_driver::register_device(pci_device *device)
{
log::info(logs::driver, "AHCI registering device %d:%d:%d:",
device->bus(), device->device(), device->function());
ahci_hba &hba = m_devices.emplace(device);
}

View File

@@ -1,72 +0,0 @@
#pragma once
/// \file ahci.h
/// AHCI driver and related definitions
#include "kutil/vector.h"
class pci_device;
struct ahci_port_data;
/// A port on an AHCI HBA
class ahci_port
{
public:
/// Constructor.
/// \arg data Pointer to the device's registers for this port
/// \arg impl Whether this port is marked as implemented in the HBA
ahci_port(ahci_port_data *data, bool impl);
enum class state { unimpl, inactive, active };
/// Get the current state of this device
/// \returns An enum representing the state
state get_state() const { return m_state; }
/// Update the state of this object from the register data
void update();
private:
ahci_port_data *m_data;
state m_state;
};
/// An AHCI host bus adapter
class ahci_hba
{
public:
/// Constructor.
/// \arg device The PCI device for this HBA
ahci_hba(pci_device *device);
private:
enum class cap : uint32_t;
enum class cap2 : uint32_t;
pci_device *m_device;
uint32_t *m_bara;
kutil::vector<ahci_port> m_ports;
cap m_cap;
cap2 m_cap2;
};
/// Basic AHCI driver
class ahci_driver
{
public:
/// Constructor.
ahci_driver();
/// Register a device with the driver
/// \arg device The PCI device to handle
void register_device(pci_device *device);
/// Unregister a device from the driver
/// \arg device The PCI device to remove
void unregister_device(pci_device *device);
private:
kutil::vector<ahci_hba> m_devices;
};

57
src/kernel/ahci/ata.h Normal file
View File

@@ -0,0 +1,57 @@
#pragma once
/// \file ata.h
/// Definitions for ATA codes
#include <stdint.h>
#include "kutil/enum_bitfields.h"
namespace ahci {
enum class ata_status : uint8_t
{
error = 0x01,
index = 0x02,
corrected = 0x04,
data_ready = 0x08,
seek_done = 0x10,
fault = 0x20,
ready = 0x40,
busy = 0x80
};
enum class ata_error : uint8_t
{
amnf = 0x01, // Address mark not found
tkznf = 0x02, // Track 0 not found
abort = 0x04, // Command abort
mcr = 0x08, // No media
idnf = 0x10, // Id not found
mc = 0x20, // No media
unc = 0x40, // Uncorrectable
bbk = 0x80, // Bad sector
};
enum class ata_cmd : uint8_t
{
read_pio = 0x20,
read_pio_ext = 0x24,
read_dma = 0xC8,
read_dma_ext = 0x25,
write_pio = 0x30,
write_pio_ext = 0x34,
write_dma = 0xCA,
write_dma_ext = 0x35,
cache_flush = 0xE7,
cache_flush_ext = 0xEA,
packet = 0xA0,
identify_packet = 0xA1,
identify = 0xEC
};
} // namespace ahci
IS_BITFIELD(ahci::ata_status);
IS_BITFIELD(ahci::ata_error);

View File

@@ -0,0 +1,19 @@
#include "kutil/enum_bitfields.h"
#include "ahci/driver.h"
#include "log.h"
#include "pci.h"
ahci_driver::ahci_driver()
{
}
void
ahci_driver::register_device(pci_device *device)
{
log::info(logs::driver, "AHCI registering device %d:%d:%d:",
device->bus(), device->device(), device->function());
m_devices.emplace(device);
}

28
src/kernel/ahci/driver.h Normal file
View File

@@ -0,0 +1,28 @@
#pragma once
/// \file ahci.h
/// AHCI driver and related definitions
#include "kutil/vector.h"
#include "ahci/hba.h"
class pci_device;
/// Basic AHCI driver
class ahci_driver
{
public:
/// Constructor.
ahci_driver();
/// Register a device with the driver
/// \arg device The PCI device to handle
void register_device(pci_device *device);
/// Unregister a device from the driver
/// \arg device The PCI device to remove
void unregister_device(pci_device *device);
private:
kutil::vector<ahci::hba> m_devices;
};

49
src/kernel/ahci/fis.h Normal file
View File

@@ -0,0 +1,49 @@
#pragma once
/// \file fis.h
/// Definitions for Frame Information Structure types. (Not for pescatarians.)
#include <stdint.h>
namespace ahci {
enum class ata_cmd : uint8_t;
enum class fis_type : uint8_t
{
register_h2d = 0x27,
register_d2h = 0x34,
dma_activate = 0x39,
dma_setup = 0x41,
data = 0x46,
bist = 0x58,
pio_setup = 0x5f,
device_bits = 0xa1
};
struct fis_register_h2d
{
fis_type type;
uint8_t pm_port; // high bit (0x80) is set for the command register flag
ata_cmd command;
uint8_t features;
uint8_t lba0;
uint8_t lba1;
uint8_t lba2;
uint8_t device;
uint8_t lba3;
uint8_t lba4;
uint8_t lba5;
uint8_t features2;
uint8_t count0;
uint8_t count1;
uint8_t icc;
uint8_t control;
uint32_t reserved;
};
} // namespace ahci

76
src/kernel/ahci/hba.cpp Normal file
View File

@@ -0,0 +1,76 @@
#include <stdint.h>
#include "ahci/hba.h"
#include "log.h"
#include "page_manager.h"
#include "pci.h"
IS_BITFIELD(ahci::hba_cap);
IS_BITFIELD(ahci::hba_cap2);
namespace ahci {
enum class hba_cap : uint32_t
{
ccc = 0x00000080, // Command completion coalescing
ahci_only = 0x00040000, // ACHI-only mode
clo = 0x01000000, // Command list override
snotify = 0x40000000, // SNotification register
ncq = 0x40000000, // Native command queuing
addr64 = 0x80000000 // 64bit addressing
};
enum class hba_cap2 : uint32_t
{
handoff = 0x00000001 // BIOS OS hand-off
};
struct hba_data
{
hba_cap cap;
uint32_t host_control;
uint32_t int_status;
uint32_t port_impl;
uint32_t version;
uint32_t ccc_control;
uint32_t ccc_ports;
uint32_t em_location;
uint32_t em_control;
hba_cap2 cap2;
uint32_t handoff_control;
} __attribute__ ((packed));
hba::hba(pci_device *device)
{
page_manager *pm = page_manager::get();
uint32_t bar5 = device->get_bar(5);
m_data = reinterpret_cast<hba_data *>(bar5 & ~0xfffull);
pm->map_offset_pointer(reinterpret_cast<void **>(&m_data), 0x2000);
uint32_t icap = static_cast<uint32_t>(m_data->cap);
unsigned ports = (icap & 0xf) + 1;
unsigned slots = ((icap >> 8) & 0x1f) + 1;
log::debug(logs::driver, " %d ports", ports);
log::debug(logs::driver, " %d command slots", slots);
port_data *pd = reinterpret_cast<port_data *>(
kutil::offset_pointer(m_data, 0x100));
m_ports.ensure_capacity(ports);
for (unsigned i = 0; i < ports; ++i) {
log::debug(logs::driver, " Registering port %d", i);
bool impl = ((m_data->port_impl & (1 << i)) != 0);
m_ports.emplace(kutil::offset_pointer(pd, 0x80 * i), impl);
}
}
} // namespace ahci

31
src/kernel/ahci/hba.h Normal file
View File

@@ -0,0 +1,31 @@
#pragma once
/// \file hba.h
/// Definition for AHCI host bus adapters
#include "kutil/vector.h"
#include "ahci/port.h"
class pci_device;
namespace ahci {
enum class hba_cap : uint32_t;
enum class hba_cap2 : uint32_t;
struct hba_data;
/// An AHCI host bus adapter
class hba
{
public:
/// Constructor.
/// \arg device The PCI device for this HBA
hba(pci_device *device);
private:
pci_device *m_device;
hba_data *m_data;
kutil::vector<port> m_ports;
};
} // namespace ahci

342
src/kernel/ahci/port.cpp Normal file
View File

@@ -0,0 +1,342 @@
#include <algorithm>
#include "kutil/assert.h"
#include "kutil/enum_bitfields.h"
#include "ahci/ata.h"
#include "ahci/fis.h"
#include "ahci/port.h"
#include "io.h"
#include "log.h"
#include "page_manager.h"
IS_BITFIELD(ahci::port_cmd);
namespace ahci {
const unsigned max_prd_count = 16;
enum class cmd_list_flags : uint16_t
{
atapi = 0x0020,
write = 0x0040,
prefetch = 0x0080,
reset = 0x0100,
bist = 0x0200,
clear_busy = 0x0400
};
inline cmd_list_flags
cmd_list_fis_size(uint8_t size)
{
return static_cast<cmd_list_flags>((size/4) & 0x1f);
}
struct cmd_list_entry
{
cmd_list_flags flags;
uint16_t prd_table_length;
uint32_t prd_byte_count;
uint32_t cmd_table_base_low;
uint32_t cmd_table_base_high;
uint32_t reserved[4];
} __attribute__ ((packed));
struct prdt_entry
{
uint32_t data_base_low;
uint32_t data_base_high;
uint32_t reserved;
uint32_t byte_count;
} __attribute__ ((packed));
struct cmd_table
{
uint8_t cmd_fis[64];
uint8_t atapi_cmd[16];
uint8_t reserved[48];
prdt_entry entries[0];
} __attribute__ ((packed));
enum class port_cmd : uint32_t
{
start = 0x00000001,
spinup = 0x00000002,
poweron = 0x00000004,
clo = 0x00000008,
fis_recv = 0x00000010,
fisr_running = 0x00004000,
cmds_running = 0x00008000,
none = 0x00000000
};
struct port_data
{
uint32_t cmd_base_low;
uint32_t cmd_base_high;
uint32_t fis_base_low;
uint32_t fis_base_high;
uint32_t interrupt_status;
uint32_t interrupt_enable;
port_cmd command;
uint32_t reserved0;
uint8_t task_file_status;
uint8_t task_file_error;
uint16_t reserved1;
uint32_t signature;
uint32_t serial_status;
uint32_t serial_control;
uint32_t serial_error;
uint32_t serial_active;
uint32_t cmd_issue;
uint32_t serial_notify;
uint32_t fis_switching;
uint32_t dev_sleep;
uint8_t reserved2[40];
uint8_t vendor[16];
} __attribute__ ((packed));
port::port(port_data *data, bool impl) :
m_state(state::unimpl),
m_data(data),
m_fis(nullptr),
m_cmd_list(nullptr),
m_cmd_table(nullptr)
{
if (impl) {
m_state = state::inactive;
update();
if (m_state == state::active)
rebase();
}
}
port::~port()
{
if (m_cmd_list) {
page_manager *pm = page_manager::get();
pm->unmap_pages(m_cmd_list, 3);
}
}
void
port::update()
{
if (m_state == state::unimpl) return;
uint32_t detected = m_data->serial_status & 0x0f;
uint32_t power = (m_data->serial_status >> 8) & 0x0f;
if (detected == 0x3 && power == 0x1)
m_state = state::active;
else
m_state = state::inactive;
}
bool
port::busy()
{
return (m_data->task_file_status & 0x88) != 0;
}
void
port::start_commands()
{
while (bitfield_has(m_data->command, port_cmd::cmds_running))
io_wait();
m_data->command |= port_cmd::fis_recv;
m_data->command |= port_cmd::start;
}
void
port::stop_commands()
{
m_data->command &= ~port_cmd::start;
while (
bitfield_has(m_data->command, port_cmd::cmds_running) ||
bitfield_has(m_data->command, port_cmd::fisr_running))
io_wait();
m_data->command &= ~port_cmd::fis_recv;
}
bool
port::read(uint64_t sector, size_t length)
{
m_data->interrupt_status = ~0u;
int slot = get_cmd_slot();
if (slot < 0) {
log::info(logs::driver, "AHCI could not get a free command slot.");
return false;
}
page_manager *pm = page_manager::get();
cmd_list_entry &ent = m_cmd_list[slot];
cmd_table &cmdt = m_cmd_table[slot];
kutil::memset(&cmdt, 0, sizeof(cmd_table) +
max_prd_count * sizeof(prdt_entry));
ent.flags = cmd_list_fis_size(sizeof(fis_register_h2d));
void *buffers[32];
size_t remaining = length;
for (int i = 0; i < max_prd_count; ++i) {
size_t prd_len = std::min(remaining, 0x200000ul);
remaining -= prd_len;
void *mem = pm->map_offset_pages(page_count(prd_len));
buffers[i] = mem;
addr_t phys = pm->offset_phys(mem);
cmdt.entries[i].data_base_low = phys & 0xffffffff;
cmdt.entries[i].data_base_high = phys >> 32;
cmdt.entries[i].byte_count = prd_len - 1;
if (remaining == 0 || i == max_prd_count - 1) {
// If this is the last one, set the interrupt flag
cmdt.entries[i].byte_count |= 0x80000000;
ent.prd_table_length = i;
break;
}
}
fis_register_h2d *fis = reinterpret_cast<fis_register_h2d *>(&cmdt.cmd_fis);
fis->type = fis_type::register_h2d;
fis->pm_port = 0x80; // set command register flag
fis->command = ata_cmd::read_dma_ext;
fis->lba0 = (sector ) & 0xff;
fis->lba1 = (sector >> 8) & 0xff;
fis->lba2 = (sector >> 16) & 0xff;
fis->lba3 = (sector >> 24) & 0xff;
fis->lba4 = (sector >> 32) & 0xff;
fis->lba5 = (sector >> 40) & 0xff;
size_t count = length >> 9; // count is in sectors
fis->count0 = (count ) & 0xff;
fis->count1 = (count >> 8) & 0xff;
const int max_tries = 10;
int tries = 0;
while (busy()) {
if (++tries == max_tries) {
log::warn(logs::driver, "AHCI port was busy too long");
return false;
}
io_wait();
}
if (tries == max_tries) {
// TODO: clean up!!!
return false;
}
m_data->cmd_issue |= (1 << slot);
// TODO: interrupt-based
while (true) {
if ((m_data->cmd_issue & (1 << slot)) == 0) break;
if (m_data->interrupt_status & 0x40000000) {
log::error(logs::driver, "AHCI task file error");
// TODO: clean up!
return false;
}
io_wait();
}
if (m_data->interrupt_status & 0x40000000) {
log::error(logs::driver, "AHCI task file error");
// TODO: clean up!
return false;
}
log::warn(logs::driver, "AHCI read success!? '%s'", buffers[0]);
return true;
}
void
port::rebase()
{
kassert(!m_cmd_list, "AHCI port called rebase() twice");
page_manager *pm = page_manager::get();
size_t prd_size = sizeof(cmd_table) + (max_prd_count * sizeof(prdt_entry));
// 1 for FIS + command list, N for PRD
size_t pages = 1 + page_count(prd_size * 32);
void *mem = pm->map_offset_pages(pages);
addr_t phys = pm->offset_phys(mem);
log::debug(logs::driver, "Rebasing address for AHCI port to %lx [%d]", mem, pages);
stop_commands();
// Command list
m_cmd_list = reinterpret_cast<cmd_list_entry *>(mem);
m_data->cmd_base_low = phys & 0xffffffff;
m_data->cmd_base_high = phys >> 32;
kutil::memset(mem, 0, 1024);
mem = kutil::offset_pointer(mem, 32 * sizeof(cmd_list_entry));
phys = pm->offset_phys(mem);
// FIS
m_fis = mem;
m_data->fis_base_low = phys & 0xffffffff;
m_data->fis_base_high = phys >> 32;
kutil::memset(mem, 0, 256);
mem = page_align(kutil::offset_pointer(mem, 256));
phys = pm->offset_phys(mem);
// Command table
m_cmd_table = reinterpret_cast<cmd_table *>(mem);
size_t cmdt_len = sizeof(cmd_table) +
max_prd_count * sizeof(prdt_entry);
kutil::memset(m_cmd_table, 0, cmdt_len * 32);
// set up each entry in the command list to point to the
// corresponding command table
for (int i = 0; i < 32; ++i) {
m_cmd_list[i].prd_table_length = max_prd_count;
m_cmd_list[i].cmd_table_base_low = phys & 0xffffffff;
m_cmd_list[i].cmd_table_base_high = phys >> 32;
mem = kutil::offset_pointer(mem, cmdt_len);
phys = pm->offset_phys(mem);
}
start_commands();
}
int
port::get_cmd_slot()
{
uint32_t used = (m_data->serial_active | m_data->cmd_issue);
for (int i = 0; i < 32; ++i)
if (used & (1 << i)) return i;
return -1;
}
} // namespace ahci

68
src/kernel/ahci/port.h Normal file
View File

@@ -0,0 +1,68 @@
#pragma once
/// \file port.h
/// Definition for AHCI ports
#include <stddef.h>
#include <stdint.h>
namespace ahci {
struct cmd_list_entry;
struct cmd_table;
enum class port_cmd : uint32_t;
struct port_data;
/// A port on an AHCI HBA
class port
{
public:
/// Constructor.
/// \arg data Pointer to the device's registers for this port
/// \arg impl Whether this port is marked as implemented in the HBA
port(port_data *data, bool impl);
/// Destructor
~port();
enum class state { unimpl, inactive, active };
/// Get the current state of this device
/// \returns An enum representing the state
state get_state() const { return m_state; }
/// Update the state of this object from the register data
void update();
/// Return whether the port is currently busy
bool busy();
/// Start command processing from this port
void start_commands();
/// Stop command processing from this port
void stop_commands();
/// Read data from the drive.
/// \arg sector Starting sector to read
/// \arg length Number of bytes to read
/// \returns True if the command succeeded
bool read(uint64_t sector, size_t length);
private:
/// Rebase the port command structures to a new location in system
/// memory, to be allocated from the page manager.
void rebase();
/// Get a free command slot
/// \returns The index of the command slot, or -1 if none available
int get_cmd_slot();
state m_state;
port_data *m_data;
void *m_fis;
cmd_list_entry *m_cmd_list;
cmd_table *m_cmd_table;
};
} // namespace ahci

View File

@@ -4,7 +4,7 @@
#include "kutil/assert.h" #include "kutil/assert.h"
#include "kutil/memory.h" #include "kutil/memory.h"
#include "acpi_tables.h" #include "acpi_tables.h"
#include "ahci.h" #include "ahci/driver.h"
#include "apic.h" #include "apic.h"
#include "device_manager.h" #include "device_manager.h"
#include "interrupts.h" #include "interrupts.h"
@@ -14,7 +14,7 @@
static const char expected_signature[] = "RSD PTR "; static const char expected_signature[] = "RSD PTR ";
ahci_driver ahci; ahci_driver ahcid;
struct acpi1_rsdp struct acpi1_rsdp
{ {
@@ -278,6 +278,6 @@ device_manager::init_drivers()
device.bus(), device.device(), device.function()); device.bus(), device.device(), device.function());
} }
ahci.register_device(&device); ahcid.register_device(&device);
} }
} }

View File

@@ -353,6 +353,9 @@ page_manager::map_pages(addr_t address, size_t count)
block->physical_address = phys; block->physical_address = phys;
block->virtual_address = address; block->virtual_address = address;
block->count = n; block->count = n;
block->flags =
page_block_flags::used |
page_block_flags::mapped;
page_block::insert(m_used, block); page_block::insert(m_used, block);
page_in(pml4, phys, address, n); page_in(pml4, phys, address, n);
@@ -364,12 +367,59 @@ page_manager::map_pages(addr_t address, size_t count)
return ret; return ret;
} }
void void *
page_manager::unmap_pages(addr_t address, size_t count) page_manager::map_offset_pages(size_t count)
{ {
page_table *pml4 = get_pml4();
page_block *free = m_free;
page_block *prev = nullptr;
log::debug(logs::memory, "Got request to offset map %d pages", count);
while (free) {
if (free->count < count) {
prev = free;
free = free->next;
continue;
}
page_block *used = get_block();
used->count = count;
used->physical_address = free->physical_address;
used->virtual_address = used->physical_address + page_offset;
used->flags =
page_block_flags::used |
page_block_flags::mapped;
page_block::insert(m_used, used);
free->physical_address += count * page_size;
free->count -= count;
if (free->count == 0) {
if (prev)
prev->next = free->next;
else
m_free = free->next;
free->zero(m_block_cache);
m_block_cache = free;
}
page_in(pml4, used->physical_address, used->virtual_address, count);
return reinterpret_cast<void *>(used->virtual_address);
}
return nullptr;
}
void
page_manager::unmap_pages(void* address, size_t count)
{
addr_t addr = reinterpret_cast<addr_t>(address);
page_block **prev = &m_used; page_block **prev = &m_used;
page_block *cur = m_used; page_block *cur = m_used;
while (cur && !cur->contains(address)) { while (cur && !cur->contains(addr)) {
prev = &cur->next; prev = &cur->next;
cur = cur->next; cur = cur->next;
} }
@@ -377,10 +427,10 @@ page_manager::unmap_pages(addr_t address, size_t count)
kassert(cur, "Couldn't find existing mapped pages to unmap"); kassert(cur, "Couldn't find existing mapped pages to unmap");
size_t size = page_size * count; size_t size = page_size * count;
addr_t end = address + size; addr_t end = addr + size;
while (cur && cur->contains(address)) { while (cur && cur->contains(addr)) {
size_t leading = address - cur->virtual_address; size_t leading = addr - cur->virtual_address;
size_t trailing = size_t trailing =
end > cur->virtual_end() ? end > cur->virtual_end() ?
0 : (cur->virtual_end() - end); 0 : (cur->virtual_end() - end);
@@ -416,7 +466,7 @@ page_manager::unmap_pages(addr_t address, size_t count)
cur->next = trail_block; cur->next = trail_block;
} }
address += cur->count * page_size; addr += cur->count * page_size;
page_block *next = cur->next; page_block *next = cur->next;
*prev = cur->next; *prev = cur->next;

View File

@@ -34,16 +34,39 @@ public:
/// \returns A pointer to the start of the mapped region /// \returns A pointer to the start of the mapped region
void * map_pages(addr_t address, size_t count); void * map_pages(addr_t address, size_t count);
/// Allocate and map contiguous pages into virtual memory, with
/// a constant offset from their physical address.
/// \arg count The number of pages to map
/// \returns A pointer to the start of the mapped region, or
/// nullptr if no region could be found to fit the request.
void * map_offset_pages(size_t count);
/// Unmap existing pages from memory. /// Unmap existing pages from memory.
/// \arg address The virtual address of the memory to unmap /// \arg address The virtual address of the memory to unmap
/// \arg count The number of pages to unmap /// \arg count The number of pages to unmap
void unmap_pages(addr_t address, size_t count); void unmap_pages(void *address, size_t count);
/// Offset-map a pointer. No physical pages will be mapped. /// Offset-map a pointer. No physical pages will be mapped.
/// \arg pointer Pointer to a pointer to the memory area to be mapped /// \arg pointer Pointer to a pointer to the memory area to be mapped
/// \arg length Length of the memory area to be mapped /// \arg length Length of the memory area to be mapped
void map_offset_pointer(void **pointer, size_t length); void map_offset_pointer(void **pointer, size_t length);
/// Get the physical address of an offset-mapped pointer
/// \arg p Virtual address of memory that has been offset-mapped
/// \returns Physical address of the memory pointed to by p
inline addr_t offset_phys(void *p) const
{
return reinterpret_cast<addr_t>(kutil::offset_pointer(p, -page_offset));
}
/// Get the virtual address of an offset-mapped physical address
/// \arg a Physical address of memory that has been offset-mapped
/// \returns Virtual address of the memory at address a
inline void * offset_virt(addr_t a) const
{
return kutil::offset_pointer(reinterpret_cast<void *>(a), page_offset);
}
/// Log the current free/used block lists. /// Log the current free/used block lists.
void dump_blocks(); void dump_blocks();
@@ -179,7 +202,7 @@ struct page_block
page_block_flags flags; page_block_flags flags;
page_block *next; page_block *next;
inline bool has_flag(page_block_flags f) const { return bitfield_contains(flags, f); } inline bool has_flag(page_block_flags f) const { return bitfield_has(flags, f); }
inline addr_t physical_end() const { return physical_address + (count * page_manager::page_size); } inline addr_t physical_end() const { return physical_address + (count * page_manager::page_size); }
inline addr_t virtual_end() const { return virtual_address + (count * page_manager::page_size); } inline addr_t virtual_end() const { return virtual_address + (count * page_manager::page_size); }
@@ -279,16 +302,29 @@ struct page_table_indices
/// Calculate a page-aligned address. /// Calculate a page-aligned address.
/// \arg p The address to align. /// \arg p The address to align.
/// \returns The next page-aligned address _after_ `p`. /// \returns The next page-aligned address _after_ `p`.
template <typename T> inline T page_align(T p) template <typename T> inline T
page_align(T p)
{ {
return ((p - 1) & ~(page_manager::page_size - 1)) + page_manager::page_size; return reinterpret_cast<T>(
((reinterpret_cast<addr_t>(p) - 1) & ~(page_manager::page_size - 1))
+ page_manager::page_size);
} }
/// Calculate a page-table-aligned address. That is, an address that is /// Calculate a page-table-aligned address. That is, an address that is
/// page-aligned to the first page in a page table. /// page-aligned to the first page in a page table.
/// \arg p The address to align. /// \arg p The address to align.
/// \returns The next page-table-aligned address _after_ `p`. /// \returns The next page-table-aligned address _after_ `p`.
template <typename T> inline T page_table_align(T p) { return ((p - 1) & ~0x1fffffull) + 0x200000; } template <typename T> inline T
page_table_align(T p)
{
return ((p - 1) & ~0x1fffffull) + 0x200000;
}
/// Calculate the number of pages needed for the give number of bytes.
/// \arg n Number of bytes
/// \returns Number of pages
inline size_t page_count(size_t n) { return ((n - 1) / page_manager::page_size) + 1; }
/// Bootstrap the memory managers. /// Bootstrap the memory managers.

View File

@@ -6,7 +6,7 @@ template<typename E>
struct is_enum_bitfield { static constexpr bool value = false; }; struct is_enum_bitfield { static constexpr bool value = false; };
#define IS_BITFIELD(name) \ #define IS_BITFIELD(name) \
template<> struct is_enum_bitfield<name> {static constexpr bool value=true;} template<> struct ::is_enum_bitfield<name> {static constexpr bool value=true;}
template <typename E> template <typename E>
typename std::enable_if<is_enum_bitfield<E>::value,E>::type typename std::enable_if<is_enum_bitfield<E>::value,E>::type
@@ -84,7 +84,7 @@ operator ! (E rhs)
template <typename E> template <typename E>
typename std::enable_if<is_enum_bitfield<E>::value,bool>::type typename std::enable_if<is_enum_bitfield<E>::value,bool>::type
bitfield_contains(E set, E flag) bitfield_has(E set, E flag)
{ {
return (set & flag) == flag; return (set & flag) == flag;
} }

View File

@@ -51,7 +51,7 @@ inline T read_from(const void *p)
/// \arg n The offset in bytes /// \arg n The offset in bytes
/// \returns The offset pointer /// \returns The offset pointer
template <typename T> template <typename T>
inline T * offset_pointer(T *p, size_t n) inline T * offset_pointer(T *p, ptrdiff_t n)
{ {
return reinterpret_cast<T *>(reinterpret_cast<addr_t>(p) + n); return reinterpret_cast<T *>(reinterpret_cast<addr_t>(p) + n);
} }