[project] Clean up src/ tree

A long overdue cleanup of the src/ tree.

- Moved src/drivers to src/user because it contains more than drivers
- Removed src/drivers/ahci because it's unused - will restore it when I
  make a real AHCI driver
- Removed unused src/tools
- Moved kernel.ld (the only used file under src/arch) to src/kernel for
  now, if/when there's a multi-platform effort that should be figured
  out as part of it
- Removed the rest of the unused src/arch
- Renamed 'fb' to 'drv.uefi_fb' and 'nulldrv' to 'testapp'
This commit is contained in:
Justin C. Miller
2021-07-25 23:47:23 -07:00
parent ec9e34c970
commit 269324c553
34 changed files with 10 additions and 1471 deletions

View File

@@ -39,18 +39,18 @@ build ${build_root}/fatroot/jsix.elf : cp ${build_root}/jsix.elf
build ${build_root}/fatroot/efi/boot/bootx64.efi : cp ${build_root}/boot/boot.efi
name = bootloader to FAT image
build ${build_root}/fatroot/nulldrv.elf : cp ${build_root}/user/nulldrv.elf
build ${build_root}/fatroot/testapp.elf : cp ${build_root}/user/testapp.elf
name = null driver to FAT image
build ${build_root}/fatroot/fb.elf : cp ${build_root}/user/fb.elf
name = fb driver to FAT image
build ${build_root}/fatroot/drv.uefi_fb.elf : cp ${build_root}/user/drv.uefi_fb.elf
name = UEFI framebuffer driver to FAT image
build ${build_root}/fatroot/symbol_table.dat : makest ${build_root}/jsix.elf
build ${build_root}/jsix.img : makefat | $
${build_root}/fatroot/symbol_table.dat $
${build_root}/fatroot/nulldrv.elf $
${build_root}/fatroot/fb.elf $
${build_root}/fatroot/testapp.elf $
${build_root}/fatroot/drv.uefi_fb.elf $
${build_root}/fatroot/jsix.elf $
${build_root}/fatroot/efi/boot/bootx64.efi
name = jsix.img

View File

@@ -79,7 +79,7 @@ cxxflags = [
ldflags = [
"${ldflags}",
"-T", "${source_root}/src/arch/x86_64/kernel.ld",
"-T", "${source_root}/src/kernel/kernel.ld",
"-g",
"-nostdlib",
"-Bstatic",

View File

@@ -1,6 +0,0 @@
AS := nasm
ASFLAGS := -felf64
LDFLAGS := -m elf_x86_64
CFLAGS := -march=nocona -m64
# vim:ft=make

View File

@@ -1 +0,0 @@
const char *KERNEL_PLATFORM = "x86_64";

View File

@@ -40,8 +40,8 @@ struct program_desc
const program_desc program_list[] = {
{L"kernel", L"jsix.elf"},
{L"null driver", L"nulldrv.elf"},
{L"fb driver", L"fb.elf"},
{L"test application", L"testapp.elf"},
{L"UEFI framebuffer driver", L"drv.uefi_fb.elf"},
};
/// Change a pointer to point to the higher-half linear-offset version

View File

@@ -1,67 +0,0 @@
#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
};
enum class sata_signature : uint32_t
{
none = 0x00000000,
sata_drive = 0x00000101,
satapi_drive = 0xeb140101,
enclosure = 0xc33c0101,
port_muxer = 0x96690101
};
} // namespace ahci
IS_BITFIELD(ahci::ata_status);
IS_BITFIELD(ahci::ata_error);

View File

@@ -1,22 +0,0 @@
#include "kutil/enum_bitfields.h"
#include "ahci/driver.h"
#include "log.h"
#include "pci.h"
namespace ahci {
driver::driver()
{
}
void
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);
}
} // namespace

View File

