Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* ahci.c : Contains the implementation of the AHCI HBA driver. (GPLv2)
- * */
- #include "kernel.h"
- #include "kdata.h"
- #include "pci_regs.h" /* defines from Linux uapi headers */
- #include "pci.h"
- #include "ahci.h"
- #include "module.h"
- #define AHCIBABBLE
- #ifdef AHCIBABBLE
- #define abprintf printf
- #else
- #define abprintf noprintf
- #endif
- struct ale {
- struct list_head devs;
- struct ahci_device* dev;
- } ahci_dl = { .dev = NULL, .devs = LIST_HEAD_INIT(ahci_dl.devs) };
- /* BITFIELDS GENERAL: writing to lower 8/16 bits can cause BYTE/WORD access
- * which is invalid -> use -fstict-voatile-bitfields for gcc -- */
- #ifndef __clang__
- STATIC_ASSERT(sizeof(struct ahci_port)==0x80, ahci_port_not_80h_bytes);
- STATIC_ASSERT(sizeof(struct ahci_hba)==0x180, ahci_hba_not_180h_bytes);
- STATIC_ASSERT(sizeof(struct ahci_cmdlst)==0x400, ahci_hba_not_400h_bytes);
- STATIC_ASSERT(sizeof(struct ahci_cmdtbl)==0x80, ahci_cmdtbl_not_80h_bytes);
- STATIC_ASSERT(sizeof(struct ahci_prdt)==0x10, ahci_prdt_not_10h_bytes);
- STATIC_ASSERT(sizeof(struct ahci_fis)==0x100, ahci_prdt_not_100h_bytes);
- STATIC_ASSERT(sizeof(struct sata_reg_h2d)==0x14, sata_reg_h2d_not_14h_bytes);
- #endif
- /* internal commands */
- enum disk_cmd {
- disk_cmd_identify,
- disk_cmd_read,
- disk_cmd_write,
- disk_cmd_trim
- };
- /* this is the concat of three structs pending/proc/ctx */
- struct cmd_ctx {
- struct list_head lst;
- enum disk_cmd c;
- /* pending cmd: initial values */
- uint64_t lba;
- uint64_t ct;
- void* ptr;
- uint16_t pmprt;
- /* proc cmd: during execution */
- struct wait_queue_head* wq_wake;
- uint16_t port;
- uint16_t slot;
- void* cur_buf;
- void* cont_ctx; /* specific continuation context */
- uint64_t bts_left;
- /* cmd_ctx: command build context */
- struct {
- uint16_t prdtl;
- uint16_t flg; /* PMP R C B R P W A CFL */
- } clh;
- struct ahci_ct_e* entry;
- uint32_t max_prdtl;
- }; /* 70bt size -> slab allocator? */
- struct ahci_ct_pool {
- uint32_t prdt_cap;
- struct ahci_ct_pool* next;
- uint32_t mask; /* not alloc always 1 */
- struct ahci_ct_e {
- uint16_t port, slot; /* dbg info? */
- struct ahci_cmdtbl* ptr;
- uint64_t pmem;
- } tables[32];
- };
- /* The IDENTIFY (PACKET) commands
- * */
- /* will (almost) always succeed */
- int ata_build_device_identify(struct ahci_device* ad, struct cmd_ctx* ctx)
- {
- struct ahci_cmdtbl* t = ctx->entry->ptr;
- struct sata_reg_h2d* r = &t->cfis.rh2d;
- void* ptr = ctx->ptr; uint64_t b1, b2 = 0;
- uint16_t bc1 = 512, bc2 = 0;
- /* we use exactly one PRD except someone manages
- * to send two pages in ptr for 512 bytes */
- if (ctx->max_prdtl < 1)
- return -1;
- /* build the FIS */
- bzero(t, sizeof(*t) + ctx->max_prdtl * sizeof(*t->prdts));
- r->fis_type = FIS_REG_H2D;
- r->pm_port = ctx->pmprt;
- r->res0 = 0;
- r->c = 1;
- r->command = ATA_CMD_IDENTIFY_DEVICE;
- r->feat_high = r->feat_low = 0; /* N/A */
- r->lba_high = r->lba_low = 0; /* N/A */
- r->count = 0; /* N/A */
- r->device = 0; /* bit4 is transport dependent */
- r->icc = 0;
- r->command = 0;
- r->res1 = 0;
- /* get the target memory */
- if (!ptr) {
- /* we use the device buffers */
- ptr = ad->identify_data[ctx->entry->port].ptr;
- b1 = ad->identify_data[ctx->entry->port].pm;
- } else {
- size_t ofs = (size_t)ptr % 4096;
- /* we have to get the offset ourself */
- b1 = mmgr_page_get_pmem(ptr, NULL);
- if (4096 - ofs < 512) { /* across pages */
- bc1 = 4096 - ofs;
- bc2 = 512 - (4096 - ofs);
- b2 = mmgr_page_get_pmem(ptr + bc1, NULL);
- /* but contiguous */
- if (b2 == b1 + 4096) {
- bc1 = 512; bc2 = 0;
- b2 = 0;
- }
- }
- b1 += ofs;
- }
- /* fill the cmdtbl */
- abprintf("IDENTIFY: %016llx %p\n", ctx->entry->pmem, t);
- abprintf("b1: %016llx\n", b1);
- t->prdts[0].dba64 = b1;
- t->prdts[0].dbc = bc1 - 1; /* IDENTIFY returns 512bt */
- t->prdts[0].I = 1; /* interrupt on completion */
- if (b2) {
- if (ctx->max_prdtl < 2)
- return -1;
- t->prdts[1].dba64 = b2;
- t->prdts[1].dbc = bc2 - 1; /* IDENTIFY returns 512bt */
- t->prdts[1].I = 1; /* interrupt on completion */
- t->prdts[0].I = 0; /* not too early */
- }
- /* and set the context */
- ctx->clh.prdtl = b2 ? 2 : 1;
- /* PMP; C (clear BSY,CI); 14h bt=5dw length*/
- ctx->clh.flg = 0x0405 | ((ctx->pmprt & 0xf) << 12);
- /* no further actions required, save for cleanup and waking */
- ctx->cont_ctx = NULL;
- ctx->cur_buf = NULL;
- ctx->bts_left = 0;
- return 0;
- }
- /* will (almost) always succeed */
- int atapi_build_packet_device_identify(struct ahci_device* ad,
- struct cmd_ctx* ctx)
- {
- printf("ATAPI IDENTIFY PACKET DEVICE %p %p\n", ad, ctx);
- return -1;
- }
- /* The READ_DMA_EXT commands
- * */
- /* The WRITE_DMA_EXT commands
- * */
- /* The SATA TRIM command
- * */
- const char* capstr(int idx, int limit, ...)
- {
- const char** rv = (const char**)(&limit + 1);
- return rv[min(idx, limit)];
- }
- void ahci_handle_interrupt(struct ahci_device* d, unsigned int irq)
- {
- /* irq is for MSI support /w base */
- printf("intr %p %u\n", d, irq);
- }
- int ahci_irq(unsigned int irq)
- {
- struct ale* d;
- printf("intr %u\n", irq);
- /*lock_list(ahci_dl.devs);*/
- list_for_each_entry(d, &ahci_dl.devs, devs) {
- if (d->dev->dev->intr_line == irq) {
- if (pci_check_interrupt(d->dev->dev)) {
- ahci_handle_interrupt(d->dev, irq);
- return 1;
- }
- }
- }
- /*unlock_list(ahci_dl.devs);*/
- return 0;
- }
- int ahci_find_slot(volatile struct ahci_port* p, int cmd_ct)
- {
- uint32_t bitmask = (p->pXci/* NCQ: | p->pXsact*/);
- for (int i = 0; i < cmd_ct; i++) {
- if (!(bitmask & 1))
- return i;
- bitmask >>= 1;
- }
- return -1;
- }
- void ahci_release_cmdtbl(struct ahci_device* ad, struct ahci_ct_e* entry)
- {
- struct ahci_ct_pool* ctp = ad->ctbl_pool;
- while (ctp) {
- ptrdiff_t ofs = ctp->tables - entry;
- if (ofs >= 0 && ofs < 32) {
- uint16_t p = entry->port, slt = entry->slot;
- /* we've found the entry */
- entry->port = entry->slot = 0xffff;
- if (!((1 << ofs) & __sync_fetch_and_and(&ctp->mask,
- ~(1 << ofs)))) {
- /* already cleared */
- abprintf("attempt to release command table at %p"
- " for port %u slot %u which was not "
- " acquired. Its offset is %ld.\n",
- entry->ptr, p, slt, ofs);
- }
- break;
- }
- ctp = ctp->next;
- }
- }
- struct ahci_ct_e* ahci_find_cmdtbl(struct ahci_device* ad, uint32_t prdt_ct)
- {
- struct ahci_ct_pool* ctp = ad->ctbl_pool;
- while (ctp) {
- if (ctp->prdt_cap >= prdt_ct) {
- int bit;
- Pos1:
- bit = bsf(~ctp->mask); /* 1 based */
- if (bit--) {
- /* one set (i.e. unset bit) */
- if (!((1 << bit) & __sync_fetch_and_or(&ctp->mask, 1 << bit))) {
- /* we have a slot */
- return &ctp->tables[bit];
- } else /* already set by a competitor */
- goto Pos1;
- }
- }
- /* advance */
- ctp = ctp->next;
- }
- /* no tables avaiable despite there being slots free */
- /* maybe we should ask for more tables? */
- static uint64_t warn_time = 0;
- if (jiffies - warn_time > HZ * 300) {
- /* warn every 5 minutes with continuous exhaustion */
- warn_time = jiffies;
- cprintf(KFMT_INFO, "Command table pool exhausted.\nI/O performance"
- " could suffer from this.\n");
- }
- return NULL;
- }
- void ahci_process_command(struct ahci_device* ad,
- struct ahci_port* p, struct list_head* lh)
- {
- struct cmd_ctx* cc; int slt, rv; uint32_t ct;
- struct ahci_cmdlst* cl = ad->command_lists[p - ad->ptr->port];
- /* sending a command to a device with CR=0? */
- if (!cl)
- abprintf("Ignoring command on port %zd as it's command"
- " engine is not running.\n", p - ad->ptr->port);
- next_cmd:
- slt = ahci_find_slot(p, ad->num_cmd); /* does not reserve -> no cleanup */
- if (slt == -1) /* still full */
- return;
- abprintf("processing command in slot %d on port %zd\n", slt, p - ad->ptr->port);
- /* we've acquired a slot */
- lock_list(*lh);
- cc = list_first_entry(lh, struct cmd_ctx, lst);
- unlock_list(*lh);
- list_del(&cc->lst, lh);
- if (!cc) /* the command vanished? */
- return;
- /* get the PRDT count needed */
- if (cc->ptr) {
- /*void* ptr2; */uint64_t s = cc->ct;
- /* we'll build a scatter list and then get a
- * count and throw the results away? insane! */
- ct = rdiv(s, 4096) + 1; /* first guess */
- if (ct > 8) {
- /* now we do this */
- die("implement this later.\n");
- }
- } else if (cc->c == disk_cmd_identify) {
- ct = 1;
- } else
- ct = 0;
- /* now acquire a command table with the matching requirements */
- /* limit is 4MiB per PRDT (BUT! PM!=contiguous) */
- find:
- cc->entry = ahci_find_cmdtbl(ad, ct);
- abprintf("find ok\n");
- if (cc->entry) {
- cc->entry->port = cc->port;
- cc->entry->slot = slt;
- cc->slot = slt;
- } else
- abprintf("no cmdtbl with %u PRDTs capacity found.\n", ct);
- if (!cc->entry && ct > 8) { /* divide by twoish */
- int log = bsr(ct) - 1;
- uint32_t new_ct = (1 << log);
- if (new_ct == ct)
- new_ct >>= 1;
- ct = new_ct;
- goto find;
- } else if (!cc->entry) { /* none available */
- /* skip command processing on this port */
- abprintf("waiting for cmdtbl to become available.\n");
- list_add(&cc->lst, lh);
- return;
- }
- abprintf("building command in cmdtbl at %p for port %u slot %u\n",
- cc->entry->ptr, cc->entry->port, cc->entry->slot);
- /* now build the command */
- cc->max_prdtl = ct;
- if (p->cmd.atapi) { /* ATAPI */
- switch (cc->c) {
- case disk_cmd_identify:
- rv = atapi_build_packet_device_identify(ad, cc);
- break;
- case disk_cmd_read:
- case disk_cmd_write:
- case disk_cmd_trim:
- die("not implemented yet!\n");
- break;
- }
- } else {
- switch (cc->c) { /* ATA */
- case disk_cmd_identify:
- rv = ata_build_device_identify(ad, cc);
- break;
- case disk_cmd_read:
- case disk_cmd_write:
- case disk_cmd_trim:
- die("not implemented yet!\n");
- break;
- }
- }
- /* only issue if rv==0 */
- if (rv) { /* cleanup */
- abprintf("ignored malformed command on port %u\n", cc->port);
- ahci_release_cmdtbl(ad, cc->entry);
- kfree(cc);
- } else if (!(p->pXci & (1 << slt))) {/* issue it on the port p slot slt */
- struct ahci_cmdhdr* ch = &ad->command_lists[cc->port]->chdrs[slt];
- bzero(ch, sizeof(*ch));
- ch->ctba64 = cc->entry->pmem;
- ch->prdtl = cc->clh.prdtl;
- ch->flags = cc->clh.flg;
- /* now enable the slot */
- p->pXci |= (1 << slt);
- abprintf("issued the command successfully.\n");
- /* and tell the IH about it */
- list_add_tail(&cc->lst, &ad->proc_cmds[cc->port]);
- } else {
- /* slot became used: parallel access to
- * inherently single threaded structure? */
- die("error posting command\n");
- }
- /* process next command */
- __sync_fetch_and_sub(&ad->pending_ct, 1);
- if (!list_empty(lh))
- goto next_cmd;
- }
- void ahci_worker(void* dat)
- {
- struct ahci_device* ad = dat;
- printf("AHCI worker is running\n");
- while (1) {
- uint32_t ps = ad->ptr->ports;
- printf("IS: %x ae: %u ie: %u\n", ad->ptr->intr,
- ad->ptr->ctrl.ae, ad->ptr->ctrl.ie);
- for (int i = 0; i < 32; i++)
- if (ps & (1 << i)) {
- printf("P%d det: %u ipm: %u serr: %x\n", i, ad->ptr->port[i].scr0.det,
- ad->ptr->port[i].scr0.ipm, ad->ptr[i].port[i].pXserr);
- printf("P%d st: %u cr: %u is: %x ie: %x sts: %x err: %x ci: %x sa: %x\n",
- i, ad->ptr->port[i].cmd.st, ad->ptr->port[i].cmd.cr,
- ad->ptr->port[i].pXis, ad->ptr->port[i].pXie,
- ad->ptr->port[i].tfd.sts, ad->ptr->port[i].tfd.err,
- ad->ptr->port[i].pXci, ad->ptr->port[i].pXsact);
- /* print the cmdlst */
- printf("CL: %016llx %016llx %p\n", ad->ptr->port[i].pXclb64,
- mmgr_page_get_pmem(ad->command_lists[i], NULL), ad->command_lists[i]);
- /* print CH0 */
- printf("CH0: %016llx flg: %x prdtl: %u bc: %u\n", ad->command_lists[i]->chdrs[0].ctba64,
- ad->command_lists[i]->chdrs[0].flags, ad->command_lists[i]->chdrs[0].prdtl,
- ad->command_lists[i]->chdrs[0].prdbc);
- /* Identify data */
- uint32_t* pti = ad->identify_data[i].ptr;
- printf("ID: %08x %08x %08x %08x\n", pti[0], pti[1],
- pti[2], pti[3]);
- }
- sleep(5);
- /* there are pending commands */
- if (ad->pending_ct) {
- for (int i = 0; i < 32; i++) {
- if (!(ps & (1 << i)))
- continue;
- if (list_empty(&ad->pending_cmds[i]))
- continue;
- /* we have commands to process */
- ahci_process_command(ad, &ad->ptr->port[i],
- &ad->pending_cmds[i]);
- }
- }
- }
- }
- void* ahci_allocate_command_table_pool(uint32_t ct, uint32_t prdt_cap, int bit64)
- {
- /* 128bt aligned size (8 PRDT/128 bt) */
- struct ahci_ct_pool* rv; uint32_t i = 0;
- size_t byte_ct, page_rest, indiv_ct, pg_pe, pg_ct, epa;
- struct page_range pr; int flg = MMGR_ALLOC_CONTIGUOUS;
- if (!bit64)
- flg = MMGR_ALLOC_32_BIT;
- if (!(rv = kzalloc(sizeof(*rv))))
- return NULL;
- /* bitmask limit */
- if (ct > 32)
- ct = 32;
- /* enforce alignment */
- if (prdt_cap % 8)
- prdt_cap += 8 - prdt_cap % 8;
- indiv_ct = 128 + 16 * prdt_cap;
- byte_ct = ct * indiv_ct;
- pg_pe = rdiv(indiv_ct, 4096);
- page_rest = pg_pe * 4096 - indiv_ct;
- /* pg_pe allocs must be contiguous, but if page_rest is low
- * individual tables can be split up to different locations */
- if (!page_rest || (page_rest % indiv_ct == 0)) {
- epa = pg_pe * 4096 / indiv_ct; /* entries per allocation */
- pg_ct = rdiv(ct, epa) * pg_pe;
- } else {
- pg_ct = rdiv(byte_ct, 4096);
- epa = ct;
- pg_pe = pg_ct;
- }
- /* allocate the physical memory */
- while (pg_ct) {
- void* ptr; size_t epa2 = epa, rem;
- mmgr_pmem_allocate(&pr, 1, pg_pe, flg);
- if (pr.count != pg_pe)
- die("Out of contiguous memory\n");
- rem = pg_pe * 4096;
- /* pg_pe zeroed contiguous memory at pr.base */
- ptr = mmgr_map_pmem(NULL, &pr, 1, MMGR_MAP_KERNEL |
- MMGR_MAP_WRITE | MMGR_MAP_NO_CACHING);
- if (!ptr)
- die("mmap not working\n");
- /* assign the entries */
- while (epa2--) {
- rv->tables[i].pmem = pr.base;
- rv->tables[i++].ptr = ptr;
- /* advance */
- pr.base += indiv_ct;
- ptr += indiv_ct;
- rem -= indiv_ct;
- if (rem < indiv_ct && epa2 > 1)
- die("algo error\n");
- }
- pg_ct -= pg_pe;
- }
- /* assign the other values */
- rv->prdt_cap = prdt_cap;
- rv->next = NULL;
- rv->mask = 0xffffffff << prdt_cap;
- /* print it */
- for (i = 0; i < ct; i++)
- printf("%016llx %p\n", rv->tables[i].pmem, rv->tables[i].ptr);
- return rv;
- }
- void ahci_issue_command(struct ahci_device* ad, int port, int pmprt, enum disk_cmd cmd,
- uint64_t lba, uint64_t ct, void* ptr, struct wait_queue_head* wq_h)
- {
- struct cmd_ctx* cc = kzalloc(sizeof(*cc));
- cc->c = cmd;
- cc->lba = lba;
- cc->ct = ct;
- cc->ptr = ptr;
- cc->wq_wake = wq_h;
- cc->pmprt = pmprt;
- cc->port = port;
- list_add_tail(&cc->lst, &ad->pending_cmds[port]);
- if (__sync_fetch_and_add(&ad->pending_ct, 1) == 1) {
- /* TODO: wake worker */
- }
- }
- int ahci_do_bohc(struct ahci_device* ad)
- {
- struct ahci_hba* hba = ad->ptr;
- if (hba->cap2.boh) {
- abprintf("performing AHCI BIOS handover...\n");
- preempt_disable();
- hba->bohc |= BOHC_OOS;
- /* SMI is issued */
- if (hba->bohc & BOHC_BOS) { /* we have to wait */
- mdelay(25);
- if (hba->bohc & BOHC_BB) { /* BIOS does it */
- int to = 30;
- while (hba->bohc & BOHC_BOS) {
- mdelay(100);
- if (!to--) /* abort after 3 seconds */
- break;
- }
- } else {;} /* we have to clean up */
- }
- /* we're done */
- preempt_enable();
- }
- /* always acquire (we're only nice to inform the bios) */
- return 0;
- }
- void ahci_reset_port(struct ahci_hba* h, struct ahci_port* p)
- {
- /* cap.sclo? */
- die("not impl %p %p\n", h, p);
- }
- void ahci_port_activate_device(struct ahci_hba* h, struct ahci_port* p)
- {
- abprintf("activate port %zd:\n", p - h->port);
- /* first check the presence flag */
- if (p->scr0.det == DET_NOT_PRESENT) {
- abprintf("activate: DET_NOT_PRESENT\n");
- /* nothing attached? */
- if (p->cmd.cpd) { /* we rely on CPD */
- if (!p->cmd.cps) {
- abprintf("confirmed by CPD\n");
- return;
- }
- /* there's something */
- if (!p->cmd.pod) {
- p->cmd.pod = 1; /* power it */
- ndelay(20);
- }
- if (p->scr0.det != DET_NOT_PRESENT)
- goto next_step;
- }
- /* spin-up? */
- if (!p->cmd.sud) { /* always !1 if cap.sss == 0 */
- abprintf("not spun-up yet?\n");
- if (p->pXserr & (1 << 26)) /* eXchange bit */
- p->pXserr |= (1 << 26); /* RWC */
- if (p->scr2.det != 0) { /* set to 0 prior sud */
- p->scr2.det = 0;
- ndelay(20);
- }
- p->cmd.sud = 1;
- udelay(50); /* wait 50 mus */
- if (p->pXserr & ((1 << 26) | (1 << 18))) { /* received sth. */
- /* COMRESET, COMWAKE */
- goto next_step;
- }
- /* send the reset */
- p->scr2.det = 1;
- mdelay(1);
- p->scr2.det = 0;
- udelay(50);
- if (p->scr0.det != DET_NOT_PRESENT)
- goto next_step;
- p->cmd.sud = 0; /* we're done enter listening mode */
- abprintf("not present\n");
- goto exit;
- }
- /* just try ICC */
- if (p->scr0.ipm != 1) {
- abprintf("IPM != ACTIVE\n");
- int ct = 50;
- while (p->cmd.icc && ct--)
- udelay(10);
- p->cmd.icc = 1;
- mdelay(10);
- if (p->scr0.det != DET_NOT_PRESENT)
- goto next_step;
- abprintf("unable to set to active\n");
- goto exit;
- }
- }
- /* det != 1*/
- next_step:
- if (p->scr0.det == DET_PRESENT) {
- /* almost done */
- if (p->scr0.ipm != IPM_ACTIVE) {
- int ct, wc;
- abprintf("Present but not active.\n");
- activ:
- ct = 50, wc = 0;
- rewait:
- while (p->cmd.icc && ct--) /* 500ms */
- mdelay(10);
- if (p->scr0.ipm != IPM_ACTIVE && !wc) {
- p->cmd.icc = 1;
- ndelay(50);
- wc++; ct = 50;
- goto rewait;
- }
- if (wc) {
- /* reset */
- p->scr2.det = 1;
- mdelay(1);
- goto next_step;
- }
- }
- abprintf("Device at port %zd is active and present.\n",
- p - h->port);
- abprintf("details: %x %x %x %u %u\n", p->pXserr, p->tfd.sts,
- p->tfd.err, p->scr0.det, p->scr0.ipm);
- return;
- } else if (p->scr0.det == DET_PHY_OFFLINE) {
- /* just deactivated */
- abprintf("PHY offline mode\n");
- goto activ;
- } else if (p->scr0.det == DET_PRESENT_NO_PHY) {
- abprintf("PRESENT_NO_PHY mode\n");
- goto activ; /* try the same */
- }
- exit:
- /* we're really done */
- abprintf("Not present at port %zd\n", p - h->port);
- return;
- }
- void ahci_enable_port(struct ahci_device* ad, int pt)
- {
- struct ahci_port* p = &ad->ptr->port[pt];
- int reset_ct = 0;
- /* skip non-implemented ports */
- if (!(ad->ptr->ports & (1 << pt)))
- return;
- Pos1:
- /* clear old data */
- p->pXserr = 0xffffffff; /* 10.1.2 -> 6. */
- p->pXis = 0xffffffff; /* clear all pending interrupts */
- /* first allow for the reception of FISes */
- p->cmd.fre = 1;
- ndelay(20); /* wait for BSY to show up */
- while (1) {
- uint8_t sts = p->tfd.sts;
- if (sts & TFS_ERR) {
- /* something went wrong! */
- if (sts == 0x7f) /* no device */
- break;
- else if (sts == 0x41 && p->tfd.err == 0x20) {
- if (p->pXsig == SIG_ATAPI)
- break; /* no medium */
- }
- cprintf(KFMT_WARN, "port%d indicated task file error %x"
- " while starting up.\n", pt, p->tfd.err);
- abprintf("scr1: %x %x\n", p->scr1.diag, p->scr1.err);
- abprintf("tfd: %x %x\n", p->tfd.sts, p->tfd.err);
- p->pXserr = 0xffffffff;
- if (!reset_ct++) {
- ahci_port_activate_device(ad->ptr, p);
- goto Pos1;
- } else if (reset_ct == 1) {
- /*ahci_reset_port(ad->ptr, p);*/
- goto Pos1;
- } else
- goto defer;
- }
- if (!(sts & (TFS_BSY | TFS_DRQ)))
- break;
- udelay(50);
- }
- /* set ST only if BSY,DRQ and DET=3h or IPM=2,6,8 */
- if (p->scr0.det != 3)
- ahci_port_activate_device(ad->ptr, p);
- if (p->tfd.sts & (TFS_BSY | TFS_DRQ))
- goto defer; /* listen */
- if (!((p->scr0.det == 3) || (p->scr0.ipm == 2) ||
- (p->scr0.ipm == 6) || (p->scr0.ipm == 8)))
- goto defer;
- /* we're allowed so set it */
- p->cmd.st = 1;
- /* Change in PhyRdy, CPS, TFS.err, PCS, DPS(I=1), UFS, HBFS, HBDS, IFS */
- p->pXie = (1 << 22) | (1 << 6) | (1 << 31) | (1 << 30)
- | (1 << 5) | (1 << 4) | (1 << 29) | (1 << 28) | (1 << 27);
- /* issue the IDENTIFY_(PACKET)_DEVICE command */
- ahci_issue_command(ad, pt, 0, disk_cmd_identify, 0, 0, NULL, NULL /* wq_worker_ad*/);
- printf("port %d is now processing commands\n", pt);
- return;
- defer: /* we're interested in status changes only */
- abprintf("port %d set to listening mode\n", pt);
- p->pXie = (1 << 22) | (1 << 31) | (1 << 6); /* PhyRdy change, CPS, CCS */
- }
- void ahci_stop_port(struct ahci_device* ad, int pt)
- {
- struct ahci_port* p = &ad->ptr->port[pt];
- int ct;
- /* skip non-implemented ports */
- if (!(ad->ptr->ports & (1 << pt)))
- return;
- /* print the BIOS provided status */
- abprintf("BIOS initial state of port %d:\n", pt);
- abprintf("\t%s%s\n", p->cmd.fre ? "FRE " : "",
- p->cmd.st ? "ST" : "");
- /* print the pointers */
- abprintf("BIOS command list located at %016llx\n", p->pXclb64);
- abprintf("BIOS FIS received buffer at %016llx\n", p->pXfb64);
- /* check if already off */
- if (!p->cmd.fre && !p->cmd.st)
- return;
- /* Now turn it off (2x500ms max else reset) */
- ct = 50;
- p->cmd.st = 0;
- while (p->cmd.cr && ct--)
- mdelay(10);
- if (ct == -1)
- goto reset;
- ct = 50;
- p->cmd.fre = 0;
- while (p->cmd.fr && ct--)
- mdelay(10);
- reset:
- if (ct == -1) {
- printf("Port %d: reset needed!\n", pt);
- /* TODO implement this */
- //ahci_reset_port(ad, pt);
- while (1);
- }
- /* The port is now off innit? */
- }
- /* we have ad->num_ports active ports needing num_cmd
- * command slots and one FIS receive buffer each.
- * The command lists are held in a shared pool. */
- void ahci_assign_buffers(struct ahci_device* ad)
- {
- size_t bt_ct, pg_ct; int flg = MMGR_ALLOC_ANY;
- struct page_range pr; int cc, fc, ic;
- /* Total size: 256*np+1024*np+512*np */
- bt_ct = ad->num_ports * (sizeof(struct ahci_fis)
- + sizeof(struct ahci_cmdlst) + 512);
- pg_ct = rdiv(bt_ct, 4096);
- /* allocate and map the physical memory */
- ic = cc = fc = ad->num_ports;
- if (!ad->ptr->cap.is_64bit)
- flg = MMGR_ALLOC_32_BIT;
- while (pg_ct) {
- void* ptr; size_t nb;
- /* we don't need contiguous memory */
- pg_ct -= mmgr_pmem_allocate(&pr, 1, pg_ct, flg);
- if (!pr.count)
- goto quit;
- nb = 4096 * pr.count;
- ptr = mmgr_map_pmem(NULL, &pr, 1, /*MMGR_MAP_MMIO | <- 0xe nope */
- MMGR_MAP_WRITE | MMGR_MAP_KERNEL | MMGR_MAP_NO_CACHING);
- if (!ptr)
- goto quit;
- /* now first process the command lists with 1K-alignment */
- while (cc && nb >= 1024) {
- int pt = ad->num_ports - cc--;
- if (ad->ptr->ports & (1 << pt)) {
- /* assign for implemented port */
- ad->ptr->port[pt].pXclb64 = pr.base;
- ad->command_lists[pt] = ptr;
- /* advance */
- pr.base += 1024;
- nb -= 1024;
- ptr += 1024;
- }
- }
- /* then the FIS buffers (256bt-aligned) */
- while (fc && nb >= 256) {
- int pt = ad->num_ports - fc--;
- if (ad->ptr->ports & (1 << pt)) {
- /* as above */
- ad->ptr->port[pt].pXfb64 = pr.base;
- ad->recv_fis[pt] = ptr;
- /* advance */
- pr.base += 256;
- nb -= 256;
- ptr += 256;
- }
- }
- while (ic && nb >= 512) {
- int pt = ad->num_ports - ic--;
- if (ad->ptr->ports & (1 << pt)) {
- /* as above */
- ad->identify_data[pt].pm = pr.base;
- ad->identify_data[pt].ptr = ptr;
- /* advance */
- pr.base += 512;
- nb -= 512;
- ptr += 512;
- } else
- ad->identify_data[pt].sig = 0;
- }
- /* exit early if we have non-impelented ports */
- if (!fc && !cc && !ic) {
- /* also deallocate if possible */
- if (nb >= 4096) {
- ptr += (size_t)ptr % 4096;
- mmgr_free_vmem(ptr, nb / 4096);
- }
- break;
- }
- }
- /* Now all implemented buffers are set */
- return;
- quit:
- die("Out of memory!\n");
- }
- /* also disables interrupts */
- void ahci_clear_interrupts(struct ahci_device* ad)
- {
- struct ahci_hba* h = ad->ptr;
- /* disable interrupts */
- h->ctrl.ie = 0;
- /* clear them in every port implemented */
- for (int i = 0; i < ad->num_ports; i++) {
- struct ahci_port* p;
- if ((h->ports & (1 << i)) == 0)
- continue;
- p = &h->port[i];
- /* disable and clear all */
- p->pXie = 0;
- p->pXis = 0xffffffff;
- }
- /* clear the global status */
- h->intr = 0xffffffff; /* W1C */
- }
- int ahci_device_init(struct pci_device* d)
- {
- int rv;
- /* Check progIf */
- if (d->device_interface != 1) {
- cprintf(KFMT_WARN, "Unsupported SATA program interface: %u\n",
- d->device_interface);
- return -ENOSYS;
- }
- /* Setup and map BAR5 if neccessary */
- if (d->ressources[5].type == PCI_BAR_PIO)
- return -ENOTSUP; /* AHCI requires MMIO at BAR5 */
- if (!d->ressources[5].mmio.base) {
- int atl = 0; uint64_t m;
- /* we first need pmem for the device */
- if (!d->ressources[5].mmio.size) {
- /* Ask PCI */
- d->ressources[5].mmio.size = pci_bar_get_size(d, 5);
- if (!d->ressources[5].mmio.size) {
- d->ressources[5].mmio.size = 0x1100; /* 32 ports */
- atl = 1; /* about to leave */
- }
- }
- /* Now we want to get some address */
- m = mmio_allocate_range(d->ressources[5].mmio.size,
- 0x3 & (d->ressources[5].type >> 1));
- if (!m)
- return -ENOMEM;
- /* and assign it */
- pci_set_bar(d, 5, d->ressources[5].type, m);
- if (d->ressources[5].mmio.base != m) {
- /* we can't */
- mmio_free_range(m, d->ressources[5].mmio.size);
- if (atl)
- d->ressources[5].mmio.size = 0;
- return -ENOTSUP;
- }
- }
- /* Now we possibly have to map the memory */
- if (!d->ressources[5].mmio.mapping) {
- void* mem; struct page_range pr;
- pr.base = d->ressources[5].mmio.base;
- pr.count = rdiv(d->ressources[5].mmio.size, 4096);
- mem = mmgr_map_pmem(NULL, &pr, 1, MMGR_MAP_MMIO |
- MMGR_MAP_WRITE | MMGR_MAP_KERNEL | MMGR_MAP_NO_CACHING);
- if (!mem)
- return -ENOMEM;
- d->ressources[5].mmio.mapping = mem;
- }
- /* we now have a working mapping */
- struct ahci_hba* h = d->ressources[5].mmio.mapping;
- struct ahci_device* ad = kzalloc(sizeof(*ad));
- if (!ad)
- return -ENOMEM;
- /* check for AE (access to other registers is disallowed before) */
- if (!(h->control & GHC_CNT_AE)) {
- abprintf("switching to AHCI mode\n");
- h->control |= GHC_CNT_AE;
- }
- /* fill the descriptors */
- ad->ptr = h;
- ad->num_ports = min(h->cap.num_ports + 1, bsr(h->ports));
- ad->num_cmd = h->cap.num_cmd + 1;
- for (int i = 0; i < 32; i++) {
- ad->pending_cmds[i] = LIST_HEAD_INIT(ad->pending_cmds[i]);
- ad->proc_cmds[i] = LIST_HEAD_INIT(ad->proc_cmds[i]);
- }
- #ifdef AHCIBABBLE /* Tell the world about it */
- printf("AHCI %.0x%x.%x%x conforming HBA detected\n",
- h->ahci_ver >> 24, 0xff & (h->ahci_ver >> 16),
- 0xff & (h->ahci_ver >> 8), h->ahci_ver & 0xff);
- /* and print the capablilities */
- printf("Features: %s%s%sspeed: %s\n",
- capstr(h->cap.ncq_supp, 1, "", "NCQ "),
- capstr(h->cap.ahci_only, 1, "Legacy support ", "AHCI only "),
- capstr(h->cap.is_64bit, 1, "32-bit ", "64-bit "),
- capstr(h->cap.if_speed, 4, "res", "1.5 Gbps", "3 Gbps", "6Gbps", ">6Gbps"));
- printf("AHCI HBA has %u ports and %u command slots\n",
- ad->num_ports, ad->num_cmd);
- #endif
- /* transfer ownership (incl NMI) */
- if (h->cap2.boh) {
- rv = ahci_do_bohc(ad);
- if (rv) {
- kfree(ad);
- return rv;
- }
- }
- /* disable every possible interrupt */
- if(pci_disable_interrupts(d))
- die("not possible!\n");
- /* Stop the running ports */
- for (int i = 0; i < ad->num_ports; i++)
- ahci_stop_port(ad, i);
- /* clear all interrupts and install the handler */
- ahci_clear_interrupts(ad); /* PCI disabled MSI and we're currently */
- install_irq_handler(ad->dev->intr_line, ahci_irq); /* not using it */
- /* assign the command lists and the FIS buffers */
- ahci_assign_buffers(ad);
- /* print them */
- for (int i = 0; i < ad->num_ports; i++) {
- printf("port%d: %016llx %016llx %p %p\n", i,
- ad->ptr->port[i].pXclb64,
- ad->ptr->port[i].pXfb64,
- ad->command_lists[i],
- ad->recv_fis[i]);
- printf(" %016llx %p\n", ad->identify_data[i].pm,
- ad->identify_data[i].ptr);
- }
- /* allow the device to write to our memory */
- pci_set_master(d);
- /* reenable the ports */
- for (int i = 0; i < ad->num_ports; i++)
- ahci_enable_port(ad, i);
- /* add the device to the list */
- struct ale* le = kmalloc(sizeof(*le));
- le->dev = ad;
- list_add_tail(&le->devs, &ahci_dl.devs);
- /* allocate the pool */
- ad->ctbl_pool = ahci_allocate_command_table_pool(
- 32, 8, h->cap.is_64bit);
- /* launch the worker */
- task_spawn(ahci_worker, ad, PRIORITY_ELEVATED);
- /* reenable interrupts */
- /* The port_enable function already set the per-port values */
- h->ctrl.ie = 1;
- pci_enable_interrupts(d);
- return 0;
- }
- int MODENTRY module_init()
- {
- printf("AHCI initialization...\n");
- struct pci_device* dev; int ct = 0, nc = 0, nf = 0;
- while ((dev = pci_find_device(1, 6, 1, ct++))) {
- int rv;
- printf("Found AHCI device at %02x:%02x.%x\n", dev->addr.bus_no,
- dev->addr.device_no, dev->addr.func_no);
- printf("Attempting initialization...\n");
- rv = ahci_device_init(dev);
- if (rv) {
- cprintf(KFMT_ERROR, "Initialization of this device failed.\n");
- nf++;
- } else {
- cprintf(KFMT_OK, "Initialization of AHCI HBA succeeded.\n");
- nc++;
- }
- }
- /* Summary */
- cprintf(nf ? KFMT_WARN : KFMT_OK, "Initialized %d AHCI Host Bus Adapters. (%d failed)\n", nc, nf);
- return 0;
- }
- void MODENTRY module_cleanup()
- {
- }
Advertisement
Add Comment
Please, Sign In to add comment