41 Commits

Author SHA1 Message Date
Justin C. Miller
d5b8902d8f Moving the rest (except ACPI tables) to high mem
Also the debug messaging to verify it.
2018-09-03 15:15:19 -07:00
Justin C. Miller
799fbbdd10 _Actually_ move the kernel to the last TiB.
More work on process page tables, including only mapping the last 2 pml4
entries (the highest 1TiB of the address space, ie, kernel space) into a
new table.

Includes the work of actually moving the kernel there, which I had
apparently done in name only previously. Oops.
2018-09-01 14:54:12 -07:00
Justin C. Miller
d33f1bc6f2 Page index to address translation script 2018-09-01 14:50:49 -07:00
Justin C. Miller
28a90e550e wscript change to dynamically detect KVM support for QEMU 2018-08-31 09:32:32 -07:00
Justin C. Miller
647801f096 Initial work on swapping page tables per process 2018-08-29 15:49:02 -07:00
Justin C. Miller
1664566bd2 enable KVM for qemu 2018-08-27 06:45:36 -07:00
Justin C. Miller
cd09c17d71 Commented out CPUID log messages, they're never differnet under qemu 2018-08-27 06:41:09 -07:00
Justin C. Miller
f74f3f03d1 Include prog_if in PCI device class log message 2018-08-27 06:40:30 -07:00
Justin C. Miller
23006b2b43 Fixed number of args in ahci interrupt log call 2018-08-27 06:39:31 -07:00
Justin C. Miller
7f69a6c9b1 Clean up AHCI: volatile, and sata_reset 2018-05-22 00:31:01 -07:00
Justin C. Miller
1726d10554 Unify syscall/interrupt handling of rsp 2018-05-21 22:57:43 -07:00
Justin C. Miller
757bc21550 Add note to implement FSXAVE 2018-05-21 09:07:53 -07:00
Justin C. Miller
e187679f93 Add 2 more chars to log names 2018-05-21 09:07:53 -07:00
Justin C. Miller
2597e2002b Get super basic ring3 task switching working
* It looks like UEFI enables SSE, so we need to tell clang -mno-sse for
  now to not use XMM* until we're ready to save them.
* SYSCALL is working from ring3 tasks, calling console printf!
2018-05-21 09:07:53 -07:00
Justin C. Miller
e6f819ed90 Fix non-packed TSS struct 2018-05-21 09:07:53 -07:00
Justin C. Miller
0c8bcb2400 Add get_rip/get_rsp helpers 2018-05-21 09:07:53 -07:00
Justin C. Miller
c5761cc51e Add more wscript options for qemu/vbox debugging 2018-05-21 09:07:53 -07:00
Justin C. Miller
24ccf65aba WIP ring3 2018-05-21 09:07:52 -07:00
Justin C. Miller
814d6f1de6 Minor GDT fixes 2018-05-21 09:07:52 -07:00
Justin C. Miller
bfaab294e6 Set up initial task switching (ring0 only) 2018-05-21 09:07:52 -07:00
Justin C. Miller
0ddcf668cb Allow for 2MiB large pages 2018-05-21 09:07:52 -07:00
Justin C. Miller
4005e9e791 Split gdt.* from interrupts.* 2018-05-21 09:07:52 -07:00
Justin C. Miller
abaa007c54 Set TSS and load it 2018-05-21 09:07:52 -07:00
Justin C. Miller
87d80f84c2 Remove AHCI debug dumps 2018-05-21 09:07:32 -07:00
Justin C. Miller
3fdf246a22 Split waf listen command out from vbox command 2018-05-20 17:59:59 -07:00
Justin C. Miller
79b95d0045 Move FIS creation into make_command 2018-05-20 17:59:08 -07:00
Justin C. Miller
1e66e5cd82 Re-add CFL setting that was lost 2018-05-20 16:34:15 -07:00
Justin C. Miller
193d9939f0 Add some AHCI debugging dumps 2018-05-20 02:02:06 -07:00
Justin C. Miller
81fc559802 Add initial ATA identify support to AHCI driver 2018-05-17 00:34:29 -07:00
Justin C. Miller
0d75cc999c Add GPT partition handling as virtual block devices 2018-05-16 10:14:40 -07:00
Justin C. Miller
a5da56d02f Add guid type 2018-05-16 09:52:06 -07:00
Justin C. Miller
a7e20fd390 Update notes about VBox 2018-05-15 21:51:20 -07:00
Justin C. Miller
9f38e7e5f5 Switch to building VBox images on-demand from QEMU image 2018-05-15 21:39:12 -07:00
Justin C. Miller
93e60cc136 Give kassert its own vector instead of DBZ 2018-05-15 21:38:44 -07:00
Justin C. Miller
5f7ec50055 Add fixes I made while looking for VBox bug 2018-05-15 21:37:27 -07:00
Justin C. Miller
ff0019841f Fix message in loader 2018-05-15 21:28:46 -07:00
Justin C. Miller
7eeeced2ca Change wscript vbox copy 2018-05-14 22:53:01 -07:00
Justin C. Miller
0fc369789e Change GDT code to enforce correct CS 2018-05-14 22:52:28 -07:00
Justin C. Miller
09f72f5ac6 GDT and GPF changes to track down Vbox bugs 2018-05-13 23:22:39 -07:00
Justin C. Miller
716109bab5 Add block device management to device manager 2018-05-12 20:27:46 -07:00
Justin C. Miller
0684fcf7e9 Separate read function into blocking and async portions 2018-05-12 20:16:25 -07:00
47 changed files with 1679 additions and 464 deletions

View File

@@ -11,6 +11,9 @@
- Serial out based on circular/bip biffer and interrupts, not spinning on - Serial out based on circular/bip biffer and interrupts, not spinning on
`write_ready()` `write_ready()`
- Split out more code into kutil for testing - Split out more code into kutil for testing
- AHCI / MSI interrupts on Vbox break?
- FXSAVE to save XMM registers.
- optimization using #NM (0x7) to detect SSE usage
- Device Tree - Device Tree
@@ -21,4 +24,3 @@
- Multiprocessing - Multiprocessing
- Processes in Ring 3 - Processes in Ring 3
- Stack tracer - Stack tracer
- build system upgrade (CMake / Waf / etc)

12
scripts/vmem_translate.py Executable file
View File

@@ -0,0 +1,12 @@
#!/usr/bin/env python
def translate(i4 = 0, i3 = 0, i2 = 0, i1 = 0, offset = 0):
addr = (i4 << 39) + (i3 << 30) + (i2 << 21) + (i1 << 12) + offset
if addr & (1 << 47):
addr |= 0xffff000000000000
return addr
if __name__ == "__main__":
import sys
print("{:016x}".format(translate(*map(int, sys.argv[1:]))))

View File

@@ -1,7 +1,7 @@
ENTRY(_start) ENTRY(_start)
SECTIONS SECTIONS
{ {
OFFSET = 0xFFFF800000000000; OFFSET = 0xFFFFFF0000000000;
. = OFFSET + 0x100000; . = OFFSET + 0x100000;
.header : { .header : {

View File

@@ -215,7 +215,7 @@ loader_load_kernel(
if (status == EFI_NOT_FOUND) if (status == EFI_NOT_FOUND)
continue; continue;
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_file: %s", kernel_name); CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_elf: %s", kernel_name);
data->font = (void *)((uint64_t)data->kernel + data->kernel_length); data->font = (void *)((uint64_t)data->kernel + data->kernel_length);
status = loader_load_font(bootsvc, root, data); status = loader_load_font(bootsvc, root, data);

View File

@@ -9,11 +9,7 @@
#endif #endif
#ifndef KERNEL_VIRT_ADDRESS #ifndef KERNEL_VIRT_ADDRESS
#define KERNEL_VIRT_ADDRESS 0xFFFF800000000000 #define KERNEL_VIRT_ADDRESS 0xFFFFFF0000000000
#endif
#ifndef VIRTUAL_OFFSET
#define VIRTUAL_OFFSET 0xf00000000
#endif #endif
#ifndef KERNEL_MEMTYPE #ifndef KERNEL_MEMTYPE

View File

@@ -82,9 +82,9 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
status = loader_load_kernel(bootsvc, &load); status = loader_load_kernel(bootsvc, &load);
CHECK_EFI_STATUS_OR_FAIL(status); CHECK_EFI_STATUS_OR_FAIL(status);
con_printf(L" %u image bytes at 0x%x\r\n", load.kernel_length, load.kernel); con_printf(L" image bytes at 0x%x : %x\r\n", load.kernel, load.kernel_length);
con_printf(L" %u font bytes at 0x%x\r\n", load.font_length, load.font); con_printf(L" font bytes at 0x%x : %x\r\n", load.font, load.font_length);
con_printf(L" %u data bytes at 0x%x\r\n", load.data_length, load.data); con_printf(L" data bytes at 0x%x : %x\r\n", load.data, load.data_length);
struct kernel_header *version = (struct kernel_header *)load.kernel; struct kernel_header *version = (struct kernel_header *)load.kernel;
if (version->magic != KERNEL_HEADER_MAGIC) { if (version->magic != KERNEL_HEADER_MAGIC) {

View File

@@ -191,6 +191,7 @@ memory_virtualize(EFI_RUNTIME_SERVICES *runsvc, struct memory_map *map)
case KERNEL_FONT_MEMTYPE: case KERNEL_FONT_MEMTYPE:
case KERNEL_DATA_MEMTYPE: case KERNEL_DATA_MEMTYPE:
d->Attribute |= EFI_MEMORY_RUNTIME; d->Attribute |= EFI_MEMORY_RUNTIME;
d->VirtualStart = d->PhysicalStart + KERNEL_VIRT_ADDRESS;
default: default:
if (d->Attribute & EFI_MEMORY_RUNTIME) { if (d->Attribute & EFI_MEMORY_RUNTIME) {

View File

@@ -16,13 +16,3 @@ ahci_driver::register_device(pci_device *device)
ahci::hba &hba = m_devices.emplace(device); ahci::hba &hba = m_devices.emplace(device);
} }
ahci::port *
ahci_driver::find_disk()
{
for (auto &hba : m_devices) {
ahci::port *d = hba.find_disk();
if (d) return d;
}
return nullptr;
}

View File

@@ -22,9 +22,6 @@ public:
/// \arg device The PCI device to remove /// \arg device The PCI device to remove
void unregister_device(pci_device *device); void unregister_device(pci_device *device);
/// Debug: find the first disk
ahci::port * find_disk();
private: private:
kutil::vector<ahci::hba> m_devices; kutil::vector<ahci::hba> m_devices;
}; };

View File

@@ -1,7 +1,9 @@
#include <stdint.h> #include <stdint.h>
#include "ahci/ata.h" #include "ahci/ata.h"
#include "ahci/hba.h" #include "ahci/hba.h"
#include "console.h"
#include "device_manager.h" #include "device_manager.h"
#include "fs/gpt.h"
#include "log.h" #include "log.h"
#include "page_manager.h" #include "page_manager.h"
#include "pci.h" #include "pci.h"
@@ -57,10 +59,14 @@ void irq_cb(void *data)
hba::hba(pci_device *device) hba::hba(pci_device *device)
{ {
page_manager *pm = page_manager::get(); page_manager *pm = page_manager::get();
device_manager &dm = device_manager::get();
uint32_t bar5 = device->get_bar(5); uint32_t bar5 = device->get_bar(5);
m_data = reinterpret_cast<hba_data *>(bar5 & ~0xfffull); log::debug(logs::driver, "HBA raw BAR5 is %08lx", bar5);
pm->map_offset_pointer(reinterpret_cast<void **>(&m_data), 0x2000);
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)) if (! bitfield_has(m_data->cap, hba_cap::ahci_only))
m_data->host_control |= 0x80000000; // Enable AHCI mode m_data->host_control |= 0x80000000; // Enable AHCI mode
@@ -70,17 +76,17 @@ hba::hba(pci_device *device)
unsigned ports = (icap & 0xf) + 1; unsigned ports = (icap & 0xf) + 1;
unsigned slots = ((icap >> 8) & 0x1f) + 1; unsigned slots = ((icap >> 8) & 0x1f) + 1;
log::debug(logs::driver, " %d ports", ports); log::debug(logs::driver, " %d ports: %08x", ports, m_data->port_impl);
log::debug(logs::driver, " %d command slots", slots); log::debug(logs::driver, " %d command slots", slots);
port_data *pd = reinterpret_cast<port_data *>( auto *pd = reinterpret_cast<port_data volatile *>(
kutil::offset_pointer(m_data, 0x100)); kutil::offset_pointer(m_data, 0x100));
bool needs_interrupt = false; bool needs_interrupt = false;
m_ports.ensure_capacity(ports); m_ports.ensure_capacity(ports);
for (unsigned i = 0; i < ports; ++i) { for (unsigned i = 0; i < ports; ++i) {
bool impl = ((m_data->port_impl & (1 << i)) != 0); bool impl = ((m_data->port_impl & (1 << i)) != 0);
port &p = m_ports.emplace(i, kutil::offset_pointer(pd, 0x80 * i), impl); port &p = m_ports.emplace(this, i, kutil::offset_pointer(pd, 0x80 * i), impl);
if (p.get_state() == port::state::active) if (p.get_state() == port::state::active)
needs_interrupt = true; needs_interrupt = true;
} }
@@ -89,29 +95,48 @@ hba::hba(pci_device *device)
device_manager::get().allocate_msi("AHCI Device", *device, irq_cb, this); device_manager::get().allocate_msi("AHCI Device", *device, irq_cb, this);
m_data->host_control |= 0x02; // enable interrupts m_data->host_control |= 0x02; // enable interrupts
} }
}
port * for (auto &p : m_ports) {
hba::find_disk() if (!p.active()) continue;
{
for (auto &port : m_ports) { if (p.get_type() == sata_signature::sata_drive) {
if (port.get_state() == port::state::active && p.sata_reconnect();
port.get_type() == sata_signature::sata_drive) /*
return &port; if (fs::partition::load(&p) == 0)
dm.register_block_device(&p);
*/
}
} }
return nullptr;
} }
void void
hba::handle_interrupt() hba::handle_interrupt()
{ {
uint32_t status = m_data->int_status;
for (auto &port : m_ports) { for (auto &port : m_ports) {
if (m_data->int_status & (1 << port.index())) { if (status & (1 << port.index())) {
port.handle_interrupt(); port.handle_interrupt();
} }
} }
m_data->int_status = 0; // 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 } // namespace ahci

View File

@@ -25,12 +25,12 @@ public:
/// Interrupt handler. /// Interrupt handler.
void handle_interrupt(); void handle_interrupt();
/// Debug: find the first disk /// Dump the HBA registers to the console
port * find_disk(); void dump();
private: private:
pci_device *m_device; pci_device *m_device;
hba_data *m_data; hba_data volatile *m_data;
kutil::vector<port> m_ports; kutil::vector<port> m_ports;
}; };