@@ -1,31 +0,0 @@
#pragma once
/// \file ahci.h
/// AHCI driver and related definitions
#include "kutil/vector.h"
#include "ahci/hba.h"
class pci_device;
namespace ahci {
/// Basic AHCI driver
class driver
{
public:
/// Constructor.
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;
};
} // namespace

View File

@@ -1,49 +0,0 @@
#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

View File

@@ -1,114 +0,0 @@
#include "kutil/assert.h"
#include "kutil/guid.h"
#include "kutil/memory.h"
#include "device_manager.h"
#include "fs/gpt.h"
#include "log.h"
namespace fs {
const kutil::guid efi_system_part = kutil::make_guid(0xC12A7328, 0xF81F, 0x11D2, 0xBA4B, 0x00A0C93EC93B);
const kutil::guid efi_unused_part = kutil::make_guid(0, 0, 0, 0, 0);
const uint64_t gpt_signature = 0x5452415020494645; // "EFI PART"
const size_t block_size = 512;
struct gpt_header
{
uint64_t signature;
uint32_t revision;
uint32_t headersize;
uint32_t crc32;
uint32_t reserved;
uint64_t my_lba;
uint64_t alt_lba;
uint64_t first_usable_lba;
uint64_t last_usable_lba;
kutil::guid disk_guid;
uint64_t table_lba;
uint32_t entry_count;
uint32_t entry_length;
uint32_t table_crc32;
} __attribute__ ((packed));
struct gpt_entry
{
kutil::guid type;
kutil::guid part_guid;
uint64_t start_lba;
uint64_t end_lba;
uint64_t attributes;
uint16_t name_wide[36];
} __attribute__ ((packed));
partition::partition(block_device *parent, size_t start, size_t length) :
m_parent(parent),
m_start(start),
m_length(length)
{
}
size_t
partition::read(size_t offset, size_t length, void *buffer)
{
if (offset + length > m_length)
offset = m_length - offset;
return m_parent->read(m_start + offset, length, buffer);
}
unsigned
partition::load(block_device *device)
{
// Read LBA 1
uint8_t block[block_size];
size_t count = device->read(block_size, block_size, &block);
kassert(count == block_size, "Short read for GPT header.");
gpt_header *header = reinterpret_cast<gpt_header *>(&block);
if (header->signature != gpt_signature)
return 0;
size_t arraysize = header->entry_length * header->entry_count;
log::debug(logs::fs, "Found GPT header: %d paritions, size 0x%lx",
header->entry_count, arraysize);
uint8_t *array = new uint8_t[arraysize];
count = device->read(block_size * header->table_lba, arraysize, array);
kassert(count == arraysize, "Short read for GPT entry array.");
auto &dm = device_manager::get();
unsigned found = 0;
gpt_entry *entry0 = reinterpret_cast<gpt_entry *>(array);
for (uint32_t i = 0; i < header->entry_count; ++i) {
gpt_entry *entry = kutil::offset_pointer(entry0, i * header->entry_length);
if (entry->type == efi_unused_part) continue;
// TODO: real UTF16->UTF8
char name[sizeof(gpt_entry::name_wide) / 2];
for (int i = 0; i < sizeof(name); ++i)
name[i] = entry->name_wide[i];
log::debug(logs::fs, "Found partition %02x at %lx-%lx", i, entry->start_lba, entry->end_lba);
if (entry->type == efi_system_part)
log::debug(logs::fs, " type EFI SYSTEM PARTITION");
else
log::debug(logs::fs, " type %G", entry->type);
log::debug(logs::fs, " name %s", name);
log::debug(logs::fs, " attr %016lx", entry->attributes);
found += 1;
partition *part = new partition(
device,
entry->start_lba * block_size,
(entry->end_lba - entry->start_lba) * block_size);
dm.register_block_device(part);
}
return found;
}
} // namespace fs

View File

