Mathieu Serandour 1 year ago
parent
commit
de70d136fb
  1. 2
      Makefile
  2. 82
      kernel/drivers/nvme/commands.c
  3. 12
      kernel/drivers/nvme/commands.h
  4. 134
      kernel/drivers/nvme/nvme.c
  5. 4
      kernel/drivers/nvme/nvme.h
  6. 13
      kernel/entry.c

2
Makefile

@ -19,6 +19,8 @@ QEMU_COMMON_ARGS := -bios /usr/share/ovmf/OVMF.fd \
-vga virtio \
-no-reboot \
-D qemu.log \
-trace "pci_nvme_*" \
-trace "apic_*" \
-device nvme,drive=NVME1,serial=deadbeef \
-drive format=raw,if=none,id=NVME1,file=

82
kernel/drivers/nvme/commands.c

@ -18,54 +18,6 @@ void doorbell_submission(
}
/**
* @brief enqueue the sqe sbmission queue entry
* structure to the given submission queue. Update
* the associated doorbell register and the
* submission tail value in the sq queue structure.
*
*
* @param doorbell_stride the NVM doorbell stride
* @param regs the virtual address of the NVME
* register space (bar0 base)
* @param sq a pointer to the submission queue
* structure in which the command is to
* be enqueued
* @param sqe a pointer to the submission queue
* entry structure describing the command
* be enqueue
* @return int
*/
static int insert_command(
unsigned doorbell_stride,
struct regs* regs,
struct queue* sq,
struct subqueuee* sqe
) {
assert(!queue_full(sq));
volatile
struct subqueuee* tail = queue_tail_ptr(sq);
memcpy(tail, sqe, sizeof(struct subqueuee));
log_warn("INSERT COMMAND %lx IN QUEUE %x", tail, sq->id);
uint16_t tail_idx = queue_produce(sq);
// doorbell
doorbell_submission(
doorbell_stride,
regs,
sq->id, // admin queue
tail_idx // new tail value
);
return tail_idx;
}
void async_command(
unsigned doorbell_stride,
struct regs* regs,
@ -76,12 +28,18 @@ void async_command(
uint32_t nsid,
uint64_t prp0,
uint64_t prp1,
uint64_t cdw10,
uint64_t cdw11,
uint64_t cdw12
uint32_t cdw10,
uint32_t cdw11,
uint32_t cdw12
) {
struct subqueuee commande = {
assert(!queue_full(sq));
volatile
struct subqueuee* tail = queue_tail_ptr(sq);
*tail = (struct subqueuee) {
.cmd = make_cmd(
opcode, // opcode
0, // fused
@ -100,12 +58,14 @@ void async_command(
.cdw12 = cdw12,
};
uint16_t tail_idx = queue_produce(sq);
insert_command(
doorbell_stride,
regs,
sq,
&commande
// doorbell
doorbell_submission(
doorbell_stride,
regs,
sq->id, // admin queue
tail_idx // new tail value
);
}
@ -120,9 +80,9 @@ void sync_command(
uint32_t nsid,
uint64_t prp0,
uint64_t prp1,
uint64_t cdw10,
uint64_t cdw11,
uint64_t cdw12
uint32_t cdw10,
uint32_t cdw11,
uint32_t cdw12
) {
async_command(

12
kernel/drivers/nvme/commands.h

@ -44,9 +44,9 @@ void async_command(
uint32_t nsid,
uint64_t prp0,
uint64_t prp1,
uint64_t cdw10,
uint64_t cdw11,
uint64_t cdw12
uint32_t cdw10,
uint32_t cdw11,
uint32_t cdw12
);
@ -84,7 +84,7 @@ void sync_command(
uint32_t nsid,
uint64_t prp0,
uint64_t prp1,
uint64_t cdw10,
uint64_t cdw11,
uint64_t cdw12
uint32_t cdw10,
uint32_t cdw11,
uint32_t cdw12
);

134
kernel/drivers/nvme/nvme.c

@ -34,7 +34,7 @@
static void remove(driver_t* this);
#define HANDLED_NAMESPACES 4
#define HANDLED_NAMESPACES 1
#define ADMIN_QUEUE_SIZE 2
#define IO_QUEUE_SIZE 64
@ -76,11 +76,8 @@ void perform_read_command(
struct data* data,
struct regs* regs,
uint64_t lba,
void* buf,
size_t count,
uint16_t cmdid,
uint32_t nsid
void* _buf,
size_t count
);
@ -471,7 +468,7 @@ static void identify_namespace(
ns->block_size_shift = lbafs[lbaid].ds;
if(!ns->block_size_shift)
ns->block_size_shift = 9;
log_info("namespace:\n"
" ns->id : %lx\n"
@ -501,8 +498,9 @@ static void identify_namespace(
static
void identify_active_namespaces(
struct data* data,
struct regs* regs
struct driver* this,
struct data* data,
struct regs* regs
) {
// physical base address
// of the destination data
@ -535,7 +533,17 @@ void identify_active_namespaces(
data->nns = i;
if(data->nns == 0)
log_warn("NVMe: no namespace found");
else if(data->nns > 1)
log_warn("NVMe: Bincows currently only handles"
"one NVMe namespace per controller. "
"%u namespaces are ignored for device %s.",
data->nns-1,
this->device->name
);
log_warn("%u found namespaces: %x", data->nns, data->namespaces[0].id);
// no need to keep this.
@ -690,17 +698,14 @@ int nvme_install(driver_t* this) {
// actually it holds no usefull information
identify_controller(data, bar0);
log_warn("creating IO queues");
createIOqueues(data, bar0);
log_warn("identify_active_namespaces");
identify_active_namespaces(data, bar0);
identify_active_namespaces(this, data, bar0);
// iterate over identified namespaces
for(unsigned i = 0; i < data->nns; i++)
identify_namespace(data, bar0, i);
//for(unsigned i = 0; i < data->nns; i++)
identify_namespace(data, bar0, 0);
sleep(10000);
this->status = DRIVER_STATE_OK;
@ -710,18 +715,17 @@ int nvme_install(driver_t* this) {
perform_read_command(
data,
bar0,
0,
0x001fca00 >> 9,
vaddr,
1,
10,
1
8
);
while(! queue_empty(&data->io_queues.sq))
sleep(1);
dump(
vaddr,
512,
0x1000 /8,
32,
DUMP_HEX8
);
@ -783,32 +787,80 @@ static void remove(driver_t* this) {
}
// better be well alligned
static
void perform_read_command(
struct data* data,
struct regs* regs,
uint64_t lba,
void* buf,
size_t count,
uint16_t cmdid,
uint32_t nsid
void* _buf,
size_t count
) {
uint64_t paddr = trv2p(buf);
// general assert protection
assert(lba < data->namespaces[0].capacity);
assert(lba + count < data->namespaces[0].capacity);
assert(data->namespaces[0].id == 1);
assert(data->nns >= 1);
async_command( // create IO completion queue
data->doorbell_stride,
regs, // NVMe register space
&data->io_queues.sq, // admin submission queue
OPCODE_IO_READ,
cmdid, // cmdid
nsid, // nsid
paddr, // prp0
0, // prp1
lba&0xffffffff, // cdw10: low lba part
lba>>32, // cdw11: high lba part
count // cdw12 low part: count
// NVME PRP: must be dword aligned
assert_aligned(_buf, 4);
uint64_t buf_vaddr = (uint64_t)_buf;
unsigned shift = data->namespaces[0].block_size_shift;
// must not cross a 4K page boudary
assert(
((buf_vaddr + (1 << shift)) & ~0x0fff)
==( buf_vaddr & ~0x0fff)
);
}
while(count != 0) {
unsigned c = 0;
uint64_t page_base = buf_vaddr & ~0x0fff;
// find the biggest size for the access
// that does not cross a page border (in
// our shitty architecture)
int i = 0;
while(1) {
uint64_t next = buf_vaddr + (1 << shift);
// crossed a 4K page border
if((next & ~0x0fff) != page_base)
break;
c++;
count--;
log_warn("%lx - %lx - %lx", next, page_base, (1 << shift));
buf_vaddr = next;
}
// c == 0 if the buffer is not well aligned
assert(c != 0);
uint64_t paddr = trv2p((void*)buf_vaddr);
async_command( // create IO completion queue
data->doorbell_stride,
regs, // NVMe register space
&data->io_queues.sq, // admin submission queue
OPCODE_IO_READ,
0, // cmdid - unused
1, // nsid
paddr, // prp0
0, // prp1
lba&0xffffffff, // cdw10: low lba part
lba>>32, // cdw11: high lba part
c // cdw12 low part: count
);
buf_vaddr += c >> shift;
}
}

4
kernel/drivers/nvme/nvme.h

@ -9,6 +9,10 @@ struct driver;
int nvme_install(struct driver*);
// 512 - 1024 - 2048 - 4096
int nvme_get_blocksize(struct driver*);
/**
* @brief read contiguous memory region from
* NVME drive. On error, a kernel panic occurs.

13
kernel/entry.c

@ -111,9 +111,7 @@ static void init_memory(
) {
log_debug("init memory...");
init_physical_allocator(memmap_tag);
init_paging(memmap_tag);
init_paging (memmap_tag);
// map MMIOs
map_pages(
@ -191,6 +189,7 @@ void _start(struct stivale2_struct *stivale2_struct) {
set_logging_level(LOG_LEVEL_DEBUG);
setup_isrs();
read_acpi_tables((void*)rsdp_tag_ptr->rsdp);
init_memory(memmap_tag, framebuffer_tag);
@ -223,13 +222,17 @@ void _start(struct stivale2_struct *stivale2_struct) {
// terminal is successfully installed
init_gdt_table();
puts(&_binary_bootmessage_txt);
printf("boot logs:\n");
puts(log_get());
log_flush();
hpet_init();
apic_setup_clock();
pcie_init();
pic_init();
@ -237,8 +240,7 @@ void _start(struct stivale2_struct *stivale2_struct) {
ps2kb_set_event_callback(kbhandler);
hpet_init();
apic_setup_clock();
//printf("issou");
@ -246,6 +248,7 @@ void _start(struct stivale2_struct *stivale2_struct) {
for(;;) {
asm volatile("hlt");
//log_info("clock()=%lu", clock());
}
__builtin_unreachable();

Loading…
Cancel
Save