View File

@@ -2,13 +2,21 @@
#include "kutil/assert.h" #include "kutil/assert.h"
#include "kutil/enum_bitfields.h" #include "kutil/enum_bitfields.h"
#include "ahci/ata.h" #include "ahci/ata.h"
#include "ahci/hba.h"
#include "ahci/fis.h" #include "ahci/fis.h"
#include "ahci/port.h" #include "ahci/port.h"
#include "console.h"
#include "io.h" #include "io.h"
#include "log.h" #include "log.h"
#include "page_manager.h" #include "page_manager.h"
namespace ahci {
enum class cmd_list_flags : uint16_t;
}
IS_BITFIELD(ahci::port_cmd); IS_BITFIELD(ahci::port_cmd);
IS_BITFIELD(volatile ahci::port_cmd);
IS_BITFIELD(ahci::cmd_list_flags);
namespace ahci { namespace ahci {
@@ -110,10 +118,11 @@ struct port_data
} __attribute__ ((packed)); } __attribute__ ((packed));
port::port(uint8_t index, port_data *data, bool impl) : port::port(hba *device, uint8_t index, port_data volatile *data, bool impl) :
m_index(index), m_index(index),
m_type(sata_signature::none), m_type(sata_signature::none),
m_state(state::unimpl), m_state(state::unimpl),
m_hba(device),
m_data(data), m_data(data),
m_fis(nullptr), m_fis(nullptr),
m_cmd_list(nullptr), m_cmd_list(nullptr),
@@ -154,7 +163,13 @@ port::update()
rebase(); rebase();
m_pending.set_size(32); m_pending.set_size(32);
m_data->interrupt_enable = 1; 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 { } else {
m_state = state::inactive; m_state = state::inactive;
} }
@@ -190,19 +205,27 @@ port::stop_commands()
} }
int int
port::make_command(size_t length) port::make_command(size_t length, fis_register_h2d **fis)
{ {
int slot = -1; int slot = -1;
uint32_t used_slots = (m_data->serial_active | m_data->cmd_issue); uint32_t used_slots =
m_data->serial_active |
m_data->cmd_issue |
m_data->interrupt_status;
for (int i = 0; i < 32; ++i) { for (int i = 0; i < 32; ++i) {
if ((used_slots & (1 << i)) == 0) { if (used_slots & (1 << i)) continue;
if (m_pending[i].type == command_type::none) {
slot = i; slot = i;
break; break;
} else {
log::debug(logs::driver, "Type is %d", m_pending[i].type);
} }
} }
if (slot < 0) { if (slot < 0) {
log::info(logs::driver, "AHCI could not get a free command slot."); log::error(logs::driver, "AHCI could not get a free command slot.");
return -1; return -1;
} }
@@ -214,6 +237,14 @@ port::make_command(size_t length)
kutil::memset(&cmdt, 0, sizeof(cmd_table) + kutil::memset(&cmdt, 0, sizeof(cmd_table) +
max_prd_count * sizeof(prdt_entry)); 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; size_t remaining = length;
for (int i = 0; i < max_prd_count; ++i) { for (int i = 0; i < max_prd_count; ++i) {
size_t prd_len = std::min(remaining, 0x200000ul); size_t prd_len = std::min(remaining, 0x200000ul);
@@ -240,21 +271,20 @@ port::make_command(size_t length)
return slot; return slot;
} }
bool int
port::read(uint64_t sector, size_t length, void *dest) port::read_async(uint64_t offset, size_t length, void *dest)
{ {
int slot = make_command(length); fis_register_h2d *fis;
int slot = make_command(length, &fis);
if (slot < 0) if (slot < 0)
return false; return 0;
cmd_table &cmdt = m_cmd_table[slot]; cmd_table &cmdt = m_cmd_table[slot];
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->command = ata_cmd::read_dma_ext;
fis->device = 0x40; // ATA8-ACS p.175 fis->device = 0x40; // ATA8-ACS p.175
uint64_t sector = offset >> 9;
fis->lba0 = (sector ) & 0xff; fis->lba0 = (sector ) & 0xff;
fis->lba1 = (sector >> 8) & 0xff; fis->lba1 = (sector >> 8) & 0xff;
fis->lba2 = (sector >> 16) & 0xff; fis->lba2 = (sector >> 16) & 0xff;
@@ -270,8 +300,64 @@ port::read(uint64_t sector, size_t length, void *dest)
count, sector, sector*512); count, sector, sector*512);
m_pending[slot].type = command_type::read; m_pending[slot].type = command_type::read;
m_pending[slot].offset = offset % 512;
m_pending[slot].count = 0;
m_pending[slot].data = dest; m_pending[slot].data = dest;
return issue_command(slot); 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 bool
@@ -297,14 +383,23 @@ port::issue_command(int slot)
void void
port::handle_interrupt() port::handle_interrupt()
{ {
log::debug(logs::driver, "AHCI port %d got an interrupt"); log::debug(logs::driver, "AHCI port %d got an interrupt", m_index);
// TODO: handle other states in interrupt_status // TODO: handle other states in interrupt_status
if (m_data->interrupt_status & 0x40000000) { 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"); log::error(logs::driver, "AHCI task file error");
// TODO: clean up! dump();
return; kassert(0, "Task file error");
} }
log::debug(logs::driver, "AHCI interrupt status: %08lx %08lx", log::debug(logs::driver, "AHCI interrupt status: %08lx %08lx",
@@ -312,19 +407,25 @@ port::handle_interrupt()
uint32_t ci = m_data->cmd_issue; uint32_t ci = m_data->cmd_issue;
for (int i = 0; i < 32; ++i) { for (int i = 0; i < 32; ++i) {
// Skip commands still listed as "issued"
if (ci & (1 << i)) continue; 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]; pending &p = m_pending[i];
switch (p.type) { switch (p.type) {
case command_type::read: case command_type::read:
finish_read(i); finish_read(i);
break; break;
case command_type::identify:
finish_identify(i);
break;
default: default:
break; break;
} }
p.type = command_type::none;
p.data = nullptr;
} }
// Clear the whole status register to mark it as handled
m_data->interrupt_status = m_data->interrupt_status; m_data->interrupt_status = m_data->interrupt_status;
} }
@@ -335,7 +436,9 @@ port::finish_read(int slot)
cmd_table &cmdt = m_cmd_table[slot]; cmd_table &cmdt = m_cmd_table[slot];
cmd_list_entry &ent = m_cmd_list[slot]; cmd_list_entry &ent = m_cmd_list[slot];
size_t count = 0;
void *p = m_pending[slot].data; void *p = m_pending[slot].data;
uint8_t offset = m_pending[slot].offset;
for (int i = 0; i < ent.prd_table_length; ++i) { for (int i = 0; i < ent.prd_table_length; ++i) {
size_t prd_len = (cmdt.entries[i].byte_count & 0x7fffffff) + 1; size_t prd_len = (cmdt.entries[i].byte_count & 0x7fffffff) + 1;
@@ -343,13 +446,82 @@ port::finish_read(int slot)
static_cast<addr_t>(cmdt.entries[i].data_base_low) | static_cast<addr_t>(cmdt.entries[i].data_base_low) |
static_cast<addr_t>(cmdt.entries[i].data_base_high) << 32; static_cast<addr_t>(cmdt.entries[i].data_base_high) << 32;
void *mem = pm->offset_virt(phys); 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); kutil::memcpy(p, mem, prd_len);
p = kutil::offset_pointer(p, prd_len); p = kutil::offset_pointer(p, prd_len - offset);
count += (prd_len - offset);
offset = 0;
pm->unmap_pages(mem, page_count(prd_len)); 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;
addr_t phys =
static_cast<addr_t>(cmdt.entries[0].data_base_low) |
static_cast<addr_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 void
@@ -430,4 +602,22 @@ port::rebase()
start_commands(); 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 } // namespace ahci

View File

@@ -4,25 +4,30 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include "kutil/vector.h" #include "kutil/vector.h"
#include "block_device.h"
namespace ahci { namespace ahci {
struct cmd_list_entry; struct cmd_list_entry;
struct cmd_table; struct cmd_table;
struct fis_register_h2d;
class hba;
enum class sata_signature : uint32_t; enum class sata_signature : uint32_t;
enum class port_cmd : uint32_t; enum class port_cmd : uint32_t;
struct port_data; struct port_data;
/// A port on an AHCI HBA /// A port on an AHCI HBA
class port class port :
public block_device
{ {
public: public:
/// Constructor. /// Constructor.
/// \arg index Index of the port on its HBA /// \arg device The HBA device this port belongs to
/// \arg data Pointer to the device's registers for this port /// \arg index Index of the port on its HBA
/// \arg impl Whether this port is marked as implemented in the HBA /// \arg data Pointer to the device's registers for this port
port(uint8_t index, port_data *data, bool impl); /// \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 /// Destructor
~port(); ~port();
@@ -37,6 +42,10 @@ public:
/// \returns An enum representing the state /// \returns An enum representing the state
inline state get_state() const { return m_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 /// Get the type signature of this device
/// \returns An enum representing the type of device /// \returns An enum representing the type of device
inline sata_signature get_type() const { return m_type; } inline sata_signature get_type() const { return m_type; }
@@ -53,16 +62,34 @@ public:
/// Stop command processing from this port /// Stop command processing from this port
void stop_commands(); void stop_commands();
/// Read data from the drive. /// Start a read operation from the drive.
/// \arg sector Starting sector to read /// \arg offset Offset to start from
/// \arg length Number of bytes to read /// \arg length Number of bytes to read
/// \arg dest A buffer where the data will be placed /// \arg dest A buffer where the data will be placed
/// \returns True if the command succeeded /// \returns A handle to the read operation, or -1 on error
bool read(uint64_t sector, size_t length, void *dest); 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 /// Handle an incoming interrupt
void handle_interrupt(); void handle_interrupt();
/// Dump the port registers to the console
void dump();
private: private:
/// Rebase the port command structures to a new location in system /// Rebase the port command structures to a new location in system
/// memory, to be allocated from the page manager. /// memory, to be allocated from the page manager.
@@ -70,8 +97,9 @@ private:
/// Initialize a command structure /// Initialize a command structure
/// \arg length The number of bytes of data needed in the PRDs /// \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 /// \returns The index of the command slot, or -1 if none available
int make_command(size_t length); int make_command(size_t length, fis_register_h2d **fis);
/// Send a constructed command to the hardware /// Send a constructed command to the hardware
/// \arg slot The index of the command slot used /// \arg slot The index of the command slot used
@@ -88,20 +116,29 @@ private:
/// \arg slot The command slot that the read command used /// \arg slot The command slot that the read command used
void finish_read(int slot); 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; sata_signature m_type;
uint8_t m_index; uint8_t m_index;
state m_state; state m_state;
port_data *m_data; hba *m_hba;
port_data volatile *m_data;
void *m_fis; void *m_fis;
cmd_list_entry *m_cmd_list; cmd_list_entry *m_cmd_list;
cmd_table *m_cmd_table; cmd_table *m_cmd_table;
enum class command_type : uint8_t { none, read, write }; enum class command_type : uint8_t { none, read, write, identify, finished };
struct pending struct pending
{ {
command_type type; command_type type;
uint8_t offset;
size_t count;
void *data; void *data;
}; };

View File

@@ -1,9 +1,18 @@
#include "kutil/memory_manager.h" #include "kutil/memory_manager.h"
#include "log.h"
kutil::memory_manager g_kernel_memory_manager; kutil::memory_manager g_kernel_memory_manager;
// kutil malloc/free implementation // kutil malloc/free implementation
namespace kutil { namespace kutil {
void * malloc(size_t n) { return g_kernel_memory_manager.allocate(n); }
void free(void *p) { g_kernel_memory_manager.free(p); } void *
malloc(size_t n) {
void *p = g_kernel_memory_manager.allocate(n);
log::debug(logs::memory, "Heap allocated %ld bytes: %016lx", n, p);
return p;
}
void free(void *p) { g_kernel_memory_manager.free(p); }
} }

View File

@@ -52,28 +52,38 @@ lapic::lapic(uint32_t *base, isr spurious) :
void void
lapic::enable_timer(isr vector, uint8_t divisor, uint32_t count, bool repeat) lapic::enable_timer(isr vector, uint8_t divisor, uint32_t count, bool repeat)
{ {
uint32_t divbits = 0;
switch (divisor) { switch (divisor) {
case 1: divisor = 11; break; case 1: divbits = 0xb; break;
case 2: divisor = 0; break; case 2: divbits = 0x0; break;
case 4: divisor = 1; break; case 4: divbits = 0x1; break;
case 8: divisor = 2; break; case 8: divbits = 0x2; break;
case 16: divisor = 3; break; case 16: divbits = 0x3; break;
case 32: divisor = 8; break; case 32: divbits = 0x8; break;
case 64: divisor = 9; break; case 64: divbits = 0x9; break;
case 128: divisor = 10; break; case 128: divbits = 0xa; break;
default: default:
kassert(0, "Invalid divisor passed to lapic::enable_timer"); kassert(0, "Invalid divisor passed to lapic::enable_timer");
} }
apic_write(m_base, 0x3e0, divisor);
apic_write(m_base, 0x380, count);
uint32_t lvte = static_cast<uint8_t>(vector); uint32_t lvte = static_cast<uint8_t>(vector);
if (repeat) if (repeat)
lvte |= 0x20000; lvte |= 0x20000;
log::debug(logs::apic, "Enabling APIC timer with isr %d.", vector); log::debug(logs::apic, "Enabling APIC timer with isr %d.", vector);
apic_write(m_base, 0x320, lvte); apic_write(m_base, 0x320, lvte);
apic_write(m_base, 0x3e0, divbits);
reset_timer(count);
}
uint32_t
lapic::reset_timer(uint32_t count)
{
uint32_t remaining = apic_read(m_base, 0x380);
apic_write(m_base, 0x380, count);
return remaining;
} }
void void

View File

@@ -37,6 +37,15 @@ public:
/// \arg repeat If false, this timer is one-off, otherwise repeating /// \arg repeat If false, this timer is one-off, otherwise repeating
void enable_timer(isr vector, uint8_t divisor, uint32_t count, bool repeat = true); void enable_timer(isr vector, uint8_t divisor, uint32_t count, bool repeat = true);
/// Reset the timer countdown.
/// \arg count The count of ticks before an interrupt, or 0 to stop the timer
/// \returns The count of ticks that were remaining before reset
uint32_t reset_timer(uint32_t count);
/// Stop the timer.
/// \returns The count of ticks remaining before an interrupt was to happen
inline uint32_t stop_timer() { return reset_timer(0); }
/// Enable interrupts for the LAPIC LINT0 pin. /// Enable interrupts for the LAPIC LINT0 pin.
/// \arg num Local interrupt number (0 or 1) /// \arg num Local interrupt number (0 or 1)
/// \arg vector Interrupt vector LINT0 should use /// \arg vector Interrupt vector LINT0 should use

View File

@@ -7,7 +7,7 @@ __kernel_assert(const char *file, unsigned line, const char *message)
console *cons = console::get(); console *cons = console::get();
if (cons) { if (cons) {
cons->set_color(9 , 0); cons->set_color(9 , 0);
cons->puts("\n\n ERROR: "); cons->puts("\n\n ERROR: ");
cons->puts(file); cons->puts(file);
cons->puts(":"); cons->puts(":");
cons->put_dec(line); cons->put_dec(line);
@@ -15,15 +15,6 @@ __kernel_assert(const char *file, unsigned line, const char *message)
cons->puts(message); cons->puts(message);
} }
__asm__ __volatile__( __asm__ ( "int $0e7h" );
"movq %0, %%r8;" while (1) __asm__ ("hlt");
"movq %1, %%r9;"
"movq %2, %%r10;"
"movq $0, %%rdx;"
"divq %%rdx;"
: // no outputs
: "r"((uint64_t)line), "r"(file), "r"(message)
: "rax", "rdx", "r8", "r9", "r10");
while (1);
} }

11
src/kernel/block_device.h Normal file
View File

@@ -0,0 +1,11 @@
#pragma once
/// \file block_device.h
/// Interface definition for block devices
#include <stddef.h>
/// Interface for block devices
class block_device
{
public:
virtual size_t read(size_t offset, size_t length, void *buffer) = 0;
};

View File

@@ -28,8 +28,8 @@ _start:
extern kernel_main extern kernel_main
call kernel_main call kernel_main
cli ; Kernel init is over, wait for the scheduler to
; take over
.hang: .hang:
hlt hlt
jmp .hang jmp .hang

View File

@@ -1,4 +1,5 @@
#include "kutil/coord.h" #include "kutil/coord.h"
#include "kutil/guid.h"
#include "kutil/memory.h" #include "kutil/memory.h"
#include "console.h" #include "console.h"
#include "font.h" #include "font.h"
@@ -334,6 +335,23 @@ void console::vprintf(const char *fmt, va_list args)
done = true; done = true;
break; break;
case 'G': {
// Special: GUID type
kutil::guid g = va_arg(args, kutil::guid);
put_hex<uint32_t>(g.a, 8, '0');
putc('-');
put_hex<uint16_t>((g.a >> 32) & 0xffff, 4, '0');
putc('-');
put_hex<uint16_t>((g.a >> 48) & 0xffff, 4, '0');
putc('-');
put_hex<uint16_t>((kutil::byteswap(g.b) >> 16) & 0xffff, 4, '0');
putc('-');
put_hex<uint16_t>(kutil::byteswap(g.b) & 0xffff, 4, '0');
put_hex<uint32_t>(kutil::byteswap(g.b >> 32), 8, '0');
}
done = true;
break;
case 'l': case 'l':
switch (*r++) { switch (*r++) {
case 'x': put_hex<uint64_t>(va_arg(args, uint64_t), right ? width : -width, pad); done = true; break; case 'x': put_hex<uint64_t>(va_arg(args, uint64_t), right ? width : -width, pad); done = true; break;

View File

@@ -1,5 +1,13 @@
#pragma once #pragma once
struct cpu_state
{
uint64_t ds;
uint64_t r15, r14, r13, r12, r11, r10, r9, r8;
uint64_t rdi, rsi, rbp, rbx, rdx, rcx, rax;
uint64_t interrupt, errorcode;
uint64_t rip, cs, rflags, user_rsp, ss;
};
class cpu_id class cpu_id
{ {

View File

@@ -1,10 +1,12 @@
global get_rsp
get_rsp:
mov rax, rsp
ret
section .text global get_rip
global do_the_set_registers get_rip:
do_the_set_registers: pop rax ; do the same thing as 'ret', except with 'jmp'
mov rax, 0xdeadbeef0badc0de jmp rax ; with the return address still in rax
mov r8, rcx
mov r9, rdi
global _halt global _halt
_halt: _halt:

View File

@@ -320,3 +320,9 @@ device_manager::allocate_msi(const char *name, pci_device &device, irq_callback
static_cast<uint16_t>(vector)); static_cast<uint16_t>(vector));
return true; return true;
} }
void
device_manager::register_block_device(block_device *blockdev)
{
m_blockdevs.append(blockdev);
}

View File

@@ -7,6 +7,7 @@
struct acpi_xsdt; struct acpi_xsdt;
struct acpi_apic; struct acpi_apic;
struct acpi_mcfg; struct acpi_mcfg;
class block_device;
class lapic; class lapic;
class ioapic; class ioapic;
@@ -65,6 +66,23 @@ public:
return false; return false;
} }
/// Register the existance of a block device.
/// \arg blockdev Pointer to the block device
void register_block_device(block_device *blockdev);
/// Get the number of block devices in the system
/// \returns A count of devices
inline unsigned get_num_block_devices() const { return m_blockdevs.count(); }
/// Get a block device
/// \arg i Index of the device to get
/// \returns A pointer to the requested device, or nullptr
inline block_device * get_block_device(unsigned i)
{
return i < m_blockdevs.count() ?
m_blockdevs[i] : nullptr;
}
private: private:
/// Parse the ACPI XSDT and load relevant sub-tables. /// Parse the ACPI XSDT and load relevant sub-tables.
/// \arg xsdt Pointer to the XSDT from the firmware /// \arg xsdt Pointer to the XSDT from the firmware
@@ -100,6 +118,8 @@ private:
}; };
kutil::vector<irq_allocation> m_irqs; kutil::vector<irq_allocation> m_irqs;
kutil::vector<block_device *> m_blockdevs;
static device_manager s_instance; static device_manager s_instance;
device_manager() = delete; device_manager() = delete;

114
src/kernel/fs/gpt.cpp Normal file
View File

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

37
src/kernel/fs/gpt.h Normal file
View File

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

273
src/kernel/gdt.cpp Normal file
View File

@@ -0,0 +1,273 @@
#include <stdint.h>
#include "kutil/assert.h"
#include "kutil/enum_bitfields.h"
#include "kutil/memory.h"
#include "console.h"
#include "log.h"
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);
struct gdt_descriptor
{
uint16_t limit_low;
uint16_t base_low;
uint8_t base_mid;
gdt_type type;
uint8_t size;
uint8_t base_high;
} __attribute__ ((packed));
struct tss_descriptor
{
uint16_t limit_low;
uint16_t base_00;
uint8_t base_16;
gdt_type type;
uint8_t size;
uint8_t base_24;
uint32_t base_32;
uint32_t reserved;
} __attribute__ ((packed));
struct tss_entry
{
uint32_t reserved0;
uint64_t rsp[3]; // stack pointers for CPL 0-2
uint64_t ist[8]; // ist[0] is reserved
uint64_t reserved1;
uint16_t reserved2;
uint16_t iomap_offset;
} __attribute__ ((packed));
struct idt_descriptor
{
uint16_t base_low;
uint16_t selector;
uint8_t ist;
uint8_t flags;
uint16_t base_mid;
uint32_t base_high;
uint32_t reserved; // must be zero
} __attribute__ ((packed));
struct table_ptr
{
uint16_t limit;
uint64_t base;
} __attribute__ ((packed));
gdt_descriptor g_gdt_table[10];
idt_descriptor g_idt_table[256];
table_ptr g_gdtr;
table_ptr g_idtr;
tss_entry g_tss;
extern "C" {
void idt_write();
void idt_load();
void gdt_write(uint16_t cs, uint16_t ds, uint16_t tr);
void gdt_load();
}
void
gdt_set_entry(uint8_t i, uint32_t base, uint64_t limit, bool is64, gdt_type type)
{
g_gdt_table[i].limit_low = limit & 0xffff;
g_gdt_table[i].size = (limit >> 16) & 0xf;
g_gdt_table[i].size |= (is64 ? 0xa0 : 0xc0);
g_gdt_table[i].base_low = base & 0xffff;
g_gdt_table[i].base_mid = (base >> 16) & 0xff;
g_gdt_table[i].base_high = (base >> 24) & 0xff;
g_gdt_table[i].type = type | gdt_type::system | gdt_type::present;
}
void
tss_set_entry(uint8_t i, uint64_t base, uint64_t limit)
{
tss_descriptor tssd;
tssd.limit_low = limit & 0xffff;
tssd.size = (limit >> 16) & 0xf;
tssd.base_00 = base & 0xffff;
tssd.base_16 = (base >> 16) & 0xff;
tssd.base_24 = (base >> 24) & 0xff;
tssd.base_32 = (base >> 32) & 0xffffffff;
tssd.reserved = 0;
tssd.type =
gdt_type::accessed |
gdt_type::execute |
gdt_type::ring3 |
gdt_type::present;
kutil::memcpy(&g_gdt_table[i], &tssd, sizeof(tss_descriptor));
}
void
idt_set_entry(uint8_t i, uint64_t addr, uint16_t selector, uint8_t flags)
{
g_idt_table[i].base_low = addr & 0xffff;
g_idt_table[i].base_mid = (addr >> 16) & 0xffff;
g_idt_table[i].base_high = (addr >> 32) & 0xffffffff;
g_idt_table[i].selector = selector;
g_idt_table[i].flags = flags;
g_idt_table[i].ist = 0;
g_idt_table[i].reserved = 0;
}
void
tss_set_stack(int ring, addr_t rsp)
{
kassert(ring < 3, "Bad ring passed to tss_set_stack.");
g_tss.rsp[ring] = rsp;
}
addr_t
tss_get_stack(int ring)
{
kassert(ring < 3, "Bad ring passed to tss_get_stack.");
return g_tss.rsp[ring];
}
void
gdt_init()
{
kutil::memset(&g_gdt_table, 0, sizeof(g_gdt_table));
kutil::memset(&g_idt_table, 0, sizeof(g_idt_table));
g_gdtr.limit = sizeof(g_gdt_table) - 1;
g_gdtr.base = reinterpret_cast<uint64_t>(&g_gdt_table);
// Kernel CS/SS - always 64bit
gdt_set_entry(1, 0, 0xfffff, true, gdt_type::read_write | gdt_type::execute);
gdt_set_entry(2, 0, 0xfffff, true, gdt_type::read_write);
// User CS32/SS/CS64 - layout expected by SYSRET
gdt_set_entry(3, 0, 0xfffff, false, gdt_type::ring3 | gdt_type::read_write | gdt_type::execute);
gdt_set_entry(4, 0, 0xfffff, true, gdt_type::ring3 | gdt_type::read_write);
gdt_set_entry(5, 0, 0xfffff, true, gdt_type::ring3 | gdt_type::read_write | gdt_type::execute);
kutil::memset(&g_tss, 0, sizeof(tss_entry));
g_tss.iomap_offset = sizeof(tss_entry);
addr_t tss_base = reinterpret_cast<addr_t>(&g_tss);
// Note that this takes TWO GDT entries
tss_set_entry(6, tss_base, sizeof(tss_entry));
gdt_write(1 << 3, 2 << 3, 6 << 3);
g_idtr.limit = sizeof(g_idt_table) - 1;
g_idtr.base = reinterpret_cast<uint64_t>(&g_idt_table);
idt_write();
}
void
gdt_dump()
{
const table_ptr &table = g_gdtr;
console *cons = console::get();
cons->printf(" GDT: loc:%lx size:%d\n", table.base, table.limit+1);
int count = (table.limit + 1) / sizeof(gdt_descriptor);
const gdt_descriptor *gdt =
reinterpret_cast<const gdt_descriptor *>(table.base);
for (int i = 0; i < count; ++i) {
uint32_t base =
(gdt[i].base_high << 24) |
(gdt[i].base_mid << 16) |
gdt[i].base_low;
uint32_t limit =
static_cast<uint32_t>(gdt[i].size & 0x0f) << 16 |
gdt[i].limit_low;
cons->printf(" %02d:", i);
if (! bitfield_has(gdt[i].type, gdt_type::present)) {
cons->puts(" Not Present\n");
continue;
}
cons->printf(" Base %08x limit %05x ", base, limit);
switch (gdt[i].type & gdt_type::ring3) {
case gdt_type::ring3: cons->puts("ring3"); break;
case gdt_type::ring2: cons->puts("ring2"); break;
case gdt_type::ring1: cons->puts("ring1"); break;
default: cons->puts("ring0"); break;
}
cons->printf(" %s %s %s %s %s %s %s\n",
bitfield_has(gdt[i].type, gdt_type::accessed) ? "A" : " ",
bitfield_has(gdt[i].type, gdt_type::read_write) ? "RW" : " ",
bitfield_has(gdt[i].type, gdt_type::conforming) ? "C" : " ",
bitfield_has(gdt[i].type, gdt_type::execute) ? "EX" : " ",
bitfield_has(gdt[i].type, gdt_type::system) ? "S" : " ",
(gdt[i].size & 0x80) ? "KB" : " B",
(gdt[i].size & 0x60) == 0x20 ? "64" :
(gdt[i].size & 0x60) == 0x40 ? "32" : "16");
}
}
void
idt_dump()
{
const table_ptr &table = g_idtr;
log::info(logs::boot, "Loaded IDT at: %lx size: %d bytes", table.base, table.limit+1);
int count = (table.limit + 1) / sizeof(idt_descriptor);
const idt_descriptor *idt =
reinterpret_cast<const idt_descriptor *>(table.base);
for (int i = 0; i < count; ++i) {
uint64_t base =
(static_cast<uint64_t>(idt[i].base_high) << 32) |
(static_cast<uint64_t>(idt[i].base_mid) << 16) |
idt[i].base_low;
char const *type;
switch (idt[i].flags & 0xf) {
case 0x5: type = " 32tsk "; break;
case 0x6: type = " 16int "; break;
case 0x7: type = " 16trp "; break;
case 0xe: type = " 32int "; break;
case 0xf: type = " 32trp "; break;
default: type = " ????? "; break;
}
if (idt[i].flags & 0x80) {
log::debug(logs::boot,
" Entry %3d: Base:%lx Sel(rpl %d, ti %d, %3d) IST:%d %s DPL:%d", i, base,
(idt[i].selector & 0x3),
((idt[i].selector & 0x4) >> 2),
(idt[i].selector >> 3),
idt[i].ist,
type,
((idt[i].flags >> 5) & 0x3));
}
}
}

32
src/kernel/gdt.h Normal file
View File

@@ -0,0 +1,32 @@
#pragma once
/// \file gdt.h
/// Definitions relating to system descriptor tables: GDT, IDT, TSS
#include <stdint.h>
#include "kutil/memory.h"
/// Set up the GDT and TSS, and switch segment registers to point
/// to them.
void gdt_init();
/// Set an entry in the IDT
/// \arg i Index in the IDT (vector of the interrupt this handles)
/// \arg addr Address of the handler
/// \arg selector GDT selector to set when invoking this handler
/// \arg flags Descriptor flags to set
void idt_set_entry(uint8_t i, uint64_t addr, uint16_t selector, uint8_t flags);
/// Set the stack pointer for a given ring in the TSS
/// \arg ring Ring to set for (0-2)
/// \arg rsp Stack pointer to set
void tss_set_stack(int ring, addr_t rsp);
/// Get the stack pointer for a given ring in the TSS
/// \arg ring Ring to get (0-2)
/// \returns Stack pointers for that ring
addr_t tss_get_stack(int ring);
/// Dump information about the current GDT to the screen
void gdt_dump();
/// Dump information about the current IDT to the screen
void idt_dump();

View File

@@ -134,6 +134,8 @@ IRQ (0x7d, 0x5d, irq5D)
IRQ (0x7e, 0x5e, irq5E) IRQ (0x7e, 0x5e, irq5E)
IRQ (0x7f, 0x5f, irq5F) IRQ (0x7f, 0x5f, irq5F)
ISR (0xe7, isrAssert)
ISR (0xec, isrTimer) ISR (0xec, isrTimer)
ISR (0xed, isrLINT0) ISR (0xed, isrLINT0)
ISR (0xee, isrLINT1) ISR (0xee, isrLINT1)

View File

@@ -1,72 +1,18 @@
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include "kutil/enum_bitfields.h"
#include "kutil/memory.h"
#include "console.h" #include "console.h"
#include "cpu.h"
#include "device_manager.h" #include "device_manager.h"
#include "gdt.h"
#include "interrupts.h" #include "interrupts.h"
#include "io.h" #include "io.h"
#include "log.h" #include "log.h"
#include "scheduler.h"
enum class gdt_flags : uint8_t
{
ac = 0x01,
rw = 0x02,
dc = 0x04,
ex = 0x08,
r1 = 0x20,
r2 = 0x40,
r3 = 0x60,
pr = 0x80,
res_1 = 0x10
};
IS_BITFIELD(gdt_flags);
struct gdt_descriptor
{
uint16_t limit_low;
uint16_t base_low;
uint8_t base_mid;
uint8_t flags;
uint8_t granularity;
uint8_t base_high;
} __attribute__ ((packed));
struct idt_descriptor
{
uint16_t base_low;
uint16_t selector;
uint8_t ist;
uint8_t flags;
uint16_t base_mid;
uint32_t base_high;
uint32_t reserved; // must be zero
} __attribute__ ((packed));
struct table_ptr
{
uint16_t limit;
uint64_t base;
} __attribute__ ((packed));
gdt_descriptor g_gdt_table[10];
idt_descriptor g_idt_table[256];
table_ptr g_gdtr;
table_ptr g_idtr;
struct registers;
extern "C" { extern "C" {
void idt_write(); addr_t isr_handler(addr_t, cpu_state);
void idt_load(); addr_t irq_handler(addr_t, cpu_state);
addr_t syscall_handler(addr_t, cpu_state);
void gdt_write();
void gdt_load();
void isr_handler(registers);
void irq_handler(registers);
#define ISR(i, name) extern void name (); #define ISR(i, name) extern void name ();
#define EISR(i, name) extern void name (); #define EISR(i, name) extern void name ();
@@ -77,9 +23,6 @@ extern "C" {
#undef ISR #undef ISR
} }
void idt_dump(const table_ptr &table);
void gdt_dump(const table_ptr &table);
isr isr
operator+(const isr &lhs, int rhs) operator+(const isr &lhs, int rhs)
{ {
@@ -103,33 +46,6 @@ get_irq(unsigned vector)
} }
} }
void
set_gdt_entry(uint8_t i, uint32_t base, uint32_t limit, bool is64, gdt_flags flags)
{
g_gdt_table[i].limit_low = limit & 0xffff;
g_gdt_table[i].base_low = base & 0xffff;
g_gdt_table[i].base_mid = (base >> 16) & 0xff;
g_gdt_table[i].base_high = (base >> 24) & 0xff;
g_gdt_table[i].granularity =
((limit >> 16) & 0xf) | (is64 ? 0xa0 : 0xc0);
g_gdt_table[i].flags = static_cast<uint8_t>(flags | gdt_flags::res_1 | gdt_flags::pr);
}
void
set_idt_entry(uint8_t i, uint64_t addr, uint16_t selector, uint8_t flags)
{
g_idt_table[i].base_low = addr & 0xffff;
g_idt_table[i].base_mid = (addr >> 16) & 0xffff;
g_idt_table[i].base_high = (addr >> 32) & 0xffffffff;
g_idt_table[i].selector = selector;
g_idt_table[i].flags = flags;
g_idt_table[i].ist = 0;
g_idt_table[i].reserved = 0;
}
static void static void
disable_legacy_pic() disable_legacy_pic()
{ {
@@ -164,48 +80,20 @@ enable_serial_interrupts()
void void
interrupts_init() interrupts_init()
{ {
kutil::memset(&g_gdt_table, 0, sizeof(g_gdt_table)); #define ISR(i, name) idt_set_entry(i, reinterpret_cast<uint64_t>(& name), 0x08, 0x8e);
kutil::memset(&g_idt_table, 0, sizeof(g_idt_table)); #define EISR(i, name) idt_set_entry(i, reinterpret_cast<uint64_t>(& name), 0x08, 0x8e);
#define IRQ(i, q, name) idt_set_entry(i, reinterpret_cast<uint64_t>(& name), 0x08, 0x8e);
g_gdtr.limit = sizeof(g_gdt_table) - 1;
g_gdtr.base = reinterpret_cast<uint64_t>(&g_gdt_table);
set_gdt_entry(1, 0, 0xfffff, false, gdt_flags::rw);
set_gdt_entry(2, 0, 0xfffff, false, gdt_flags::rw | gdt_flags::ex | gdt_flags::dc);
set_gdt_entry(3, 0, 0xfffff, false, gdt_flags::rw);
set_gdt_entry(4, 0, 0xfffff, false, gdt_flags::rw | gdt_flags::ex);
set_gdt_entry(6, 0, 0xfffff, false, gdt_flags::rw);
set_gdt_entry(7, 0, 0xfffff, true, gdt_flags::rw | gdt_flags::ex);
gdt_write();
g_idtr.limit = sizeof(g_idt_table) - 1;
g_idtr.base = reinterpret_cast<uint64_t>(&g_idt_table);
#define ISR(i, name) set_idt_entry(i, reinterpret_cast<uint64_t>(& name), 0x38, 0x8e);
#define EISR(i, name) set_idt_entry(i, reinterpret_cast<uint64_t>(& name), 0x38, 0x8e);
#define IRQ(i, q, name) set_idt_entry(i, reinterpret_cast<uint64_t>(& name), 0x38, 0x8e);
#include "interrupt_isrs.inc" #include "interrupt_isrs.inc"
#undef IRQ #undef IRQ
#undef EISR #undef EISR
#undef ISR #undef ISR
idt_write();
disable_legacy_pic(); disable_legacy_pic();
enable_serial_interrupts(); enable_serial_interrupts();
log::info(logs::boot, "Interrupts enabled."); log::info(logs::boot, "Interrupts enabled.");
} }
struct registers
{
uint64_t ds;
uint64_t rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax;
uint64_t interrupt, errorcode;
uint64_t rip, cs, eflags, user_esp, ss;
};
#define print_reg(name, value) cons->printf(" %s: %016lx\n", name, (value)); #define print_reg(name, value) cons->printf(" %s: %016lx\n", name, (value));
extern "C" uint64_t get_frame(int frame); extern "C" uint64_t get_frame(int frame);
@@ -222,14 +110,16 @@ print_stacktrace(int skip = 0)
} }
} }
void addr_t
isr_handler(registers regs) isr_handler(addr_t return_rsp, cpu_state regs)
{ {
console *cons = console::get(); console *cons = console::get();
switch (static_cast<isr>(regs.interrupt & 0xff)) { switch (static_cast<isr>(regs.interrupt & 0xff)) {
case isr::isrTimer: case isr::isrTimer: {
cons->puts("\nTICK\n"); scheduler &s = scheduler::get();
return_rsp = s.tick(return_rsp);
}
break; break;
case isr::isrLINT0: case isr::isrLINT0:
@@ -248,11 +138,67 @@ isr_handler(registers regs)
case isr::isrIgnore5: case isr::isrIgnore5:
case isr::isrIgnore6: case isr::isrIgnore6:
case isr::isrIgnore7: case isr::isrIgnore7:
break;
/* case isr::isrGPFault: {
cons->printf("\nIGNORED PIC INTERRUPT %d\n", cons->set_color(9);
(regs.interrupt % 0xff) - 0xf0); cons->puts("\nGeneral Protection Fault:\n");
*/ cons->set_color();
cons->puts(" flags:");
if (regs.errorcode & 0x01) cons->puts(" external");
int index = (regs.errorcode & 0xf8) >> 3;
if (index) {
switch (regs.errorcode & 0x06) {
case 0:
cons->printf(" GDT[%d]\n", index);
gdt_dump();
break;
case 1:
case 3:
cons->printf(" IDT[%d]\n", index);
idt_dump();
break;
default:
cons->printf(" LDT[%d]??\n", index);
break;
}
} else {
cons->putc('\n');
}
cons->puts("\n");
print_reg(" ds", regs.ds);
print_reg(" cs", regs.cs);
print_reg(" ss", regs.ss);
cons->puts("\n");
print_reg("rax", regs.rax);
print_reg("rbx", regs.rbx);
print_reg("rcx", regs.rcx);
print_reg("rdx", regs.rdx);
print_reg("rdi", regs.rdi);
print_reg("rsi", regs.rsi);
cons->puts("\n");
print_reg("rbp", regs.rbp);
cons->puts("\n");
print_reg("rip", regs.rip);
print_stacktrace(2);
cons->puts("\nStack:\n");
uint64_t sp = regs.user_rsp;
while (sp <= regs.rbp) {
cons->printf("%016x: %016x\n", sp, *reinterpret_cast<uint64_t *>(sp));
sp += sizeof(uint64_t);
}
}
while(1) asm("hlt");
break; break;
case isr::isrPageFault: { case isr::isrPageFault: {
@@ -272,6 +218,7 @@ isr_handler(registers regs)
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2)); __asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
print_reg("cr2", cr2); print_reg("cr2", cr2);
print_reg("rsp", regs.user_rsp);
print_reg("rip", regs.rip); print_reg("rip", regs.rip);
cons->puts("\n"); cons->puts("\n");
@@ -280,6 +227,28 @@ isr_handler(registers regs)
while(1) asm("hlt"); while(1) asm("hlt");
break; break;
case isr::isrAssert: {
cons->set_color();
cons->puts("\n");
print_reg("rax", regs.rax);
print_reg("rbx", regs.rbx);
print_reg("rcx", regs.rcx);
print_reg("rdx", regs.rdx);
print_reg("rdi", regs.rdi);
print_reg("rsi", regs.rsi);
cons->puts("\n");
print_reg("rbp", regs.rbp);
print_reg("rsp", regs.user_rsp);
cons->puts("\n");
print_reg("rip", regs.rip);
print_stacktrace(2);
}
while(1) asm("hlt");
break;
default: default:
cons->set_color(9); cons->set_color(9);
cons->puts("\nReceived ISR interrupt:\n"); cons->puts("\nReceived ISR interrupt:\n");
@@ -293,7 +262,6 @@ isr_handler(registers regs)
print_reg("rdi", regs.rdi); print_reg("rdi", regs.rdi);
print_reg("rsi", regs.rsi); print_reg("rsi", regs.rsi);
print_reg("rbp", regs.rbp); print_reg("rbp", regs.rbp);
print_reg("rsp", regs.rsp);
print_reg("rbx", regs.rbx); print_reg("rbx", regs.rbx);
print_reg("rdx", regs.rdx); print_reg("rdx", regs.rdx);
print_reg("rcx", regs.rcx); print_reg("rcx", regs.rcx);
@@ -302,20 +270,21 @@ isr_handler(registers regs)
print_reg("rip", regs.rip); print_reg("rip", regs.rip);
print_reg(" cs", regs.cs); print_reg(" cs", regs.cs);
print_reg(" ef", regs.eflags); print_reg(" ef", regs.rflags);
print_reg("esp", regs.user_esp); print_reg("rsp", regs.user_rsp);
print_reg(" ss", regs.ss); print_reg(" ss", regs.ss);
cons->puts("\n"); cons->puts("\n");
print_stacktrace(2); print_stacktrace(2);
while(1) asm("hlt"); while(1) asm("hlt");
} }
*reinterpret_cast<uint32_t *>(0xffffff80fee000b0) = 0; *reinterpret_cast<uint32_t *>(0xffffff80fee000b0) = 0;
return return_rsp;
} }
void addr_t
irq_handler(registers regs) irq_handler(addr_t return_rsp, cpu_state regs)
{ {
console *cons = console::get(); console *cons = console::get();
uint8_t irq = get_irq(regs.interrupt); uint8_t irq = get_irq(regs.interrupt);
@@ -329,7 +298,6 @@ irq_handler(registers regs)
print_reg("rdi", regs.rdi); print_reg("rdi", regs.rdi);
print_reg("rsi", regs.rsi); print_reg("rsi", regs.rsi);
print_reg("rbp", regs.rbp); print_reg("rbp", regs.rbp);
print_reg("rsp", regs.rsp);
print_reg("rbx", regs.rbx); print_reg("rbx", regs.rbx);
print_reg("rdx", regs.rdx); print_reg("rdx", regs.rdx);
print_reg("rcx", regs.rcx); print_reg("rcx", regs.rcx);
@@ -338,87 +306,46 @@ irq_handler(registers regs)
print_reg("rip", regs.rip); print_reg("rip", regs.rip);
print_reg(" cs", regs.cs); print_reg(" cs", regs.cs);
print_reg(" ef", regs.eflags); print_reg(" ef", regs.rflags);
print_reg("esp", regs.user_esp); print_reg("rsp", regs.user_rsp);
print_reg(" ss", regs.ss); print_reg(" ss", regs.ss);
while(1) asm("hlt"); while(1) asm("hlt");
} }
*reinterpret_cast<uint32_t *>(0xffffff80fee000b0) = 0; *reinterpret_cast<uint32_t *>(0xffffff80fee000b0) = 0;
return return_rsp;
} }
addr_t
void syscall_handler(addr_t return_rsp, cpu_state regs)
gdt_dump(const table_ptr &table)
{ {
log::info(logs::boot, "Loaded GDT at: %lx size: %d bytes", table.base, table.limit+1); console *cons = console::get();
cons->printf("SYSCALL\n");
int count = (table.limit + 1) / sizeof(gdt_descriptor); cons->puts("\n");
const gdt_descriptor *gdt = print_reg("rax", regs.rax);
reinterpret_cast<const gdt_descriptor *>(table.base); print_reg("rbx", regs.rbx);
print_reg("rcx", regs.rcx);
print_reg("rdx", regs.rdx);
print_reg("rdi", regs.rdi);
print_reg("rsi", regs.rsi);
for (int i = 0; i < count; ++i) { cons->puts("\n");
uint32_t base = print_reg(" r8", regs.r8);
(gdt[i].base_high << 24) | print_reg(" r9", regs.r9);
(gdt[i].base_mid << 16) | print_reg("r10", regs.r10);
gdt[i].base_low; print_reg("r11", regs.r11);
print_reg("r12", regs.r12);
print_reg("r13", regs.r13);
print_reg("r14", regs.r14);
print_reg("r15", regs.r15);
uint32_t limit = cons->puts("\n");
static_cast<uint32_t>(gdt[i].granularity & 0x0f) << 16 | print_reg("rbp", regs.rbp);
gdt[i].limit_low; print_reg("rsp", regs.user_rsp);
print_reg("sp0", tss_get_stack(0));
if (gdt[i].flags & 0x80) { cons->puts("\n");
log::debug(logs::boot, print_reg("rip", regs.rip);
" Entry %3d: Base %x limit %x privs %d flags %s%s%s%s%s%s", return return_rsp;
i, base, limit, ((gdt[i].flags >> 5) & 0x03),
(gdt[i].flags & 0x80) ? "P " : " ",
(gdt[i].flags & 0x08) ? "ex " : " ",
(gdt[i].flags & 0x04) ? "dc " : " ",
(gdt[i].flags & 0x02) ? "rw " : " ",
(gdt[i].granularity & 0x80) ? "kb " : "b ",
(gdt[i].granularity & 0x60) == 0x60 ? "64" :
(gdt[i].granularity & 0x60) == 0x40 ? "32" : "16"
);
}
}
}
void
idt_dump(const table_ptr &table)
{
log::info(logs::boot, "Loaded IDT at: %lx size: %d bytes", table.base, table.limit+1);
int count = (table.limit + 1) / sizeof(idt_descriptor);
const idt_descriptor *idt =
reinterpret_cast<const idt_descriptor *>(table.base);
for (int i = 0; i < count; ++i) {
uint64_t base =
(static_cast<uint64_t>(idt[i].base_high) << 32) |
(static_cast<uint64_t>(idt[i].base_mid) << 16) |
idt[i].base_low;
char const *type;
switch (idt[i].flags & 0xf) {
case 0x5: type = " 32tsk "; break;
case 0x6: type = " 16int "; break;
case 0x7: type = " 16trp "; break;
case 0xe: type = " 32int "; break;
case 0xf: type = " 32trp "; break;
default: type = " ????? "; break;
}
if (idt[i].flags & 0x80) {
log::debug(logs::boot,
" Entry %3d: Base:%lx Sel(rpl %d, ti %d, %3d) IST:%d %s DPL:%d", i, base,
(idt[i].selector & 0x3),
((idt[i].selector & 0x4) >> 2),
(idt[i].selector >> 3),
idt[i].ist,
type,
((idt[i].flags >> 5) & 0x3));
}
}
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
/// \file interrupts.h /// \file interrupts.h
/// Free functions and definitions related to interrupt service vectors /// Free functions and definitions related to interrupt service vectors
#include <stdint.h>
/// Enum of all defined ISR/IRQ vectors /// Enum of all defined ISR/IRQ vectors
@@ -17,11 +18,17 @@ enum class isr : uint8_t
_zero = 0 _zero = 0
}; };
/// Helper operator to add an offset to an isr vector
isr operator+(const isr &lhs, int rhs); isr operator+(const isr &lhs, int rhs);
extern "C" { extern "C" {
/// Set the CPU interrupt enable flag (sti)
void interrupts_enable(); void interrupts_enable();
/// Set the CPU interrupt disable flag (cli)
void interrupts_disable(); void interrupts_disable();
} }
/// Fill the IDT with our ISRs, and disable the legacy
/// PIC interrupts.
void interrupts_init(); void interrupts_init();