@@ -1,37 +0,0 @@
#pragma once
/// \file gpt.h
/// Definitions for dealing with GUID Partition Tables
#include "block_device.h"
namespace fs {
class partition :
public block_device
{
public:
/// Constructor.
/// \arg parent The block device this partition is a part of
/// \arg start The starting offset in bytes from the start of the parent
/// \arg lenght The length in bytes of this partition
partition(block_device *parent, size_t start, size_t length);
/// Read bytes from the partition.
/// \arg offset The offset in bytes at which to start reading
/// \arg length The number of bytes to read
/// \arg buffer [out] Data is read into this buffer
/// \returns The number of bytes read
virtual size_t read(size_t offset, size_t length, void *buffer);
/// Find partitions on a block device and add them to the device manager
/// \arg device The device to search for partitions
/// \returns The number of partitions found
static unsigned load(block_device *device);
private:
block_device *m_parent;
size_t m_start;
size_t m_length;
};
} // namespace fs

View File

@@ -1,143 +0,0 @@
#include <stdint.h>
#include "ahci/ata.h"
#include "ahci/hba.h"
#include "console.h"
#include "device_manager.h"
#include "fs/gpt.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));
void irq_cb(void *data)
{
hba *h = reinterpret_cast<hba *>(data);
h->handle_interrupt();
}
hba::hba(pci_device *device)
{
page_manager *pm = page_manager::get();
device_manager &dm = device_manager::get();
uint32_t bar5 = device->get_bar(5);
log::debug(logs::driver, "HBA raw BAR5 is %08lx", bar5);
void *data = reinterpret_cast<void *>(bar5 & ~0xfffull);
pm->map_offset_pointer(&data, 0x2000);
m_data = reinterpret_cast<hba_data volatile *>(data);
if (! bitfield_has(m_data->cap, hba_cap::ahci_only))
m_data->host_control |= 0x80000000; // Enable AHCI mode
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: %08x", ports, m_data->port_impl);
log::debug(logs::driver, " %d command slots", slots);
auto *pd = reinterpret_cast<port_data volatile *>(
kutil::offset_pointer(m_data, 0x100));
bool needs_interrupt = false;
m_ports.ensure_capacity(ports);
for (unsigned i = 0; i < ports; ++i) {
bool impl = ((m_data->port_impl & (1 << i)) != 0);
port &p = m_ports.emplace(this, i, kutil::offset_pointer(pd, 0x80 * i), impl);
if (p.get_state() == port::state::active)
needs_interrupt = true;
}
if (needs_interrupt) {
device_manager::get().allocate_msi("AHCI Device", *device, irq_cb, this);
m_data->host_control |= 0x02; // enable interrupts
}
for (auto &p : m_ports) {
if (!p.active()) continue;
if (p.get_type() == sata_signature::sata_drive) {
p.sata_reconnect();
/*
if (fs::partition::load(&p) == 0)
dm.register_block_device(&p);
*/
}
}
}
void
hba::handle_interrupt()
{
uint32_t status = m_data->int_status;
for (auto &port : m_ports) {
if (status & (1 << port.index())) {
port.handle_interrupt();
}
}
// Write 1 to the handled interrupts
m_data->int_status = status;
}
void
hba::dump()
{
console *cons = console::get();
static const char *regs[] = {
" CAP", " GHC", " IS", " PI", " VS", " C3C",
" C3P", " EML", " EMC", "CAP2", "BOHC"
};
cons->printf("HBA Registers:\n");
auto *data = reinterpret_cast<uint32_t volatile *>(m_data);
for (int i = 0; i < 11; ++i) {
cons->printf(" %s: %08x\n", regs[i], data[i]);
}
cons->putc('\n');
}
} // namespace ahci

View File

@@ -1,37 +0,0 @@
#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);
/// Interrupt handler.
void handle_interrupt();
/// Dump the HBA registers to the console
void dump();
private:
pci_device *m_device;
hba_data volatile *m_data;
kutil::vector<port> m_ports;
};
} // namespace ahci

View File