View File

@@ -14,6 +14,18 @@ idt_load:
global gdt_write global gdt_write
gdt_write: gdt_write:
lgdt [rel g_gdtr] lgdt [rel g_gdtr]
mov ax, si ; second arg is data segment
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
push qword rdi ; first arg is code segment
lea rax, [rel .next]
push rax
o64 retf
.next:
ltr dx
ret ret
global gdt_load global gdt_load
@@ -26,11 +38,19 @@ gdt_load:
push rcx push rcx
push rdx push rdx
push rbx push rbx
push rsp
push rbp push rbp
push rsi push rsi
push rdi push rdi
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
mov ax, ds mov ax, ds
push rax push rax
%endmacro %endmacro
@@ -42,31 +62,32 @@ gdt_load:
mov fs, ax mov fs, ax
mov gs, ax mov gs, ax
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rdi pop rdi
pop rsi pop rsi
pop rbp pop rbp
pop rsp
pop rbx pop rbx
pop rdx pop rdx
pop rcx pop rcx
pop rax pop rax
%endmacro %endmacro
%macro load_kernel_segments 0
mov ax, 0x10 ; load the kernel data segment
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
%endmacro
extern isr_handler extern isr_handler
global isr_handler_prelude global isr_handler_prelude
isr_handler_prelude: isr_handler_prelude:
push_all_and_segments push_all_and_segments
load_kernel_segments
mov rdi, rsp
call isr_handler call isr_handler
mov rsp, rax
pop_all_and_segments pop_all_and_segments
@@ -78,9 +99,10 @@ extern irq_handler
global irq_handler_prelude global irq_handler_prelude
irq_handler_prelude: irq_handler_prelude:
push_all_and_segments push_all_and_segments
load_kernel_segments
mov rdi, rsp
call irq_handler call irq_handler
mov rsp, rax
pop_all_and_segments pop_all_and_segments
@@ -120,3 +142,75 @@ irq_handler_prelude:
section .isrs section .isrs
%include "interrupt_isrs.inc" %include "interrupt_isrs.inc"
extern syscall_handler
syscall_handler_prelude:
push 0 ; ss, doesn't matter here
push rsp
pushf
push 0 ; cs, doesn't matter here
push rcx ; user rip
push 0 ; bogus interrupt
push 0 ; bogus errorcode
push_all_and_segments
mov rdi, rsp
call syscall_handler
mov rsp, rax
pop_all_and_segments
add rsp, 16 ; ignore bogus interrupt / error
pop rcx ; user rip
add rsp, 32 ; ignore cs, flags, rsp, ss
o64 sysret
global syscall_enable
syscall_enable:
; IA32_EFER - set bit 0, syscall enable
mov rcx, 0xc0000080
rdmsr
or rax, 0x1
wrmsr
; IA32_STAR - cs for syscall
mov rcx, 0xc0000081
mov rax, 0 ; not used
mov rdx, 0x00180008 ; GDT:3 (user code), GDT:1 (kernel code)
wrmsr
; IA32_LSTAR - RIP for syscall
mov rcx, 0xc0000082
lea rax, [rel syscall_handler_prelude]
mov rdx, rax
shr rdx, 32
wrmsr
; IA32_FMASK - FLAGS mask inside syscall
mov rcx, 0xc0000084
mov rax, 0x200
mov rdx, 0
wrmsr
ret
global taskA
taskA:
push rbp
mov rbp, rsp
mov rax, 0xaaaaaaaaaaaaaaaa
.loop:
syscall
jmp .loop
global taskB
taskB:
push rbp
mov rbp, rsp
mov rax, 0xbbbbbbbbbbbbbbbb
.loop:
syscall
jmp .loop

View File

@@ -31,7 +31,8 @@ wrmsr(uint64_t addr, uint64_t value)
} }
void void
io_wait() io_wait(unsigned times)
{ {
outb(0x80, 0); for (unsigned i = 0; i < times; ++i)
outb(0x80, 0);
} }

View File

@@ -25,7 +25,8 @@ uint64_t rdmsr(uint64_t addr);
void wrmsr(uint64_t addr, uint64_t value); void wrmsr(uint64_t addr, uint64_t value);
/// Pause briefly by doing IO to port 0x80 /// Pause briefly by doing IO to port 0x80
void io_wait(); /// \arg times Number of times to delay by writing
void io_wait(unsigned times = 1);
} }

View File

@@ -10,11 +10,13 @@ static const uint8_t level_colors[] = {0x07, 0x0f, 0x0b, 0x09};
static const char *levels[] = {"debug", " info", " warn", "error", "fatal"}; static const char *levels[] = {"debug", " info", " warn", "error", "fatal"};
static const char *areas[] = { static const char *areas[] = {
"boot", "boot ",
"mem ", "memory",
"apic", "apic ",
"dev ", "device",
"driv", "driver",
"file ",
"task ",
nullptr nullptr
}; };

View File

@@ -12,6 +12,8 @@ enum class logs
apic, apic,
device, device,
driver, driver,
fs,
task,
max max
}; };