@@ -1,623 +0,0 @@
#include <algorithm>
#include "kutil/assert.h"
#include "kutil/enum_bitfields.h"
#include "ahci/ata.h"
#include "ahci/hba.h"
#include "ahci/fis.h"
#include "ahci/port.h"
#include "console.h"
#include "io.h"
#include "log.h"
#include "page_manager.h"
namespace ahci {
enum class cmd_list_flags : uint16_t;
}
IS_BITFIELD(ahci::port_cmd);
IS_BITFIELD(volatile ahci::port_cmd);
IS_BITFIELD(ahci::cmd_list_flags);
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;
sata_signature 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(hba *device, uint8_t index, port_data volatile *data, bool impl) :
m_index(index),
m_type(sata_signature::none),
m_state(state::unimpl),
m_hba(device),
m_data(data),
m_fis(nullptr),
m_cmd_list(nullptr),
m_cmd_table(nullptr)
{
if (impl) {
m_state = state::inactive;
update();
}
}
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;
m_type = m_data->signature;
const char *name =
m_type == sata_signature::sata_drive ? "SATA" :
m_type == sata_signature::satapi_drive ? "SATAPI" :
"Other";
log::info(logs::driver, "Found device type %s at port %d", name, m_index);
rebase();
m_pending.set_size(32);
for (auto &pend : m_pending) {
pend.type = command_type::none;
}
// Clear any old pending interrupts and enable interrupts
m_data->interrupt_status = m_data->interrupt_status;
m_data->interrupt_enable = 0xffffffff;
} 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;
}
int
port::make_command(size_t length, fis_register_h2d **fis)
{
int slot = -1;
uint32_t used_slots =
m_data->serial_active |
m_data->cmd_issue |
m_data->interrupt_status;
for (int i = 0; i < 32; ++i) {
if (used_slots & (1 << i)) continue;
if (m_pending[i].type == command_type::none) {
slot = i;
break;
} else {
log::debug(logs::driver, "Type is %d", m_pending[i].type);
}
}
if (slot < 0) {
log::error(logs::driver, "AHCI could not get a free command slot.");
return -1;
}
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));
fis_register_h2d *cfis = reinterpret_cast<fis_register_h2d *>(&cmdt.cmd_fis);
kutil::memset(cfis, 0, sizeof(fis_register_h2d));
cfis->type = fis_type::register_h2d;
cfis->pm_port = 0x80; // set command register flag
*fis = cfis;
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));
kutil::memset(mem, 0xaf, prd_len);
uintptr_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 + 1;
break;
}
}
log::debug(logs::driver, "Created command, slot %d, %d PRD entries.",
slot, ent.prd_table_length);
return slot;
}
int
port::read_async(uint64_t offset, size_t length, void *dest)
{
fis_register_h2d *fis;
int slot = make_command(length, &fis);
if (slot < 0)
return 0;
cmd_table &cmdt = m_cmd_table[slot];
fis->command = ata_cmd::read_dma_ext;
fis->device = 0x40; // ATA8-ACS p.175
uint64_t sector = offset >> 9;
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 - 1) >> 9) + 1; // count is in sectors
fis->count0 = (count ) & 0xff;
fis->count1 = (count >> 8) & 0xff;
log::debug(logs::driver, "Reading %d sectors, starting from %d (0x%lx)",
count, sector, sector*512);
m_pending[slot].type = command_type::read;
m_pending[slot].offset = offset % 512;
m_pending[slot].count = 0;
m_pending[slot].data = dest;
if(issue_command(slot))
return slot;
else
return -1;
}
size_t
port::read(uint64_t offset, size_t length, void *dest)
{
int slot = read_async(offset, length, dest);
int timeout = 0;
while (m_pending[slot].type == command_type::read) {
if (timeout++ > 5) {
return 0;
}
asm("hlt");
}
kassert(m_pending[slot].type == command_type::finished,
"Read got unexpected command type");
size_t count = m_pending[slot].count;
m_pending[slot].type = command_type::none;
m_pending[slot].count = 0;
return count;
}
int
port::identify_async()
{
fis_register_h2d *fis;
int slot = make_command(512, &fis);
if (slot < 0)
return 0;
fis->command = ata_cmd::identify;
m_pending[slot].type = command_type::identify;
m_pending[slot].offset = 0;
m_pending[slot].count = 0;
m_pending[slot].data = 0;
if(issue_command(slot))
return slot;
else
return -1;
}
void
port::sata_reconnect()
{
m_data->serial_control |= 1;
io_wait(1000); // About 1ms
m_data->serial_control &= ~1;
}
bool
port::issue_command(int slot)
{
const int max_tries = 10;
int tries = 0;
while (busy()) {
if (++tries == max_tries) {
log::warn(logs::driver, "AHCI port was busy too long");
free_command(slot);
return false;
}
io_wait();
}
// Set bit in CI. Note that only new bits should be written, not
// previous state.
m_data->cmd_issue = (1 << slot);
return true;
}
void
port::handle_interrupt()
{
log::debug(logs::driver, "AHCI port %d got an interrupt", m_index);
// TODO: handle other states in interrupt_status
uint32_t is = m_data->interrupt_status;
if (is & 0x00000040) {
// Port connect status change: For now clear the "exchange"
// bit in SERR, this should probably kick off diagnostics.
m_data->serial_error = 0x04000000;
identify_async();
}
if (is & 0x40000000) {
log::error(logs::driver, "AHCI task file error");
dump();
kassert(0, "Task file error");
}
log::debug(logs::driver, "AHCI interrupt status: %08lx %08lx",
m_data->interrupt_status, m_data->serial_error);
uint32_t ci = m_data->cmd_issue;
for (int i = 0; i < 32; ++i) {
// Skip commands still listed as "issued"
if (ci & (1 << i)) continue;
// Any commands not still listed as "issued" that are still pending for
// the driver are now finished, so handle them.
pending &p = m_pending[i];
switch (p.type) {
case command_type::read:
finish_read(i);
break;
case command_type::identify:
finish_identify(i);
break;
default:
break;
}
}
// Clear the whole status register to mark it as handled
m_data->interrupt_status = m_data->interrupt_status;
}
void
port::finish_read(int slot)
{
page_manager *pm = page_manager::get();
cmd_table &cmdt = m_cmd_table[slot];
cmd_list_entry &ent = m_cmd_list[slot];
size_t count = 0;
void *p = m_pending[slot].data;
uint8_t offset = m_pending[slot].offset;
for (int i = 0; i < ent.prd_table_length; ++i) {
size_t prd_len = (cmdt.entries[i].byte_count & 0x7fffffff) + 1;
uintptr_t phys =
static_cast<uintptr_t>(cmdt.entries[i].data_base_low) |
static_cast<uintptr_t>(cmdt.entries[i].data_base_high) << 32;
void *mem = kutil::offset_pointer(pm->offset_virt(phys), offset);
log::debug(logs::driver, "Reading PRD %2d: %016lx->%016lx [%lxb]", i, mem, p, prd_len);
kutil::memcpy(p, mem, prd_len);
p = kutil::offset_pointer(p, prd_len - offset);
count += (prd_len - offset);
offset = 0;
pm->unmap_pages(mem, page_count(prd_len));
}
m_pending[slot].count = count;
m_pending[slot].type = command_type::finished;
m_pending[slot].data = nullptr;
}
static void
ident_strcpy(uint16_t *from, int words, char *dest)
{
for (int i = 0; i < words; ++i) {
*dest++ = *from >> 8;
*dest++ = *from & 0xff;
from++;
}
*dest = 0;
}
void
port::finish_identify(int slot)
{
page_manager *pm = page_manager::get();
cmd_table &cmdt = m_cmd_table[slot];
cmd_list_entry &ent = m_cmd_list[slot];
kassert(ent.prd_table_length == 1, "AHCI identify used multiple PRDs");
size_t prd_len = (cmdt.entries[0].byte_count & 0x7fffffff) + 1;
uintptr_t phys =
static_cast<uintptr_t>(cmdt.entries[0].data_base_low) |
static_cast<uintptr_t>(cmdt.entries[0].data_base_high) << 32;
log::debug(logs::driver, "Reading ident PRD:");
uint16_t *mem = reinterpret_cast<uint16_t *>(pm->offset_virt(phys));
char string[41];
ident_strcpy(&mem[10], 10, &string[0]);
log::debug(logs::driver, " Device serial: %s", string);
ident_strcpy(&mem[23], 4, &string[0]);
log::debug(logs::driver, " Device version: %s", string);
ident_strcpy(&mem[27], 20, &string[0]);
log::debug(logs::driver, " Device model: %s", string);
uint32_t sectors = mem[60] | (mem[61] << 16);
log::debug(logs::driver, " Max sectors: %xh", sectors);
uint16_t lb_size = mem[106];
log::debug(logs::driver, " lsects per psect: %d %s %s", 1 << (lb_size & 0xf),
lb_size & 0x20 ? "multiple logical per physical" : "",
lb_size & 0x10 ? "physical > 512b" : "");
uint32_t b_per_ls = 2 * (mem[117] | (mem[118] << 16));
log::debug(logs::driver, " b per lsect: %d", b_per_ls);
/*
for (int i=0; i<256; i += 4)
log::debug(logs::driver, " %3d: %04x %3d: %04x %3d: %04x %3d: %04x",
i, mem[i], i+1, mem[i+1], i+2, mem[i+2], i+3, mem[i+3]);
*/
pm->unmap_pages(mem, page_count(prd_len));
m_pending[slot].type = command_type::none;
}
void
port::free_command(int slot)
{
page_manager *pm = page_manager::get();
cmd_table &cmdt = m_cmd_table[slot];
cmd_list_entry &ent = m_cmd_list[slot];
for (int i = 0; i < ent.prd_table_length; ++i) {
size_t prd_len = (cmdt.entries[i].byte_count & 0x7fffffff) + 1;
uintptr_t phys =
static_cast<uintptr_t>(cmdt.entries[i].data_base_low) |
static_cast<uintptr_t>(cmdt.entries[i].data_base_high) << 32;
void *mem = pm->offset_virt(phys);
pm->unmap_pages(mem, page_count(prd_len));
}
ent.prd_table_length = max_prd_count;
}
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);
uintptr_t phys = pm->offset_phys(mem);
log::debug(logs::driver, "Rebasing address for AHCI port %d to %lx [%d]", m_index, 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();
}
void
port::dump()
{
console *cons = console::get();
static const char *regs[] = {
" CLB", "+CLB", " FB", " +FB", " IS", " IE",
" CMD", nullptr, " TFD", " SIG", "SSTS", "SCTL", "SERR",
"SACT", " CI", "SNTF", " FBS", "DEVS"
};
cons->printf("Port Registers:\n");
auto *data = reinterpret_cast<volatile uint32_t *>(m_data);
for (int i = 0; i < 18; ++i) {
if (regs[i]) cons->printf(" %s: %08x\n", regs[i], data[i]);
}
cons->putc('\n');
}
} // namespace ahci