View File

@@ -3,32 +3,29 @@
#include "kutil/assert.h" #include "kutil/assert.h"
#include "kutil/memory.h" #include "kutil/memory.h"
#include "block_device.h"
#include "ahci/driver.h"
#include "ahci/port.h"
#include "console.h" #include "console.h"
#include "cpu.h" #include "cpu.h"
#include "device_manager.h" #include "device_manager.h"
#include "font.h" #include "font.h"
#include "gdt.h"
#include "interrupts.h" #include "interrupts.h"
#include "io.h" #include "io.h"
#include "kernel_data.h" #include "kernel_data.h"
#include "log.h" #include "log.h"
#include "memory.h" #include "memory.h"
#include "page_manager.h" #include "page_manager.h"
#include "scheduler.h"
#include "screen.h" #include "screen.h"
#include "serial.h" #include "serial.h"
extern "C" { extern "C" {
void do_the_set_registers(popcorn_data *header);
void kernel_main(popcorn_data *header); void kernel_main(popcorn_data *header);
void syscall_enable();
void *__bss_start, *__bss_end; void *__bss_start, *__bss_end;
} }
extern ahci_driver ahcid;
extern void __kernel_assert(const char *, unsigned, const char *); extern void __kernel_assert(const char *, unsigned, const char *);
void void
@@ -58,7 +55,9 @@ init_console(const popcorn_data *header)
log::enable(logs::apic, log::level::info); log::enable(logs::apic, log::level::info);
log::enable(logs::device, log::level::debug); log::enable(logs::device, log::level::debug);
log::enable(logs::driver, log::level::debug); log::enable(logs::driver, log::level::debug);
log::enable(logs::memory, log::level::debug); log::enable(logs::memory, log::level::info);
log::enable(logs::fs, log::level::debug);
log::enable(logs::task, log::level::debug);
} }
void do_error_3() { volatile int x = 1; volatile int y = 0; volatile int z = x / y; } void do_error_3() { volatile int x = 1; volatile int y = 0; volatile int z = x / y; }
@@ -70,6 +69,9 @@ kernel_main(popcorn_data *header)
{ {
kutil::assert_set_callback(__kernel_assert); kutil::assert_set_callback(__kernel_assert);
gdt_init();
interrupts_init();
page_manager *pager = new (&g_page_manager) page_manager; page_manager *pager = new (&g_page_manager) page_manager;
memory_initialize( memory_initialize(
@@ -82,43 +84,73 @@ kernel_main(popcorn_data *header)
header->frame_buffer_length); header->frame_buffer_length);
init_console(header); init_console(header);
// pager->dump_blocks();
interrupts_init(); log::debug(logs::boot, " Popcorn header is at: %016lx", header);
log::debug(logs::boot, " Framebuffer is at: %016lx", header->frame_buffer);
log::debug(logs::boot, " Font data is at: %016lx", header->font);
log::debug(logs::boot, " Kernel data is at: %016lx", header->data);
log::debug(logs::boot, " Memory map is at: %016lx", header->memory_map);
log::debug(logs::boot, "ACPI root table is at: %016lx", header->acpi_table);
log::debug(logs::boot, "Runtime service is at: %016lx", header->runtime);
/*
pager->dump_pml4(nullptr, 0);
pager->dump_blocks(true);
*/
device_manager *devices = device_manager *devices =
new (&device_manager::get()) device_manager(header->acpi_table); new (&device_manager::get()) device_manager(header->acpi_table);
interrupts_enable(); interrupts_enable();
/*
cpu_id cpu; cpu_id cpu;
log::info(logs::boot, "CPU Vendor: %s", cpu.vendor_id()); log::info(logs::boot, "CPU Vendor: %s", cpu.vendor_id());
log::info(logs::boot, "CPU Family %x Model %x Stepping %x", log::info(logs::boot, "CPU Family %x Model %x Stepping %x",
cpu.family(), cpu.model(), cpu.stepping()); cpu.family(), cpu.model(), cpu.stepping());
auto r = cpu.get(0x15);
log::info(logs::boot, "CPU Crystal: %dHz", r.ecx);
addr_t cr4 = 0;
__asm__ __volatile__ ( "mov %%cr4, %0" : "=r" (cr4) );
log::info(logs::boot, "cr4: %016x", cr4);
*/
devices->init_drivers(); devices->init_drivers();
ahci::port *disk = ahcid.find_disk(); /*
block_device *disk = devices->get_block_device(0);
if (disk) { if (disk) {
uint8_t buf[512]; for (int i=0; i<1; ++i) {
kutil::memset(buf, 0, 512); uint8_t buf[512];
kutil::memset(buf, 0, 512);
disk->read(1, sizeof(buf), buf); kassert(disk->read(0x200, sizeof(buf), buf),
while (buf[0] == 0) io_wait(); "Disk read returned 0");
console *cons = console::get(); console *cons = console::get();
uint8_t *p = &buf[0]; uint8_t *p = &buf[0];
for (int i = 0; i < 8; ++i) { for (int i = 0; i < 8; ++i) {
for (int j = 0; j < 16; ++j) { for (int j = 0; j < 16; ++j) {
cons->printf(" %02x", *p++); cons->printf(" %02x", *p++);
}
cons->putc('\n');
} }
cons->putc('\n');
} }
} else {
log::warn(logs::boot, "No block devices present.");
} }
*/
// do_error_1(); // do_error_1();
// __asm__ __volatile__("int $15"); // __asm__ __volatile__("int $15");
g_console.puts("boogity!"); // pager->dump_pml4();
do_the_set_registers(header);
syscall_enable();
scheduler *sched = new (&scheduler::get()) scheduler(devices->get_lapic());
sched->start();
g_console.puts("boogity!\n");
} }

View File

@@ -4,6 +4,7 @@
#include "page_manager.h" #include "page_manager.h"
const unsigned efi_page_size = 0x1000; const unsigned efi_page_size = 0x1000;
const unsigned ident_page_flags = 0xb;
enum class efi_memory_type : uint32_t enum class efi_memory_type : uint32_t
{ {
@@ -306,7 +307,7 @@ copy_new_table(page_table *base, unsigned index, page_table *new_table)
for (int i = 0; i < 512; ++i) new_table->entries[i] = 0; for (int i = 0; i < 512; ++i) new_table->entries[i] = 0;
} }
base->entries[index] = reinterpret_cast<uint64_t>(new_table) | 0xb; base->entries[index] = reinterpret_cast<uint64_t>(new_table) | ident_page_flags;
} }
static uint64_t static uint64_t
@@ -363,7 +364,7 @@ check_needs_page_ident(page_table *table, unsigned index, page_table **free_page
page_table *new_table = (*free_pages)++; page_table *new_table = (*free_pages)++;
for (int i=0; i<512; ++i) new_table->entries[i] = 0; for (int i=0; i<512; ++i) new_table->entries[i] = 0;
table->entries[index] = reinterpret_cast<uint64_t>(new_table) | 0xb; table->entries[index] = reinterpret_cast<uint64_t>(new_table) | ident_page_flags;
return 1; return 1;
} }
@@ -390,12 +391,24 @@ page_in_ident(
tables[1]->entries[idx[1]] & ~0xfffull); tables[1]->entries[idx[1]] & ~0xfffull);
for (; idx[2] < 512; idx[2] += 1, idx[3] = 0) { for (; idx[2] < 512; idx[2] += 1, idx[3] = 0) {
if (idx[3] == 0 &&
count >= 512 &&
tables[2]->get(idx[2]) == nullptr) {
// Do a 2MiB page instead
tables[2]->entries[idx[2]] = phys_addr | 0x80 | ident_page_flags;
phys_addr += page_manager::page_size * 512;
count -= 512;
if (count == 0) return pages_consumed;
continue;
}
pages_consumed += check_needs_page_ident(tables[2], idx[2], &free_pages); pages_consumed += check_needs_page_ident(tables[2], idx[2], &free_pages);
tables[3] = reinterpret_cast<page_table *>( tables[3] = reinterpret_cast<page_table *>(
tables[2]->entries[idx[2]] & ~0xfffull); tables[2]->entries[idx[2]] & ~0xfffull);
for (; idx[3] < 512; idx[3] += 1) { for (; idx[3] < 512; idx[3] += 1) {
tables[3]->entries[idx[3]] = phys_addr | 0xb; tables[3]->entries[idx[3]] = phys_addr | ident_page_flags;
phys_addr += page_manager::page_size; phys_addr += page_manager::page_size;
if (--count == 0) return pages_consumed; if (--count == 0) return pages_consumed;
} }
@@ -423,7 +436,7 @@ memory_initialize(const void *memory_map, size_t map_length, size_t desc_length)
// Offset-map this region into the higher half. // Offset-map this region into the higher half.
uint64_t free_region_start_virt = uint64_t free_region_start_virt =
free_region_start_phys + page_manager::high_offset; free_region_start_phys + page_manager::page_offset;
uint64_t free_next = free_region_start_virt; uint64_t free_next = free_region_start_virt;

View File

@@ -2,6 +2,7 @@
#include "kutil/assert.h" #include "kutil/assert.h"
#include "kutil/memory_manager.h" #include "kutil/memory_manager.h"
#include "console.h"
#include "log.h" #include "log.h"
#include "page_manager.h" #include "page_manager.h"
@@ -140,7 +141,7 @@ page_block::dump(page_block *list, const char *name, bool show_unmapped)
if (cur->virtual_address) { if (cur->virtual_address) {
page_table_indices start{cur->virtual_address}; page_table_indices start{cur->virtual_address};
log::info(logs::memory, " %lx %x [%6d] %lx (%d,%d,%d,%d)", log::info(logs::memory, " %016lx %08x [%6d] %016lx (%d,%d,%d,%d)",
cur->physical_address, cur->physical_address,
cur->flags, cur->flags,
cur->count, cur->count,
@@ -148,7 +149,7 @@ page_block::dump(page_block *list, const char *name, bool show_unmapped)
start[0], start[1], start[2], start[3]); start[0], start[1], start[2], start[3]);
} else { } else {
page_table_indices start{cur->virtual_address}; page_table_indices start{cur->virtual_address};
log::info(logs::memory, " %lx %x [%6d]", log::info(logs::memory, " %016lx %08x [%6d]",
cur->physical_address, cur->physical_address,
cur->flags, cur->flags,
cur->count); cur->count);
@@ -218,6 +219,34 @@ page_manager::init(
new (&g_kernel_memory_manager) kutil::memory_manager( new (&g_kernel_memory_manager) kutil::memory_manager(
reinterpret_cast<void *>(end), reinterpret_cast<void *>(end),
mm_grow_callback); mm_grow_callback);
m_kernel_pml4 = get_pml4();
}
page_table *
page_manager::create_process_map()
{
page_table *table = get_table_page();
kutil::memset(table, 0, page_size);
table->entries[510] = m_kernel_pml4->entries[510];
table->entries[511] = m_kernel_pml4->entries[511];
// Create the initial user stack
map_pages(
initial_stack - (initial_stack_pages * page_size),
initial_stack_pages,
true, // This is the ring3 stack, user = true
table);
return table;
}
void
page_manager::delete_process_map(page_table *table)
{
// TODO: recurse table entries and mark them free
unmap_pages(table, 1);
} }
void void
@@ -247,10 +276,19 @@ page_manager::map_offset_pointer(void **pointer, size_t length)
} }
void void
page_manager::dump_blocks() page_manager::dump_blocks(bool used_only)
{ {
page_block::dump(m_used, "used", true); page_block::dump(m_used, "used", true);
page_block::dump(m_free, "free", true); if (!used_only)
page_block::dump(m_free, "free", true);
}
void
page_manager::dump_pml4(page_table *pml4, int max_index)
{
if (pml4 == nullptr)
pml4 = get_pml4();
pml4->dump(4, max_index);
} }
page_block * page_block *
@@ -338,10 +376,11 @@ page_manager::consolidate_blocks()
} }
void * void *
page_manager::map_pages(addr_t address, size_t count) page_manager::map_pages(addr_t address, size_t count, bool user, page_table *pml4)
{ {
void *ret = reinterpret_cast<void *>(address); void *ret = reinterpret_cast<void *>(address);
page_table *pml4 = get_pml4(); if (pml4 == nullptr)
pml4 = get_pml4();
while (count) { while (count) {
kassert(m_free, "page_manager::map_pages ran out of free pages!"); kassert(m_free, "page_manager::map_pages ran out of free pages!");
@@ -358,7 +397,10 @@ page_manager::map_pages(addr_t address, size_t count)
page_block_flags::mapped; page_block_flags::mapped;
page_block::insert(m_used, block); page_block::insert(m_used, block);
page_in(pml4, phys, address, n); log::debug(logs::memory, "Paging in %d pages at p:%016lx to v:%016lx into %016lx table",
n, phys, address, pml4);
page_in(pml4, phys, address, n, user);
address += n * page_size; address += n * page_size;
count -= n; count -= n;
@@ -374,7 +416,6 @@ page_manager::map_offset_pages(size_t count)
page_block *free = m_free; page_block *free = m_free;
page_block *prev = nullptr; page_block *prev = nullptr;
log::debug(logs::memory, "Got request to offset map %d pages", count);
while (free) { while (free) {
if (free->count < count) { if (free->count < count) {
@@ -405,6 +446,7 @@ page_manager::map_offset_pages(size_t count)
m_block_cache = free; m_block_cache = free;
} }
log::debug(logs::memory, "Got request for offset map %016lx [%d]", used->virtual_address, count);
page_in(pml4, used->physical_address, used->virtual_address, count); page_in(pml4, used->physical_address, used->virtual_address, count);
return reinterpret_cast<void *>(used->virtual_address); return reinterpret_cast<void *>(used->virtual_address);
} }
@@ -480,36 +522,47 @@ page_manager::unmap_pages(void* address, size_t count)
} }
void void
page_manager::check_needs_page(page_table *table, unsigned index) page_manager::check_needs_page(page_table *table, unsigned index, bool user)
{ {
if ((table->entries[index] & 0x1) == 1) return; if ((table->entries[index] & 0x1) == 1) return;
page_table *new_table = get_table_page(); page_table *new_table = get_table_page();
for (int i=0; i<512; ++i) new_table->entries[i] = 0; for (int i=0; i<512; ++i) new_table->entries[i] = 0;
table->entries[index] = pt_to_phys(new_table) | 0xb; table->entries[index] = pt_to_phys(new_table) | (user ? 0xf : 0xb);
} }
void void
page_manager::page_in(page_table *pml4, addr_t phys_addr, addr_t virt_addr, size_t count) page_manager::page_in(page_table *pml4, addr_t phys_addr, addr_t virt_addr, size_t count, bool user)
{ {
page_table_indices idx{virt_addr}; page_table_indices idx{virt_addr};
page_table *tables[4] = {pml4, nullptr, nullptr, nullptr}; page_table *tables[4] = {pml4, nullptr, nullptr, nullptr};
for (; idx[0] < 512; idx[0] += 1) { for (; idx[0] < 512; idx[0] += 1) {
check_needs_page(tables[0], idx[0]); check_needs_page(tables[0], idx[0], user);
tables[1] = tables[0]->get(idx[0]); tables[1] = tables[0]->get(idx[0]);
for (; idx[1] < 512; idx[1] += 1, idx[2] = 0, idx[3] = 0) { for (; idx[1] < 512; idx[1] += 1, idx[2] = 0, idx[3] = 0) {
check_needs_page(tables[1], idx[1]); check_needs_page(tables[1], idx[1], user);
tables[2] = tables[1]->get(idx[1]); tables[2] = tables[1]->get(idx[1]);
for (; idx[2] < 512; idx[2] += 1, idx[3] = 0) { for (; idx[2] < 512; idx[2] += 1, idx[3] = 0) {
check_needs_page(tables[2], idx[2]); if (idx[3] == 0 &&
count >= 512 &&
tables[2]->get(idx[2]) == nullptr) {
// Do a 2MiB page instead
tables[2]->entries[idx[2]] = phys_addr | (user ? 0x8f : 0x8b);
phys_addr += page_size * 512;
count -= 512;
if (count == 0) return;
continue;
}
check_needs_page(tables[2], idx[2], user);
tables[3] = tables[2]->get(idx[2]); tables[3] = tables[2]->get(idx[2]);
for (; idx[3] < 512; idx[3] += 1) { for (; idx[3] < 512; idx[3] += 1) {
tables[3]->entries[idx[3]] = phys_addr | 0xb; tables[3]->entries[idx[3]] = phys_addr | (user ? 0xf : 0xb);
phys_addr += page_manager::page_size; phys_addr += page_size;
if (--count == 0) return; if (--count == 0) return;
} }
} }
@@ -571,40 +624,40 @@ page_manager::pop_pages(size_t count, addr_t *address)
void void
page_table::dump(int level, uint64_t offset) page_table::dump(int level, int max_index, uint64_t offset)
{ {
log::info(logs::memory, "Level %d page table @ %lx (off %lx):", level, this, offset); console *cons = console::get();
cons->printf("\nLevel %d page table @ %lx (off %lx):\n", level, this, offset);
for (int i=0; i<512; ++i) { for (int i=0; i<512; ++i) {
uint64_t ent = entries[i]; uint64_t ent = entries[i];
if (ent == 0) continue; if (ent == 0) continue;
if ((ent & 0x1) == 0) { if ((ent & 0x1) == 0) {
log::info(logs::memory, " %3d: %lx NOT PRESENT", i, ent); cons->printf(" %3d: %016lx NOT PRESENT\n", i, ent);
continue; continue;
} }
if ((level == 2 || level == 3) && (ent & 0x80) == 0x80) { if ((level == 2 || level == 3) && (ent & 0x80) == 0x80) {
log::info(logs::memory, " %3d: %lx -> Large page at %lx", cons->printf(" %3d: %016lx -> Large page at %016lx\n", i, ent, ent & ~0xfffull);
i, ent, ent & ~0xfffull);
continue; continue;
} else if (level == 1) { } else if (level == 1) {
log::info(logs::memory, " %3d: %lx -> Page at %lx", cons->printf(" %3d: %016lx -> Page at %016lx\n", i, ent, ent & ~0xfffull);
i, ent, ent & ~0xfffull);
} else { } else {
log::info(logs::memory, " %3d: %lx -> Level %d table at %lx", cons->printf(" %3d: %016lx -> Level %d table at %016lx\n",
i, ent, level - 1, (ent & ~0xfffull) + offset); i, ent, level - 1, (ent & ~0xfffull) + offset);
continue; continue;
} }
} }
if (--level > 0) { if (--level > 0) {
for (int i=0; i<512; ++i) { for (int i=0; i<=max_index; ++i) {
uint64_t ent = entries[i]; uint64_t ent = entries[i];
if ((ent & 0x1) == 0) continue; if ((ent & 0x1) == 0) continue;
if ((ent & 0x80)) continue; if ((ent & 0x80)) continue;
page_table *next = reinterpret_cast<page_table *>((ent & ~0xffful) + offset); page_table *next = reinterpret_cast<page_table *>((ent & ~0xffful) + offset);
next->dump(level, offset); next->dump(level, 511, offset);
} }
} }
} }

View File

@@ -21,18 +21,51 @@ public:
static const size_t page_size = 0x1000; static const size_t page_size = 0x1000;
/// Start of the higher half. /// Start of the higher half.
static const addr_t high_offset = 0xffff800000000000; static const addr_t high_offset = 0xffffff0000000000;
/// Offset from physical where page tables are mapped. /// Offset from physical where page tables are mapped.
static const addr_t page_offset = 0xffffff8000000000; static const addr_t page_offset = 0xffffff8000000000;
/// Initial process thread's stack address
static const addr_t initial_stack = 0x0000800000000000;
/// Initial process thread's stack size, in pages
static const unsigned initial_stack_pages = 1;
page_manager(); page_manager();
/// Helper to read the PML4 table from CR3.
/// \returns A pointer to the current PML4 table.
static inline page_table * get_pml4()
{
addr_t pml4 = 0;
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (pml4) );
return reinterpret_cast<page_table *>((pml4 & ~0xfffull) + page_offset);
}
/// Helper to set the PML4 table pointer in CR3.
/// \arg pml4 A pointer to the PML4 table to install.
static inline void set_pml4(page_table *pml4)
{
addr_t p = reinterpret_cast<addr_t>(pml4) - page_offset;
__asm__ __volatile__ ( "mov %0, %%cr3" :: "r" (p & ~0xfffull) );
}
/// Allocate but don't switch to a new PML4 table. This table
/// should only have global kernel pages mapped.
/// \returns A pointer to the PML4 table
page_table * create_process_map();
/// Deallocate a process' PML4 table.
void delete_process_map(page_table *table);
/// Allocate and map pages into virtual memory. /// Allocate and map pages into virtual memory.
/// \arg address The virtual address at which to map the pages /// \arg address The virtual address at which to map the pages
/// \arg count The number of pages to map /// \arg count The number of pages to map
/// \arg user True is this memory is user-accessible
/// \arg pml4 The pml4 to map into - null for the current one
/// \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, bool user = false, page_table *pml4 = nullptr);
/// Allocate and map contiguous pages into virtual memory, with /// Allocate and map contiguous pages into virtual memory, with
/// a constant offset from their physical address. /// a constant offset from their physical address.
@@ -68,7 +101,13 @@ public:
} }
/// Log the current free/used block lists. /// Log the current free/used block lists.
void dump_blocks(); /// \arg used_only If true, skip printing free list. Default false.
void dump_blocks(bool used_only = false);
/// Dump the given or current PML4 to the console
/// \arg pml4 The page table to use, null for the current one
/// \arg max_index The max index of pml4 to print
void dump_pml4(page_table *pml4 = nullptr, int max_index = 511);
/// Get the system page manager. /// Get the system page manager.
/// \returns A pointer to the system page manager /// \returns A pointer to the system page manager
@@ -105,40 +144,26 @@ private:
/// to the cache. /// to the cache.
void consolidate_blocks(); void consolidate_blocks();
/// Helper to read the PML4 table from CR3.
/// \returns A pointer to the current PML4 table.
static inline page_table * get_pml4()
{
addr_t pml4 = 0;
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (pml4) );
return reinterpret_cast<page_table *>((pml4 & ~0xfffull) + page_offset);
}
/// Helper to set the PML4 table pointer in CR3.
/// \arg pml4 A pointer to the PML4 table to install.
static inline void set_pml4(page_table *pml4)
{
addr_t p = reinterpret_cast<addr_t>(pml4) - page_offset;
__asm__ __volatile__ ( "mov %0, %%cr3" :: "r" (p & ~0xfffull) );
}
/// Helper function to allocate a new page table. If table entry `i` in /// Helper function to allocate a new page table. If table entry `i` in
/// table `base` is empty, allocate a new page table and point `base[i]` at /// table `base` is empty, allocate a new page table and point `base[i]` at
/// it. /// it.
/// \arg base Existing page table being indexed into /// \arg base Existing page table being indexed into
/// \arg i Index into the existing table to check /// \arg i Index into the existing table to check
void check_needs_page(page_table *base, unsigned i); /// \art user True if this is a userspace mapping
void check_needs_page(page_table *base, unsigned i, bool user);
/// Low-level routine for mapping a number of pages into the given page table. /// Low-level routine for mapping a number of pages into the given page table.
/// \arg pml4 The root page table to map into /// \arg pml4 The root page table to map into
/// \arg phys_addr The starting physical address of the pages to be mapped /// \arg phys_addr The starting physical address of the pages to be mapped
/// \arg virt_addr The starting virtual address ot the memory to be mapped /// \arg virt_addr The starting virtual address ot the memory to be mapped
/// \arg count The number of pages to map /// \arg count The number of pages to map
/// \art user True if this is a userspace mapping
void page_in( void page_in(
page_table *pml4, page_table *pml4,
addr_t phys_addr, addr_t phys_addr,
addr_t virt_addr, addr_t virt_addr,
size_t count); size_t count,
bool user = true);
/// Low-level routine for unmapping a number of pages from the given page table. /// Low-level routine for unmapping a number of pages from the given page table.
/// \arg pml4 The root page table for this mapping /// \arg pml4 The root page table for this mapping
@@ -157,6 +182,8 @@ private:
/// \returns The number of pages retrieved /// \returns The number of pages retrieved
size_t pop_pages(size_t count, addr_t *address); size_t pop_pages(size_t count, addr_t *address);
page_table *m_kernel_pml4; ///< The PML4 of just kernel pages
page_block *m_free; ///< Free pages list page_block *m_free; ///< Free pages list
page_block *m_used; ///< In-use pages list page_block *m_used; ///< In-use pages list
@@ -278,7 +305,7 @@ struct page_table
entries[i] = (reinterpret_cast<uint64_t>(p) - pm::page_offset) | (flags & 0xfff); entries[i] = (reinterpret_cast<uint64_t>(p) - pm::page_offset) | (flags & 0xfff);
} }
void dump(int level = 4, uint64_t offset = page_manager::page_offset); void dump(int level = 4, int max_index = 511, uint64_t offset = page_manager::page_offset);
}; };