View File

@@ -1,148 +0,0 @@
#pragma once
/// \file port.h
/// Definition for AHCI ports
#include <stddef.h>
#include <stdint.h>
#include "kutil/vector.h"
#include "block_device.h"
namespace ahci {
struct cmd_list_entry;
struct cmd_table;
struct fis_register_h2d;
class hba;
enum class sata_signature : uint32_t;
enum class port_cmd : uint32_t;
struct port_data;
/// A port on an AHCI HBA
class port :
public block_device
{
public:
/// Constructor.
/// \arg device The HBA device this port belongs to
/// \arg index Index of the port on its HBA
/// \arg data Pointer to the device's registers for this port
/// \arg impl Whether this port is marked as implemented in the HBA
port(hba *device, uint8_t index, port_data volatile *data, bool impl);
/// Destructor
~port();
/// Get the index of this port on the HBA
/// \returns The port index
inline uint8_t index() const { return m_index; }
enum class state : uint8_t { unimpl, inactive, active };
/// Get the current state of this device
/// \returns An enum representing the state
inline state get_state() const { return m_state; }
/// Check if this device is active
/// \returns True if the device state is active
inline bool active() const { return m_state == state::active; }
/// Get the type signature of this device
/// \returns An enum representing the type of device
inline sata_signature get_type() const { return m_type; }
/// 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();
/// Start a read operation from the drive.
/// \arg offset Offset to start from
/// \arg length Number of bytes to read
/// \arg dest A buffer where the data will be placed
/// \returns A handle to the read operation, or -1 on error
int read_async(uint64_t offset, size_t length, void *dest);
/// Read from the drive, blocking until finished.
/// \arg offset Offset to start from
/// \arg length Number of bytes to read
/// \arg dest A buffer where the data will be placed
/// \returns The number of bytes read
virtual size_t read(uint64_t offset, size_t length, void *dest);
/// Start an identify operation for the drive.
/// \returns A handle to the read operation, or -1 on error
int identify_async();
/// Tell the HBA to reconnect to the SATA device. A successful
/// reconnect will kick off an identify command.
void sata_reconnect();
/// Handle an incoming interrupt
void handle_interrupt();
/// Dump the port registers to the console
void dump();
private:
/// Rebase the port command structures to a new location in system
/// memory, to be allocated from the page manager.
void rebase();
/// Initialize a command structure
/// \arg length The number of bytes of data needed in the PRDs
/// \arg fis [out] The FIS for this command
/// \returns The index of the command slot, or -1 if none available
int make_command(size_t length, fis_register_h2d **fis);
/// Send a constructed command to the hardware
/// \arg slot The index of the command slot used
/// \returns True if the command was successfully sent
bool issue_command(int slot);
/// Free the data structures allocated by command creation
/// \arg slot The index of the command slot used
void free_command(int slot);
/// Finish a read command started by `read()`. This will
/// free the structures allocated, so `free_command()` is
/// not necessary.
/// \arg slot The command slot that the read command used
void finish_read(int slot);
/// Finish an identify command started by `identify_async()`.
/// This will free the structures allocated, so `free_command()` is
/// not necessary.
/// \arg slot The command slot that the read command used
void finish_identify(int slot);
sata_signature m_type;
uint8_t m_index;
state m_state;
hba *m_hba;
port_data volatile *m_data;
void *m_fis;
cmd_list_entry *m_cmd_list;
cmd_table *m_cmd_table;
enum class command_type : uint8_t { none, read, write, identify, finished };
struct pending
{
command_type type;
uint8_t offset;
size_t count;
void *data;
};
kutil::vector<pending> m_pending;
};
} // namespace ahci