View File

@@ -1,11 +1,32 @@
#include "kutil/assert.h" #include "kutil/assert.h"
#include "console.h"
#include "log.h" #include "log.h"
#include "interrupts.h" #include "interrupts.h"
#include "pci.h" #include "pci.h"
struct pci_cap_msi : struct pci_cap_msi
public pci_cap
{ {
pci_cap::type id;
uint8_t next;
uint16_t control;
} __attribute__ ((packed));
struct pci_cap_msi32
{
pci_cap::type id;
uint8_t next;
uint16_t control;
uint32_t address;
uint16_t data;
uint16_t reserved;
uint32_t mask;
uint32_t pending;
} __attribute__ ((packed));
struct pci_cap_msi64
{
pci_cap::type id;
uint8_t next;
uint16_t control; uint16_t control;
uint64_t address; uint64_t address;
uint16_t data; uint16_t data;
@@ -14,17 +35,33 @@ struct pci_cap_msi :
uint32_t pending; uint32_t pending;
} __attribute__ ((packed)); } __attribute__ ((packed));
struct pci_cap_msix :
public pci_cap
{
uint16_t control;
uint64_t address;
uint16_t data;
uint16_t reserved;
uint32_t mask;
uint32_t pending;
} __attribute__ ((packed));
void dump_msi(pci_cap_msi *cap)
{
auto cons = console::get();
cons->printf("MSI Cap:\n");
cons->printf(" id: %02x\n", cap->id);
cons->printf(" next: %02x\n", cap->next);
cons->printf("control: %04x\n", cap->control);
if (cap->control & 0x0080) {
pci_cap_msi64 *cap64 = reinterpret_cast<pci_cap_msi64 *>(cap);
cons->printf("address: %016x\n", cap64->address);
cons->printf(" data: %04x\n", cap64->data);
if (cap->control & 0x100) {
cons->printf(" mask: %08x\n", cap64->mask);
cons->printf("pending: %08x\n", cap64->pending);
}
} else {
pci_cap_msi32 *cap32 = reinterpret_cast<pci_cap_msi32 *>(cap);
cons->printf("address: %08x\n", cap32->address);
cons->printf(" data: %04x\n", cap32->data);
if (cap->control & 0x100) {
cons->printf(" mask: %08x\n", cap32->mask);
cons->printf("pending: %08x\n", cap32->pending);
}
}
cons->putc('\n');
};
pci_device::pci_device() : pci_device::pci_device() :
m_base(nullptr), m_base(nullptr),
@@ -60,19 +97,25 @@ pci_device::pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t fu
uint16_t *command = reinterpret_cast<uint16_t *>(&m_base[1]); uint16_t *command = reinterpret_cast<uint16_t *>(&m_base[1]);
*command |= 0x400; // Mask old INTx style interrupts *command |= 0x400; // Mask old INTx style interrupts
log::info(logs::device, "Found PCIe device at %02d:%02d:%d of type %d.%d id %04x:%04x", uint16_t *status = command + 1;
bus, device, func, m_class, m_subclass, m_vendor, m_device);
// Walk the extended capabilities list log::info(logs::device, "Found PCIe device at %02d:%02d:%d of type %x.%x.%x id %04x:%04x",
uint8_t next = m_base[13] & 0xff; bus, device, func, m_class, m_subclass, m_progif, m_vendor, m_device);
while (next) {
pci_cap *cap = reinterpret_cast<pci_cap *>(kutil::offset_pointer(m_base, next));
next = cap->next;
if (cap->id == pci_cap::type::msi) { if (*status & 0x0010) {
m_msi = cap; // Walk the extended capabilities list
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(cap); uint8_t next = m_base[13] & 0xff;
mcap->control |= ~0x1; // Mask interrupts while (next) {
pci_cap *cap = reinterpret_cast<pci_cap *>(kutil::offset_pointer(m_base, next));
next = cap->next;
log::debug(logs::device, " - found PCI cap type %02x", cap->id);
if (cap->id == pci_cap::type::msi) {
m_msi = cap;
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(cap);
mcap->control &= ~0x70; // at most 1 vector allocated
mcap->control |= 0x01; // Enable interrupts, at most 1 vector allocated
}
} }
} }
} }
@@ -111,9 +154,19 @@ pci_device::write_msi_regs(addr_t address, uint16_t data)
kassert(m_msi, "Tried to write MSI for a device without that cap"); kassert(m_msi, "Tried to write MSI for a device without that cap");
if (m_msi->id == pci_cap::type::msi) { if (m_msi->id == pci_cap::type::msi) {
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(m_msi); pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(m_msi);
mcap->address = address; if (mcap->control & 0x0080) {
mcap->data = data; pci_cap_msi64 *mcap64 = reinterpret_cast<pci_cap_msi64 *>(m_msi);
mcap->control |= 1; mcap64->address = address;
mcap64->data = data;
} else {
pci_cap_msi32 *mcap32 = reinterpret_cast<pci_cap_msi32 *>(m_msi);
mcap32->address = address;
mcap32->data = data;
}
uint16_t control = mcap->control;
control &= 0xff8f; // We're allocating one vector, clear 6::4
control |= 0x0001; // Enable MSI
mcap->control = control;
} else { } else {
kassert(0, "MIS-X is NYI"); kassert(0, "MIS-X is NYI");
} }