View File

@@ -1,15 +0,0 @@
#include "entry.h"
entry::entry(const std::string &in, const std::string &out,
bool executable, bool symbols) :
m_in(in),
m_out(out),
m_file(in, std::ios_base::binary),
m_exec(executable),
m_syms(symbols)
{
m_file.seekg(0, std::ios_base::end);
m_size = m_file.tellg();
m_file.seekg(0);
}

View File

@@ -1,31 +0,0 @@
#pragma once
#include <string>
#include <fstream>
class entry
{
public:
entry(
const std::string &in,
const std::string &out,
bool executable = false,
bool symbols = false);
inline const std::string & in() const { return m_in; }
inline const std::string & out() const { return m_out; }
inline const std::ifstream & file() const { return m_file; }
inline bool executable() const { return m_exec; }
inline bool symbols() const { return m_syms; }
inline size_t size() const { return m_size; }
inline bool good() const { return m_file.good(); }
private:
std::string m_in;
std::string m_out;
std::ifstream m_file;
size_t m_size;
bool m_exec;
bool m_syms;
};

View File

@@ -1,137 +0,0 @@
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include "cpptoml.h"
#include "initrd/headers.h"
#include "entry.h"
int main(int argc, char **argv)
{
if (argc < 3 || argc > 4) {
std::cerr << "Usage: " << argv[0] << " <manifest> <output> [sourceroot]" << std::endl;
return 1;
}
if (argc == 4 && chdir(argv[3]) != 0) {
std::cerr
<< "Failed to change to " << argv[3] << ": "
<< std::strerror(errno) << std::endl;
return 1;
}
std::vector<entry> entries;
auto manifest = cpptoml::parse_file(argv[1]);
size_t files_size = 0;
size_t names_size = 0;
auto files = manifest->get_table_array("files");
for (const auto &file : *files) {
auto dest = file->get_as<std::string>("dest");
if (!dest) {
std::cerr << "File has no destination!" << std::endl;
return 1;
}
auto source = file->get_as<std::string>("source");
if (!source) {
std::cerr << "File " << *dest << " has no source!" << std::endl;
return 1;
}
auto exec = file->get_as<bool>("executable").value_or(false);
auto syms = file->get_as<bool>("symbols").value_or(false);
entries.emplace_back(*source, *dest, exec, syms);
const entry &e = entries.back();
if (!e.good()) {
std::cerr << "Error reading file: " << *source << std::endl;
return 1;
}
files_size += e.size();
names_size += dest->length() + 1;
}
std::fstream out(argv[2],
std::ios_base::binary |
std::ios_base::trunc |
std::ios_base::out |
std::ios_base::in);
if (!out.good()) {
std::cerr << "Error opening file: " << argv[2] << std::endl;
return 1;
}
uint32_t total_length =
sizeof(initrd::disk_header) +
sizeof(initrd::file_header) * entries.size() +
names_size + files_size;
initrd::disk_header dheader;
std::memset(&dheader, 0, sizeof(dheader));
dheader.file_count = entries.size();
dheader.length = total_length;
out.write(reinterpret_cast<const char *>(&dheader), sizeof(dheader));
size_t name_offset =
sizeof(initrd::disk_header) +
sizeof(initrd::file_header) * entries.size();
size_t file_offset = name_offset + names_size;
for (auto &e : entries) {
initrd::file_header fheader;
std::memset(&fheader, 0, sizeof(fheader));
fheader.offset = file_offset;
fheader.length = e.size();
fheader.name_offset = name_offset;
if (e.executable())
fheader.flags |= initrd::file_flags::executable;
if (e.symbols())
fheader.flags |= initrd::file_flags::symbols;
out.write(
reinterpret_cast<const char *>(&fheader),
sizeof(fheader));
name_offset += e.out().length() + 1;
file_offset += e.size();
}
for (auto &e : entries) {
out.write(e.out().c_str(), e.out().length() + 1);
}
for (auto &e : entries) {
out << e.file().rdbuf();
}
char buffer[1024];
uint8_t checksum = 0;
out.seekg(0);
while (true) {
size_t n = out.readsome(&buffer[0], 1024);
if (n == 0 || out.eof()) break;
for (size_t i=0; i<n; ++i)
checksum += static_cast<uint8_t>(buffer[i]);
}
dheader.checksum = static_cast<uint8_t>(0 - checksum);
out.seekp(0);
out.write(reinterpret_cast<const char *>(&dheader), sizeof(dheader));
out.close();
return 0;
}

View File

@@ -1,4 +1,4 @@
name = "fb"
name = "drv.uefi_fb"
targets = ["user"]
deps = ["libc"]
sources = [

View File

@@ -1,4 +1,4 @@
name = "nulldrv"
name = "testapp"
targets = ["user"]
deps = ["libc"]
sources = [