90
src/kernel/scheduler.cpp Normal file
View File

@@ -0,0 +1,90 @@
#include "apic.h"
#include "console.h"
#include "cpu.h"
#include "gdt.h"
#include "interrupts.h"
#include "log.h"
#include "page_manager.h"
#include "scheduler.h"
scheduler scheduler::s_instance(nullptr);
//static const uint32_t quantum = 2000000;
static const uint32_t quantum = 20000000;
const int stack_size = 0x1000;
extern "C" void taskA();
extern "C" void taskB();
scheduler::scheduler(lapic *apic) :
m_apic(apic),
m_current(0)
{
m_processes.ensure_capacity(50);
}
static process
create_process(uint16_t pid, void (*rip)())
{
uint64_t flags;
__asm__ __volatile__ ( "pushf; pop %0" : "=r" (flags) );
uint16_t kcs = (1 << 3) | 0; // Kernel CS is GDT entry 1, ring 0
uint16_t cs = (5 << 3) | 3; // User CS is GDT entry 5, ring 3
uint16_t kss = (2 << 3) | 0; // Kernel SS is GDT entry 2, ring 0
uint16_t ss = (4 << 3) | 3; // User SS is GDT entry 4, ring 3
void *stack0 = kutil::malloc(stack_size);
void *sp0 = kutil::offset_pointer(stack0, stack_size);
cpu_state *state = reinterpret_cast<cpu_state *>(sp0) - 1;
kutil::memset(state, 0, sizeof(cpu_state));
state->ds = state->ss = ss;
state->cs = cs;
state->rflags = 0x202;
state->rip = reinterpret_cast<uint64_t>(rip);
page_table *pml4 = page_manager::get()->create_process_map();
state->user_rsp = page_manager::initial_stack;
log::debug(logs::task, "Creating PID %d:", pid);
log::debug(logs::task, " RSP0 %016lx", state);
log::debug(logs::task, " RSP3 %016lx", state->user_rsp);
log::debug(logs::task, " PML4 %016lx", pml4);
return {pid, reinterpret_cast<addr_t>(state), pml4};
}
void
scheduler::start()
{
log::info(logs::task, "Starting scheduler.");
m_apic->enable_timer(isr::isrTimer, 128, quantum, false);
m_processes.append({0, 0, page_manager::get_pml4()}); // The kernel idle task, also the thread we're in now
m_processes.append(create_process(1, &taskA));
m_processes.append(create_process(2, &taskB));
}
addr_t
scheduler::tick(addr_t rsp0)
{
log::debug(logs::task, "Scheduler tick.");
m_processes[m_current].rsp = rsp0;
m_current = (m_current + 1) % m_processes.count();
rsp0 = m_processes[m_current].rsp;
// Set rsp0 to after the end of the about-to-be-popped cpu state
tss_set_stack(0, rsp0 + sizeof(cpu_state));
// Swap page tables
page_table *pml4 = m_processes[m_current].pml4;
page_manager::set_pml4(pml4);
m_apic->reset_timer(quantum);
return rsp0;
}

46
src/kernel/scheduler.h Normal file
View File

@@ -0,0 +1,46 @@
#pragma once
/// \file scheduler.h
/// The task scheduler and related definitions
#include "kutil/memory.h"
#include "kutil/vector.h"
class lapic;
struct page_table;
struct process
{
uint16_t pid;
addr_t rsp;
page_table *pml4;
};
/// The task scheduler
class scheduler
{
public:
/// Constructor.
/// \arg apic Pointer to the local APIC object
scheduler(lapic *apic);
/// Start the scheduler working. This may involve starting
/// timer interrupts or other preemption methods.
void start();
/// Handle a timer tick
/// \arg rsp0 The stack pointer of the current interrupt handler
/// \returns The stack pointer to handler to switch to
addr_t tick(addr_t rsp0);
/// Get a reference to the system scheduler
/// \returns A reference to the global system scheduler
static scheduler & get() { return s_instance; }
private:
lapic *m_apic;
kutil::vector<process> m_processes;
uint16_t m_current;
static scheduler s_instance;
};

View File

@@ -29,7 +29,11 @@ def build(bld):
return node.path_from(node.ctx.launch_node()) return node.path_from(node.ctx.launch_node())
def run(self): def run(self):
from subprocess import check_output from subprocess import check_output
args = self.env.objdump + ["--source", "-D", self.inputs[0].abspath()] args = self.env.objdump + [
"--source",
"-D",
"-M", "intel",
self.inputs[0].abspath()]
with file(self.outputs[0].abspath(), 'w') as output: with file(self.outputs[0].abspath(), 'w') as output:
output.write(check_output(args)) output.write(check_output(args))

View File

@@ -8,27 +8,27 @@ 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 F>
typename std::enable_if<is_enum_bitfield<E>::value,E>::type typename std::enable_if<is_enum_bitfield<E>::value,E>::type
operator & (E lhs, E rhs) operator & (E lhs, F rhs)
{ {
return static_cast<E> ( return static_cast<E> (
static_cast<typename std::underlying_type<E>::type>(lhs) & static_cast<typename std::underlying_type<E>::type>(lhs) &
static_cast<typename std::underlying_type<E>::type>(rhs)); static_cast<typename std::underlying_type<E>::type>(rhs));
} }
template <typename E> template <typename E, typename F>
typename std::enable_if<is_enum_bitfield<E>::value,E>::type typename std::enable_if<is_enum_bitfield<E>::value,E>::type
operator | (E lhs, E rhs) operator | (E lhs, F rhs)
{ {
return static_cast<E> ( return static_cast<E> (
static_cast<typename std::underlying_type<E>::type>(lhs) | static_cast<typename std::underlying_type<E>::type>(lhs) |
static_cast<typename std::underlying_type<E>::type>(rhs)); static_cast<typename std::underlying_type<E>::type>(rhs));
} }
template <typename E> template <typename E, typename F>
typename std::enable_if<is_enum_bitfield<E>::value,E>::type typename std::enable_if<is_enum_bitfield<E>::value,E>::type
operator ^ (E lhs, E rhs) operator ^ (E lhs, F rhs)
{ {
return static_cast<E> ( return static_cast<E> (
static_cast<typename std::underlying_type<E>::type>(lhs) ^ static_cast<typename std::underlying_type<E>::type>(lhs) ^
@@ -42,9 +42,9 @@ operator ~ (E rhs)
return static_cast<E>(~static_cast<typename std::underlying_type<E>::type>(rhs)); return static_cast<E>(~static_cast<typename std::underlying_type<E>::type>(rhs));
} }
template <typename E> template <typename E, typename F>
typename std::enable_if<is_enum_bitfield<E>::value,E>::type& typename std::enable_if<is_enum_bitfield<E>::value,E>::type&
operator |= (E &lhs, E rhs) operator |= (E &lhs, F rhs)
{ {
lhs = static_cast<E>( lhs = static_cast<E>(
static_cast<typename std::underlying_type<E>::type>(lhs) | static_cast<typename std::underlying_type<E>::type>(lhs) |
@@ -53,9 +53,9 @@ operator |= (E &lhs, E rhs)
return lhs; return lhs;
} }
template <typename E> template <typename E, typename F>
typename std::enable_if<is_enum_bitfield<E>::value,E>::type& typename std::enable_if<is_enum_bitfield<E>::value,E>::type&
operator &= (E &lhs, E rhs) operator &= (E &lhs, F rhs)
{ {
lhs = static_cast<E>( lhs = static_cast<E>(
static_cast<typename std::underlying_type<E>::type>(lhs) & static_cast<typename std::underlying_type<E>::type>(lhs) &
@@ -64,9 +64,9 @@ operator &= (E &lhs, E rhs)
return lhs; return lhs;
} }
template <typename E> template <typename E, typename F>
typename std::enable_if<is_enum_bitfield<E>::value,E>::type& typename std::enable_if<is_enum_bitfield<E>::value,E>::type&
operator ^= (E &lhs, E rhs) operator ^= (E &lhs, F rhs)
{ {
lhs = static_cast<E>( lhs = static_cast<E>(
static_cast<typename std::underlying_type<E>::type>(lhs) ^ static_cast<typename std::underlying_type<E>::type>(lhs) ^

38
src/modules/kutil/guid.h Normal file
View File

@@ -0,0 +1,38 @@
#pragma once
/// \file guid.h
/// Definition of the guid type and related functions
#include <stdint.h>
#include "kutil/misc.h"
namespace kutil {
/// A GUID
struct guid
{
uint64_t a, b;
};
/// Make a GUID by writing it naturally-ordered in code:
/// AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE becomes:
/// make_guid(0xAAAAAAAA, 0xBBBB, 0xCCCC, 0xDDDD, 0xEEEEEEEEEEEE);
/// \returns The guid object
inline constexpr guid make_guid(uint32_t a, uint16_t b, uint16_t c, uint16_t d, uint64_t e)
{
const uint64_t h =
static_cast<uint64_t>(c) << 48 |
static_cast<uint64_t>(b) << 32 |
a;
const uint64_t l =
static_cast<uint64_t>(byteswap(e & 0xffffffff)) << 32 |
(byteswap(e >> 32) & 0xffff0000) |
((d << 8) & 0xff00) | ((d >> 8) & 0xff);
return {h, l};
}
} // namespace kutil
inline bool operator==(const kutil::guid &a, const kutil::guid &b) { return a.a == b.a && a.b == b.b; }

55
wscript
View File

@@ -80,6 +80,7 @@ def configure(ctx):
'-ffreestanding', '-ffreestanding',
'-nodefaultlibs', '-nodefaultlibs',
'-fno-builtin', '-fno-builtin',
'-mno-sse',
'-fno-omit-frame-pointer', '-fno-omit-frame-pointer',
'-mno-red-zone', '-mno-red-zone',
] ]
@@ -141,6 +142,20 @@ def configure(ctx):
'-nostartfiles', '-nostartfiles',
]) ])
ctx.env.append_value('QEMUOPTS', [
'-smp', '1',
'-m', '512',
'-d', 'mmu,int,guest_errors',
'-D', 'popcorn.log',
'-cpu', 'Broadwell',
'-M', 'q35',
'-no-reboot',
'-nographic',
])
if os.path.exists('/dev/kvm'):
ctx.env.append_value('QEMUOPTS', ['--enable-kvm'])
env = ctx.env env = ctx.env
ctx.setenv('boot', env=env) ctx.setenv('boot', env=env)
ctx.recurse(join("src", "boot")) ctx.recurse(join("src", "boot"))
@@ -268,6 +283,14 @@ def build(bld):
bld.recurse(join("src", "tests")) bld.recurse(join("src", "tests"))
class QemuContext(BuildContext):
cmd = 'qemu'
fun = 'qemu'
class DebugQemuContext(QemuContext):
cmd = 'debug'
fun = 'qemu'
def qemu(ctx): def qemu(ctx):
import subprocess import subprocess
subprocess.call("rm popcorn.log", shell=True) subprocess.call("rm popcorn.log", shell=True)
@@ -275,24 +298,34 @@ def qemu(ctx):
'qemu-system-x86_64', 'qemu-system-x86_64',
'-drive', 'if=pflash,format=raw,file={}/flash.img'.format(out), '-drive', 'if=pflash,format=raw,file={}/flash.img'.format(out),
'-drive', 'format=raw,file={}/popcorn.img'.format(out), '-drive', 'format=raw,file={}/popcorn.img'.format(out),
'-smp', '1', ] + ctx.env.QEMUOPTS)
'-m', '512',
'-d', 'mmu,int,guest_errors', def debug(ctx):
'-D', 'popcorn.log', import subprocess
'-cpu', 'Broadwell', subprocess.call("rm popcorn.log", shell=True)
'-M', 'q35', subprocess.call([
'-no-reboot', 'qemu-system-x86_64',
'-nographic', '-drive', 'if=pflash,format=raw,file={}/flash.img'.format(out),
]) '-drive', 'format=raw,file={}/popcorn.img'.format(out),
'-s',
] + ctx.env.QEMUOPTS)
def vbox(ctx): def vbox(ctx):
import os import os
from os.path import join
from shutil import copy from shutil import copy
from subprocess import call from subprocess import call
dest = os.getenv("VBOX_DEST") vbox_dest = os.getenv("VBOX_DEST")
copy("{}/popcorn.img".format(out), "{}/popcorn.img".format(dest))
ext = 'qed'
dest = join(vbox_dest, "popcorn.{}".format(ext))
call("qemu-img convert -f raw -O {} build/popcorn.img {}".format(ext, dest), shell=True)
copy(join("build", "src", "kernel", "popcorn.elf"), vbox_dest);
def listen(ctx):
from subprocess import call
call("nc -l -p 5555", shell=True) call("nc -l -p 5555", shell=True)